From 7276e6ed9b5532677d185f4fa5117d33be603b13 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 21 Mar 2025 09:26:17 +0100 Subject: [PATCH 001/121] Add Components to core --- application/apps/indexer/Cargo.toml | 1 + .../apps/indexer/components/Cargo.toml | 12 ++ .../apps/indexer/components/src/lib.rs | 187 ++++++++++++++++++ .../apps/indexer/components/src/tys.rs | 24 +++ application/apps/indexer/parsers/Cargo.toml | 4 + .../apps/indexer/parsers/src/dlt/mod.rs | 61 +++++- application/apps/indexer/parsers/src/lib.rs | 11 +- .../apps/indexer/parsers/src/prelude.rs | 1 + .../apps/indexer/parsers/src/someip.rs | 31 +++ application/apps/indexer/parsers/src/text.rs | 31 +++ .../apps/indexer/plugins_host/Cargo.toml | 4 +- .../plugins_host/src/parser_shared/mod.rs | 14 ++ .../plugins_host/src/v0_1_0/parser/mod.rs | 14 ++ application/apps/indexer/session/Cargo.toml | 1 + .../indexer/session/src/components/api.rs | 14 ++ .../indexer/session/src/components/mod.rs | 130 ++++++++++++ .../apps/indexer/session/src/handlers/mod.rs | 1 + .../indexer/session/src/handlers/options.rs | 65 ++++++ application/apps/indexer/session/src/lib.rs | 1 + application/apps/indexer/sources/Cargo.toml | 1 + .../indexer/sources/src/binary/pcap/legacy.rs | 23 ++- .../indexer/sources/src/binary/pcap/ng.rs | 19 +- .../apps/indexer/sources/src/binary/raw.rs | 17 ++ .../indexer/sources/src/command/process.rs | 16 ++ application/apps/indexer/sources/src/lib.rs | 3 +- .../apps/indexer/sources/src/prelude.rs | 9 + .../indexer/sources/src/serial/serialport.rs | 16 ++ .../apps/indexer/sources/src/socket/tcp.rs | 16 ++ .../apps/indexer/sources/src/socket/udp.rs | 16 ++ application/apps/indexer/stypes/Cargo.toml | 4 +- application/apps/indexer/stypes/src/lib.rs | 2 + .../apps/indexer/stypes/src/observe/mod.rs | 28 +++ .../apps/indexer/stypes/src/options/mod.rs | 139 +++++++++++++ 33 files changed, 900 insertions(+), 16 deletions(-) create mode 100644 application/apps/indexer/components/Cargo.toml create mode 100644 application/apps/indexer/components/src/lib.rs create mode 100644 application/apps/indexer/components/src/tys.rs create mode 100644 application/apps/indexer/parsers/src/prelude.rs create mode 100644 application/apps/indexer/session/src/components/api.rs create mode 100644 application/apps/indexer/session/src/components/mod.rs create mode 100644 application/apps/indexer/session/src/handlers/options.rs create mode 100644 application/apps/indexer/sources/src/prelude.rs create mode 100644 application/apps/indexer/stypes/src/options/mod.rs diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index fc422a3b5a..09a2367a41 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -7,6 +7,7 @@ members = [ "addons/file-tools", "addons/text_grep", "addons/bufread", + "components", "indexer_base", "merging", "parsers", diff --git a/application/apps/indexer/components/Cargo.toml b/application/apps/indexer/components/Cargo.toml new file mode 100644 index 0000000000..2bd627a543 --- /dev/null +++ b/application/apps/indexer/components/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "components" +version = "0.1.0" +edition = "2021" + +[dependencies] +thiserror.workspace = true +stypes = { path = "../stypes", features=["rustcore"] } +uuid = { workspace = true , features = ["serde", "v4"] } +tokio-util.workspace = true +tokio = { workspace = true , features = ["full"] } +log.workspace = true diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs new file mode 100644 index 0000000000..814be259df --- /dev/null +++ b/application/apps/indexer/components/src/lib.rs @@ -0,0 +1,187 @@ +mod tys; + +use log::error; +use tokio::{sync::oneshot, task}; +use tokio_util::sync::CancellationToken; +pub use tys::*; + +use std::collections::HashMap; +use uuid::Uuid; + +type Metadata<'a> = ( + ( + &'a stypes::Ident, + &'a (Option, Option), + ), + ( + &'a stypes::Ident, + &'a (Option, Option), + ), +); + +pub struct LazyLoadingTask { + ident: stypes::Ident, + rx: oneshot::Receiver, stypes::NativeError>>, + cancel: CancellationToken, +} + +impl LazyLoadingTask { + pub fn new( + ident: stypes::Ident, + rx: oneshot::Receiver, stypes::NativeError>>, + cancel: &CancellationToken, + ) -> Self { + Self { + ident, + rx, + cancel: cancel.clone(), + } + } +} + +pub struct Options { + pub source: Vec, + pub parser: Vec, + pub lazy_source: Option, + pub lazy_parser: Option, +} + +impl Options { + pub fn with_static(source: Vec, parser: Vec) -> Self { + Self { + source, + parser, + lazy_source: None, + lazy_parser: None, + } + } + pub fn source_has_lazy(&self) -> bool { + self.source + .iter() + .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) + } + pub fn parser_has_lazy(&self) -> bool { + self.parser + .iter() + .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) + } +} + +#[derive(Default)] +pub struct Components { + //TODO: "hot" validators also here + options: HashMap, Option)>, + sources: HashMap, + parsers: HashMap, +} + +impl Components { + pub fn register( + &mut self, + ident: &stypes::Ident, + static_fields_handle: Option, + lazy_fields_handle: Option, + ) -> Result<(), stypes::NativeError> { + if self.options.contains_key(&ident.uuid) || self.sources.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.options + .insert(ident.uuid, (static_fields_handle, lazy_fields_handle)); + Ok(()) + } + + /// Register source metadata and set options getter + pub fn register_source( + &mut self, + ident: &stypes::Ident, + static_fields_handle: Option, + lazy_fields_handle: Option, + ) -> Result<(), stypes::NativeError> { + self.register(ident, static_fields_handle, lazy_fields_handle)?; + self.sources.insert(ident.uuid, ident.to_owned()); + Ok(()) + } + + /// Register parser metadata and set options getter + pub fn register_parser( + &mut self, + ident: &stypes::Ident, + static_fields_handle: Option, + lazy_fields_handle: Option, + ) -> Result<(), stypes::NativeError> { + self.register(ident, static_fields_handle, lazy_fields_handle)?; + self.parsers.insert(ident.uuid, ident.to_owned()); + Ok(()) + } + + pub fn get_options( + &self, + source_origin: stypes::SourceOrigin, + source: Uuid, + parser: Uuid, + ) -> Result { + let ( + (source_ident, (source_static, source_lazy)), + (parser_ident, (parser_static, parser_lazy)), + ) = self.get(&source, &parser)?; + let source_fields = source_static + .map(|getter| getter(&source_origin)) + .unwrap_or(Ok(Vec::new()))?; + let parser_fields = parser_static + .map(|getter| getter(&source_origin)) + .unwrap_or(Ok(Vec::new()))?; + let mut options = Options::with_static(source_fields, parser_fields); + // Check do we have some lazy settings + if let (Some(getter), true) = (source_lazy, options.source_has_lazy()) { + let (tx, rx) = oneshot::channel(); + let tk = CancellationToken::new(); + getter(&source_origin, tx, &tk)?; + options.lazy_source = Some(LazyLoadingTask::new(source_ident.clone(), rx, &tk)); + } + if let (Some(getter), true) = (parser_lazy, options.source_has_lazy()) { + let (tx, rx) = oneshot::channel(); + let tk = CancellationToken::new(); + getter(&source_origin, tx, &tk)?; + options.lazy_parser = Some(LazyLoadingTask::new(parser_ident.clone(), rx, &tk)); + } + Ok(options) + } + + fn get<'a>( + &'a self, + source: &Uuid, + parser: &Uuid, + ) -> Result, stypes::NativeError> { + let source_ident = self.sources.get(source).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Source {} not found", source)), + })?; + let source_getter = self.options.get(source).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Source options of {} ({}) not found", + source_ident.name, source + )), + })?; + let parser_ident = self.parsers.get(parser).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Parser {} not found", parser)), + })?; + let parser_getter = self.options.get(parser).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Parser options of {} ({}) not found", + parser_ident.name, parser + )), + })?; + Ok(((source_ident, source_getter), (parser_ident, parser_getter))) + } +} diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs new file mode 100644 index 0000000000..3b9ec2d463 --- /dev/null +++ b/application/apps/indexer/components/src/tys.rs @@ -0,0 +1,24 @@ +use crate::*; +use std::{future::Future, pin::Pin}; +use stypes::NativeError; +use tokio_util::sync::CancellationToken; + +pub type FieldsResult = Result, stypes::NativeError>; + +pub type FieldsGetter = fn(&stypes::SourceOrigin) -> FieldsResult; + +pub type StaticFieldsResult = Result, stypes::NativeError>; + +pub type LazyFieldsGetter = fn( + &stypes::SourceOrigin, + oneshot::Sender, + &CancellationToken, +) -> Result<(), NativeError>; + +pub trait Component { + /// Returns an identificator + fn ident() -> stypes::Ident; + + /// Registration + fn register(components: &mut Components) -> Result<(), stypes::NativeError>; +} diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index f93b167980..85f612bfce 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -18,6 +18,10 @@ rand.workspace = true someip-messages = { git = "https://github.com/esrlabs/someip" } someip-payload = { git = "https://github.com/esrlabs/someip-payload" } someip-tools = { path = "../addons/someip-tools" } +stypes = { path = "../stypes", features=["rustcore"] } +uuid = { workspace = true , features = ["serde", "v4"] } +components = { path = "../components" } +tokio-util.workspace = true [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 68cdf16a7d..7a0279f8f4 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -211,7 +211,24 @@ impl SingleParser for DltRangeParser { impl SingleParser for DltRawParser { const MIN_MSG_LEN: usize = MIN_MSG_LEN; - fn parse_item( +const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +impl<'a> components::Component for DltParser<'a> { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Parser"), + uuid: DLT_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + +impl Parser for DltRangeParser { + fn parse( &mut self, input: &[u8], _timestamp: Option, @@ -223,6 +240,46 @@ impl SingleParser for DltRawParser { let total_consumed = input.len() - rest.len(); let item = (total_consumed, msg.map(|m| m.into())); - Ok(item) +const DLT_RANGE_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +impl components::Component for DltRangeParser { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Range Parser"), + uuid: DLT_RANGE_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + +impl Parser for DltRawParser { + fn parse( + &mut self, + input: &[u8], + timestamp: Option, + ) -> Result>)>, Error> { + parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { + self.parse_item(input, timestamp) + }) + } +} + +const DLT_RAW_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +impl components::Component for DltRawParser { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Raw Parser"), + uuid: DLT_RAW_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 36ecf6adc9..d4c20c73f2 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,14 +1,13 @@ #![deny(unused_crate_dependencies)] pub mod dlt; +pub mod prelude; pub mod someip; + pub mod text; use serde::Serialize; -use std::{ - fmt::{Debug, Display}, - io::Write, - iter, -}; +use std::{fmt::Display, future::Future, io::Write, iter}; use thiserror::Error; +use tokio_util::sync::CancellationToken; extern crate log; @@ -39,7 +38,7 @@ impl From for ParseYield { /// Parser trait that needs to be implemented for any parser we support /// in chipmunk -pub trait Parser { +pub trait Parser: components::Component { /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` /// diff --git a/application/apps/indexer/parsers/src/prelude.rs b/application/apps/indexer/parsers/src/prelude.rs new file mode 100644 index 0000000000..5336c3ed77 --- /dev/null +++ b/application/apps/indexer/parsers/src/prelude.rs @@ -0,0 +1 @@ +pub use crate::{dlt::DltParser, someip::SomeipParser, text::StringTokenizer}; diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index 4d71b02d16..d48eb5b612 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -300,6 +300,37 @@ impl SingleParser for SomeipParser { } } +unsafe impl Send for SomeipParser {} +unsafe impl Sync for SomeipParser {} + +impl Parser for SomeipParser { + fn parse( + &mut self, + input: &[u8], + timestamp: Option, + ) -> Result>)>, Error> { + parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { + self.parse_item(input, timestamp) + }) + } +} + +const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, +]); + +impl components::Component for SomeipParser { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("SomeIP Parser"), + uuid: SOMEIP_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + fn header_string(header: &Header) -> String { format!( "{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}", diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text.rs index 689cf20943..1a900c0f07 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text.rs @@ -56,6 +56,37 @@ where } } +impl Parser for StringTokenizer +where + StringMessage: LogMessage, +{ + fn parse( + &mut self, + input: &[u8], + timestamp: Option, + ) -> Result>)>, Error> { + parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { + self.parse_item(input, timestamp) + }) + } +} + +const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +]); + +impl components::Component for StringTokenizer { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Text Parser"), + uuid: TEXT_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use crate::Parser; diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index 8be0604c86..dcbf16d1c5 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -14,10 +14,10 @@ dirs.workspace = true toml.workspace = true blake3.workspace = true rand.workspace = true - wasmtime = "33.0" wasmtime-wasi = "33.0" - +uuid = { workspace = true , features = ["serde", "v4"] } +components = { path = "../components" } parsers = { path = "../parsers" } sources = { path = "../sources" } stypes = { path = "../stypes" } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index a15a434651..0634dbdfaa 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -180,3 +180,17 @@ impl p::Parser for PluginsParser { res } } + +impl components::Component for PluginsParser { + /// **ATTANTION** That's placeholder. Should be another way to delivery data + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Plugin"), + uuid: uuid::Uuid::new_v4(), + } + } + + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 8c2f465079..279f6ad3dd 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -169,3 +169,17 @@ impl p::Parser for PluginParser { Ok(res) } } + +impl components::Component for PluginParser { + /// **ATTANTION** That's placeholder. Should be another way to delivery data + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Plugin Parser"), + uuid: uuid::Uuid::new_v4(), + } + } + + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index de703dc6db..8d54a3b4e1 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -32,6 +32,7 @@ tokio-util.workspace = true uuid = { workspace = true , features = ["serde", "v4"] } walkdir.workspace = true plugins_host = {path = "../plugins_host/"} +components = { path = "../components" } [dev-dependencies] lazy_static.workspace = true diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs new file mode 100644 index 0000000000..d2ef631d7b --- /dev/null +++ b/application/apps/indexer/session/src/components/api.rs @@ -0,0 +1,14 @@ +use stypes::NativeError; +use tokio::sync::oneshot; +use uuid::Uuid; + +#[derive(Debug)] +pub enum API { + Shutdown(oneshot::Sender<()>), + GetOptions { + parser: Uuid, + source: Uuid, + origin: stypes::SourceOrigin, + tx: oneshot::Sender, Vec), NativeError>>, + }, +} diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs new file mode 100644 index 0000000000..ead496f1c5 --- /dev/null +++ b/application/apps/indexer/session/src/components/mod.rs @@ -0,0 +1,130 @@ +mod api; + +use api::*; + +use components::{Component, Components}; +use log::{debug, error, warn}; +use parsers::prelude as parsers; +use sources::prelude as sources; +use tokio::{ + sync::{ + mpsc::{error::SendError, unbounded_channel, UnboundedReceiver, UnboundedSender}, + oneshot::{self, error::RecvError}, + }, + task::{self}, +}; +use tokio_util::sync::CancellationToken; +use uuid::Uuid; + +pub struct ComponentsSession { + tx_api: UnboundedSender, +} + +impl ComponentsSession { + /// Starts a global components session + pub async fn new( + ) -> Result<(Self, UnboundedReceiver), stypes::NativeError> { + let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = + unbounded_channel(); + let (tx_callback_events, rx_callback_events): ( + UnboundedSender, + UnboundedReceiver, + ) = unbounded_channel(); + let mut components = Components::default(); + { + parsers::DltParser::register(&mut components)?; + parsers::SomeipParser::register(&mut components)?; + parsers::StringTokenizer::register(&mut components)?; + } + { + sources::BinaryByteSource::::register(&mut components)?; + sources::PcapLegacyByteSource::::register(&mut components)?; + sources::PcapngByteSource::::register(&mut components)?; + sources::TcpSource::register(&mut components)?; + sources::UdpSource::register(&mut components)?; + sources::SerialSource::register(&mut components)?; + sources::ProcessSource::register(&mut components)?; + } + let session = Self { tx_api }; + let handle = task::spawn(async move { + while let Some(msg) = rx_api.recv().await { + match msg { + API::GetOptions { + parser, + source, + origin, + tx, + } => { + let options = match components.get_options(origin, source, parser) { + Ok(options) => options, + Err(err) => { + log_if_err(tx.send(Err(err))); + continue; + } + }; + } + API::Shutdown(tx) => { + // Close pending + let _ = tx.send(()); + } + } + } + debug!("Session is started"); + // session work + debug!("Session task is finished"); + }); + Ok((session, rx_callback_events)) + } + + pub async fn get_options( + &self, + parser: Uuid, + source: Uuid, + origin: stypes::SourceOrigin, + ) -> Result<(Vec, Vec), stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(API::GetOptions { + parser, + source, + origin, + tx, + }), + "Fail to send API::GetOptions", + )?; + response(rx.await, "Fail to get response from API::GetOptions")? + } + + pub async fn shutdown(&self) -> Result<(), stypes::NativeError> { + let (tx, rx): (oneshot::Sender<()>, oneshot::Receiver<()>) = oneshot::channel(); + send( + self.tx_api.send(API::Shutdown(tx)), + "Fail to send API::Shutdown", + )?; + response(rx.await, "Fail to get response from API::Shutdown") + } +} + +fn log_if_err(res: Result<(), E>) { + if res.is_err() { + error!("[Components] Fail to send responce to API"); + } +} +fn send>( + res: Result<(), SendError>, + msg: S, +) -> Result<(), stypes::NativeError> { + res.map_err(|_| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::ChannelError, + message: Some(msg.as_ref().to_string()), + }) +} + +fn response>(res: Result, msg: S) -> Result { + res.map_err(|e| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::ChannelError, + message: Some(format!("{}: {e:?}", msg.as_ref())), + }) +} diff --git a/application/apps/indexer/session/src/handlers/mod.rs b/application/apps/indexer/session/src/handlers/mod.rs index f0301268e7..c5cfe1b525 100644 --- a/application/apps/indexer/session/src/handlers/mod.rs +++ b/application/apps/indexer/session/src/handlers/mod.rs @@ -2,6 +2,7 @@ pub mod export_raw; pub mod extract; pub mod observe; mod observing; +pub mod options; pub mod search; pub mod search_values; pub mod sleep; diff --git a/application/apps/indexer/session/src/handlers/options.rs b/application/apps/indexer/session/src/handlers/options.rs new file mode 100644 index 0000000000..ddd226100c --- /dev/null +++ b/application/apps/indexer/session/src/handlers/options.rs @@ -0,0 +1,65 @@ +use std::{collections::HashMap, future::Future, pin::Pin, time::Duration}; + +use crate::operations::{OperationAPI, OperationResult}; +use log::error; +use parsers::{dlt::DltParser, someip::SomeipParser, text::StringTokenizer}; +use sources::{ + binary::{ + pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, + raw::BinaryByteSource, + }, + command::process::ProcessSource, + serial::serialport::SerialSource, + socket::{tcp::TcpSource, udp::UdpSource}, +}; +use tokio::time::sleep; +use tokio_util::sync::CancellationToken; +use uuid::Uuid; + +type StaticFieldsResult = Result; + +type StaticFieldsGetter = fn(&stypes::SourceOrigin, &stypes::Ident) -> StaticFieldsResult; + +type LazyFieldsGetter = fn( + &stypes::SourceOrigin, + &stypes::Ident, + CancellationToken, +) -> Pin + Send>>; + +enum OptionEntity { + Static(stypes::StaticFieldDesc), + Lazy(stypes::LazyFieldDesc, LazyFieldsGetter), +} + +#[derive(Default)] +pub struct OptionsManager { + map: HashMap, Option)>, +} + +impl OptionsManager { + pub fn add( + &mut self, + ident: Uuid, + static_fields_handle: Option, + lazy_fields_handle: Option, + ) { + self.map + .insert(ident, (static_fields_handle, lazy_fields_handle)); + } + pub fn get_parser_options( + source_ident: stypes::SourceOrigin, + parser_ident: stypes::Ident, + ) -> Result, stypes::NativeError> { + Ok(Vec::new()) + } +} + +pub async fn get( + operation_api: OperationAPI, + source_ident: stypes::SourceOrigin, + parser_ident: stypes::Ident, +) -> OperationResult<()> { + let mut manager = OptionsManager::default(); + + Ok(None) +} diff --git a/application/apps/indexer/session/src/lib.rs b/application/apps/indexer/session/src/lib.rs index 81f6bb982d..4080942be5 100644 --- a/application/apps/indexer/session/src/lib.rs +++ b/application/apps/indexer/session/src/lib.rs @@ -1,3 +1,4 @@ +pub mod components; mod handlers; pub mod operations; pub mod paths; diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 7433c73c7a..574b52628e 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -26,6 +26,7 @@ lazy_static.workspace = true shellexpand = "3.1" stypes = { path = "../stypes", features=["rustcore"] } socket2 = "0.5.8" +components = { path = "../components" } [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 54264fbe8a..48e7236759 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -155,6 +155,23 @@ impl ByteSource for PcapLegacyByteSource { } } +const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +]); + +impl components::Component for PcapLegacyByteSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("PCAP Source"), + uuid: PCAP_SOURCE_UUID, + } + } + + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { @@ -189,7 +206,7 @@ mod tests { 0xff, 0xff, 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x00, 0x09, 0x04, 0x00, 0xac, 0x16, 0x0c, 0x4f, 0x00, 0x11, 0x75, 0x30, + 0x00, 0x0c, 0x00, 0x10, 0x04, 0x00, 0xac, 0x16, 0x0c, 0x4f, 0x00, 0x11, 0x75, 0x30, ]; let udp_payload = &SAMPLE_PCAP_DATA[82..=137]; @@ -219,7 +236,7 @@ mod tests { 0x81, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20, 0x00, 0x7b, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x00, 0x09, + 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x06, 0x75, 0x30, 0x52, 0x90, 0x5a, 0x64, 0x08, 0xda, 0x0e, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5e, 0x40, 0xff, 0xfb, 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0x08, 0x00, 0x45, 0x00, @@ -228,7 +245,7 @@ mod tests { 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x10, 0x00, 0x7b, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x52, 0x90, + 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x52, 0x90, ]; let pcap_file = std::io::Cursor::new(SAMPLE_PCAP_DATA); diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 2e7c6a0733..0a9d3a9707 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -159,6 +159,23 @@ impl ByteSource for PcapngByteSource { } } +const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +]); + +impl components::Component for PcapngByteSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("PCAP NG Source"), + uuid: PCAPNG_SOURCE_UUID, + } + } + + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use crate::{ @@ -195,7 +212,7 @@ mod tests { 0x62, 0x00, 0x00, 0x00, /* orig. packet len */ // start of ethernet packet ------------------- /*82*/ - 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0xb8, 0x27, 0xeb, 0x98, 0x94, 0xfa, 0x08, 0x00, 0x45, + 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0xb8, 0x27, 0xeb, 0x98, 0x94, 0xfa, 0x09, 0x00, 0x45, 0x00, 0x00, 0x54, 0xa0, 0x48, 0x40, 0x00, 0x40, 0x11, 0x29, 0x85, 0xac, 0x16, 0x0c, 0x4f, 0xac, 0x16, 0x0c, 0x50, // start of udp frame ------------------------- 0xc3, 0x50, 0xc3, 0x50, 0x00, 0x40, 0x8e, 0xe3, diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 38c20a4e5d..e059bb1b17 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -76,6 +76,23 @@ impl ByteSource for BinaryByteSource { } } +const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +]); + +impl components::Component for BinaryByteSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Binary Source"), + uuid: BIN_SOURCE_UUID, + } + } + + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use crate::{ diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 1fadf6af89..c5da56f235 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -226,6 +226,22 @@ impl ByteSource for ProcessSource { } } +const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +]); + +impl components::Component for ProcessSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Terminal Source"), + uuid: TERM_SOURCE_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 9242003d1e..3317b802a3 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -14,6 +14,7 @@ mod tests; pub mod binary; pub mod command; +pub mod prelude; pub mod producer; pub mod sde; pub mod serial; @@ -99,7 +100,7 @@ pub(crate) const DEFAULT_MIN_BUFFER_SPACE: usize = 10 * 1024; /// want to extract the data part from certain frames, the `relaod` method will load only the relevant /// data into an internal buffer. /// This data can then be accessed via the `current_slice` method. -pub trait ByteSource: Send { +pub trait ByteSource: Send + components::Component { /// Indicate that we have consumed a certain amount of data from our internal /// buffer and that this part can be discarded fn consume(&mut self, offset: usize); diff --git a/application/apps/indexer/sources/src/prelude.rs b/application/apps/indexer/sources/src/prelude.rs new file mode 100644 index 0000000000..4c02944e7c --- /dev/null +++ b/application/apps/indexer/sources/src/prelude.rs @@ -0,0 +1,9 @@ +pub use crate::{ + binary::{ + pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, + raw::BinaryByteSource, + }, + command::process::ProcessSource, + serial::serialport::SerialSource, + socket::{tcp::TcpSource, udp::UdpSource}, +}; diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index 68c79aca4f..cfa0d429fc 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -216,6 +216,22 @@ impl ByteSource for SerialSource { } } +const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, +]); + +impl components::Component for SerialSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("Serial Source"), + uuid: SERIAL_SOURCE_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(target_os = "windows")] unsafe impl Send for SerialSource {} diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 6f9e7d849e..e65b666ff5 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -168,6 +168,22 @@ impl ByteSource for TcpSource { } } +const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, +]); + +impl components::Component for TcpSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("TCP Source"), + uuid: TCP_SOURCE_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 36394949b2..5ded1ed8be 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -130,6 +130,22 @@ impl ByteSource for UdpSource { } } +const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, +]); + +impl components::Component for UdpSource { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("UDP Source"), + uuid: UDP_SOURCE_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + #[cfg(test)] mod tests { use std::time::Duration; diff --git a/application/apps/indexer/stypes/Cargo.toml b/application/apps/indexer/stypes/Cargo.toml index e85dcfc1f4..7f761781ff 100644 --- a/application/apps/indexer/stypes/Cargo.toml +++ b/application/apps/indexer/stypes/Cargo.toml @@ -12,9 +12,10 @@ rustcore = [ "dep:regex", "dep:envvars", "dlt-core/fibex", + "dep:anyhow", + "dep:tokio-util", "dlt-core/statistics", "dlt-core/serialization", - "dep:anyhow", ] nodejs = [ "dep:node-bindgen" @@ -33,6 +34,7 @@ thiserror.workspace = true walkdir = { workspace = true, optional = true } envvars = { workspace = true, optional = true } anyhow = { workspace = true, optional = true } +tokio-util = { workspace = true , features = ["full"], optional = true } [dev-dependencies] tokio = { workspace = true } diff --git a/application/apps/indexer/stypes/src/lib.rs b/application/apps/indexer/stypes/src/lib.rs index eec6a7fd06..94fa905f85 100644 --- a/application/apps/indexer/stypes/src/lib.rs +++ b/application/apps/indexer/stypes/src/lib.rs @@ -100,6 +100,7 @@ mod lf_transition; mod miscellaneous; mod observe; mod operations; +mod options; mod plugins; mod progress; @@ -111,6 +112,7 @@ pub use lf_transition::*; pub use miscellaneous::*; pub use observe::*; pub use operations::*; +pub use options::*; pub use plugins::*; pub use progress::*; diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 3e1d7b93af..799f841701 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -11,6 +11,34 @@ pub use extending::*; use crate::*; use dlt_core::filtering::DltFilterConfig; +#[derive(Clone, Serialize, Deserialize, Debug)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub enum SourceOrigin { + File(PathBuf), + Files(Vec), + Folder(PathBuf), + Folders(Vec), + /// `String` - stream identificator + Stream(Ident), +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub struct Ident { + pub name: String, + pub uuid: Uuid, +} + /// Multicast configuration information. /// - `multiaddr`: A valid multicast address. /// - `interface`: The address of the local interface used to join the multicast group. diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs new file mode 100644 index 0000000000..3c914beb89 --- /dev/null +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -0,0 +1,139 @@ +use crate::*; +// TODOs and open topics +// ## Validation of fields? +// Probably parser/source traites has to be update to have validation method +// +// ## Settings actions (like DLT stats)? +// Problematic places: +// - dlt stat +// - envvars (terminal command) + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum FieldDesc { + Static(StaticFieldDesc), + Lazy(LazyFieldDesc), +} +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct StaticFieldDesc { + pub id: String, + pub name: String, + pub desc: String, + pub required: bool, + pub default: Option, + pub interface: ValueInterface, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct LazyFieldDesc { + pub id: String, + pub name: String, + pub desc: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct Field { + pub id: String, + pub value: Value, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum Value { + // Bool value field + Boolean(bool), + // Integer field + Integer(i32), + // Float number field + Float(f32), + // Text field + Text(String), + // List with value + List(Vec), + // List of folders + Directories(Vec), + // List of files + Files(Vec), + // Path to file + File(PathBuf), + // Path to directory + Directory(PathBuf), + // Keys with multiple values per key + KeyValues(HashMap>), +} + +/// How value will be represented to user +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum ValueInterface { + // Bool value field + Boolean, + // Integer field + Integer, + // Float number field + Float, + // Text field + Text, + // Dropdown list with known values + DropList(Vec), + // List with known values + List(Vec), + // Request to get list of folders + RequestDirectories, + // Request to get list of files + RequestFiles, + // Request to get folder's path + RequestDirectory, + // Request to get a file's path + RequestFile, +} + +/// Represents events sent to the client. +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum CallbackOptionsEvent { + /// Triggered when lazy field is ready + Options(Vec), + + /// Triggered when a lazy fields wasn't loaded. + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "[string, string][]"))] + Errors(Vec<(String, String)>), +} From 9eb1b62dfb920dc0ce9da14e427de22a8f2fee0d Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 26 Mar 2025 15:44:43 +0100 Subject: [PATCH 002/121] Introduce sources and parsers as components --- .../apps/indexer/components/src/lib.rs | 143 +++++++++- .../apps/indexer/components/src/tys.rs | 1 - application/apps/indexer/parsers/Cargo.toml | 1 - .../apps/indexer/parsers/src/dlt/mod.rs | 11 +- application/apps/indexer/parsers/src/lib.rs | 1 - .../apps/indexer/parsers/src/someip.rs | 3 +- application/apps/indexer/parsers/src/text.rs | 3 +- .../plugins_host/src/parser_shared/mod.rs | 3 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 3 +- .../indexer/session/src/components/api.rs | 19 +- .../indexer/session/src/components/mod.rs | 158 +++++++++-- .../apps/indexer/session/src/handlers/mod.rs | 1 - .../indexer/session/src/handlers/options.rs | 65 ----- .../indexer/sources/src/binary/pcap/legacy.rs | 3 +- .../indexer/sources/src/binary/pcap/ng.rs | 3 +- .../apps/indexer/sources/src/binary/raw.rs | 3 +- .../indexer/sources/src/command/process.rs | 3 +- .../indexer/sources/src/serial/serialport.rs | 3 +- .../apps/indexer/sources/src/socket/tcp.rs | 3 +- .../apps/indexer/sources/src/socket/udp.rs | 3 +- .../apps/indexer/stypes/bindings/observe.ts | 22 +- .../apps/indexer/stypes/bindings/options.ts | 53 ++++ .../indexer/stypes/src/observe/converting.rs | 12 + .../apps/indexer/stypes/src/observe/mod.rs | 12 + .../apps/indexer/stypes/src/observe/nodejs.rs | 3 + .../apps/indexer/stypes/src/options/mod.rs | 17 ++ .../apps/indexer/stypes/src/options/nodejs.rs | 9 + application/apps/protocol/src/lib.rs | 6 + .../apps/rustcore/rs-bindings/Cargo.lock | 19 ++ .../rs-bindings/src/js/components/mod.rs | 139 ++++++++++ .../apps/rustcore/rs-bindings/src/js/mod.rs | 1 + .../src/api/components.provider.ts | 80 ++++++ .../ts-bindings/src/api/components.ts | 74 ++++++ .../ts-bindings/src/interfaces/errors.ts | 2 + .../src/native/native.components.ts | 249 ++++++++++++++++++ .../rustcore/ts-bindings/src/native/native.ts | 1 + application/holder/src/loaders/services.ts | 1 + application/holder/src/register/services.ts | 4 + application/holder/src/service/components.ts | 15 ++ application/platform/types/bindings/index.ts | 1 + .../platform/types/bindings/observe.ts | 232 +++++++++------- .../platform/types/bindings/options.ts | 53 ++++ 42 files changed, 1208 insertions(+), 230 deletions(-) delete mode 100644 application/apps/indexer/session/src/handlers/options.rs create mode 100644 application/apps/indexer/stypes/bindings/options.ts create mode 100644 application/apps/indexer/stypes/src/observe/converting.rs create mode 100644 application/apps/indexer/stypes/src/options/nodejs.rs create mode 100644 application/apps/rustcore/rs-bindings/src/js/components/mod.rs create mode 100644 application/apps/rustcore/ts-bindings/src/api/components.provider.ts create mode 100644 application/apps/rustcore/ts-bindings/src/api/components.ts create mode 100644 application/apps/rustcore/ts-bindings/src/native/native.components.ts create mode 100644 application/holder/src/service/components.ts create mode 100644 application/platform/types/bindings/options.ts diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 814be259df..0be3d6b06d 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -1,7 +1,6 @@ mod tys; -use log::error; -use tokio::{sync::oneshot, task}; +use tokio::{select, sync::oneshot}; use tokio_util::sync::CancellationToken; pub use tys::*; @@ -19,10 +18,29 @@ type Metadata<'a> = ( ), ); +#[derive(Debug)] +pub enum LazyLoadingResult { + Feilds(Vec), + Cancelled, +} + +#[derive(Debug, Clone)] +pub struct LazyLoadingTaskMeta { + pub uuid: Uuid, + pub ident: stypes::Ident, + pub cancel: CancellationToken, + pub fields: Vec, +} + +impl LazyLoadingTaskMeta { + pub fn contains(&self, fields: &[String]) -> bool { + self.fields.iter().any(|id| fields.contains(id)) + } +} + pub struct LazyLoadingTask { - ident: stypes::Ident, - rx: oneshot::Receiver, stypes::NativeError>>, - cancel: CancellationToken, + meta: LazyLoadingTaskMeta, + rx: Option, stypes::NativeError>>>, } impl LazyLoadingTask { @@ -30,11 +48,50 @@ impl LazyLoadingTask { ident: stypes::Ident, rx: oneshot::Receiver, stypes::NativeError>>, cancel: &CancellationToken, + fields: Vec, ) -> Self { Self { - ident, - rx, - cancel: cancel.clone(), + meta: LazyLoadingTaskMeta { + uuid: Uuid::new_v4(), + ident, + cancel: cancel.clone(), + fields, + }, + rx: Some(rx), + } + } + + pub fn get_meta(&self) -> LazyLoadingTaskMeta { + self.meta.clone() + } + + pub async fn wait(&mut self) -> Result { + let Some(rx) = self.rx.take() else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Settings already requested for {}", + self.meta.ident.name + )), + }); + }; + // TODO: something about cancel safe + select! { + _ = self.meta.cancel.cancelled() => { + Ok(LazyLoadingResult::Cancelled) + } + fields = rx => { + let fields = fields.map_err(|_| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::ChannelError, + message: Some(format!( + "Fail to get settings for {}", + self.meta.ident.name + )), + })??; + Ok(LazyLoadingResult::Feilds(fields)) + } } } } @@ -55,6 +112,32 @@ impl Options { lazy_parser: None, } } + pub fn set_source_lazy_task( + &mut self, + ident: stypes::Ident, + rx: oneshot::Receiver, stypes::NativeError>>, + cancel: &CancellationToken, + ) { + self.lazy_source = Some(LazyLoadingTask::new( + ident, + rx, + cancel, + self.source_lazy_uuids(), + )); + } + pub fn set_parser_lazy_task( + &mut self, + ident: stypes::Ident, + rx: oneshot::Receiver, stypes::NativeError>>, + cancel: &CancellationToken, + ) { + self.lazy_parser = Some(LazyLoadingTask::new( + ident, + rx, + cancel, + self.parser_lazy_uuids(), + )); + } pub fn source_has_lazy(&self) -> bool { self.source .iter() @@ -65,6 +148,30 @@ impl Options { .iter() .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) } + pub fn source_lazy_uuids(&self) -> Vec { + self.source + .iter() + .filter_map(|f| { + if let stypes::FieldDesc::Lazy(f) = f { + Some(f.id.to_owned()) + } else { + None + } + }) + .collect() + } + pub fn parser_lazy_uuids(&self) -> Vec { + self.parser + .iter() + .filter_map(|f| { + if let stypes::FieldDesc::Lazy(f) = f { + Some(f.id.to_owned()) + } else { + None + } + }) + .collect() + } } #[derive(Default)] @@ -118,6 +225,22 @@ impl Components { Ok(()) } + pub fn get_sources( + &self, + _source_origin: stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + // TODO: "ask" source based on origin + Ok(self.sources.values().cloned().collect()) + } + + pub fn get_parsers( + &self, + _source_origin: stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + // TODO: "ask" parser based on origin + Ok(self.parsers.values().cloned().collect()) + } + pub fn get_options( &self, source_origin: stypes::SourceOrigin, @@ -140,13 +263,13 @@ impl Components { let (tx, rx) = oneshot::channel(); let tk = CancellationToken::new(); getter(&source_origin, tx, &tk)?; - options.lazy_source = Some(LazyLoadingTask::new(source_ident.clone(), rx, &tk)); + options.set_source_lazy_task(source_ident.clone(), rx, &tk); } if let (Some(getter), true) = (parser_lazy, options.source_has_lazy()) { let (tx, rx) = oneshot::channel(); let tk = CancellationToken::new(); getter(&source_origin, tx, &tk)?; - options.lazy_parser = Some(LazyLoadingTask::new(parser_ident.clone(), rx, &tk)); + options.set_parser_lazy_task(parser_ident.clone(), rx, &tk); } Ok(options) } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 3b9ec2d463..65c7774c7f 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -1,5 +1,4 @@ use crate::*; -use std::{future::Future, pin::Pin}; use stypes::NativeError; use tokio_util::sync::CancellationToken; diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 85f612bfce..be66d6fe2b 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -21,7 +21,6 @@ someip-tools = { path = "../addons/someip-tools" } stypes = { path = "../stypes", features=["rustcore"] } uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } -tokio-util.workspace = true [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 7a0279f8f4..859edbedf4 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -215,14 +215,15 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ]); -impl<'a> components::Component for DltParser<'a> { +impl components::Component for DltParser<'_> { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("DLT Parser"), + desc: String::from("DLT Parser"), uuid: DLT_PARSER_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } @@ -248,10 +249,11 @@ impl components::Component for DltRangeParser { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("DLT Range Parser"), + desc: String::from("DLT Range Parser"), uuid: DLT_RANGE_PARSER_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } @@ -276,10 +278,11 @@ impl components::Component for DltRawParser { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("DLT Raw Parser"), + desc: String::from("DLT Raw Parser"), uuid: DLT_RAW_PARSER_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index d4c20c73f2..5e49429911 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -7,7 +7,6 @@ pub mod text; use serde::Serialize; use std::{fmt::Display, future::Future, io::Write, iter}; use thiserror::Error; -use tokio_util::sync::CancellationToken; extern crate log; diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index d48eb5b612..a806b5397e 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -323,10 +323,11 @@ impl components::Component for SomeipParser { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("SomeIP Parser"), + desc: String::from("SomeIP Parser"), uuid: SOMEIP_PARSER_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text.rs index 1a900c0f07..480c97a801 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text.rs @@ -79,10 +79,11 @@ impl components::Component for StringTokenizer { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Text Parser"), + desc: String::from("Text Parser"), uuid: TEXT_PARSER_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 0634dbdfaa..48c123e54d 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -186,11 +186,12 @@ impl components::Component for PluginsParser { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Plugin"), + desc: String::from("Plugin"), uuid: uuid::Uuid::new_v4(), } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 279f6ad3dd..d006a4de70 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -175,11 +175,12 @@ impl components::Component for PluginParser { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Plugin Parser"), + desc: String::from("Plugin Parser"), uuid: uuid::Uuid::new_v4(), } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index d2ef631d7b..766fc85662 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -1,14 +1,25 @@ -use stypes::NativeError; +use components::LazyLoadingResult; +use stypes::{Ident, NativeError, SourceOrigin}; use tokio::sync::oneshot; use uuid::Uuid; #[derive(Debug)] -pub enum API { +pub enum Api { Shutdown(oneshot::Sender<()>), + LazyTaskComplite(Uuid, Result), + CancelLoading(Vec), GetOptions { parser: Uuid, source: Uuid, - origin: stypes::SourceOrigin, - tx: oneshot::Sender, Vec), NativeError>>, + origin: SourceOrigin, + tx: oneshot::Sender>, }, + GetSources( + SourceOrigin, + oneshot::Sender, NativeError>>, + ), + GetParsers( + SourceOrigin, + oneshot::Sender, NativeError>>, + ), } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index ead496f1c5..ee0e5aa59f 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -1,9 +1,11 @@ mod api; +use std::collections::HashMap; + use api::*; -use components::{Component, Components}; -use log::{debug, error, warn}; +use components::{Component, Components, LazyLoadingResult, LazyLoadingTaskMeta}; +use log::{debug, error}; use parsers::prelude as parsers; use sources::prelude as sources; use tokio::{ @@ -11,20 +13,19 @@ use tokio::{ mpsc::{error::SendError, unbounded_channel, UnboundedReceiver, UnboundedSender}, oneshot::{self, error::RecvError}, }, - task::{self}, + task::{self, JoinHandle}, }; -use tokio_util::sync::CancellationToken; use uuid::Uuid; pub struct ComponentsSession { - tx_api: UnboundedSender, + tx_api: UnboundedSender, } impl ComponentsSession { /// Starts a global components session pub async fn new( ) -> Result<(Self, UnboundedReceiver), stypes::NativeError> { - let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = + let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = unbounded_channel(); let (tx_callback_events, rx_callback_events): ( UnboundedSender, @@ -45,11 +46,14 @@ impl ComponentsSession { sources::SerialSource::register(&mut components)?; sources::ProcessSource::register(&mut components)?; } + let tx_api_inner = tx_api.clone(); let session = Self { tx_api }; - let handle = task::spawn(async move { + task::spawn(async move { + debug!("Session is started"); + let mut tasks: HashMap)> = HashMap::new(); while let Some(msg) = rx_api.recv().await { match msg { - API::GetOptions { + Api::GetOptions { parser, source, origin, @@ -62,15 +66,94 @@ impl ComponentsSession { continue; } }; + // Send static fields + log_if_err(tx.send(Ok(stypes::ComponentsOptions { + source: options.source, + parser: options.parser, + }))); + if let Some(mut source_loading_task) = options.lazy_source { + // If exists, request lazy source fields + let meta = source_loading_task.get_meta(); + let uuid = meta.uuid; + let tx_api = tx_api_inner.clone(); + tasks.insert( + uuid, + ( + meta, + task::spawn(async move { + log_if_err(tx_api.send(Api::LazyTaskComplite( + uuid, + source_loading_task.wait().await, + ))); + }), + ), + ); + } + if let Some(mut parser_loading_task) = options.lazy_parser { + // If exists, request lazy parser fields + let meta = parser_loading_task.get_meta(); + let uuid = meta.uuid; + let tx_api = tx_api_inner.clone(); + tasks.insert( + uuid, + ( + meta, + task::spawn(async move { + log_if_err(tx_api.send(Api::LazyTaskComplite( + uuid, + parser_loading_task.wait().await, + ))); + }), + ), + ); + } + } + // Delivery lazy fields to client. + Api::LazyTaskComplite(uuid, results) => { + tasks.remove(&uuid); + match results { + Ok(LazyLoadingResult::Feilds(fields)) => { + log_if_err( + tx_callback_events + .send(stypes::CallbackOptionsEvent::Options(fields)), + ); + } + Ok(..) => { + // Task has been cancelled + continue; + } + Err(err) => { + // TODO: Error should be delivered to client with bound field UUID + error!("Fail to load lazy field with: {err:?}"); + } + } } - API::Shutdown(tx) => { - // Close pending - let _ = tx.send(()); + Api::GetParsers(origin, tx) => { + log_if_err(tx.send(components.get_parsers(origin))); + } + Api::GetSources(origin, tx) => { + log_if_err(tx.send(components.get_sources(origin))); + } + // Client doesn't need any more field data. Loading task should be cancelled + Api::CancelLoading(fields) => { + for (_, (meta, _)) in tasks.iter() { + if meta.contains(&fields) { + meta.cancel.cancel(); + } + } + } + Api::Shutdown(tx) => { + // Cancel / kill pending tasks + tasks.iter().for_each(|(_, (meta, handle))| { + meta.cancel.cancel(); + // TODO: we should wait for tasks will be cancelled by it self before abort. + handle.abort(); + }); + tasks.clear(); + log_if_err(tx.send(())); } } } - debug!("Session is started"); - // session work debug!("Session task is finished"); }); Ok((session, rx_callback_events)) @@ -78,36 +161,67 @@ impl ComponentsSession { pub async fn get_options( &self, - parser: Uuid, source: Uuid, + parser: Uuid, origin: stypes::SourceOrigin, - ) -> Result<(Vec, Vec), stypes::NativeError> { + ) -> Result { let (tx, rx) = oneshot::channel(); send( - self.tx_api.send(API::GetOptions { + self.tx_api.send(Api::GetOptions { parser, source, origin, tx, }), - "Fail to send API::GetOptions", + "Fail to send Api::GetOptions", + )?; + response(rx.await, "Fail to get response from Api::GetOptions")? + } + + pub async fn get_sources( + &self, + origin: stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetSources(origin, tx)), + "Fail to send Api::GetSources", + )?; + response(rx.await, "Fail to get response from Api::GetSources")? + } + + pub async fn get_parsers( + &self, + origin: stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetParsers(origin, tx)), + "Fail to send Api::GetParsers", )?; - response(rx.await, "Fail to get response from API::GetOptions")? + response(rx.await, "Fail to get response from Api::GetParsers")? + } + + pub fn abort(&self, fields: Vec) -> Result<(), stypes::NativeError> { + send( + self.tx_api.send(Api::CancelLoading(fields)), + "Fail to send Api::CancelLoading", + ) } pub async fn shutdown(&self) -> Result<(), stypes::NativeError> { let (tx, rx): (oneshot::Sender<()>, oneshot::Receiver<()>) = oneshot::channel(); send( - self.tx_api.send(API::Shutdown(tx)), - "Fail to send API::Shutdown", + self.tx_api.send(Api::Shutdown(tx)), + "Fail to send Api::Shutdown", )?; - response(rx.await, "Fail to get response from API::Shutdown") + response(rx.await, "Fail to get response from Api::Shutdown") } } fn log_if_err(res: Result<(), E>) { if res.is_err() { - error!("[Components] Fail to send responce to API"); + error!("[Components] Fail to send responce to Api"); } } fn send>( diff --git a/application/apps/indexer/session/src/handlers/mod.rs b/application/apps/indexer/session/src/handlers/mod.rs index c5cfe1b525..f0301268e7 100644 --- a/application/apps/indexer/session/src/handlers/mod.rs +++ b/application/apps/indexer/session/src/handlers/mod.rs @@ -2,7 +2,6 @@ pub mod export_raw; pub mod extract; pub mod observe; mod observing; -pub mod options; pub mod search; pub mod search_values; pub mod sleep; diff --git a/application/apps/indexer/session/src/handlers/options.rs b/application/apps/indexer/session/src/handlers/options.rs deleted file mode 100644 index ddd226100c..0000000000 --- a/application/apps/indexer/session/src/handlers/options.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::{collections::HashMap, future::Future, pin::Pin, time::Duration}; - -use crate::operations::{OperationAPI, OperationResult}; -use log::error; -use parsers::{dlt::DltParser, someip::SomeipParser, text::StringTokenizer}; -use sources::{ - binary::{ - pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, - raw::BinaryByteSource, - }, - command::process::ProcessSource, - serial::serialport::SerialSource, - socket::{tcp::TcpSource, udp::UdpSource}, -}; -use tokio::time::sleep; -use tokio_util::sync::CancellationToken; -use uuid::Uuid; - -type StaticFieldsResult = Result; - -type StaticFieldsGetter = fn(&stypes::SourceOrigin, &stypes::Ident) -> StaticFieldsResult; - -type LazyFieldsGetter = fn( - &stypes::SourceOrigin, - &stypes::Ident, - CancellationToken, -) -> Pin + Send>>; - -enum OptionEntity { - Static(stypes::StaticFieldDesc), - Lazy(stypes::LazyFieldDesc, LazyFieldsGetter), -} - -#[derive(Default)] -pub struct OptionsManager { - map: HashMap, Option)>, -} - -impl OptionsManager { - pub fn add( - &mut self, - ident: Uuid, - static_fields_handle: Option, - lazy_fields_handle: Option, - ) { - self.map - .insert(ident, (static_fields_handle, lazy_fields_handle)); - } - pub fn get_parser_options( - source_ident: stypes::SourceOrigin, - parser_ident: stypes::Ident, - ) -> Result, stypes::NativeError> { - Ok(Vec::new()) - } -} - -pub async fn get( - operation_api: OperationAPI, - source_ident: stypes::SourceOrigin, - parser_ident: stypes::Ident, -) -> OperationResult<()> { - let mut manager = OptionsManager::default(); - - Ok(None) -} diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 48e7236759..be8d070942 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -163,11 +163,12 @@ impl components::Component for PcapLegacyByteSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("PCAP Source"), + desc: String::from("PCAP Source"), uuid: PCAP_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 0a9d3a9707..669d0f31e4 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -167,11 +167,12 @@ impl components::Component for PcapngByteSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("PCAP NG Source"), + desc: String::from("PCAP NG Source"), uuid: PCAPNG_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index e059bb1b17..e079f50ba3 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -84,11 +84,12 @@ impl components::Component for BinaryByteSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Binary Source"), + desc: String::from("Binary Source"), uuid: BIN_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index c5da56f235..75223a033f 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -234,10 +234,11 @@ impl components::Component for ProcessSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Terminal Source"), + desc: String::from("Terminal Source"), uuid: TERM_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index cfa0d429fc..2012ac8cf6 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -224,10 +224,11 @@ impl components::Component for SerialSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("Serial Source"), + desc: String::from("Serial Source"), uuid: SERIAL_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index e65b666ff5..ce0cde3f8c 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -176,10 +176,11 @@ impl components::Component for TcpSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("TCP Source"), + desc: String::from("TCP Source"), uuid: TCP_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 5ded1ed8be..4ceab9b13a 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -138,10 +138,11 @@ impl components::Component for UdpSource { fn ident() -> stypes::Ident { stypes::Ident { name: String::from("UDP Source"), + desc: String::from("UDP Source"), uuid: UDP_SOURCE_UUID, } } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 5af7a3d3f1..92fb270ea8 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,12 +1,5 @@ -/** - * ATTENTION: - * REFERENCE TO `DltFilterConfig` HAS BEEN ADDED MANUALLY - * BECAUSE THIS TYPE IS NOT GENERATED BY `ts_rs`. - */ -import { DltFilterConfig } from './dlt'; -import type { PluginParserSettings } from './plugins'; - // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginParserSettings } from './plugins'; /** * Settings for the DLT parser. @@ -33,7 +26,11 @@ export type DltParserSettings = { /** * Supported file formats for observation. */ -export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; +export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary' | 'ParserPlugin'; + +export type Ident = { name: string; desc: string; uuid: string }; + +export type IdentList = Array; /** * Multicast configuration information. @@ -140,6 +137,13 @@ export type SomeIpParserSettings = { fibex_file_paths: Array | null; }; +export type SourceOrigin = + | { File: string } + | { Files: Array } + | { Folder: string } + | { Folders: Array } + | { Stream: Ident }; + /** * Configuration for TCP connections. */ diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts new file mode 100644 index 0000000000..0287da9d22 --- /dev/null +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -0,0 +1,53 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Represents events sent to the client. + */ +export type CallbackOptionsEvent = + | { Options: Array } + | { Errors: [string, string][] } + | 'Destroyed'; + +export type ComponentsOptions = { source: Array; parser: Array }; + +export type Field = { id: string; value: Value }; + +export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; + +export type LazyFieldDesc = { id: string; name: string; desc: string }; + +export type StaticFieldDesc = { + id: string; + name: string; + desc: string; + required: boolean; + default: Value | null; + interface: ValueInterface; +}; + +export type Value = + | { Boolean: boolean } + | { Integer: number } + | { Float: number } + | { Text: string } + | { List: Array } + | { Directories: Array } + | { Files: Array } + | { File: string } + | { Directory: string } + | { KeyValues: { [key in string]?: Array } }; + +/** + * How value will be represented to user + */ +export type ValueInterface = + | 'Boolean' + | 'Integer' + | 'Float' + | 'Text' + | { DropList: Array } + | { List: Array } + | 'RequestDirectories' + | 'RequestFiles' + | 'RequestDirectory' + | 'RequestFile'; diff --git a/application/apps/indexer/stypes/src/observe/converting.rs b/application/apps/indexer/stypes/src/observe/converting.rs new file mode 100644 index 0000000000..e2e8a38b29 --- /dev/null +++ b/application/apps/indexer/stypes/src/observe/converting.rs @@ -0,0 +1,12 @@ +use crate::*; + +/// Converts a `Vec` into an `AttachmentList`. +/// +/// This implementation allows you to create an `IdentList` directly from a vector of +/// `Ident` objects. It simplifies the conversion and ensures compatibility +/// between these types. +impl From> for IdentList { + fn from(value: Vec) -> Self { + Self(value) + } +} diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 799f841701..b685408ef9 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -1,4 +1,6 @@ #[cfg(feature = "rustcore")] +mod converting; +#[cfg(feature = "rustcore")] mod extending; #[cfg(feature = "nodejs")] mod nodejs; @@ -27,6 +29,15 @@ pub enum SourceOrigin { Stream(Ident), } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub struct IdentList(pub Vec); + #[derive(Clone, Serialize, Deserialize, Debug)] #[extend::encode_decode] #[cfg_attr( @@ -36,6 +47,7 @@ pub enum SourceOrigin { )] pub struct Ident { pub name: String, + pub desc: String, pub uuid: Uuid, } diff --git a/application/apps/indexer/stypes/src/observe/nodejs.rs b/application/apps/indexer/stypes/src/observe/nodejs.rs index b79f2bd9d4..afdb5ab534 100644 --- a/application/apps/indexer/stypes/src/observe/nodejs.rs +++ b/application/apps/indexer/stypes/src/observe/nodejs.rs @@ -12,3 +12,6 @@ try_into_js!(UDPTransportConfig); try_into_js!(FileFormat); try_into_js!(ObserveOrigin); try_into_js!(ObserveOptions); +try_into_js!(Ident); +try_into_js!(IdentList); +try_into_js!(SourceOrigin); diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 3c914beb89..9b64e58209 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "nodejs")] +mod nodejs; + use crate::*; // TODOs and open topics // ## Validation of fields? @@ -8,6 +11,18 @@ use crate::*; // - dlt stat // - envvars (terminal command) +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct ComponentsOptions { + pub source: Vec, + pub parser: Vec, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( @@ -136,4 +151,6 @@ pub enum CallbackOptionsEvent { /// Triggered when a lazy fields wasn't loaded. #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "[string, string][]"))] Errors(Vec<(String, String)>), + + Destroyed, } diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs new file mode 100644 index 0000000000..3cf65d9e65 --- /dev/null +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -0,0 +1,9 @@ +use crate::*; + +try_into_js!(FieldDesc); +try_into_js!(StaticFieldDesc); +try_into_js!(LazyFieldDesc); +try_into_js!(Value); +try_into_js!(ValueInterface); +try_into_js!(CallbackOptionsEvent); +try_into_js!(ComponentsOptions); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index ab7b93c794..d266d944c7 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -205,3 +205,9 @@ gen_encode_decode_fns!(Point); gen_encode_decode_fns!(ResultSearchValues); gen_encode_decode_fns!(ResultScaledDistribution); gen_encode_decode_fns!(DltLevelDistribution); +gen_encode_decode_fns!(SourceOrigin); +gen_encode_decode_fns!(IdentList); +gen_encode_decode_fns!(ComponentsOptions); +gen_encode_decode_fns!(FieldDesc); +gen_encode_decode_fns!(LazyFieldDesc); +gen_encode_decode_fns!(StaticFieldDesc); diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 9c2fd9c2f8..f1321de38b 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -558,6 +558,18 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +[[package]] +name = "components" +version = "0.1.0" +dependencies = [ + "log", + "stypes", + "thiserror 2.0.3", + "tokio", + "tokio-util", + "uuid", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2390,6 +2402,7 @@ dependencies = [ "byteorder", "chrono", "chrono-tz", + "components", "dlt-core", "lazy_static", "log", @@ -2401,6 +2414,8 @@ dependencies = [ "someip-payload", "someip-tools", "thiserror 2.0.12", + "stypes", + "uuid", ] [[package]] @@ -2505,6 +2520,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "toml", + "uuid", "wasmtime", "wasmtime-wasi", ] @@ -3023,6 +3039,7 @@ name = "session" version = "0.1.0" dependencies = [ "blake3", + "components", "crossbeam-channel", "dirs", "dlt-core", @@ -3168,6 +3185,7 @@ dependencies = [ "async-stream", "bufread", "bytes", + "components", "etherparse", "futures", "indexer_base", @@ -3226,6 +3244,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "tokio", + "tokio-util", "uuid", "walkdir", ] diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs new file mode 100644 index 0000000000..c58140c2ee --- /dev/null +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -0,0 +1,139 @@ +use log::{debug, error}; +use node_bindgen::{core::buffer::JSArrayBuffer, derive::node_bindgen}; +use session::components::ComponentsSession; +use std::{str::FromStr, thread}; +use tokio::{runtime::Runtime, sync::oneshot}; +use uuid::Uuid; + +struct Components { + session: Option, +} + +#[node_bindgen] +impl Components { + #[node_bindgen(constructor)] + pub fn new() -> Self { + Self { session: None } + } + + #[node_bindgen(mt)] + async fn init( + &mut self, + callback: F, + ) -> Result<(), stypes::ComputationError> { + let rt = Runtime::new().map_err(|e| { + stypes::ComputationError::Process(format!("Could not start tokio runtime: {e}")) + })?; + let (tx_session, rx_session) = oneshot::channel(); + thread::spawn(move || { + rt.block_on(async { + match ComponentsSession::new().await { + Ok((session, mut rx_callback_events)) => { + if tx_session.send(Some(session)).is_err() { + error!("Cannot setup session instance"); + return; + } + debug!("task is started"); + while let Some(event) = rx_callback_events.recv().await { + callback(event) + } + debug!("sending Destroyed event"); + callback(stypes::CallbackOptionsEvent::Destroyed); + debug!("task is finished"); + } + Err(e) => { + error!("Cannot create session instance: {e:?}"); + if tx_session.send(None).is_err() { + error!("Cannot setup session instance"); + } + } + } + }) + }); + self.session = rx_session.await.map_err(|_| { + stypes::ComputationError::Communication(String::from( + "Fail to get session instance to setup", + )) + })?; + Ok(()) + } + + #[node_bindgen] + async fn get_sources( + &self, + origin: JSArrayBuffer, + ) -> Result { + let origin = + stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_sources(origin) + .await + .map(|idents| idents.into()) + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn get_parsers( + &self, + origin: JSArrayBuffer, + ) -> Result { + let origin = + stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_parsers(origin) + .await + .map(|idents| idents.into()) + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn get_options( + &self, + source: String, + parser: String, + origin: JSArrayBuffer, + ) -> Result { + let source = Uuid::from_str(&source).map_err(|_| stypes::ComputationError::InvalidData)?; + let parser = Uuid::from_str(&parser).map_err(|_| stypes::ComputationError::InvalidData)?; + let origin = + stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_options(source, parser, origin) + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + fn abort(&self, fields: Vec) -> Result<(), stypes::ComputationError> { + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .abort(fields) + .map_err(stypes::ComputationError::NativeError)?; + Ok(()) + } + + #[node_bindgen] + async fn destroy(&self) -> Result<(), stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .shutdown() + .await + .map_err(stypes::ComputationError::NativeError) + } +} diff --git a/application/apps/rustcore/rs-bindings/src/js/mod.rs b/application/apps/rustcore/rs-bindings/src/js/mod.rs index 1be11b72fd..eb106a0e0b 100644 --- a/application/apps/rustcore/rs-bindings/src/js/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/mod.rs @@ -1,3 +1,4 @@ +pub mod components; pub mod converting; pub mod jobs; pub mod session; diff --git a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts new file mode 100644 index 0000000000..8b234caea0 --- /dev/null +++ b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts @@ -0,0 +1,80 @@ +import { Subject } from 'platform/env/subscription'; +import { Computation } from '../provider/provider'; +import { StaticFieldDesc } from 'platform/types/bindings'; + +import * as protocol from 'protocol'; + +export interface IComponentsEvents { + Options: Subject; + Errors: Subject<[string, string][]>; + Destroy: Subject; +} + +export interface IComponentsEventsConvertors {} + +export interface IComponentsEventsSignatures { + Options: 'Options'; + Errors: 'Errors'; + Destroy: 'Destroy'; +} + +const SessionEventsSignatures: IComponentsEventsSignatures = { + Options: 'Options', + Errors: 'Errors', + Destroy: 'Destroy', +}; + +export interface IComponentsEventsInterfaces { + Options: { self: ['object', null] }; + Errors: { self: [typeof Array, null] }; + Destroy: { self: null }; +} + +const SessionEventsInterfaces: IComponentsEventsInterfaces = { + Options: { self: ['object', null] }, + Errors: { self: [Array, null] }, + Destroy: { self: null }, +}; + +export class ComponentsEventProvider extends Computation< + IComponentsEvents, + IComponentsEventsSignatures, + IComponentsEventsInterfaces +> { + private readonly _events: IComponentsEvents = { + Options: new Subject(), + Errors: new Subject<[string, string][]>(), + Destroy: new Subject(), + }; + + private readonly _convertors: IComponentsEventsConvertors = {}; + + constructor(uuid: string) { + super(uuid, protocol.decodeCallbackEvent); + } + + public getName(): string { + return 'ComponentsEventProvider'; + } + + public getEvents(): IComponentsEvents { + return this._events; + } + + public getEventsSignatures(): IComponentsEventsSignatures { + return SessionEventsSignatures; + } + + public getEventsInterfaces(): IComponentsEventsInterfaces { + return SessionEventsInterfaces; + } + + public getConvertor(event: keyof IComponentsEventsSignatures, data: T): T | O | Error { + const convertors = this._convertors as unknown as { [key: string]: (data: T) => T | O }; + if (typeof convertors[event] !== 'function') { + return data; + } else { + return convertors[event](data); + } + } +} diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts new file mode 100644 index 0000000000..f132bcaa70 --- /dev/null +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -0,0 +1,74 @@ +import { Logger } from 'platform/log'; +import { scope } from 'platform/env/scope'; +import { v4 as uuid } from 'uuid'; +import { Base as Native } from '../native/native.components'; +import { ComponentsEventProvider } from '../api/components.provider'; +import { SessionStream } from '../api/session.stream'; +import { SessionSearch } from '../api/session.search'; +import { Subscriber } from 'platform/env/subscription'; +import { ComponentsOptions, Ident, SourceOrigin } from 'platform/types/bindings'; + +export { + ISessionEvents, + IProgressEvent, + IProgressState, + IEventMapUpdated, + IEventMatchesUpdated, + IEventIndexedMapUpdated, +} from '../api/session.provider'; +export { ComponentsEventProvider, SessionStream, SessionSearch }; + +export * as $ from 'platform/types/observe'; +export * as Factory from 'platform/types/observe/factory'; + +export class Components extends Subscriber { + private readonly native: Native; + private readonly provider: ComponentsEventProvider; + private readonly logger: Logger; + private readonly uuid: string = uuid(); + + constructor(resolver: (err: Error | undefined) => void) { + super(); + this.logger = scope.getLogger(`Components: ${this.uuid}`); + this.provider = new ComponentsEventProvider(this.uuid); + this.native = new Native(this.provider, (err: Error | undefined) => { + if (err instanceof Error) { + this.logger.error(`Fail to create a components session: ${err.message}`); + } + resolver(err); + }); + } + + public destroy(): Promise { + return this.native.destroy().catch((err: Error) => { + this.logger.error(`Fail to destroy session: ${err.message}`); + return Promise.reject(err); + }); + } + + public get(origin: SourceOrigin): { + sources(): Promise; + parsers(): Promise; + } { + return { + sources: (): Promise => { + return this.native.getSources(origin); + }, + parsers: (): Promise => { + return this.native.getParsers(origin); + }, + }; + } + + public getOptions( + source: string, + parser: string, + origin: SourceOrigin, + ): Promise { + return this.native.getOptions(source, parser, origin); + } + + public abort(fields: string[]): Error | undefined { + return this.native.abort(fields); + } +} diff --git a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts index 9d5dc0583d..225358c7cb 100644 --- a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts +++ b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts @@ -73,6 +73,8 @@ export enum Source { SetIndexingMode = 'SetIndexingMode', GetIndexedLen = 'GetIndexedLen', getAroundIndexes = 'getAroundIndexes', + ComponentsOptions = 'ComponentsOptions', + IdentList = 'IdentList', Native = 'Native', Other = 'Other', } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts new file mode 100644 index 0000000000..4e51a8951f --- /dev/null +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -0,0 +1,249 @@ +import { scope } from 'platform/env/scope'; +import { getNativeModule } from '../native/native'; +import { Type, Source, NativeError } from '../interfaces/errors'; +import { SourceOrigin, IdentList, ComponentsOptions } from 'platform/types/bindings'; +import { Logger, utils } from 'platform/log'; +import { TEventEmitter } from '../provider/provider.general'; +import { Computation } from '../provider/provider'; +import { Subscriber } from 'platform/env/subscription'; +import { + IComponentsEvents, + IComponentsEventsSignatures, + IComponentsEventsInterfaces, +} from '../api/components.provider'; + +import * as protocol from 'protocol'; + +export abstract class ComponentsNative { + public abstract init(callback: TEventEmitter): Promise; + + public abstract destroy(): Promise; + + public abstract getSources(origin: Uint8Array): Promise; + + public abstract getParsers(origin: Uint8Array): Promise; + + public abstract getOptions( + source: string, + parser: string, + origin: Uint8Array, + ): Promise; + + public abstract abort(fields: string[]): void; +} + +const DESTROY_TIMEOUT = 5000; + +enum State { + Destroyed, + Destroying, + Available, + Created, + Unavailable, +} + +type DestroyResolver = () => void; + +export class Base extends Subscriber { + protected readonly logger: Logger = scope.getLogger(`Components`); + protected readonly native: ComponentsNative; + protected readonly provider: Computation< + IComponentsEvents, + IComponentsEventsSignatures, + IComponentsEventsInterfaces + >; + private state: State = State.Created; + private destroyResolver: DestroyResolver | undefined; + + constructor( + provider: Computation< + IComponentsEvents, + IComponentsEventsSignatures, + IComponentsEventsInterfaces + >, + resolver: (err: Error | undefined) => void, + ) { + super(); + this.native = new (getNativeModule().Components)() as ComponentsNative; + this.logger.debug(`Rust Components native session is created`); + this.provider = provider; + this.register( + this.provider.getEvents().Destroy.subscribe(() => { + this.state = State.Destroyed; + if (this.destroyResolver === undefined) { + this.logger.error(`Session has been destroyed before call of "destroy"`); + this.state = State.Unavailable; + return; + } + this.logger.error(`Destroy session confirmation has been gotten`); + // Confirm destroying + this.destroyResolver(); + // Shutdown provider to drop all subscriptions + this.provider.destroy(); + }), + ); + this.native + .init(provider.getEmitter()) + .then(() => { + this.logger.debug(`Rust Components native session is inited`); + this.state = State.Available; + resolver(undefined); + }) + .catch((err: Error) => { + this.logger.error( + `Fail to init Components session: ${err instanceof Error ? err.message : err}`, + ); + resolver(err); + }); + } + + public destroy(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + this.state = State.Destroying; + return new Promise((resolve, reject) => { + this.destroyResolver = () => { + clearTimeout(timeout); + resolve(); + }; + const timeout = setTimeout(() => { + this.state = State.Unavailable; + reject( + new Error( + this.logger.error( + `Timeout error. Session wasn't closed in ${ + DESTROY_TIMEOUT / 1000 + } sec.`, + ), + ), + ); + }, DESTROY_TIMEOUT); + this.native + .destroy() + .then(() => { + this.logger.debug( + `Signal to destroy session has been sent. Wait for confirmation`, + ); + }) + .catch((err: Error) => { + this.logger.error( + `Fail to send destroy signal due error: ${ + err instanceof Error ? err.message : err + }`, + ); + this.state = State.Unavailable; + reject(err); + }); + }); + } + + public getSources(origin: SourceOrigin): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native.getSources(protocol.encodeSourceOrigin(origin)).then((buf: Uint8Array) => { + try { + resolve(protocol.decodeIdentList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.IdentList, + ), + ); + } + }); + }); + } + + public getParsers(origin: SourceOrigin): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native.getParsers(protocol.encodeSourceOrigin(origin)).then((buf: Uint8Array) => { + try { + resolve(protocol.decodeIdentList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.IdentList, + ), + ); + } + }); + }); + } + + public getOptions( + source: string, + parser: string, + origin: SourceOrigin, + ): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native + .getOptions(source, parser, protocol.encodeSourceOrigin(origin)) + .then((buf: Uint8Array) => { + try { + resolve(protocol.decodeComponentsOptions(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error( + `Fail to decode message: ${utils.error(err)}`, + ), + ), + Type.InvalidOutput, + Source.ComponentsOptions, + ), + ); + } + }); + }); + } + + public abort(fields: string[]): Error | undefined { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return err; + } + try { + this.native.abort(fields); + return undefined; + } catch (err) { + return NativeError.from(err); + } + } + + protected getSessionAccessErr(): Error | undefined { + switch (this.state) { + case State.Destroying: + return new Error(`Session is already in destroy process`); + case State.Destroyed: + return new Error(`Session is already destroyed`); + case State.Created: + return new Error(`Session wasn't inited yet`); + case State.Unavailable: + return new Error(`Session is unavailable`); + case State.Available: + return undefined; + } + } +} diff --git a/application/apps/rustcore/ts-bindings/src/native/native.ts b/application/apps/rustcore/ts-bindings/src/native/native.ts index 46a6d9167f..b17f7e0f4b 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.ts @@ -8,6 +8,7 @@ import * as fs from 'fs'; export interface IRustModuleExports { RustSession: any; UnboundJobs: any; + Components: any; RustProgressTracker: any; } diff --git a/application/holder/src/loaders/services.ts b/application/holder/src/loaders/services.ts index 8c9e21ddba..fefa666b79 100644 --- a/application/holder/src/loaders/services.ts +++ b/application/holder/src/loaders/services.ts @@ -18,3 +18,4 @@ import '@service/cli'; import '@service/menu'; import '@service/unbound'; import '@service/github'; +import '@service/components'; diff --git a/application/holder/src/register/services.ts b/application/holder/src/register/services.ts index 9f8ae7607f..f900e5df84 100644 --- a/application/holder/src/register/services.ts +++ b/application/holder/src/register/services.ts @@ -30,6 +30,10 @@ export const services: { [key: string]: Inputs } = { name: 'unbound', uuid: v4(), }, + components: { + name: 'components', + uuid: v4(), + }, storage: { name: 'storage', uuid: v4(), diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts new file mode 100644 index 0000000000..06e6050d4e --- /dev/null +++ b/application/holder/src/service/components.ts @@ -0,0 +1,15 @@ +import { + SetupService, + Interface, + Implementation, + register, + DependOn, +} from 'platform/entity/service'; +import { services } from '@register/services'; +import { electron } from '@service/electron'; + +@DependOn(electron) +@SetupService(services['components']) +export class Service extends Implementation {} +export interface Service extends Interface {} +export const components = register(new Service()); diff --git a/application/platform/types/bindings/index.ts b/application/platform/types/bindings/index.ts index 1ebc9c2ab8..159a5ae862 100644 --- a/application/platform/types/bindings/index.ts +++ b/application/platform/types/bindings/index.ts @@ -9,3 +9,4 @@ export * from './progress'; export * from './dlt'; export * from './operations'; export * from './plugins'; +export * from './options'; diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index cb65d0fe1e..a19a307487 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -1,160 +1,190 @@ import { DltFilterConfig } from './dlt'; -import type { PluginParserSettings } from './plugins'; // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PluginParserSettings } from './plugins'; /** * Settings for the DLT parser. */ -export type DltParserSettings = { -/** - * Configuration for filtering DLT messages. - */ -filter_config: DltFilterConfig, -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, -/** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ -with_storage_header: boolean, -/** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ -tz: string | null, }; +export type DltParserSettings = { + /** + * Configuration for filtering DLT messages. + */ + filter_config: DltFilterConfig; + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; + /** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ + with_storage_header: boolean; + /** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ + tz: string | null; +}; /** * Supported file formats for observation. */ export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; +export type Ident = { name: string; desc: string; uuid: string }; + +export type IdentList = Array; + /** * Multicast configuration information. * - `multiaddr`: A valid multicast address. * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string, interface: string | null, }; +export type MulticastInfo = { multiaddr: string; interface: string | null }; /** * Options for observing data within a session. */ -export type ObserveOptions = { -/** - * The description of the data source. - */ -origin: ObserveOrigin, -/** - * The parser configuration to be applied. - */ -parser: ParserType, }; +export type ObserveOptions = { + /** + * The description of the data source. + */ + origin: ObserveOrigin; + /** + * The parser configuration to be applied. + */ + parser: ParserType; +}; /** * Describes the source of data for observation. */ -export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; +export type ObserveOrigin = + | { File: [string, FileFormat, string] } + | { Concat: Array<[string, FileFormat, string]> } + | { Stream: [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { Plugin: PluginParserSettings }; +export type ParserType = + | { Dlt: DltParserSettings } + | { SomeIp: SomeIpParserSettings } + | { Text: null } + | { Plugin: PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { -/** - * The working directory for the command. - */ -cwd: string, -/** - * The command to execute. - */ -command: string, -/** - * Environment variables. If empty, the default environment variables are used. - */ -envs: Map, }; +export type ProcessTransportConfig = { + /** + * The working directory for the command. + */ + cwd: string; + /** + * The command to execute. + */ + command: string; + /** + * Environment variables. If empty, the default environment variables are used. + */ + envs: Map; +}; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { -/** - * The path to the serial port. - */ -path: string, -/** - * The baud rate for the connection. - */ -baud_rate: number, -/** - * The number of data bits per frame. - */ -data_bits: number, -/** - * The flow control setting. - */ -flow_control: number, -/** - * The parity setting. - */ -parity: number, -/** - * The number of stop bits. - */ -stop_bits: number, -/** - * The delay in sending data, in milliseconds. - */ -send_data_delay: number, -/** - * Whether the connection is exclusive. - */ -exclusive: boolean, }; +export type SerialTransportConfig = { + /** + * The path to the serial port. + */ + path: string; + /** + * The baud rate for the connection. + */ + baud_rate: number; + /** + * The number of data bits per frame. + */ + data_bits: number; + /** + * The flow control setting. + */ + flow_control: number; + /** + * The parity setting. + */ + parity: number; + /** + * The number of stop bits. + */ + stop_bits: number; + /** + * The delay in sending data, in milliseconds. + */ + send_data_delay: number; + /** + * Whether the connection is exclusive. + */ + exclusive: boolean; +}; /** * Settings for the SomeIp parser. */ -export type SomeIpParserSettings = { -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, }; +export type SomeIpParserSettings = { + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; +}; + +export type SourceOrigin = + | { File: string } + | { Files: Array } + | { Folder: string } + | { Folders: Array } + | { Stream: Ident }; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { -/** - * The address to bind the TCP connection to. - */ -bind_addr: string, }; +export type TCPTransportConfig = { + /** + * The address to bind the TCP connection to. + */ + bind_addr: string; +}; /** * Describes the transport source for a session. */ -export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; +export type Transport = + | { Process: ProcessTransportConfig } + | { TCP: TCPTransportConfig } + | { UDP: UDPTransportConfig } + | { Serial: SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { -/** - * The address to bind the UDP connection to. - */ -bind_addr: string, -/** - * A list of multicast configurations. - */ -multicast: Array, }; +export type UDPTransportConfig = { + /** + * The address to bind the UDP connection to. + */ + bind_addr: string; + /** + * A list of multicast configurations. + */ + multicast: Array; +}; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { -/** - * A list of multicast addresses to listen on. - */ -multicast_addr: Array, }; +export type UdpConnectionInfo = { + /** + * A list of multicast addresses to listen on. + */ + multicast_addr: Array; +}; diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts new file mode 100644 index 0000000000..0287da9d22 --- /dev/null +++ b/application/platform/types/bindings/options.ts @@ -0,0 +1,53 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Represents events sent to the client. + */ +export type CallbackOptionsEvent = + | { Options: Array } + | { Errors: [string, string][] } + | 'Destroyed'; + +export type ComponentsOptions = { source: Array; parser: Array }; + +export type Field = { id: string; value: Value }; + +export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; + +export type LazyFieldDesc = { id: string; name: string; desc: string }; + +export type StaticFieldDesc = { + id: string; + name: string; + desc: string; + required: boolean; + default: Value | null; + interface: ValueInterface; +}; + +export type Value = + | { Boolean: boolean } + | { Integer: number } + | { Float: number } + | { Text: string } + | { List: Array } + | { Directories: Array } + | { Files: Array } + | { File: string } + | { Directory: string } + | { KeyValues: { [key in string]?: Array } }; + +/** + * How value will be represented to user + */ +export type ValueInterface = + | 'Boolean' + | 'Integer' + | 'Float' + | 'Text' + | { DropList: Array } + | { List: Array } + | 'RequestDirectories' + | 'RequestFiles' + | 'RequestDirectory' + | 'RequestFile'; From a6427fc26c29a8f88070faade635657dec79476c Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 27 Mar 2025 18:12:53 +0100 Subject: [PATCH 003/121] Update components API --- .../apps/indexer/components/src/lib.rs | 111 ++++++------------ .../apps/indexer/components/src/tys.rs | 11 +- application/apps/indexer/parsers/Cargo.toml | 1 + .../apps/indexer/parsers/src/dlt/mod.rs | 35 +----- .../apps/indexer/parsers/src/dlt/options.rs | 99 ++++++++++++++++ .../apps/indexer/parsers/src/someip.rs | 17 ++- application/apps/indexer/parsers/src/text.rs | 3 +- .../plugins_host/src/parser_shared/mod.rs | 3 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 3 +- .../indexer/session/src/components/mod.rs | 24 ++-- .../indexer/sources/src/binary/pcap/legacy.rs | 3 +- .../indexer/sources/src/binary/pcap/ng.rs | 3 +- .../apps/indexer/sources/src/binary/raw.rs | 3 +- .../indexer/sources/src/command/process.rs | 3 +- .../indexer/sources/src/serial/serialport.rs | 3 +- .../apps/indexer/sources/src/socket/tcp.rs | 3 +- .../apps/indexer/sources/src/socket/udp.rs | 3 +- 17 files changed, 189 insertions(+), 139 deletions(-) create mode 100644 application/apps/indexer/parsers/src/dlt/options.rs diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 0be3d6b06d..d13a1941de 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -1,6 +1,6 @@ mod tys; -use tokio::{select, sync::oneshot}; +use tokio::select; use tokio_util::sync::CancellationToken; pub use tys::*; @@ -10,11 +10,11 @@ use uuid::Uuid; type Metadata<'a> = ( ( &'a stypes::Ident, - &'a (Option, Option), + &'a (Option, Option), ), ( &'a stypes::Ident, - &'a (Option, Option), + &'a (Option, Option), ), ); @@ -40,13 +40,13 @@ impl LazyLoadingTaskMeta { pub struct LazyLoadingTask { meta: LazyLoadingTaskMeta, - rx: Option, stypes::NativeError>>>, + task: Option, } impl LazyLoadingTask { pub fn new( ident: stypes::Ident, - rx: oneshot::Receiver, stypes::NativeError>>, + task: LazyFieldsTask, cancel: &CancellationToken, fields: Vec, ) -> Self { @@ -57,7 +57,7 @@ impl LazyLoadingTask { cancel: cancel.clone(), fields, }, - rx: Some(rx), + task: Some(task), } } @@ -66,7 +66,7 @@ impl LazyLoadingTask { } pub async fn wait(&mut self) -> Result { - let Some(rx) = self.rx.take() else { + let Some(task) = self.task.take() else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -81,7 +81,7 @@ impl LazyLoadingTask { _ = self.meta.cancel.cancelled() => { Ok(LazyLoadingResult::Cancelled) } - fields = rx => { + fields = task => { let fields = fields.map_err(|_| stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::ChannelError, @@ -89,7 +89,7 @@ impl LazyLoadingTask { "Fail to get settings for {}", self.meta.ident.name )), - })??; + })?; Ok(LazyLoadingResult::Feilds(fields)) } } @@ -97,71 +97,33 @@ impl LazyLoadingTask { } pub struct Options { - pub source: Vec, - pub parser: Vec, - pub lazy_source: Option, - pub lazy_parser: Option, + pub statics: Vec, + pub lazy: Option, } impl Options { - pub fn with_static(source: Vec, parser: Vec) -> Self { + pub fn new(statics: Vec) -> Self { Self { - source, - parser, - lazy_source: None, - lazy_parser: None, + statics, + lazy: None, } } - pub fn set_source_lazy_task( + pub fn set_lazy( &mut self, ident: stypes::Ident, - rx: oneshot::Receiver, stypes::NativeError>>, + task: LazyFieldsTask, cancel: &CancellationToken, ) { - self.lazy_source = Some(LazyLoadingTask::new( - ident, - rx, - cancel, - self.source_lazy_uuids(), - )); + self.lazy = Some(LazyLoadingTask::new(ident, task, cancel, self.lazy_uuids())); } - pub fn set_parser_lazy_task( - &mut self, - ident: stypes::Ident, - rx: oneshot::Receiver, stypes::NativeError>>, - cancel: &CancellationToken, - ) { - self.lazy_parser = Some(LazyLoadingTask::new( - ident, - rx, - cancel, - self.parser_lazy_uuids(), - )); - } - pub fn source_has_lazy(&self) -> bool { - self.source + pub fn has_lazy(&self) -> bool { + self.statics .iter() .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) } - pub fn parser_has_lazy(&self) -> bool { - self.parser - .iter() - .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) - } - pub fn source_lazy_uuids(&self) -> Vec { - self.source - .iter() - .filter_map(|f| { - if let stypes::FieldDesc::Lazy(f) = f { - Some(f.id.to_owned()) - } else { - None - } - }) - .collect() - } - pub fn parser_lazy_uuids(&self) -> Vec { - self.parser + + fn lazy_uuids(&self) -> Vec { + self.statics .iter() .filter_map(|f| { if let stypes::FieldDesc::Lazy(f) = f { @@ -177,7 +139,7 @@ impl Options { #[derive(Default)] pub struct Components { //TODO: "hot" validators also here - options: HashMap, Option)>, + options: HashMap, Option)>, sources: HashMap, parsers: HashMap, } @@ -187,9 +149,9 @@ impl Components { &mut self, ident: &stypes::Ident, static_fields_handle: Option, - lazy_fields_handle: Option, + lazy_fields_handle: Option, ) -> Result<(), stypes::NativeError> { - if self.options.contains_key(&ident.uuid) || self.sources.contains_key(&ident.uuid) { + if self.options.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -206,7 +168,7 @@ impl Components { &mut self, ident: &stypes::Ident, static_fields_handle: Option, - lazy_fields_handle: Option, + lazy_fields_handle: Option, ) -> Result<(), stypes::NativeError> { self.register(ident, static_fields_handle, lazy_fields_handle)?; self.sources.insert(ident.uuid, ident.to_owned()); @@ -218,7 +180,7 @@ impl Components { &mut self, ident: &stypes::Ident, static_fields_handle: Option, - lazy_fields_handle: Option, + lazy_fields_handle: Option, ) -> Result<(), stypes::NativeError> { self.register(ident, static_fields_handle, lazy_fields_handle)?; self.parsers.insert(ident.uuid, ident.to_owned()); @@ -246,7 +208,7 @@ impl Components { source_origin: stypes::SourceOrigin, source: Uuid, parser: Uuid, - ) -> Result { + ) -> Result<(Options, Options), stypes::NativeError> { let ( (source_ident, (source_static, source_lazy)), (parser_ident, (parser_static, parser_lazy)), @@ -257,21 +219,18 @@ impl Components { let parser_fields = parser_static .map(|getter| getter(&source_origin)) .unwrap_or(Ok(Vec::new()))?; - let mut options = Options::with_static(source_fields, parser_fields); + let mut source_options = Options::new(source_fields); + let mut parser_options = Options::new(parser_fields); // Check do we have some lazy settings - if let (Some(getter), true) = (source_lazy, options.source_has_lazy()) { - let (tx, rx) = oneshot::channel(); + if let (Some(getter), true) = (source_lazy, source_options.has_lazy()) { let tk = CancellationToken::new(); - getter(&source_origin, tx, &tk)?; - options.set_source_lazy_task(source_ident.clone(), rx, &tk); + source_options.set_lazy(source_ident.clone(), getter(&source_origin, &tk), &tk); } - if let (Some(getter), true) = (parser_lazy, options.source_has_lazy()) { - let (tx, rx) = oneshot::channel(); + if let (Some(getter), true) = (parser_lazy, parser_options.has_lazy()) { let tk = CancellationToken::new(); - getter(&source_origin, tx, &tk)?; - options.set_parser_lazy_task(parser_ident.clone(), rx, &tk); + parser_options.set_lazy(parser_ident.clone(), getter(&source_origin, &tk), &tk); } - Ok(options) + Ok((source_options, parser_options)) } fn get<'a>( diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 65c7774c7f..2fd4dc72bf 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -1,5 +1,6 @@ +use std::{future::Future, pin::Pin}; + use crate::*; -use stypes::NativeError; use tokio_util::sync::CancellationToken; pub type FieldsResult = Result, stypes::NativeError>; @@ -7,12 +8,8 @@ pub type FieldsResult = Result, stypes::NativeError>; pub type FieldsGetter = fn(&stypes::SourceOrigin) -> FieldsResult; pub type StaticFieldsResult = Result, stypes::NativeError>; - -pub type LazyFieldsGetter = fn( - &stypes::SourceOrigin, - oneshot::Sender, - &CancellationToken, -) -> Result<(), NativeError>; +pub type LazyFieldsTask = Pin + Send>>; +pub type LazyFieldsTaskGetter = fn(&stypes::SourceOrigin, &CancellationToken) -> LazyFieldsTask; pub trait Component { /// Returns an identificator diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index be66d6fe2b..85f612bfce 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -21,6 +21,7 @@ someip-tools = { path = "../addons/someip-tools" } stypes = { path = "../stypes", features=["rustcore"] } uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } +tokio-util.workspace = true [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 859edbedf4..9d6b4ac5a8 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -1,5 +1,6 @@ pub mod attachment; pub mod fmt; +mod options; use crate::{ Error, LogMessage, ParseYield, SingleParser, dlt::fmt::FormattableMessage, @@ -241,23 +242,6 @@ impl Parser for DltRangeParser { let total_consumed = input.len() - rest.len(); let item = (total_consumed, msg.map(|m| m.into())); -const DLT_RANGE_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -]); - -impl components::Component for DltRangeParser { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Range Parser"), - desc: String::from("DLT Range Parser"), - uuid: DLT_RANGE_PARSER_UUID, - } - } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { - Ok(()) - } -} - impl Parser for DltRawParser { fn parse( &mut self, @@ -269,20 +253,3 @@ impl Parser for DltRawParser { }) } } - -const DLT_RAW_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -]); - -impl components::Component for DltRawParser { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Raw Parser"), - desc: String::from("DLT Raw Parser"), - uuid: DLT_RAW_PARSER_UUID, - } - } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { - Ok(()) - } -} diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs new file mode 100644 index 0000000000..9bf9876cb6 --- /dev/null +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -0,0 +1,99 @@ +use tokio_util::sync::CancellationToken; + +use crate::dlt::*; + +const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +const FIELD_LOG_LEVEL: &str = "log_level"; +const FIELD_STATISTICS: &str = "statistics"; + +impl DltParser<'_> { + pub fn fields( + _origin: &stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + Ok(vec![ + stypes::FieldDesc::Static(stypes::StaticFieldDesc { + id: FIELD_LOG_LEVEL.to_owned(), + name: String::from("Log Level"), + desc: String::from("DLT Log Level"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }), + stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Statistics"), + desc: String::from("Collected Statistis Data"), + }), + ]) + } + pub fn statistits( + _origin: &stypes::SourceOrigin, + _cancel: &CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async { + Ok(vec![stypes::StaticFieldDesc { + id: FIELD_LOG_LEVEL.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }]) + }) + } +} + +impl components::Component for DltParser<'_> { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Parser"), + desc: String::from("DLT Parser"), + uuid: DLT_PARSER_UUID, + } + } + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_parser( + &Self::ident(), + Some(DltParser::fields), + Some(DltParser::statistits), + )?; + Ok(()) + } +} + +const DLT_RANGE_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +impl components::Component for DltRangeParser { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Range Parser"), + desc: String::from("DLT Range Parser"), + uuid: DLT_RANGE_PARSER_UUID, + } + } + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} + +const DLT_RAW_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +impl components::Component for DltRawParser { + fn ident() -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Raw Parser"), + desc: String::from("DLT Raw Parser"), + uuid: DLT_RAW_PARSER_UUID, + } + } + fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + Ok(()) + } +} diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index a806b5397e..07d0180df1 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -319,6 +319,20 @@ const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, ]); +impl SomeipParser { + pub fn fields( + _origin: &stypes::SourceOrigin, + ) -> Result, stypes::NativeError> { + Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { + id: String::from("log_level"), + name: String::from("Log Level"), + desc: String::from("DLT Log Level"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + })]) + } +} impl components::Component for SomeipParser { fn ident() -> stypes::Ident { stypes::Ident { @@ -327,7 +341,8 @@ impl components::Component for SomeipParser { uuid: SOMEIP_PARSER_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_parser(&Self::ident(), Some(SomeipParser::fields), None)?; Ok(()) } } diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text.rs index 480c97a801..6dfc364f08 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text.rs @@ -83,7 +83,8 @@ impl components::Component for StringTokenizer { uuid: TEXT_PARSER_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_parser(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 48c123e54d..a32d3e0384 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -191,7 +191,8 @@ impl components::Component for PluginsParser { } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_parser(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index d006a4de70..1a27c525d0 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -180,7 +180,8 @@ impl components::Component for PluginParser { } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_parser(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index ee0e5aa59f..191a663c4e 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -59,19 +59,20 @@ impl ComponentsSession { origin, tx, } => { - let options = match components.get_options(origin, source, parser) { - Ok(options) => options, - Err(err) => { - log_if_err(tx.send(Err(err))); - continue; - } - }; + let (source_options, parser_options) = + match components.get_options(origin, source, parser) { + Ok(options) => options, + Err(err) => { + log_if_err(tx.send(Err(err))); + continue; + } + }; // Send static fields log_if_err(tx.send(Ok(stypes::ComponentsOptions { - source: options.source, - parser: options.parser, + source: source_options.statics, + parser: parser_options.statics, }))); - if let Some(mut source_loading_task) = options.lazy_source { + if let Some(mut source_loading_task) = source_options.lazy { // If exists, request lazy source fields let meta = source_loading_task.get_meta(); let uuid = meta.uuid; @@ -89,7 +90,7 @@ impl ComponentsSession { ), ); } - if let Some(mut parser_loading_task) = options.lazy_parser { + if let Some(mut parser_loading_task) = parser_options.lazy { // If exists, request lazy parser fields let meta = parser_loading_task.get_meta(); let uuid = meta.uuid; @@ -151,6 +152,7 @@ impl ComponentsSession { }); tasks.clear(); log_if_err(tx.send(())); + break; } } } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index be8d070942..7e14ce7a6b 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -168,7 +168,8 @@ impl components::Component for PcapLegacyByteSource { } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 669d0f31e4..0b4dff8d64 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -172,7 +172,8 @@ impl components::Component for PcapngByteSource { } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index e079f50ba3..7c44e67f23 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -89,7 +89,8 @@ impl components::Component for BinaryByteSource { } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 75223a033f..b0b07233dd 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -238,7 +238,8 @@ impl components::Component for ProcessSource { uuid: TERM_SOURCE_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index 2012ac8cf6..b2b65c1c74 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -228,7 +228,8 @@ impl components::Component for SerialSource { uuid: SERIAL_SOURCE_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index ce0cde3f8c..1eded530f0 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -180,7 +180,8 @@ impl components::Component for TcpSource { uuid: TCP_SOURCE_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 4ceab9b13a..dc9bc637a6 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -142,7 +142,8 @@ impl components::Component for UdpSource { uuid: UDP_SOURCE_UUID, } } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register_source(&Self::ident(), None, None)?; Ok(()) } } From 806d0e9d097b3be3ba44b5a558da0dcdcb86398d Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 27 Mar 2025 18:13:15 +0100 Subject: [PATCH 004/121] Correct protocol (components context) --- application/apps/protocol/src/lib.rs | 1 + .../apps/rustcore/rs-bindings/Cargo.lock | 1 + .../apps/rustcore/ts-bindings/package.json | 3 +- .../apps/rustcore/ts-bindings/spec/config.ts | 3 ++ .../rustcore/ts-bindings/spec/defaults.json | 9 ++++ .../spec/session.components.spec.ts | 53 +++++++++++++++++++ .../src/api/components.provider.ts | 14 ++--- .../apps/rustcore/ts-bindings/src/index.ts | 1 + .../src/native/native.components.ts | 4 +- 9 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 application/apps/rustcore/ts-bindings/spec/session.components.spec.ts diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index d266d944c7..5f8ad0489e 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -211,3 +211,4 @@ gen_encode_decode_fns!(ComponentsOptions); gen_encode_decode_fns!(FieldDesc); gen_encode_decode_fns!(LazyFieldDesc); gen_encode_decode_fns!(StaticFieldDesc); +gen_encode_decode_fns!(CallbackOptionsEvent); diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index f1321de38b..9f07fc768f 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2415,6 +2415,7 @@ dependencies = [ "someip-tools", "thiserror 2.0.12", "stypes", + "tokio-util", "uuid", ] diff --git a/application/apps/rustcore/ts-bindings/package.json b/application/apps/rustcore/ts-bindings/package.json index 4b886af211..af41ccf3f7 100644 --- a/application/apps/rustcore/ts-bindings/package.json +++ b/application/apps/rustcore/ts-bindings/package.json @@ -6,8 +6,9 @@ "types": "./dist/index.d.ts", "scripts": { "build": "node_modules/.bin/tsc -p tsconfig.json", + "build-jasmine": "node_modules/.bin/tsc -p ./spec/tsconfig.json", "prod": "node_modules/.bin/tsc -p tsconfig.json", - "test_cancel": "node_modules/.bin/electron node_modules/jasmine-ts/lib/index.js ./spec/session.cancel.spec.ts", + "test": "node_modules/.bin/jasmine ./spec/build/spec/session.components.spec.js", "lint": "node_modules/.bin/eslint . --max-warnings=0", "check": "node_modules/.bin/tsc -p tsconfig.json --noemit" }, diff --git a/application/apps/rustcore/ts-bindings/spec/config.ts b/application/apps/rustcore/ts-bindings/spec/config.ts index 775deb23a1..ea323a872a 100644 --- a/application/apps/rustcore/ts-bindings/spec/config.ts +++ b/application/apps/rustcore/ts-bindings/spec/config.ts @@ -76,6 +76,9 @@ export interface IConfiguration { observing: { regular: IRegularTests; }; + components: { + regular: IRegularTests; + }; }; } diff --git a/application/apps/rustcore/ts-bindings/spec/defaults.json b/application/apps/rustcore/ts-bindings/spec/defaults.json index 03f8288e96..fd4d77f7ab 100644 --- a/application/apps/rustcore/ts-bindings/spec/defaults.json +++ b/application/apps/rustcore/ts-bindings/spec/defaults.json @@ -285,6 +285,15 @@ }, "files": {} } + }, + "components": { + "regular": { + "execute_only": [], + "list": { + "1": "Test 1. Basic test" + }, + "files": {} + } } } } diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts new file mode 100644 index 0000000000..a2b3df33e0 --- /dev/null +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -0,0 +1,53 @@ +// tslint:disable +// We need to provide path to TypeScript types definitions +/// +/// + +import { initLogger } from './logger'; +initLogger(); +import { Components } from '../src/index'; +import { readConfigurationFile } from './config'; +import { finish } from './common'; +import { SourceOrigin } from 'platform/types/bindings'; + +import * as runners from './runners'; + +const config = readConfigurationFile().get().tests.components; + +describe('Jobs', function () { + it(config.regular.list[1], function () { + return runners.unbound(config.regular, 1, async (logger, done, collector) => { + const components = new Components(async (err: Error | undefined) => { + if (err instanceof Error) { + return finish([], done, err); + } + let parsers = await components.get({ File: 'somefile' }).parsers(); + let sources = await components.get({ File: 'somefile' }).sources(); + console.log(parsers); + console.log(sources); + components + .getOptions( + '05050505-0505-0505-0505-050505050505', + '01010101-0101-0101-0101-010101010101', + { File: 'somefile' }, + ) + .then((fields) => { + console.log(JSON.stringify(fields)); + components + .destroy() + .then(() => { + setTimeout(() => { + finish([], done); + }, 1000); + }) + .catch((err) => { + finish([], done, err); + }); + }) + .catch((err) => { + finish([], done, err); + }); + }); + }); + }); +}); diff --git a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts index 8b234caea0..97864a6e6c 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts @@ -7,7 +7,7 @@ import * as protocol from 'protocol'; export interface IComponentsEvents { Options: Subject; Errors: Subject<[string, string][]>; - Destroy: Subject; + Destroyed: Subject; } export interface IComponentsEventsConvertors {} @@ -15,25 +15,25 @@ export interface IComponentsEventsConvertors {} export interface IComponentsEventsSignatures { Options: 'Options'; Errors: 'Errors'; - Destroy: 'Destroy'; + Destroyed: 'Destroyed'; } const SessionEventsSignatures: IComponentsEventsSignatures = { Options: 'Options', Errors: 'Errors', - Destroy: 'Destroy', + Destroyed: 'Destroyed', }; export interface IComponentsEventsInterfaces { Options: { self: ['object', null] }; Errors: { self: [typeof Array, null] }; - Destroy: { self: null }; + Destroyed: { self: null }; } const SessionEventsInterfaces: IComponentsEventsInterfaces = { Options: { self: ['object', null] }, Errors: { self: [Array, null] }, - Destroy: { self: null }, + Destroyed: { self: null }, }; export class ComponentsEventProvider extends Computation< @@ -44,13 +44,13 @@ export class ComponentsEventProvider extends Computation< private readonly _events: IComponentsEvents = { Options: new Subject(), Errors: new Subject<[string, string][]>(), - Destroy: new Subject(), + Destroyed: new Subject(), }; private readonly _convertors: IComponentsEventsConvertors = {}; constructor(uuid: string) { - super(uuid, protocol.decodeCallbackEvent); + super(uuid, protocol.decodeCallbackOptionsEvent); } public getName(): string { diff --git a/application/apps/rustcore/ts-bindings/src/index.ts b/application/apps/rustcore/ts-bindings/src/index.ts index da344f180b..c5bbbdb863 100644 --- a/application/apps/rustcore/ts-bindings/src/index.ts +++ b/application/apps/rustcore/ts-bindings/src/index.ts @@ -24,6 +24,7 @@ export { } from './api/session'; export { Jobs } from './api/jobs'; export { Tracker } from './api/tracker'; +export { Components } from './api/components'; export { Units, Events, Interfaces }; export * as $ from 'platform/types/observe'; diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 4e51a8951f..8097c9d76c 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -68,14 +68,14 @@ export class Base extends Subscriber { this.logger.debug(`Rust Components native session is created`); this.provider = provider; this.register( - this.provider.getEvents().Destroy.subscribe(() => { + this.provider.getEvents().Destroyed.subscribe(() => { this.state = State.Destroyed; if (this.destroyResolver === undefined) { this.logger.error(`Session has been destroyed before call of "destroy"`); this.state = State.Unavailable; return; } - this.logger.error(`Destroy session confirmation has been gotten`); + this.logger.debug(`Destroy session confirmation has been gotten`); // Confirm destroying this.destroyResolver(); // Shutdown provider to drop all subscriptions From 46328ba2a91499379dbc27fdd7d4bc6406ebcd72 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:31:19 +0100 Subject: [PATCH 005/121] Update components controller (public events) --- .../ts-bindings/src/api/components.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index f132bcaa70..7229210964 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -2,7 +2,7 @@ import { Logger } from 'platform/log'; import { scope } from 'platform/env/scope'; import { v4 as uuid } from 'uuid'; import { Base as Native } from '../native/native.components'; -import { ComponentsEventProvider } from '../api/components.provider'; +import { ComponentsEventProvider, IComponentsEvents } from '../api/components.provider'; import { SessionStream } from '../api/session.stream'; import { SessionSearch } from '../api/session.search'; import { Subscriber } from 'platform/env/subscription'; @@ -39,6 +39,18 @@ export class Components extends Subscriber { }); } + static create(): Promise { + return new Promise((resolve, reject) => { + const components = new Components((err: Error | undefined) => { + if (err instanceof Error) { + reject(err); + } else { + resolve(components); + } + }); + }); + } + public destroy(): Promise { return this.native.destroy().catch((err: Error) => { this.logger.error(`Fail to destroy session: ${err.message}`); @@ -46,6 +58,13 @@ export class Components extends Subscriber { }); } + public getEvents(): IComponentsEvents { + if (this.provider === undefined) { + throw new Error(`EventProvider wasn't created`); + } + return this.provider.getEvents(); + } + public get(origin: SourceOrigin): { sources(): Promise; parsers(): Promise; From f16867ce46a42e7373ed6f53cc3cd59d0ba2ad96 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:31:51 +0100 Subject: [PATCH 006/121] Consider canelation order (fields) --- application/apps/indexer/components/src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index d13a1941de..b24f514274 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -76,9 +76,11 @@ impl LazyLoadingTask { )), }); }; - // TODO: something about cancel safe + // TODO: cancel safe select! { _ = self.meta.cancel.cancelled() => { + // TODO: the question - shell we controll cancellation about parser/source, + // or we can deligate it? I think we should controll it above. Ok(LazyLoadingResult::Cancelled) } fields = task => { @@ -90,7 +92,12 @@ impl LazyLoadingTask { self.meta.ident.name )), })?; - Ok(LazyLoadingResult::Feilds(fields)) + // Check one more time in case if cancel was catched first on level of parser/source + if self.meta.cancel.is_cancelled() { + Ok(LazyLoadingResult::Cancelled) + } else { + Ok(LazyLoadingResult::Feilds(fields)) + } } } } From 0ace80864dd39951daa99cb23b7783c57f329bd1 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:32:07 +0100 Subject: [PATCH 007/121] Add options to parsers for tests --- application/apps/indexer/parsers/Cargo.toml | 1 + .../apps/indexer/parsers/src/dlt/options.rs | 25 ++++++++--- .../apps/indexer/parsers/src/someip.rs | 45 ++++++++++++++++++- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 85f612bfce..69e5e8c7b7 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -22,6 +22,7 @@ stypes = { path = "../stypes", features=["rustcore"] } uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } tokio-util.workspace = true +tokio.workspace = true [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 9bf9876cb6..d5c0eea3ad 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,3 +1,7 @@ +use tokio::{ + select, + time::{sleep, Duration}, +}; use tokio_util::sync::CancellationToken; use crate::dlt::*; @@ -29,13 +33,24 @@ impl DltParser<'_> { }), ]) } - pub fn statistits( + pub fn lazy( _origin: &stypes::SourceOrigin, - _cancel: &CancellationToken, + cancel: &CancellationToken, ) -> components::LazyFieldsTask { - Box::pin(async { + let tk: CancellationToken = cancel.clone(); + Box::pin(async move { + let duration = Duration::from_millis(5000); + select! { + _ = sleep(duration) => { + // no cancelation + }, + _ = tk.cancelled() => { + // cancelled + return Ok(Vec::new()); + } + }; Ok(vec![stypes::StaticFieldDesc { - id: FIELD_LOG_LEVEL.to_owned(), + id: FIELD_STATISTICS.to_owned(), name: String::from("Example"), desc: String::from("Example"), required: true, @@ -58,7 +73,7 @@ impl components::Component for DltParser<'_> { components.register_parser( &Self::ident(), Some(DltParser::fields), - Some(DltParser::statistits), + Some(DltParser::lazy), )?; Ok(()) } diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index 07d0180df1..dff02eed3a 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -20,6 +20,11 @@ use lazy_static::lazy_static; use log::{debug, error}; use regex::Regex; use serde::Serialize; +use tokio::{ + select, + time::{sleep, Duration}, +}; +use tokio_util::sync::CancellationToken; /// Marker for a column separator in the output string. const COLUMN_SEP: &str = "\u{0004}"; // EOT @@ -319,12 +324,17 @@ const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, ]); +// TODO: fields IDs could be same for diff parser/source... on level of Options event +// we should also provide a master of field to prevent conflicts. +const FIELD_LOG_LEVEL: &str = "log_level"; +const FIELD_STATISTICS: &str = "statistics"; + impl SomeipParser { pub fn fields( _origin: &stypes::SourceOrigin, ) -> Result, stypes::NativeError> { Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { - id: String::from("log_level"), + id: FIELD_LOG_LEVEL.to_owned(), name: String::from("Log Level"), desc: String::from("DLT Log Level"), required: true, @@ -332,6 +342,33 @@ impl SomeipParser { interface: stypes::ValueInterface::DropList(Vec::new()), })]) } + pub fn lazy( + _origin: &stypes::SourceOrigin, + cancel: &CancellationToken, + ) -> components::LazyFieldsTask { + let tk: CancellationToken = cancel.clone(); + Box::pin(async move { + // Sleep a little to emulate loading + let duration = Duration::from_millis(100); + select! { + _ = sleep(duration) => { + // no cancelation + }, + _ = tk.cancelled() => { + // cancelled + return Ok(Vec::new()); + } + }; + Ok(vec![stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }]) + }) + } } impl components::Component for SomeipParser { fn ident() -> stypes::Ident { @@ -342,7 +379,11 @@ impl components::Component for SomeipParser { } } fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser(&Self::ident(), Some(SomeipParser::fields), None)?; + components.register_parser( + &Self::ident(), + Some(SomeipParser::fields), + Some(SomeipParser::lazy), + )?; Ok(()) } } From 090f1ed1742464d70c90ef61387b9349c558c443 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:32:29 +0100 Subject: [PATCH 008/121] Update jasmine test --- .../apps/rustcore/ts-bindings/spec/common.ts | 10 +- .../rustcore/ts-bindings/spec/defaults.json | 3 +- .../apps/rustcore/ts-bindings/spec/runners.ts | 20 ++- .../spec/session.components.spec.ts | 148 ++++++++++++++---- 4 files changed, 142 insertions(+), 39 deletions(-) diff --git a/application/apps/rustcore/ts-bindings/spec/common.ts b/application/apps/rustcore/ts-bindings/spec/common.ts index 5f6a65d2c6..fb827da821 100644 --- a/application/apps/rustcore/ts-bindings/spec/common.ts +++ b/application/apps/rustcore/ts-bindings/spec/common.ts @@ -1,6 +1,6 @@ // tslint:disable -import { Jobs, Tracker, Session } from '../src/index'; +import { Jobs, Tracker, Session, Components } from '../src/index'; import { Logger } from './logger'; import { error, numToLogLevel } from 'platform/log/utils'; import { state } from 'platform/log'; @@ -70,7 +70,13 @@ export function readConfigFile(filenameEnvVar: string, defaultPaths: string[] } export function finish( - sessions: Array | Session | Jobs | Tracker | undefined, + sessions: + | Array + | Session + | Jobs + | Tracker + | Components + | undefined, done: (...args: any[]) => void, err?: Error, ): void { diff --git a/application/apps/rustcore/ts-bindings/spec/defaults.json b/application/apps/rustcore/ts-bindings/spec/defaults.json index fd4d77f7ab..74b44fdddd 100644 --- a/application/apps/rustcore/ts-bindings/spec/defaults.json +++ b/application/apps/rustcore/ts-bindings/spec/defaults.json @@ -290,7 +290,8 @@ "regular": { "execute_only": [], "list": { - "1": "Test 1. Basic test" + "1": "Test 1. Cancel loading options", + "2": "Test 2. Wait for lazy fields" }, "files": {} } diff --git a/application/apps/rustcore/ts-bindings/spec/runners.ts b/application/apps/rustcore/ts-bindings/spec/runners.ts index 161e9b0a66..ae96f7443c 100644 --- a/application/apps/rustcore/ts-bindings/spec/runners.ts +++ b/application/apps/rustcore/ts-bindings/spec/runners.ts @@ -1,4 +1,12 @@ -import { Jobs, Tracker, Session, SessionStream, ISessionEvents, SessionSearch } from '../src/index'; +import { + Jobs, + Tracker, + Session, + SessionStream, + ISessionEvents, + SessionSearch, + Components, +} from '../src/index'; import { Logger, getLogger } from './logger'; import { error } from 'platform/log/utils'; import { IRegularTests } from './config'; @@ -118,11 +126,13 @@ export function unbound( test: ( logger: Logger, done: () => void, - add: ScopeInjector, + add: ScopeInjector, ) => Promise, ): Promise { - const scope: Array = []; - const injector: ScopeInjector = (obj: Session | Tracker | Jobs) => { + const scope: Array = []; + const injector: ScopeInjector = ( + obj: Session | Tracker | Jobs | Components, + ) => { scope.push(obj); return obj; }; @@ -140,4 +150,4 @@ export function unbound( finish(scope, done, new Error(error(err))); } }); -} \ No newline at end of file +} diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts index a2b3df33e0..a097330101 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -8,46 +8,132 @@ initLogger(); import { Components } from '../src/index'; import { readConfigurationFile } from './config'; import { finish } from './common'; -import { SourceOrigin } from 'platform/types/bindings'; +import { SourceOrigin, FieldDesc, LazyFieldDesc, StaticFieldDesc } from 'platform/types/bindings'; import * as runners from './runners'; +import { error } from 'platform/log/utils'; const config = readConfigurationFile().get().tests.components; +const TCP_SOURCE_UUID: string = '05050505-0505-0505-0505-050505050505'; +const DLT_PARSER_UUID: string = '01010101-0101-0101-0101-010101010101'; +const SOMEIP_PARSER_UUID: string = '02020202-0202-0202-0202-020202020202'; + describe('Jobs', function () { it(config.regular.list[1], function () { return runners.unbound(config.regular, 1, async (logger, done, collector) => { - const components = new Components(async (err: Error | undefined) => { - if (err instanceof Error) { - return finish([], done, err); + try { + const components = collector(await Components.create()) as Components; + const origin: SourceOrigin = { File: 'somefile' }; + // Request available parsers and sources + let parsers = await components.get(origin).parsers(); + let sources = await components.get(origin).sources(); + if (!parsers.find((ident) => ident.uuid === DLT_PARSER_UUID)) { + return finish( + [components], + done, + new Error(`Fail to find DLT parser by uuid: ${DLT_PARSER_UUID}`), + ); + } + if (!sources.find((ident) => ident.uuid === TCP_SOURCE_UUID)) { + return finish( + [components], + done, + new Error(`Fail to find DLT parser by uuid: ${TCP_SOURCE_UUID}`), + ); + } + // Subscribe to events + let received_lazy: StaticFieldDesc[] | undefined = undefined; + components.getEvents().Options.subscribe((fields: StaticFieldDesc[]) => { + received_lazy = fields; + }); + // Request options scheme + const fields = await components.getOptions( + TCP_SOURCE_UUID, + DLT_PARSER_UUID, + origin, + ); + const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.parser.find( + (field: FieldDesc) => 'Lazy' in field, + ); + if (!lazy_field) { + return finish([components], done, new Error(`No lazy fields from DLT parser`)); + } + // Do not wait for field + components.abort([lazy_field.Lazy.id]); + setTimeout(() => { + if (received_lazy !== undefined) { + finish( + [components], + done, + new Error( + `Lazy options received, but should not: ${JSON.stringify( + received_lazy, + )}`, + ), + ); + } else { + // We are good + finish([components], done); + } + }, 2000); + } catch (err) { + finish([], done, new Error(error(err))); + } + }); + }); + + it(config.regular.list[2], function () { + return runners.unbound(config.regular, 2, async (logger, done, collector) => { + try { + const components = collector(await Components.create()) as Components; + const origin: SourceOrigin = { File: 'somefile' }; + // Request available parsers and sources + let parsers = await components.get(origin).parsers(); + let sources = await components.get(origin).sources(); + if (!parsers.find((ident) => ident.uuid === SOMEIP_PARSER_UUID)) { + return finish( + [components], + done, + new Error(`Fail to find DLT parser by uuid: ${SOMEIP_PARSER_UUID}`), + ); + } + if (!sources.find((ident) => ident.uuid === TCP_SOURCE_UUID)) { + return finish( + [components], + done, + new Error(`Fail to find DLT parser by uuid: ${TCP_SOURCE_UUID}`), + ); + } + // Subscribe to events + let received_lazy: StaticFieldDesc[] | undefined = undefined; + components.getEvents().Options.subscribe((fields: StaticFieldDesc[]) => { + received_lazy = fields; + }); + // Request options scheme + const fields = await components.getOptions( + TCP_SOURCE_UUID, + DLT_PARSER_UUID, + origin, + ); + const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.parser.find( + (field: FieldDesc) => 'Lazy' in field, + ); + if (!lazy_field) { + return finish([components], done, new Error(`No lazy fields from DLT parser`)); } - let parsers = await components.get({ File: 'somefile' }).parsers(); - let sources = await components.get({ File: 'somefile' }).sources(); - console.log(parsers); - console.log(sources); - components - .getOptions( - '05050505-0505-0505-0505-050505050505', - '01010101-0101-0101-0101-010101010101', - { File: 'somefile' }, - ) - .then((fields) => { - console.log(JSON.stringify(fields)); - components - .destroy() - .then(() => { - setTimeout(() => { - finish([], done); - }, 1000); - }) - .catch((err) => { - finish([], done, err); - }); - }) - .catch((err) => { - finish([], done, err); - }); - }); + setTimeout(() => { + if (received_lazy === undefined) { + // We are good + finish([components], done); + } else { + // We didn't get lazy options + finish([components], done, new Error(`Lazy options not received`)); + } + }, 1000); + } catch (err) { + finish([], done, new Error(error(err))); + } }); }); }); From 6cc3dd2acf1c14151c1b7a8e23735ea36a69e053 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:32:51 +0100 Subject: [PATCH 009/121] Update lock files --- .../apps/rustcore/rs-bindings/Cargo.lock | 1 + cli/chipmunk-cli/Cargo.lock | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 9f07fc768f..9fc56ea35f 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2415,6 +2415,7 @@ dependencies = [ "someip-tools", "thiserror 2.0.12", "stypes", + "tokio", "tokio-util", "uuid", ] diff --git a/cli/chipmunk-cli/Cargo.lock b/cli/chipmunk-cli/Cargo.lock index ee3b5dda72..838a2e2787 100644 --- a/cli/chipmunk-cli/Cargo.lock +++ b/cli/chipmunk-cli/Cargo.lock @@ -329,6 +329,18 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "components" +version = "0.1.0" +dependencies = [ + "log", + "stypes", + "thiserror 2.0.11", + "tokio", + "tokio-util", + "uuid", +] + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -933,6 +945,7 @@ dependencies = [ "byteorder", "chrono", "chrono-tz", + "components", "dlt-core", "lazy_static", "log", @@ -943,7 +956,11 @@ dependencies = [ "someip-messages", "someip-payload", "someip-tools", - "thiserror 2.0.12", + "stypes", + "thiserror 2.0.11", + "tokio", + "tokio-util", + "uuid", ] [[package]] @@ -1343,6 +1360,7 @@ dependencies = [ "async-stream", "bufread", "bytes", + "components", "etherparse", "futures", "indexer_base", @@ -1388,6 +1406,7 @@ dependencies = [ "serde", "thiserror 2.0.12", "tokio", + "tokio-util", "uuid", "walkdir", ] From 3629aad6795a01a0e71fb646ce9ed484fee29ee3 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 28 Mar 2025 15:47:07 +0100 Subject: [PATCH 010/121] Revert test data changes --- application/apps/indexer/sources/src/binary/pcap/legacy.rs | 6 +++--- application/apps/indexer/sources/src/binary/pcap/ng.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 7e14ce7a6b..ca50169e12 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -208,7 +208,7 @@ mod tests { 0xff, 0xff, 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0c, 0x00, 0x10, 0x04, 0x00, 0xac, 0x16, 0x0c, 0x4f, 0x00, 0x11, 0x75, 0x30, + 0x00, 0x0c, 0x00, 0x09, 0x04, 0x00, 0xac, 0x16, 0x0c, 0x4f, 0x00, 0x11, 0x75, 0x30, ]; let udp_payload = &SAMPLE_PCAP_DATA[82..=137]; @@ -238,7 +238,7 @@ mod tests { 0x81, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20, 0x00, 0x7b, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x00, 0x10, + 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x06, 0x75, 0x30, 0x52, 0x90, 0x5a, 0x64, 0x08, 0xda, 0x0e, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5e, 0x40, 0xff, 0xfb, 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0x08, 0x00, 0x45, 0x00, @@ -247,7 +247,7 @@ mod tests { 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x10, 0x00, 0x7b, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x10, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x52, 0x90, + 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x52, 0x90, ]; let pcap_file = std::io::Cursor::new(SAMPLE_PCAP_DATA); diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 0b4dff8d64..33f16742d8 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -214,7 +214,7 @@ mod tests { 0x62, 0x00, 0x00, 0x00, /* orig. packet len */ // start of ethernet packet ------------------- /*82*/ - 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0xb8, 0x27, 0xeb, 0x98, 0x94, 0xfa, 0x09, 0x00, 0x45, + 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0xb8, 0x27, 0xeb, 0x98, 0x94, 0xfa, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0xa0, 0x48, 0x40, 0x00, 0x40, 0x11, 0x29, 0x85, 0xac, 0x16, 0x0c, 0x4f, 0xac, 0x16, 0x0c, 0x50, // start of udp frame ------------------------- 0xc3, 0x50, 0xc3, 0x50, 0x00, 0x40, 0x8e, 0xe3, From f9b769b7935ef25f659fd1840b394f03e17f6016 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 31 Mar 2025 18:23:48 +0200 Subject: [PATCH 011/121] Change signatures for events --- .../apps/indexer/components/src/lib.rs | 5 +- .../apps/indexer/components/src/tys.rs | 3 +- .../apps/indexer/parsers/src/dlt/options.rs | 19 +++--- .../apps/indexer/parsers/src/someip.rs | 19 +++--- .../indexer/session/src/components/api.rs | 8 ++- .../indexer/session/src/components/mod.rs | 56 ++++++++++++++--- .../apps/indexer/stypes/bindings/options.ts | 60 +++++-------------- .../indexer/stypes/src/error/converting.rs | 35 +++++++++++ .../apps/indexer/stypes/src/options/mod.rs | 32 ++++++++-- .../spec/session.components.spec.ts | 9 +-- .../src/api/components.provider.ts | 58 ++++++++++++++---- .../platform/types/bindings/options.ts | 8 ++- 12 files changed, 218 insertions(+), 94 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index b24f514274..b7816b6df1 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -20,7 +20,7 @@ type Metadata<'a> = ( #[derive(Debug)] pub enum LazyLoadingResult { - Feilds(Vec), + Feilds(Vec), Cancelled, } @@ -36,6 +36,9 @@ impl LazyLoadingTaskMeta { pub fn contains(&self, fields: &[String]) -> bool { self.fields.iter().any(|id| fields.contains(id)) } + pub fn owner(&self) -> Uuid { + self.ident.uuid + } } pub struct LazyLoadingTask { diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 2fd4dc72bf..6ac91c20e8 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -7,7 +7,8 @@ pub type FieldsResult = Result, stypes::NativeError>; pub type FieldsGetter = fn(&stypes::SourceOrigin) -> FieldsResult; -pub type StaticFieldsResult = Result, stypes::NativeError>; +pub type StaticFieldResult = (String, Result); +pub type StaticFieldsResult = Result, stypes::NativeError>; pub type LazyFieldsTask = Pin + Send>>; pub type LazyFieldsTaskGetter = fn(&stypes::SourceOrigin, &CancellationToken) -> LazyFieldsTask; diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index d5c0eea3ad..4cac24688e 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -49,14 +49,17 @@ impl DltParser<'_> { return Ok(Vec::new()); } }; - Ok(vec![stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), - }]) + Ok(vec![( + FIELD_STATISTICS.to_owned(), + Ok(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }), + )]) }) } } diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip.rs index dff02eed3a..ed75265adc 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip.rs @@ -359,14 +359,17 @@ impl SomeipParser { return Ok(Vec::new()); } }; - Ok(vec![stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), - }]) + Ok(vec![( + FIELD_STATISTICS.to_owned(), + Ok(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }), + )]) }) } } diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index 766fc85662..fb7013bd5e 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -1,4 +1,4 @@ -use components::LazyLoadingResult; +use components::{LazyLoadingResult, LazyLoadingTaskMeta}; use stypes::{Ident, NativeError, SourceOrigin}; use tokio::sync::oneshot; use uuid::Uuid; @@ -6,7 +6,11 @@ use uuid::Uuid; #[derive(Debug)] pub enum Api { Shutdown(oneshot::Sender<()>), - LazyTaskComplite(Uuid, Result), + LazyTaskComplite( + Uuid, + LazyLoadingTaskMeta, + Result, + ), CancelLoading(Vec), GetOptions { parser: Uuid, diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 191a663c4e..da4ef28e55 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -84,6 +84,7 @@ impl ComponentsSession { task::spawn(async move { log_if_err(tx_api.send(Api::LazyTaskComplite( uuid, + source_loading_task.get_meta(), source_loading_task.wait().await, ))); }), @@ -102,6 +103,7 @@ impl ComponentsSession { task::spawn(async move { log_if_err(tx_api.send(Api::LazyTaskComplite( uuid, + parser_loading_task.get_meta(), parser_loading_task.wait().await, ))); }), @@ -110,22 +112,62 @@ impl ComponentsSession { } } // Delivery lazy fields to client. - Api::LazyTaskComplite(uuid, results) => { + Api::LazyTaskComplite(uuid, meta, results) => { tasks.remove(&uuid); match results { Ok(LazyLoadingResult::Feilds(fields)) => { - log_if_err( - tx_callback_events - .send(stypes::CallbackOptionsEvent::Options(fields)), - ); + let (success, fail): (Vec<_>, Vec<_>) = + fields.into_iter().partition(|(_, result)| result.is_ok()); + let fields: Vec = success + .into_iter() + .filter_map(|(_, field)| field.ok()) + .collect(); + let errors: Vec = fail + .into_iter() + .filter_map(|(id, err)| { + if let Err(err) = err { + Some(stypes::FieldLoadingError { id, err }) + } else { + None + } + }) + .collect(); + if !fields.is_empty() { + log_if_err(tx_callback_events.send( + stypes::CallbackOptionsEvent::LoadingDone { + owner: meta.owner(), + fields, + }, + )); + } + if !errors.is_empty() { + log_if_err(tx_callback_events.send( + stypes::CallbackOptionsEvent::LoadingErrors { + owner: meta.owner(), + errors, + }, + )); + } } Ok(..) => { // Task has been cancelled + log_if_err(tx_callback_events.send( + stypes::CallbackOptionsEvent::LoadingCancelled { + owner: meta.owner(), + fields: meta.fields, + }, + )); continue; } Err(err) => { - // TODO: Error should be delivered to client with bound field UUID - error!("Fail to load lazy field with: {err:?}"); + error!("Fail to load lazy field with: {err}"); + log_if_err(tx_callback_events.send( + stypes::CallbackOptionsEvent::LoadingError { + owner: meta.owner(), + error: err.to_string(), + fields: meta.fields, + }, + )); } } } diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 0287da9d22..30f3379554 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -3,51 +3,23 @@ /** * Represents events sent to the client. */ -export type CallbackOptionsEvent = - | { Options: Array } - | { Errors: [string, string][] } - | 'Destroyed'; - -export type ComponentsOptions = { source: Array; parser: Array }; - -export type Field = { id: string; value: Value }; - -export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; - -export type LazyFieldDesc = { id: string; name: string; desc: string }; - -export type StaticFieldDesc = { - id: string; - name: string; - desc: string; - required: boolean; - default: Value | null; - interface: ValueInterface; -}; - -export type Value = - | { Boolean: boolean } - | { Integer: number } - | { Float: number } - | { Text: string } - | { List: Array } - | { Directories: Array } - | { Files: Array } - | { File: string } - | { Directory: string } - | { KeyValues: { [key in string]?: Array } }; +export type CallbackOptionsEvent = { "LoadingDone": { owner: string, fields: Array, } } | { "LoadingErrors": { owner: string, errors: Array, } } | { "LoadingError": { owner: string, error: string, fields: Array, } } | { "LoadingCancelled": { owner: string, fields: Array, } } | "Destroyed"; + +export type ComponentsOptions = { source: Array, parser: Array, }; + +export type Field = { id: string, value: Value, }; + +export type FieldDesc = { "Static": StaticFieldDesc } | { "Lazy": LazyFieldDesc }; + +export type FieldLoadingError = { id: string, err: string, }; + +export type LazyFieldDesc = { id: string, name: string, desc: string, }; + +export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInterface, }; + +export type Value = { "Boolean": boolean } | { "Integer": number } | { "Float": number } | { "Text": string } | { "List": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyValues": { [key in string]?: Array } }; /** * How value will be represented to user */ -export type ValueInterface = - | 'Boolean' - | 'Integer' - | 'Float' - | 'Text' - | { DropList: Array } - | { List: Array } - | 'RequestDirectories' - | 'RequestFiles' - | 'RequestDirectory' - | 'RequestFile'; +export type ValueInterface = "Boolean" | "Integer" | "Float" | "Text" | { "DropList": Array } | { "List": Array } | "RequestDirectories" | "RequestFiles" | "RequestDirectory" | "RequestFile"; diff --git a/application/apps/indexer/stypes/src/error/converting.rs b/application/apps/indexer/stypes/src/error/converting.rs index de1c2cd0b3..0e948899a5 100644 --- a/application/apps/indexer/stypes/src/error/converting.rs +++ b/application/apps/indexer/stypes/src/error/converting.rs @@ -1,5 +1,40 @@ use crate::*; +impl std::fmt::Display for NativeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.message + .as_ref() + .map(|s| s.to_owned()) + .unwrap_or_else(|| format!("Error without message ({})", self.kind)) + ) + } +} + +impl std::fmt::Display for NativeErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Self::FileNotFound => "FileNotFound", + Self::UnsupportedFileType => "UnsupportedFileType", + Self::ComputationFailed => "ComputationFailed", + Self::Configuration => "Configuration", + Self::Interrupted => "Interrupted", + Self::OperationSearch => "OperationSearch", + Self::NotYetImplemented => "NotYetImplemented", + Self::ChannelError => "ChannelError", + Self::Io => "Io", + Self::Grabber => "Grabber", + Self::Plugins => "Plugins", + } + ) + } +} + impl From for NativeError { /// Converts a `std::io::Error` into a `NativeError`. /// diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 9b64e58209..4fa84944c7 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -136,6 +136,17 @@ pub enum ValueInterface { RequestFile, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct FieldLoadingError { + pub id: String, + pub err: String, +} + /// Represents events sent to the client. #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] @@ -146,11 +157,24 @@ pub enum ValueInterface { )] pub enum CallbackOptionsEvent { /// Triggered when lazy field is ready - Options(Vec), + LoadingDone { + owner: Uuid, + fields: Vec, + }, /// Triggered when a lazy fields wasn't loaded. - #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "[string, string][]"))] - Errors(Vec<(String, String)>), - + LoadingErrors { + owner: Uuid, + errors: Vec, + }, + LoadingError { + owner: Uuid, + error: String, + fields: Vec, + }, + LoadingCancelled { + owner: Uuid, + fields: Vec, + }, Destroyed, } diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts index a097330101..6d399078da 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -12,6 +12,7 @@ import { SourceOrigin, FieldDesc, LazyFieldDesc, StaticFieldDesc } from 'platfor import * as runners from './runners'; import { error } from 'platform/log/utils'; +import { LoadingDoneEvent } from '../src/api/components.provider'; const config = readConfigurationFile().get().tests.components; @@ -44,8 +45,8 @@ describe('Jobs', function () { } // Subscribe to events let received_lazy: StaticFieldDesc[] | undefined = undefined; - components.getEvents().Options.subscribe((fields: StaticFieldDesc[]) => { - received_lazy = fields; + components.getEvents().LoadingDone.subscribe((event: LoadingDoneEvent) => { + received_lazy = event.fields; }); // Request options scheme const fields = await components.getOptions( @@ -107,8 +108,8 @@ describe('Jobs', function () { } // Subscribe to events let received_lazy: StaticFieldDesc[] | undefined = undefined; - components.getEvents().Options.subscribe((fields: StaticFieldDesc[]) => { - received_lazy = fields; + components.getEvents().LoadingDone.subscribe((event: LoadingDoneEvent) => { + received_lazy = event.fields; }); // Request options scheme const fields = await components.getOptions( diff --git a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts index 97864a6e6c..2178a520d9 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts @@ -1,38 +1,68 @@ import { Subject } from 'platform/env/subscription'; import { Computation } from '../provider/provider'; -import { StaticFieldDesc } from 'platform/types/bindings'; +import { StaticFieldDesc, FieldLoadingError } from 'platform/types/bindings'; import * as protocol from 'protocol'; +export interface LoadingDoneEvent { + owner: string; + fields: StaticFieldDesc[]; +} + +export interface LoadingErrorsEvent { + owner: string; + errors: FieldLoadingError[]; +} + +export interface LoadingErrorEvent { + owner: string; + error: string; + fields: string[]; +} +export interface LoadingCancelledEvent { + owner: string; + fields: string[]; +} + export interface IComponentsEvents { - Options: Subject; - Errors: Subject<[string, string][]>; + LoadingDone: Subject; + LoadingErrors: Subject; + LoadingError: Subject; + LoadingCancelled: Subject; Destroyed: Subject; } export interface IComponentsEventsConvertors {} export interface IComponentsEventsSignatures { - Options: 'Options'; - Errors: 'Errors'; + LoadingDone: 'LoadingDone'; + LoadingErrors: 'LoadingErrors'; + LoadingError: 'LoadingError'; + LoadingCancelled: 'LoadingCancelled'; Destroyed: 'Destroyed'; } const SessionEventsSignatures: IComponentsEventsSignatures = { - Options: 'Options', - Errors: 'Errors', + LoadingDone: 'LoadingDone', + LoadingErrors: 'LoadingErrors', + LoadingError: 'LoadingError', + LoadingCancelled: 'LoadingCancelled', Destroyed: 'Destroyed', }; export interface IComponentsEventsInterfaces { - Options: { self: ['object', null] }; - Errors: { self: [typeof Array, null] }; + LoadingDone: { self: 'object'; owner: 'string'; fields: typeof Array }; + LoadingErrors: { self: 'object'; owner: 'string'; errors: typeof Array }; + LoadingError: { self: 'object'; owner: 'string'; error: 'string'; fields: typeof Array }; + LoadingCancelled: { self: 'object'; owner: 'string'; fields: typeof Array }; Destroyed: { self: null }; } const SessionEventsInterfaces: IComponentsEventsInterfaces = { - Options: { self: ['object', null] }, - Errors: { self: [Array, null] }, + LoadingDone: { self: 'object', owner: 'string', fields: Array }, + LoadingErrors: { self: 'object', owner: 'string', errors: Array }, + LoadingError: { self: 'object', owner: 'string', error: 'string', fields: Array }, + LoadingCancelled: { self: 'object', owner: 'string', fields: Array }, Destroyed: { self: null }, }; @@ -42,8 +72,10 @@ export class ComponentsEventProvider extends Computation< IComponentsEventsInterfaces > { private readonly _events: IComponentsEvents = { - Options: new Subject(), - Errors: new Subject<[string, string][]>(), + LoadingDone: new Subject(), + LoadingErrors: new Subject(), + LoadingError: new Subject(), + LoadingCancelled: new Subject(), Destroyed: new Subject(), }; diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 0287da9d22..3a02955c0b 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -4,8 +4,10 @@ * Represents events sent to the client. */ export type CallbackOptionsEvent = - | { Options: Array } - | { Errors: [string, string][] } + | { LoadingDone: { owner: string; fields: Array } } + | { LoadingErrors: { owner: string; errors: Array } } + | { LoadingError: { owner: string; error: string; fields: Array } } + | { LoadingCancelled: { owner: string; fields: Array } } | 'Destroyed'; export type ComponentsOptions = { source: Array; parser: Array }; @@ -14,6 +16,8 @@ export type Field = { id: string; value: Value }; export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; +export type FieldLoadingError = { id: string; err: string }; + export type LazyFieldDesc = { id: string; name: string; desc: string }; export type StaticFieldDesc = { From c613da8312b6486ecff2fab9fba4b1b67f670428 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 31 Mar 2025 18:38:14 +0200 Subject: [PATCH 012/121] Resolve rebase conflicts --- application/apps/indexer/parsers/src/dlt/mod.rs | 17 ----------------- application/apps/indexer/parsers/src/lib.rs | 4 +--- .../apps/rustcore/rs-bindings/Cargo.lock | 1 + 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 9d6b4ac5a8..4984736176 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -212,23 +212,6 @@ impl SingleParser for DltRangeParser { impl SingleParser for DltRawParser { const MIN_MSG_LEN: usize = MIN_MSG_LEN; -const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -]); - -impl components::Component for DltParser<'_> { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Parser"), - desc: String::from("DLT Parser"), - uuid: DLT_PARSER_UUID, - } - } - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { - Ok(()) - } -} - impl Parser for DltRangeParser { fn parse( &mut self, diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 5e49429911..6a5de806e4 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -5,11 +5,9 @@ pub mod someip; pub mod text; use serde::Serialize; -use std::{fmt::Display, future::Future, io::Write, iter}; +use std::{fmt::Display, io::Write, iter}; use thiserror::Error; -extern crate log; - #[derive(Error, Debug)] pub enum Error { #[error("Unrecoverable error, cannot continue: {0}")] diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 9fc56ea35f..fde11db9f0 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2510,6 +2510,7 @@ version = "0.1.0" dependencies = [ "anyhow", "blake3", + "components", "dir_checksum", "dirs", "log", From 05404caf875ddcf060c10672867d55f261d9ffa1 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 1 Apr 2025 14:57:03 +0200 Subject: [PATCH 013/121] Refactoring (switch to descriptor) --- .../apps/indexer/components/src/lib.rs | 176 ++++++------------ .../apps/indexer/components/src/tys.rs | 36 +++- .../apps/indexer/parsers/src/dlt/options.rs | 65 +++---- .../parsers/src/{someip.rs => someip/mod.rs} | 79 +------- .../indexer/parsers/src/someip/options.rs | 79 ++++++++ .../parsers/src/{text.rs => text/mod.rs} | 21 +-- .../apps/indexer/parsers/src/text/options.rs | 30 +++ .../plugins_host/src/parser_shared/mod.rs | 15 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 15 +- .../indexer/session/src/components/api.rs | 12 +- .../indexer/session/src/components/mod.rs | 122 +++++------- .../indexer/sources/src/binary/pcap/legacy.rs | 15 +- .../indexer/sources/src/binary/pcap/ng.rs | 15 +- .../apps/indexer/sources/src/binary/raw.rs | 15 +- .../indexer/sources/src/command/process.rs | 16 +- .../indexer/sources/src/serial/serialport.rs | 16 +- .../apps/indexer/sources/src/socket/tcp.rs | 21 ++- .../apps/indexer/sources/src/socket/udp.rs | 16 +- .../apps/indexer/stypes/src/observe/mod.rs | 10 + .../apps/indexer/stypes/src/options/mod.rs | 5 +- 20 files changed, 405 insertions(+), 374 deletions(-) rename application/apps/indexer/parsers/src/{someip.rs => someip/mod.rs} (93%) create mode 100644 application/apps/indexer/parsers/src/someip/options.rs rename application/apps/indexer/parsers/src/{text.rs => text/mod.rs} (85%) create mode 100644 application/apps/indexer/parsers/src/text/options.rs diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index b7816b6df1..3304a50637 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -7,17 +7,6 @@ pub use tys::*; use std::collections::HashMap; use uuid::Uuid; -type Metadata<'a> = ( - ( - &'a stypes::Ident, - &'a (Option, Option), - ), - ( - &'a stypes::Ident, - &'a (Option, Option), - ), -); - #[derive(Debug)] pub enum LazyLoadingResult { Feilds(Vec), @@ -148,132 +137,89 @@ impl Options { #[derive(Default)] pub struct Components { - //TODO: "hot" validators also here - options: HashMap, Option)>, - sources: HashMap, - parsers: HashMap, + components: HashMap>, } impl Components { - pub fn register( + pub fn register( &mut self, - ident: &stypes::Ident, - static_fields_handle: Option, - lazy_fields_handle: Option, + descriptor: D, ) -> Result<(), stypes::NativeError> { - if self.options.contains_key(&ident.uuid) { + let ident = descriptor.ident(); + if self.components.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.options - .insert(ident.uuid, (static_fields_handle, lazy_fields_handle)); - Ok(()) - } - - /// Register source metadata and set options getter - pub fn register_source( - &mut self, - ident: &stypes::Ident, - static_fields_handle: Option, - lazy_fields_handle: Option, - ) -> Result<(), stypes::NativeError> { - self.register(ident, static_fields_handle, lazy_fields_handle)?; - self.sources.insert(ident.uuid, ident.to_owned()); - Ok(()) - } - - /// Register parser metadata and set options getter - pub fn register_parser( - &mut self, - ident: &stypes::Ident, - static_fields_handle: Option, - lazy_fields_handle: Option, - ) -> Result<(), stypes::NativeError> { - self.register(ident, static_fields_handle, lazy_fields_handle)?; - self.parsers.insert(ident.uuid, ident.to_owned()); + self.components.insert(ident.uuid, Box::new(descriptor)); Ok(()) } - pub fn get_sources( + pub fn get_components( &self, + target: &stypes::ComponentType, _source_origin: stypes::SourceOrigin, ) -> Result, stypes::NativeError> { - // TODO: "ask" source based on origin - Ok(self.sources.values().cloned().collect()) - } - - pub fn get_parsers( - &self, - _source_origin: stypes::SourceOrigin, - ) -> Result, stypes::NativeError> { - // TODO: "ask" parser based on origin - Ok(self.parsers.values().cloned().collect()) + // TODO: "ask" source/parser based on origin + Ok(self + .components + .iter() + .filter_map(|(_, desc)| { + if desc.is_ty(target) { + Some(desc.ident()) + } else { + None + } + }) + .collect()) } pub fn get_options( &self, - source_origin: stypes::SourceOrigin, - source: Uuid, - parser: Uuid, - ) -> Result<(Options, Options), stypes::NativeError> { - let ( - (source_ident, (source_static, source_lazy)), - (parser_ident, (parser_static, parser_lazy)), - ) = self.get(&source, &parser)?; - let source_fields = source_static - .map(|getter| getter(&source_origin)) - .unwrap_or(Ok(Vec::new()))?; - let parser_fields = parser_static - .map(|getter| getter(&source_origin)) - .unwrap_or(Ok(Vec::new()))?; - let mut source_options = Options::new(source_fields); - let mut parser_options = Options::new(parser_fields); - // Check do we have some lazy settings - if let (Some(getter), true) = (source_lazy, source_options.has_lazy()) { - let tk = CancellationToken::new(); - source_options.set_lazy(source_ident.clone(), getter(&source_origin, &tk), &tk); + origin: stypes::SourceOrigin, + mut targets: Vec, + ) -> Result, stypes::NativeError> { + let descriptors: Vec<&Box> = self + .components + .iter() + .filter_map(|(uuid, desc)| { + if targets.contains(uuid) { + targets.retain(|v| v != uuid); + Some(desc) + } else { + None + } + }) + .collect(); + if !targets.is_empty() { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Fail to find: {}", + targets + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + )), + }); } - if let (Some(getter), true) = (parser_lazy, parser_options.has_lazy()) { - let tk = CancellationToken::new(); - parser_options.set_lazy(parser_ident.clone(), getter(&source_origin, &tk), &tk); + let mut list: Vec = Vec::new(); + for desc in descriptors.into_iter() { + let mut options = Options::new(desc.fields_getter(&origin)?); + if options.has_lazy() { + let cancel = CancellationToken::new(); + options.set_lazy( + desc.ident(), + desc.lazy_fields_getter(origin.clone(), cancel.clone()), + &cancel, + ); + } + list.push(options); } - Ok((source_options, parser_options)) - } - - fn get<'a>( - &'a self, - source: &Uuid, - parser: &Uuid, - ) -> Result, stypes::NativeError> { - let source_ident = self.sources.get(source).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Source {} not found", source)), - })?; - let source_getter = self.options.get(source).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Source options of {} ({}) not found", - source_ident.name, source - )), - })?; - let parser_ident = self.parsers.get(parser).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Parser {} not found", parser)), - })?; - let parser_getter = self.options.get(parser).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Parser options of {} ({}) not found", - parser_ident.name, parser - )), - })?; - Ok(((source_ident, source_getter), (parser_ident, parser_getter))) + Ok(list) } } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 6ac91c20e8..ef485b8746 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -5,17 +5,41 @@ use tokio_util::sync::CancellationToken; pub type FieldsResult = Result, stypes::NativeError>; -pub type FieldsGetter = fn(&stypes::SourceOrigin) -> FieldsResult; - pub type StaticFieldResult = (String, Result); + pub type StaticFieldsResult = Result, stypes::NativeError>; + pub type LazyFieldsTask = Pin + Send>>; -pub type LazyFieldsTaskGetter = fn(&stypes::SourceOrigin, &CancellationToken) -> LazyFieldsTask; pub trait Component { - /// Returns an identificator - fn ident() -> stypes::Ident; - /// Registration fn register(components: &mut Components) -> Result<(), stypes::NativeError>; } + +pub trait ComponentDescriptor { + fn ident(&self) -> stypes::Ident; + fn ty(&self) -> stypes::ComponentType; + fn is_ty(&self, ty: &stypes::ComponentType) -> bool { + &self.ty() == ty + } + #[allow(unused)] + fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { + Ok(Vec::new()) + } + #[allow(unused)] + fn lazy_fields_getter( + &self, + origin: stypes::SourceOrigin, + cancel: CancellationToken, + ) -> LazyFieldsTask { + Box::pin(async { Ok(Vec::new()) }) + } + #[allow(unused)] + fn validate( + &self, + origin: &stypes::SourceOrigin, + fields: &[stypes::Field], + ) -> HashMap { + HashMap::new() + } +} diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 4cac24688e..fcd8d0de18 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,3 +1,4 @@ +use components::ComponentDescriptor; use tokio::{ select, time::{sleep, Duration}, @@ -13,10 +14,11 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ const FIELD_LOG_LEVEL: &str = "log_level"; const FIELD_STATISTICS: &str = "statistics"; -impl DltParser<'_> { - pub fn fields( - _origin: &stypes::SourceOrigin, - ) -> Result, stypes::NativeError> { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { Ok(vec![ stypes::FieldDesc::Static(stypes::StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), @@ -33,18 +35,25 @@ impl DltParser<'_> { }), ]) } - pub fn lazy( - _origin: &stypes::SourceOrigin, - cancel: &CancellationToken, + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Parser"), + desc: String::from("DLT Parser"), + uuid: DLT_PARSER_UUID, + } + } + fn lazy_fields_getter( + &self, + _origin: stypes::SourceOrigin, + cancel: CancellationToken, ) -> components::LazyFieldsTask { - let tk: CancellationToken = cancel.clone(); Box::pin(async move { let duration = Duration::from_millis(5000); select! { _ = sleep(duration) => { // no cancelation }, - _ = tk.cancelled() => { + _ = cancel.cancelled() => { // cancelled return Ok(Vec::new()); } @@ -62,55 +71,25 @@ impl DltParser<'_> { )]) }) } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Parser + } } impl components::Component for DltParser<'_> { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Parser"), - desc: String::from("DLT Parser"), - uuid: DLT_PARSER_UUID, - } - } fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser( - &Self::ident(), - Some(DltParser::fields), - Some(DltParser::lazy), - )?; + components.register(Descriptor::default())?; Ok(()) } } -const DLT_RANGE_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -]); - impl components::Component for DltRangeParser { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Range Parser"), - desc: String::from("DLT Range Parser"), - uuid: DLT_RANGE_PARSER_UUID, - } - } fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } } -const DLT_RAW_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, -]); - impl components::Component for DltRawParser { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("DLT Raw Parser"), - desc: String::from("DLT Raw Parser"), - uuid: DLT_RAW_PARSER_UUID, - } - } fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { Ok(()) } diff --git a/application/apps/indexer/parsers/src/someip.rs b/application/apps/indexer/parsers/src/someip/mod.rs similarity index 93% rename from application/apps/indexer/parsers/src/someip.rs rename to application/apps/indexer/parsers/src/someip/mod.rs index ed75265adc..91d21bbe89 100644 --- a/application/apps/indexer/parsers/src/someip.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -1,4 +1,5 @@ -use crate::{Error, LogMessage, ParseYield, SingleParser}; +mod options; +use crate::{parse_all, Error, LogMessage, ParseYield, Parser}; use std::{ borrow::Cow, cmp::Ordering, @@ -20,11 +21,6 @@ use lazy_static::lazy_static; use log::{debug, error}; use regex::Regex; use serde::Serialize; -use tokio::{ - select, - time::{sleep, Duration}, -}; -use tokio_util::sync::CancellationToken; /// Marker for a column separator in the output string. const COLUMN_SEP: &str = "\u{0004}"; // EOT @@ -320,77 +316,6 @@ impl Parser for SomeipParser { } } -const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, -]); - -// TODO: fields IDs could be same for diff parser/source... on level of Options event -// we should also provide a master of field to prevent conflicts. -const FIELD_LOG_LEVEL: &str = "log_level"; -const FIELD_STATISTICS: &str = "statistics"; - -impl SomeipParser { - pub fn fields( - _origin: &stypes::SourceOrigin, - ) -> Result, stypes::NativeError> { - Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { - id: FIELD_LOG_LEVEL.to_owned(), - name: String::from("Log Level"), - desc: String::from("DLT Log Level"), - required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), - })]) - } - pub fn lazy( - _origin: &stypes::SourceOrigin, - cancel: &CancellationToken, - ) -> components::LazyFieldsTask { - let tk: CancellationToken = cancel.clone(); - Box::pin(async move { - // Sleep a little to emulate loading - let duration = Duration::from_millis(100); - select! { - _ = sleep(duration) => { - // no cancelation - }, - _ = tk.cancelled() => { - // cancelled - return Ok(Vec::new()); - } - }; - Ok(vec![( - FIELD_STATISTICS.to_owned(), - Ok(stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), - }), - )]) - }) - } -} -impl components::Component for SomeipParser { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("SomeIP Parser"), - desc: String::from("SomeIP Parser"), - uuid: SOMEIP_PARSER_UUID, - } - } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser( - &Self::ident(), - Some(SomeipParser::fields), - Some(SomeipParser::lazy), - )?; - Ok(()) - } -} - fn header_string(header: &Header) -> String { format!( "{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}{COLUMN_SEP}{}", diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs new file mode 100644 index 0000000000..a4efa6a9d1 --- /dev/null +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -0,0 +1,79 @@ +use super::SomeipParser; +use components::ComponentDescriptor; +use tokio::{ + select, + time::{sleep, Duration}, +}; +use tokio_util::sync::CancellationToken; + +const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, +]); + +// TODO: fields IDs could be same for diff parser/source... on level of Options event +// we should also provide a master of field to prevent conflicts. +const FIELD_LOG_LEVEL: &str = "log_level"; +const FIELD_STATISTICS: &str = "statistics"; + +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { + Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { + id: FIELD_LOG_LEVEL.to_owned(), + name: String::from("Log Level"), + desc: String::from("DLT Log Level"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + })]) + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("SomeIP Parser"), + desc: String::from("SomeIP Parser"), + uuid: SOMEIP_PARSER_UUID, + } + } + fn lazy_fields_getter( + &self, + _origin: stypes::SourceOrigin, + cancel: CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async move { + // Sleep a little to emulate loading + let duration = Duration::from_millis(100); + select! { + _ = sleep(duration) => { + // no cancelation + }, + _ = cancel.cancelled() => { + // cancelled + return Ok(Vec::new()); + } + }; + Ok(vec![( + FIELD_STATISTICS.to_owned(), + Ok(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Integer(1)), + interface: stypes::ValueInterface::DropList(Vec::new()), + }), + )]) + }) + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Parser + } +} + +impl components::Component for SomeipParser { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register(Descriptor::default())?; + Ok(()) + } +} diff --git a/application/apps/indexer/parsers/src/text.rs b/application/apps/indexer/parsers/src/text/mod.rs similarity index 85% rename from application/apps/indexer/parsers/src/text.rs rename to application/apps/indexer/parsers/src/text/mod.rs index 6dfc364f08..e6045393e2 100644 --- a/application/apps/indexer/parsers/src/text.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,4 +1,5 @@ -use crate::{Error, LogMessage, ParseYield, SingleParser}; +mod options; +use crate::{parse_all, Error, LogMessage, ParseYield, Parser}; use serde::Serialize; use std::{fmt, io::Write}; @@ -71,24 +72,6 @@ where } } -const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, -]); - -impl components::Component for StringTokenizer { - fn ident() -> stypes::Ident { - stypes::Ident { - name: String::from("Text Parser"), - desc: String::from("Text Parser"), - uuid: TEXT_PARSER_UUID, - } - } - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser(&Self::ident(), None, None)?; - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::Parser; diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/options.rs new file mode 100644 index 0000000000..9d24ab30f1 --- /dev/null +++ b/application/apps/indexer/parsers/src/text/options.rs @@ -0,0 +1,30 @@ +use components::ComponentDescriptor; + +use super::StringTokenizer; + +const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, +]); + +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("Text Parser"), + desc: String::from("Text Parser"), + uuid: TEXT_PARSER_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Parser + } +} + +impl components::Component for StringTokenizer { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register(Descriptor::default())?; + Ok(()) + } +} diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index a32d3e0384..921c6c5028 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -1,5 +1,6 @@ use std::path::{Path, PathBuf}; +use components::ComponentDescriptor; use stypes::{PluginInfo, SemanticVersion}; use wasmtime::component::Component; @@ -181,18 +182,26 @@ impl p::Parser for PluginsParser { } } -impl components::Component for PluginsParser { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { /// **ATTANTION** That's placeholder. Should be another way to delivery data - fn ident() -> stypes::Ident { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Plugin"), desc: String::from("Plugin"), uuid: uuid::Uuid::new_v4(), } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Parser + } +} +impl components::Component for PluginsParser { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 1a27c525d0..fc283b7acd 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -4,6 +4,7 @@ mod bindings; mod parser_plugin_state; +use components::ComponentDescriptor; use stypes::{ParserRenderOptions, RenderOptions, SemanticVersion}; use tokio::runtime::Handle; use wasmtime::{ @@ -170,18 +171,26 @@ impl p::Parser for PluginParser { } } -impl components::Component for PluginParser { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { /// **ATTANTION** That's placeholder. Should be another way to delivery data - fn ident() -> stypes::Ident { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Plugin Parser"), desc: String::from("Plugin Parser"), uuid: uuid::Uuid::new_v4(), } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Parser + } +} +impl components::Component for PluginParser { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_parser(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index fb7013bd5e..a5f0f2da9d 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -13,17 +13,13 @@ pub enum Api { ), CancelLoading(Vec), GetOptions { - parser: Uuid, - source: Uuid, origin: SourceOrigin, - tx: oneshot::Sender>, + targets: Vec, + tx: oneshot::Sender>, }, - GetSources( - SourceOrigin, - oneshot::Sender, NativeError>>, - ), - GetParsers( + GetComponents( SourceOrigin, + stypes::ComponentType, oneshot::Sender, NativeError>>, ), } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index da4ef28e55..878f43dafc 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -54,61 +54,47 @@ impl ComponentsSession { while let Some(msg) = rx_api.recv().await { match msg { Api::GetOptions { - parser, - source, origin, + targets, tx, } => { - let (source_options, parser_options) = - match components.get_options(origin, source, parser) { - Ok(options) => options, - Err(err) => { - log_if_err(tx.send(Err(err))); - continue; - } - }; + let mut options = match components.get_options(origin, targets) { + Ok(options) => options, + Err(err) => { + log_if_err(tx.send(Err(err))); + continue; + } + }; // Send static fields - log_if_err(tx.send(Ok(stypes::ComponentsOptions { - source: source_options.statics, - parser: parser_options.statics, - }))); - if let Some(mut source_loading_task) = source_options.lazy { - // If exists, request lazy source fields - let meta = source_loading_task.get_meta(); - let uuid = meta.uuid; - let tx_api = tx_api_inner.clone(); - tasks.insert( - uuid, - ( - meta, - task::spawn(async move { - log_if_err(tx_api.send(Api::LazyTaskComplite( - uuid, - source_loading_task.get_meta(), - source_loading_task.wait().await, - ))); - }), - ), - ); - } - if let Some(mut parser_loading_task) = parser_options.lazy { - // If exists, request lazy parser fields - let meta = parser_loading_task.get_meta(); - let uuid = meta.uuid; - let tx_api = tx_api_inner.clone(); - tasks.insert( - uuid, - ( - meta, - task::spawn(async move { - log_if_err(tx_api.send(Api::LazyTaskComplite( - uuid, - parser_loading_task.get_meta(), - parser_loading_task.wait().await, - ))); - }), - ), - ); + log_if_err( + tx.send(Ok(stypes::ComponentsOptionsList { + options: options + .iter_mut() + .map(|opt| opt.statics.drain(..).collect()) + .collect(), + })), + ); + // Loading lazy if exist + for opt in options.into_iter() { + if let Some(mut loading_task) = opt.lazy { + // If exists, request lazy source fields + let meta = loading_task.get_meta(); + let uuid = meta.uuid; + let tx_api = tx_api_inner.clone(); + tasks.insert( + uuid, + ( + meta, + task::spawn(async move { + log_if_err(tx_api.send(Api::LazyTaskComplite( + uuid, + loading_task.get_meta(), + loading_task.wait().await, + ))); + }), + ), + ); + } } } // Delivery lazy fields to client. @@ -171,11 +157,8 @@ impl ComponentsSession { } } } - Api::GetParsers(origin, tx) => { - log_if_err(tx.send(components.get_parsers(origin))); - } - Api::GetSources(origin, tx) => { - log_if_err(tx.send(components.get_sources(origin))); + Api::GetComponents(origin, ty, tx) => { + log_if_err(tx.send(components.get_components(&ty, origin))); } // Client doesn't need any more field data. Loading task should be cancelled Api::CancelLoading(fields) => { @@ -205,15 +188,13 @@ impl ComponentsSession { pub async fn get_options( &self, - source: Uuid, - parser: Uuid, + targets: Vec, origin: stypes::SourceOrigin, - ) -> Result { + ) -> Result { let (tx, rx) = oneshot::channel(); send( self.tx_api.send(Api::GetOptions { - parser, - source, + targets, origin, tx, }), @@ -222,28 +203,17 @@ impl ComponentsSession { response(rx.await, "Fail to get response from Api::GetOptions")? } - pub async fn get_sources( - &self, - origin: stypes::SourceOrigin, - ) -> Result, stypes::NativeError> { - let (tx, rx) = oneshot::channel(); - send( - self.tx_api.send(Api::GetSources(origin, tx)), - "Fail to send Api::GetSources", - )?; - response(rx.await, "Fail to get response from Api::GetSources")? - } - - pub async fn get_parsers( + pub async fn get_components( &self, origin: stypes::SourceOrigin, + ty: stypes::ComponentType, ) -> Result, stypes::NativeError> { let (tx, rx) = oneshot::channel(); send( - self.tx_api.send(Api::GetParsers(origin, tx)), + self.tx_api.send(Api::GetComponents(origin, ty, tx)), "Fail to send Api::GetParsers", )?; - response(rx.await, "Fail to get response from Api::GetParsers")? + response(rx.await, "Fail to get response from Api::GetComponents")? } pub fn abort(&self, fields: Vec) -> Result<(), stypes::NativeError> { diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index ca50169e12..c9c1870ebd 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -3,6 +3,7 @@ use crate::{ binary::pcap::debug_block, }; use bufread::DeqBuffer; +use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; @@ -159,17 +160,25 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, ]); -impl components::Component for PcapLegacyByteSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("PCAP Source"), desc: String::from("PCAP Source"), uuid: PCAP_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} +impl components::Component for PcapLegacyByteSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 33f16742d8..33b217e45e 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -3,6 +3,7 @@ use crate::{ binary::pcap::debug_block, }; use bufread::DeqBuffer; +use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; @@ -163,17 +164,25 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, ]); -impl components::Component for PcapngByteSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("PCAP NG Source"), desc: String::from("PCAP NG Source"), uuid: PCAPNG_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} +impl components::Component for PcapngByteSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 7c44e67f23..c43a1bc0cf 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -3,6 +3,7 @@ use crate::{ ReloadInfo, SourceFilter, }; use bufread::BufReader; +use components::ComponentDescriptor; use std::io::{BufRead, Read}; pub struct BinaryByteSource @@ -80,17 +81,25 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, ]); -impl components::Component for BinaryByteSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Binary Source"), desc: String::from("Binary Source"), uuid: BIN_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} +impl components::Component for BinaryByteSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index b0b07233dd..16a9ece530 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,5 +1,6 @@ use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; +use components::ComponentDescriptor; use regex::{Captures, Regex}; use shellexpand::tilde; use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; @@ -230,16 +231,25 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, ]); -impl components::Component for ProcessSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Terminal Source"), desc: String::from("Terminal Source"), uuid: TERM_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + +impl components::Component for ProcessSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index b2b65c1c74..fc3605d89d 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,6 +1,7 @@ use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; +use components::ComponentDescriptor; use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, @@ -220,16 +221,25 @@ const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, ]); -impl components::Component for SerialSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Serial Source"), desc: String::from("Serial Source"), uuid: SERIAL_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + +impl components::Component for SerialSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 1eded530f0..6bfcae5e56 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,4 +1,10 @@ +use crate::{ + socket::ReconnectStateMsg, ByteSource, Error as SourceError, ReloadInfo, SourceFilter, +}; +use bufread::DeqBuffer; +use components::ComponentDescriptor; use std::{net::SocketAddr, time::Duration}; +use tokio::{net::TcpStream, task::yield_now}; use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; @@ -172,16 +178,25 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, ]); -impl components::Component for TcpSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("TCP Source"), desc: String::from("TCP Source"), uuid: TCP_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + +impl components::Component for TcpSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index dc9bc637a6..924f3a6048 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -1,4 +1,5 @@ use bufread::DeqBuffer; +use components::ComponentDescriptor; use log::trace; use std::net::{IpAddr, Ipv4Addr}; use thiserror::Error; @@ -134,16 +135,25 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, ]); -impl components::Component for UdpSource { - fn ident() -> stypes::Ident { +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("UDP Source"), desc: String::from("UDP Source"), uuid: UDP_SOURCE_UUID, } } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + +impl components::Component for UdpSource { fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register_source(&Self::ident(), None, None)?; + components.register(Descriptor::default())?; Ok(()) } } diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index b685408ef9..269221fbe6 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -51,6 +51,16 @@ pub struct Ident { pub uuid: Uuid, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub enum ComponentType { + Parser, + Source, +} /// Multicast configuration information. /// - `multiaddr`: A valid multicast address. /// - `interface`: The address of the local interface used to join the multicast group. diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 4fa84944c7..c7c04486bb 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -18,9 +18,8 @@ use crate::*; derive(TS), ts(export, export_to = "options.ts") )] -pub struct ComponentsOptions { - pub source: Vec, - pub parser: Vec, +pub struct ComponentsOptionsList { + pub options: Vec>, } #[derive(Debug, Serialize, Deserialize, Clone)] From 650e682d592eae352e9b636b1835d6c66e98f7e1 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 1 Apr 2025 22:49:17 +0200 Subject: [PATCH 014/121] Update code on upper core levels --- .../apps/indexer/stypes/bindings/observe.ts | 234 ++++++++--------- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/bindings/plugins.ts | 238 ++++++++---------- .../apps/indexer/stypes/src/observe/mod.rs | 1 + .../apps/indexer/stypes/src/options/nodejs.rs | 2 +- application/apps/protocol/src/lib.rs | 3 +- .../rs-bindings/src/js/components/mod.rs | 37 +-- .../spec/session.components.spec.ts | 15 +- .../ts-bindings/src/api/components.ts | 14 +- .../src/native/native.components.ts | 88 +++---- .../platform/types/bindings/observe.ts | 3 +- .../platform/types/bindings/options.ts | 2 +- 12 files changed, 269 insertions(+), 370 deletions(-) diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 92fb270ea8..f426102e47 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,34 +1,35 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { PluginParserSettings } from './plugins'; +import type { PluginParserSettings } from "./plugins"; + +export type ComponentType = "Parser" | "Source"; /** * Settings for the DLT parser. */ -export type DltParserSettings = { - /** - * Configuration for filtering DLT messages. - */ - filter_config: DltFilterConfig; - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; - /** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ - with_storage_header: boolean; - /** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ - tz: string | null; -}; +export type DltParserSettings = { +/** + * Configuration for filtering DLT messages. + */ +filter_config: DltFilterConfig, +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, +/** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ +with_storage_header: boolean, +/** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ +tz: string | null, }; /** * Supported file formats for observation. */ -export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary' | 'ParserPlugin'; +export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; -export type Ident = { name: string; desc: string; uuid: string }; +export type Ident = { name: string, desc: string, uuid: string, }; export type IdentList = Array; @@ -38,151 +39,128 @@ export type IdentList = Array; * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string; interface: string | null }; +export type MulticastInfo = { multiaddr: string, interface: string | null, }; /** * Options for observing data within a session. */ -export type ObserveOptions = { - /** - * The description of the data source. - */ - origin: ObserveOrigin; - /** - * The parser configuration to be applied. - */ - parser: ParserType; -}; +export type ObserveOptions = { +/** + * The description of the data source. + */ +origin: ObserveOrigin, +/** + * The parser configuration to be applied. + */ +parser: ParserType, }; /** * Describes the source of data for observation. */ -export type ObserveOrigin = - | { File: [string, FileFormat, string] } - | { Concat: Array<[string, FileFormat, string]> } - | { Stream: [string, Transport] }; +export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = - | { Dlt: DltParserSettings } - | { SomeIp: SomeIpParserSettings } - | { Text: null } - | { Plugin: PluginParserSettings }; +export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { "Plugin": PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { - /** - * The working directory for the command. - */ - cwd: string; - /** - * The command to execute. - */ - command: string; - /** - * Environment variables. If empty, the default environment variables are used. - */ - envs: Map; -}; +export type ProcessTransportConfig = { +/** + * The working directory for the command. + */ +cwd: string, +/** + * The command to execute. + */ +command: string, +/** + * Environment variables. If empty, the default environment variables are used. + */ +envs: Map, }; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { - /** - * The path to the serial port. - */ - path: string; - /** - * The baud rate for the connection. - */ - baud_rate: number; - /** - * The number of data bits per frame. - */ - data_bits: number; - /** - * The flow control setting. - */ - flow_control: number; - /** - * The parity setting. - */ - parity: number; - /** - * The number of stop bits. - */ - stop_bits: number; - /** - * The delay in sending data, in milliseconds. - */ - send_data_delay: number; - /** - * Whether the connection is exclusive. - */ - exclusive: boolean; -}; +export type SerialTransportConfig = { +/** + * The path to the serial port. + */ +path: string, +/** + * The baud rate for the connection. + */ +baud_rate: number, +/** + * The number of data bits per frame. + */ +data_bits: number, +/** + * The flow control setting. + */ +flow_control: number, +/** + * The parity setting. + */ +parity: number, +/** + * The number of stop bits. + */ +stop_bits: number, +/** + * The delay in sending data, in milliseconds. + */ +send_data_delay: number, +/** + * Whether the connection is exclusive. + */ +exclusive: boolean, }; /** * Settings for the SomeIp parser. */ -export type SomeIpParserSettings = { - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; -}; +export type SomeIpParserSettings = { +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, }; -export type SourceOrigin = - | { File: string } - | { Files: Array } - | { Folder: string } - | { Folders: Array } - | { Stream: Ident }; +export type SourceOrigin = { "File": string } | { "Files": Array } | { "Folder": string } | { "Folders": Array } | { "Stream": Ident }; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { - /** - * The address to bind the TCP connection to. - */ - bind_addr: string; -}; +export type TCPTransportConfig = { +/** + * The address to bind the TCP connection to. + */ +bind_addr: string, }; /** * Describes the transport source for a session. */ -export type Transport = - | { Process: ProcessTransportConfig } - | { TCP: TCPTransportConfig } - | { UDP: UDPTransportConfig } - | { Serial: SerialTransportConfig }; +export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { - /** - * The address to bind the UDP connection to. - */ - bind_addr: string; - /** - * A list of multicast configurations. - */ - multicast: Array; -}; +export type UDPTransportConfig = { +/** + * The address to bind the UDP connection to. + */ +bind_addr: string, +/** + * A list of multicast configurations. + */ +multicast: Array, }; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { - /** - * A list of multicast addresses to listen on. - */ - multicast_addr: Array; -}; +export type UdpConnectionInfo = { +/** + * A list of multicast addresses to listen on. + */ +multicast_addr: Array, }; diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 30f3379554..58fc64544d 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -5,7 +5,7 @@ */ export type CallbackOptionsEvent = { "LoadingDone": { owner: string, fields: Array, } } | { "LoadingErrors": { owner: string, errors: Array, } } | { "LoadingError": { owner: string, error: string, fields: Array, } } | { "LoadingCancelled": { owner: string, fields: Array, } } | "Destroyed"; -export type ComponentsOptions = { source: Array, parser: Array, }; +export type ComponentsOptionsList = { options: Array>, }; export type Field = { id: string, value: Value, }; diff --git a/application/apps/indexer/stypes/bindings/plugins.ts b/application/apps/indexer/stypes/bindings/plugins.ts index fdd9107ec8..650b6b2a2d 100644 --- a/application/apps/indexer/stypes/bindings/plugins.ts +++ b/application/apps/indexer/stypes/bindings/plugins.ts @@ -3,54 +3,51 @@ /** * Represents the infos of a column that will be used in the render options. */ -export type ColumnInfo = { - /** - * Header title to be rendered on the top of the column in log view. - */ - caption: string; - /** - * Description to be shown as tooltip for the column. - */ - description: string; - /** - * Width of column (-1) for unlimited. - */ - width: number; -}; +export type ColumnInfo = { +/** + * Header title to be rendered on the top of the column in log view. + */ +caption: string, +/** + * Description to be shown as tooltip for the column. + */ +description: string, +/** + * Width of column (-1) for unlimited. + */ +width: number, }; /** * Represents the options needs to render columns information if they exist. */ -export type ColumnsRenderOptions = { - /** - * List of columns infos providing the needed information for each column in log view. - * - * Note: The count of this list must match the count of the column of each log message. - */ - columns: Array; - /** - * Minimum column width. - */ - min_width: number; - /** - * Maximum column width. - */ - max_width: number; -}; +export type ColumnsRenderOptions = { +/** + * List of columns infos providing the needed information for each column in log view. + * + * Note: The count of this list must match the count of the column of each log message. + */ +columns: Array, +/** + * Minimum column width. + */ +min_width: number, +/** + * Maximum column width. + */ +max_width: number, }; /** * Represents the informations of an invalid plugin. */ -export type InvalidPluginEntity = { - /** - * Directory path of the plugin. Qualify as ID for the plugin. - */ - dir_path: string; - /** - * Represents the plugin type. - */ - plugin_type: PluginType; -}; +export type InvalidPluginEntity = { +/** + * Directory path of the plugin. Qualify as ID for the plugin. + */ +dir_path: string, +/** + * Represents the plugin type. + */ +plugin_type: PluginType, }; /** * Represents a list of [`InvalidPluginEntity`]. @@ -60,165 +57,128 @@ export type InvalidPluginsList = Array; /** * Provides additional information to be rendered in the log view. */ -export type ParserRenderOptions = { - /** - * Rendering information for the column if log messages have multiple columns. - * - * # Note: - * The count of the provided columns must match the count of the columns of each log message as well. - */ - columns_options: ColumnsRenderOptions | null; -}; +export type ParserRenderOptions = { +/** + * Rendering information for the column if log messages have multiple columns. + * + * # Note: + * The count of the provided columns must match the count of the columns of each log message as well. + */ +columns_options: ColumnsRenderOptions | null, }; /** * General settings for all byte-sources as plugins */ -export type PluginByteSourceGeneralSettings = { placeholder: string }; +export type PluginByteSourceGeneralSettings = { placeholder: string, }; /** * Settings for the Plugin Byte-Sources. */ -export type PluginByteSourceSettings = { - plugin_path: string; - general_settings: PluginByteSourceGeneralSettings; - plugin_configs: Array; -}; +export type PluginByteSourceSettings = { plugin_path: string, general_settings: PluginByteSourceGeneralSettings, plugin_configs: Array, }; /** * Represents a configuration item, which includes an identifier and its corresponding value. */ -export type PluginConfigItem = { id: string; value: PluginConfigValue }; +export type PluginConfigItem = { id: string, value: PluginConfigValue, }; /** * Represents the schema for a configuration item. */ -export type PluginConfigSchemaItem = { - id: string; - title: string; - description: string | null; - input_type: PluginConfigSchemaType; -}; +export type PluginConfigSchemaItem = { id: string, title: string, description: string | null, input_type: PluginConfigSchemaType, }; /** * Defines the possible input types for configuration schemas. */ -export type PluginConfigSchemaType = - | { Boolean: boolean } - | { Integer: number } - | { Float: number } - | { Text: string } - | 'Directories' - | { Files: Array } - | { Dropdown: [Array, string] }; +export type PluginConfigSchemaType = { "Boolean": boolean } | { "Integer": number } | { "Float": number } | { "Text": string } | "Directories" | { "Files": Array } | { "Dropdown": [Array, string] }; /** * Represents the value of a configuration item. */ -export type PluginConfigValue = - | { Boolean: boolean } - | { Integer: number } - | { Float: number } - | { Text: string } - | { Directories: Array } - | { Files: Array } - | { Dropdown: string }; +export type PluginConfigValue = { "Boolean": boolean } | { "Integer": number } | { "Float": number } | { "Text": string } | { "Directories": Array } | { "Files": Array } | { "Dropdown": string }; /** * Represents an installed plugin entity informations and configurations. */ -export type PluginEntity = { - /** - * Directory path of the plugin. Qualify as ID for the plugin. - */ - dir_path: string; - /** - * Represents the plugin type. - */ - plugin_type: PluginType; - /** - * Include various information about the plugin. - */ - info: PluginInfo; - /** - * Provides Plugins Metadata from separate source than the plugin binary. - * Currently they are saved inside plugin `*.toml` file. - */ - metadata: PluginMetadata; - /** - * Path of the readme file for the plugin to be rendered on the front-end - */ - readme_path: string | null; -}; +export type PluginEntity = { +/** + * Directory path of the plugin. Qualify as ID for the plugin. + */ +dir_path: string, +/** + * Represents the plugin type. + */ +plugin_type: PluginType, +/** + * Include various information about the plugin. + */ +info: PluginInfo, +/** + * Provides Plugins Metadata from separate source than the plugin binary. + * Currently they are saved inside plugin `*.toml` file. + */ +metadata: PluginMetadata, +/** + * Path of the readme file for the plugin to be rendered on the front-end + */ +readme_path: string | null, }; /** * Contains the infos and options for a valid plugin. */ -export type PluginInfo = { - wasm_file_path: string; - api_version: SemanticVersion; - plugin_version: SemanticVersion; - config_schemas: Array; - render_options: RenderOptions; -}; +export type PluginInfo = { wasm_file_path: string, api_version: SemanticVersion, plugin_version: SemanticVersion, config_schemas: Array, render_options: RenderOptions, }; /** * Represents different levels of logging severity for a plugin. */ -export type PluginLogLevel = 'Err' | 'Warn' | 'Debug' | 'Info'; +export type PluginLogLevel = "Err" | "Warn" | "Debug" | "Info"; /** * Represents a log message generated by a plugin. */ -export type PluginLogMessage = { - /** - * The severity level of the log message. - */ - level: PluginLogLevel; - /** - * The timestamp of when the log message was generated, represented as a Unix timestamp. - */ - timestamp: bigint; - /** - * The actual content of the log message. - */ - msg: string; -}; +export type PluginLogMessage = { +/** + * The severity level of the log message. + */ +level: PluginLogLevel, +/** + * The timestamp of when the log message was generated, represented as a Unix timestamp. + */ +timestamp: bigint, +/** + * The actual content of the log message. + */ +msg: string, }; /** * Represents the plugins metadata like title, description... */ -export type PluginMetadata = { title: string; description: string | null }; +export type PluginMetadata = { title: string, description: string | null, }; /** * General settings for all parsers as plugins */ -export type PluginParserGeneralSettings = { placeholder: string }; +export type PluginParserGeneralSettings = { placeholder: string, }; /** * Settings for the Plugins parser. */ -export type PluginParserSettings = { - plugin_path: string; - general_settings: PluginParserGeneralSettings; - plugin_configs: Array; -}; +export type PluginParserSettings = { plugin_path: string, general_settings: PluginParserGeneralSettings, plugin_configs: Array, }; /** * Maintains the state of a plugin, including its log messages. * Represented as a struct (but not just a vector) to reserve * a space for a future fields. */ -export type PluginRunData = { - /** - * A collection of log messages associated with the plugin. - */ - logs: Array; -}; +export type PluginRunData = { +/** + * A collection of log messages associated with the plugin. + */ +logs: Array, }; /** * Represents plugins main types */ -export type PluginType = 'Parser' | 'ByteSource'; +export type PluginType = "Parser" | "ByteSource"; /** * Represents a list of [`PluginEntity`]. @@ -233,9 +193,9 @@ export type PluginsPathsList = Array; /** * Represents the render options (columns headers, etc.) for the plugins. */ -export type RenderOptions = { Parser: ParserRenderOptions } | 'ByteSource'; +export type RenderOptions = { "Parser": ParserRenderOptions } | "ByteSource"; /** * Represents the semantic version used in the plugins system. */ -export type SemanticVersion = { major: number; minor: number; patch: number }; +export type SemanticVersion = { major: number, minor: number, patch: number, }; diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 269221fbe6..00a2561a64 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -52,6 +52,7 @@ pub struct Ident { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[extend::encode_decode] #[cfg_attr( all(test, feature = "test_and_gen"), derive(TS), diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs index 3cf65d9e65..074485127b 100644 --- a/application/apps/indexer/stypes/src/options/nodejs.rs +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -6,4 +6,4 @@ try_into_js!(LazyFieldDesc); try_into_js!(Value); try_into_js!(ValueInterface); try_into_js!(CallbackOptionsEvent); -try_into_js!(ComponentsOptions); +try_into_js!(ComponentsOptionsList); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 5f8ad0489e..1d3a0f6b57 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -207,8 +207,9 @@ gen_encode_decode_fns!(ResultScaledDistribution); gen_encode_decode_fns!(DltLevelDistribution); gen_encode_decode_fns!(SourceOrigin); gen_encode_decode_fns!(IdentList); -gen_encode_decode_fns!(ComponentsOptions); +gen_encode_decode_fns!(ComponentsOptionsList); gen_encode_decode_fns!(FieldDesc); gen_encode_decode_fns!(LazyFieldDesc); gen_encode_decode_fns!(StaticFieldDesc); gen_encode_decode_fns!(CallbackOptionsEvent); +gen_encode_decode_fns!(ComponentType); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index c58140c2ee..84f7152f19 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -59,10 +59,12 @@ impl Components { } #[node_bindgen] - async fn get_sources( + async fn get_components( &self, origin: JSArrayBuffer, + ty: JSArrayBuffer, ) -> Result { + let ty = stypes::ComponentType::decode(&ty).map_err(stypes::ComputationError::Decoding)?; let origin = stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; let session = self @@ -70,25 +72,7 @@ impl Components { .as_ref() .ok_or(stypes::ComputationError::SessionUnavailable)?; session - .get_sources(origin) - .await - .map(|idents| idents.into()) - .map_err(stypes::ComputationError::NativeError) - } - - #[node_bindgen] - async fn get_parsers( - &self, - origin: JSArrayBuffer, - ) -> Result { - let origin = - stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; - let session = self - .session - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)?; - session - .get_parsers(origin) + .get_components(origin, ty) .await .map(|idents| idents.into()) .map_err(stypes::ComputationError::NativeError) @@ -97,12 +81,13 @@ impl Components { #[node_bindgen] async fn get_options( &self, - source: String, - parser: String, origin: JSArrayBuffer, - ) -> Result { - let source = Uuid::from_str(&source).map_err(|_| stypes::ComputationError::InvalidData)?; - let parser = Uuid::from_str(&parser).map_err(|_| stypes::ComputationError::InvalidData)?; + targets: Vec, + ) -> Result { + let targets: Vec = targets + .into_iter() + .map(|uuid| Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)) + .collect::>()?; let origin = stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; let session = self @@ -110,7 +95,7 @@ impl Components { .as_ref() .ok_or(stypes::ComputationError::SessionUnavailable)?; session - .get_options(source, parser, origin) + .get_options(targets, origin) .await .map_err(stypes::ComputationError::NativeError) } diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts index 6d399078da..3f34907264 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -49,12 +49,11 @@ describe('Jobs', function () { received_lazy = event.fields; }); // Request options scheme - const fields = await components.getOptions( + const fields = await components.getOptions(origin, [ TCP_SOURCE_UUID, DLT_PARSER_UUID, - origin, - ); - const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.parser.find( + ]); + const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.options[1].find( (field: FieldDesc) => 'Lazy' in field, ); if (!lazy_field) { @@ -112,14 +111,14 @@ describe('Jobs', function () { received_lazy = event.fields; }); // Request options scheme - const fields = await components.getOptions( + const fields = await components.getOptions(origin, [ TCP_SOURCE_UUID, DLT_PARSER_UUID, - origin, - ); - const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.parser.find( + ]); + const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.options[1].find( (field: FieldDesc) => 'Lazy' in field, ); + console.log(fields); if (!lazy_field) { return finish([components], done, new Error(`No lazy fields from DLT parser`)); } diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 7229210964..fe8d268f66 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -6,7 +6,7 @@ import { ComponentsEventProvider, IComponentsEvents } from '../api/components.pr import { SessionStream } from '../api/session.stream'; import { SessionSearch } from '../api/session.search'; import { Subscriber } from 'platform/env/subscription'; -import { ComponentsOptions, Ident, SourceOrigin } from 'platform/types/bindings'; +import { ComponentsOptionsList, Ident, SourceOrigin } from 'platform/types/bindings'; export { ISessionEvents, @@ -71,20 +71,16 @@ export class Components extends Subscriber { } { return { sources: (): Promise => { - return this.native.getSources(origin); + return this.native.getComponents(origin, 'Source'); }, parsers: (): Promise => { - return this.native.getParsers(origin); + return this.native.getComponents(origin, 'Parser'); }, }; } - public getOptions( - source: string, - parser: string, - origin: SourceOrigin, - ): Promise { - return this.native.getOptions(source, parser, origin); + public getOptions(origin: SourceOrigin, targets: string[]): Promise { + return this.native.getOptions(origin, targets); } public abort(fields: string[]): Error | undefined { diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 8097c9d76c..1de2e60825 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -1,7 +1,12 @@ import { scope } from 'platform/env/scope'; import { getNativeModule } from '../native/native'; import { Type, Source, NativeError } from '../interfaces/errors'; -import { SourceOrigin, IdentList, ComponentsOptions } from 'platform/types/bindings'; +import { + SourceOrigin, + IdentList, + ComponentsOptionsList, + ComponentType, +} from 'platform/types/bindings'; import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; import { Computation } from '../provider/provider'; @@ -19,15 +24,9 @@ export abstract class ComponentsNative { public abstract destroy(): Promise; - public abstract getSources(origin: Uint8Array): Promise; - - public abstract getParsers(origin: Uint8Array): Promise; + public abstract getComponents(origin: Uint8Array, ty: Uint8Array): Promise; - public abstract getOptions( - source: string, - parser: string, - origin: Uint8Array, - ): Promise; + public abstract getOptions(origin: Uint8Array, targets: string[]): Promise; public abstract abort(fields: string[]): void; } @@ -139,69 +138,48 @@ export class Base extends Subscriber { }); } - public getSources(origin: SourceOrigin): Promise { + public getComponents(origin: SourceOrigin, ty: ComponentType): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { return Promise.reject(err); } return new Promise((resolve, reject) => { - this.native.getSources(protocol.encodeSourceOrigin(origin)).then((buf: Uint8Array) => { - try { - resolve(protocol.decodeIdentList(buf)); - } catch (err) { - reject( - new NativeError( - new Error( - this.logger.error(`Fail to decode message: ${utils.error(err)}`), - ), - Type.InvalidOutput, - Source.IdentList, - ), - ); - } - }); - }); - } - - public getParsers(origin: SourceOrigin): Promise { - const err = this.getSessionAccessErr(); - if (err instanceof Error) { - return Promise.reject(err); - } - return new Promise((resolve, reject) => { - this.native.getParsers(protocol.encodeSourceOrigin(origin)).then((buf: Uint8Array) => { - try { - resolve(protocol.decodeIdentList(buf)); - } catch (err) { - reject( - new NativeError( - new Error( - this.logger.error(`Fail to decode message: ${utils.error(err)}`), + this.native + .getComponents( + protocol.encodeSourceOrigin(origin), + protocol.encodeComponentType(ty), + ) + .then((buf: Uint8Array) => { + try { + resolve(protocol.decodeIdentList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error( + `Fail to decode message: ${utils.error(err)}`, + ), + ), + Type.InvalidOutput, + Source.IdentList, ), - Type.InvalidOutput, - Source.IdentList, - ), - ); - } - }); + ); + } + }); }); } - public getOptions( - source: string, - parser: string, - origin: SourceOrigin, - ): Promise { + public getOptions(origin: SourceOrigin, targets: string[]): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { return Promise.reject(err); } return new Promise((resolve, reject) => { this.native - .getOptions(source, parser, protocol.encodeSourceOrigin(origin)) + .getOptions(protocol.encodeSourceOrigin(origin), targets) .then((buf: Uint8Array) => { try { - resolve(protocol.decodeComponentsOptions(buf)); + resolve(protocol.decodeComponentsOptionsList(buf)); } catch (err) { reject( new NativeError( diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index a19a307487..2451dad6b2 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -1,8 +1,9 @@ import { DltFilterConfig } from './dlt'; - // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { PluginParserSettings } from './plugins'; +export type ComponentType = 'Parser' | 'Source'; + /** * Settings for the DLT parser. */ diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 3a02955c0b..376c9d23ea 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -10,7 +10,7 @@ export type CallbackOptionsEvent = | { LoadingCancelled: { owner: string; fields: Array } } | 'Destroyed'; -export type ComponentsOptions = { source: Array; parser: Array }; +export type ComponentsOptionsList = { options: Array> }; export type Field = { id: string; value: Value }; From 6e848344a67b11decd3cc1d34583dd6448aa09bf Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 2 Apr 2025 12:58:47 +0200 Subject: [PATCH 015/121] Add basic docs and small renaming --- .../apps/indexer/components/src/lib.rs | 143 +-------- .../apps/indexer/components/src/scheme.rs | 299 ++++++++++++++++++ .../apps/indexer/components/src/tys.rs | 119 ++++++- .../indexer/session/src/components/api.rs | 58 +++- .../indexer/session/src/components/mod.rs | 144 ++++++++- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 6 +- .../spec/session.components.spec.ts | 13 +- .../platform/types/bindings/options.ts | 2 +- 9 files changed, 636 insertions(+), 150 deletions(-) create mode 100644 application/apps/indexer/components/src/scheme.rs diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 3304a50637..917ec5d4c7 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -1,139 +1,12 @@ +mod scheme; mod tys; -use tokio::select; -use tokio_util::sync::CancellationToken; -pub use tys::*; - use std::collections::HashMap; +use tokio_util::sync::CancellationToken; use uuid::Uuid; -#[derive(Debug)] -pub enum LazyLoadingResult { - Feilds(Vec), - Cancelled, -} - -#[derive(Debug, Clone)] -pub struct LazyLoadingTaskMeta { - pub uuid: Uuid, - pub ident: stypes::Ident, - pub cancel: CancellationToken, - pub fields: Vec, -} - -impl LazyLoadingTaskMeta { - pub fn contains(&self, fields: &[String]) -> bool { - self.fields.iter().any(|id| fields.contains(id)) - } - pub fn owner(&self) -> Uuid { - self.ident.uuid - } -} - -pub struct LazyLoadingTask { - meta: LazyLoadingTaskMeta, - task: Option, -} - -impl LazyLoadingTask { - pub fn new( - ident: stypes::Ident, - task: LazyFieldsTask, - cancel: &CancellationToken, - fields: Vec, - ) -> Self { - Self { - meta: LazyLoadingTaskMeta { - uuid: Uuid::new_v4(), - ident, - cancel: cancel.clone(), - fields, - }, - task: Some(task), - } - } - - pub fn get_meta(&self) -> LazyLoadingTaskMeta { - self.meta.clone() - } - - pub async fn wait(&mut self) -> Result { - let Some(task) = self.task.take() else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Settings already requested for {}", - self.meta.ident.name - )), - }); - }; - // TODO: cancel safe - select! { - _ = self.meta.cancel.cancelled() => { - // TODO: the question - shell we controll cancellation about parser/source, - // or we can deligate it? I think we should controll it above. - Ok(LazyLoadingResult::Cancelled) - } - fields = task => { - let fields = fields.map_err(|_| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::ChannelError, - message: Some(format!( - "Fail to get settings for {}", - self.meta.ident.name - )), - })?; - // Check one more time in case if cancel was catched first on level of parser/source - if self.meta.cancel.is_cancelled() { - Ok(LazyLoadingResult::Cancelled) - } else { - Ok(LazyLoadingResult::Feilds(fields)) - } - } - } - } -} - -pub struct Options { - pub statics: Vec, - pub lazy: Option, -} - -impl Options { - pub fn new(statics: Vec) -> Self { - Self { - statics, - lazy: None, - } - } - pub fn set_lazy( - &mut self, - ident: stypes::Ident, - task: LazyFieldsTask, - cancel: &CancellationToken, - ) { - self.lazy = Some(LazyLoadingTask::new(ident, task, cancel, self.lazy_uuids())); - } - pub fn has_lazy(&self) -> bool { - self.statics - .iter() - .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) - } - - fn lazy_uuids(&self) -> Vec { - self.statics - .iter() - .filter_map(|f| { - if let stypes::FieldDesc::Lazy(f) = f { - Some(f.id.to_owned()) - } else { - None - } - }) - .collect() - } -} +pub use scheme::*; +pub use tys::*; #[derive(Default)] pub struct Components { @@ -180,7 +53,7 @@ impl Components { &self, origin: stypes::SourceOrigin, mut targets: Vec, - ) -> Result, stypes::NativeError> { + ) -> Result, stypes::NativeError> { let descriptors: Vec<&Box> = self .components .iter() @@ -207,9 +80,9 @@ impl Components { )), }); } - let mut list: Vec = Vec::new(); + let mut list: HashMap = HashMap::new(); for desc in descriptors.into_iter() { - let mut options = Options::new(desc.fields_getter(&origin)?); + let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); if options.has_lazy() { let cancel = CancellationToken::new(); options.set_lazy( @@ -218,7 +91,7 @@ impl Components { &cancel, ); } - list.push(options); + list.insert(desc.ident().uuid, options); } Ok(list) } diff --git a/application/apps/indexer/components/src/scheme.rs b/application/apps/indexer/components/src/scheme.rs new file mode 100644 index 0000000000..070e7026c0 --- /dev/null +++ b/application/apps/indexer/components/src/scheme.rs @@ -0,0 +1,299 @@ +pub use crate::*; +use tokio::select; +use tokio_util::sync::CancellationToken; +use uuid::Uuid; + +/// Represents the result of a lazy field loading operation. +/// +/// This enum is used to capture the outcome of asynchronously loading field descriptions. +/// The result can either contain the loaded fields or indicate that the operation was cancelled. +/// +/// # Variants +/// +/// * `Fields(Vec)` - A vector of static field results successfully loaded. +/// * `Cancelled` - Indicates that the lazy loading operation was cancelled before completion. +#[derive(Debug)] +pub enum LazyLoadingResult { + Fields(Vec), + Cancelled, +} + +/// Metadata associated with a lazy loading operation for field schemas. +/// +/// This struct contains information required to manage and track the process of +/// lazy loading field descriptions. It includes a unique identifier, component +/// identification, a cancellation token, and a list of field identifiers to be loaded. +/// +/// # Fields +/// +/// * `uuid` - A unique identifier for the lazy loading operation. +/// * `ident` - An identifier of the component initiating the loading process. +/// * `cancel` - A cancellation token that can be used to prematurely terminate the loading task. +/// * `fields` - A list of field identifiers that will be loaded as part of this operation. +#[derive(Debug, Clone)] +pub struct LazyLoadingTaskMeta { + /// A unique identifier for the lazy loading operation. + pub uuid: Uuid, + /// An identifier of the component initiating the loading process. + pub ident: stypes::Ident, + /// A cancellation token that allows for premature termination of the loading task. + pub cancel: CancellationToken, + /// A list of field identifiers to be loaded during the operation. + pub fields: Vec, +} + +impl LazyLoadingTaskMeta { + /// Checks if the lazy loading task contains any of the specified field identifiers. + /// + /// This method iterates over the internal list of field identifiers and checks + /// whether any of them are present in the given slice of field identifiers. + /// + /// # Arguments + /// + /// * `fields` - A slice of `String` identifiers to check against the stored field list. + /// + /// # Returns + /// + /// * `true` if at least one of the given field identifiers is found in the task's field list. + /// * `false` otherwise. + pub fn contains(&self, fields: &[String]) -> bool { + self.fields.iter().any(|id| fields.contains(id)) + } + + /// Retrieves the unique identifier (UUID) of the component owner associated with this task. + /// + /// This method returns the UUID that uniquely identifies the component + /// responsible for initiating the lazy loading operation. + /// + /// # Returns + /// + /// * `Uuid` - The unique identifier of the component owner. + pub fn owner(&self) -> Uuid { + self.ident.uuid + } +} + +/// Represents a task for lazy loading of configuration schema fields. +/// +/// This struct encapsulates metadata and an optional asynchronous task related to the lazy loading +/// of settings. The lazy loading task is used when the component requires field data that needs +/// to be fetched asynchronously. +/// +/// # Fields +/// +/// * `meta` - Metadata related to the lazy loading operation, including task ID, component ID, +/// cancellation token, and the list of fields to be loaded. +/// * `task` - An optional asynchronous task responsible for retrieving the field data. +/// The task is wrapped in `Option` to allow for cases where it may not be initialized or has +/// already completed. +pub struct LazyLoadingTask { + /// Metadata related to the lazy loading operation. + meta: LazyLoadingTaskMeta, + /// An optional asynchronous task responsible for loading the fields. + task: Option, +} + +impl LazyLoadingTask { + /// Creates a new lazy loading task for retrieving configuration schema fields. + /// + /// This method initializes the task with metadata and an asynchronous task function. + /// The task can be canceled using the provided cancellation token. + /// + /// # Arguments + /// + /// * `ident` - An identifier of the component for which the settings are being loaded. + /// * `task` - The asynchronous task responsible for loading the fields. + /// * `cancel` - A reference to a cancellation token that can interrupt the loading process. + /// * `fields` - A vector of field identifiers to be lazily loaded. + /// + /// # Returns + /// + /// * `Self` - A new instance of `LazyLoadingTask`. + pub fn new( + ident: stypes::Ident, + task: LazyFieldsTask, + cancel: &CancellationToken, + fields: Vec, + ) -> Self { + Self { + meta: LazyLoadingTaskMeta { + uuid: Uuid::new_v4(), + ident, + cancel: cancel.clone(), + fields, + }, + task: Some(task), + } + } + + /// Retrieves the metadata associated with the lazy loading task. + /// + /// This method returns a cloned copy of the metadata, which includes the task's unique ID, + /// component identifier, cancellation token, and field list. + /// + /// # Returns + /// + /// * `LazyLoadingTaskMeta` - The metadata associated with the task. + pub fn get_meta(&self) -> LazyLoadingTaskMeta { + self.meta.clone() + } + + /// Executes the lazy loading task and waits for its completion. + /// + /// This asynchronous method either completes the field loading or detects task cancellation. + /// + /// # Returns + /// + /// * `Ok(LazyLoadingResult::Fields(Vec))` - If the fields were successfully loaded. + /// * `Ok(LazyLoadingResult::Cancelled)` - If the loading operation was cancelled. + /// * `Err(stypes::NativeError)` - If there was an error during the loading process. + /// + /// # Behavior + /// + /// The method uses asynchronous selection to await either the task's completion or a cancellation signal. + /// If the cancellation token signals cancellation before the task completes, the method returns + /// `LazyLoadingResult::Cancelled`. Otherwise, it returns the loaded fields. + /// + /// # Note + /// + /// The cancellation mechanism is designed to be safe and non-blocking. + /// The decision on whether to control cancellation at the parser/source level or delegate it + /// to a higher layer is still under consideration. + pub async fn wait(&mut self) -> Result { + let Some(task) = self.task.take() else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Settings already requested for {}", + self.meta.ident.name + )), + }); + }; + // TODO: cancel safe + select! { + _ = self.meta.cancel.cancelled() => { + // TODO: the question - shell we controll cancellation about parser/source, + // or we can deligate it? I think we should controll it above. + Ok(LazyLoadingResult::Cancelled) + } + fields = task => { + let fields = fields.map_err(|_| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::ChannelError, + message: Some(format!( + "Fail to get settings for {}", + self.meta.ident.name + )), + })?; + // Check one more time in case if cancel was catched first on level of parser/source + if self.meta.cancel.is_cancelled() { + Ok(LazyLoadingResult::Cancelled) + } else { + Ok(LazyLoadingResult::Fields(fields)) + } + } + } + } +} + +/// Represents the configuration schema of a component. +/// +/// This struct defines the structure of a component's settings, including both +/// static fields and an optional lazy loading task for fields that require asynchronous loading. +/// +/// # Fields +/// +/// * `spec` - A vector containing field descriptors that define the structure and types of settings. +/// * `lazy` - An optional task that handles the lazy loading of settings fields that are not immediately available. +pub struct OptionsScheme { + /// Describes all the fields in the component's configuration. + pub spec: Vec, + /// Contains a task responsible for the "lazy" loading of specific configuration fields. + pub lazy: Option, +} + +impl OptionsScheme { + /// Creates a new `OptionsScheme` with the given field descriptors. + /// + /// This method initializes a new instance of `OptionsScheme` with the specified static fields + /// and no lazy loading task. + /// + /// # Arguments + /// + /// * `spec` - A vector of field descriptors (`stypes::FieldDesc`) that define the component's settings. + /// + /// # Returns + /// + /// * `Self` - A new `OptionsScheme` instance. + pub fn new(spec: Vec) -> Self { + Self { spec, lazy: None } + } + + /// Sets a lazy loading task for the current options scheme. + /// + /// This method associates a lazy loading task with the current scheme, allowing for + /// asynchronous loading of configuration fields that are not immediately available. + /// + /// # Arguments + /// + /// * `ident` - An identifier of the component for which the lazy task is created. + /// * `task` - An asynchronous task responsible for fetching lazy field data. + /// * `cancel` - A cancellation token that can interrupt the lazy loading process. + pub fn set_lazy( + &mut self, + ident: stypes::Ident, + task: LazyFieldsTask, + cancel: &CancellationToken, + ) { + self.lazy = Some(LazyLoadingTask::new(ident, task, cancel, self.lazy_uuids())); + } + + /// Extracts and removes all field descriptors from the options scheme. + /// + /// This method drains the vector of field descriptors, effectively emptying it, + /// and returns the collected fields as a new vector. + /// + /// # Returns + /// + /// * `Vec` - A vector containing the extracted field descriptors. + pub fn extract_spec(&mut self) -> Vec { + self.spec.drain(..).collect::>() + } + + /// Checks whether the options scheme contains any lazy fields. + /// + /// This method iterates over the field descriptors and checks for any field that + /// is marked as lazy, indicating that it requires asynchronous loading. + /// + /// # Returns + /// + /// * `true` if the scheme contains at least one lazy field. + /// * `false` if all fields are static. + pub fn has_lazy(&self) -> bool { + self.spec + .iter() + .any(|f| matches!(f, stypes::FieldDesc::Lazy(..))) + } + + /// Retrieves a list of UUIDs for all lazy fields in the options scheme. + /// + /// This method filters the field descriptors to extract only the IDs of lazy fields + /// and returns them as a vector of strings. + /// + /// # Returns + /// + /// * `Vec` - A vector containing the UUIDs of lazy fields. + fn lazy_uuids(&self) -> Vec { + self.spec + .iter() + .filter_map(|f| { + if let stypes::FieldDesc::Lazy(f) = f { + Some(f.id.to_owned()) + } else { + None + } + }) + .collect() + } +} diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index ef485b8746..04837a6166 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -3,29 +3,130 @@ use std::{future::Future, pin::Pin}; use crate::*; use tokio_util::sync::CancellationToken; +/// A type alias for a result containing a vector of field descriptors or a native error. +/// +/// # Variants +/// +/// * `Ok(Vec)` - A vector containing field descriptors. +/// * `Err(stypes::NativeError)` - An error indicating a problem while retrieving the fields. pub type FieldsResult = Result, stypes::NativeError>; +/// A type alias representing the result of obtaining a single static field description. +/// +/// The tuple contains: +/// +/// * `String` - The name or identifier of the static field. +/// * `Result` - A result that contains either: +/// - `stypes::StaticFieldDesc` on success. +/// - A `String` representing an error message on failure. pub type StaticFieldResult = (String, Result); +/// A type alias for a result containing a vector of static field results or a native error. +/// +/// # Variants +/// +/// * `Ok(Vec)` - A vector containing static field results. +/// * `Err(stypes::NativeError)` - An error indicating a problem while retrieving static fields. pub type StaticFieldsResult = Result, stypes::NativeError>; +/// A type alias for an asynchronous task that yields a static fields result. +/// +/// Represents a future that will eventually produce a `StaticFieldsResult`. +/// The task is wrapped in a `Pin>`, allowing for asynchronous +/// execution while being safely pinned in memory. +/// +/// # Output +/// +/// * `StaticFieldsResult` - A result containing static field data or a native error. pub type LazyFieldsTask = Pin + Send>>; +/// A trait responsible for registering a component within the system. +/// +/// Registration of a component is performed only once during the application's lifetime. +/// Implementors of this trait define the mechanism for adding their component to the system registry. pub trait Component { - /// Registration + /// Registers the component in the system. + /// + /// This method is responsible for adding the component to the given collection of components. + /// It should be called only once per application runtime. + /// + /// # Arguments + /// + /// * `components` - A mutable reference to the system's component registry. + /// + /// # Returns + /// + /// * `Ok(())` if the registration was successful. + /// * `Err(stypes::NativeError)` if an error occurred during registration. fn register(components: &mut Components) -> Result<(), stypes::NativeError>; } +/// A trait that defines the characteristics and behavior of a component. +/// +/// Each component (such as a parser, source, etc.) must specify its type and identifier. +/// Additional parameters are optional, meaning a component might have no settings at all +/// or only static settings. Similarly, the validation procedure for settings is optional. +/// +/// This trait provides a standardized way to describe a component, including its type, +/// identifier, field getters, lazy field loaders, and optional validation logic. pub trait ComponentDescriptor { + /// Retrieves the unique identifier of the component. + /// + /// # Returns + /// + /// * `stypes::Ident` - The unique identifier for the component. fn ident(&self) -> stypes::Ident; + + /// Retrieves the type of the component. + /// + /// # Returns + /// + /// * `stypes::ComponentType` - The type of the component (e.g., parser, source). fn ty(&self) -> stypes::ComponentType; + + /// Checks if the component's type matches the given type. + /// + /// # Arguments + /// + /// * `ty` - A reference to the type to check against. + /// + /// # Returns + /// + /// * `true` if the component type matches the given type. + /// * `false` otherwise. fn is_ty(&self, ty: &stypes::ComponentType) -> bool { &self.ty() == ty } + + /// Retrieves the static field descriptors for the component. + /// + /// This method returns a list of field descriptors that are directly available without + /// requiring asynchronous loading. + /// + /// # Arguments + /// + /// * `origin` - The source origin related to the component. + /// + /// # Returns + /// + /// * `FieldsResult` - A result containing a vector of field descriptors or a native error. #[allow(unused)] fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { Ok(Vec::new()) } + + /// Retrieves the lazy loading task for fields that require asynchronous fetching. + /// + /// This method returns a future that asynchronously loads the component's fields. + /// + /// # Arguments + /// + /// * `origin` - The source origin from which the data will be loaded. + /// * `cancel` - A cancellation token that can interrupt the loading process. + /// + /// # Returns + /// + /// * `LazyFieldsTask` - An asynchronous task that will eventually yield the field descriptors. #[allow(unused)] fn lazy_fields_getter( &self, @@ -34,6 +135,22 @@ pub trait ComponentDescriptor { ) -> LazyFieldsTask { Box::pin(async { Ok(Vec::new()) }) } + + /// Validates the provided settings for the component. + /// + /// This method checks whether the given fields are valid according to the component's + /// configuration rules. Since validation is optional, the default implementation + /// returns an empty map, indicating no validation issues. + /// + /// # Arguments + /// + /// * `origin` - The source origin related to the component. + /// * `fields` - A slice of fields to be validated. + /// + /// # Returns + /// + /// * `HashMap` - A map where keys are field names and values are + /// error messages, if any. An empty map means no validation errors. #[allow(unused)] fn validate( &self, diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index a5f0f2da9d..80e558235a 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -3,20 +3,76 @@ use stypes::{Ident, NativeError, SourceOrigin}; use tokio::sync::oneshot; use uuid::Uuid; +/// Represents the various API commands for managing component sessions and operations. +/// +/// The `Api` enum defines different commands used within the system, including both public and internal APIs. +/// These commands handle session management, lazy loading operations, and communication between components and clients. #[derive(Debug)] pub enum Api { + /// Initiates the shutdown procedure for the component session controller. + /// + /// This API is public and can be accessed by the client. + /// Upon successful shutdown, it sends a confirmation signal through the provided `oneshot::Sender<()>`. Shutdown(oneshot::Sender<()>), - LazyTaskComplite( + + /// Triggered when the lazy loading of a component's configuration schema has completed. + /// + /// This API is internal and not accessible to clients. + /// + /// # Arguments + /// + /// * `Uuid` - A unique identifier of the component. + /// * `LazyLoadingTaskMeta` - Metadata associated with the lazy loading task. + /// * `Result` - The result of the lazy loading operation, indicating success or failure. + LazyTaskComplete( Uuid, LazyLoadingTaskMeta, Result, ), + + /// Cancels the ongoing lazy loading of configuration schemas for one or more components. + /// + /// This API is triggered by the client when a previously requested lazy loading operation needs to be stopped. + /// The client does not have knowledge of the internal task identifiers, so it provides a vector of field IDs. + /// The controller is responsible for identifying the corresponding loading tasks and canceling them. + /// + /// # Important + /// + /// The client does not receive any confirmation of the cancellation result. + /// + /// # Arguments + /// + /// * `Vec` - A vector of field IDs for which the loading tasks should be canceled. CancelLoading(Vec), + + /// Requests the configuration schema for the specified components. + /// + /// This API is used by the client to retrieve the settings of one or more components. + /// + /// # Arguments + /// + /// * `origin` - The source type within which the client intends to use the components. + /// * `targets` - A vector of component UUIDs whose configuration schemas are being requested. + /// * `tx` - A one-shot sender used to deliver the result back to the client. + /// + /// # Result + /// + /// * `Result` - The result containing the list of component options or an error. GetOptions { origin: SourceOrigin, targets: Vec, tx: oneshot::Sender>, }, + + /// Retrieves a list of components available in the system. + /// + /// This API is invoked by the client when it needs to get the list of available components of a specific type. + /// + /// # Arguments + /// + /// * `SourceOrigin` - The origin type indicating the context in which the components will be used. + /// * `stypes::ComponentType` - The type of components to retrieve (e.g., parser, source). + /// * `oneshot::Sender, NativeError>>` - A sender channel for delivering the result back to the client. GetComponents( SourceOrigin, stypes::ComponentType, diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 878f43dafc..1714678425 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -17,12 +17,36 @@ use tokio::{ }; use uuid::Uuid; +/// A controller responsible for managing all available components in the system (such as parsers, sources, etc.). +/// +/// This structure acts as a central registry that stores and manages the state of all components throughout +/// the entire application lifecycle. It is instantiated only once and remains active for the application's duration. +/// +/// # Fields +/// +/// * `tx_api` - An unbounded sender used for communicating with the API. It allows sending API-related messages +/// or commands to manage components or respond to client requests. pub struct ComponentsSession { tx_api: UnboundedSender, } impl ComponentsSession { - /// Starts a global components session + /// Creates a new components session. + /// + /// This method initializes the `ComponentsSession` and returns a tuple containing the session instance + /// and an unbounded receiver. The receiver acts as a feedback channel, allowing the delivery of + /// asynchronous operation results to the client. + /// + /// # Returns + /// + /// * `Ok((Self, UnboundedReceiver))` - A tuple containing the newly created + /// session and the receiver for asynchronous callbacks. + /// * `Err(stypes::NativeError)` - An error if the session could not be created. + /// + /// # Usage + /// + /// This method is asynchronous and should be awaited when called. The returned receiver can be used + /// to listen for callback events related to the components' operations. pub async fn new( ) -> Result<(Self, UnboundedReceiver), stypes::NativeError> { let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = @@ -70,12 +94,12 @@ impl ComponentsSession { tx.send(Ok(stypes::ComponentsOptionsList { options: options .iter_mut() - .map(|opt| opt.statics.drain(..).collect()) + .map(|(uuid, opt)| (*uuid, opt.extract_spec())) .collect(), })), ); // Loading lazy if exist - for opt in options.into_iter() { + for (_, opt) in options.into_iter() { if let Some(mut loading_task) = opt.lazy { // If exists, request lazy source fields let meta = loading_task.get_meta(); @@ -86,7 +110,7 @@ impl ComponentsSession { ( meta, task::spawn(async move { - log_if_err(tx_api.send(Api::LazyTaskComplite( + log_if_err(tx_api.send(Api::LazyTaskComplete( uuid, loading_task.get_meta(), loading_task.wait().await, @@ -98,10 +122,10 @@ impl ComponentsSession { } } // Delivery lazy fields to client. - Api::LazyTaskComplite(uuid, meta, results) => { + Api::LazyTaskComplete(uuid, meta, results) => { tasks.remove(&uuid); match results { - Ok(LazyLoadingResult::Feilds(fields)) => { + Ok(LazyLoadingResult::Fields(fields)) => { let (success, fail): (Vec<_>, Vec<_>) = fields.into_iter().partition(|(_, result)| result.is_ok()); let fields: Vec = success @@ -186,6 +210,26 @@ impl ComponentsSession { Ok((session, rx_callback_events)) } + /// Retrieves the configuration options for a list of components. + /// + /// This method sends an asynchronous request to obtain the settings of the specified components. + /// It uses the `Api::GetOptions` command to communicate with the underlying system. + /// + /// # Arguments + /// + /// * `targets` - A vector of UUIDs representing the components whose settings are being requested. + /// * `origin` - The source origin within which the components are being used. + /// + /// # Returns + /// + /// * `Ok(stypes::ComponentsOptionsList)` - The list of component configuration options. + /// * `Err(stypes::NativeError)` - An error if the request fails or the response is not received. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. pub async fn get_options( &self, targets: Vec, @@ -203,6 +247,26 @@ impl ComponentsSession { response(rx.await, "Fail to get response from Api::GetOptions")? } + /// Retrieves the list of available components of a specified type. + /// + /// This method sends an asynchronous request to get the list of components, filtered by type. + /// It uses the `Api::GetComponents` command to query the system. + /// + /// # Arguments + /// + /// * `origin` - The source origin within which the components are being used. + /// * `ty` - The type of components to retrieve (e.g., parser, source). + /// + /// # Returns + /// + /// * `Ok(Vec)` - A vector of component identifiers. + /// * `Err(stypes::NativeError)` - An error if the request fails or the response is not received. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. pub async fn get_components( &self, origin: stypes::SourceOrigin, @@ -216,6 +280,19 @@ impl ComponentsSession { response(rx.await, "Fail to get response from Api::GetComponents")? } + /// Aborts the lazy loading tasks for the specified fields. + /// + /// This method sends a cancellation request for ongoing lazy loading tasks associated with the given field IDs. + /// It uses the `Api::CancelLoading` command to perform the operation. + /// + /// # Arguments + /// + /// * `fields` - A vector of field IDs for which the lazy loading should be aborted. + /// + /// # Returns + /// + /// * `Ok(())` - If the cancellation request was successfully sent. + /// * `Err(stypes::NativeError)` - An error if the API request could not be sent. pub fn abort(&self, fields: Vec) -> Result<(), stypes::NativeError> { send( self.tx_api.send(Api::CancelLoading(fields)), @@ -223,6 +300,21 @@ impl ComponentsSession { ) } + /// Initiates the shutdown process for the components session. + /// + /// This method sends a shutdown command to gracefully terminate the session. + /// It uses the `Api::Shutdown` command to initiate the shutdown. + /// + /// # Returns + /// + /// * `Ok(())` - If the shutdown request was successfully sent and acknowledged. + /// * `Err(stypes::NativeError)` - An error if the shutdown process fails. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. pub async fn shutdown(&self) -> Result<(), stypes::NativeError> { let (tx, rx): (oneshot::Sender<()>, oneshot::Receiver<()>) = oneshot::channel(); send( @@ -233,11 +325,35 @@ impl ComponentsSession { } } +/// Logs an error if the given result contains an error. +/// +/// This function is a simple utility to reduce boilerplate when handling errors +/// that should be logged but do not require further processing. If the result is +/// an error, a log message will be recorded. +/// +/// # Arguments +/// +/// * `res` - A `Result` that may contain an error. If `Err`, it will be logged. fn log_if_err(res: Result<(), E>) { if res.is_err() { - error!("[Components] Fail to send responce to Api"); + error!("[Components] Fail to send response to Api"); } } + +/// Wraps the result of sending a message and converts a sending error into a native error. +/// +/// This function simplifies error handling when sending messages between asynchronous tasks. +/// It converts a `SendError` into a standardized `NativeError` format. +/// +/// # Arguments +/// +/// * `res` - A result from attempting to send a message via a channel. +/// * `msg` - A string slice providing context for the error message. +/// +/// # Returns +/// +/// * `Ok(())` - If the message was sent successfully. +/// * `Err(stypes::NativeError)` - If there was a failure during message sending. fn send>( res: Result<(), SendError>, msg: S, @@ -249,6 +365,20 @@ fn send>( }) } +/// Wraps the result of receiving a message and converts a receiving error into a native error. +/// +/// This function simplifies error handling when awaiting a response from an asynchronous channel. +/// It converts a `RecvError` into a standardized `NativeError` format with a detailed message. +/// +/// # Arguments +/// +/// * `res` - A result from awaiting a response from a channel. +/// * `msg` - A string slice providing context for the error message. +/// +/// # Returns +/// +/// * `Ok(T)` - The successfully received value. +/// * `Err(stypes::NativeError)` - If there was a failure during message reception. fn response>(res: Result, msg: S) -> Result { res.map_err(|e| stypes::NativeError { severity: stypes::Severity::ERROR, diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 58fc64544d..f58ca3a43d 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -5,7 +5,7 @@ */ export type CallbackOptionsEvent = { "LoadingDone": { owner: string, fields: Array, } } | { "LoadingErrors": { owner: string, errors: Array, } } | { "LoadingError": { owner: string, error: string, fields: Array, } } | { "LoadingCancelled": { owner: string, fields: Array, } } | "Destroyed"; -export type ComponentsOptionsList = { options: Array>, }; +export type ComponentsOptionsList = { options: Map, }; export type Field = { id: string, value: Value, }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index c7c04486bb..4c7f07441b 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -19,7 +19,11 @@ use crate::*; ts(export, export_to = "options.ts") )] pub struct ComponentsOptionsList { - pub options: Vec>, + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map") + )] + pub options: HashMap>, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts index 3f34907264..ed319fa282 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -53,7 +53,11 @@ describe('Jobs', function () { TCP_SOURCE_UUID, DLT_PARSER_UUID, ]); - const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.options[1].find( + const dlt_opts: FieldDesc[] | undefined = fields.options.get(DLT_PARSER_UUID); + if (!dlt_opts) { + return finish([components], done, new Error(`No fields from DLT parser`)); + } + const lazy_field: { Lazy: LazyFieldDesc } | undefined = dlt_opts.find( (field: FieldDesc) => 'Lazy' in field, ); if (!lazy_field) { @@ -115,10 +119,13 @@ describe('Jobs', function () { TCP_SOURCE_UUID, DLT_PARSER_UUID, ]); - const lazy_field: { Lazy: LazyFieldDesc } | undefined = fields.options[1].find( + const dlt_opts: FieldDesc[] | undefined = fields.options.get(DLT_PARSER_UUID); + if (!dlt_opts) { + return finish([components], done, new Error(`No fields from DLT parser`)); + } + const lazy_field: { Lazy: LazyFieldDesc } | undefined = dlt_opts.find( (field: FieldDesc) => 'Lazy' in field, ); - console.log(fields); if (!lazy_field) { return finish([components], done, new Error(`No lazy fields from DLT parser`)); } diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 376c9d23ea..ccedece8d7 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -10,7 +10,7 @@ export type CallbackOptionsEvent = | { LoadingCancelled: { owner: string; fields: Array } } | 'Destroyed'; -export type ComponentsOptionsList = { options: Array> }; +export type ComponentsOptionsList = { options: Map }; export type Field = { id: string; value: Value }; From 84c0277e42d15686b63451293e75962137f64c49 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 7 Apr 2025 14:35:36 +0200 Subject: [PATCH 016/121] Provide validation fields API --- .../apps/indexer/components/src/lib.rs | 14 +++++++ .../indexer/session/src/components/api.rs | 24 +++++++++++ .../indexer/session/src/components/mod.rs | 39 ++++++++++++++++++ .../apps/indexer/sources/src/socket/tcp.rs | 10 +---- .../apps/indexer/stypes/bindings/options.ts | 4 ++ .../apps/indexer/stypes/src/options/mod.rs | 32 +++++++++++---- .../apps/indexer/stypes/src/options/nodejs.rs | 1 + application/apps/protocol/src/lib.rs | 2 + .../rs-bindings/src/js/components/mod.rs | 22 ++++++++++ .../ts-bindings/src/api/components.ts | 16 +++++++- .../ts-bindings/src/interfaces/errors.ts | 1 + .../src/native/native.components.ts | 41 +++++++++++++++++++ .../platform/types/bindings/options.ts | 4 ++ 13 files changed, 193 insertions(+), 17 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 917ec5d4c7..b9f5149c24 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -95,4 +95,18 @@ impl Components { } Ok(list) } + + pub fn validate( + &self, + origin: &stypes::SourceOrigin, + target: &Uuid, + fields: &[stypes::Field], + ) -> Result, stypes::NativeError> { + let descriptor = self.components.get(target).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {target}")), + })?; + Ok(descriptor.validate(origin, fields)) + } } diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index 80e558235a..b709686a06 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use components::{LazyLoadingResult, LazyLoadingTaskMeta}; use stypes::{Ident, NativeError, SourceOrigin}; use tokio::sync::oneshot; @@ -78,4 +80,26 @@ pub enum Api { stypes::ComponentType, oneshot::Sender, NativeError>>, ), + /// Validates the configuration for correctness. + /// + /// # Arguments + /// + /// * `SourceOrigin` - The origin type indicating the context in which the validation is performed. + /// * `Uuid` - The identifier of the component (parser, source). + /// * `Vec` - A list of configuration field values. + /// * `oneshot::Sender, NativeError>>` - A sender channel for delivering validation results to the client. + /// + /// # Returns + /// + /// `HashMap`: + /// * Key (`String`) - The field's identifier. + /// * Value (`String`) - The error message related to the field. + /// + /// If all fields are valid and have no errors, an empty `HashMap` will be returned. + Validate( + SourceOrigin, + Uuid, + Vec, + oneshot::Sender, NativeError>>, + ), } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 1714678425..38892447a2 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -192,6 +192,9 @@ impl ComponentsSession { } } } + Api::Validate(origin, target, fields, tx) => { + log_if_err(tx.send(components.validate(&origin, &target, &fields))); + } Api::Shutdown(tx) => { // Cancel / kill pending tasks tasks.iter().for_each(|(_, (meta, handle))| { @@ -300,6 +303,42 @@ impl ComponentsSession { ) } + /// Asynchronously validates the configuration of a specific component. + /// + /// This method sends a validation request to the system and waits for the response asynchronously. + /// + /// # Arguments + /// + /// * `source` - The origin type indicating the context in which the validation is performed. + /// * `target` - The identifier of the component (parser, source). + /// * `fields` - A list of configuration field values to be validated. + /// + /// # Returns + /// + /// `Result, stypes::NativeError>`: + /// * On success: A `HashMap` where the key is the field's identifier (`String`) and the value is the error message (`String`). + /// * If all fields are valid and have no errors, an empty `HashMap` is returned. + /// * On failure: A `stypes::NativeError` indicating the reason for the validation failure. + /// + /// # Errors + /// + /// Returns an error if: + /// * The API call to validate the configuration fails to send. + /// * The response from the validation API cannot be retrieved. + pub async fn validate( + &self, + source: stypes::SourceOrigin, + target: Uuid, + fields: Vec, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::Validate(source, target, fields, tx)), + "Fail to send Api::Validate", + )?; + response(rx.await, "Fail to get response from Api::Validate")? + } + /// Initiates the shutdown process for the components session. /// /// This method sends a shutdown command to gracefully terminate the session. diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 6bfcae5e56..12995d8840 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,15 +1,9 @@ -use crate::{ - socket::ReconnectStateMsg, ByteSource, Error as SourceError, ReloadInfo, SourceFilter, -}; -use bufread::DeqBuffer; -use components::ComponentDescriptor; -use std::{net::SocketAddr, time::Duration}; -use tokio::{net::TcpStream, task::yield_now}; - use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; +use components::ComponentDescriptor; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; +use std::{net::SocketAddr, time::Duration}; use tokio::net::TcpStream; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index f58ca3a43d..cfa5072b2d 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -5,6 +5,8 @@ */ export type CallbackOptionsEvent = { "LoadingDone": { owner: string, fields: Array, } } | { "LoadingErrors": { owner: string, errors: Array, } } | { "LoadingError": { owner: string, error: string, fields: Array, } } | { "LoadingCancelled": { owner: string, fields: Array, } } | "Destroyed"; +export type ComponentOptions = { fields: Array, uuid: string, }; + export type ComponentsOptionsList = { options: Map, }; export type Field = { id: string, value: Value, }; @@ -13,6 +15,8 @@ export type FieldDesc = { "Static": StaticFieldDesc } | { "Lazy": LazyFieldDesc export type FieldLoadingError = { id: string, err: string, }; +export type FieldsValidationErrors = { errors: Map, }; + export type LazyFieldDesc = { id: string, name: string, desc: string, }; export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInterface, }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 4c7f07441b..99bb20a4b0 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -2,14 +2,6 @@ mod nodejs; use crate::*; -// TODOs and open topics -// ## Validation of fields? -// Probably parser/source traites has to be update to have validation method -// -// ## Settings actions (like DLT stats)? -// Problematic places: -// - dlt stat -// - envvars (terminal command) #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] @@ -26,6 +18,30 @@ pub struct ComponentsOptionsList { pub options: HashMap>, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct FieldsValidationErrors { + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] + pub errors: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct ComponentOptions { + pub fields: Vec, + pub uuid: Uuid, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs index 074485127b..e0a2f58d35 100644 --- a/application/apps/indexer/stypes/src/options/nodejs.rs +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -7,3 +7,4 @@ try_into_js!(Value); try_into_js!(ValueInterface); try_into_js!(CallbackOptionsEvent); try_into_js!(ComponentsOptionsList); +try_into_js!(FieldsValidationErrors); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 1d3a0f6b57..dfbdc379b5 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -213,3 +213,5 @@ gen_encode_decode_fns!(LazyFieldDesc); gen_encode_decode_fns!(StaticFieldDesc); gen_encode_decode_fns!(CallbackOptionsEvent); gen_encode_decode_fns!(ComponentType); +gen_encode_decode_fns!(FieldsValidationErrors); +gen_encode_decode_fns!(ComponentOptions); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 84f7152f19..66587d09f4 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -100,6 +100,28 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + #[node_bindgen] + async fn validate( + &self, + origin: JSArrayBuffer, + options: JSArrayBuffer, + ) -> Result { + let origin = + stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let options = stypes::ComponentOptions::decode(&options) + .map_err(stypes::ComputationError::Decoding)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + Ok(stypes::FieldsValidationErrors { + errors: session + .validate(origin, options.uuid, options.fields) + .await + .map_err(stypes::ComputationError::NativeError)?, + }) + } + #[node_bindgen] fn abort(&self, fields: Vec) -> Result<(), stypes::ComputationError> { let session = self diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index fe8d268f66..d28cd5e650 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -6,7 +6,13 @@ import { ComponentsEventProvider, IComponentsEvents } from '../api/components.pr import { SessionStream } from '../api/session.stream'; import { SessionSearch } from '../api/session.search'; import { Subscriber } from 'platform/env/subscription'; -import { ComponentsOptionsList, Ident, SourceOrigin } from 'platform/types/bindings'; +import { + ComponentsOptionsList, + Field, + FieldsValidationErrors, + Ident, + SourceOrigin, +} from 'platform/types/bindings'; export { ISessionEvents, @@ -83,6 +89,14 @@ export class Components extends Subscriber { return this.native.getOptions(origin, targets); } + public validate( + origin: SourceOrigin, + target: string, + fields: Field[], + ): Promise { + return this.native.validate(origin, target, fields); + } + public abort(fields: string[]): Error | undefined { return this.native.abort(fields); } diff --git a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts index 225358c7cb..ba3d61e458 100644 --- a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts +++ b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts @@ -74,6 +74,7 @@ export enum Source { GetIndexedLen = 'GetIndexedLen', getAroundIndexes = 'getAroundIndexes', ComponentsOptions = 'ComponentsOptions', + ComponentsValidate = 'ComponentsValidate', IdentList = 'IdentList', Native = 'Native', Other = 'Other', diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 1de2e60825..f828b87d0a 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -6,6 +6,9 @@ import { IdentList, ComponentsOptionsList, ComponentType, + Field, + ComponentOptions, + FieldsValidationErrors, } from 'platform/types/bindings'; import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; @@ -28,6 +31,8 @@ export abstract class ComponentsNative { public abstract getOptions(origin: Uint8Array, targets: string[]): Promise; + public abstract validate(origin: Uint8Array, fields: Uint8Array): Promise; + public abstract abort(fields: string[]): void; } @@ -197,6 +202,42 @@ export class Base extends Subscriber { }); } + public validate( + origin: SourceOrigin, + target: string, + fields: Field[], + ): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + const options: ComponentOptions = { fields: fields, uuid: target }; + return new Promise((resolve, reject) => { + this.native + .validate( + protocol.encodeSourceOrigin(origin), + protocol.encodeComponentOptions(options), + ) + .then((buf: Uint8Array) => { + try { + resolve(protocol.decodeFieldsValidationErrors(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error( + `Fail to decode message: ${utils.error(err)}`, + ), + ), + Type.InvalidOutput, + Source.ComponentsValidate, + ), + ); + } + }); + }); + } + public abort(fields: string[]): Error | undefined { const err = this.getSessionAccessErr(); if (err instanceof Error) { diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index ccedece8d7..a14a8c7522 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -10,6 +10,8 @@ export type CallbackOptionsEvent = | { LoadingCancelled: { owner: string; fields: Array } } | 'Destroyed'; +export type ComponentOptions = { fields: Array; uuid: string }; + export type ComponentsOptionsList = { options: Map }; export type Field = { id: string; value: Value }; @@ -18,6 +20,8 @@ export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; export type FieldLoadingError = { id: string; err: string }; +export type FieldsValidationErrors = { errors: Map }; + export type LazyFieldDesc = { id: string; name: string; desc: string }; export type StaticFieldDesc = { From ed57a17c23f8fb30275a532f91cca3aec7c1408b Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 8 Apr 2025 19:30:51 +0200 Subject: [PATCH 017/121] Update types for options --- .../apps/indexer/parsers/src/dlt/options.rs | 10 +- .../indexer/parsers/src/someip/options.rs | 10 +- .../apps/indexer/stypes/bindings/options.ts | 9 +- .../apps/indexer/stypes/src/options/mod.rs | 97 ++++++++++--------- .../apps/indexer/stypes/src/options/nodejs.rs | 2 +- .../ts-bindings/src/native/remarks.txt | 52 ---------- .../platform/types/bindings/options.ts | 44 +++++---- 7 files changed, 93 insertions(+), 131 deletions(-) delete mode 100644 application/apps/rustcore/ts-bindings/src/native/remarks.txt diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index fcd8d0de18..f1e060e3a7 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use components::ComponentDescriptor; use tokio::{ select, @@ -25,8 +27,8 @@ impl ComponentDescriptor for Descriptor { name: String::from("Log Level"), desc: String::from("DLT Log Level"), required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), }), stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { id: FIELD_STATISTICS.to_owned(), @@ -65,8 +67,8 @@ impl ComponentDescriptor for Descriptor { name: String::from("Example"), desc: String::from("Example"), required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), }), )]) }) diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index a4efa6a9d1..73adc52bc9 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use super::SomeipParser; use components::ComponentDescriptor; use tokio::{ @@ -25,8 +27,8 @@ impl ComponentDescriptor for Descriptor { name: String::from("Log Level"), desc: String::from("DLT Log Level"), required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), })]) } fn ident(&self) -> stypes::Ident { @@ -60,8 +62,8 @@ impl ComponentDescriptor for Descriptor { name: String::from("Example"), desc: String::from("Example"), required: true, - default: Some(stypes::Value::Integer(1)), - interface: stypes::ValueInterface::DropList(Vec::new()), + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), }), )]) }) diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index cfa5072b2d..bd5d3d2241 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -19,11 +19,8 @@ export type FieldsValidationErrors = { errors: Map, }; export type LazyFieldDesc = { id: string, name: string, desc: string, }; -export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInterface, }; +export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInput, }; -export type Value = { "Boolean": boolean } | { "Integer": number } | { "Float": number } | { "Text": string } | { "List": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyValues": { [key in string]?: Array } }; +export type Value = { "Boolean": boolean } | { "Number": bigint } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyStrings": { [key in string]?: Array } } | { "KeyString": { [key in string]?: string } } | { "KeyNumbers": { [key in string]?: Array } } | { "KeyNumber": { [key in string]?: bigint } }; -/** - * How value will be represented to user - */ -export type ValueInterface = "Boolean" | "Integer" | "Float" | "Text" | { "DropList": Array } | { "List": Array } | "RequestDirectories" | "RequestFiles" | "RequestDirectory" | "RequestFile"; +export type ValueInput = "Checkbox" | "Number" | "String" | { "Numbers": Array } | { "Strings": Array } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 99bb20a4b0..3118c6c4f1 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -66,7 +66,7 @@ pub struct StaticFieldDesc { pub desc: String, pub required: bool, pub default: Option, - pub interface: ValueInterface, + pub interface: ValueInput, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -101,30 +101,36 @@ pub struct Field { derive(TS), ts(export, export_to = "options.ts") )] -pub enum Value { - // Bool value field - Boolean(bool), - // Integer field - Integer(i32), - // Float number field - Float(f32), - // Text field - Text(String), - // List with value - List(Vec), - // List of folders - Directories(Vec), - // List of files - Files(Vec), - // Path to file - File(PathBuf), - // Path to directory - Directory(PathBuf), - // Keys with multiple values per key - KeyValues(HashMap>), +pub enum ValueInput { + Checkbox, + Number, + String, + Numbers(Vec), + Strings(Vec), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] + KeyNumber(HashMap), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map") + )] + KeyNumbers(HashMap>), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] + KeyString(HashMap), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map") + )] + KeyStrings(HashMap>), + Directories, + Files, + File, + Directory, + Bound { + output: Box, + inputs: Vec, + }, } -/// How value will be represented to user #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( @@ -132,27 +138,30 @@ pub enum Value { derive(TS), ts(export, export_to = "options.ts") )] -pub enum ValueInterface { - // Bool value field - Boolean, - // Integer field - Integer, - // Float number field - Float, - // Text field - Text, - // Dropdown list with known values - DropList(Vec), - // List with known values - List(Vec), - // Request to get list of folders - RequestDirectories, - // Request to get list of files - RequestFiles, - // Request to get folder's path - RequestDirectory, - // Request to get a file's path - RequestFile, +pub enum Value { + Boolean(bool), + Number(i64), + Numbers(Vec), + String(String), + Strings(Vec), + Directories(Vec), + Files(Vec), + File(PathBuf), + Directory(PathBuf), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] + KeyNumber(HashMap), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map") + )] + KeyNumbers(HashMap>), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] + KeyString(HashMap), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map") + )] + KeyStrings(HashMap>), } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs index e0a2f58d35..7678486399 100644 --- a/application/apps/indexer/stypes/src/options/nodejs.rs +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -4,7 +4,7 @@ try_into_js!(FieldDesc); try_into_js!(StaticFieldDesc); try_into_js!(LazyFieldDesc); try_into_js!(Value); -try_into_js!(ValueInterface); +try_into_js!(ValueInput); try_into_js!(CallbackOptionsEvent); try_into_js!(ComponentsOptionsList); try_into_js!(FieldsValidationErrors); diff --git a/application/apps/rustcore/ts-bindings/src/native/remarks.txt b/application/apps/rustcore/ts-bindings/src/native/remarks.txt deleted file mode 100644 index e41a33ed63..0000000000 --- a/application/apps/rustcore/ts-bindings/src/native/remarks.txt +++ /dev/null @@ -1,52 +0,0 @@ -12-11-2021 -- add time measurements for JS side too and compare it with Rust scope - -e.g. show text file content - -1. create session => RustSession - - -// TODO: -- rename computation to something like Emitter or EventsStore or EventSource or EventProvider etc... -- rename channel to just session. - -// Possible events -{ Progress: { uuid: string, ticks: { ... }}}, where uuid - reference to async operation -{ Done: { uuid: string, status: finished | canceled }} - - -// -//-js-------------------------> Rust: - -let text_session = new RustSession(emitter callback); - - ----------------------------> rust-session-channel - -JS -> RUST: assign file to session -// rust will build index asynchronically - - ----------------------------> rust-session-channel.file = "path/to/textfile.txt" - ----------------------------> rust is indexing - // [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx] - // [xxxxx] => report progress 100 rows from 1000 rows - //... - JS <- RUST: Event::Progress (callback(PROGRESS, (100, 1000))) - ... - JS -> RUST: grabStreamChunk(20, 10) - JS <- RUST: content [20..30] - JS -> RUST: grabStreamChunk(900, 10) - JS <- RUST: Error(content not yet available) - ... - JS <- RUST: Event::Progress (callback(PROGRESS, 1000, 1000)) - JS <- RUST: Event::Finished (callback(FINISHED)) - ...fully initialized - - JS -> RUST: grabStreamChunk(900, 10) - JS <- RUST: content [900..910] - ... - Js -> RUST: setSearch(filters) - // Rust: session-file: Path, index-of-file, search-filters: [String], index-of-search-results - RUST -> RipGrep: create search result file & index - JS <- RUST: Event::Progress (callback(SEARCH_PROGRESS, 40)) - - diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index a14a8c7522..683122b4b5 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -30,32 +30,36 @@ export type StaticFieldDesc = { desc: string; required: boolean; default: Value | null; - interface: ValueInterface; + interface: ValueInput; }; export type Value = | { Boolean: boolean } - | { Integer: number } - | { Float: number } - | { Text: string } - | { List: Array } + | { Number: bigint } + | { Numbers: Array } + | { String: string } + | { Strings: Array } | { Directories: Array } | { Files: Array } | { File: string } | { Directory: string } - | { KeyValues: { [key in string]?: Array } }; + | { KeyStrings: { [key in string]?: Array } } + | { KeyString: { [key in string]?: string } } + | { KeyNumbers: { [key in string]?: Array } } + | { KeyNumber: { [key in string]?: bigint } }; -/** - * How value will be represented to user - */ -export type ValueInterface = - | 'Boolean' - | 'Integer' - | 'Float' - | 'Text' - | { DropList: Array } - | { List: Array } - | 'RequestDirectories' - | 'RequestFiles' - | 'RequestDirectory' - | 'RequestFile'; +export type ValueInput = + | 'Checkbox' + | 'Number' + | 'String' + | { Numbers: Array } + | { Strings: Array } + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map } + | 'Directories' + | 'Files' + | 'File' + | 'Directory' + | { Bound: { output: ValueInput; inputs: Array } }; From 697adf53e082369010f737052dd3d23080888f63 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 9 Apr 2025 14:38:57 +0200 Subject: [PATCH 018/121] Binding for all layers --- .../src/api/components.provider.ts | 27 +--- .../ts-bindings/src/api/components.ts | 28 ++-- .../client/src/app/register/services.ts | 4 + .../client/src/app/service/components.ts | 153 ++++++++++++++++++ application/client/src/app/service/ilc.ts | 2 + application/holder/src/service/components.ts | 101 +++++++++++- .../holder/src/service/components/abort.ts | 24 +++ .../src/service/components/get_options.ts | 25 +++ .../src/service/components/get_parsers.ts | 26 +++ .../src/service/components/get_sources.ts | 26 +++ .../holder/src/service/components/index.ts | 5 + .../holder/src/service/components/validate.ts | 24 +++ application/holder/src/service/sessions.ts | 2 + .../ipc/event/components/destroyed.ts | 6 + .../platform/ipc/event/components/index.ts | 5 + .../ipc/event/components/loading_cancelled.ts | 17 ++ .../ipc/event/components/loading_done.ts | 17 ++ .../ipc/event/components/loading_error.ts | 17 ++ .../ipc/event/components/loading_errors.ts | 17 ++ application/platform/ipc/event/index.ts | 1 + .../platform/ipc/request/components/abort.ts | 20 +++ .../ipc/request/components/get_options.ts | 31 ++++ .../ipc/request/components/get_parsers.ts | 29 ++++ .../ipc/request/components/get_sources.ts | 29 ++++ .../platform/ipc/request/components/index.ts | 5 + .../ipc/request/components/validate.ts | 33 ++++ application/platform/ipc/request/index.ts | 1 + application/platform/package.json | 1 + application/platform/types/components.ts | 21 +++ application/platform/types/index.ts | 1 + 30 files changed, 657 insertions(+), 41 deletions(-) create mode 100644 application/client/src/app/service/components.ts create mode 100644 application/holder/src/service/components/abort.ts create mode 100644 application/holder/src/service/components/get_options.ts create mode 100644 application/holder/src/service/components/get_parsers.ts create mode 100644 application/holder/src/service/components/get_sources.ts create mode 100644 application/holder/src/service/components/index.ts create mode 100644 application/holder/src/service/components/validate.ts create mode 100644 application/platform/ipc/event/components/destroyed.ts create mode 100644 application/platform/ipc/event/components/index.ts create mode 100644 application/platform/ipc/event/components/loading_cancelled.ts create mode 100644 application/platform/ipc/event/components/loading_done.ts create mode 100644 application/platform/ipc/event/components/loading_error.ts create mode 100644 application/platform/ipc/event/components/loading_errors.ts create mode 100644 application/platform/ipc/request/components/abort.ts create mode 100644 application/platform/ipc/request/components/get_options.ts create mode 100644 application/platform/ipc/request/components/get_parsers.ts create mode 100644 application/platform/ipc/request/components/get_sources.ts create mode 100644 application/platform/ipc/request/components/index.ts create mode 100644 application/platform/ipc/request/components/validate.ts create mode 100644 application/platform/types/components.ts diff --git a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts index 2178a520d9..805bc57d20 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts @@ -1,29 +1,14 @@ import { Subject } from 'platform/env/subscription'; import { Computation } from '../provider/provider'; -import { StaticFieldDesc, FieldLoadingError } from 'platform/types/bindings'; +import { + LoadingDoneEvent, + LoadingErrorsEvent, + LoadingErrorEvent, + LoadingCancelledEvent, +} from 'platform/types/components'; import * as protocol from 'protocol'; -export interface LoadingDoneEvent { - owner: string; - fields: StaticFieldDesc[]; -} - -export interface LoadingErrorsEvent { - owner: string; - errors: FieldLoadingError[]; -} - -export interface LoadingErrorEvent { - owner: string; - error: string; - fields: string[]; -} -export interface LoadingCancelledEvent { - owner: string; - fields: string[]; -} - export interface IComponentsEvents { LoadingDone: Subject; LoadingErrors: Subject; diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index d28cd5e650..0605cb5169 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -3,30 +3,16 @@ import { scope } from 'platform/env/scope'; import { v4 as uuid } from 'uuid'; import { Base as Native } from '../native/native.components'; import { ComponentsEventProvider, IComponentsEvents } from '../api/components.provider'; -import { SessionStream } from '../api/session.stream'; -import { SessionSearch } from '../api/session.search'; import { Subscriber } from 'platform/env/subscription'; import { ComponentsOptionsList, Field, + FieldDesc, FieldsValidationErrors, Ident, SourceOrigin, } from 'platform/types/bindings'; -export { - ISessionEvents, - IProgressEvent, - IProgressState, - IEventMapUpdated, - IEventMatchesUpdated, - IEventIndexedMapUpdated, -} from '../api/session.provider'; -export { ComponentsEventProvider, SessionStream, SessionSearch }; - -export * as $ from 'platform/types/observe'; -export * as Factory from 'platform/types/observe/factory'; - export class Components extends Subscriber { private readonly native: Native; private readonly provider: ComponentsEventProvider; @@ -85,16 +71,20 @@ export class Components extends Subscriber { }; } - public getOptions(origin: SourceOrigin, targets: string[]): Promise { - return this.native.getOptions(origin, targets); + public getOptions(origin: SourceOrigin, targets: string[]): Promise> { + return this.native + .getOptions(origin, targets) + .then((list: ComponentsOptionsList) => list.options); } public validate( origin: SourceOrigin, target: string, fields: Field[], - ): Promise { - return this.native.validate(origin, target, fields); + ): Promise> { + return this.native + .validate(origin, target, fields) + .then((list: FieldsValidationErrors) => list.errors); } public abort(fields: string[]): Error | undefined { diff --git a/application/client/src/app/register/services.ts b/application/client/src/app/register/services.ts index 5ab9a4233f..16659fa8e0 100644 --- a/application/client/src/app/register/services.ts +++ b/application/client/src/app/register/services.ts @@ -30,6 +30,10 @@ export const services: { [key: string]: Inputs } = { name: 'Jobs', uuid: v4(), }, + components: { + name: 'Components', + uuid: v4(), + }, files: { name: 'Files', uuid: v4(), diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts new file mode 100644 index 0000000000..b6210b2a23 --- /dev/null +++ b/application/client/src/app/service/components.ts @@ -0,0 +1,153 @@ +import { SetupService, Interface, Implementation, register } from '@platform/entity/service'; +import { services } from '@register/services'; +import { Subjects, Subject } from '@platform/env/subscription'; +import { + LoadingDoneEvent, + LoadingCancelledEvent, + LoadingErrorEvent, + LoadingErrorsEvent, +} from '@platform/types/components'; + +import * as Events from '@platform/ipc/event/index'; +import * as Requests from '@platform/ipc/request/index'; + +import { Field, FieldDesc, Ident, SourceOrigin } from '@platform/types/bindings'; + +@SetupService(services['components']) +export class Service extends Implementation { + public subjects: Subjects<{ + LoadingDone: Subject; + LoadingErrors: Subject; + LoadingError: Subject; + LoadingCancelled: Subject; + }> = new Subjects({ + LoadingDone: new Subject(), + LoadingErrors: new Subject(), + LoadingError: new Subject(), + LoadingCancelled: new Subject(), + }); + + public override ready(): Promise { + this.register( + Events.IpcEvent.subscribe( + Events.Components.LoadingDone.Event, + (event) => { + this.subjects.get().LoadingDone.emit(event.event); + }, + ), + Events.IpcEvent.subscribe( + Events.Components.LoadingErrors.Event, + (event) => { + this.subjects.get().LoadingErrors.emit(event.event); + }, + ), + Events.IpcEvent.subscribe( + Events.Components.LoadingError.Event, + (event) => { + this.subjects.get().LoadingError.emit(event.event); + }, + ), + Events.IpcEvent.subscribe( + Events.Components.LoadingCancelled.Event, + (event) => { + this.subjects.get().LoadingCancelled.emit(event.event); + }, + ), + ); + return Promise.resolve(); + } + + public override destroy(): Promise { + this.unsubscribe(); + return Promise.resolve(); + } + + public abort(fields: string[]): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.Abort.Response, + new Requests.Components.Abort.Request({ + fields, + }), + ) + .then((_) => { + resolve(); + }) + .catch(reject); + }); + } + + public getOptions(origin: SourceOrigin, targets: string[]): Promise> { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetOptions.Response, + new Requests.Components.GetOptions.Request({ + origin, + targets, + }), + ) + .then((response) => { + resolve(response.options); + }) + .catch(reject); + }); + } + + public validate( + origin: SourceOrigin, + target: string, + fields: Field[], + ): Promise> { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.Validate.Response, + new Requests.Components.Validate.Request({ + origin, + target, + fields, + }), + ) + .then((response) => { + resolve(response.errors); + }) + .catch(reject); + }); + } + public get(origin: SourceOrigin): { + sources(): Promise; + parsers(): Promise; + } { + return { + sources: (): Promise => { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetSources.Response, + new Requests.Components.GetSources.Request({ + origin, + }), + ) + .then((response) => { + resolve(response.list); + }) + .catch(reject); + }); + }, + parsers: (): Promise => { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetParsers.Response, + new Requests.Components.GetParsers.Request({ + origin, + }), + ) + .then((response) => { + resolve(response.list); + }) + .catch(reject); + }); + }, + }; + } +} +export interface Service extends Interface {} +export const components = register(new Service()); diff --git a/application/client/src/app/service/ilc.ts b/application/client/src/app/service/ilc.ts index 81f1b08768..812cf718b5 100644 --- a/application/client/src/app/service/ilc.ts +++ b/application/client/src/app/service/ilc.ts @@ -14,6 +14,7 @@ import { Logger } from '@platform/log'; import { session, Session, UnboundTab } from '@service/session'; import { state } from '@service/state'; import { jobs } from '@service/jobs'; +import { components } from '@service/components'; import { popup } from '@ui/service/popup'; import { notifications } from '@ui/service/notifications'; import { contextmenu } from '@ui/service/contextmenu'; @@ -71,6 +72,7 @@ export interface IlcInterface { @DependOn(favorites) @DependOn(sys) @DependOn(plugins) +@DependOn(components) // UI services @DependOn(sidebar) @DependOn(toolbar) diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index 06e6050d4e..b76e52794d 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -7,9 +7,108 @@ import { } from 'platform/entity/service'; import { services } from '@register/services'; import { electron } from '@service/electron'; +import { Components } from 'rustcore'; +import { Mutable } from 'platform/types/unity/mutable'; + +import * as RequestHandlers from './components/index'; +import * as Requests from 'platform/ipc/request'; +import * as Events from 'platform/ipc/event'; + +import { + LoadingCancelledEvent, + LoadingDoneEvent, + LoadingErrorEvent, + LoadingErrorsEvent, +} from 'platform/types/components'; @DependOn(electron) @SetupService(services['components']) -export class Service extends Implementation {} +export class Service extends Implementation { + public readonly components!: Components; + + public override async ready(): Promise { + (this as Mutable).components = await Components.create(); + this.register( + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.Abort.Request, + RequestHandlers.Abort.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetOptions.Request, + RequestHandlers.GetOptions.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetParsers.Request, + RequestHandlers.GetParsers.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetSources.Request, + RequestHandlers.GetSources.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.Validate.Request, + RequestHandlers.Validate.handler, + ), + this.components.getEvents().LoadingDone.subscribe((event: LoadingDoneEvent) => { + Events.IpcEvent.emit( + new Events.Components.LoadingDone.Event({ + event, + }), + ); + }), + this.components.getEvents().LoadingError.subscribe((event: LoadingErrorEvent) => { + Events.IpcEvent.emit( + new Events.Components.LoadingError.Event({ + event, + }), + ); + }), + this.components.getEvents().LoadingErrors.subscribe((event: LoadingErrorsEvent) => { + Events.IpcEvent.emit( + new Events.Components.LoadingErrors.Event({ + event, + }), + ); + }), + this.components + .getEvents() + .LoadingCancelled.subscribe((event: LoadingCancelledEvent) => { + Events.IpcEvent.emit( + new Events.Components.LoadingCancelled.Event({ + event, + }), + ); + }), + this.components.getEvents().Destroyed.subscribe(() => { + // Stop getting incoming requests + this.unsubscribe(); + }), + ); + + return Promise.resolve(); + } + + public override destroy(): Promise { + this.unsubscribe(); + return this.components.destroy().catch((err: Error) => { + this.log().error(`Fail to shutdown Components: ${err.message}`); + }); + } +} export interface Service extends Interface {} export const components = register(new Service()); diff --git a/application/holder/src/service/components/abort.ts b/application/holder/src/service/components/abort.ts new file mode 100644 index 0000000000..767cda0524 --- /dev/null +++ b/application/holder/src/service/components/abort.ts @@ -0,0 +1,24 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.Abort.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.Abort.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + const error = components.components.abort(request.fields); + if (error instanceof Error) { + reject(error); + } else { + resolve(new Requests.Components.Abort.Response()); + } + }); + }, +); diff --git a/application/holder/src/service/components/get_options.ts b/application/holder/src/service/components/get_options.ts new file mode 100644 index 0000000000..dc67d048c8 --- /dev/null +++ b/application/holder/src/service/components/get_options.ts @@ -0,0 +1,25 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { FieldDesc } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetOptions.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetOptions.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .getOptions(request.origin, request.targets) + .then((options: Map) => { + resolve(new Requests.Components.GetOptions.Response({ options })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/get_parsers.ts b/application/holder/src/service/components/get_parsers.ts new file mode 100644 index 0000000000..34ad96b8ba --- /dev/null +++ b/application/holder/src/service/components/get_parsers.ts @@ -0,0 +1,26 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { Ident } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetParsers.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetParsers.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .get(request.origin) + .parsers() + .then((list: Ident[]) => { + resolve(new Requests.Components.GetParsers.Response({ list })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/get_sources.ts b/application/holder/src/service/components/get_sources.ts new file mode 100644 index 0000000000..6157e54320 --- /dev/null +++ b/application/holder/src/service/components/get_sources.ts @@ -0,0 +1,26 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { Ident } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetSources.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetSources.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .get(request.origin) + .sources() + .then((list: Ident[]) => { + resolve(new Requests.Components.GetSources.Response({ list })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts new file mode 100644 index 0000000000..92c414533d --- /dev/null +++ b/application/holder/src/service/components/index.ts @@ -0,0 +1,5 @@ +export * as GetSources from './get_sources'; +export * as GetParsers from './get_parsers'; +export * as GetOptions from './get_options'; +export * as Abort from './abort'; +export * as Validate from './validate'; diff --git a/application/holder/src/service/components/validate.ts b/application/holder/src/service/components/validate.ts new file mode 100644 index 0000000000..2e418e38cc --- /dev/null +++ b/application/holder/src/service/components/validate.ts @@ -0,0 +1,24 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.Validate.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.Validate.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .validate(request.origin, request.target, request.fields) + .then((errors: Map) => { + resolve(new Requests.Components.Validate.Response({ errors })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/sessions.ts b/application/holder/src/service/sessions.ts index 473ff09e16..ca78947de4 100644 --- a/application/holder/src/service/sessions.ts +++ b/application/holder/src/service/sessions.ts @@ -7,6 +7,7 @@ import { } from 'platform/entity/service'; import { electron } from '@service/electron'; import { jobs } from '@service/jobs'; +import { components } from '@service/components'; import { services } from '@register/services'; import { Session } from 'rustcore'; import { Active } from './sessions/active'; @@ -19,6 +20,7 @@ import * as Requests from 'platform/ipc/request'; export { Jobs } from './sessions/holder'; @DependOn(jobs) +@DependOn(components) @DependOn(electron) @SetupService(services['sessions']) export class Service extends Implementation { diff --git a/application/platform/ipc/event/components/destroyed.ts b/application/platform/ipc/event/components/destroyed.ts new file mode 100644 index 0000000000..91edc6207e --- /dev/null +++ b/application/platform/ipc/event/components/destroyed.ts @@ -0,0 +1,6 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +@Define({ name: 'EmitComponentsDestroyed' }) +export class Event extends SignatureRequirement {} + +export interface Event extends Interface {} diff --git a/application/platform/ipc/event/components/index.ts b/application/platform/ipc/event/components/index.ts new file mode 100644 index 0000000000..07d33be29e --- /dev/null +++ b/application/platform/ipc/event/components/index.ts @@ -0,0 +1,5 @@ +export * as Destroyed from './destroyed'; +export * as LoadingCancelled from './loading_cancelled'; +export * as LoadingDone from './loading_done'; +export * as LoadingError from './loading_error'; +export * as LoadingErrors from './loading_errors'; diff --git a/application/platform/ipc/event/components/loading_cancelled.ts b/application/platform/ipc/event/components/loading_cancelled.ts new file mode 100644 index 0000000000..c92284c622 --- /dev/null +++ b/application/platform/ipc/event/components/loading_cancelled.ts @@ -0,0 +1,17 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +import * as validator from '../../../env/obj'; +import { LoadingCancelledEvent } from '../../../types/components'; + +@Define({ name: 'EmitComponentsLoadingCancelledEvent' }) +export class Event extends SignatureRequirement { + public event: LoadingCancelledEvent; + + constructor(input: { event: LoadingCancelledEvent }) { + super(); + validator.isObject(input); + this.event = validator.getAsObj(input, 'event'); + } +} + +export interface Event extends Interface {} diff --git a/application/platform/ipc/event/components/loading_done.ts b/application/platform/ipc/event/components/loading_done.ts new file mode 100644 index 0000000000..7bb63f7973 --- /dev/null +++ b/application/platform/ipc/event/components/loading_done.ts @@ -0,0 +1,17 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +import * as validator from '../../../env/obj'; +import { LoadingDoneEvent } from '../../../types/components'; + +@Define({ name: 'EmitComponentsLoadingFieldsDone' }) +export class Event extends SignatureRequirement { + public event: LoadingDoneEvent; + + constructor(input: { event: LoadingDoneEvent }) { + super(); + validator.isObject(input); + this.event = validator.getAsObj(input, 'event'); + } +} + +export interface Event extends Interface {} diff --git a/application/platform/ipc/event/components/loading_error.ts b/application/platform/ipc/event/components/loading_error.ts new file mode 100644 index 0000000000..4536e5b717 --- /dev/null +++ b/application/platform/ipc/event/components/loading_error.ts @@ -0,0 +1,17 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +import * as validator from '../../../env/obj'; +import { LoadingErrorEvent } from '../../../types/components'; + +@Define({ name: 'EmitComponentsLoadingErrorEvent' }) +export class Event extends SignatureRequirement { + public event: LoadingErrorEvent; + + constructor(input: { event: LoadingErrorEvent }) { + super(); + validator.isObject(input); + this.event = validator.getAsObj(input, 'event'); + } +} + +export interface Event extends Interface {} diff --git a/application/platform/ipc/event/components/loading_errors.ts b/application/platform/ipc/event/components/loading_errors.ts new file mode 100644 index 0000000000..a368d41070 --- /dev/null +++ b/application/platform/ipc/event/components/loading_errors.ts @@ -0,0 +1,17 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +import * as validator from '../../../env/obj'; +import { LoadingErrorsEvent } from '../../../types/components'; + +@Define({ name: 'EmitComponentsLoadingErrorsEvent' }) +export class Event extends SignatureRequirement { + public event: LoadingErrorsEvent; + + constructor(input: { event: LoadingErrorsEvent }) { + super(); + validator.isObject(input); + this.event = validator.getAsObj(input, 'event'); + } +} + +export interface Event extends Interface {} diff --git a/application/platform/ipc/event/index.ts b/application/platform/ipc/event/index.ts index 305553c9ca..76f54bc60f 100644 --- a/application/platform/ipc/event/index.ts +++ b/application/platform/ipc/event/index.ts @@ -9,3 +9,4 @@ export * as Cli from './cli'; export * as Logs from './logs'; export * as Values from './values'; export * as GitHub from './github'; +export * as Components from './components'; diff --git a/application/platform/ipc/request/components/abort.ts b/application/platform/ipc/request/components/abort.ts new file mode 100644 index 0000000000..59ddfe7271 --- /dev/null +++ b/application/platform/ipc/request/components/abort.ts @@ -0,0 +1,20 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'AbortFieldsLoadingsRequest' }) +export class Request extends SignatureRequirement { + public fields: string[]; + + constructor(input: { fields: string[] }) { + super(); + validator.isObject(input); + this.fields = validator.getAsArray(input, 'fields'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'AbortFieldsLoadingsResponse' }) +export class Response extends SignatureRequirement {} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/get_options.ts b/application/platform/ipc/request/components/get_options.ts new file mode 100644 index 0000000000..c3219b257f --- /dev/null +++ b/application/platform/ipc/request/components/get_options.ts @@ -0,0 +1,31 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { FieldDesc, SourceOrigin } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetComponentsOptionsRequest' }) +export class Request extends SignatureRequirement { + public targets: string[]; + public origin: SourceOrigin; + + constructor(input: { origin: SourceOrigin; targets: string[] }) { + super(); + validator.isObject(input); + this.targets = validator.getAsArray(input, 'targets'); + this.origin = validator.getAsObj(input, 'origin'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetComponentsOptionsResponse' }) +export class Response extends SignatureRequirement { + public options: Map; + + constructor(input: { options: Map }) { + super(); + validator.isObject(input); + this.options = validator.getAsMap(input, 'options'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/get_parsers.ts b/application/platform/ipc/request/components/get_parsers.ts new file mode 100644 index 0000000000..0cee947576 --- /dev/null +++ b/application/platform/ipc/request/components/get_parsers.ts @@ -0,0 +1,29 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { SourceOrigin, Ident } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetAvailableParsersRequest' }) +export class Request extends SignatureRequirement { + public origin: SourceOrigin; + + constructor(input: { origin: SourceOrigin }) { + super(); + validator.isObject(input); + this.origin = validator.getAsObj(input, 'origin'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetAvailableParsersResponse' }) +export class Response extends SignatureRequirement { + public list: Ident[]; + + constructor(input: { list: Ident[] }) { + super(); + validator.isObject(input); + this.list = validator.getAsArray(input, 'list'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/get_sources.ts b/application/platform/ipc/request/components/get_sources.ts new file mode 100644 index 0000000000..b6de5ea86a --- /dev/null +++ b/application/platform/ipc/request/components/get_sources.ts @@ -0,0 +1,29 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { SourceOrigin, Ident } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetAvailableSourcesRequest' }) +export class Request extends SignatureRequirement { + public origin: SourceOrigin; + + constructor(input: { origin: SourceOrigin }) { + super(); + validator.isObject(input); + this.origin = validator.getAsObj(input, 'origin'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetAvailableSourcesResponse' }) +export class Response extends SignatureRequirement { + public list: Ident[]; + + constructor(input: { list: Ident[] }) { + super(); + validator.isObject(input); + this.list = validator.getAsArray(input, 'list'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/index.ts b/application/platform/ipc/request/components/index.ts new file mode 100644 index 0000000000..92c414533d --- /dev/null +++ b/application/platform/ipc/request/components/index.ts @@ -0,0 +1,5 @@ +export * as GetSources from './get_sources'; +export * as GetParsers from './get_parsers'; +export * as GetOptions from './get_options'; +export * as Abort from './abort'; +export * as Validate from './validate'; diff --git a/application/platform/ipc/request/components/validate.ts b/application/platform/ipc/request/components/validate.ts new file mode 100644 index 0000000000..f5fe0996ad --- /dev/null +++ b/application/platform/ipc/request/components/validate.ts @@ -0,0 +1,33 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { Field, SourceOrigin } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'ValidateComponentsOptionsRequest' }) +export class Request extends SignatureRequirement { + public origin: SourceOrigin; + public target: string; + public fields: Field[]; + + constructor(input: { origin: SourceOrigin; target: string; fields: Field[] }) { + super(); + validator.isObject(input); + this.target = validator.getAsNotEmptyString(input, 'target'); + this.origin = validator.getAsObj(input, 'origin'); + this.fields = validator.getAsArray(input, 'fields'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'ValidateComponentsOptionsResponse' }) +export class Response extends SignatureRequirement { + public errors: Map; + + constructor(input: { errors: Map }) { + super(); + validator.isObject(input); + this.errors = validator.getAsMap(input, 'errors'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/index.ts b/application/platform/ipc/request/index.ts index 1d074a24bf..66aacb2522 100644 --- a/application/platform/ipc/request/index.ts +++ b/application/platform/ipc/request/index.ts @@ -22,3 +22,4 @@ export * as System from './system'; export * as Values from './values'; export * as GitHub from './github'; export * as Plugins from './plugins'; +export * as Components from './components'; diff --git a/application/platform/package.json b/application/platform/package.json index 865aa4b46e..c49660dbb0 100644 --- a/application/platform/package.json +++ b/application/platform/package.json @@ -143,6 +143,7 @@ "./types/github/filemetadata": "./dist/types/github/filemetadata.js", "./types/github/filter": "./dist/types/github/filter.js", "./types/bindings": "./dist/types/bindings/index.js", + "./types/components": "./dist/types/components.js", "./package.json": "./package.json" }, "packageManager": "yarn@4.6.0" diff --git a/application/platform/types/components.ts b/application/platform/types/components.ts new file mode 100644 index 0000000000..c472c89475 --- /dev/null +++ b/application/platform/types/components.ts @@ -0,0 +1,21 @@ +import { FieldLoadingError, StaticFieldDesc } from './bindings'; + +export interface LoadingDoneEvent { + owner: string; + fields: StaticFieldDesc[]; +} + +export interface LoadingErrorsEvent { + owner: string; + errors: FieldLoadingError[]; +} + +export interface LoadingErrorEvent { + owner: string; + error: string; + fields: string[]; +} +export interface LoadingCancelledEvent { + owner: string; + fields: string[]; +} diff --git a/application/platform/types/index.ts b/application/platform/types/index.ts index 27fce49da4..3568f72b52 100644 --- a/application/platform/types/index.ts +++ b/application/platform/types/index.ts @@ -10,3 +10,4 @@ export * as comment from './comment'; export * as bookmark from './bookmark'; export * as sde from './sde'; export * as bindings from './bindings'; +export * as components from './components'; From f828546d7e1a4c0b6847aa57eaa4013831f7c49a Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Apr 2025 14:49:56 +0200 Subject: [PATCH 019/121] Add settings scheme to serial port (backend) --- application/apps/indexer/Cargo.toml | 1 + .../apps/indexer/components/src/tys.rs | 31 +++- .../apps/indexer/parsers/src/dlt/options.rs | 24 +-- .../indexer/parsers/src/someip/options.rs | 23 ++- application/apps/indexer/session/Cargo.toml | 2 +- .../indexer/session/src/components/mod.rs | 16 +- application/apps/indexer/sources/Cargo.toml | 1 + .../indexer/sources/src/serial/serialport.rs | 145 +++++++++++++++++- .../apps/indexer/stypes/bindings/observe.ts | 2 +- .../apps/indexer/stypes/bindings/options.ts | 8 +- .../apps/indexer/stypes/src/observe/mod.rs | 3 +- .../apps/indexer/stypes/src/options/mod.rs | 3 + .../apps/rustcore/rs-bindings/Cargo.lock | 51 +++++- 13 files changed, 252 insertions(+), 58 deletions(-) diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index 09a2367a41..44213a3691 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -43,6 +43,7 @@ envvars = "0.1" anyhow = "1.0" toml = "0.8" blake3 = "1.8" +serialport = "4.6" ## Development Dependencies ## # Support for `html_reports` needs running the benchmarks via `cargo-criterion` tool. diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 04837a6166..074d217ac0 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -11,15 +11,30 @@ use tokio_util::sync::CancellationToken; /// * `Err(stypes::NativeError)` - An error indicating a problem while retrieving the fields. pub type FieldsResult = Result, stypes::NativeError>; +#[derive(Debug)] /// A type alias representing the result of obtaining a single static field description. -/// -/// The tuple contains: -/// -/// * `String` - The name or identifier of the static field. -/// * `Result` - A result that contains either: -/// - `stypes::StaticFieldDesc` on success. -/// - A `String` representing an error message on failure. -pub type StaticFieldResult = (String, Result); +pub enum StaticFieldResult { + Success(stypes::StaticFieldDesc), + Failed(stypes::FieldLoadingError), +} + +impl StaticFieldResult { + pub fn is_ok(&self) -> bool { + matches!(self, StaticFieldResult::Success(..)) + } + pub fn ok(self) -> Option { + match self { + Self::Success(desc) => Some(desc), + Self::Failed(..) => None, + } + } + pub fn err(self) -> Option { + match self { + Self::Success(..) => None, + Self::Failed(err) => Some(err), + } + } +} /// A type alias for a result containing a vector of static field results or a native error. /// diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index f1e060e3a7..dba1d7a753 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, StaticFieldResult}; use tokio::{ select, time::{sleep, Duration}, @@ -29,11 +29,13 @@ impl ComponentDescriptor for Descriptor { required: true, default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), + binding: None, }), stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { id: FIELD_STATISTICS.to_owned(), name: String::from("Statistics"), desc: String::from("Collected Statistis Data"), + binding: None, }), ]) } @@ -60,17 +62,15 @@ impl ComponentDescriptor for Descriptor { return Ok(Vec::new()); } }; - Ok(vec![( - FIELD_STATISTICS.to_owned(), - Ok(stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - default: Some(stypes::Value::Number(1)), - interface: stypes::ValueInput::KeyString(HashMap::new()), - }), - )]) + Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), + binding: None, + })]) }) } fn ty(&self) -> stypes::ComponentType { diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index 73adc52bc9..ab4ff9d149 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use super::SomeipParser; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, StaticFieldResult}; use tokio::{ select, time::{sleep, Duration}, @@ -29,6 +29,7 @@ impl ComponentDescriptor for Descriptor { required: true, default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), + binding: None, })]) } fn ident(&self) -> stypes::Ident { @@ -55,17 +56,15 @@ impl ComponentDescriptor for Descriptor { return Ok(Vec::new()); } }; - Ok(vec![( - FIELD_STATISTICS.to_owned(), - Ok(stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - default: Some(stypes::Value::Number(1)), - interface: stypes::ValueInput::KeyString(HashMap::new()), - }), - )]) + Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + default: Some(stypes::Value::Number(1)), + interface: stypes::ValueInput::KeyString(HashMap::new()), + binding: None, + })]) }) } fn ty(&self) -> stypes::ComponentType { diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index 8d54a3b4e1..c567c3c2d8 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -23,7 +23,7 @@ rustc-hash = "2.1" stypes = { path = "../stypes", features=["rustcore"] } serde = { workspace = true , features = ["derive"] } serde_json.workspace = true -serialport = "4.6" +serialport.workspace = true sources = { path = "../sources" } thiserror.workspace = true tokio = { workspace = true , features = ["full"] } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 38892447a2..fd45943c8e 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -127,21 +127,13 @@ impl ComponentsSession { match results { Ok(LazyLoadingResult::Fields(fields)) => { let (success, fail): (Vec<_>, Vec<_>) = - fields.into_iter().partition(|(_, result)| result.is_ok()); + fields.into_iter().partition(|result| result.is_ok()); let fields: Vec = success .into_iter() - .filter_map(|(_, field)| field.ok()) - .collect(); - let errors: Vec = fail - .into_iter() - .filter_map(|(id, err)| { - if let Err(err) = err { - Some(stypes::FieldLoadingError { id, err }) - } else { - None - } - }) + .filter_map(|result| result.ok()) .collect(); + let errors: Vec = + fail.into_iter().filter_map(|result| result.err()).collect(); if !fields.is_empty() { log_if_err(tx_callback_events.send( stypes::CallbackOptionsEvent::LoadingDone { diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 574b52628e..5c1f2c6ff0 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -27,6 +27,7 @@ shellexpand = "3.1" stypes = { path = "../stypes", features=["rustcore"] } socket2 = "0.5.8" components = { path = "../components" } +serialport.workspace = true [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index fc3605d89d..91f3d92e04 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,12 +1,13 @@ use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, StaticFieldResult}; use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, }; use std::{io, str}; +use stypes::{FieldDesc, LazyFieldDesc, StaticFieldDesc, Value, ValueInput}; use tokio::time::{Duration, sleep}; use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilderExt, SerialStream, StopBits}; use tokio_util::codec::{Decoder, Encoder, Framed}; @@ -221,20 +222,158 @@ const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, ]); +/// The path to the serial port. +const FIELD_PATH: &str = "SERIAL_SOURCE_FIELD_PATH"; +/// The baud rate for the connection. +const FIELD_BAUD_RATE: &str = "SERIAL_SOURCE_FIELD_BAUD_RATE"; +/// The number of data bits per frame. +const FIELD_DATA_BITS: &str = "SERIAL_SOURCE_FIELD_DATA_BITS"; +/// The flow control setting. +const FIELD_FLOW_CONTROL: &str = "SERIAL_SOURCE_FIELD_FLOW_CONTROL"; +/// The parity setting. +const FIELD_PARITY: &str = "SERIAL_SOURCE_FIELD_PARITY"; +/// The number of stop bits. +const FIELD_STOP_BITS: &str = "SERIAL_SOURCE_FIELD_STOP_BITS"; +/// The delay in sending data, in milliseconds. +const FIELD_SEND_DATA_DELAY: &str = "SERIAL_SOURCE_FIELD_SEND_DATA_DELAY"; +/// Whether the connection is exclusive. +const FIELD_EXCLUSIVE: &str = "SERIAL_SOURCE_FIELD_EXCLUSIVE"; +/// List of ports +const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; + #[derive(Default)] struct Descriptor {} impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("Serial Source"), - desc: String::from("Serial Source"), + name: String::from("Serial"), + desc: String::from("Access to Serial Devices"), uuid: SERIAL_SOURCE_UUID, } } fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } + fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_PATH.to_owned(), + name: "Path to dev".to_owned(), + desc: "Full path to serial device/port".to_owned(), + required: true, + default: None, + interface: ValueInput::String, + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_BAUD_RATE.to_owned(), + name: "Boud rate".to_owned(), + desc: "Boud rate".to_owned(), + required: true, + default: Some(Value::Number(115200)), + interface: ValueInput::Numbers(vec![ + 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, + 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, + ]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_DATA_BITS.to_owned(), + name: "Data bits".to_owned(), + desc: "Data bits".to_owned(), + required: true, + default: Some(Value::Number(8)), + interface: ValueInput::Numbers(vec![8, 7, 6, 5]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_STOP_BITS.to_owned(), + name: "Stop bits".to_owned(), + desc: "Stop bits".to_owned(), + required: true, + default: Some(Value::Number(1)), + interface: ValueInput::Numbers(vec![1, 2]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_FLOW_CONTROL.to_owned(), + name: "Flow control".to_owned(), + desc: "Flow control".to_owned(), + required: true, + default: Some(Value::String(String::from("None"))), + interface: ValueInput::Strings(vec![ + String::from("None"), + String::from("Hardware"), + String::from("Software"), + ]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_PARITY.to_owned(), + name: "Parity".to_owned(), + desc: "Parity".to_owned(), + required: true, + default: Some(Value::String(String::from("None"))), + interface: ValueInput::Strings(vec![ + String::from("None"), + String::from("Odd"), + String::from("Even"), + ]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_EXCLUSIVE.to_owned(), + name: "Exclusive".to_owned(), + desc: "Exclusive".to_owned(), + required: true, + default: Some(Value::Boolean(false)), + interface: ValueInput::NamedValues(vec![ + (String::from("Yes"), Value::Boolean(true)), + (String::from("No"), Value::Boolean(false)), + ]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_SEND_DATA_DELAY.to_owned(), + name: "Send delay, ms".to_owned(), + desc: "Data sending delay (between bytes), ms".to_owned(), + required: true, + default: Some(Value::Number(0)), + interface: ValueInput::Numbers(vec![10, 20, 30, 40, 50]), + binding: None, + }), + FieldDesc::Lazy(LazyFieldDesc { + id: FIELD_PORTS_LIST.to_string(), + name: "Ports".to_string(), + desc: "List of available ports".to_string(), + binding: Some(FIELD_PATH.to_string()), + }), + ]) + } + fn lazy_fields_getter( + &self, + _origin: stypes::SourceOrigin, + _cancel: tokio_util::sync::CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async move { + let ports = serialport::available_ports() + .map_err(|e| stypes::ComputationError::IoOperation(e.to_string()))? + .into_iter() + .map(|p| p.port_name) + .collect::>(); + Ok(vec![StaticFieldResult::Success(StaticFieldDesc { + id: FIELD_PORTS_LIST.to_owned(), + name: "Ports".to_string(), + desc: "List of available ports".to_string(), + required: false, + default: None, + interface: ValueInput::Strings(ports), + binding: Some(FIELD_PATH.to_string()), + })]) + }) + } } impl components::Component for SerialSource { diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index f426102e47..1722fadb84 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -127,7 +127,7 @@ export type SomeIpParserSettings = { */ fibex_file_paths: Array | null, }; -export type SourceOrigin = { "File": string } | { "Files": Array } | { "Folder": string } | { "Folders": Array } | { "Stream": Ident }; +export type SourceOrigin = { "File": string } | { "Files": Array } | { "Folder": string } | { "Folders": Array } | "Source"; /** * Configuration for TCP connections. diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index bd5d3d2241..decf22ca36 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -17,10 +17,10 @@ export type FieldLoadingError = { id: string, err: string, }; export type FieldsValidationErrors = { errors: Map, }; -export type LazyFieldDesc = { id: string, name: string, desc: string, }; +export type LazyFieldDesc = { id: string, name: string, desc: string, binding: string | null, }; -export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInput, }; +export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInput, binding: string | null, }; -export type Value = { "Boolean": boolean } | { "Number": bigint } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyStrings": { [key in string]?: Array } } | { "KeyString": { [key in string]?: string } } | { "KeyNumbers": { [key in string]?: Array } } | { "KeyNumber": { [key in string]?: bigint } }; +export type Value = { "Boolean": boolean } | { "Number": bigint } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = "Checkbox" | "Number" | "String" | { "Numbers": Array } | { "Strings": Array } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = "Checkbox" | "Number" | "String" | { "Numbers": Array } | { "Strings": Array } | { "NamedValues": Array<[string, Value]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 00a2561a64..be00bb20d9 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -25,8 +25,7 @@ pub enum SourceOrigin { Files(Vec), Folder(PathBuf), Folders(Vec), - /// `String` - stream identificator - Stream(Ident), + Source, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 3118c6c4f1..654d359ffd 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -67,6 +67,7 @@ pub struct StaticFieldDesc { pub required: bool, pub default: Option, pub interface: ValueInput, + pub binding: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -80,6 +81,7 @@ pub struct LazyFieldDesc { pub id: String, pub name: String, pub desc: String, + pub binding: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -107,6 +109,7 @@ pub enum ValueInput { String, Numbers(Vec), Strings(Vec), + NamedValues(Vec<(String, Value)>), #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] KeyNumber(HashMap), #[cfg_attr( diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index fde11db9f0..ee44764bec 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -971,6 +971,38 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys" version = "0.5.0" @@ -2512,7 +2544,8 @@ dependencies = [ "blake3", "components", "dir_checksum", - "dirs", + "dirs 6.0.0", + "futures", "log", "parsers", "rand 0.9.1", @@ -2765,6 +2798,17 @@ name = "redox_users" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom", + "libredox", + "thiserror 2.0.3", +] + +[[package]] +name = "regalloc2" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.16", "libredox", @@ -3044,7 +3088,7 @@ dependencies = [ "blake3", "components", "crossbeam-channel", - "dirs", + "dirs 6.0.0", "dlt-core", "envvars", "file-tools", @@ -3198,7 +3242,8 @@ dependencies = [ "pcap-parser", "regex", "serde", - "shellexpand", + "serialport", + "shellexpand 3.1.0", "socket2", "stypes", "thiserror 2.0.12", From ff05480c2083b8506bf39dcb7aa0879d30589df4 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Apr 2025 14:50:16 +0200 Subject: [PATCH 020/121] Basic template or setup tab --- application/client/src/app/service/session.ts | 8 +- application/client/src/app/ui/tabs/module.ts | 2 + .../client/src/app/ui/tabs/setup/component.ts | 55 ++++++++++++++ .../client/src/app/ui/tabs/setup/module.ts | 23 ++++++ .../client/src/app/ui/tabs/setup/state.ts | 74 +++++++++++++++++++ .../client/src/app/ui/tabs/setup/styles.less | 74 +++++++++++++++++++ .../src/app/ui/tabs/setup/template.html | 43 +++++++++++ .../ipc/request/components/get_options.ts | 3 +- .../ipc/request/components/get_parsers.ts | 3 +- .../ipc/request/components/get_sources.ts | 3 +- .../ipc/request/components/validate.ts | 3 +- .../platform/types/bindings/observe.ts | 2 +- .../platform/types/bindings/options.ts | 12 +-- 13 files changed, 291 insertions(+), 14 deletions(-) create mode 100644 application/client/src/app/ui/tabs/setup/component.ts create mode 100644 application/client/src/app/ui/tabs/setup/module.ts create mode 100644 application/client/src/app/ui/tabs/setup/state.ts create mode 100644 application/client/src/app/ui/tabs/setup/styles.less create mode 100644 application/client/src/app/ui/tabs/setup/template.html diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 56e91b57ca..cc6013063e 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -21,6 +21,7 @@ import { File } from '@platform/types/files'; import { Observe } from '@platform/types/observe'; import { getRender } from '@schema/render/tools'; import { TabObserve } from '@tabs/observe/component'; +import { SetupObserve } from '@tabs/setup/component'; import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; @@ -267,9 +268,8 @@ export class Service extends Implementation { const api = this.add().tab({ name: observe.origin.title(), content: { - factory: TabObserve, - inputs: TabObserve.inputs({ - observe, + factory: SetupObserve, + inputs: { api: { finish: (observe: Observe): Promise => { return new Promise((success, failed) => { @@ -293,7 +293,7 @@ export class Service extends Implementation { return api as unknown as TabControls; }, }, - }), + }, }, active: true, }); diff --git a/application/client/src/app/ui/tabs/module.ts b/application/client/src/app/ui/tabs/module.ts index b723638d68..8e36775805 100644 --- a/application/client/src/app/ui/tabs/module.ts +++ b/application/client/src/app/ui/tabs/module.ts @@ -6,6 +6,7 @@ import { SettingsModule } from '@ui/tabs/settings/module'; import { ChangelogModule } from '@ui/tabs/changelogs/module'; import { HelpModule } from '@ui/tabs/help/module'; import { PluginsManagerModule } from '@ui/tabs/plugins/module'; +import { SetupModule } from '@ui/tabs/setup/module'; @NgModule({ imports: [ @@ -16,6 +17,7 @@ import { PluginsManagerModule } from '@ui/tabs/plugins/module'; ChangelogModule, HelpModule, PluginsManagerModule, + SetupModule, ], declarations: [], exports: [ObserveModule, MultipleFilesModule, SettingsModule, ChangelogModule, HelpModule], diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts new file mode 100644 index 0000000000..43489d2ad4 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -0,0 +1,55 @@ +import { + Component, + ChangeDetectorRef, + ChangeDetectionStrategy, + Input, + AfterViewInit, + AfterContentInit, + OnDestroy, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { State, IApi } from './state'; + +@Component({ + selector: 'app-tabs-setup', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false, +}) +@Initial() +@Ilc() +export class SetupObserve + extends ChangesDetector + implements AfterViewInit, AfterContentInit, OnDestroy +{ + @Input() api!: IApi; + + public state: State = new State(); + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngOnDestroy(): void { + this.state.destroy(); + } + + public ngAfterContentInit(): void { + this.state.load(); + } + + public ngAfterViewInit(): void { + this.env().subscriber.register( + this.state.subjects.get().parsers.subscribe(() => { + this.detectChanges(); + }), + this.state.subjects.get().sources.subscribe(() => { + this.detectChanges(); + }), + ); + } +} +export interface SetupObserve extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/setup/module.ts b/application/client/src/app/ui/tabs/setup/module.ts new file mode 100644 index 0000000000..a41f99cebe --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; + +import { SetupObserve } from './component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatFormFieldModule, + MatSelectModule, + ], + declarations: [SetupObserve], + exports: [SetupObserve], + bootstrap: [SetupObserve], +}) +export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts new file mode 100644 index 0000000000..68e6b65a78 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -0,0 +1,74 @@ +import { SourceOrigin, Ident } from '@platform/types/bindings'; +import { components } from '@service/components'; +import { Logger } from '@env/logs'; +import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; +import { TabControls } from '@service/session'; + +export interface IApi { + finish(): Promise; + cancel(): void; + tab(): TabControls; +} + +export class State extends Subscriber { + public origin: SourceOrigin = 'Source'; + public sources: Ident[] = []; + public parsers: Ident[] = []; + public subjects: Subjects<{ + sources: Subject; + parsers: Subject; + error: Subject; + }> = new Subjects({ + sources: new Subject(), + parsers: new Subject(), + error: new Subject(), + }); + public selected: { + source: string; + parser: string; + } = { + source: '', + parser: '', + }; + + protected readonly logger = new Logger(`Setup`); + + public destroy() { + this.unsubscribe(); + this.subjects.destroy(); + } + public load() { + components + .get(this.origin) + .sources() + .then((sources: Ident[]) => { + this.sources = sources; + this.subjects.get().sources.emit(this.sources); + }) + .catch((err: Error) => { + this.logger.error(`Fail to get sources list: ${err.message}`); + this.subjects.get().error.emit(err.message); + }); + components + .get(this.origin) + .parsers() + .then((parsers: Ident[]) => { + this.parsers = parsers; + this.subjects.get().parsers.emit(this.sources); + }) + .catch((err: Error) => { + this.logger.error(`Fail to get parsers list: ${err.message}`); + this.subjects.get().error.emit(err.message); + }); + } + + public change(): { + stream(): void; + parser(): void; + } { + return { + stream: (): void => {}, + parser: (): void => {}, + }; + } +} diff --git a/application/client/src/app/ui/tabs/setup/styles.less b/application/client/src/app/ui/tabs/setup/styles.less new file mode 100644 index 0000000000..7ccc097fce --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/styles.less @@ -0,0 +1,74 @@ +@import '../../styles/variables.less'; + + +:host { + position: absolute; + display: block; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + & div.preset-container { + top: 48px; + bottom: 0px; + height: auto; + } + & div.title { + position: absolute; + display: flex; + flex-direction: row; + align-items: center; + left:0; + right: 0; + padding: 0 8px; + background: @scheme-color-5; + box-shadow: 0 3px 3px @scheme-color-7; + height: 48px; + overflow: hidden; + & *.entity { + display: flex; + flex-direction: row; + align-items: center; + left:0; + right: 0; + padding: 0 8px; + background: @scheme-color-5; + height: 48px; + &.filler { + flex: auto; + width: 100%; + margin: 0; + justify-content: end; + } + &.info { + color: @scheme-color-2; + white-space: nowrap; + align-items: center; + flex: auto; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + & span { + margin-left: 8px; + } + } + & button { + position: relative; + display: inline-block; + margin: 0 16px 0 0; + margin-bottom: 0!important; + &:last-child { + margin-right: 0; + } + } + & p.info { + color: @scheme-color-2; + white-space: nowrap; + } + } + & > * { + margin: 0 8px; + } + } +} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html new file mode 100644 index 0000000000..647a961c01 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -0,0 +1,43 @@ +
+
+

Source:

+ + + + {{source.desc}} + + + +
+
+

Used Parser:

+ + + + {{parser.desc}} + + + +
+
+ +
+
+
+
+
diff --git a/application/platform/ipc/request/components/get_options.ts b/application/platform/ipc/request/components/get_options.ts index c3219b257f..e9008cc829 100644 --- a/application/platform/ipc/request/components/get_options.ts +++ b/application/platform/ipc/request/components/get_options.ts @@ -12,7 +12,8 @@ export class Request extends SignatureRequirement { super(); validator.isObject(input); this.targets = validator.getAsArray(input, 'targets'); - this.origin = validator.getAsObj(input, 'origin'); + this.origin = + typeof input.origin === 'string' ? input.origin : validator.getAsObj(input, 'origin'); } } export interface Request extends Interface {} diff --git a/application/platform/ipc/request/components/get_parsers.ts b/application/platform/ipc/request/components/get_parsers.ts index 0cee947576..8005dc6572 100644 --- a/application/platform/ipc/request/components/get_parsers.ts +++ b/application/platform/ipc/request/components/get_parsers.ts @@ -10,7 +10,8 @@ export class Request extends SignatureRequirement { constructor(input: { origin: SourceOrigin }) { super(); validator.isObject(input); - this.origin = validator.getAsObj(input, 'origin'); + this.origin = + typeof input.origin === 'string' ? input.origin : validator.getAsObj(input, 'origin'); } } export interface Request extends Interface {} diff --git a/application/platform/ipc/request/components/get_sources.ts b/application/platform/ipc/request/components/get_sources.ts index b6de5ea86a..a72f94fcc6 100644 --- a/application/platform/ipc/request/components/get_sources.ts +++ b/application/platform/ipc/request/components/get_sources.ts @@ -10,7 +10,8 @@ export class Request extends SignatureRequirement { constructor(input: { origin: SourceOrigin }) { super(); validator.isObject(input); - this.origin = validator.getAsObj(input, 'origin'); + this.origin = + typeof input.origin === 'string' ? input.origin : validator.getAsObj(input, 'origin'); } } export interface Request extends Interface {} diff --git a/application/platform/ipc/request/components/validate.ts b/application/platform/ipc/request/components/validate.ts index f5fe0996ad..bc52b2b11f 100644 --- a/application/platform/ipc/request/components/validate.ts +++ b/application/platform/ipc/request/components/validate.ts @@ -13,7 +13,8 @@ export class Request extends SignatureRequirement { super(); validator.isObject(input); this.target = validator.getAsNotEmptyString(input, 'target'); - this.origin = validator.getAsObj(input, 'origin'); + this.origin = + typeof input.origin === 'string' ? input.origin : validator.getAsObj(input, 'origin'); this.fields = validator.getAsArray(input, 'fields'); } } diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 2451dad6b2..e92ba30818 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -145,7 +145,7 @@ export type SourceOrigin = | { Files: Array } | { Folder: string } | { Folders: Array } - | { Stream: Ident }; + | 'Source'; /** * Configuration for TCP connections. diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 683122b4b5..2eca6d2a13 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -22,7 +22,7 @@ export type FieldLoadingError = { id: string; err: string }; export type FieldsValidationErrors = { errors: Map }; -export type LazyFieldDesc = { id: string; name: string; desc: string }; +export type LazyFieldDesc = { id: string; name: string; desc: string; binding: string | null }; export type StaticFieldDesc = { id: string; @@ -31,6 +31,7 @@ export type StaticFieldDesc = { required: boolean; default: Value | null; interface: ValueInput; + binding: string | null; }; export type Value = @@ -43,10 +44,10 @@ export type Value = | { Files: Array } | { File: string } | { Directory: string } - | { KeyStrings: { [key in string]?: Array } } - | { KeyString: { [key in string]?: string } } - | { KeyNumbers: { [key in string]?: Array } } - | { KeyNumber: { [key in string]?: bigint } }; + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map }; export type ValueInput = | 'Checkbox' @@ -54,6 +55,7 @@ export type ValueInput = | 'String' | { Numbers: Array } | { Strings: Array } + | { NamedValues: Array<[string, Value]> } | { KeyNumber: Map } | { KeyNumbers: Map } | { KeyString: Map } From 5a49cb9708afb18aefc1a15971b736c4e162f16f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 28 Apr 2025 09:07:31 +0200 Subject: [PATCH 021/121] Bindins between settings fields --- .../apps/indexer/parsers/src/dlt/options.rs | 2 - .../indexer/parsers/src/someip/options.rs | 2 - .../indexer/sources/src/serial/serialport.rs | 60 +++---- .../apps/indexer/stypes/bindings/options.ts | 6 +- .../apps/indexer/stypes/src/options/mod.rs | 26 ++- .../client/src/app/ui/elements/module.ts | 3 + .../app/ui/elements/scheme/bound/component.ts | 96 +++++++++++ .../app/ui/elements/scheme/bound/styles.less | 7 + .../ui/elements/scheme/bound/template.html | 5 + .../src/app/ui/elements/scheme/component.ts | 89 ++++++++++ .../src/app/ui/elements/scheme/element.ts | 153 ++++++++++++++++++ .../app/ui/elements/scheme/entry/component.ts | 62 +++++++ .../app/ui/elements/scheme/entry/styles.less | 7 + .../ui/elements/scheme/entry/template.html | 48 ++++++ .../src/app/ui/elements/scheme/field.ts | 30 ++++ .../app/ui/elements/scheme/inner/component.ts | 37 +++++ .../app/ui/elements/scheme/inner/styles.less | 7 + .../ui/elements/scheme/inner/template.html | 30 ++++ .../src/app/ui/elements/scheme/module.ts | 34 ++++ .../src/app/ui/elements/scheme/provider.ts | 17 ++ .../src/app/ui/elements/scheme/styles.less | 7 + .../src/app/ui/elements/scheme/template.html | 11 ++ .../client/src/app/ui/tabs/setup/component.ts | 3 + .../client/src/app/ui/tabs/setup/module.ts | 3 +- .../client/src/app/ui/tabs/setup/provider.ts | 107 ++++++++++++ .../client/src/app/ui/tabs/setup/state.ts | 32 +++- .../src/app/ui/tabs/setup/template.html | 13 +- .../platform/types/bindings/options.ts | 19 +-- cli/chipmunk-cli/Cargo.lock | 28 ++++ 29 files changed, 885 insertions(+), 59 deletions(-) create mode 100644 application/client/src/app/ui/elements/scheme/bound/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/bound/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/bound/template.html create mode 100644 application/client/src/app/ui/elements/scheme/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/element.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/entry/template.html create mode 100644 application/client/src/app/ui/elements/scheme/field.ts create mode 100644 application/client/src/app/ui/elements/scheme/inner/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/inner/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/inner/template.html create mode 100644 application/client/src/app/ui/elements/scheme/module.ts create mode 100644 application/client/src/app/ui/elements/scheme/provider.ts create mode 100644 application/client/src/app/ui/elements/scheme/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/template.html create mode 100644 application/client/src/app/ui/tabs/setup/provider.ts diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index dba1d7a753..05eccc8e67 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -27,7 +27,6 @@ impl ComponentDescriptor for Descriptor { name: String::from("Log Level"), desc: String::from("DLT Log Level"), required: true, - default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), binding: None, }), @@ -67,7 +66,6 @@ impl ComponentDescriptor for Descriptor { name: String::from("Example"), desc: String::from("Example"), required: true, - default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), binding: None, })]) diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index ab4ff9d149..4ce0c1f63c 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -27,7 +27,6 @@ impl ComponentDescriptor for Descriptor { name: String::from("Log Level"), desc: String::from("DLT Log Level"), required: true, - default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), binding: None, })]) @@ -61,7 +60,6 @@ impl ComponentDescriptor for Descriptor { name: String::from("Example"), desc: String::from("Example"), required: true, - default: Some(stypes::Value::Number(1)), interface: stypes::ValueInput::KeyString(HashMap::new()), binding: None, })]) diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index 91f3d92e04..ed823cc1bf 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -262,8 +262,7 @@ impl ComponentDescriptor for Descriptor { name: "Path to dev".to_owned(), desc: "Full path to serial device/port".to_owned(), required: true, - default: None, - interface: ValueInput::String, + interface: ValueInput::String(String::new()), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -271,12 +270,14 @@ impl ComponentDescriptor for Descriptor { name: "Boud rate".to_owned(), desc: "Boud rate".to_owned(), required: true, - default: Some(Value::Number(115200)), - interface: ValueInput::Numbers(vec![ - 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, 1152000, - 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, - ]), + interface: ValueInput::Numbers( + vec![ + 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, + 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, + ], + 115200, + ), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -284,8 +285,7 @@ impl ComponentDescriptor for Descriptor { name: "Data bits".to_owned(), desc: "Data bits".to_owned(), required: true, - default: Some(Value::Number(8)), - interface: ValueInput::Numbers(vec![8, 7, 6, 5]), + interface: ValueInput::Numbers(vec![8, 7, 6, 5], 8), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -293,8 +293,7 @@ impl ComponentDescriptor for Descriptor { name: "Stop bits".to_owned(), desc: "Stop bits".to_owned(), required: true, - default: Some(Value::Number(1)), - interface: ValueInput::Numbers(vec![1, 2]), + interface: ValueInput::Numbers(vec![1, 2], 1), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -302,12 +301,14 @@ impl ComponentDescriptor for Descriptor { name: "Flow control".to_owned(), desc: "Flow control".to_owned(), required: true, - default: Some(Value::String(String::from("None"))), - interface: ValueInput::Strings(vec![ + interface: ValueInput::Strings( + vec![ + String::from("None"), + String::from("Hardware"), + String::from("Software"), + ], String::from("None"), - String::from("Hardware"), - String::from("Software"), - ]), + ), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -315,12 +316,14 @@ impl ComponentDescriptor for Descriptor { name: "Parity".to_owned(), desc: "Parity".to_owned(), required: true, - default: Some(Value::String(String::from("None"))), - interface: ValueInput::Strings(vec![ + interface: ValueInput::Strings( + vec![ + String::from("None"), + String::from("Odd"), + String::from("Even"), + ], String::from("None"), - String::from("Odd"), - String::from("Even"), - ]), + ), binding: None, }), FieldDesc::Static(StaticFieldDesc { @@ -328,10 +331,9 @@ impl ComponentDescriptor for Descriptor { name: "Exclusive".to_owned(), desc: "Exclusive".to_owned(), required: true, - default: Some(Value::Boolean(false)), - interface: ValueInput::NamedValues(vec![ - (String::from("Yes"), Value::Boolean(true)), - (String::from("No"), Value::Boolean(false)), + interface: ValueInput::NamedBools(vec![ + (String::from("Yes"), true), + (String::from("No"), false), ]), binding: None, }), @@ -340,8 +342,7 @@ impl ComponentDescriptor for Descriptor { name: "Send delay, ms".to_owned(), desc: "Data sending delay (between bytes), ms".to_owned(), required: true, - default: Some(Value::Number(0)), - interface: ValueInput::Numbers(vec![10, 20, 30, 40, 50]), + interface: ValueInput::Numbers(vec![0, 10, 20, 30, 40, 50], 0), binding: None, }), FieldDesc::Lazy(LazyFieldDesc { @@ -368,8 +369,7 @@ impl ComponentDescriptor for Descriptor { name: "Ports".to_string(), desc: "List of available ports".to_string(), required: false, - default: None, - interface: ValueInput::Strings(ports), + interface: ValueInput::Strings(ports, String::new()), binding: Some(FIELD_PATH.to_string()), })]) }) diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index decf22ca36..cfe423340c 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -19,8 +19,8 @@ export type FieldsValidationErrors = { errors: Map, }; export type LazyFieldDesc = { id: string, name: string, desc: string, binding: string | null, }; -export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, default: Value | null, interface: ValueInput, binding: string | null, }; +export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, interface: ValueInput, binding: string | null, }; -export type Value = { "Boolean": boolean } | { "Number": bigint } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; +export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = "Checkbox" | "Number" | "String" | { "Numbers": Array } | { "Strings": Array } | { "NamedValues": Array<[string, Value]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 654d359ffd..eaebda04b5 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -65,7 +65,6 @@ pub struct StaticFieldDesc { pub name: String, pub desc: String, pub required: bool, - pub default: Option, pub interface: ValueInput, pub binding: Option, } @@ -104,12 +103,23 @@ pub struct Field { ts(export, export_to = "options.ts") )] pub enum ValueInput { - Checkbox, - Number, - String, - Numbers(Vec), - Strings(Vec), - NamedValues(Vec<(String, Value)>), + Checkbox(bool), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "number"))] + Number(i64), + String(String), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "[Array, number]") + )] + Numbers(Vec, i64), + Strings(Vec, String), + NamedBools(Vec<(String, bool)>), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Array<[string, number]>") + )] + NamedNumbers(Vec<(String, i64)>), + NamedStrings(Vec<(String, String)>), #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] KeyNumber(HashMap), #[cfg_attr( @@ -143,7 +153,9 @@ pub enum ValueInput { )] pub enum Value { Boolean(bool), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "number"))] Number(i64), + #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Array"))] Numbers(Vec), String(String), Strings(Vec), diff --git a/application/client/src/app/ui/elements/module.ts b/application/client/src/app/ui/elements/module.ts index 98c01be2e0..78fc68bd06 100644 --- a/application/client/src/app/ui/elements/module.ts +++ b/application/client/src/app/ui/elements/module.ts @@ -19,6 +19,7 @@ import { TeamworkAppletModule } from '@elements/teamwork/module'; import { TimezoneSelectorModule } from '@elements/timezones/module'; import { ComTooltipComponent } from '@elements/tooltip/component'; import { TreeModule } from '@elements/tree/module'; +import { SettingsSchemeModule } from '@elements/scheme/module'; @NgModule({ imports: [ @@ -28,6 +29,7 @@ import { TreeModule } from '@elements/tree/module'; TabsModule, RecentActionsModule, TreeModule, + SettingsSchemeModule, LocksHistoryModule, AutocompleteModule, FolderInputModule, @@ -48,6 +50,7 @@ import { TreeModule } from '@elements/tree/module'; TabsModule, RecentActionsModule, TreeModule, + SettingsSchemeModule, LocksHistoryModule, AutocompleteModule, FolderInputModule, diff --git a/application/client/src/app/ui/elements/scheme/bound/component.ts b/application/client/src/app/ui/elements/scheme/bound/component.ts new file mode 100644 index 0000000000..99bcbdc44a --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/bound/component.ts @@ -0,0 +1,96 @@ +import { + Component, + ChangeDetectorRef, + Input, + AfterViewInit, + AfterContentInit, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { SchemeProvider } from '../provider'; +import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; +import { Element, ChangeEvent } from '../element'; +import { WrappedField } from '../field'; +@Component({ + selector: 'app-settings-scheme-entry-bound', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class SchemeEntryBound extends ChangesDetector implements AfterViewInit, AfterContentInit { + @Input() provider!: SchemeProvider; + @Input() fields!: FieldDesc[]; + + public pending: Map = new Map(); + public loaded: Map = new Map(); + public elements: Map = new Map(); + public name!: string; + public desc!: string; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void { + this.fields.forEach((field) => { + const pending = (field as { Lazy: LazyFieldDesc }).Lazy; + const loaded = (field as { Static: StaticFieldDesc }).Static; + if (loaded) { + const element = new Element(loaded.id, loaded.interface); + this.env().subscriber.register( + element.changed.subscribe(this.onChanges.bind(this)), + ); + this.elements.set(loaded.id, element); + if (loaded.binding === undefined) { + this.desc = loaded.desc; + this.name = loaded.name; + } + this.loaded.set(loaded.id, loaded); + return; + } + this.pending.set(pending.id, pending); + }); + this.env().subscriber.register( + this.provider + .subjects() + .get() + .loaded.subscribe((loaded: StaticFieldDesc) => { + if (!this.pending.has(loaded.id)) { + return; + } + this.loaded.set(loaded.id, loaded); + const element = new Element(loaded.id, loaded.interface); + this.env().subscriber.register( + element.changed.subscribe(this.onChanges.bind(this)), + ); + this.elements.set(loaded.id, element); + this.pending.delete(loaded.id); + this.detectChanges(); + }), + ); + } + + public ngAfterViewInit(): void { + this.detectChanges(); + } + + public onChanges(event: ChangeEvent) { + const owner = this.loaded.get(event.uuid); + if (!owner) { + return; + } + if (!owner.binding) { + return; + } + const bound = this.elements.get(owner.binding); + if (bound === undefined) { + return; + } + bound.setValue(event.value); + this.detectChanges(); + } +} +export interface SchemeEntryBound extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/bound/styles.less b/application/client/src/app/ui/elements/scheme/bound/styles.less new file mode 100644 index 0000000000..f90559a55d --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/bound/styles.less @@ -0,0 +1,7 @@ +@import '../../../styles/variables.less'; + + +:host { + position: relative; + display: block; +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/bound/template.html b/application/client/src/app/ui/elements/scheme/bound/template.html new file mode 100644 index 0000000000..aad7703c56 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/bound/template.html @@ -0,0 +1,5 @@ +

{{name}}

+ diff --git a/application/client/src/app/ui/elements/scheme/component.ts b/application/client/src/app/ui/elements/scheme/component.ts new file mode 100644 index 0000000000..97e46f8b75 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/component.ts @@ -0,0 +1,89 @@ +import { + Component, + ChangeDetectorRef, + AfterContentInit, + AfterViewInit, + AfterContentChecked, + Input, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { SchemeProvider } from './provider'; +import { FieldDesc } from '@platform/types/bindings'; +import { WrappedField } from './field'; + +@Component({ + selector: 'app-settings-scheme', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class SettingsScheme + extends ChangesDetector + implements AfterContentInit, AfterContentChecked, AfterViewInit +{ + @Input() provider!: SchemeProvider; + + public single: FieldDesc[] = []; + public bound: Map = new Map(); + + protected id: string | undefined; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentChecked(): void { + if (this.id === undefined || this.provider === undefined) { + return; + } + if (this.id === this.provider.id) { + return; + } + this.ngAfterViewInit(); + } + public ngAfterContentInit(): void {} + + public ngAfterViewInit(): void { + this.id = this.provider.id; + this.provider + .get() + .then((fields) => { + let bindings: string[] = []; + fields.forEach((field) => { + const wrapped = new WrappedField(field); + if (wrapped.binding) { + bindings.push(wrapped.id); + if (!bindings.includes(wrapped.binding)) { + bindings.push(wrapped.binding); + } + let bound = this.bound.get(wrapped.binding); + bound = bound ? bound : []; + this.bound.set(wrapped.binding, bound); + if (bound.length === 0) { + const master = fields.find( + (field) => new WrappedField(field).id == wrapped.binding, + ); + if (master === undefined) { + // TODO: report error + return; + } + bound.push(master); + } + bound.push(field); + } + }); + this.single = fields.filter( + (field) => !bindings.includes(new WrappedField(field).id), + ); + this.detectChanges(); + }) + .catch((err: Error) => { + this.log().error(`Fail get fields: ${err.message}`); + }); + } +} +export interface SettingsScheme extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/element.ts b/application/client/src/app/ui/elements/scheme/element.ts new file mode 100644 index 0000000000..26876a7b95 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element.ts @@ -0,0 +1,153 @@ +import { ValueInput } from '@platform/types/bindings'; +import { Subject } from '@platform/env/subscription'; +/** +export type ValueInput = + | { Checkbox: boolean } + | { Number: number } + | { String: string } + | { Numbers: [Array, number] } + | { Strings: [Array, string] } + | { NamedBools: Array<[string, boolean]> } + | { NamedNumbers: Array<[string, number]> } + | { NamedStrings: Array<[string, string]> } + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map } + | 'Directories' + | 'Files' + | 'File' + | 'Directory' + | { Bound: { output: ValueInput; inputs: Array } }; + + + + */ + +export class CheckboxElement { + public value: boolean; + constructor(public defaults: boolean = false) { + this.value = defaults; + } +} + +export class InputElement { + public value: T | undefined; + constructor(public defaults: T) { + this.value = defaults; + } +} + +export class ListElement { + public value: T | undefined; + public items: T[]; + constructor(defaults: T | undefined, items: T[]) { + this.value = defaults; + this.items = items; + } +} + +export class NamedValuesElement { + public value: T | undefined; + public items: { key: string; value: T }[]; + constructor(defaults: T | undefined, items: { key: string; value: T }[]) { + this.value = defaults; + this.items = items; + } +} + +export interface ChangeEvent { + uuid: string; + value: any; +} + +export class Element { + public checkbox: CheckboxElement | undefined; + public numeric_list: ListElement | undefined; + public strings_list: ListElement | undefined; + public string_input: InputElement | undefined; + public number_input: InputElement | undefined; + public named: NamedValuesElement | undefined; + + public readonly changed: Subject = new Subject(); + + constructor(protected readonly uuid: string, protected readonly origin: ValueInput) { + this.checkbox = Element.as_checkbox(origin); + this.numeric_list = Element.as_numbers_list(origin); + this.strings_list = Element.as_string_list(origin); + this.string_input = Element.as_string(origin); + this.number_input = Element.as_number(origin); + this.named = Element.as_named_bools(origin); + } + + public change() { + if (this.checkbox !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.checkbox.value }); + } else if (this.numeric_list !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.numeric_list.value }); + } else if (this.strings_list !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.strings_list.value }); + } else if (this.string_input !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.string_input.value }); + } else if (this.number_input !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.number_input.value }); + } else if (this.named !== undefined) { + this.changed.emit({ uuid: this.uuid, value: this.named.value }); + } + } + + public setValue(value: any) { + if (this.checkbox !== undefined) { + this.checkbox.value = value; + } else if (this.numeric_list !== undefined) { + this.numeric_list.value = value; + } else if (this.strings_list !== undefined) { + this.strings_list.value = value; + } else if (this.string_input !== undefined) { + this.string_input.value = value; + } else if (this.number_input !== undefined) { + this.number_input.value = value; + } else if (this.named !== undefined) { + this.named.value = value; + } + } + + static as_checkbox(origin: ValueInput): CheckboxElement | undefined { + const vl = origin as { Checkbox: boolean }; + return typeof vl.Checkbox === 'boolean' ? new CheckboxElement(vl.Checkbox) : undefined; + } + + static as_string(origin: ValueInput): InputElement | undefined { + const vl = origin as { String: string }; + return typeof vl.String === 'string' ? new InputElement(vl.String) : undefined; + } + + static as_number(origin: ValueInput): InputElement | undefined { + const vl = origin as { Number: number }; + return typeof vl.Number === 'number' ? new InputElement(vl.Number) : undefined; + } + + static as_numbers_list(origin: ValueInput): ListElement | undefined { + const vl = origin as { Numbers: [Array, number] }; + return vl.Numbers ? new ListElement(vl.Numbers[1], vl.Numbers[0]) : undefined; + } + + static as_string_list(origin: ValueInput): ListElement | undefined { + const vl = origin as { Strings: [Array, string] }; + return vl.Strings ? new ListElement(vl.Strings[1], vl.Strings[0]) : undefined; + } + + static as_named_bools(origin: ValueInput): NamedValuesElement | undefined { + const vl = origin as { NamedBools: Array<[string, boolean]> }; + return vl.NamedBools instanceof Array + ? vl.NamedBools.length > 0 + ? new NamedValuesElement( + vl.NamedBools[0][1], + vl.NamedBools.map((item) => { + return { key: item[0], value: item[1] }; + }), + ) + : undefined + : undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts new file mode 100644 index 0000000000..83ce705012 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -0,0 +1,62 @@ +import { + Component, + ChangeDetectorRef, + Input, + AfterViewInit, + AfterContentInit, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { SchemeProvider } from '../provider'; +import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; +import { Element } from '../element'; + +@Component({ + selector: 'app-settings-scheme-entry', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class SchemeEntry extends ChangesDetector implements AfterViewInit, AfterContentInit { + @Input() provider!: SchemeProvider; + @Input() field!: FieldDesc; + + public pending: LazyFieldDesc | undefined; + public loaded: StaticFieldDesc | undefined; + public element: Element | undefined; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void { + this.pending = (this.field as { Lazy: LazyFieldDesc }).Lazy; + this.loaded = (this.field as { Static: StaticFieldDesc }).Static; + if (this.loaded) { + this.element = new Element(this.loaded.id, this.loaded.interface); + return; + } + this.env().subscriber.register( + this.provider + .subjects() + .get() + .loaded.subscribe((loaded: StaticFieldDesc) => { + if (!this.pending || this.pending.id !== loaded.id) { + return; + } + this.loaded = loaded; + this.pending = undefined; + this.element = new Element(this.loaded.id, this.loaded.interface); + this.detectChanges(); + }), + ); + } + + public ngAfterViewInit(): void { + this.detectChanges(); + } +} +export interface SchemeEntry extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less new file mode 100644 index 0000000000..f90559a55d --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -0,0 +1,7 @@ +@import '../../../styles/variables.less'; + + +:host { + position: relative; + display: block; +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/template.html b/application/client/src/app/ui/elements/scheme/entry/template.html new file mode 100644 index 0000000000..6c7c4cd631 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/template.html @@ -0,0 +1,48 @@ + + diff --git a/application/client/src/app/ui/elements/scheme/field.ts b/application/client/src/app/ui/elements/scheme/field.ts new file mode 100644 index 0000000000..19d2552c84 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/field.ts @@ -0,0 +1,30 @@ +import { FieldDesc, LazyFieldDesc, StaticFieldDesc } from '@platform/types/bindings'; + +export class WrappedField { + public id: string; + public name: string; + public desc: string; + public binding: string | undefined; + public pending: LazyFieldDesc | undefined; + public loaded: StaticFieldDesc | undefined; + + constructor(field: FieldDesc) { + const pending = (field as { Lazy: LazyFieldDesc }).Lazy; + const loaded = (field as { Static: StaticFieldDesc }).Static; + this.pending = pending; + this.loaded = loaded; + if (pending) { + this.id = pending.id; + this.desc = pending.desc; + this.name = pending.name; + this.binding = !pending.binding ? undefined : pending.binding; + } else if (loaded) { + this.id = loaded.id; + this.desc = loaded.desc; + this.name = loaded.name; + this.binding = !loaded.binding ? undefined : loaded.binding; + } else { + throw new Error(`Invalid FieldDesc: ${JSON.stringify(field)}`); + } + } +} diff --git a/application/client/src/app/ui/elements/scheme/inner/component.ts b/application/client/src/app/ui/elements/scheme/inner/component.ts new file mode 100644 index 0000000000..88f414759f --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/inner/component.ts @@ -0,0 +1,37 @@ +import { + Component, + ChangeDetectorRef, + Input, + AfterViewInit, + AfterContentInit, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { SchemeProvider } from '../provider'; +import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; +import { Element } from '../element'; + +@Component({ + selector: 'app-settings-scheme-element', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class SchemeEntryElement extends ChangesDetector implements AfterViewInit, AfterContentInit { + @Input() provider!: SchemeProvider; + @Input() element!: Element; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void {} + + public ngAfterViewInit(): void { + this.detectChanges(); + } +} +export interface SchemeEntryElement extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/inner/styles.less b/application/client/src/app/ui/elements/scheme/inner/styles.less new file mode 100644 index 0000000000..f90559a55d --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/inner/styles.less @@ -0,0 +1,7 @@ +@import '../../../styles/variables.less'; + + +:host { + position: relative; + display: block; +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html new file mode 100644 index 0000000000..0e80a37bc2 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -0,0 +1,30 @@ + + + + + + {{item}} + + + + + + + {{item}} + + + + + + + {{item.key}} + + + + + + + + + + diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts new file mode 100644 index 0000000000..f41683c573 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -0,0 +1,34 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatSelectModule } from '@angular/material/select'; + +import { SettingsScheme } from './component'; +import { SchemeEntry } from './entry/component'; +import { SchemeEntryBound } from './bound/component'; +import { SchemeEntryElement } from './inner/component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatCheckboxModule, + MatCardModule, + MatDividerModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatSelectModule, + ], + declarations: [SettingsScheme, SchemeEntry, SchemeEntryBound, SchemeEntryElement], + exports: [SettingsScheme], + bootstrap: [SettingsScheme, SchemeEntry, SchemeEntryBound, SchemeEntryElement], +}) +export class SettingsSchemeModule {} diff --git a/application/client/src/app/ui/elements/scheme/provider.ts b/application/client/src/app/ui/elements/scheme/provider.ts new file mode 100644 index 0000000000..86417c9415 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/provider.ts @@ -0,0 +1,17 @@ +import { FieldDesc, StaticFieldDesc } from '@platform/types/bindings'; +import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; +import { unique } from '@platform/env/sequence'; + +export abstract class SchemeProvider extends Subscriber { + public readonly id: string; + constructor() { + super(); + this.id = unique(); + } + public abstract get(): Promise; + public abstract destroy(): Promise; + public abstract subjects(): Subjects<{ + loaded: Subject; + error: Subject>; + }>; +} diff --git a/application/client/src/app/ui/elements/scheme/styles.less b/application/client/src/app/ui/elements/scheme/styles.less new file mode 100644 index 0000000000..d34de6b5f9 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/styles.less @@ -0,0 +1,7 @@ +@import '../../styles/variables.less'; + + +:host { + + +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/template.html b/application/client/src/app/ui/elements/scheme/template.html new file mode 100644 index 0000000000..a1a2c0424b --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/template.html @@ -0,0 +1,11 @@ + + + diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index 43489d2ad4..d3b3d3daba 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -49,6 +49,9 @@ export class SetupObserve this.state.subjects.get().sources.subscribe(() => { this.detectChanges(); }), + this.state.subjects.get().updated.subscribe(() => { + this.detectChanges(); + }), ); } } diff --git a/application/client/src/app/ui/tabs/setup/module.ts b/application/client/src/app/ui/tabs/setup/module.ts index a41f99cebe..6f2cde53d6 100644 --- a/application/client/src/app/ui/tabs/setup/module.ts +++ b/application/client/src/app/ui/tabs/setup/module.ts @@ -4,7 +4,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; - +import { SettingsSchemeModule } from '@elements/scheme/module'; import { SetupObserve } from './component'; @NgModule({ @@ -15,6 +15,7 @@ import { SetupObserve } from './component'; MatButtonModule, MatFormFieldModule, MatSelectModule, + SettingsSchemeModule, ], declarations: [SetupObserve], exports: [SetupObserve], diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts new file mode 100644 index 0000000000..f493807430 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -0,0 +1,107 @@ +import { SchemeProvider } from '@elements/scheme/provider'; +import { FieldDesc, StaticFieldDesc } from '@platform/types/bindings'; +import { SourceOrigin, Ident, LazyFieldDesc } from '@platform/types/bindings'; +import { components } from '@service/components'; +import { Logger } from '@env/logs'; +import { + LoadingCancelledEvent, + LoadingDoneEvent, + LoadingErrorEvent, + LoadingErrorsEvent, +} from '@platform/types/components'; +import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; + +export class Proivder extends SchemeProvider { + protected pending: string[] = []; + protected readonly logger = new Logger(`ObserveSetupProivder`); + protected readonly subs: Subjects<{ + loaded: Subject; + error: Subject>; + }> = new Subjects({ + loaded: new Subject(), + error: new Subject(), + }); + constructor(protected readonly origin: SourceOrigin, protected readonly target: string) { + super(); + this.register( + components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), + components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), + components.subjects.get().LoadingErrors.subscribe(this.onLoadingErrors.bind(this)), + components.subjects + .get() + .LoadingCancelled.subscribe(this.onLoadingCancelled.bind(this)), + ); + } + + public override subjects(): Subjects<{ + loaded: Subject; + error: Subject>; + }> { + return this.subs; + } + public override get(): Promise { + this.pending.length === 0 && components.abort(this.pending); + this.pending = []; + return components + .getOptions(this.origin, [this.target]) + .then((map: Map) => { + const fields = map.get(this.target); + if (!fields) { + return Promise.resolve([]); + } + this.pending = fields + .map((field: FieldDesc) => { + const lazy = field as { Lazy: LazyFieldDesc }; + if (lazy.Lazy) { + return lazy.Lazy.id; + } else { + return undefined; + } + }) + .filter((id) => id !== undefined); + return Promise.resolve(fields === undefined ? [] : fields); + }); + } + + public override destroy(): Promise { + this.unsubscribe(); + this.subs.destroy(); + if (this.pending.length === 0) { + return Promise.resolve(); + } else { + return components.abort(this.pending).catch((err: Error) => { + this.logger.error(`Fail to abort loading of pending fields: ${err.message}`); + }); + } + } + + protected onLoadingDone(event: LoadingDoneEvent) { + event.fields.forEach((field) => { + if (!this.pending.includes(field.id)) { + return; + } + this.subs.get().loaded.emit(field); + }); + } + protected onLoadingError(event: LoadingErrorEvent) { + const errors = new Map(); + event.fields.forEach((id) => { + if (!this.pending.includes(id)) { + return; + } + errors.set(id, event.error); + }); + errors.size > 0 && this.subs.get().error.emit(errors); + } + protected onLoadingErrors(event: LoadingErrorsEvent) { + const errors = new Map(); + event.errors.forEach((error) => { + if (!this.pending.includes(error.id)) { + return; + } + errors.set(error.id, error.err); + }); + errors.size > 0 && this.subs.get().error.emit(errors); + } + protected onLoadingCancelled(event: LoadingCancelledEvent) {} +} diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 68e6b65a78..5f10f983d5 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -3,6 +3,7 @@ import { components } from '@service/components'; import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; import { TabControls } from '@service/session'; +import { Proivder } from './provider'; export interface IApi { finish(): Promise; @@ -18,10 +19,12 @@ export class State extends Subscriber { sources: Subject; parsers: Subject; error: Subject; + updated: Subject; }> = new Subjects({ sources: new Subject(), parsers: new Subject(), error: new Subject(), + updated: new Subject(), }); public selected: { source: string; @@ -30,6 +33,13 @@ export class State extends Subscriber { source: '', parser: '', }; + public providers: { + source: Proivder | undefined; + parser: Proivder | undefined; + } = { + source: undefined, + parser: undefined, + }; protected readonly logger = new Logger(`Setup`); @@ -63,12 +73,28 @@ export class State extends Subscriber { } public change(): { - stream(): void; + source(): void; parser(): void; } { return { - stream: (): void => {}, - parser: (): void => {}, + source: (): void => { + if (this.providers.source !== undefined) { + this.providers.source.destroy().catch((err: Error) => { + this.logger.error(`Fail to destroy source provider: ${err.message}`); + }); + } + this.providers.source = new Proivder(this.origin, this.selected.source); + this.subjects.get().updated.emit(); + }, + parser: (): void => { + if (this.providers.parser !== undefined) { + this.providers.parser.destroy().catch((err: Error) => { + this.logger.error(`Fail to destroy parser provider: ${err.message}`); + }); + } + this.providers.parser = new Proivder(this.origin, this.selected.parser); + this.subjects.get().updated.emit(); + }, }; } } diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index 647a961c01..5a49340fee 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -4,7 +4,7 @@
-
+
+ + +
diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 2eca6d2a13..5f0d66397f 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -29,15 +29,14 @@ export type StaticFieldDesc = { name: string; desc: string; required: boolean; - default: Value | null; interface: ValueInput; binding: string | null; }; export type Value = | { Boolean: boolean } - | { Number: bigint } - | { Numbers: Array } + | { Number: number } + | { Numbers: Array } | { String: string } | { Strings: Array } | { Directories: Array } @@ -50,12 +49,14 @@ export type Value = | { KeyStrings: Map }; export type ValueInput = - | 'Checkbox' - | 'Number' - | 'String' - | { Numbers: Array } - | { Strings: Array } - | { NamedValues: Array<[string, Value]> } + | { Checkbox: boolean } + | { Number: number } + | { String: string } + | { Numbers: [Array, number] } + | { Strings: [Array, string] } + | { NamedBools: Array<[string, boolean]> } + | { NamedNumbers: Array<[string, number]> } + | { NamedStrings: Array<[string, string]> } | { KeyNumber: Map } | { KeyNumbers: Map } | { KeyString: Map } diff --git a/cli/chipmunk-cli/Cargo.lock b/cli/chipmunk-cli/Cargo.lock index 838a2e2787..cb81f94e4f 100644 --- a/cli/chipmunk-cli/Cargo.lock +++ b/cli/chipmunk-cli/Cargo.lock @@ -766,6 +766,26 @@ dependencies = [ "libc", ] +[[package]] +name = "libudev" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" +dependencies = [ + "libc", + "libudev-sys", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "lock_api" version = "0.4.13" @@ -1024,6 +1044,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1259,6 +1285,7 @@ dependencies = [ "core-foundation", "core-foundation-sys", "io-kit-sys", + "libudev", "mach2", "nix 0.26.4", "scopeguard", @@ -1370,6 +1397,7 @@ dependencies = [ "pcap-parser", "regex", "serde", + "serialport", "shellexpand", "socket2", "stypes", From ea6279ff4e9f99ad24ec2a1f94ad1ff4559269e7 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 28 Apr 2025 16:00:59 +0200 Subject: [PATCH 022/121] Remove special bound field component --- .../indexer/sources/src/serial/serialport.rs | 31 +++- .../app/ui/elements/scheme/bound/component.ts | 96 ------------ .../app/ui/elements/scheme/bound/styles.less | 7 - .../ui/elements/scheme/bound/template.html | 5 - .../src/app/ui/elements/scheme/component.ts | 31 +--- .../src/app/ui/elements/scheme/element.ts | 138 ++++++++++++++---- .../app/ui/elements/scheme/entry/component.ts | 94 +++++++++--- .../app/ui/elements/scheme/entry/styles.less | 15 +- .../ui/elements/scheme/entry/template.html | 46 +----- .../ui/elements/scheme/inner/template.html | 14 +- .../src/app/ui/elements/scheme/module.ts | 5 +- .../src/app/ui/elements/scheme/provider.ts | 24 ++- .../src/app/ui/elements/scheme/template.html | 8 +- .../client/src/app/ui/tabs/setup/provider.ts | 52 ++++--- 14 files changed, 297 insertions(+), 269 deletions(-) delete mode 100644 application/client/src/app/ui/elements/scheme/bound/component.ts delete mode 100644 application/client/src/app/ui/elements/scheme/bound/styles.less delete mode 100644 application/client/src/app/ui/elements/scheme/bound/template.html diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index ed823cc1bf..cce09511ea 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -6,7 +6,7 @@ use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, }; -use std::{io, str}; +use std::{collections::HashMap, io, str}; use stypes::{FieldDesc, LazyFieldDesc, StaticFieldDesc, Value, ValueInput}; use tokio::time::{Duration, sleep}; use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilderExt, SerialStream, StopBits}; @@ -265,6 +265,12 @@ impl ComponentDescriptor for Descriptor { interface: ValueInput::String(String::new()), binding: None, }), + FieldDesc::Lazy(LazyFieldDesc { + id: FIELD_PORTS_LIST.to_string(), + name: "Ports".to_string(), + desc: "List of available ports".to_string(), + binding: Some(FIELD_PATH.to_string()), + }), FieldDesc::Static(StaticFieldDesc { id: FIELD_BAUD_RATE.to_owned(), name: "Boud rate".to_owned(), @@ -345,12 +351,6 @@ impl ComponentDescriptor for Descriptor { interface: ValueInput::Numbers(vec![0, 10, 20, 30, 40, 50], 0), binding: None, }), - FieldDesc::Lazy(LazyFieldDesc { - id: FIELD_PORTS_LIST.to_string(), - name: "Ports".to_string(), - desc: "List of available ports".to_string(), - binding: Some(FIELD_PATH.to_string()), - }), ]) } fn lazy_fields_getter( @@ -374,6 +374,23 @@ impl ComponentDescriptor for Descriptor { })]) }) } + fn validate( + &self, + _origin: &stypes::SourceOrigin, + fields: &[stypes::Field], + ) -> HashMap { + let mut errors = HashMap::new(); + fields.iter().for_each(|field| { + if field.id == FIELD_PATH { + if let Value::String(path) = &field.value { + if path.trim().is_empty() { + errors.insert(field.id.clone(), "Path cannot be empty".to_owned()); + } + } + } + }); + errors + } } impl components::Component for SerialSource { diff --git a/application/client/src/app/ui/elements/scheme/bound/component.ts b/application/client/src/app/ui/elements/scheme/bound/component.ts deleted file mode 100644 index 99bcbdc44a..0000000000 --- a/application/client/src/app/ui/elements/scheme/bound/component.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterViewInit, - AfterContentInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { SchemeProvider } from '../provider'; -import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; -import { Element, ChangeEvent } from '../element'; -import { WrappedField } from '../field'; -@Component({ - selector: 'app-settings-scheme-entry-bound', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class SchemeEntryBound extends ChangesDetector implements AfterViewInit, AfterContentInit { - @Input() provider!: SchemeProvider; - @Input() fields!: FieldDesc[]; - - public pending: Map = new Map(); - public loaded: Map = new Map(); - public elements: Map = new Map(); - public name!: string; - public desc!: string; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.fields.forEach((field) => { - const pending = (field as { Lazy: LazyFieldDesc }).Lazy; - const loaded = (field as { Static: StaticFieldDesc }).Static; - if (loaded) { - const element = new Element(loaded.id, loaded.interface); - this.env().subscriber.register( - element.changed.subscribe(this.onChanges.bind(this)), - ); - this.elements.set(loaded.id, element); - if (loaded.binding === undefined) { - this.desc = loaded.desc; - this.name = loaded.name; - } - this.loaded.set(loaded.id, loaded); - return; - } - this.pending.set(pending.id, pending); - }); - this.env().subscriber.register( - this.provider - .subjects() - .get() - .loaded.subscribe((loaded: StaticFieldDesc) => { - if (!this.pending.has(loaded.id)) { - return; - } - this.loaded.set(loaded.id, loaded); - const element = new Element(loaded.id, loaded.interface); - this.env().subscriber.register( - element.changed.subscribe(this.onChanges.bind(this)), - ); - this.elements.set(loaded.id, element); - this.pending.delete(loaded.id); - this.detectChanges(); - }), - ); - } - - public ngAfterViewInit(): void { - this.detectChanges(); - } - - public onChanges(event: ChangeEvent) { - const owner = this.loaded.get(event.uuid); - if (!owner) { - return; - } - if (!owner.binding) { - return; - } - const bound = this.elements.get(owner.binding); - if (bound === undefined) { - return; - } - bound.setValue(event.value); - this.detectChanges(); - } -} -export interface SchemeEntryBound extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/bound/styles.less b/application/client/src/app/ui/elements/scheme/bound/styles.less deleted file mode 100644 index f90559a55d..0000000000 --- a/application/client/src/app/ui/elements/scheme/bound/styles.less +++ /dev/null @@ -1,7 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: block; -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/bound/template.html b/application/client/src/app/ui/elements/scheme/bound/template.html deleted file mode 100644 index aad7703c56..0000000000 --- a/application/client/src/app/ui/elements/scheme/bound/template.html +++ /dev/null @@ -1,5 +0,0 @@ -

{{name}}

- diff --git a/application/client/src/app/ui/elements/scheme/component.ts b/application/client/src/app/ui/elements/scheme/component.ts index 97e46f8b75..8a10589444 100644 --- a/application/client/src/app/ui/elements/scheme/component.ts +++ b/application/client/src/app/ui/elements/scheme/component.ts @@ -27,8 +27,7 @@ export class SettingsScheme { @Input() provider!: SchemeProvider; - public single: FieldDesc[] = []; - public bound: Map = new Map(); + public fields: FieldDesc[] = []; protected id: string | undefined; @@ -52,33 +51,7 @@ export class SettingsScheme this.provider .get() .then((fields) => { - let bindings: string[] = []; - fields.forEach((field) => { - const wrapped = new WrappedField(field); - if (wrapped.binding) { - bindings.push(wrapped.id); - if (!bindings.includes(wrapped.binding)) { - bindings.push(wrapped.binding); - } - let bound = this.bound.get(wrapped.binding); - bound = bound ? bound : []; - this.bound.set(wrapped.binding, bound); - if (bound.length === 0) { - const master = fields.find( - (field) => new WrappedField(field).id == wrapped.binding, - ); - if (master === undefined) { - // TODO: report error - return; - } - bound.push(master); - } - bound.push(field); - } - }); - this.single = fields.filter( - (field) => !bindings.includes(new WrappedField(field).id), - ); + this.fields = fields; this.detectChanges(); }) .catch((err: Error) => { diff --git a/application/client/src/app/ui/elements/scheme/element.ts b/application/client/src/app/ui/elements/scheme/element.ts index 26876a7b95..ecfb829463 100644 --- a/application/client/src/app/ui/elements/scheme/element.ts +++ b/application/client/src/app/ui/elements/scheme/element.ts @@ -1,4 +1,4 @@ -import { ValueInput } from '@platform/types/bindings'; +import { Value, ValueInput } from '@platform/types/bindings'; import { Subject } from '@platform/env/subscription'; /** export type ValueInput = @@ -19,46 +19,64 @@ export type ValueInput = | 'File' | 'Directory' | { Bound: { output: ValueInput; inputs: Array } }; - - - */ -export class CheckboxElement { +interface ValueGetter { + getValue(): Value; +} + +export class CheckboxElement implements ValueGetter { public value: boolean; constructor(public defaults: boolean = false) { this.value = defaults; } + public getValue(): Value { + return { Boolean: this.value }; + } } -export class InputElement { - public value: T | undefined; - constructor(public defaults: T) { +export class InputElement implements ValueGetter { + public value: T; + constructor(public defaults: T, protected readonly getter: (value: T) => Value) { this.value = defaults; } + public getValue(): Value { + return this.getter(this.value); + } } -export class ListElement { - public value: T | undefined; +export class ListElement implements ValueGetter { + public value: T; public items: T[]; - constructor(defaults: T | undefined, items: T[]) { + constructor(defaults: T, items: T[], protected readonly getter: (value: T) => Value) { this.value = defaults; this.items = items; } + public getValue(): Value { + return this.getter(this.value); + } } -export class NamedValuesElement { - public value: T | undefined; +export class NamedValuesElement implements ValueGetter { + public value: T; public items: { key: string; value: T }[]; - constructor(defaults: T | undefined, items: { key: string; value: T }[]) { + constructor( + defaults: T, + items: { key: string; value: T }[], + protected readonly getter: (value: T) => Value, + ) { this.value = defaults; this.items = items; } + public getValue(): Value { + return this.getter(this.value); + } } export interface ChangeEvent { uuid: string; - value: any; + inner: any; + value: Value; } export class Element { @@ -81,18 +99,22 @@ export class Element { } public change() { + const value = this.getValue(); + if (!value) { + return; + } if (this.checkbox !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.checkbox.value }); + this.changed.emit({ uuid: this.uuid, inner: this.checkbox.value, value }); } else if (this.numeric_list !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.numeric_list.value }); + this.changed.emit({ uuid: this.uuid, inner: this.numeric_list.value, value }); } else if (this.strings_list !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.strings_list.value }); + this.changed.emit({ uuid: this.uuid, inner: this.strings_list.value, value }); } else if (this.string_input !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.string_input.value }); + this.changed.emit({ uuid: this.uuid, inner: this.string_input.value, value }); } else if (this.number_input !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.number_input.value }); + this.changed.emit({ uuid: this.uuid, inner: this.number_input.value, value }); } else if (this.named !== undefined) { - this.changed.emit({ uuid: this.uuid, value: this.named.value }); + this.changed.emit({ uuid: this.uuid, inner: this.named.value, value }); } } @@ -112,6 +134,35 @@ export class Element { } } + /* + | { Numbers: Array } + | { Strings: Array } + | { Directories: Array } + | { Files: Array } + | { File: string } + | { Directory: string } + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map }; +*/ + public getValue(): Value | undefined { + if (this.checkbox !== undefined) { + return this.checkbox.getValue(); + } else if (this.numeric_list !== undefined) { + return this.numeric_list.getValue(); + } else if (this.strings_list !== undefined) { + return this.strings_list.getValue(); + } else if (this.string_input !== undefined) { + return this.string_input.getValue(); + } else if (this.number_input !== undefined) { + return this.number_input.getValue(); + } else if (this.named !== undefined) { + return this.named.getValue(); + } + return undefined; + } + static as_checkbox(origin: ValueInput): CheckboxElement | undefined { const vl = origin as { Checkbox: boolean }; return typeof vl.Checkbox === 'boolean' ? new CheckboxElement(vl.Checkbox) : undefined; @@ -119,22 +170,54 @@ export class Element { static as_string(origin: ValueInput): InputElement | undefined { const vl = origin as { String: string }; - return typeof vl.String === 'string' ? new InputElement(vl.String) : undefined; + return typeof vl.String === 'string' + ? new InputElement(vl.String, (value: string): Value => { + return { String: value }; + }) + : undefined; } static as_number(origin: ValueInput): InputElement | undefined { const vl = origin as { Number: number }; - return typeof vl.Number === 'number' ? new InputElement(vl.Number) : undefined; + return typeof vl.Number === 'number' + ? new InputElement(vl.Number, (value: number): Value => { + return { + Number: + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseInt(value, 10) + : 0, + }; + }) + : undefined; } static as_numbers_list(origin: ValueInput): ListElement | undefined { const vl = origin as { Numbers: [Array, number] }; - return vl.Numbers ? new ListElement(vl.Numbers[1], vl.Numbers[0]) : undefined; + return vl.Numbers + ? new ListElement(vl.Numbers[1], vl.Numbers[0], (value: number): Value => { + return { + Number: + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseInt(value, 10) + : 0, + }; + }) + : undefined; } static as_string_list(origin: ValueInput): ListElement | undefined { const vl = origin as { Strings: [Array, string] }; - return vl.Strings ? new ListElement(vl.Strings[1], vl.Strings[0]) : undefined; + return vl.Strings + ? new ListElement(vl.Strings[1], vl.Strings[0], (value: string): Value => { + return { + String: value, + }; + }) + : undefined; } static as_named_bools(origin: ValueInput): NamedValuesElement | undefined { @@ -146,6 +229,11 @@ export class Element { vl.NamedBools.map((item) => { return { key: item[0], value: item[1] }; }), + (value: boolean): Value => { + return { + Boolean: value, + }; + }, ) : undefined : undefined; diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts index 83ce705012..3326a47fd2 100644 --- a/application/client/src/app/ui/elements/scheme/entry/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -8,9 +8,10 @@ import { import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import { SchemeProvider } from '../provider'; +import { SchemeProvider, ForcedValueChanges } from '../provider'; import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; -import { Element } from '../element'; +import { ChangeEvent, Element } from '../element'; +import { WrappedField } from '../field'; @Component({ selector: 'app-settings-scheme-entry', @@ -27,6 +28,9 @@ export class SchemeEntry extends ChangesDetector implements AfterViewInit, After public pending: LazyFieldDesc | undefined; public loaded: StaticFieldDesc | undefined; public element: Element | undefined; + public name!: string; + public desc!: string; + public error: string | undefined; constructor(cdRef: ChangeDetectorRef) { super(cdRef); @@ -35,28 +39,82 @@ export class SchemeEntry extends ChangesDetector implements AfterViewInit, After public ngAfterContentInit(): void { this.pending = (this.field as { Lazy: LazyFieldDesc }).Lazy; this.loaded = (this.field as { Static: StaticFieldDesc }).Static; - if (this.loaded) { - this.element = new Element(this.loaded.id, this.loaded.interface); - return; - } + const wrapped = new WrappedField(this.field); + this.name = wrapped.name; + this.desc = wrapped.desc; + this.init(); this.env().subscriber.register( - this.provider - .subjects() - .get() - .loaded.subscribe((loaded: StaticFieldDesc) => { - if (!this.pending || this.pending.id !== loaded.id) { - return; - } - this.loaded = loaded; - this.pending = undefined; - this.element = new Element(this.loaded.id, this.loaded.interface); - this.detectChanges(); - }), + this.provider.subjects.get().loaded.subscribe((loaded: StaticFieldDesc) => { + if (!this.pending || this.pending.id !== loaded.id) { + return; + } + this.loaded = loaded; + this.pending = undefined; + this.init(); + this.detectChanges(); + }), ); } public ngAfterViewInit(): void { this.detectChanges(); } + + init() { + if (this.loaded) { + this.element = new Element(this.loaded.id, this.loaded.interface); + this.env().subscriber.register( + this.element.changed.subscribe(this.onSelfChanges.bind(this)), + ); + this.env().subscriber.register( + this.provider.subjects.get().forced.subscribe(this.onFieldsChanges.bind(this)), + ); + this.env().subscriber.register( + this.provider.subjects.get().error.subscribe(this.onError.bind(this)), + ); + const value = this.element.getValue(); + value && this.provider.setValue(this.loaded.id, value); + return; + } + } + + protected onError(errs: Map) { + const prev = this.error; + if (!this.element || !this.loaded) { + this.error = undefined; + return; + } + this.error = errs.get(this.loaded.id); + if (prev !== this.error) { + this.detectChanges(); + } + } + + protected onSelfChanges(event: ChangeEvent) { + if (!this.element || !this.loaded) { + return; + } + const value = this.element.getValue(); + if (value) { + if (this.loaded.binding) { + this.provider.force(this.loaded.binding, event.inner); + } + this.provider.setValue(new WrappedField(this.field).id, value); + } + } + + protected onFieldsChanges(event: ForcedValueChanges) { + if (!this.loaded || !this.element) { + return; + } + if (event.target !== this.loaded.id) { + return; + } + this.element.setValue(event.value); + const value = this.element.getValue(); + if (value) { + this.provider.setValue(this.loaded.id, value); + } + } } export interface SchemeEntry extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index f90559a55d..d169471308 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -3,5 +3,18 @@ :host { position: relative; - display: block; + display: inline-block; + padding: 2px; + margin: 6px; + height: 42px; + p.title { + display: block; + position: absolute; + color: @scheme-color-2; + z-index: 1; + padding: 3px 0px 0 16px; + } + p.error { + color: @scheme-color-error-light; + } } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/template.html b/application/client/src/app/ui/elements/scheme/entry/template.html index 6c7c4cd631..d358b48256 100644 --- a/application/client/src/app/ui/elements/scheme/entry/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/template.html @@ -1,48 +1,6 @@ +

{{name}}

+

{{error}}

- diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html index 0e80a37bc2..0ca70ae5df 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -22,9 +22,19 @@
- + - +
diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts index f41683c573..239b97e356 100644 --- a/application/client/src/app/ui/elements/scheme/module.ts +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -11,7 +11,6 @@ import { MatSelectModule } from '@angular/material/select'; import { SettingsScheme } from './component'; import { SchemeEntry } from './entry/component'; -import { SchemeEntryBound } from './bound/component'; import { SchemeEntryElement } from './inner/component'; @NgModule({ @@ -27,8 +26,8 @@ import { SchemeEntryElement } from './inner/component'; MatButtonModule, MatSelectModule, ], - declarations: [SettingsScheme, SchemeEntry, SchemeEntryBound, SchemeEntryElement], + declarations: [SettingsScheme, SchemeEntry, SchemeEntryElement], exports: [SettingsScheme], - bootstrap: [SettingsScheme, SchemeEntry, SchemeEntryBound, SchemeEntryElement], + bootstrap: [SettingsScheme, SchemeEntry, SchemeEntryElement], }) export class SettingsSchemeModule {} diff --git a/application/client/src/app/ui/elements/scheme/provider.ts b/application/client/src/app/ui/elements/scheme/provider.ts index 86417c9415..f5e687b061 100644 --- a/application/client/src/app/ui/elements/scheme/provider.ts +++ b/application/client/src/app/ui/elements/scheme/provider.ts @@ -1,17 +1,31 @@ -import { FieldDesc, StaticFieldDesc } from '@platform/types/bindings'; +import { FieldDesc, StaticFieldDesc, Value } from '@platform/types/bindings'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; import { unique } from '@platform/env/sequence'; +export interface ForcedValueChanges { + target: string; + value: any; +} export abstract class SchemeProvider extends Subscriber { public readonly id: string; + public subjects: Subjects<{ + loaded: Subject; + forced: Subject; + error: Subject>; + }> = new Subjects({ + loaded: new Subject(), + forced: new Subject(), + error: new Subject>(), + }); + constructor() { super(); this.id = unique(); } public abstract get(): Promise; public abstract destroy(): Promise; - public abstract subjects(): Subjects<{ - loaded: Subject; - error: Subject>; - }>; + public abstract setValue(uuid: string, value: Value): void; + public force(target: string, value: any) { + this.subjects.get().forced.emit({ target, value }); + } } diff --git a/application/client/src/app/ui/elements/scheme/template.html b/application/client/src/app/ui/elements/scheme/template.html index a1a2c0424b..fec17fbb8b 100644 --- a/application/client/src/app/ui/elements/scheme/template.html +++ b/application/client/src/app/ui/elements/scheme/template.html @@ -1,11 +1,5 @@ - - diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index f493807430..7114644829 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -1,6 +1,6 @@ import { SchemeProvider } from '@elements/scheme/provider'; -import { FieldDesc, StaticFieldDesc } from '@platform/types/bindings'; -import { SourceOrigin, Ident, LazyFieldDesc } from '@platform/types/bindings'; +import { Field, FieldDesc, Value } from '@platform/types/bindings'; +import { SourceOrigin, LazyFieldDesc } from '@platform/types/bindings'; import { components } from '@service/components'; import { Logger } from '@env/logs'; import { @@ -9,18 +9,14 @@ import { LoadingErrorEvent, LoadingErrorsEvent, } from '@platform/types/components'; -import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; +import { WrappedField } from '@ui/elements/scheme/field'; export class Proivder extends SchemeProvider { protected pending: string[] = []; protected readonly logger = new Logger(`ObserveSetupProivder`); - protected readonly subs: Subjects<{ - loaded: Subject; - error: Subject>; - }> = new Subjects({ - loaded: new Subject(), - error: new Subject(), - }); + protected readonly values: Map = new Map(); + protected fields: string[] = []; + constructor(protected readonly origin: SourceOrigin, protected readonly target: string) { super(); this.register( @@ -33,12 +29,6 @@ export class Proivder extends SchemeProvider { ); } - public override subjects(): Subjects<{ - loaded: Subject; - error: Subject>; - }> { - return this.subs; - } public override get(): Promise { this.pending.length === 0 && components.abort(this.pending); this.pending = []; @@ -49,6 +39,7 @@ export class Proivder extends SchemeProvider { if (!fields) { return Promise.resolve([]); } + this.fields = fields.map((field) => new WrappedField(field).id); this.pending = fields .map((field: FieldDesc) => { const lazy = field as { Lazy: LazyFieldDesc }; @@ -63,9 +54,30 @@ export class Proivder extends SchemeProvider { }); } + public override setValue(uuid: string, value: Value): void { + if (!this.fields.includes(uuid)) { + this.logger.error(`Field ${uuid} doesn't belong to current provider`); + return; + } + this.values.set(uuid, value); + const fields: Field[] = []; + this.values.forEach((value, id) => { + fields.push({ id, value }); + }); + components + .validate(this.origin, this.target, fields) + .then((errs: Map) => { + // Always forward errors info, even no errors + this.subjects.get().error.emit(errs); + }) + .catch((err: Error) => { + this.logger.error(`Fail to validate settings: ${err.message}`); + }); + } + public override destroy(): Promise { this.unsubscribe(); - this.subs.destroy(); + this.subjects.destroy(); if (this.pending.length === 0) { return Promise.resolve(); } else { @@ -80,7 +92,7 @@ export class Proivder extends SchemeProvider { if (!this.pending.includes(field.id)) { return; } - this.subs.get().loaded.emit(field); + this.subjects.get().loaded.emit(field); }); } protected onLoadingError(event: LoadingErrorEvent) { @@ -91,7 +103,7 @@ export class Proivder extends SchemeProvider { } errors.set(id, event.error); }); - errors.size > 0 && this.subs.get().error.emit(errors); + errors.size > 0 && this.subjects.get().error.emit(errors); } protected onLoadingErrors(event: LoadingErrorsEvent) { const errors = new Map(); @@ -101,7 +113,7 @@ export class Proivder extends SchemeProvider { } errors.set(error.id, error.err); }); - errors.size > 0 && this.subs.get().error.emit(errors); + errors.size > 0 && this.subjects.get().error.emit(errors); } protected onLoadingCancelled(event: LoadingCancelledEvent) {} } From 352def4655fb194418f40a3b52f601568f98fd29 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 30 Apr 2025 21:42:48 +0200 Subject: [PATCH 023/121] Introduce nested dictionary element --- .../apps/indexer/parsers/src/dlt/options.rs | 189 +++++++++++++++--- .../apps/indexer/sources/src/serial/mod.rs | 1 + .../indexer/sources/src/serial/options.rs | 186 +++++++++++++++++ .../indexer/sources/src/serial/serialport.rs | 186 +---------------- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 14 +- .../complex/nested_dictionary/component.ts | 152 ++++++++++++++ .../complex/nested_dictionary/module.ts | 40 ++++ .../scheme/complex/nested_dictionary/state.ts | 134 +++++++++++++ .../nested_dictionary/structure/component.ts | 82 ++++++++ .../nested_dictionary/structure/section.ts | 27 +++ .../nested_dictionary/structure/statentity.ts | 52 +++++ .../nested_dictionary/structure/styles.less | 40 ++++ .../nested_dictionary/structure/template.html | 24 +++ .../complex/nested_dictionary/styles.less | 111 ++++++++++ .../complex/nested_dictionary/summary.ts | 24 +++ .../complex/nested_dictionary/template.html | 21 ++ .../src/app/ui/elements/scheme/element.ts | 41 ++++ .../app/ui/elements/scheme/inner/component.ts | 6 +- .../ui/elements/scheme/inner/template.html | 3 +- .../src/app/ui/elements/scheme/module.ts | 2 + .../parsers/extra/dlt/structure/template.html | 124 ++++++------ .../platform/types/bindings/options.ts | 6 +- 23 files changed, 1185 insertions(+), 282 deletions(-) create mode 100644 application/apps/indexer/sources/src/serial/options.rs create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 05eccc8e67..d4dad1677b 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,42 +1,90 @@ -use std::collections::HashMap; - +use crate::dlt::*; use components::{ComponentDescriptor, StaticFieldResult}; -use tokio::{ - select, - time::{sleep, Duration}, +use dlt_core::{ + read::DltMessageReader, + statistics::{ + collect_statistics, + common::{StatisticInfo, StatisticInfoCollector}, + }, +}; +use std::collections::HashMap; +use std::fmt; +use std::fs::File; +use stypes::{ + FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, ValueInput, }; use tokio_util::sync::CancellationToken; -use crate::dlt::*; - const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ]); -const FIELD_LOG_LEVEL: &str = "log_level"; -const FIELD_STATISTICS: &str = "statistics"; +const FIELD_LOG_LEVEL: &str = "DLT_PARSER_FIELD_LOG_LEVEL"; +const FIELD_FIBEX_FILES: &str = "DLT_PARSER_FIELD_FIBEX_FILES"; +const FIELD_STATISTICS: &str = "DLT_PARSER_FIELD_STATISTICS"; #[derive(Default)] struct Descriptor {} +enum StatFields { + AppIds, + ContextIds, + EcuIds, +} + +impl fmt::Display for StatFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::AppIds => "app_ids", + Self::ContextIds => "context_ids", + Self::EcuIds => "ecu_ids", + } + ) + } +} + impl ComponentDescriptor for Descriptor { - fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { - Ok(vec![ - stypes::FieldDesc::Static(stypes::StaticFieldDesc { + fn fields_getter(&self, origin: &SourceOrigin) -> components::FieldsResult { + let mut options = vec![ + FieldDesc::Static(StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), - name: String::from("Log Level"), - desc: String::from("DLT Log Level"), + name: "Log Level".to_owned(), + desc: "Log Level".to_owned(), required: true, - interface: stypes::ValueInput::KeyString(HashMap::new()), + interface: ValueInput::Strings( + vec![ + "Fatal".to_owned(), + "Error".to_owned(), + "Warn".to_owned(), + "Info".to_owned(), + "Debug".to_owned(), + "Verbose".to_owned(), + ], + "Verbose".to_owned(), + ), binding: None, }), - stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { + FieldDesc::Static(StaticFieldDesc { + id: FIELD_FIBEX_FILES.to_owned(), + name: "Fibex Files".to_owned(), + desc: "Fibex Files".to_owned(), + required: true, + interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), + binding: None, + }), + ]; + if matches!(origin, SourceOrigin::File(..) | SourceOrigin::Files(..)) { + options.push(stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { id: FIELD_STATISTICS.to_owned(), name: String::from("Statistics"), desc: String::from("Collected Statistis Data"), binding: None, - }), - ]) + })); + } + Ok(options) } fn ident(&self) -> stypes::Ident { stypes::Ident { @@ -47,26 +95,107 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: stypes::SourceOrigin, + origin: SourceOrigin, cancel: CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { - let duration = Duration::from_millis(5000); - select! { - _ = sleep(duration) => { - // no cancelation - }, - _ = cancel.cancelled() => { - // cancelled - return Ok(Vec::new()); + let file_paths = match origin { + SourceOrigin::File(fp) => { + vec![fp] + } + SourceOrigin::Files(fps) => fps, + _ => { + return Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { + id: FIELD_STATISTICS.to_owned(), + name: String::from("Example"), + desc: String::from("Example"), + required: true, + interface: ValueInput::KeyString(HashMap::new()), + binding: None, + })]) } }; + let mut stat = StatisticInfo::new(); + let mut error: Option = None; + file_paths.iter().for_each(|file_path| { + if error.is_some() || cancel.is_cancelled() { + return; + } + match File::open(file_path) { + Ok(file) => { + let mut reader = DltMessageReader::new(file, true); + let mut collector = StatisticInfoCollector::default(); + match collect_statistics(&mut reader, &mut collector) { + Ok(()) => { + stat.merge(collector.collect()); + } + Err(err) => { + error = Some(err.to_string()); + } + } + } + Err(err) => { + error = Some(err.to_string()); + } + } + }); + if cancel.is_cancelled() { + return Err(NativeError { + kind: NativeErrorKind::Interrupted, + severity: Severity::WARNING, + message: Some("Operation has been cancelled".to_owned()), + }); + } + if let Some(err) = error { + return Err(NativeError { + kind: NativeErrorKind::Io, + severity: Severity::ERROR, + message: Some(err), + }); + } + static NON_LOG: &str = "non_log"; + static LOG_FATAL: &str = "log_fatal"; + static LOG_ERROR: &str = "log_error"; + static LOG_WARNING: &str = "log_warning"; + static LOG_INFO: &str = "log_info"; + static LOG_DEBUG: &str = "log_debug"; + static LOG_VERBOSE: &str = "log_verbose"; + static LOG_INVALID: &str = "log_invalid"; + let mut converted: HashMap>> = + HashMap::new(); + [ + StatFields::AppIds, + StatFields::ContextIds, + StatFields::EcuIds, + ] + .into_iter() + .for_each(|key| { + let inner = match key { + StatFields::AppIds => &stat.app_ids, + StatFields::ContextIds => &stat.context_ids, + StatFields::EcuIds => &stat.ecu_ids, + }; + let mut entity: HashMap> = HashMap::new(); + inner.into_iter().for_each(|(id, levels)| { + let mut map: HashMap = HashMap::new(); + map.insert(NON_LOG.to_owned(), levels.non_log); + map.insert(LOG_FATAL.to_owned(), levels.log_fatal); + map.insert(LOG_ERROR.to_owned(), levels.log_error); + map.insert(LOG_WARNING.to_owned(), levels.log_warning); + map.insert(LOG_INFO.to_owned(), levels.log_info); + map.insert(LOG_DEBUG.to_owned(), levels.log_debug); + map.insert(LOG_VERBOSE.to_owned(), levels.log_verbose); + map.insert(LOG_INVALID.to_owned(), levels.log_invalid); + entity.insert(id.to_owned(), map); + }); + converted.insert(key.to_string(), entity); + }); Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), + name: String::from("Statistics"), + desc: String::from("Dlt File(s) Statistics data"), required: true, - interface: stypes::ValueInput::KeyString(HashMap::new()), + interface: ValueInput::NestedNumbersMap(converted), binding: None, })]) }) diff --git a/application/apps/indexer/sources/src/serial/mod.rs b/application/apps/indexer/sources/src/serial/mod.rs index d5689fad13..9c23886d3a 100644 --- a/application/apps/indexer/sources/src/serial/mod.rs +++ b/application/apps/indexer/sources/src/serial/mod.rs @@ -1 +1,2 @@ +pub mod options; pub mod serialport; diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs new file mode 100644 index 0000000000..89fe0831ed --- /dev/null +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -0,0 +1,186 @@ +use crate::serial::serialport::SerialSource; +use components::{ComponentDescriptor, StaticFieldResult}; +use std::{collections::HashMap, str}; +use stypes::{FieldDesc, LazyFieldDesc, StaticFieldDesc, Value, ValueInput}; + +const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, +]); + +/// The path to the serial port. +const FIELD_PATH: &str = "SERIAL_SOURCE_FIELD_PATH"; +/// The baud rate for the connection. +const FIELD_BAUD_RATE: &str = "SERIAL_SOURCE_FIELD_BAUD_RATE"; +/// The number of data bits per frame. +const FIELD_DATA_BITS: &str = "SERIAL_SOURCE_FIELD_DATA_BITS"; +/// The flow control setting. +const FIELD_FLOW_CONTROL: &str = "SERIAL_SOURCE_FIELD_FLOW_CONTROL"; +/// The parity setting. +const FIELD_PARITY: &str = "SERIAL_SOURCE_FIELD_PARITY"; +/// The number of stop bits. +const FIELD_STOP_BITS: &str = "SERIAL_SOURCE_FIELD_STOP_BITS"; +/// The delay in sending data, in milliseconds. +const FIELD_SEND_DATA_DELAY: &str = "SERIAL_SOURCE_FIELD_SEND_DATA_DELAY"; +/// Whether the connection is exclusive. +const FIELD_EXCLUSIVE: &str = "SERIAL_SOURCE_FIELD_EXCLUSIVE"; +/// List of ports +const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; + +#[derive(Default)] +struct Descriptor {} + +impl ComponentDescriptor for Descriptor { + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("Serial"), + desc: String::from("Access to Serial Devices"), + uuid: SERIAL_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } + fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_PATH.to_owned(), + name: "Path to dev".to_owned(), + desc: "Full path to serial device/port".to_owned(), + required: true, + interface: ValueInput::String(String::new()), + binding: None, + }), + FieldDesc::Lazy(LazyFieldDesc { + id: FIELD_PORTS_LIST.to_string(), + name: "Ports".to_string(), + desc: "List of available ports".to_string(), + binding: Some(FIELD_PATH.to_string()), + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_BAUD_RATE.to_owned(), + name: "Boud rate".to_owned(), + desc: "Boud rate".to_owned(), + required: true, + interface: ValueInput::Numbers( + vec![ + 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, + 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, + ], + 115200, + ), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_DATA_BITS.to_owned(), + name: "Data bits".to_owned(), + desc: "Data bits".to_owned(), + required: true, + interface: ValueInput::Numbers(vec![8, 7, 6, 5], 8), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_STOP_BITS.to_owned(), + name: "Stop bits".to_owned(), + desc: "Stop bits".to_owned(), + required: true, + interface: ValueInput::Numbers(vec![1, 2], 1), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_FLOW_CONTROL.to_owned(), + name: "Flow control".to_owned(), + desc: "Flow control".to_owned(), + required: true, + interface: ValueInput::Strings( + vec![ + String::from("None"), + String::from("Hardware"), + String::from("Software"), + ], + String::from("None"), + ), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_PARITY.to_owned(), + name: "Parity".to_owned(), + desc: "Parity".to_owned(), + required: true, + interface: ValueInput::Strings( + vec![ + String::from("None"), + String::from("Odd"), + String::from("Even"), + ], + String::from("None"), + ), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_EXCLUSIVE.to_owned(), + name: "Exclusive".to_owned(), + desc: "Exclusive".to_owned(), + required: true, + interface: ValueInput::NamedBools(vec![ + (String::from("Yes"), true), + (String::from("No"), false), + ]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_SEND_DATA_DELAY.to_owned(), + name: "Send delay, ms".to_owned(), + desc: "Data sending delay (between bytes), ms".to_owned(), + required: true, + interface: ValueInput::Numbers(vec![0, 10, 20, 30, 40, 50], 0), + binding: None, + }), + ]) + } + fn lazy_fields_getter( + &self, + _origin: stypes::SourceOrigin, + _cancel: tokio_util::sync::CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async move { + let ports = serialport::available_ports() + .map_err(|e| stypes::ComputationError::IoOperation(e.to_string()))? + .into_iter() + .map(|p| p.port_name) + .collect::>(); + Ok(vec![StaticFieldResult::Success(StaticFieldDesc { + id: FIELD_PORTS_LIST.to_owned(), + name: "Ports".to_string(), + desc: "List of available ports".to_string(), + required: false, + interface: ValueInput::Strings(ports, String::new()), + binding: Some(FIELD_PATH.to_string()), + })]) + }) + } + fn validate( + &self, + _origin: &stypes::SourceOrigin, + fields: &[stypes::Field], + ) -> HashMap { + let mut errors = HashMap::new(); + fields.iter().for_each(|field| { + if field.id == FIELD_PATH { + if let Value::String(path) = &field.value { + if path.trim().is_empty() { + errors.insert(field.id.clone(), "Path cannot be empty".to_owned()); + } + } + } + }); + errors + } +} + +impl components::Component for SerialSource { + fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { + components.register(Descriptor::default())?; + Ok(()) + } +} diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index cce09511ea..68c79aca4f 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,13 +1,11 @@ use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; -use components::{ComponentDescriptor, StaticFieldResult}; use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, }; -use std::{collections::HashMap, io, str}; -use stypes::{FieldDesc, LazyFieldDesc, StaticFieldDesc, Value, ValueInput}; +use std::{io, str}; use tokio::time::{Duration, sleep}; use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilderExt, SerialStream, StopBits}; use tokio_util::codec::{Decoder, Encoder, Framed}; @@ -218,188 +216,6 @@ impl ByteSource for SerialSource { } } -const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, -]); - -/// The path to the serial port. -const FIELD_PATH: &str = "SERIAL_SOURCE_FIELD_PATH"; -/// The baud rate for the connection. -const FIELD_BAUD_RATE: &str = "SERIAL_SOURCE_FIELD_BAUD_RATE"; -/// The number of data bits per frame. -const FIELD_DATA_BITS: &str = "SERIAL_SOURCE_FIELD_DATA_BITS"; -/// The flow control setting. -const FIELD_FLOW_CONTROL: &str = "SERIAL_SOURCE_FIELD_FLOW_CONTROL"; -/// The parity setting. -const FIELD_PARITY: &str = "SERIAL_SOURCE_FIELD_PARITY"; -/// The number of stop bits. -const FIELD_STOP_BITS: &str = "SERIAL_SOURCE_FIELD_STOP_BITS"; -/// The delay in sending data, in milliseconds. -const FIELD_SEND_DATA_DELAY: &str = "SERIAL_SOURCE_FIELD_SEND_DATA_DELAY"; -/// Whether the connection is exclusive. -const FIELD_EXCLUSIVE: &str = "SERIAL_SOURCE_FIELD_EXCLUSIVE"; -/// List of ports -const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; - -#[derive(Default)] -struct Descriptor {} - -impl ComponentDescriptor for Descriptor { - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("Serial"), - desc: String::from("Access to Serial Devices"), - uuid: SERIAL_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } - fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { - Ok(vec![ - FieldDesc::Static(StaticFieldDesc { - id: FIELD_PATH.to_owned(), - name: "Path to dev".to_owned(), - desc: "Full path to serial device/port".to_owned(), - required: true, - interface: ValueInput::String(String::new()), - binding: None, - }), - FieldDesc::Lazy(LazyFieldDesc { - id: FIELD_PORTS_LIST.to_string(), - name: "Ports".to_string(), - desc: "List of available ports".to_string(), - binding: Some(FIELD_PATH.to_string()), - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_BAUD_RATE.to_owned(), - name: "Boud rate".to_owned(), - desc: "Boud rate".to_owned(), - required: true, - interface: ValueInput::Numbers( - vec![ - 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, - 38400, 57600, 115200, 230400, 460800, 500000, 576000, 921600, 1000000, - 1152000, 1500000, 2000000, 2500000, 3000000, 3500000, 4000000, - ], - 115200, - ), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_DATA_BITS.to_owned(), - name: "Data bits".to_owned(), - desc: "Data bits".to_owned(), - required: true, - interface: ValueInput::Numbers(vec![8, 7, 6, 5], 8), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_STOP_BITS.to_owned(), - name: "Stop bits".to_owned(), - desc: "Stop bits".to_owned(), - required: true, - interface: ValueInput::Numbers(vec![1, 2], 1), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_FLOW_CONTROL.to_owned(), - name: "Flow control".to_owned(), - desc: "Flow control".to_owned(), - required: true, - interface: ValueInput::Strings( - vec![ - String::from("None"), - String::from("Hardware"), - String::from("Software"), - ], - String::from("None"), - ), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_PARITY.to_owned(), - name: "Parity".to_owned(), - desc: "Parity".to_owned(), - required: true, - interface: ValueInput::Strings( - vec![ - String::from("None"), - String::from("Odd"), - String::from("Even"), - ], - String::from("None"), - ), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_EXCLUSIVE.to_owned(), - name: "Exclusive".to_owned(), - desc: "Exclusive".to_owned(), - required: true, - interface: ValueInput::NamedBools(vec![ - (String::from("Yes"), true), - (String::from("No"), false), - ]), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_SEND_DATA_DELAY.to_owned(), - name: "Send delay, ms".to_owned(), - desc: "Data sending delay (between bytes), ms".to_owned(), - required: true, - interface: ValueInput::Numbers(vec![0, 10, 20, 30, 40, 50], 0), - binding: None, - }), - ]) - } - fn lazy_fields_getter( - &self, - _origin: stypes::SourceOrigin, - _cancel: tokio_util::sync::CancellationToken, - ) -> components::LazyFieldsTask { - Box::pin(async move { - let ports = serialport::available_ports() - .map_err(|e| stypes::ComputationError::IoOperation(e.to_string()))? - .into_iter() - .map(|p| p.port_name) - .collect::>(); - Ok(vec![StaticFieldResult::Success(StaticFieldDesc { - id: FIELD_PORTS_LIST.to_owned(), - name: "Ports".to_string(), - desc: "List of available ports".to_string(), - required: false, - interface: ValueInput::Strings(ports, String::new()), - binding: Some(FIELD_PATH.to_string()), - })]) - }) - } - fn validate( - &self, - _origin: &stypes::SourceOrigin, - fields: &[stypes::Field], - ) -> HashMap { - let mut errors = HashMap::new(); - fields.iter().for_each(|field| { - if field.id == FIELD_PATH { - if let Value::String(path) = &field.value { - if path.trim().is_empty() { - errors.insert(field.id.clone(), "Path cannot be empty".to_owned()); - } - } - } - }); - errors - } -} - -impl components::Component for SerialSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(target_os = "windows")] unsafe impl Send for SerialSource {} diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index cfe423340c..fdbb63a053 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -23,4 +23,4 @@ export type StaticFieldDesc = { id: string, name: string, desc: string, required export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | "Directories" | "Files" | "File" | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": Map>> } | { "NestedStringsMap": Map>> } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index eaebda04b5..5bfe25b2bc 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -134,9 +134,19 @@ pub enum ValueInput { ts(type = "Map") )] KeyStrings(HashMap>), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map>>") + )] + NestedNumbersMap(HashMap>>), + #[cfg_attr( + all(test, feature = "test_and_gen"), + ts(type = "Map>>") + )] + NestedStringsMap(HashMap>>), Directories, - Files, - File, + Files(Vec), + File(Vec), Directory, Bound { output: Box, diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts new file mode 100644 index 0000000000..c820f53a59 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts @@ -0,0 +1,152 @@ +import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { bytesToStr, timestampToUTC } from '@env/str'; +import { State } from './state'; +import { Element } from '../../element'; + +@Component({ + selector: 'app-settings-scheme-nested-dictionary', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class NestedDictionary extends ChangesDetector implements AfterContentInit { + @Input() element!: Element; + + protected state: State | undefined; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void { + this.state = this.element.nested_dictionary + ? new State(this.element.nested_dictionary.items) + : undefined; + } + + public ngOnEntitySelect() { + if (!this.state) { + return; + } + this.state.buildSummary().selected(); + this.detectChanges(); + } + + public ngContextMenu(event: MouseEvent) { + const state = this.state; + if (!state) { + return; + } + const after = () => { + state.buildSummary().selected(); + this.detectChanges(); + }; + this.ilc().emitter.ui.contextmenu.open({ + items: [ + { + caption: 'Select all', + handler: () => { + state.structure.forEach((section) => { + section.entities.forEach((e) => e.select()); + }); + after(); + }, + }, + { + caption: 'Unselect all', + handler: () => { + state.structure.forEach((section) => { + section.entities.forEach((e) => e.unselect()); + }); + after(); + }, + }, + { + caption: 'Reverse selection', + handler: () => { + state.structure.forEach((section) => { + section.entities.forEach((e) => e.toggle()); + }); + after(); + }, + }, + {}, + { + caption: 'Select with fotal', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_fatal > 0 && e.select(); + // }); + }); + after(); + }, + }, + { + caption: 'Select with errors', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_error > 0 && e.select(); + // }); + }); + after(); + }, + }, + { + caption: 'Select with warnings', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_warning > 0 && e.select(); + // }); + }); + after(); + }, + }, + {}, + { + caption: 'Unselect without fotal', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_fatal === 0 && e.unselect(); + // }); + }); + after(); + }, + }, + { + caption: 'Unselect without errors', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_error === 0 && e.unselect(); + // }); + }); + after(); + }, + }, + { + caption: 'Unselect without warnings', + handler: () => { + state.structure.forEach((section) => { + // section.entities.forEach((e) => { + // e.log_warning === 0 && e.unselect(); + // }); + }); + after(); + }, + }, + ], + x: event.x, + y: event.y, + }); + } +} +export interface NestedDictionary extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts new file mode 100644 index 0000000000..3f2bd88d57 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts @@ -0,0 +1,40 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatIconModule } from '@angular/material/icon'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; +import { MatListModule } from '@angular/material/list'; +import { MatTableModule } from '@angular/material/table'; +import { MatSortModule } from '@angular/material/sort'; + +import { NestedDictionary } from './component'; +import { NestedDictionaryStructure } from './structure/component'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatCardModule, + MatDividerModule, + MatProgressBarModule, + MatChipsModule, + MatIconModule, + MatFormFieldModule, + MatSelectModule, + MatListModule, + MatTableModule, + MatSortModule, + ], + declarations: [NestedDictionary, NestedDictionaryStructure], + exports: [NestedDictionary], + bootstrap: [NestedDictionary, NestedDictionaryStructure], +}) +export class NestedDictionaryModule {} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts new file mode 100644 index 0000000000..96e063bde0 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts @@ -0,0 +1,134 @@ +import { Section } from './structure/section'; +import { Summary } from './summary'; +import { DictionaryEntities } from './structure/statentity'; +import { getTypedProp } from '@platform/env/obj'; +import { NestedDictionaryStructure } from '../../element'; +import { Holder as MatcherHolder } from '@module/matcher'; + +export const ENTITIES = { + app_ids: 'app_ids', + context_ids: 'context_ids', + ecu_ids: 'ecu_ids', +}; + +export const NAMES: { [key: string]: string } = { + [ENTITIES.app_ids]: 'Applications', + [ENTITIES.context_ids]: 'Contexts', + [ENTITIES.ecu_ids]: 'ECUs', +}; + +export class State extends MatcherHolder { + public structure: Section[] = []; + public summary: { + total: Summary; + selected: Summary; + } = { + total: new Summary(), + selected: new Summary(), + }; + + constructor(protected readonly data: NestedDictionaryStructure) { + super(); + } + + public getSelectedEntities(): DictionaryEntities[] { + let selected: DictionaryEntities[] = []; + this.structure.forEach((section) => { + selected = selected.concat(section.getSelected()); + }); + return selected; + } + + public struct(): { + build(preselection: Map): void; + filter(value: string): void; + } { + return { + build: (preselection: Map): void => { + const structure: Section[] = []; + this.data.forEach((section, key_section) => { + const selected = preselection.get(key_section); + const entities: DictionaryEntities[] = []; + section.forEach((values, key_entity) => { + const entity = new DictionaryEntities( + key_entity, + key_section, + values, + this.matcher, + ); + if (selected && selected.includes(key_entity)) { + entity.select(); + } + entities.push(entity); + }); + structure.push(new Section(key_section, key_section).fill(entities)); + }); + this.structure = structure; + this.buildSummary().all(); + }, + filter: (value: string): void => { + this.matcher.search(value); + this.structure.forEach((structure) => { + structure.entities.sort( + (a: DictionaryEntities, b: DictionaryEntities) => + b.getScore() - a.getScore(), + ); + structure.update.emit(); + }); + }, + }; + } + + public buildSummary(): { + total(): void; + selected(): void; + all(): void; + } { + return { + total: (): void => { + this.summary.total.reset(); + this.structure.forEach((structure) => { + structure.entities.forEach((entity) => { + this.summary.total.inc(entity); + }); + }); + }, + selected: (): void => { + this.summary.selected.reset(); + this.structure.forEach((structure) => { + structure.entities.forEach((entity) => { + entity.selected && this.summary.selected.inc(entity); + }); + }); + // const conf = this.observe.parser.as(Dlt.Configuration); + // if (conf === undefined) { + // return; + // } + // conf.setDefaultsFilterConfig(); + // let app_id_count = 0; + // let context_id_count = 0; + // this.structure.forEach((structure) => { + // if (structure.key === ENTITIES.app_ids) { + // app_id_count = structure.entities.length; + // } else if (structure.key === ENTITIES.context_ids) { + // context_id_count = structure.entities.length; + // } + // const selected = structure.getSelected().map((f) => f.id); + // if (selected.length === 0) { + // (conf.configuration.filter_config as any)[structure.key] = undefined; + // } else { + // (conf.configuration.filter_config as any)[structure.key] = selected; + // } + // }); + // if (conf.configuration.filter_config) { + // conf.configuration.filter_config.app_id_count = app_id_count; + // conf.configuration.filter_config.context_id_count = context_id_count; + // } + }, + all: (): void => { + this.buildSummary().total(); + this.buildSummary().selected(); + }, + }; + } +} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts new file mode 100644 index 0000000000..ba6dc368fe --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts @@ -0,0 +1,82 @@ +import { + Component, + ViewChild, + ChangeDetectorRef, + AfterViewInit, + AfterContentInit, + Input, + Output, + EventEmitter, + ViewEncapsulation, +} from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { DictionaryEntities } from './statentity'; +import { Section } from './section'; +import { MatTable } from '@angular/material/table'; + +@Component({ + selector: 'app-settings-scheme-nested-dictionary-structure', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + encapsulation: ViewEncapsulation.None, + standalone: false, +}) +@Ilc() +export class NestedDictionaryStructure + extends ChangesDetector + implements AfterContentInit, AfterViewInit +{ + @Input() section!: Section; + + @Output() select: EventEmitter = new EventEmitter(); + + @ViewChild(MatSort) sort!: MatSort; + @ViewChild(MatTable) table!: MatTable; + + public data!: MatTableDataSource; + public keys: string[] = []; + public columns: string[] = []; + + constructor(cdRef: ChangeDetectorRef, private _sanitizer: DomSanitizer) { + super(cdRef); + } + + public ngAfterContentInit(): void { + this.data = new MatTableDataSource(this.section.entities); + this.keys = this.section.keys(); + this.columns = this.section.keys().concat(['id']); + this.env().subscriber.register( + this.section.update.subscribe(() => { + this.data.data = this.section.entities.filter( + (e: DictionaryEntities) => !e.hidden(), + ); + this.table.renderRows(); + this.detectChanges(); + }), + ); + } + + public ngAfterViewInit(): void { + this.data.sort = this.sort; + } + + public ngOnSortChange() { + this.detectChanges(); + this.table.renderRows(); + } + + public ngOnRowSelect(entity: DictionaryEntities) { + entity.toggle(); + this.select.emit(entity); + this.detectChanges(); + } + + public safeHtml(html: string): SafeHtml { + return this._sanitizer.bypassSecurityTrustHtml(html); + } +} +export interface NestedDictionaryStructure extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts new file mode 100644 index 0000000000..d1adb7203f --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts @@ -0,0 +1,27 @@ +import { Subject } from '@platform/env/subscription'; +import { DictionaryEntities } from './statentity'; + +export class Section { + public key: string; + public name: string; + public update: Subject = new Subject(); + public entities: DictionaryEntities[] = []; + + constructor(key: string, name: string) { + this.key = key; + this.name = name; + } + + public getSelected(): DictionaryEntities[] { + return this.entities.filter((f) => f.selected); + } + + public fill(entities: DictionaryEntities[]): Section { + this.entities = entities; + return this; + } + + public keys(): string[] { + return this.entities.length > 0 ? this.entities[0].keys() : []; + } +} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts new file mode 100644 index 0000000000..adeb8e18cf --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts @@ -0,0 +1,52 @@ +import { Matchee } from '@module/matcher'; + +import * as wasm from '@loader/wasm'; + +export class DictionaryEntities extends Matchee { + public selected: boolean = false; + + constructor( + public readonly id: string, + public readonly parent: string, + public readonly data: Map, + protected readonly matcher: wasm.Matcher, + ) { + super(matcher, { id: id }); + } + + public keys(): string[] { + return Array.from(this.data.keys()); + } + public value(key: string): string | number | undefined { + return this.data.get(key); + } + + public html(): string { + const html: string | undefined = this.getHtmlOf('html_id'); + return html === undefined ? this.id : html; + } + + public hash(): string { + return `${this.parent}-${this.id}`; + } + + public equal(entity: DictionaryEntities): boolean { + return entity.hash() === this.hash(); + } + + public toggle() { + this.selected = !this.selected; + } + + public select() { + this.selected = true; + } + + public unselect() { + this.selected = false; + } + + public hidden(): boolean { + return this.getScore() === 0; + } +} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less new file mode 100644 index 0000000000..a55ffe0b31 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less @@ -0,0 +1,40 @@ +@import '../../../../../styles/variables.less'; + + +app-el-dlt-extra-structure { + position: relative; + display: block; + width: 100%; + & p.title { + margin: 8px 0 0px 16px; + color: @scheme-color-2; + font-size: 14px; + font-weight: 500; + } + & tr{ + position: relative; + &:hover { + background-color: @scheme-color-4!important; + } + } + & tr[dataselected="true"] { + background: @scheme-color-active; + &::after { + position: absolute; + display: block; + content: ''; + width: 4px; + height: 100%; + background: @scheme-color-accent; + left: 0; + } + &:hover { + background-color: @scheme-color-active-75!important; + } + } + & span.id{ + & > span { + background-color: @scheme-color-3; + } + } +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html new file mode 100644 index 0000000000..b2a2dbad89 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html @@ -0,0 +1,24 @@ +

{{section.name}}

+ + + + + + + + + + + +
+ ID + + + + {{key}} + {{element.value(key)}}
diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less new file mode 100644 index 0000000000..a59a057663 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less @@ -0,0 +1,111 @@ +@import '../../../../styles/variables.less'; + + +:host { + position: absolute; + display: block; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + & app-transport { + margin-top: 12px; + } + & div.controlls { + text-align: right; + } + & h1 { + font-size: 14px; + } + & button { + position: relative; + display: inline-block; + margin: 6px 0 6px 6px; + } + & div.left { + position: absolute; + display: flex; + flex-direction: column; + top: 0; + bottom: 0; + left: 0; + right: 330px; + overflow: hidden; + justify-content: stretch; + justify-items: stretch; + & div.filter { + position: absolute; + z-index: 1000; + padding: 6px 12px; + background: @scheme-color-warning-light; + top: 6px; + right: 6px; + box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); + border-radius: 3px; + } + & .fill { + overflow: auto; + } + & .section { + padding-top: 12px; + & mat-divider { + margin-top: -12px; + } + & .mat-standard-chip{ + min-height: 24px; + } + } + } + & div.right { + position: absolute; + width: 320px; + right: 6px; + bottom: 0; + top: 0; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + & mat-divider.fibex-divider { + margin-top: 12px; + } + & div.fibex-controlls{ + position: relative; + width: 100%; + margin-top: 19px; + text-align: right; + } + & div.spacer { + flex: auto; + } + & app-tabs-source-actions { + position: sticky; + bottom: 0; + z-index: 1; + box-shadow: 0px 0px 16px @scheme-color-5; + } + } + & .table-fill { + width: 100%; + } + & .info { + & .caption { + text-align: left; + padding-right: 12px; + } + & .value { + text-align: left; + } + } + + & div.progress { + margin-top: 24px; + & p { + text-align: center; + } + } + & .mat-progress-bar-fill::after { + background-color: @scheme-color-accent; + } +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts new file mode 100644 index 0000000000..35291b2f73 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts @@ -0,0 +1,24 @@ +import { DictionaryEntities } from './structure/statentity'; + +export class Summary { + public data: Map = new Map(); + + public inc(entity: DictionaryEntities) { + entity.data.forEach((value: string | number, key: string) => { + const current = this.data.get(key); + if (typeof value === 'number') { + this.data.set(key, current ? value + current : value); + } else { + this.data.set(key, current ? current + 1 : 1); + } + }); + } + + public reset() { + this.data.clear(); + } + + public count(): number { + return this.data.size; + } +} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html new file mode 100644 index 0000000000..ce3f9b4d3d --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html @@ -0,0 +1,21 @@ + + + +

+ Summary ({{state.summary.total.count()}} / {{state.summary.selected.count()}}) +

+ + + + + +
{{selected.key}}{{selected.value}}
+
+ +
+
diff --git a/application/client/src/app/ui/elements/scheme/element.ts b/application/client/src/app/ui/elements/scheme/element.ts index ecfb829463..fae137b6d0 100644 --- a/application/client/src/app/ui/elements/scheme/element.ts +++ b/application/client/src/app/ui/elements/scheme/element.ts @@ -14,6 +14,7 @@ export type ValueInput = | { KeyNumbers: Map } | { KeyString: Map } | { KeyStrings: Map } + | { NestedNumbersMap: Map>> } | 'Directories' | 'Files' | 'File' @@ -25,6 +26,22 @@ interface ValueGetter { getValue(): Value; } +export type NestedDictionaryStructure = Map>>; + +export class NestedDictionary implements ValueGetter { + public value: V; + constructor( + public defaults: V, + public readonly items: NestedDictionaryStructure, + protected readonly getter: (value: V) => Value, + ) { + this.value = defaults; + } + public getValue(): Value { + return this.getter(this.value); + } +} + export class CheckboxElement implements ValueGetter { public value: boolean; constructor(public defaults: boolean = false) { @@ -86,6 +103,7 @@ export class Element { public string_input: InputElement | undefined; public number_input: InputElement | undefined; public named: NamedValuesElement | undefined; + public nested_dictionary: NestedDictionary | undefined; public readonly changed: Subject = new Subject(); @@ -96,6 +114,18 @@ export class Element { this.string_input = Element.as_string(origin); this.number_input = Element.as_number(origin); this.named = Element.as_named_bools(origin); + this.nested_dictionary = Element.as_nested_dictionary_numeric(origin); + } + + public isField(): boolean { + return ( + this.checkbox !== undefined || + this.numeric_list !== undefined || + this.strings_list !== undefined || + this.string_input !== undefined || + this.number_input !== undefined || + this.named !== undefined + ); } public change() { @@ -238,4 +268,15 @@ export class Element { : undefined : undefined; } + + static as_nested_dictionary_numeric( + origin: ValueInput, + ): NestedDictionary | undefined { + const vl = origin as { NestedNumbersMap: Map>> }; + return vl.NestedNumbersMap instanceof Map + ? new NestedDictionary([], vl.NestedNumbersMap, (selections: string[]) => { + return { Strings: selections }; + }) + : undefined; + } } diff --git a/application/client/src/app/ui/elements/scheme/inner/component.ts b/application/client/src/app/ui/elements/scheme/inner/component.ts index 88f414759f..441f33e5ea 100644 --- a/application/client/src/app/ui/elements/scheme/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/inner/component.ts @@ -24,11 +24,15 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit @Input() provider!: SchemeProvider; @Input() element!: Element; + public field: boolean = false; + constructor(cdRef: ChangeDetectorRef) { super(cdRef); } - public ngAfterContentInit(): void {} + public ngAfterContentInit(): void { + this.field = this.element.isField(); + } public ngAfterViewInit(): void { this.detectChanges(); diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html index 0ca70ae5df..966bf6a58d 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -1,5 +1,4 @@ - - + diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts index 239b97e356..6e2d6ccfa8 100644 --- a/application/client/src/app/ui/elements/scheme/module.ts +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -8,6 +8,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { MatSelectModule } from '@angular/material/select'; +import { NestedDictionaryModule } from './complex/nested_dictionary/module'; import { SettingsScheme } from './component'; import { SchemeEntry } from './entry/component'; @@ -25,6 +26,7 @@ import { SchemeEntryElement } from './inner/component'; MatInputModule, MatButtonModule, MatSelectModule, + NestedDictionaryModule, ], declarations: [SettingsScheme, SchemeEntry, SchemeEntryElement], exports: [SettingsScheme], diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html index 605dd3223c..311f57ed30 100644 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html +++ b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html @@ -1,62 +1,68 @@

{{section.name}}

+ + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ID + + + - ID - - FATAL - {{element.log_fatal}} - ERROR - {{element.log_error}} - WARN - {{element.log_warning}} - DEBUG - {{element.log_debug}} - INFO - {{element.log_info}} - VERB - {{element.log_verbose}} - NON - {{element.non_log}}
\ No newline at end of file + + + FATAL + + {{element.log_fatal}} + + + + + ERROR + + {{element.log_error}} + + + + + WARN + + {{element.log_warning}} + + + + + DEBUG + + {{element.log_debug}} + + + + + INFO + + {{element.log_info}} + + + + + VERB + + {{element.log_verbose}} + + + + + NON + + {{element.non_log}} + + + + + diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 5f0d66397f..f1e313e3ca 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -61,8 +61,10 @@ export type ValueInput = | { KeyNumbers: Map } | { KeyString: Map } | { KeyStrings: Map } + | { NestedNumbersMap: Map>> } + | { NestedStringsMap: Map>> } | 'Directories' - | 'Files' - | 'File' + | { Files: Array } + | { File: Array } | 'Directory' | { Bound: { output: ValueInput; inputs: Array } }; From 7ce2b145dcb32a1ce31397c79a8c5427807747ce Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Sun, 4 May 2025 20:44:57 +0200 Subject: [PATCH 024/121] Delivery dlt statistic with new API --- .../apps/indexer/components/src/lib.rs | 4 +- .../apps/indexer/components/src/tys.rs | 10 +++ .../apps/indexer/parsers/src/dlt/options.rs | 32 +++++++++- .../indexer/parsers/src/someip/options.rs | 24 ++++++- .../apps/indexer/parsers/src/text/options.rs | 3 + .../plugins_host/src/parser_shared/mod.rs | 3 + .../plugins_host/src/v0_1_0/parser/mod.rs | 3 + .../indexer/sources/src/binary/pcap/legacy.rs | 17 +++++ .../indexer/sources/src/binary/pcap/ng.rs | 17 +++++ .../apps/indexer/sources/src/binary/raw.rs | 4 ++ .../indexer/sources/src/command/process.rs | 10 +++ .../indexer/sources/src/serial/options.rs | 11 +++- .../apps/indexer/sources/src/socket/tcp.rs | 10 +++ .../apps/indexer/sources/src/socket/udp.rs | 10 +++ .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 18 ++++-- .../complex/nested_dictionary/component.ts | 6 +- .../scheme/complex/nested_dictionary/state.ts | 47 ++++++++++---- .../nested_dictionary/structure/component.ts | 8 ++- .../nested_dictionary/structure/statentity.ts | 8 +-- .../nested_dictionary/structure/styles.less | 2 +- .../nested_dictionary/structure/template.html | 4 +- .../complex/nested_dictionary/styles.less | 6 +- .../complex/nested_dictionary/summary.ts | 40 ++++++++++-- .../complex/nested_dictionary/template.html | 42 ++++++++++--- .../src/app/ui/elements/scheme/component.ts | 1 - .../src/app/ui/elements/scheme/element.ts | 62 +++++++++++++++---- .../app/ui/elements/scheme/entry/component.ts | 7 ++- .../app/ui/elements/scheme/entry/styles.less | 14 +++-- .../app/ui/elements/scheme/inner/component.ts | 6 +- .../ui/elements/scheme/inner/template.html | 4 ++ .../client/src/app/ui/tabs/setup/state.ts | 4 +- .../platform/types/bindings/options.ts | 4 +- 33 files changed, 371 insertions(+), 72 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index b9f5149c24..ddabaf799c 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -33,14 +33,14 @@ impl Components { pub fn get_components( &self, target: &stypes::ComponentType, - _source_origin: stypes::SourceOrigin, + origin: stypes::SourceOrigin, ) -> Result, stypes::NativeError> { // TODO: "ask" source/parser based on origin Ok(self .components .iter() .filter_map(|(_, desc)| { - if desc.is_ty(target) { + if desc.is_ty(target) && desc.is_compatible(&origin) { Some(desc.ident()) } else { None diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 074d217ac0..99b473607f 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -85,6 +85,16 @@ pub trait Component { /// This trait provides a standardized way to describe a component, including its type, /// identifier, field getters, lazy field loaders, and optional validation logic. pub trait ComponentDescriptor { + /// Check is component is campatible with given origin + /// + /// * `origin` - The source origin + /// + /// # Returns + /// + /// * `true` - if component can be used with origin + /// * `false` - if component can not be used with origin + fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool; + /// Retrieves the unique identifier of the component. /// /// # Returns diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index d4dad1677b..cf9f1240f5 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -47,6 +47,22 @@ impl fmt::Display for StatFields { } impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + return true + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "dlt") + .unwrap_or_default() + }) + } fn fields_getter(&self, origin: &SourceOrigin) -> components::FieldsResult { let mut options = vec![ FieldDesc::Static(StaticFieldDesc { @@ -195,7 +211,21 @@ impl ComponentDescriptor for Descriptor { name: String::from("Statistics"), desc: String::from("Dlt File(s) Statistics data"), required: true, - interface: ValueInput::NestedNumbersMap(converted), + interface: ValueInput::NestedNumbersMap( + converted, + vec![ + (String::from("non_log"), String::from("NON LOG")), + (String::from("log_fatal"), String::from("FATAL")), + (String::from("log_error"), String::from("ERROR")), + (String::from("log_warning"), String::from("WARNING")), + (String::from("log_info"), String::from("INFO")), + (String::from("log_debug"), String::from("DEBUG")), + (String::from("log_verbose"), String::from("VERBOSE")), + (String::from("log_invalid"), String::from("INVALID")), + ] + .into_iter() + .collect(), + ), binding: None, })]) }) diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index 4ce0c1f63c..be990a1020 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use super::SomeipParser; use components::{ComponentDescriptor, StaticFieldResult}; +use stypes::SourceOrigin; use tokio::{ select, time::{sleep, Duration}, @@ -21,7 +22,26 @@ const FIELD_STATISTICS: &str = "statistics"; struct Descriptor {} impl ComponentDescriptor for Descriptor { - fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + return true + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| { + ["dlt", "pcap", "pcapng"] + .contains(&ext.to_string_lossy().to_lowercase().as_str()) + }) + .unwrap_or_default() + }) + } + fn fields_getter(&self, _origin: &SourceOrigin) -> components::FieldsResult { Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), name: String::from("Log Level"), @@ -40,7 +60,7 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: stypes::SourceOrigin, + _origin: SourceOrigin, cancel: CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/options.rs index 9d24ab30f1..5db81fb847 100644 --- a/application/apps/indexer/parsers/src/text/options.rs +++ b/application/apps/indexer/parsers/src/text/options.rs @@ -10,6 +10,9 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { + true + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Text Parser"), diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 921c6c5028..5e446e4c33 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -186,6 +186,9 @@ impl p::Parser for PluginsParser { struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { + true + } /// **ATTANTION** That's placeholder. Should be another way to delivery data fn ident(&self) -> stypes::Ident { stypes::Ident { diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index fc283b7acd..9e6675d27d 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -175,6 +175,9 @@ impl p::Parser for PluginParser { struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { + true + } /// **ATTANTION** That's placeholder. Should be another way to delivery data fn ident(&self) -> stypes::Ident { stypes::Ident { diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index c9c1870ebd..66a48a7842 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -8,6 +8,7 @@ use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; use std::io::Read; +use stypes::SourceOrigin; pub struct PcapLegacyByteSource { pcap_reader: LegacyPcapReader, @@ -164,6 +165,22 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + return true + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "pcap") + .unwrap_or_default() + }) + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("PCAP Source"), diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 33b217e45e..4864b2016c 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -8,6 +8,7 @@ use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; use std::io::Read; +use stypes::SourceOrigin; pub struct PcapngByteSource { pcapng_reader: PcapNGReader, @@ -168,6 +169,22 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + return true + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "pcapng") + .unwrap_or_default() + }) + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("PCAP NG Source"), diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index c43a1bc0cf..9b97d8cd9c 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -5,6 +5,7 @@ use crate::{ use bufread::BufReader; use components::ComponentDescriptor; use std::io::{BufRead, Read}; +use stypes::SourceOrigin; pub struct BinaryByteSource where @@ -85,6 +86,9 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, _origin: &SourceOrigin) -> bool { + true + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Binary Source"), diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 16a9ece530..9e85384a94 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -4,6 +4,7 @@ use components::ComponentDescriptor; use regex::{Captures, Regex}; use shellexpand::tilde; use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; +use stypes::SourceOrigin; use thiserror::Error; use tokio::{ io::AsyncWriteExt, @@ -235,6 +236,15 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + match origin { + SourceOrigin::File(..) + | SourceOrigin::Files(..) + | SourceOrigin::Folder(..) + | SourceOrigin::Folders(..) => false, + SourceOrigin::Source => true, + } + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Terminal Source"), diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 89fe0831ed..a3b757677a 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -1,7 +1,7 @@ use crate::serial::serialport::SerialSource; use components::{ComponentDescriptor, StaticFieldResult}; use std::{collections::HashMap, str}; -use stypes::{FieldDesc, LazyFieldDesc, StaticFieldDesc, Value, ValueInput}; +use stypes::{FieldDesc, LazyFieldDesc, SourceOrigin, StaticFieldDesc, Value, ValueInput}; const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, @@ -30,6 +30,15 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + match origin { + SourceOrigin::File(..) + | SourceOrigin::Files(..) + | SourceOrigin::Folder(..) + | SourceOrigin::Folders(..) => false, + SourceOrigin::Source => true, + } + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Serial"), diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 12995d8840..da4de54c7f 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -4,6 +4,7 @@ use components::ComponentDescriptor; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{net::SocketAddr, time::Duration}; +use stypes::SourceOrigin; use tokio::net::TcpStream; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; @@ -176,6 +177,15 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + match origin { + SourceOrigin::File(..) + | SourceOrigin::Files(..) + | SourceOrigin::Folder(..) + | SourceOrigin::Folders(..) => false, + SourceOrigin::Source => true, + } + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("TCP Source"), diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 924f3a6048..6ca9ef9f62 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -2,6 +2,7 @@ use bufread::DeqBuffer; use components::ComponentDescriptor; use log::trace; use std::net::{IpAddr, Ipv4Addr}; +use stypes::SourceOrigin; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -139,6 +140,15 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + match origin { + SourceOrigin::File(..) + | SourceOrigin::Files(..) + | SourceOrigin::Folder(..) + | SourceOrigin::Folders(..) => false, + SourceOrigin::Source => true, + } + } fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("UDP Source"), diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index fdbb63a053..7916bb79a8 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -23,4 +23,4 @@ export type StaticFieldDesc = { id: string, name: string, desc: string, required export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": Map>> } | { "NestedStringsMap": Map>> } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 5bfe25b2bc..9bfdeabf55 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -136,14 +136,24 @@ pub enum ValueInput { KeyStrings(HashMap>), #[cfg_attr( all(test, feature = "test_and_gen"), - ts(type = "Map>>") + ts(type = "[Map>>, Map]") )] - NestedNumbersMap(HashMap>>), + /// `HashMap>>` - table of data + /// `HashMap` - dictionary of headers + NestedNumbersMap( + HashMap>>, + HashMap, + ), #[cfg_attr( all(test, feature = "test_and_gen"), - ts(type = "Map>>") + ts(type = "[Map>>, Map]") )] - NestedStringsMap(HashMap>>), + /// `HashMap>>` - table of data + /// `HashMap` - dictionary of headers + NestedStringsMap( + HashMap>>, + HashMap, + ), Directories, Files(Vec), File(Vec), diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts index c820f53a59..5fa6c01db4 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts @@ -16,7 +16,6 @@ import { Element } from '../../element'; @Ilc() export class NestedDictionary extends ChangesDetector implements AfterContentInit { @Input() element!: Element; - protected state: State | undefined; constructor(cdRef: ChangeDetectorRef) { @@ -25,7 +24,10 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni public ngAfterContentInit(): void { this.state = this.element.nested_dictionary - ? new State(this.element.nested_dictionary.items) + ? new State( + this.element.nested_dictionary.items, + this.element.nested_dictionary.dictionary, + ) : undefined; } diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts index 96e063bde0..626fbf0225 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts @@ -19,16 +19,24 @@ export const NAMES: { [key: string]: string } = { export class State extends MatcherHolder { public structure: Section[] = []; - public summary: { - total: Summary; - selected: Summary; - } = { - total: new Summary(), - selected: new Summary(), - }; + public summary: + | { + total: Summary; + selected: Summary; + } + | undefined; - constructor(protected readonly data: NestedDictionaryStructure) { + constructor( + protected readonly data: NestedDictionaryStructure, + protected readonly dictionary: Map, + ) { super(); + this.struct().build(new Map()); + } + + public getName(key: string): string { + const label = this.dictionary.get(key); + return label ? label : key; } public getSelectedEntities(): DictionaryEntities[] { @@ -50,6 +58,13 @@ export class State extends MatcherHolder { const selected = preselection.get(key_section); const entities: DictionaryEntities[] = []; section.forEach((values, key_entity) => { + if (!this.summary) { + const map = new Map(); + values.keys().forEach((key) => { + map.set(key, 0); + }); + this.summary = { total: new Summary(map), selected: new Summary(map) }; + } const entity = new DictionaryEntities( key_entity, key_section, @@ -86,18 +101,26 @@ export class State extends MatcherHolder { } { return { total: (): void => { - this.summary.total.reset(); + const total = this.summary ? this.summary.total : undefined; + if (!total) { + return; + } + total.reset(); this.structure.forEach((structure) => { structure.entities.forEach((entity) => { - this.summary.total.inc(entity); + total.inc(entity); }); }); }, selected: (): void => { - this.summary.selected.reset(); + const selected = this.summary ? this.summary.selected : undefined; + if (!selected) { + return; + } + selected.reset(); this.structure.forEach((structure) => { structure.entities.forEach((entity) => { - entity.selected && this.summary.selected.inc(entity); + entity.selected && selected.inc(entity); }); }); // const conf = this.observe.parser.as(Dlt.Configuration); diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts index ba6dc368fe..c47ce542bd 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts @@ -17,6 +17,7 @@ import { MatTableDataSource } from '@angular/material/table'; import { DictionaryEntities } from './statentity'; import { Section } from './section'; import { MatTable } from '@angular/material/table'; +import { State } from '../state'; @Component({ selector: 'app-settings-scheme-nested-dictionary-structure', @@ -31,6 +32,7 @@ export class NestedDictionaryStructure implements AfterContentInit, AfterViewInit { @Input() section!: Section; + @Input() state!: State; @Output() select: EventEmitter = new EventEmitter(); @@ -48,7 +50,7 @@ export class NestedDictionaryStructure public ngAfterContentInit(): void { this.data = new MatTableDataSource(this.section.entities); this.keys = this.section.keys(); - this.columns = this.section.keys().concat(['id']); + this.columns = ['id'].concat(this.section.keys()); this.env().subscriber.register( this.section.update.subscribe(() => { this.data.data = this.section.entities.filter( @@ -62,6 +64,10 @@ export class NestedDictionaryStructure public ngAfterViewInit(): void { this.data.sort = this.sort; + this.data.sortingDataAccessor = (item, property) => { + const value = property === 'id' ? item.id : item.values[property]; + return typeof value === 'string' ? value.toLowerCase() : value; + }; } public ngOnSortChange() { diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts index adeb8e18cf..8bc572dd17 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts @@ -4,7 +4,7 @@ import * as wasm from '@loader/wasm'; export class DictionaryEntities extends Matchee { public selected: boolean = false; - + public values: { [key: string]: string | number } = {}; constructor( public readonly id: string, public readonly parent: string, @@ -12,14 +12,14 @@ export class DictionaryEntities extends Matchee { protected readonly matcher: wasm.Matcher, ) { super(matcher, { id: id }); + data.forEach((value, key) => { + this.values[key] = value; + }); } public keys(): string[] { return Array.from(this.data.keys()); } - public value(key: string): string | number | undefined { - return this.data.get(key); - } public html(): string { const html: string | undefined = this.getHtmlOf('html_id'); diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less index a55ffe0b31..9ea4ddd588 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less @@ -1,7 +1,7 @@ @import '../../../../../styles/variables.less'; -app-el-dlt-extra-structure { +app-settings-scheme-nested-dictionary-structure { position: relative; display: block; width: 100%; diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html index b2a2dbad89..e43c74cf17 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html @@ -10,9 +10,9 @@
- {{key}} + {{state.getName(key)}} - {{element.value(key)}} + {{element.values[key]}} = new Map(); + public rows: Map[] = []; + + protected convert(data: Map) { + const perRows = Math.floor(data.size / ROWS); + let row = 0; + data.forEach((value, key) => { + if (!this.rows[row]) { + this.rows.push(new Map()); + } + this.rows[row].set(key, value); + if (this.rows[row].size >= perRows) { + row += 1; + } + }); + } + + protected getRow(key: string): Map | undefined { + return this.rows.find((row) => row.has(key)); + } + + constructor(data: Map) { + this.convert(data); + } public inc(entity: DictionaryEntities) { entity.data.forEach((value: string | number, key: string) => { - const current = this.data.get(key); + const row = this.getRow(key); + if (!row) { + return; + } + const current = row.get(key); if (typeof value === 'number') { - this.data.set(key, current ? value + current : value); + row.set(key, current ? value + current : value); } else { - this.data.set(key, current ? current + 1 : 1); + row.set(key, current ? current + 1 : 1); } }); } public reset() { - this.data.clear(); + this.rows.forEach((row) => row.keys().forEach((key) => row.set(key, 0))); } public count(): number { - return this.data.size; + return this.rows.map((row) => row.size).reduce((acc, val) => acc + val, 0); } } diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html index ce3f9b4d3d..021c649fef 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html @@ -1,13 +1,38 @@ - -

- Summary ({{state.summary.total.count()}} / {{state.summary.selected.count()}}) -

- - - - + +
{{selected.key}}{{selected.value}}
+ + +
+

+ Summary: {{state.summary.total.count()}} +

+ + + + + + + +
+ {{state.getName(column.key)}} + {{column.value}}
+
+

+ Selected: {{state.summary.selected.count()}} +

+ + + + + + + +
+ {{state.getName(column.key)}} + {{column.value}}
+
@@ -16,6 +41,7 @@ (contextmenu)="ngContextMenu($event)" (select)="ngOnEntitySelect()" [section]="section" + [state]="state" >
diff --git a/application/client/src/app/ui/elements/scheme/component.ts b/application/client/src/app/ui/elements/scheme/component.ts index 8a10589444..f500c8b928 100644 --- a/application/client/src/app/ui/elements/scheme/component.ts +++ b/application/client/src/app/ui/elements/scheme/component.ts @@ -11,7 +11,6 @@ import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { SchemeProvider } from './provider'; import { FieldDesc } from '@platform/types/bindings'; -import { WrappedField } from './field'; @Component({ selector: 'app-settings-scheme', diff --git a/application/client/src/app/ui/elements/scheme/element.ts b/application/client/src/app/ui/elements/scheme/element.ts index fae137b6d0..613e3b1288 100644 --- a/application/client/src/app/ui/elements/scheme/element.ts +++ b/application/client/src/app/ui/elements/scheme/element.ts @@ -1,5 +1,5 @@ import { Value, ValueInput } from '@platform/types/bindings'; -import { Subject } from '@platform/env/subscription'; +import { Subject, Subjects } from '@platform/env/subscription'; /** export type ValueInput = | { Checkbox: boolean } @@ -33,6 +33,7 @@ export class NestedDictionary implements ValueGetter { constructor( public defaults: V, public readonly items: NestedDictionaryStructure, + public readonly dictionary: Map, protected readonly getter: (value: V) => Value, ) { this.value = defaults; @@ -105,7 +106,13 @@ export class Element { public named: NamedValuesElement | undefined; public nested_dictionary: NestedDictionary | undefined; - public readonly changed: Subject = new Subject(); + public readonly subjects: Subjects<{ + changed: Subject; + loaded: Subject; + }> = new Subjects({ + changed: new Subject(), + loaded: new Subject(), + }); constructor(protected readonly uuid: string, protected readonly origin: ValueInput) { this.checkbox = Element.as_checkbox(origin); @@ -117,6 +124,10 @@ export class Element { this.nested_dictionary = Element.as_nested_dictionary_numeric(origin); } + public loaded() { + this.subjects.get().loaded.emit(); + } + public isField(): boolean { return ( this.checkbox !== undefined || @@ -134,17 +145,31 @@ export class Element { return; } if (this.checkbox !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.checkbox.value, value }); + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.checkbox.value, value }); } else if (this.numeric_list !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.numeric_list.value, value }); + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.numeric_list.value, value }); } else if (this.strings_list !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.strings_list.value, value }); + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.strings_list.value, value }); } else if (this.string_input !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.string_input.value, value }); + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.string_input.value, value }); } else if (this.number_input !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.number_input.value, value }); + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.number_input.value, value }); } else if (this.named !== undefined) { - this.changed.emit({ uuid: this.uuid, inner: this.named.value, value }); + this.subjects.get().changed.emit({ uuid: this.uuid, inner: this.named.value, value }); + } else if (this.nested_dictionary !== undefined) { + this.subjects + .get() + .changed.emit({ uuid: this.uuid, inner: this.nested_dictionary.value, value }); } } @@ -161,6 +186,8 @@ export class Element { this.number_input.value = value; } else if (this.named !== undefined) { this.named.value = value; + } else if (this.nested_dictionary !== undefined) { + this.nested_dictionary.value = value; } } @@ -189,6 +216,8 @@ export class Element { return this.number_input.getValue(); } else if (this.named !== undefined) { return this.named.getValue(); + } else if (this.nested_dictionary !== undefined) { + return this.nested_dictionary.getValue(); } return undefined; } @@ -272,11 +301,18 @@ export class Element { static as_nested_dictionary_numeric( origin: ValueInput, ): NestedDictionary | undefined { - const vl = origin as { NestedNumbersMap: Map>> }; - return vl.NestedNumbersMap instanceof Map - ? new NestedDictionary([], vl.NestedNumbersMap, (selections: string[]) => { - return { Strings: selections }; - }) + const vl = origin as { + NestedNumbersMap: [Map>>, Map]; + }; + return vl.NestedNumbersMap instanceof Array && vl.NestedNumbersMap.length === 2 + ? new NestedDictionary( + [], + vl.NestedNumbersMap[0], + vl.NestedNumbersMap[1], + (selections: string[]) => { + return { Strings: selections }; + }, + ) : undefined; } } diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts index 3326a47fd2..6c75c97ba2 100644 --- a/application/client/src/app/ui/elements/scheme/entry/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -4,6 +4,7 @@ import { Input, AfterViewInit, AfterContentInit, + HostBinding, } from '@angular/core'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; @@ -25,6 +26,8 @@ export class SchemeEntry extends ChangesDetector implements AfterViewInit, After @Input() provider!: SchemeProvider; @Input() field!: FieldDesc; + @HostBinding('class') classes = 'field'; + public pending: LazyFieldDesc | undefined; public loaded: StaticFieldDesc | undefined; public element: Element | undefined; @@ -64,7 +67,7 @@ export class SchemeEntry extends ChangesDetector implements AfterViewInit, After if (this.loaded) { this.element = new Element(this.loaded.id, this.loaded.interface); this.env().subscriber.register( - this.element.changed.subscribe(this.onSelfChanges.bind(this)), + this.element.subjects.get().changed.subscribe(this.onSelfChanges.bind(this)), ); this.env().subscriber.register( this.provider.subjects.get().forced.subscribe(this.onFieldsChanges.bind(this)), @@ -74,6 +77,8 @@ export class SchemeEntry extends ChangesDetector implements AfterViewInit, After ); const value = this.element.getValue(); value && this.provider.setValue(this.loaded.id, value); + this.element.loaded(); + this.classes = this.element.isField() ? 'field' : 'panel'; return; } } diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index d169471308..d75661615b 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -3,10 +3,16 @@ :host { position: relative; - display: inline-block; - padding: 2px; - margin: 6px; - height: 42px; + &.field { + display: inline-block; + padding: 2px; + margin: 6px; + height: 42px; + } + &.panel { + display: block; + width: 100%; + } p.title { display: block; position: absolute; diff --git a/application/client/src/app/ui/elements/scheme/inner/component.ts b/application/client/src/app/ui/elements/scheme/inner/component.ts index 441f33e5ea..31afdb7455 100644 --- a/application/client/src/app/ui/elements/scheme/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/inner/component.ts @@ -35,7 +35,11 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit } public ngAfterViewInit(): void { - this.detectChanges(); + this.env().subscriber.register( + this.element.subjects.get().loaded.subscribe(() => { + this.detectChanges(); + }), + ); } } export interface SchemeEntryElement extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html index 966bf6a58d..018ad73140 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -37,3 +37,7 @@ />
+ + + + diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 5f10f983d5..dc7839acdf 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -12,7 +12,9 @@ export interface IApi { } export class State extends Subscriber { - public origin: SourceOrigin = 'Source'; + public origin: SourceOrigin = { + File: '/storage/projects/esrlabs/logs-examples/dlt/images.dlt', + }; public sources: Ident[] = []; public parsers: Ident[] = []; public subjects: Subjects<{ diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index f1e313e3ca..5081384256 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -61,8 +61,8 @@ export type ValueInput = | { KeyNumbers: Map } | { KeyString: Map } | { KeyStrings: Map } - | { NestedNumbersMap: Map>> } - | { NestedStringsMap: Map>> } + | { NestedNumbersMap: [Map>>, Map] } + | { NestedStringsMap: [Map>>, Map] } | 'Directories' | { Files: Array } | { File: Array } From ac467832c5e0e5cec567d0e80d4566fd9e0ae812 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 5 May 2025 16:27:50 +0200 Subject: [PATCH 025/121] Refactoring element definition --- .../complex/nested_dictionary/component.ts | 42 +-- .../complex/nested_dictionary/module.ts | 15 - .../nested_dictionary/structure/template.html | 2 +- .../src/app/ui/elements/scheme/element.ts | 318 ------------------ .../ui/elements/scheme/element/checkbox.ts | 30 ++ .../elements/scheme/element/files_selector.ts | 61 ++++ .../app/ui/elements/scheme/element/index.ts | 123 +++++++ .../ui/elements/scheme/element/input_field.ts | 77 +++++ .../app/ui/elements/scheme/element/list.ts | 88 +++++ .../app/ui/elements/scheme/element/named.ts | 50 +++ .../scheme/element/nested_dictionary.ts | 48 +++ .../app/ui/elements/scheme/entry/styles.less | 10 +- .../ui/elements/scheme/entry/template.html | 4 + .../app/ui/elements/scheme/inner/component.ts | 26 +- .../ui/elements/scheme/inner/template.html | 41 +-- .../src/app/ui/elements/scheme/module.ts | 4 + 16 files changed, 550 insertions(+), 389 deletions(-) delete mode 100644 application/client/src/app/ui/elements/scheme/element.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/checkbox.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/files_selector.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/index.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/input_field.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/list.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/named.ts create mode 100644 application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts index 5fa6c01db4..697f004ee2 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts @@ -4,7 +4,9 @@ import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { bytesToStr, timestampToUTC } from '@env/str'; import { State } from './state'; -import { Element } from '../../element'; +import { Element, NestedDictionaryElement } from '../../element'; + +import * as els from '../../element'; @Component({ selector: 'app-settings-scheme-nested-dictionary', @@ -16,36 +18,26 @@ import { Element } from '../../element'; @Ilc() export class NestedDictionary extends ChangesDetector implements AfterContentInit { @Input() element!: Element; - protected state: State | undefined; + @Input() inner!: NestedDictionaryElement; + + protected state!: State; constructor(cdRef: ChangeDetectorRef) { super(cdRef); } public ngAfterContentInit(): void { - this.state = this.element.nested_dictionary - ? new State( - this.element.nested_dictionary.items, - this.element.nested_dictionary.dictionary, - ) - : undefined; + this.state = new State(this.inner.items, this.inner.dictionary); } public ngOnEntitySelect() { - if (!this.state) { - return; - } this.state.buildSummary().selected(); this.detectChanges(); } public ngContextMenu(event: MouseEvent) { - const state = this.state; - if (!state) { - return; - } const after = () => { - state.buildSummary().selected(); + this.state.buildSummary().selected(); this.detectChanges(); }; this.ilc().emitter.ui.contextmenu.open({ @@ -53,7 +45,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Select all', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { section.entities.forEach((e) => e.select()); }); after(); @@ -62,7 +54,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Unselect all', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { section.entities.forEach((e) => e.unselect()); }); after(); @@ -71,7 +63,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Reverse selection', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { section.entities.forEach((e) => e.toggle()); }); after(); @@ -81,7 +73,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Select with fotal', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_fatal > 0 && e.select(); // }); @@ -92,7 +84,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Select with errors', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_error > 0 && e.select(); // }); @@ -103,7 +95,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Select with warnings', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_warning > 0 && e.select(); // }); @@ -115,7 +107,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Unselect without fotal', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_fatal === 0 && e.unselect(); // }); @@ -126,7 +118,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Unselect without errors', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_error === 0 && e.unselect(); // }); @@ -137,7 +129,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni { caption: 'Unselect without warnings', handler: () => { - state.structure.forEach((section) => { + this.state.structure.forEach((section) => { // section.entities.forEach((e) => { // e.log_warning === 0 && e.unselect(); // }); diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts index 3f2bd88d57..46a440d971 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts @@ -1,15 +1,8 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatDividerModule } from '@angular/material/divider'; -import { MatChipsModule } from '@angular/material/chips'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; import { MatTableModule } from '@angular/material/table'; import { MatSortModule } from '@angular/material/sort'; @@ -19,17 +12,9 @@ import { NestedDictionaryStructure } from './structure/component'; @NgModule({ imports: [ CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, MatCardModule, MatDividerModule, MatProgressBarModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, MatTableModule, MatSortModule, ], diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html index e43c74cf17..61e2ca51b4 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html @@ -1,4 +1,4 @@ -

{{section.name}}

+

{{state.getName(section.name)}}

diff --git a/application/client/src/app/ui/elements/scheme/element.ts b/application/client/src/app/ui/elements/scheme/element.ts deleted file mode 100644 index 613e3b1288..0000000000 --- a/application/client/src/app/ui/elements/scheme/element.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { Value, ValueInput } from '@platform/types/bindings'; -import { Subject, Subjects } from '@platform/env/subscription'; -/** -export type ValueInput = - | { Checkbox: boolean } - | { Number: number } - | { String: string } - | { Numbers: [Array, number] } - | { Strings: [Array, string] } - | { NamedBools: Array<[string, boolean]> } - | { NamedNumbers: Array<[string, number]> } - | { NamedStrings: Array<[string, string]> } - | { KeyNumber: Map } - | { KeyNumbers: Map } - | { KeyString: Map } - | { KeyStrings: Map } - | { NestedNumbersMap: Map>> } - | 'Directories' - | 'Files' - | 'File' - | 'Directory' - | { Bound: { output: ValueInput; inputs: Array } }; - */ - -interface ValueGetter { - getValue(): Value; -} - -export type NestedDictionaryStructure = Map>>; - -export class NestedDictionary implements ValueGetter { - public value: V; - constructor( - public defaults: V, - public readonly items: NestedDictionaryStructure, - public readonly dictionary: Map, - protected readonly getter: (value: V) => Value, - ) { - this.value = defaults; - } - public getValue(): Value { - return this.getter(this.value); - } -} - -export class CheckboxElement implements ValueGetter { - public value: boolean; - constructor(public defaults: boolean = false) { - this.value = defaults; - } - public getValue(): Value { - return { Boolean: this.value }; - } -} - -export class InputElement implements ValueGetter { - public value: T; - constructor(public defaults: T, protected readonly getter: (value: T) => Value) { - this.value = defaults; - } - public getValue(): Value { - return this.getter(this.value); - } -} - -export class ListElement implements ValueGetter { - public value: T; - public items: T[]; - constructor(defaults: T, items: T[], protected readonly getter: (value: T) => Value) { - this.value = defaults; - this.items = items; - } - public getValue(): Value { - return this.getter(this.value); - } -} - -export class NamedValuesElement implements ValueGetter { - public value: T; - public items: { key: string; value: T }[]; - constructor( - defaults: T, - items: { key: string; value: T }[], - protected readonly getter: (value: T) => Value, - ) { - this.value = defaults; - this.items = items; - } - public getValue(): Value { - return this.getter(this.value); - } -} - -export interface ChangeEvent { - uuid: string; - inner: any; - value: Value; -} - -export class Element { - public checkbox: CheckboxElement | undefined; - public numeric_list: ListElement | undefined; - public strings_list: ListElement | undefined; - public string_input: InputElement | undefined; - public number_input: InputElement | undefined; - public named: NamedValuesElement | undefined; - public nested_dictionary: NestedDictionary | undefined; - - public readonly subjects: Subjects<{ - changed: Subject; - loaded: Subject; - }> = new Subjects({ - changed: new Subject(), - loaded: new Subject(), - }); - - constructor(protected readonly uuid: string, protected readonly origin: ValueInput) { - this.checkbox = Element.as_checkbox(origin); - this.numeric_list = Element.as_numbers_list(origin); - this.strings_list = Element.as_string_list(origin); - this.string_input = Element.as_string(origin); - this.number_input = Element.as_number(origin); - this.named = Element.as_named_bools(origin); - this.nested_dictionary = Element.as_nested_dictionary_numeric(origin); - } - - public loaded() { - this.subjects.get().loaded.emit(); - } - - public isField(): boolean { - return ( - this.checkbox !== undefined || - this.numeric_list !== undefined || - this.strings_list !== undefined || - this.string_input !== undefined || - this.number_input !== undefined || - this.named !== undefined - ); - } - - public change() { - const value = this.getValue(); - if (!value) { - return; - } - if (this.checkbox !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.checkbox.value, value }); - } else if (this.numeric_list !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.numeric_list.value, value }); - } else if (this.strings_list !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.strings_list.value, value }); - } else if (this.string_input !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.string_input.value, value }); - } else if (this.number_input !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.number_input.value, value }); - } else if (this.named !== undefined) { - this.subjects.get().changed.emit({ uuid: this.uuid, inner: this.named.value, value }); - } else if (this.nested_dictionary !== undefined) { - this.subjects - .get() - .changed.emit({ uuid: this.uuid, inner: this.nested_dictionary.value, value }); - } - } - - public setValue(value: any) { - if (this.checkbox !== undefined) { - this.checkbox.value = value; - } else if (this.numeric_list !== undefined) { - this.numeric_list.value = value; - } else if (this.strings_list !== undefined) { - this.strings_list.value = value; - } else if (this.string_input !== undefined) { - this.string_input.value = value; - } else if (this.number_input !== undefined) { - this.number_input.value = value; - } else if (this.named !== undefined) { - this.named.value = value; - } else if (this.nested_dictionary !== undefined) { - this.nested_dictionary.value = value; - } - } - - /* - | { Numbers: Array } - | { Strings: Array } - | { Directories: Array } - | { Files: Array } - | { File: string } - | { Directory: string } - | { KeyNumber: Map } - | { KeyNumbers: Map } - | { KeyString: Map } - | { KeyStrings: Map }; -*/ - public getValue(): Value | undefined { - if (this.checkbox !== undefined) { - return this.checkbox.getValue(); - } else if (this.numeric_list !== undefined) { - return this.numeric_list.getValue(); - } else if (this.strings_list !== undefined) { - return this.strings_list.getValue(); - } else if (this.string_input !== undefined) { - return this.string_input.getValue(); - } else if (this.number_input !== undefined) { - return this.number_input.getValue(); - } else if (this.named !== undefined) { - return this.named.getValue(); - } else if (this.nested_dictionary !== undefined) { - return this.nested_dictionary.getValue(); - } - return undefined; - } - - static as_checkbox(origin: ValueInput): CheckboxElement | undefined { - const vl = origin as { Checkbox: boolean }; - return typeof vl.Checkbox === 'boolean' ? new CheckboxElement(vl.Checkbox) : undefined; - } - - static as_string(origin: ValueInput): InputElement | undefined { - const vl = origin as { String: string }; - return typeof vl.String === 'string' - ? new InputElement(vl.String, (value: string): Value => { - return { String: value }; - }) - : undefined; - } - - static as_number(origin: ValueInput): InputElement | undefined { - const vl = origin as { Number: number }; - return typeof vl.Number === 'number' - ? new InputElement(vl.Number, (value: number): Value => { - return { - Number: - typeof value === 'number' - ? value - : typeof value === 'string' - ? parseInt(value, 10) - : 0, - }; - }) - : undefined; - } - - static as_numbers_list(origin: ValueInput): ListElement | undefined { - const vl = origin as { Numbers: [Array, number] }; - return vl.Numbers - ? new ListElement(vl.Numbers[1], vl.Numbers[0], (value: number): Value => { - return { - Number: - typeof value === 'number' - ? value - : typeof value === 'string' - ? parseInt(value, 10) - : 0, - }; - }) - : undefined; - } - - static as_string_list(origin: ValueInput): ListElement | undefined { - const vl = origin as { Strings: [Array, string] }; - return vl.Strings - ? new ListElement(vl.Strings[1], vl.Strings[0], (value: string): Value => { - return { - String: value, - }; - }) - : undefined; - } - - static as_named_bools(origin: ValueInput): NamedValuesElement | undefined { - const vl = origin as { NamedBools: Array<[string, boolean]> }; - return vl.NamedBools instanceof Array - ? vl.NamedBools.length > 0 - ? new NamedValuesElement( - vl.NamedBools[0][1], - vl.NamedBools.map((item) => { - return { key: item[0], value: item[1] }; - }), - (value: boolean): Value => { - return { - Boolean: value, - }; - }, - ) - : undefined - : undefined; - } - - static as_nested_dictionary_numeric( - origin: ValueInput, - ): NestedDictionary | undefined { - const vl = origin as { - NestedNumbersMap: [Map>>, Map]; - }; - return vl.NestedNumbersMap instanceof Array && vl.NestedNumbersMap.length === 2 - ? new NestedDictionary( - [], - vl.NestedNumbersMap[0], - vl.NestedNumbersMap[1], - (selections: string[]) => { - return { Strings: selections }; - }, - ) - : undefined; - } -} diff --git a/application/client/src/app/ui/elements/scheme/element/checkbox.ts b/application/client/src/app/ui/elements/scheme/element/checkbox.ts new file mode 100644 index 0000000000..c8ebe37ed5 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/checkbox.ts @@ -0,0 +1,30 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +export class CheckboxElement implements ElementInner { + public value: boolean; + + constructor(public defaults: boolean = false) { + this.value = defaults; + } + + public getInnerValue(): any { + return this.value; + } + + public isField(): boolean { + return true; + } + public setValue(value: any) { + this.value = value; + } + + public getValue(): Value { + return { Boolean: this.value }; + } +} + +export function tryFromOrigin(origin: ValueInput): CheckboxElement | undefined { + const vl = origin as { Checkbox: boolean }; + return typeof vl.Checkbox === 'boolean' ? new CheckboxElement(vl.Checkbox) : undefined; +} diff --git a/application/client/src/app/ui/elements/scheme/element/files_selector.ts b/application/client/src/app/ui/elements/scheme/element/files_selector.ts new file mode 100644 index 0000000000..be19da51ad --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/files_selector.ts @@ -0,0 +1,61 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +export enum FilesFoldersSelectorTarget { + File, + Files, + Folder, + Folders, +} + +export class FilesFolderSelectorElement implements ElementInner { + public value: string[] = []; + + constructor( + public readonly target: FilesFoldersSelectorTarget, + public readonly exts: string[] = [], + ) {} + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return false; + } + + public getValue(): Value { + switch (this.target) { + case FilesFoldersSelectorTarget.File: + case FilesFoldersSelectorTarget.Folder: + return { File: this.value[0] == undefined ? '' : this.value[0] }; + case FilesFoldersSelectorTarget.Files: + case FilesFoldersSelectorTarget.Folders: + return { Files: this.value }; + } + } +} + +export function tryFromOrigin(origin: ValueInput): FilesFolderSelectorElement | undefined { + if (typeof origin === 'string' && origin === 'Directories') { + return new FilesFolderSelectorElement(FilesFoldersSelectorTarget.Folders); + } else if (typeof origin === 'string' && origin === 'Directory') { + return new FilesFolderSelectorElement(FilesFoldersSelectorTarget.Folder); + } else if ((origin as { File: string[] }).File) { + return new FilesFolderSelectorElement( + FilesFoldersSelectorTarget.File, + (origin as { File: string[] }).File, + ); + } else if ((origin as { Files: string[] }).Files) { + return new FilesFolderSelectorElement( + FilesFoldersSelectorTarget.Files, + (origin as { Files: string[] }).Files, + ); + } else { + return undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/element/index.ts b/application/client/src/app/ui/elements/scheme/element/index.ts new file mode 100644 index 0000000000..a2dd31b57c --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/index.ts @@ -0,0 +1,123 @@ +import { Value, ValueInput } from '@platform/types/bindings'; +import { Subject, Subjects } from '@platform/env/subscription'; + +import * as CheckboxElement from './checkbox'; +import * as FilesFolderSelectorElement from './files_selector'; +import * as InputElement from './input_field'; +import * as ListElement from './list'; +import * as NamedValuesElement from './named'; +import * as NestedDictionaryElement from './nested_dictionary'; + +export { CheckboxElement } from './checkbox'; +export { FilesFolderSelectorElement } from './files_selector'; +export { InputElement } from './input_field'; +export { ListElement } from './list'; +export { NamedValuesElement } from './named'; +export { NestedDictionaryElement, NestedDictionaryStructure } from './nested_dictionary'; + +/** +export type ValueInput = + | { Checkbox: boolean } + | { Number: number } + | { String: string } + | { Numbers: [Array, number] } + | { Strings: [Array, string] } + | { NamedBools: Array<[string, boolean]> } + | { NamedNumbers: Array<[string, number]> } + | { NamedStrings: Array<[string, string]> } + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map } + | { NestedNumbersMap: [Map>>, Map] } + | { NestedStringsMap: [Map>>, Map] } + | 'Directories' + | { Files: Array } + | { File: Array } + | 'Directory' + | { Bound: { output: ValueInput; inputs: Array } }; + */ + +export abstract class ElementInner { + public abstract getValue(): Value; + public abstract getInnerValue(): any; + public abstract setValue(value: any): void; + public abstract isField(): boolean; +} + +export interface ChangeEvent { + uuid: string; + inner: any; + value: Value; +} + +export class Element { + public readonly inner: ElementInner; + + public readonly subjects: Subjects<{ + changed: Subject; + loaded: Subject; + }> = new Subjects({ + changed: new Subject(), + loaded: new Subject(), + }); + + constructor(protected readonly uuid: string, protected readonly origin: ValueInput) { + let inner = undefined; + for (let el of [ + CheckboxElement, + FilesFolderSelectorElement, + InputElement, + ListElement, + NamedValuesElement, + NestedDictionaryElement, + ]) { + let element = el.tryFromOrigin(origin); + if (element !== undefined) { + inner = element; + break; + } + } + if (inner) { + this.inner = inner; + } else { + throw new Error(`No controller for element ${JSON.stringify(origin)}`); + } + } + + public loaded() { + this.subjects.get().loaded.emit(); + } + + public isField(): boolean { + return this.inner.isField(); + } + + public change() { + this.subjects.get().changed.emit({ + uuid: this.uuid, + inner: this.inner.getInnerValue(), + value: this.inner.getValue(), + }); + } + + public setValue(value: any) { + this.inner.setValue(value); + } + + /* + | { Numbers: Array } + | { Strings: Array } + | { Directories: Array } + | { Files: Array } + | { File: string } + | { Directory: string } + | { KeyNumber: Map } + | { KeyNumbers: Map } + | { KeyString: Map } + | { KeyStrings: Map }; +*/ + public getValue(): Value { + return this.inner.getValue(); + } +} diff --git a/application/client/src/app/ui/elements/scheme/element/input_field.ts b/application/client/src/app/ui/elements/scheme/element/input_field.ts new file mode 100644 index 0000000000..da4d66321f --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/input_field.ts @@ -0,0 +1,77 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +enum InnerType { + String, + Number, +} + +export class InputElement implements ElementInner { + public value: T; + + constructor( + public defaults: T, + protected readonly innerType: InnerType, + protected readonly getter: (value: T) => Value, + ) { + this.value = defaults; + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return true; + } + + public getValue(): Value { + return this.getter(this.value); + } + + public isNumeric(): boolean { + return this.innerType === InnerType.Number; + } + + public isString(): boolean { + return this.innerType === InnerType.String; + } +} + +export function tryFromOrigin(origin: ValueInput): InputElement | undefined { + function as_string(origin: ValueInput): InputElement | undefined { + const vl = origin as { String: string }; + return typeof vl.String === 'string' + ? new InputElement(vl.String, InnerType.String, (value: string): Value => { + return { String: value }; + }) + : undefined; + } + + function as_number(origin: ValueInput): InputElement | undefined { + const vl = origin as { Number: number }; + return typeof vl.Number === 'number' + ? new InputElement(vl.Number, InnerType.Number, (value: number): Value => { + return { + Number: + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseInt(value, 10) + : 0, + }; + }) + : undefined; + } + if ((origin as { Number: number }).Number !== undefined) { + return as_number(origin) as InputElement; + } else if ((origin as { String: string }).String !== undefined) { + return as_string(origin) as InputElement; + } else { + return undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/element/list.ts b/application/client/src/app/ui/elements/scheme/element/list.ts new file mode 100644 index 0000000000..b5711184b7 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/list.ts @@ -0,0 +1,88 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +enum InnerType { + String, + Number, +} + +export class ListElement implements ElementInner { + public value: T; + public items: T[]; + constructor( + defaults: T, + items: T[], + protected readonly innerType: InnerType, + protected readonly getter: (value: T) => Value, + ) { + this.value = defaults; + this.items = items; + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return true; + } + public getValue(): Value { + return this.getter(this.value); + } + public isNumeric(): boolean { + return this.innerType === InnerType.Number; + } + + public isString(): boolean { + return this.innerType === InnerType.String; + } +} + +export function tryFromOrigin(origin: ValueInput): ListElement | undefined { + function as_numbers_list(origin: ValueInput): ListElement | undefined { + const vl = origin as { Numbers: [Array, number] }; + return vl.Numbers + ? new ListElement( + vl.Numbers[1], + vl.Numbers[0], + InnerType.Number, + (value: number): Value => { + return { + Number: + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseInt(value, 10) + : 0, + }; + }, + ) + : undefined; + } + function as_string_list(origin: ValueInput): ListElement | undefined { + const vl = origin as { Strings: [Array, string] }; + return vl.Strings + ? new ListElement( + vl.Strings[1], + vl.Strings[0], + InnerType.String, + (value: string): Value => { + return { + String: value, + }; + }, + ) + : undefined; + } + if ((origin as { Numbers: [Array, number] }).Numbers) { + return as_numbers_list(origin) as ListElement; + } else if ((origin as { Strings: [Array, string] }).Strings) { + return as_string_list(origin) as ListElement; + } else { + return undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/element/named.ts b/application/client/src/app/ui/elements/scheme/element/named.ts new file mode 100644 index 0000000000..29c7e5cbcc --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/named.ts @@ -0,0 +1,50 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +export class NamedValuesElement implements ElementInner { + public value: T; + public items: { key: string; value: T }[]; + constructor( + defaults: T, + items: { key: string; value: T }[], + protected readonly getter: (value: T) => Value, + ) { + this.value = defaults; + this.items = items; + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return true; + } + + public getValue(): Value { + return this.getter(this.value); + } +} + +export function tryFromOrigin(origin: ValueInput): NamedValuesElement | undefined { + const vl = origin as { NamedBools: Array<[string, boolean]> }; + return vl.NamedBools instanceof Array + ? vl.NamedBools.length > 0 + ? new NamedValuesElement( + vl.NamedBools[0][1], + vl.NamedBools.map((item) => { + return { key: item[0], value: item[1] }; + }), + (value: boolean): Value => { + return { + Boolean: value, + }; + }, + ) + : undefined + : undefined; +} diff --git a/application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts b/application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts new file mode 100644 index 0000000000..f043988468 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts @@ -0,0 +1,48 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +export type NestedDictionaryStructure = Map>>; + +export class NestedDictionaryElement implements ElementInner { + public value: V; + constructor( + public defaults: V, + public readonly items: NestedDictionaryStructure, + public readonly dictionary: Map, + protected readonly getter: (value: V) => Value, + ) { + this.value = defaults; + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return false; + } + + public getValue(): Value { + return this.getter(this.value); + } +} + +export function tryFromOrigin(origin: ValueInput): NestedDictionaryElement | undefined { + const vl = origin as { + NestedNumbersMap: [Map>>, Map]; + }; + return vl.NestedNumbersMap instanceof Array && vl.NestedNumbersMap.length === 2 + ? new NestedDictionaryElement( + [], + vl.NestedNumbersMap[0], + vl.NestedNumbersMap[1], + (selections: string[]) => { + return { Strings: selections }; + }, + ) + : undefined; +} diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index d75661615b..6f7bc30f6d 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -11,16 +11,20 @@ } &.panel { display: block; - width: 100%; + width: auto; + height: auto; } p.title { display: block; - position: absolute; color: @scheme-color-2; - z-index: 1; padding: 3px 0px 0 16px; } p.error { color: @scheme-color-error-light; } + div.loading { + position: relative; + height: 2px; + width: 100%; + } } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/template.html b/application/client/src/app/ui/elements/scheme/entry/template.html index d358b48256..608e1e1de6 100644 --- a/application/client/src/app/ui/elements/scheme/entry/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/template.html @@ -1,5 +1,9 @@

{{name}}

{{error}}

+
+

Loading...

+ +
| undefined; + public elListElement: els.ListElement | undefined; + public elNamedValuesElement: els.NamedValuesElement | undefined; + public elNestedDictionaryElement: els.NestedDictionaryElement | undefined; + public field: boolean = false; constructor(cdRef: ChangeDetectorRef) { @@ -32,6 +40,22 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit public ngAfterContentInit(): void { this.field = this.element.isField(); + this.elCheckboxElement = + this.element.inner instanceof els.CheckboxElement ? this.element.inner : undefined; + this.elFilesFolderSelectorElement = + this.element.inner instanceof els.FilesFolderSelectorElement + ? this.element.inner + : undefined; + this.elInputElement = + this.element.inner instanceof els.InputElement ? this.element.inner : undefined; + this.elListElement = + this.element.inner instanceof els.ListElement ? this.element.inner : undefined; + this.elNamedValuesElement = + this.element.inner instanceof els.NamedValuesElement ? this.element.inner : undefined; + this.elNestedDictionaryElement = + this.element.inner instanceof els.NestedDictionaryElement + ? this.element.inner + : undefined; } public ngAfterViewInit(): void { diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html index 018ad73140..b8978e4283 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -1,43 +1,32 @@ - - - + + + {{item}} - - - - {{item}} - - - - - - + + + {{item.key}} - - - - + - - + + + + + + diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts index 6e2d6ccfa8..906ce4b25d 100644 --- a/application/client/src/app/ui/elements/scheme/module.ts +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -8,7 +8,9 @@ import { MatInputModule } from '@angular/material/input'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { MatSelectModule } from '@angular/material/select'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { NestedDictionaryModule } from './complex/nested_dictionary/module'; +import { FilesSelectorModule } from './complex/files_selector/module'; import { SettingsScheme } from './component'; import { SchemeEntry } from './entry/component'; @@ -26,7 +28,9 @@ import { SchemeEntryElement } from './inner/component'; MatInputModule, MatButtonModule, MatSelectModule, + MatProgressBarModule, NestedDictionaryModule, + FilesSelectorModule, ], declarations: [SettingsScheme, SchemeEntry, SchemeEntryElement], exports: [SettingsScheme], From 195e1b089b73dc4d055a892f72c0ec0e0516fbf8 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 5 May 2025 16:28:06 +0200 Subject: [PATCH 026/121] Add files selector element --- .../complex/files_selector/component.ts | 66 +++++++++++++++++++ .../scheme/complex/files_selector/module.ts | 24 +++++++ .../scheme/complex/files_selector/styles.less | 8 +++ .../complex/files_selector/template.html | 14 ++++ 4 files changed, 112 insertions(+) create mode 100644 application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/complex/files_selector/template.html diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts b/application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts new file mode 100644 index 0000000000..202325f4be --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts @@ -0,0 +1,66 @@ +import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { Element, FilesFolderSelectorElement } from '../../element'; +import { bridge } from '@service/bridge'; +import { File } from '@platform/types/files'; + +interface Path { + name: string; + path: string; +} + +@Component({ + selector: 'app-settings-scheme-files-selector', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class FilesSelector extends ChangesDetector implements AfterContentInit { + @Input() element!: Element; + @Input() inner!: FilesFolderSelectorElement; + + public paths: Path[] = []; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void {} + + public ngAddTarget() { + function dialog(exts: string): Promise { + if (exts.trim() === '') { + return bridge.files().select.any(); + } else { + return bridge.files().select.custom(exts); + } + } + dialog(this.inner.exts.join(',')) + .then((paths: File[]) => { + paths = paths.filter((added) => { + return this.paths.find((exist) => exist.path === added.filename) === undefined; + }); + this.paths = this.paths.concat( + paths.map((file) => { + return { path: file.filename, name: file.name }; + }), + ); + }) + .catch((err: Error) => { + this.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); + }) + .finally(() => { + this.detectChanges(); + }); + } + + public ngOnRemovePath(path: string) { + this.paths = this.paths.filter((item) => item.path !== path); + this.detectChanges(); + } +} +export interface FilesSelector extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts b/application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts new file mode 100644 index 0000000000..62a01de735 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts @@ -0,0 +1,24 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatChipsModule } from '@angular/material/chips'; +import { MatSelectModule } from '@angular/material/select'; +import { MatListModule } from '@angular/material/list'; +import { MatIcon } from '@angular/material/icon'; + +import { FilesSelector } from './component'; + +@NgModule({ + imports: [ + CommonModule, + MatButtonModule, + MatChipsModule, + MatSelectModule, + MatListModule, + MatIcon, + ], + declarations: [FilesSelector], + exports: [FilesSelector], + bootstrap: [FilesSelector], +}) +export class FilesSelectorModule {} diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less b/application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less new file mode 100644 index 0000000000..d2f993c6ae --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less @@ -0,0 +1,8 @@ +@import '../../../../styles/variables.less'; + + +:host { + position: relative; + display: block; + +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/template.html b/application/client/src/app/ui/elements/scheme/complex/files_selector/template.html new file mode 100644 index 0000000000..d69834e3ff --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/files_selector/template.html @@ -0,0 +1,14 @@ + +
+ + + {{path.name}} + + + +
From 4720a8751910f7168bb023a53d2e2f453a650c77 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 6 May 2025 17:29:44 +0200 Subject: [PATCH 027/121] Update compatible-filter --- application/apps/indexer/parsers/Cargo.toml | 1 + .../apps/indexer/parsers/src/text/options.rs | 17 +++++++++++++++-- application/apps/indexer/sources/Cargo.toml | 1 + .../indexer/sources/src/binary/pcap/legacy.rs | 12 ++++++++++-- .../apps/indexer/sources/src/binary/pcap/ng.rs | 12 ++++++++++-- .../apps/indexer/sources/src/binary/raw.rs | 18 ++++++++++++++++-- 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 69e5e8c7b7..f8d697a6c9 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -23,6 +23,7 @@ uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } tokio-util.workspace = true tokio.workspace = true +file-tools = { path = "../addons/file-tools" } [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/options.rs index 5db81fb847..a9166911d3 100644 --- a/application/apps/indexer/parsers/src/text/options.rs +++ b/application/apps/indexer/parsers/src/text/options.rs @@ -1,4 +1,6 @@ use components::ComponentDescriptor; +use file_tools::is_binary; +use stypes::SourceOrigin; use super::StringTokenizer; @@ -10,8 +12,19 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { - true + fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source => return true, + SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => return false, + }; + // If at least some file doesn't exist or it's binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) } fn ident(&self) -> stypes::Ident { stypes::Ident { diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 5c1f2c6ff0..3196892bea 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -28,6 +28,7 @@ stypes = { path = "../stypes", features=["rustcore"] } socket2 = "0.5.8" components = { path = "../components" } serialport.workspace = true +file-tools = { path = "../addons/file-tools" } [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 66a48a7842..2674c969b7 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -5,6 +5,7 @@ use crate::{ use bufread::DeqBuffer; use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; +use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; use std::io::Read; @@ -171,15 +172,22 @@ impl ComponentDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { return true } + SourceOrigin::Source => { + return false; + } }; files.iter().any(|fp| { fp.extension() .map(|ext| ext.to_ascii_lowercase() == "pcap") .unwrap_or_default() - }) + }) && + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) } fn ident(&self) -> stypes::Ident { stypes::Ident { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 4864b2016c..0fe3420c9a 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -5,6 +5,7 @@ use crate::{ use bufread::DeqBuffer; use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; +use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; use std::io::Read; @@ -175,15 +176,22 @@ impl ComponentDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { + SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { return true } + SourceOrigin::Source => { + return false; + } }; files.iter().any(|fp| { fp.extension() .map(|ext| ext.to_ascii_lowercase() == "pcapng") .unwrap_or_default() - }) + }) && + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) } fn ident(&self) -> stypes::Ident { stypes::Ident { diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 9b97d8cd9c..9893819d8f 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -4,6 +4,7 @@ use crate::{ }; use bufread::BufReader; use components::ComponentDescriptor; +use file_tools::is_binary; use std::io::{BufRead, Read}; use stypes::SourceOrigin; @@ -86,8 +87,21 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, _origin: &SourceOrigin) -> bool { - true + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => return false, + SourceOrigin::Source => { + return false; + } + }; + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) } fn ident(&self) -> stypes::Ident { stypes::Ident { From c715a54568b5995af4eb35d9528cac7193a9ef32 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 6 May 2025 17:30:16 +0200 Subject: [PATCH 028/121] Remove observe calls / switch to origin usage --- .../src/app/service/actions/file.any.ts | 27 +-- .../src/app/service/actions/file.dlt.ts | 7 +- .../app/service/actions/file.parserPlugin.ts | 11 +- .../src/app/service/actions/file.pcap.ts | 5 +- .../src/app/service/actions/file.pcapng.ts | 5 +- .../src/app/service/actions/file.text.ts | 3 +- .../src/app/service/actions/folder.any.ts | 31 +--- .../src/app/service/actions/folder.dlt.ts | 7 +- .../service/actions/folder.parserPlugin.ts | 11 +- .../src/app/service/actions/folder.pcap.ts | 5 +- .../src/app/service/actions/folder.pcapng.ts | 5 +- .../src/app/service/actions/folder.plugins.ts | 11 +- .../src/app/service/actions/folder.text.ts | 7 +- .../service/actions/serial.parserPlugin.ts | 3 +- .../src/app/service/actions/serial.text.ts | 3 +- .../src/app/service/actions/stdout.plugin.ts | 3 +- .../src/app/service/actions/stdout.text.ts | 3 +- .../client/src/app/service/actions/tcp.dlt.ts | 3 +- .../app/service/actions/tcp.parserPlugin.ts | 3 +- .../client/src/app/service/actions/udp.dlt.ts | 3 +- .../app/service/actions/udp.parserPlugin.ts | 3 +- .../client/src/app/service/cli/observe.ts | 58 +++--- .../client/src/app/service/recent/action.ts | 84 +++++---- application/client/src/app/service/session.ts | 173 +++++++++--------- .../observing/implementations/processes.ts | 3 +- .../observing/implementations/serial.ts | 3 +- .../observing/implementations/tcp.ts | 3 +- .../observing/implementations/udp.ts | 3 +- .../dependencies/observing/provider.ts | 8 +- .../client/src/app/service/session/origin.ts | 78 ++++++++ .../client/src/app/service/session/session.ts | 28 +-- .../elements/menu.attachsource/component.ts | 9 +- .../navigator/providers/provider.files.ts | 57 +----- .../src/app/ui/elements/tree/component.ts | 46 +---- .../src/app/ui/tabs/multiplefiles/state.ts | 81 ++------ .../stream/transport/listed/component.ts | 30 +-- .../client/src/app/ui/tabs/setup/component.ts | 5 +- .../client/src/app/ui/tabs/setup/module.ts | 5 +- .../src/app/ui/tabs/setup/origin/component.ts | 38 ++++ .../src/app/ui/tabs/setup/origin/styles.less | 26 +++ .../app/ui/tabs/setup/origin/template.html | 2 + .../client/src/app/ui/tabs/setup/provider.ts | 7 +- .../client/src/app/ui/tabs/setup/state.ts | 25 ++- .../src/app/ui/tabs/setup/template.html | 16 +- 44 files changed, 459 insertions(+), 488 deletions(-) create mode 100644 application/client/src/app/service/session/origin.ts create mode 100644 application/client/src/app/ui/tabs/setup/origin/component.ts create mode 100644 application/client/src/app/ui/tabs/setup/origin/styles.less create mode 100644 application/client/src/app/ui/tabs/setup/origin/template.html diff --git a/application/client/src/app/service/actions/file.any.ts b/application/client/src/app/service/actions/file.any.ts index 73d8dee9d5..ddcd06c9b8 100644 --- a/application/client/src/app/service/actions/file.any.ts +++ b/application/client/src/app/service/actions/file.any.ts @@ -6,6 +6,7 @@ import { File } from '@platform/types/files'; import { FileType } from '@platform/types/observe/types/file'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_file'; @@ -39,33 +40,13 @@ export class Action extends Base { case FileType.Binary: case FileType.PcapNG: case FileType.PcapLegacy: - session - .initialize() - .configure( - new Factory.File().type(file.type).file(file.filename).asDlt().get(), - ); + session.initialize().configure(SessionSourceOrigin.file(file.filename)); break; case FileType.Text: - session - .initialize() - .observe( - new Factory.File() - .type(Factory.FileType.Text) - .file(file.filename) - .asText() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(file.filename)); break; case FileType.ParserPlugin: - session - .initialize() - .observe( - new Factory.File() - .type(Factory.FileType.ParserPlugin) - .file(file.filename) - .asParserPlugin() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(file.filename)); break; } } diff --git a/application/client/src/app/service/actions/file.dlt.ts b/application/client/src/app/service/actions/file.dlt.ts index fcea279e44..472d27b87f 100644 --- a/application/client/src/app/service/actions/file.dlt.ts +++ b/application/client/src/app/service/actions/file.dlt.ts @@ -4,6 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_file'; @@ -35,11 +36,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure( - new Factory.File().type(files[0].type).file(files[0].filename).asDlt().get(), - ); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.parserPlugin.ts b/application/client/src/app/service/actions/file.parserPlugin.ts index 527ce7af84..2791df8f5e 100644 --- a/application/client/src/app/service/actions/file.parserPlugin.ts +++ b/application/client/src/app/service/actions/file.parserPlugin.ts @@ -4,6 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_file_parser_plugins'; @@ -36,15 +37,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asParserPlugin() - .get(), - ); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.pcap.ts b/application/client/src/app/service/actions/file.pcap.ts index 6cc89148de..5b1df2f3f8 100644 --- a/application/client/src/app/service/actions/file.pcap.ts +++ b/application/client/src/app/service/actions/file.pcap.ts @@ -4,6 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_file'; @@ -35,9 +36,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure(new Factory.File().type(files[0].type).file(files[0].filename).get()); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.pcapng.ts b/application/client/src/app/service/actions/file.pcapng.ts index d98dfa224a..2e02890132 100644 --- a/application/client/src/app/service/actions/file.pcapng.ts +++ b/application/client/src/app/service/actions/file.pcapng.ts @@ -4,6 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_file'; @@ -35,9 +36,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure(new Factory.File().type(files[0].type).file(files[0].filename).get()); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.text.ts b/application/client/src/app/service/actions/file.text.ts index 5517ab5dcb..53031fa453 100644 --- a/application/client/src/app/service/actions/file.text.ts +++ b/application/client/src/app/service/actions/file.text.ts @@ -4,6 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_file'; @@ -35,7 +36,7 @@ export class Action extends Base { }, }); } else { - session.initialize().observe(new Factory.File().type(files[0].type).file(files[0].filename).asText().get()); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.any.ts b/application/client/src/app/service/actions/folder.any.ts index 9b2cf7cb10..4a77b972ff 100644 --- a/application/client/src/app/service/actions/folder.any.ts +++ b/application/client/src/app/service/actions/folder.any.ts @@ -6,6 +6,7 @@ import { FileType } from '@platform/types/observe/types/file'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_folder'; @@ -47,37 +48,13 @@ export class Action extends Base { case FileType.Binary: case FileType.PcapNG: case FileType.PcapLegacy: - session - .initialize() - .configure( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asDlt() - .get(), - ); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); break; case FileType.Text: - session - .initialize() - .observe( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asText() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); break; case FileType.ParserPlugin: - session - .initialize() - .observe( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asParserPlugin() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); break; } } diff --git a/application/client/src/app/service/actions/folder.dlt.ts b/application/client/src/app/service/actions/folder.dlt.ts index 313d34cd89..54ccdef4ba 100644 --- a/application/client/src/app/service/actions/folder.dlt.ts +++ b/application/client/src/app/service/actions/folder.dlt.ts @@ -5,6 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_folder'; @@ -43,11 +44,7 @@ export class Action extends Base { }); return Promise.resolve(); } else { - session - .initialize() - .configure( - new Factory.File().type(files[0].type).file(files[0].filename).asDlt().get(), - ); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.parserPlugin.ts b/application/client/src/app/service/actions/folder.parserPlugin.ts index afa2fbaedc..d26a95f729 100644 --- a/application/client/src/app/service/actions/folder.parserPlugin.ts +++ b/application/client/src/app/service/actions/folder.parserPlugin.ts @@ -5,6 +5,7 @@ import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; @@ -44,15 +45,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .observe( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asParserPlugin() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); diff --git a/application/client/src/app/service/actions/folder.pcap.ts b/application/client/src/app/service/actions/folder.pcap.ts index 2d909ffd14..e53361a8fd 100644 --- a/application/client/src/app/service/actions/folder.pcap.ts +++ b/application/client/src/app/service/actions/folder.pcap.ts @@ -5,6 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_folder'; @@ -42,9 +43,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure(new Factory.File().type(files[0].type).file(files[0].filename).get()); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.pcapng.ts b/application/client/src/app/service/actions/folder.pcapng.ts index ce8ed13b81..528ceb6826 100644 --- a/application/client/src/app/service/actions/folder.pcapng.ts +++ b/application/client/src/app/service/actions/folder.pcapng.ts @@ -5,6 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_folder'; @@ -42,9 +43,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure(new Factory.File().type(files[0].type).file(files[0].filename).get()); + session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.plugins.ts b/application/client/src/app/service/actions/folder.plugins.ts index afa2fbaedc..d26a95f729 100644 --- a/application/client/src/app/service/actions/folder.plugins.ts +++ b/application/client/src/app/service/actions/folder.plugins.ts @@ -5,6 +5,7 @@ import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; @@ -44,15 +45,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .observe( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asParserPlugin() - .get(), - ); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); diff --git a/application/client/src/app/service/actions/folder.text.ts b/application/client/src/app/service/actions/folder.text.ts index c7b2b1245b..e8359bbdba 100644 --- a/application/client/src/app/service/actions/folder.text.ts +++ b/application/client/src/app/service/actions/folder.text.ts @@ -5,6 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_folder'; @@ -42,11 +43,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .observe( - new Factory.File().type(files[0].type).file(files[0].filename).asText().get(), - ); + session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/serial.parserPlugin.ts b/application/client/src/app/service/actions/serial.parserPlugin.ts index 2035e76b6d..0d18919bff 100644 --- a/application/client/src/app/service/actions/serial.parserPlugin.ts +++ b/application/client/src/app/service/actions/serial.parserPlugin.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_serial'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().asParserPlugin().serial().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/serial.text.ts b/application/client/src/app/service/actions/serial.text.ts index a956150c14..a91e173b21 100644 --- a/application/client/src/app/service/actions/serial.text.ts +++ b/application/client/src/app/service/actions/serial.text.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_serial'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().asText().serial().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/stdout.plugin.ts b/application/client/src/app/service/actions/stdout.plugin.ts index 4b43679912..bf45b98892 100644 --- a/application/client/src/app/service/actions/stdout.plugin.ts +++ b/application/client/src/app/service/actions/stdout.plugin.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_plugin'; @@ -17,7 +18,7 @@ export class Action extends Base { return 'Execute command with plugins'; } public override apply(): Promise { - session.initialize().configure(new Factory.Stream().process().asParserPlugin().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/stdout.text.ts b/application/client/src/app/service/actions/stdout.text.ts index b1e31c4c0b..55dea4bfdc 100644 --- a/application/client/src/app/service/actions/stdout.text.ts +++ b/application/client/src/app/service/actions/stdout.text.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_stdout'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().asText().process().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/tcp.dlt.ts b/application/client/src/app/service/actions/tcp.dlt.ts index 24ff06a942..429efdd594 100644 --- a/application/client/src/app/service/actions/tcp.dlt.ts +++ b/application/client/src/app/service/actions/tcp.dlt.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_tcp'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().tcp().asDlt().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/tcp.parserPlugin.ts b/application/client/src/app/service/actions/tcp.parserPlugin.ts index b46f13412c..df9b9a4f9c 100644 --- a/application/client/src/app/service/actions/tcp.parserPlugin.ts +++ b/application/client/src/app/service/actions/tcp.parserPlugin.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_tcp'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().tcp().asParserPlugin().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/udp.dlt.ts b/application/client/src/app/service/actions/udp.dlt.ts index a62a086c3b..f659f1d38c 100644 --- a/application/client/src/app/service/actions/udp.dlt.ts +++ b/application/client/src/app/service/actions/udp.dlt.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_udp'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().udp().asDlt().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/udp.parserPlugin.ts b/application/client/src/app/service/actions/udp.parserPlugin.ts index 44f0faba6b..381608020e 100644 --- a/application/client/src/app/service/actions/udp.parserPlugin.ts +++ b/application/client/src/app/service/actions/udp.parserPlugin.ts @@ -2,6 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_udp'; @@ -18,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(new Factory.Stream().udp().asParserPlugin().get()); + session.initialize().configure(SessionSourceOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/cli/observe.ts b/application/client/src/app/service/cli/observe.ts index 4b58028737..4684962f6b 100644 --- a/application/client/src/app/service/cli/observe.ts +++ b/application/client/src/app/service/cli/observe.ts @@ -30,34 +30,36 @@ export async function action( } let uuid: string | undefined = undefined; for (const observe of request.observe) { - if (uuid === undefined) { - uuid = await session - .initialize() - .auto(new Observe(observe).locker().guess()) - .catch((err: Error) => { - cli.log().warn( - `Fail to apply action (Events.Cli.Observe.Event): ${err.message}`, - ); - return undefined; - }); - if (uuid === undefined) { - return Promise.resolve(undefined); - } - } else { - const instance = session.get(uuid); - if (instance === undefined) { - return Promise.resolve(undefined); - } - await session - .initialize() - .auto(new Observe(observe).locker().guess(), instance) - .catch((err: Error) => { - cli.log().warn( - `Fail to apply action (Events.Cli.Observe.Event): ${err.message}`, - ); - return undefined; - }); - } + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + // if (uuid === undefined) { + // uuid = await session + // .initialize() + // .auto(new Observe(observe).locker().guess()) + // .catch((err: Error) => { + // cli.log().warn( + // `Fail to apply action (Events.Cli.Observe.Event): ${err.message}`, + // ); + // return undefined; + // }); + // if (uuid === undefined) { + // return Promise.resolve(undefined); + // } + // } else { + // const instance = session.get(uuid); + // if (instance === undefined) { + // return Promise.resolve(undefined); + // } + // await session + // .initialize() + // .auto(new Observe(observe).locker().guess(), instance) + // .catch((err: Error) => { + // cli.log().warn( + // `Fail to apply action (Events.Cli.Observe.Event): ${err.message}`, + // ); + // return undefined; + // }); + // } } return Promise.resolve(uuid); } diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index f7965aa74a..48cdc64847 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -121,14 +121,16 @@ export class Action { { caption: 'Open with new configuration', handler: () => { - session - .initialize() - .configure(observe) - .catch((err: Error) => { - this.logger.error( - `Fail to configure observe object: ${err.message}`, - ); - }); + console.error(`Not implemented`); + + // session + // .initialize() + // .configure(observe) + // .catch((err: Error) => { + // this.logger.error( + // `Fail to configure observe object: ${err.message}`, + // ); + // }); }, }, ] @@ -143,38 +145,40 @@ export class Action { } public apply(): Promise { - return session - .initialize() - .auto(this.observe.locker().lock()) - .then(() => { - return undefined; - }) - .catch((err: Error) => { - const message = lockers.lock( - new Locker(false, `Fail to apply action via error: ${err.message}`) - .set() - .buttons([ - { - caption: `Remove`, - handler: () => { - this.remove().finally(() => { - message.popup.close(); - }); - }, - }, - { - caption: `Cancel`, - handler: () => { - message.popup.close(); - }, - }, - ]) - .end(), - { - closable: false, - }, - ); - }); + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + // return session + // .initialize() + // .auto(this.observe.locker().lock()) + // .then(() => { + // return undefined; + // }) + // .catch((err: Error) => { + // const message = lockers.lock( + // new Locker(false, `Fail to apply action via error: ${err.message}`) + // .set() + // .buttons([ + // { + // caption: `Remove`, + // handler: () => { + // this.remove().finally(() => { + // message.popup.close(); + // }); + // }, + // }, + // { + // caption: `Cancel`, + // handler: () => { + // message.popup.close(); + // }, + // }, + // ]) + // .end(), + // { + // closable: false, + // }, + // ); + // }); } public merge(action: Action): void { diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index cc6013063e..6800837dc8 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -18,12 +18,11 @@ import { unique } from '@platform/env/sequence'; import { history } from '@service/history'; import { Render } from '@schema/render'; import { File } from '@platform/types/files'; -import { Observe } from '@platform/types/observe'; import { getRender } from '@schema/render/tools'; -import { TabObserve } from '@tabs/observe/component'; import { SetupObserve } from '@tabs/setup/component'; import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; +import { SessionSourceOrigin } from './session/origin'; import * as Factory from '@platform/types/observe/factory'; @@ -227,9 +226,9 @@ export class Service extends Implementation { public initialize(): { suggest(filename: string, session?: Session): Promise; - auto(observe: Observe, session?: Session): Promise; - configure(observe: Observe, session?: Session): Promise; - observe(observe: Observe, session?: Session): Promise; + auto(origin: SessionSourceOrigin, session?: Session): Promise; + configure(origin: SessionSourceOrigin, session?: Session): Promise; + observe(origin: SessionSourceOrigin, session?: Session): Promise; multiple(files: File[]): Promise; } { return { @@ -237,44 +236,37 @@ export class Service extends Implementation { return bridge .files() .isBinary(filename) - .then((binary: boolean) => { - if (!binary) { - return this.initialize().observe( - new Factory.File() - .type(Factory.FileType.Text) - .asText() - .file(filename) - .get(), - session, - ); - } else { - return this.initialize().configure( - new Factory.File() - .type(Factory.FileType.Binary) - .file(filename) - .get(), - session, - ); - } + .then((_binary: boolean) => { + // TODO: considering binary or not should happen on rustcore now + return this.initialize().observe( + SessionSourceOrigin.file(filename), + session, + ); }); }, - auto: (observe: Observe, session?: Session): Promise => { - return observe.locker().is() - ? this.initialize().observe(observe, session) - : this.initialize().configure(observe, session); + auto: (origin: SessionSourceOrigin, session?: Session): Promise => { + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + // return observe.locker().is() + // ? this.initialize().observe(observe, session) + // : this.initialize().configure(observe, session); }, - configure: (observe: Observe, session?: Session): Promise => { + configure: ( + origin: SessionSourceOrigin, + session?: Session, + ): Promise => { return new Promise((resolve) => { const api = this.add().tab({ - name: observe.origin.title(), + name: origin.getTitle(), content: { factory: SetupObserve, inputs: { + origin, api: { - finish: (observe: Observe): Promise => { + finish: (origin: SessionSourceOrigin): Promise => { return new Promise((success, failed) => { this.initialize() - .observe(observe, session) + .observe(origin, session) .then((session) => { success(); api?.close(); @@ -299,48 +291,51 @@ export class Service extends Implementation { }); }); }, - observe: async (observe: Observe, existed?: Session): Promise => { - const render = await getRender(observe); - if (render instanceof Error) { - throw render; - } - const session = - existed !== undefined ? existed : await this.add(false).empty(render); - return new Promise((resolve, reject) => { - session.stream - .observe() - .start(observe) - .then((uuid: string) => { - if (existed === undefined) { - const error = this.bind( - session.uuid(), - observe.origin.desc().major, - true, - ); - if (error instanceof Error) { - this.log().error(`Fail to bind session: ${error.message}`); - } - recent.add(observe).catch((err: Error) => { - this.log().error( - `Fail to save action as recent: ${err.message}`, - ); - }); - } - resolve(uuid); - }) - .catch((err: Error) => { - if (existed !== undefined) { - return reject(err); - } - this.kill(session.uuid()) - .catch((closeErr: Error) => { - this.log().error(`Fail to close session: ${closeErr.message}`); - }) - .finally(() => { - reject(err); - }); - }); - }); + observe: async (origin: SessionSourceOrigin, existed?: Session): Promise => { + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + + // const render = await getRender(observe); + // if (render instanceof Error) { + // throw render; + // } + // const session = + // existed !== undefined ? existed : await this.add(false).empty(render); + // return new Promise((resolve, reject) => { + // session.stream + // .observe() + // .start(observe) + // .then((uuid: string) => { + // if (existed === undefined) { + // const error = this.bind( + // session.uuid(), + // observe.origin.desc().major, + // true, + // ); + // if (error instanceof Error) { + // this.log().error(`Fail to bind session: ${error.message}`); + // } + // recent.add(observe).catch((err: Error) => { + // this.log().error( + // `Fail to save action as recent: ${err.message}`, + // ); + // }); + // } + // resolve(uuid); + // }) + // .catch((err: Error) => { + // if (existed !== undefined) { + // return reject(err); + // } + // this.kill(session.uuid()) + // .catch((closeErr: Error) => { + // this.log().error(`Fail to close session: ${closeErr.message}`); + // }) + // .finally(() => { + // reject(err); + // }); + // }); + // }); }, multiple: (files: File[]): Promise => { return new Promise((resolve, reject) => { @@ -353,21 +348,21 @@ export class Service extends Implementation { setTitle: (title: string) => { api?.setTitle(title); }, - done: (observe: Observe) => { - this.initialize() - .observe(observe) - .then((session) => { - resolve(session); - }) - .catch((err: Error) => { - this.log().error( - `Fail to setup observe: ${err.message}`, - ); - reject(err); - }) - .finally(() => { - api?.close(); - }); + done: (origin: SessionSourceOrigin) => { + console.error(`Not implemented`); + // .observe(observe) + // .then((session) => { + // resolve(session); + // }) + // .catch((err: Error) => { + // this.log().error( + // `Fail to setup observe: ${err.message}`, + // ); + // reject(err); + // }) + // .finally(() => { + // api?.close(); + // }); }, cancel: () => { api?.close(); diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts b/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts index bb6dec2e9a..b923c79027 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts @@ -7,6 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -46,7 +47,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(new Factory.Stream().process().get()); + session.initialize().configure(SessionSourceOrigin.source()); } public sources(): ObserveSource[] { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts b/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts index 63c12383a4..43c97148ee 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts @@ -7,6 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -39,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(new Factory.Stream().serial().get()); + session.initialize().configure(SessionSourceOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts index 03c7ae8827..4937a27049 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts @@ -7,6 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -39,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(new Factory.Stream().tcp().get()); + session.initialize().configure(SessionSourceOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts index 86e974ce2e..a97de8d960 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts @@ -7,6 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -39,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(new Factory.Stream().udp().get()); + session.initialize().configure(SessionSourceOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/dependencies/observing/provider.ts b/application/client/src/app/service/session/dependencies/observing/provider.ts index fb9a4f6e81..e798377d44 100644 --- a/application/client/src/app/service/session/dependencies/observing/provider.ts +++ b/application/client/src/app/service/session/dependencies/observing/provider.ts @@ -108,9 +108,11 @@ export abstract class Provider { } public openAsNew(source: ObserveSource | Observe): Promise { - return session - .initialize() - .configure(source instanceof ObserveSource ? source.observe : source); + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + // return session + // .initialize() + // .configure(source instanceof ObserveSource ? source.observe : source); } public setPanels(): Provider { diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts new file mode 100644 index 0000000000..07eb98ccd7 --- /dev/null +++ b/application/client/src/app/service/session/origin.ts @@ -0,0 +1,78 @@ +import { SourceOrigin, Ident } from '@platform/types/bindings'; + +export class SessionOptionsPreset { + public parser: Ident | undefined; + public source: Ident | undefined; +} + +export class SessionSourceOrigin { + static file(path: string): SessionSourceOrigin { + return new SessionSourceOrigin({ File: path }, undefined); + } + static files(paths: string[]): SessionSourceOrigin { + return new SessionSourceOrigin({ Files: paths }, undefined); + } + static folder(path: string): SessionSourceOrigin { + return new SessionSourceOrigin({ Folder: path }, undefined); + } + static folders(paths: string[]): SessionSourceOrigin { + return new SessionSourceOrigin({ Folders: paths }, undefined); + } + static source(): SessionSourceOrigin { + return new SessionSourceOrigin('Source', undefined); + } + constructor( + public readonly origin: SourceOrigin, + public options: SessionOptionsPreset | undefined, + ) {} + + public getDef(): SourceOrigin { + return this.origin; + } + public getTitle(): string { + if (this.origin === 'Source') { + // TODO: Check idents + return `Source`; + } else if ((this.origin as { File: string }).File) { + return (this.origin as { File: string }).File; + } else if ((this.origin as { Folder: string }).Folder) { + return (this.origin as { Folder: string }).Folder; + } else if ((this.origin as { Files: string[] }).Files) { + return `${(this.origin as { Files: string[] }).Files.length} files`; + } else if ((this.origin as { Folders: string[] }).Folders) { + return `${(this.origin as { Folders: string[] }).Folders.length} folders`; + } else { + return `unknown`; + } + } + public getDescription(): { title: string; desctiption: string | undefined } { + if (this.origin === 'Source') { + // TODO: Check idents + return { + title: 'Custom Source', + desctiption: `Data comes from selected source provider`, + }; + } else if ((this.origin as { File: string }).File) { + return { title: `Selected File`, desctiption: (this.origin as { File: string }).File }; + } else if ((this.origin as { Folder: string }).Folder) { + return { + title: `Selected Folder`, + desctiption: (this.origin as { Folder: string }).Folder, + }; + } else if ((this.origin as { Files: string[] }).Files) { + return { + title: `Collection of Files`, + desctiption: `${(this.origin as { Files: string[] }).Files.length} files`, + }; + } else if ((this.origin as { Folders: string[] }).Folders) { + return { + title: 'Collection of Folder', + desctiption: `${ + (this.origin as { Folders: string[] }).Folders.length + } folders selected`, + }; + } else { + return { title: 'Unknown', desctiption: undefined }; + } + } +} diff --git a/application/client/src/app/service/session/session.ts b/application/client/src/app/service/session/session.ts index 3a865e1818..ac055b645b 100644 --- a/application/client/src/app/service/session/session.ts +++ b/application/client/src/app/service/session/session.ts @@ -499,19 +499,21 @@ export class Session extends Base { return observe; } })(); - return session - .initialize() - .observe(observe) - .then((uuid: string) => { - const created = session.get(uuid); - if (created === undefined) { - this.log().error(`Fail to find created session ${uuid}`); - return; - } - created.search.store().filters().overwrite(parentSearchStore.filters().get()); - created.search.store().charts().overwrite(parentSearchStore.charts().get()); - created.search.store().disabled().overwrite(parentSearchStore.disabled().get()); - }); + console.error(`Not implemented`); + return Promise.reject(new Error(`Not implemented`)); + // return session + // .initialize() + // .observe(observe) + // .then((uuid: string) => { + // const created = session.get(uuid); + // if (created === undefined) { + // this.log().error(`Fail to find created session ${uuid}`); + // return; + // } + // created.search.store().filters().overwrite(parentSearchStore.filters().get()); + // created.search.store().charts().overwrite(parentSearchStore.charts().get()); + // created.search.store().disabled().overwrite(parentSearchStore.disabled().get()); + // }); } } export interface Session extends LoggerInterface {} diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index bc60fe14ea..70603fcff7 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -11,6 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { ChangesDetector } from '@ui/env/extentions/changes'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; @Component({ selector: 'app-attach-new-source-menu', @@ -39,7 +40,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(new Factory.Stream().tcp().get(), this.session); + .configure(SessionSourceOrigin.source(), this.session); }, }, { @@ -48,7 +49,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(new Factory.Stream().udp().get(), this.session); + .configure(SessionSourceOrigin.source(), this.session); }, }, { @@ -57,7 +58,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(new Factory.Stream().serial().get(), this.session); + .configure(SessionSourceOrigin.source(), this.session); }, }, null, @@ -67,7 +68,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(new Factory.Stream().process().get(), this.session); + .configure(SessionSourceOrigin.source(), this.session); }, }, ]; diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts index ec4270f98c..23311cdc6a 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts @@ -8,6 +8,7 @@ import { FolderEntityType } from '@platform/types/bindings'; import * as Factory from '@platform/types/observe/factory'; import { IMenuItem } from '@ui/service/contextmenu'; +import { SessionSourceOrigin } from '@service/session/origin'; const DEFAULT_DEEP = 5; const DEFAULT_LEN = 20000; @@ -83,13 +84,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.Binary) - .file(item.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -98,13 +93,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.PcapNG) - .file(item.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -113,13 +102,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.PcapLegacy) - .file(item.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -128,13 +111,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .asText() - .type(Factory.FileType.Text) - .file(item.filename) - .get(), - ) + .observe(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -143,13 +120,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .asParserPlugin() - .type(Factory.FileType.ParserPlugin) - .file(item.filename) - .get(), - ) + .observe(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() @@ -170,13 +141,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .asText() - .type(filetype) - .file(item.filename) - .get(), - ) + .observe(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() @@ -186,13 +151,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .type(filetype) - .file(item.filename) - .guessParser() - .get(), - ) + .configure(SessionSourceOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() diff --git a/application/client/src/app/ui/elements/tree/component.ts b/application/client/src/app/ui/elements/tree/component.ts index c6c3b1ae81..95610a03fe 100644 --- a/application/client/src/app/ui/elements/tree/component.ts +++ b/application/client/src/app/ui/elements/tree/component.ts @@ -16,6 +16,7 @@ import { stop } from '@ui/env/dom'; import * as Scheme from './scheme'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; @Component({ selector: 'app-elements-tree', @@ -35,10 +36,7 @@ export class ElementsTreeSelector public state: State; private init!: Promise; - constructor( - cdRef: ChangeDetectorRef, - private _sanitizer: DomSanitizer, - ) { + constructor(cdRef: ChangeDetectorRef, private _sanitizer: DomSanitizer) { super(cdRef); this.state = new State(this); } @@ -113,13 +111,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .type(Factory.FileType.Text) - .asText() - .file(entity.getPath()) - .get(), - ) + .observe(SessionSourceOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -130,13 +122,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .asDlt() - .type(Factory.FileType.Binary) - .file(entity.getPath()) - .get(), - ) + .observe(SessionSourceOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open dlt file; error: ${err.message}`); }); @@ -147,13 +133,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .type(Factory.FileType.PcapNG) - .asDlt() - .file(entity.getPath()) - .get(), - ) + .observe(SessionSourceOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open pcapng file; error: ${err.message}`); }); @@ -164,13 +144,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .type(Factory.FileType.PcapLegacy) - .asDlt() - .file(entity.getPath()) - .get(), - ) + .observe(SessionSourceOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open pcapng file; error: ${err.message}`); }); @@ -181,13 +155,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .type(Factory.FileType.ParserPlugin) - .asParserPlugin() - .file(entity.getPath()) - .get(), - ) + .observe(SessionSourceOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error( `Fail to open file with plugins: errord ${err.message}`, diff --git a/application/client/src/app/ui/tabs/multiplefiles/state.ts b/application/client/src/app/ui/tabs/multiplefiles/state.ts index 83a4b5042a..646b2ad0fd 100644 --- a/application/client/src/app/ui/tabs/multiplefiles/state.ts +++ b/application/client/src/app/ui/tabs/multiplefiles/state.ts @@ -13,6 +13,7 @@ import { Sort } from '@angular/material/sort'; import { ChangesDetector } from '@ui/env/extentions/changes'; import * as Factory from '@platform/types/observe/factory'; +import { SessionSourceOrigin } from '@service/session/origin'; export interface IMultifile { usedColors: string[]; @@ -147,57 +148,27 @@ export class State extends Holder { return this._ref .ilc() .services.system.session.initialize() - .observe( - new Factory.Concat() - .asText() - .type(Factory.FileType.Text) - .files(files) - .get(), - ); + .observe(SessionSourceOrigin.files(files)); case FileType.PcapNG: return this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.Concat() - .asDlt() - .type(Factory.FileType.PcapNG) - .files(files) - .get(), - ); + .configure(SessionSourceOrigin.files(files)); case FileType.PcapLegacy: return this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.Concat() - .asDlt() - .type(Factory.FileType.PcapLegacy) - .files(files) - .get(), - ); + .configure(SessionSourceOrigin.files(files)); case FileType.Binary: return this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.Concat() - .asDlt() - .type(Factory.FileType.Binary) - .files(files) - .get(), - ); + .configure(SessionSourceOrigin.files(files)); case FileType.ParserPlugin: return this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.Concat() - .asParserPlugin() - .type(Factory.FileType.ParserPlugin) - .files(files) - .get(), - ); + .configure(SessionSourceOrigin.files(files)); default: return Promise.reject( new Error(`Unsupported type ${this.files[0].type}`), @@ -230,13 +201,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .observe( - new Factory.File() - .asText() - .type(Factory.FileType.Text) - .file(file.filename) - .get(), - ) + .observe(SessionSourceOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -249,13 +214,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.Binary) - .file(file.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -268,13 +227,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.PcapNG) - .file(file.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -287,13 +240,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asDlt() - .type(Factory.FileType.PcapLegacy) - .file(file.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -306,13 +253,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure( - new Factory.File() - .asParserPlugin() - .type(Factory.FileType.ParserPlugin) - .file(file.filename) - .get(), - ) + .configure(SessionSourceOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts index 676a432b2f..ced07f494f 100644 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts +++ b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts @@ -75,17 +75,18 @@ export class Transport extends ChangesDetector implements AfterContentInit { { caption: 'Restart', handler: () => { - this.ilc() - .services.system.session.initialize() - .observe(this.observe, this.session) - .catch((err: Error) => { - this.log().error( - `Fail to restart observe operation: ${err.message}`, - ); - }) - .finally(() => { - this.detectChanges(); - }); + console.log(`Not implemented`); + // this.ilc() + // .services.system.session.initialize() + // .observe(this.observe, this.session) + // .catch((err: Error) => { + // this.log().error( + // `Fail to restart observe operation: ${err.message}`, + // ); + // }) + // .finally(() => { + // this.detectChanges(); + // }); }, }, ], @@ -98,9 +99,10 @@ export class Transport extends ChangesDetector implements AfterContentInit { { caption: 'Parameters', handler: () => { - this.ilc() - .services.system.session.initialize() - .configure(this.observe, this.session); + console.log(`Not implemented`); + // this.ilc() + // .services.system.session.initialize() + // .configure(this.observe, this.session); }, }, ], diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index d3b3d3daba..0f6c5a671e 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -11,6 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, IApi } from './state'; +import { SessionSourceOrigin } from '@service/session/origin'; @Component({ selector: 'app-tabs-setup', @@ -26,8 +27,9 @@ export class SetupObserve implements AfterViewInit, AfterContentInit, OnDestroy { @Input() api!: IApi; + @Input() origin!: SessionSourceOrigin; - public state: State = new State(); + public state!: State; constructor(cdRef: ChangeDetectorRef) { super(cdRef); @@ -38,6 +40,7 @@ export class SetupObserve } public ngAfterContentInit(): void { + this.state = new State(this.origin); this.state.load(); } diff --git a/application/client/src/app/ui/tabs/setup/module.ts b/application/client/src/app/ui/tabs/setup/module.ts index 6f2cde53d6..1e26a350bd 100644 --- a/application/client/src/app/ui/tabs/setup/module.ts +++ b/application/client/src/app/ui/tabs/setup/module.ts @@ -6,6 +6,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { SettingsSchemeModule } from '@elements/scheme/module'; import { SetupObserve } from './component'; +import { SourceOriginComponent } from './origin/component'; @NgModule({ imports: [ @@ -17,8 +18,8 @@ import { SetupObserve } from './component'; MatSelectModule, SettingsSchemeModule, ], - declarations: [SetupObserve], + declarations: [SetupObserve, SourceOriginComponent], exports: [SetupObserve], - bootstrap: [SetupObserve], + bootstrap: [SetupObserve, SourceOriginComponent], }) export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/setup/origin/component.ts b/application/client/src/app/ui/tabs/setup/origin/component.ts new file mode 100644 index 0000000000..faba0ff184 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/origin/component.ts @@ -0,0 +1,38 @@ +import { + Component, + ChangeDetectorRef, + ChangeDetectionStrategy, + Input, + AfterViewInit, + AfterContentInit, + OnDestroy, +} from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { SessionSourceOrigin } from '@service/session/origin'; + +@Component({ + selector: 'app-tabs-setup-origin', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: false, +}) +@Initial() +@Ilc() +export class SourceOriginComponent implements AfterContentInit, OnDestroy { + @Input() origin!: SessionSourceOrigin; + + public title: string = ''; + public description: string | undefined; + + public ngOnDestroy(): void {} + + public ngAfterContentInit(): void { + const description = this.origin.getDescription(); + this.title = description.title; + this.description = description.desctiption; + } +} +export interface SourceOriginComponent extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/setup/origin/styles.less b/application/client/src/app/ui/tabs/setup/origin/styles.less new file mode 100644 index 0000000000..be80a97242 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/origin/styles.less @@ -0,0 +1,26 @@ +@import '../../../styles/variables.less'; + + +:host { + position: relative; + display: block; + p.title { + max-width: 200px; + color: @scheme-color-0; + white-space: nowrap; + text-overflow: ellipsis; + position: relative; + display: inline-block; + overflow: hidden; + } + p.description { + max-width: 400px; + color: @scheme-color-2; + white-space: nowrap; + text-overflow: ellipsis; + position: relative; + display: inline-block; + overflow: hidden; + direction: rtl; + } +} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/setup/origin/template.html b/application/client/src/app/ui/tabs/setup/origin/template.html new file mode 100644 index 0000000000..55a8792ea1 --- /dev/null +++ b/application/client/src/app/ui/tabs/setup/origin/template.html @@ -0,0 +1,2 @@ +

{{title}}

+

{{description}}

diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 7114644829..69b23f4737 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -10,6 +10,7 @@ import { LoadingErrorsEvent, } from '@platform/types/components'; import { WrappedField } from '@ui/elements/scheme/field'; +import { SessionSourceOrigin } from '@service/session/origin'; export class Proivder extends SchemeProvider { protected pending: string[] = []; @@ -17,7 +18,7 @@ export class Proivder extends SchemeProvider { protected readonly values: Map = new Map(); protected fields: string[] = []; - constructor(protected readonly origin: SourceOrigin, protected readonly target: string) { + constructor(protected readonly origin: SessionSourceOrigin, protected readonly target: string) { super(); this.register( components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), @@ -33,7 +34,7 @@ export class Proivder extends SchemeProvider { this.pending.length === 0 && components.abort(this.pending); this.pending = []; return components - .getOptions(this.origin, [this.target]) + .getOptions(this.origin.getDef(), [this.target]) .then((map: Map) => { const fields = map.get(this.target); if (!fields) { @@ -65,7 +66,7 @@ export class Proivder extends SchemeProvider { fields.push({ id, value }); }); components - .validate(this.origin, this.target, fields) + .validate(this.origin.getDef(), this.target, fields) .then((errs: Map) => { // Always forward errors info, even no errors this.subjects.get().error.emit(errs); diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index dc7839acdf..3c604ac082 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -4,6 +4,7 @@ import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; import { TabControls } from '@service/session'; import { Proivder } from './provider'; +import { SessionSourceOrigin } from '@service/session/origin'; export interface IApi { finish(): Promise; @@ -12,9 +13,6 @@ export interface IApi { } export class State extends Subscriber { - public origin: SourceOrigin = { - File: '/storage/projects/esrlabs/logs-examples/dlt/images.dlt', - }; public sources: Ident[] = []; public parsers: Ident[] = []; public subjects: Subjects<{ @@ -45,28 +43,39 @@ export class State extends Subscriber { protected readonly logger = new Logger(`Setup`); + constructor(protected readonly origin: SessionSourceOrigin) { + super(); + } public destroy() { this.unsubscribe(); this.subjects.destroy(); } public load() { components - .get(this.origin) + .get(this.origin.getDef()) .sources() .then((sources: Ident[]) => { this.sources = sources; + if (this.sources.length > 0) { + this.selected.source = this.sources[0].uuid; + } this.subjects.get().sources.emit(this.sources); + this.change().source(); }) .catch((err: Error) => { this.logger.error(`Fail to get sources list: ${err.message}`); this.subjects.get().error.emit(err.message); }); components - .get(this.origin) + .get(this.origin.getDef()) .parsers() .then((parsers: Ident[]) => { this.parsers = parsers; + if (this.parsers.length > 0) { + this.selected.parser = this.parsers[0].uuid; + } this.subjects.get().parsers.emit(this.sources); + this.change().parser(); }) .catch((err: Error) => { this.logger.error(`Fail to get parsers list: ${err.message}`); @@ -80,6 +89,9 @@ export class State extends Subscriber { } { return { source: (): void => { + if (this.selected.source === undefined) { + return; + } if (this.providers.source !== undefined) { this.providers.source.destroy().catch((err: Error) => { this.logger.error(`Fail to destroy source provider: ${err.message}`); @@ -89,6 +101,9 @@ export class State extends Subscriber { this.subjects.get().updated.emit(); }, parser: (): void => { + if (this.selected.parser === undefined) { + return; + } if (this.providers.parser !== undefined) { this.providers.parser.destroy().catch((err: Error) => { this.logger.error(`Fail to destroy parser provider: ${err.message}`); diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index 5a49340fee..6f08f05f44 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -1,16 +1,13 @@
+
-

Source:

- + {{source.desc}} @@ -22,12 +19,9 @@ - + {{parser.desc}} From 863fb37aab76de95a4293a277a46e2113bea128a Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 8 May 2025 18:03:28 +0200 Subject: [PATCH 029/121] Parser/Source API refactoring (step 1) --- application/apps/indexer/Cargo.toml | 3 + .../apps/indexer/addons/dlt-tools/Cargo.toml | 7 +- .../apps/indexer/addons/dlt-tools/src/lib.rs | 3 +- .../apps/indexer/components/Cargo.toml | 4 +- .../apps/indexer/components/src/tys.rs | 20 ++ .../apps/indexer/definitions/Cargo.toml | 11 + .../apps/indexer/definitions/src/lib.rs | 5 + .../apps/indexer/definitions/src/parser.rs | 217 ++++++++++++++++++ .../apps/indexer/definitions/src/source.rs | 209 +++++++++++++++++ application/apps/indexer/parsers/Cargo.toml | 7 +- .../indexer/parsers/src/dlt/attachment.rs | 2 +- .../apps/indexer/parsers/src/dlt/fmt.rs | 29 ++- .../apps/indexer/parsers/src/dlt/mod.rs | 136 ++++++----- .../apps/indexer/parsers/src/dlt/options.rs | 63 ++++- application/apps/indexer/parsers/src/lib.rs | 164 ------------- .../apps/indexer/parsers/src/someip/mod.rs | 16 +- .../apps/indexer/parsers/src/text/mod.rs | 6 +- .../apps/indexer/plugins_host/Cargo.toml | 1 + .../plugins_host/src/parser_shared/mod.rs | 17 +- .../src/parser_shared/plugin_parse_message.rs | 2 +- .../src/v0_1_0/parser/bindings.rs | 24 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 10 +- application/apps/indexer/processor/Cargo.toml | 11 +- .../apps/indexer/processor/src/export/mod.rs | 4 +- application/apps/indexer/session/Cargo.toml | 18 +- .../session/src/handlers/export_raw.rs | 6 +- .../session/src/handlers/observing/mod.rs | 8 +- .../apps/indexer/session/src/state/api.rs | 8 +- .../indexer/session/src/state/attachments.rs | 5 +- .../apps/indexer/session/src/state/mod.rs | 3 +- application/apps/indexer/sources/Cargo.toml | 8 +- .../indexer/sources/src/binary/pcap/legacy.rs | 8 +- .../indexer/sources/src/binary/pcap/ng.rs | 8 +- .../apps/indexer/sources/src/binary/raw.rs | 17 +- .../indexer/sources/src/command/process.rs | 4 +- application/apps/indexer/sources/src/lib.rs | 130 ----------- .../apps/indexer/sources/src/producer.rs | 9 +- .../indexer/sources/src/serial/serialport.rs | 4 +- .../apps/indexer/sources/src/socket/tcp.rs | 4 +- .../apps/indexer/sources/src/socket/udp.rs | 11 +- .../apps/indexer/stypes/bindings/observe.ts | 5 + .../apps/indexer/stypes/src/observe/mod.rs | 24 ++ .../apps/rustcore/rs-bindings/Cargo.lock | 3 + application/platform/types/observe/index.ts | 14 ++ cli/chipmunk-cli/Cargo.lock | 10 + 45 files changed, 808 insertions(+), 470 deletions(-) create mode 100644 application/apps/indexer/definitions/Cargo.toml create mode 100644 application/apps/indexer/definitions/src/lib.rs create mode 100644 application/apps/indexer/definitions/src/parser.rs create mode 100644 application/apps/indexer/definitions/src/source.rs diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index 44213a3691..5cd2fc78d0 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -8,6 +8,7 @@ members = [ "addons/text_grep", "addons/bufread", "components", + "definitions", "indexer_base", "merging", "parsers", @@ -44,6 +45,8 @@ anyhow = "1.0" toml = "0.8" blake3 = "1.8" serialport = "4.6" +etherparse = "0.18" +async-trait = "0.1" ## Development Dependencies ## # Support for `html_reports` needs running the benchmarks via `cargo-criterion` tool. diff --git a/application/apps/indexer/addons/dlt-tools/Cargo.toml b/application/apps/indexer/addons/dlt-tools/Cargo.toml index 8c4a46e4d0..6cd6510ffd 100644 --- a/application/apps/indexer/addons/dlt-tools/Cargo.toml +++ b/application/apps/indexer/addons/dlt-tools/Cargo.toml @@ -5,11 +5,12 @@ edition = "2024" [dependencies] dlt-core.workspace = true -indexer_base = { path = "../../indexer_base" } log.workspace = true -parsers = { path = "../../parsers" } -sources = { path = "../../sources" } tokio = { workspace = true , features = ["full"] } tokio-util = { workspace = true, features = ["codec", "net"] } +definitions = { path = "../../definitions" } +indexer_base = { path = "../../indexer_base" } +parsers = { path = "../../parsers" } +sources = { path = "../../sources" } [dev-dependencies] diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index e07f6f515d..6df927034e 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -15,8 +15,9 @@ extern crate indexer_base; #[macro_use] extern crate log; +use definitions::{Attachment, MessageStreamItem, ParseYield}; use dlt_core::filtering::DltFilterConfig; -use parsers::{Attachment, MessageStreamItem, ParseYield, dlt::DltParser}; +use parsers::dlt::DltParser; use sources::{binary::raw::BinaryByteSource, producer::MessageProducer}; use std::{ fs::File, diff --git a/application/apps/indexer/components/Cargo.toml b/application/apps/indexer/components/Cargo.toml index 2bd627a543..ba7ba13946 100644 --- a/application/apps/indexer/components/Cargo.toml +++ b/application/apps/indexer/components/Cargo.toml @@ -5,8 +5,10 @@ edition = "2021" [dependencies] thiserror.workspace = true -stypes = { path = "../stypes", features=["rustcore"] } uuid = { workspace = true , features = ["serde", "v4"] } tokio-util.workspace = true tokio = { workspace = true , features = ["full"] } log.workspace = true + +definitions = { path = "../definitions" } +stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 99b473607f..2f5983aa53 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -1,6 +1,7 @@ use std::{future::Future, pin::Pin}; use crate::*; +use stypes::SourceOrigin; use tokio_util::sync::CancellationToken; /// A type alias for a result containing a vector of field descriptors or a native error. @@ -85,6 +86,25 @@ pub trait Component { /// This trait provides a standardized way to describe a component, including its type, /// identifier, field getters, lazy field loaders, and optional validation logic. pub trait ComponentDescriptor { + fn to_source( + _inner: R, + _origin: SourceOrigin, + _options: Vec, + ) -> Result>, stypes::NativeError> + where + Self: Sized, + { + Ok(None) + } + fn to_parser( + _options: Vec, + _origin: SourceOrigin, + ) -> Result>>, stypes::NativeError> + where + Self: Sized, + { + Ok(None) + } /// Check is component is campatible with given origin /// /// * `origin` - The source origin diff --git a/application/apps/indexer/definitions/Cargo.toml b/application/apps/indexer/definitions/Cargo.toml new file mode 100644 index 0000000000..639dd592ad --- /dev/null +++ b/application/apps/indexer/definitions/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "definitions" +version = "0.1.0" +edition = "2021" + +[dependencies] +thiserror.workspace = true +etherparse.workspace = true +serde = { workspace = true , features = ["derive"] } +async-trait.workspace = true +stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/definitions/src/lib.rs b/application/apps/indexer/definitions/src/lib.rs new file mode 100644 index 0000000000..138e0f2fac --- /dev/null +++ b/application/apps/indexer/definitions/src/lib.rs @@ -0,0 +1,5 @@ +mod parser; +mod source; + +pub use parser::*; +pub use source::*; diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs new file mode 100644 index 0000000000..150af0ee3c --- /dev/null +++ b/application/apps/indexer/definitions/src/parser.rs @@ -0,0 +1,217 @@ +use serde::Serialize; +use std::{fmt::Display, io::Write}; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ParserError { + #[error("Unrecoverable error, cannot continue: {0}")] + Unrecoverable(String), + #[error("Parse error: {0}")] + Parse(String), + #[error("Incomplete, not enough data for a message")] + Incomplete, + #[error("End of file reached")] + Eof, +} + +#[derive(Debug)] +pub enum ParseYield { + Message(T), + Attachment(Attachment), + MessageAndAttachment((T, Attachment)), +} + +impl From for ParseYield { + fn from(item: T) -> Self { + Self::Message(item) + } +} + +/// Parser trait that needs to be implemented for any parser we support +/// in chipmunk +pub trait Parser { + /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, + /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` + /// + /// if the slice does not have enough bytes to parse any item, an [`ParserError`] is returned. + /// + /// in case we could parse a message but the message was filtered out, `None` is returned on + /// that item. + /// + /// # Note: + /// + /// If the parsers encounter any error while it already has parsed any items, then it must + /// return those items without the error, then on the next call it can return the errors in + /// case it was provided with the same slice of bytes. + fn parse( + &mut self, + input: &[u8], + timestamp: Option, + ) -> Result>)>, ParserError>; +} + +#[derive(Debug, Clone, Serialize)] +pub struct Attachment { + pub name: String, + pub size: usize, + pub created_date: Option, + pub modified_date: Option, + /// The indexes of the message within the original trace (0-based). + pub messages: Vec, + pub data: Vec, +} + +impl Attachment { + pub fn add_data(&mut self, new_data: &[u8]) { + self.data.extend_from_slice(new_data); + } +} + +pub trait Collector { + fn register_message(&mut self, offset: usize, msg: &T); + fn attachment_indexes(&self) -> Vec; +} + +pub trait LineFormat { + fn format_line(&self) -> String; +} + +pub enum ByteRepresentation { + Owned(Vec), + Range((usize, usize)), +} + +pub trait LogMessage: Display + Serialize { + /// Serializes a message directly into a Writer + /// returns the size of the serialized message + fn to_writer(&self, writer: &mut W) -> Result; +} + +#[derive(Debug)] +pub enum MessageStreamItem { + Item(ParseYield), + Skipped, + Incomplete, + Empty, + Done, +} + +/// ### Historical Context +/// +/// * The initial parser design was intended to return only one element per parse call. +/// * It was later discovered that in certain scenarios, it is beneficial to return multiple elements if they are available within a single chunk of data. +/// * As a result, the parser interface was modified to return an iterator instead of a single item. +/// * However, this approach has proven to be overly complex and restrictive, and a simpler vector-based solution is now being considered. +/// +/// --- +/// +/// ### Rationale for Removing `impl Iterator>)>` +/// +/// Using an iterator for returning collections of messages has been rejected for the following reasons: +/// +/// 1. Misalignment with the Parser's Role: +/// +/// * A parser is a component that processes a chunk of data and returns fully parsed messages. +/// * Using an iterator is excessive, as the parser always produces a complete set of messages, not a stream of potentially infinite items. +/// * The natural output of a parser is a finite collection of messages, making `Vec` a more suitable container. +/// +/// --- +/// +/// 2. Improved Memory Management: +/// +/// * `Vec` allows for single memory allocation, reducing the risk of fragmentation. +/// * Iterators add overhead through internal state management and additional allocations for each step. +/// * `Vec` provides contiguous memory, which improves cache locality and overall access speed. +/// * Given that message batches are typically small (tens to hundreds of items), the fixed structure of `Vec` is generally more efficient. +/// +/// --- +/// +/// 3. Preserved Architectural Flexibility: +/// +/// * Using an iterator prevents the parser from being boxed as a `Box`, limiting the flexibility of the architecture. +/// * Vectors are fully compatible with dynamic objects, simplifying modular testing and isolation of components. +/// * Iterators implicitly create lifetime constraints, complicating the safe sharing of data. +/// +/// --- +/// +/// 4. Reduced Code Complexity: +/// +/// * Vectors are easier to use, test, and extend, as they can be compared directly and modified without reconstructing the iterator. +/// * Code using vectors is typically clearer, more maintainable, and less error-prone. +/// * Iterators in this context add unnecessary complexity without significant benefit. +/// +/// --- +/// +/// 5. Predictable Processing Flow: +/// +/// * Message producers always process the entire batch of messages, making streaming unnecessary. +/// * This simplifies profiling and debugging, as the data lifecycle is more straightforward. +/// +/// --- +/// +/// ### Conclusion: +/// +/// * Using `Vec` instead of iterators results in better performance, simpler code, and greater architectural flexibility. +/// * The iterator approach introduces unnecessary complexity without clear advantages, making it inappropriate for this context. +/// +/// Continuously applies the [`parse_fn`] function to the given [`input`] bytes, +/// extracting all possible items until no more can be parsed. +/// +/// This function serves as a helper for parsing methods that return only the first +/// successfully parsed item. It repeatedly invokes [`parse_fn`] as long as there +/// are enough bytes remaining in [`input`] to parse another message. +/// +/// # Behavior +/// +/// - If the first invocation of [`parse_fn`] fails, the function returns an error immediately. +/// - If the first invocation succeeds, parsing continues until either: +/// - There are not enough remaining bytes to parse another message. +/// - [`parse_fn`] returns an error, which will be ignored after the first success. +/// +/// # Arguments +/// +/// * `input`: A slice of bytes to be parsed. +/// * `timestamp`: An optional timestamp associated with the message. +/// * `min_bytes_count`: The minimum number of bytes likely required to parse a message. +/// * `parse_fn`: A function that attempts to parse a message from a byte slice. +/// - It takes a byte slice and an optional timestamp. +/// - It returns a result containing: +/// - The number of bytes consumed. +/// - An optional parsed item [`ParseYield`]. +/// - If parsing fails, it returns an [`ParserError`]. +pub fn parse_all( + input: &[u8], + timestamp: Option, + min_bytes_count: usize, + mut parse_fn: F, +) -> Result>)>, ParserError> +where + F: FnMut(&[u8], Option) -> Result<(usize, Option>), ParserError>, +{ + let mut slice = input; + let mut results = Vec::new(); + + // Parsing of the first item should be sensentive to errors + let (consumed, item) = parse_fn(slice, timestamp)?; + results.push((consumed, item)); + + // Continue parsing until end (or error) + loop { + slice = &slice[consumed..]; + + if slice.len() < min_bytes_count { + break; + } + + match parse_fn(slice, timestamp) { + Ok((consumed, item)) => { + results.push((consumed, item)); + if consumed == 0 { + break; + } + } + Err(_) => break, + } + } + Ok(results) +} diff --git a/application/apps/indexer/definitions/src/source.rs b/application/apps/indexer/definitions/src/source.rs new file mode 100644 index 0000000000..5cbb432c9c --- /dev/null +++ b/application/apps/indexer/definitions/src/source.rs @@ -0,0 +1,209 @@ +use async_trait::async_trait; +use thiserror::Error; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TransportProtocol { + TCP, + UDP, + Unknown, +} + +impl From> for TransportProtocol { + fn from(tp_slice: etherparse::TransportSlice<'_>) -> Self { + match tp_slice { + etherparse::TransportSlice::Tcp(_) => TransportProtocol::TCP, + etherparse::TransportSlice::Udp(_) => TransportProtocol::UDP, + _ => TransportProtocol::Unknown, + } + } +} + +#[derive(Debug)] +pub struct SourceFilter { + pub transport: Option, +} + +#[derive(Debug)] +pub struct ReloadInfo { + pub newly_loaded_bytes: usize, + pub available_bytes: usize, + pub skipped_bytes: usize, + pub last_known_ts: Option, +} + +impl ReloadInfo { + pub fn new( + newly_loaded_bytes: usize, + available_bytes: usize, + skipped_bytes: usize, + last_known_ts: Option, + ) -> Self { + Self { + newly_loaded_bytes, + available_bytes, + skipped_bytes, + last_known_ts, + } + } +} + +#[derive(Error, Debug)] +pub enum SourceError { + #[error("Sources setup problem: {0}")] + Setup(String), + #[error("Unrecoverable source error: {0}")] + Unrecoverable(String), + #[error("IO error: {0}")] + Io(std::io::Error), + #[error("Not supported feature")] + NotSupported, +} + +impl From for stypes::NativeError { + fn from(err: SourceError) -> Self { + stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::ComputationFailed, + message: Some(format!("Fail create source: {err}")), + } + } +} + +pub const DEFAULT_READER_CAPACITY: usize = 10 * 1024 * 1024; +pub const DEFAULT_MIN_BUFFER_SPACE: usize = 10 * 1024; + +/// Requirements to `ByteSource` inner reader +pub trait InnerReader: std::io::Read + Send + Unpin + 'static {} + +/// **Important NOTE** +/// +/// ### Restoring the Original Asynchronous Trait Design with `async_trait` +/// +/// Initially, we used the standard approach for implementing asynchronous methods in traits via `Pin>>`, which is a well-established and widely supported pattern in Rust. Later, the API was adapted to leverage the new experimental directive `#[allow(async_fn_in_trait)], which allows direct use of asynchronous methods in traits. +/// +/// However, it is important to note that Rust developers intentionally did not enable this feature by default, and it requires explicit activation for several critical reasons: +/// +/// 1. Partial Stabilization: +/// +/// * This feature is only partially stabilized, meaning it is still subject to significant changes in future releases. +/// * Relying on partially stabilized functionality in production code can lead to unexpected maintenance costs and compatibility issues. +/// +/// 2. Potential Breaking Changes: +/// +/// * Given the ongoing discussions within the Rust community, this feature may undergo significant modifications, potentially breaking backward compatibility. +/// +/// 3. Loss of Flexibility (Critical Issue): +/// +/// * Using `#[allow(async_fn_in_trait)]` completely blocks the ability to use `Box`, leading to the infamous compilation error: +/// +/// ```text +/// the trait is not dyn compatible because method `XXX` is `async` +/// error[E0038]: the trait `YYY` is not dyn compatible +/// ``` +/// +/// * This restriction completely eliminates the possibility of packing `ByteSource` instances into trait objects, which is a critical design flaw, as it significantly reduces architectural flexibility. +/// * Without this flexibility, it becomes impossible to extend the system with additional `ByteSource` and `Parser` implementations without direct modification of the core codebase. +/// +/// --- +/// +/// ### Why Boxing is Not a Real Problem +/// +/// The practice of boxing a `ByteSource` instance in `Box` is not inherently problematic, for several reasons: +/// +/// * Efficient Use in Hot Loops: +/// +/// * Once the boxed instance is unpacked at the level of the `MessageProducer`, the cost of indirection is eliminated, allowing direct access to the underlying object. +/// +/// * Negligible Allocation Overhead: +/// +/// * The cost of allocating a boxed instance is a one-time event, occurring only during the initialization of a session. +/// * Given that session objects are long-lived and only created once per session, this overhead is negligible compared to the benefits of a flexible architecture. +/// +/// --- +/// +/// ### Restoring Flexibility and Safety with `async_trait` +/// +/// Given these considerations, we have decided to revert to the "classic" and well-established approach of using `async_trait`, which wraps asynchronous methods in `Pin>>`. While this approach has a slightly higher runtime cost, it offers several critical advantages: +/// +/// 1. Technical Correctness and Safety: +/// +/// * We are aligning with the only officially supported and widely accepted Rust practice for asynchronous traits, which is both safe and fully optimized. +/// +/// 2. Avoiding Experimental Features: +/// +/// * We are removing the dependency on partially stabilized and potentially breaking features, reducing the risk of future maintenance headaches. +/// +/// 3. Preserving Full Architectural Flexibility: +/// +/// * This approach completely preserves the ability to separate `ByteSource` and `Parser` implementations from the core, making the architecture more modular and extensible. +/// * It also simplifies unit testing, as components can be developed, tested, and deployed independently of the core logic. +/// +/// 4. Realistic Performance Considerations: +/// +/// * We acknowledge a potential minor performance impact, but this is insignificant in real-world use cases. +/// * In a typical session, the initial "frame" of data is presented to the user immediately, while the full file is loaded in the background, making a 100-500ms delay on a 1GB file completely acceptable in terms of UI/UX. +/// +/// --- +/// +/// In summary, this decision prioritizes architectural flexibility, reduces long-term maintenance risks, and ensures code stability, making the overall system more sustainable and extensible in the long run. +/// +/// A `ByteSource` provides a way to read data from some underlying data source. But it does +/// not provide a simple read interface, rather it allows implementations to filter the data +/// while reading it from it's underlying source. +/// A good example is a network trace where complete ethernet frames are described. If we only +/// want to extract the data part from certain frames, the `relaod` method will load only the relevant +/// data into an internal buffer. +/// This data can then be accessed via the `current_slice` method. +#[async_trait] +pub trait ByteSource: Send { + /// Indicate that we have consumed a certain amount of data from our internal + /// buffer and that this part can be discarded + fn consume(&mut self, offset: usize); + + /// Provide access to the filtered data that is currently loaded + fn current_slice(&self) -> &[u8]; + + /// count of currently loaded bytes + fn len(&self) -> usize; + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// will load more bytes from the underlying source + /// when the source has reached it's end, this function + /// will return Ok((None, _)) + /// + /// A successful reload operation will return the number + /// of bytes that were newly loaded `newly_loaded_bytes` + /// along with all currently available bytes `available_bytes` + /// In some cases it is possible that some bytes had to be skipped in order to + /// reach the next usable bytes, this is indicated in the `skipped_bytes` number + /// + /// + /// If the source has access to some timestamp (e.g. timestamp of network package), + /// this timestamp is passed on additionally (`last_known_ts`) + /// + /// # Note: + /// + /// This function must be **Cancel-Safe** + async fn load( + &mut self, + filter: Option<&SourceFilter>, + ) -> Result, SourceError>; + + /// In case the ByteSource is some kind of connection that does not end, + /// cancel can be implemented that will give the ByteSource the chance to perform some + /// cleanup before the ByteSource is discarded + async fn cancel(&mut self) -> Result<(), SourceError> { + Ok(()) + } + + /// Append incoming (SDE) Source-Data-Exchange to the data. + async fn income( + &mut self, + _msg: stypes::SdeRequest, + ) -> Result { + Err(SourceError::NotSupported) + } +} diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index f8d697a6c9..6c3172e172 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -13,17 +13,18 @@ log.workspace = true regex.workspace = true memchr = "2.7" serde = { workspace = true , features = ["derive"] } -thiserror.workspace = true rand.workspace = true someip-messages = { git = "https://github.com/esrlabs/someip" } someip-payload = { git = "https://github.com/esrlabs/someip-payload" } -someip-tools = { path = "../addons/someip-tools" } -stypes = { path = "../stypes", features=["rustcore"] } uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } tokio-util.workspace = true tokio.workspace = true + +stypes = { path = "../stypes", features=["rustcore"] } +definitions = { path = "../definitions" } file-tools = { path = "../addons/file-tools" } +someip-tools = { path = "../addons/someip-tools" } [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/attachment.rs b/application/apps/indexer/parsers/src/dlt/attachment.rs index d9400f42af..46703f3676 100644 --- a/application/apps/indexer/parsers/src/dlt/attachment.rs +++ b/application/apps/indexer/parsers/src/dlt/attachment.rs @@ -1,7 +1,7 @@ use dlt_core::dlt::{Argument, LogLevel, Message, MessageType, PayloadContent, Value}; use std::{collections::HashMap, path::PathBuf}; -use crate::Attachment; +use definitions::*; const FT_START_TAG: &str = "FLST"; const FT_DATA_TAG: &str = "FLDA"; diff --git a/application/apps/indexer/parsers/src/dlt/fmt.rs b/application/apps/indexer/parsers/src/dlt/fmt.rs index 9d4a6b7934..c7ee3f179e 100644 --- a/application/apps/indexer/parsers/src/dlt/fmt.rs +++ b/application/apps/indexer/parsers/src/dlt/fmt.rs @@ -35,6 +35,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::{ fmt::{self, Formatter}, str, + sync::Arc, }; /// Separator to used between the columns in DLT [`FormattableMessage`]. @@ -206,14 +207,14 @@ impl From> for FormatOptions { } /// A dlt message that can be formatted with optional FIBEX data support -pub struct FormattableMessage<'a> { +pub struct FormattableMessage { pub message: Message, - pub fibex_dlt_metadata: Option<&'a FibexDltMetadata>, - pub fibex_someip_metadata: Option<&'a FibexSomeipMetadata>, - pub options: Option<&'a FormatOptions>, + pub fibex_dlt_metadata: Option>, + pub fibex_someip_metadata: Option>, + pub options: Option>, } -impl Serialize for FormattableMessage<'_> { +impl Serialize for FormattableMessage { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -310,7 +311,7 @@ impl Serialize for FormattableMessage<'_> { } } -impl From for FormattableMessage<'_> { +impl From for FormattableMessage { fn from(message: Message) -> Self { FormattableMessage { message, @@ -344,7 +345,7 @@ impl<'a> PrintableMessage<'a> { } } -impl FormattableMessage<'_> { +impl FormattableMessage { pub fn printable_parts<'b>( &'b self, ext_h_app_id: &'b str, @@ -500,8 +501,8 @@ impl FormattableMessage<'_> { } fn info_from_metadata<'b>(&'b self, id: u32, data: &[u8]) -> Option> { - let fibex = self.fibex_dlt_metadata?; - let md = extract_metadata(fibex, id, self.message.extended_header.as_ref())?; + let fibex = self.fibex_dlt_metadata.as_ref()?; + let md = extract_metadata(&fibex, id, self.message.extended_header.as_ref())?; let msg_type: Option = message_type(&self.message, md.message_info.as_deref()); let app_id = md.application_id.as_deref().or_else(|| { self.message @@ -549,7 +550,7 @@ impl FormattableMessage<'_> { } } -impl fmt::Display for FormattableMessage<'_> { +impl fmt::Display for FormattableMessage { /// will format dlt Message with those fields: /// ********* storage-header ******** /// date-time @@ -568,7 +569,7 @@ impl fmt::Display for FormattableMessage<'_> { /// payload fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { if let Some(h) = &self.message.storage_header { - let tz = self.options.map(|o| o.tz); + let tz = self.options.as_ref().map(|o| o.tz); match tz { Some(Some(tz)) => { write_tz_string(f, &h.timestamp, &tz)?; @@ -613,7 +614,11 @@ impl fmt::Display for FormattableMessage<'_> { }) { if let Some(slice) = slices.get(1) { - match SomeipParser::parse_message(self.fibex_someip_metadata, slice, None) { + match SomeipParser::parse_message( + self.fibex_someip_metadata.as_deref(), + slice, + None, + ) { Ok((_, message)) => { let prefix = slices.first().map_or_else(String::default, |s| { parse_prefix(s) diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 4984736176..dcede6abf1 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -2,10 +2,7 @@ pub mod attachment; pub mod fmt; mod options; -use crate::{ - Error, LogMessage, ParseYield, SingleParser, dlt::fmt::FormattableMessage, - someip::FibexMetadata as FibexSomeipMetadata, -}; +use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; use byteorder::{BigEndian, WriteBytesExt}; use dlt_core::{ dlt, @@ -17,14 +14,15 @@ pub use dlt_core::{ filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; use serde::Serialize; -use std::{io::Write, ops::Range}; +use std::{io::Write, ops::Range, sync::Arc}; use self::{attachment::FtScanner, fmt::FormatOptions}; +use definitions::*; /// The most likely minimal bytes count needed to parse a DLT message. const MIN_MSG_LEN: usize = 20; -impl LogMessage for FormattableMessage<'_> { +impl LogMessage for FormattableMessage { fn to_writer(&self, writer: &mut W) -> Result { let bytes = self.message.as_bytes(); let len = bytes.len(); @@ -72,13 +70,13 @@ impl LogMessage for RawMessage { } #[derive(Default)] -pub struct DltParser<'m> { +pub struct DltParser { pub filter_config: Option, - pub fibex_dlt_metadata: Option<&'m FibexDltMetadata>, - pub fmt_options: Option<&'m FormatOptions>, + pub fibex_dlt_metadata: Option>, + pub fmt_options: Option>, pub with_storage_header: bool, ft_scanner: FtScanner, - fibex_someip_metadata: Option<&'m FibexSomeipMetadata>, + fibex_someip_metadata: Option>, offset: usize, } @@ -97,29 +95,65 @@ impl DltRawParser { with_storage_header, } } + + fn parse_item( + &mut self, + input: &[u8], + _timestamp: Option, + ) -> Result<(usize, Option>), ParserError> { + let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; + let msg = consumed.map(|c| RawMessage { + content: Vec::from(&input[0..c as usize]), + }); + let total_consumed = input.len() - rest.len(); + let item = (total_consumed, msg.map(|m| m.into())); + + Ok(item) + } } impl DltRangeParser { pub fn new() -> Self { Self { offset: 0 } } + + fn parse_item( + &mut self, + input: &[u8], + _timestamp: Option, + ) -> Result<(usize, Option>), ParserError> { + let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; + let msg = consumed.map(|c| { + self.offset += c as usize; + RangeMessage { + range: Range { + start: self.offset, + end: self.offset + c as usize, + }, + } + }); + let total_consumed = input.len() - rest.len(); + let item = (total_consumed, msg.map(|m| m.into())); + + Ok(item) + } } -impl<'m> DltParser<'m> { +impl DltParser { pub fn new( filter_config: Option, - fibex_dlt_metadata: Option<&'m FibexDltMetadata>, - fmt_options: Option<&'m FormatOptions>, - fibex_someip_metadata: Option<&'m FibexSomeipMetadata>, + fibex_dlt_metadata: Option, + fmt_options: Option, + fibex_someip_metadata: Option, with_storage_header: bool, ) -> Self { Self { filter_config, - fibex_dlt_metadata, + fibex_dlt_metadata: fibex_dlt_metadata.map(|md| Arc::new(md)), with_storage_header, - fmt_options, + fmt_options: fmt_options.map(|fo| Arc::new(fo)), ft_scanner: FtScanner::new(), - fibex_someip_metadata, + fibex_someip_metadata: fibex_someip_metadata.map(|md| Arc::new(md)), offset: 0, } } @@ -143,15 +177,17 @@ impl<'m> SingleParser> for DltParser<'m> { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option>>), Error> { - match dlt_message(input, self.filter_config.as_ref(), self.with_storage_header)? { + ) -> Result<(usize, Option>), ParserError> { + match dlt_message(input, self.filter_config.as_ref(), self.with_storage_header) + .map_err(map_dlt_err)? + { (rest, dlt_core::parse::ParsedMessage::FilteredOut(_n)) => { let consumed = input.len() - rest.len(); self.offset += consumed; Ok((consumed, None)) } (_, dlt_core::parse::ParsedMessage::Invalid) => { - Err(Error::Parse("Invalid parse".to_owned())) + Err(ParserError::Parse("Invalid parse".to_owned())) } (rest, dlt_core::parse::ParsedMessage::Item(i)) => { let attachment = self.ft_scanner.process(&i); @@ -163,9 +199,9 @@ impl<'m> SingleParser> for DltParser<'m> { let msg = FormattableMessage { message: msg_with_storage_header, - fibex_dlt_metadata: self.fibex_dlt_metadata, - options: self.fmt_options, - fibex_someip_metadata: self.fibex_someip_metadata, + fibex_dlt_metadata: self.fibex_dlt_metadata.clone(), + options: self.fmt_options.clone(), + fibex_someip_metadata: self.fibex_someip_metadata.clone(), }; let consumed = input.len() - rest.len(); self.offset += consumed; @@ -184,53 +220,45 @@ impl<'m> SingleParser> for DltParser<'m> { } } -impl SingleParser for DltRangeParser { - const MIN_MSG_LEN: usize = MIN_MSG_LEN; +fn map_dlt_err(err: DltParseError) -> ParserError { + match err { + DltParseError::Unrecoverable(e) | DltParseError::ParsingHickup(e) => { + ParserError::Parse(e.to_string()) + } + DltParseError::IncompleteParse { needed: _ } => ParserError::Incomplete, + } +} - fn parse_item( +impl Parser for DltParser { + fn parse( &mut self, input: &[u8], - _timestamp: Option, - ) -> Result<(usize, Option>), Error> { - let (rest, consumed) = dlt_consume_msg(input)?; - let msg = consumed.map(|c| { - self.offset += c as usize; - RangeMessage { - range: Range { - start: self.offset, - end: self.offset + c as usize, - }, - } - }); - let total_consumed = input.len() - rest.len(); - let item = (total_consumed, msg.map(|m| m.into())); - - Ok(item) + timestamp: Option, + ) -> Result>)>, ParserError> { + parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { + self.parse_item(input, timestamp) + }) } } -impl SingleParser for DltRawParser { - const MIN_MSG_LEN: usize = MIN_MSG_LEN; - impl Parser for DltRangeParser { fn parse( &mut self, input: &[u8], - _timestamp: Option, - ) -> Result<(usize, Option>), Error> { - let (rest, consumed) = dlt_consume_msg(input)?; - let msg = consumed.map(|c| RawMessage { - content: Vec::from(&input[0..c as usize]), - }); - let total_consumed = input.len() - rest.len(); - let item = (total_consumed, msg.map(|m| m.into())); + timestamp: Option, + ) -> Result>)>, ParserError> { + parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { + self.parse_item(input, timestamp) + }) + } +} impl Parser for DltRawParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, Error> { + ) -> Result>)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index cf9f1240f5..39d2edf947 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -7,11 +7,12 @@ use dlt_core::{ common::{StatisticInfo, StatisticInfoCollector}, }, }; -use std::collections::HashMap; use std::fmt; use std::fs::File; +use std::{collections::HashMap, path::PathBuf}; use stypes::{ - FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, ValueInput, + FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, Value, + ValueInput, }; use tokio_util::sync::CancellationToken; @@ -22,6 +23,7 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ const FIELD_LOG_LEVEL: &str = "DLT_PARSER_FIELD_LOG_LEVEL"; const FIELD_FIBEX_FILES: &str = "DLT_PARSER_FIELD_FIBEX_FILES"; const FIELD_STATISTICS: &str = "DLT_PARSER_FIELD_STATISTICS"; +const FIELD_TZ: &str = "DLT_PARSER_FIELD_TIMEZONE"; #[derive(Default)] struct Descriptor {} @@ -46,7 +48,56 @@ impl fmt::Display for StatFields { } } +fn to_native_cfg_err(msg: S) -> NativeError { + NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Configuration, + message: Some(msg.to_string()), + } +} impl ComponentDescriptor for Descriptor { + fn to_parser( + options: Vec, + origin: SourceOrigin, + ) -> Result>>, NativeError> + where + Self: Sized, + { + let mut filter_config: Option = None; + let mut someip_metadata: Option = None; + let mut dlt_metadata: Option = None; + for field in options.into_iter() { + if field.id == FIELD_FIBEX_FILES { + let Value::Files(paths) = field.value else { + return Err(to_native_cfg_err("Invalid settings for Fibex files paths")); + }; + someip_metadata = if paths.is_empty() { + None + } else { + FibexSomeipMetadata::from_fibex_files(paths.clone()) + }; + dlt_metadata = if paths.is_empty() { + None + } else { + dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { + fibex_file_paths: paths + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect(), + }) + }; + } + } + let parser = Box::new(DltParser::new( + None, + dlt_metadata, + None, + someip_metadata, + // If it's source - no storage header expected + !matches!(origin, SourceOrigin::Source), + )); + Ok(None) + } fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -235,21 +286,21 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for DltParser<'_> { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { +impl components::Component for DltParser { + fn register(components: &mut components::Components) -> Result<(), NativeError> { components.register(Descriptor::default())?; Ok(()) } } impl components::Component for DltRangeParser { - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), NativeError> { Ok(()) } } impl components::Component for DltRawParser { - fn register(_components: &mut components::Components) -> Result<(), stypes::NativeError> { + fn register(_components: &mut components::Components) -> Result<(), NativeError> { Ok(()) } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 6a5de806e4..e9be47fcda 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -2,168 +2,4 @@ pub mod dlt; pub mod prelude; pub mod someip; - pub mod text; -use serde::Serialize; -use std::{fmt::Display, io::Write, iter}; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("Unrecoverable error, cannot continue: {0}")] - Unrecoverable(String), - #[error("Parse error: {0}")] - Parse(String), - #[error("Incomplete, not enough data for a message")] - Incomplete, - #[error("End of file reached")] - Eof, -} - -#[derive(Debug)] -pub enum ParseYield { - Message(T), - Attachment(Attachment), - MessageAndAttachment((T, Attachment)), -} - -impl From for ParseYield { - fn from(item: T) -> Self { - Self::Message(item) - } -} - -/// Parser trait that needs to be implemented for any parser we support -/// in chipmunk -pub trait Parser: components::Component { - /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, - /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` - /// - /// if the slice does not have enough bytes to parse any item, an [`Error`] is returned. - /// - /// in case we could parse a message but the message was filtered out, `None` is returned on - /// that item. - /// - /// # Note: - /// - /// If the parsers encounter any error while it already has parsed any items, then it must - /// return those items without the error, then on the next call it can return the errors in - /// case it was provided with the same slice of bytes. - fn parse( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result>)>, Error>; -} - -#[derive(Debug, Clone, Serialize)] -pub struct Attachment { - pub name: String, - pub size: usize, - pub created_date: Option, - pub modified_date: Option, - /// The indexes of the message within the original trace (0-based). - pub messages: Vec, - pub data: Vec, -} - -impl Attachment { - pub fn add_data(&mut self, new_data: &[u8]) { - self.data.extend_from_slice(new_data); - } -} - -pub trait Collector { - fn register_message(&mut self, offset: usize, msg: &T); - fn attachment_indexes(&self) -> Vec; -} - -pub trait LineFormat { - fn format_line(&self) -> String; -} - -pub enum ByteRepresentation { - Owned(Vec), - Range((usize, usize)), -} - -pub trait LogMessage: Display + Serialize { - /// Serializes a message directly into a Writer - /// returns the size of the serialized message - fn to_writer(&self, writer: &mut W) -> Result; -} - -#[derive(Debug)] -pub enum MessageStreamItem { - Item(ParseYield), - Skipped, - Incomplete, - Empty, - Done, -} - -/// A trait for parsers that extract one item at a time from a byte slice. -/// -/// Any type implementing this trait will automatically implement the [`Parser`] trait -/// due to a blanket implementation. This means that such types will support extracting -/// all available items from an input slice by repeatedly calling [`SingleParser::parse_item()`] -/// until no more items can be parsed. -/// -/// # Behavior -/// -/// - The blanket implementation of [`Parser`] will repeatedly invoke `parse_item()`, -/// extracting as many items as possible. -/// - If `parse_item()` fails on the first call, an error is returned immediately. -/// - If `parse_item()` succeeds, parsing continues until: -/// - The remaining input is too short to parse another item. -/// - `parse_item()` returns an error, which is ignored after the first successful parse. -pub trait SingleParser { - /// The minimum number of bytes required to parse an item. - /// - /// # Notes: - /// - This value is used to prevent unnecessary parsing attempts when the remaining input - /// is too short to contain a valid message. - /// - The default value (`1`) indicates that the parser has no minimum length requirement. - const MIN_MSG_LEN: usize = 1; - - /// Parses a single item from the given byte slice. - /// - /// in case we could parse a message but the message was filtered out, `None` is returned on - /// that item. - fn parse_item( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result<(usize, Option>), Error>; -} - -/// This blanket implementation repeatedly applies [`SingleParser::parse_item()`] function, -/// extracting as many items as possible from the provided input until no more can be parsed. -impl Parser for P -where - P: SingleParser, -{ - fn parse( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result>)>, Error> { - let mut slice = input; - - // return early if function errors on first parse call. - let first_res = self.parse_item(slice, timestamp)?; - - // Otherwise keep parsing and stop on first error, returning the parsed items at the end. - let iter = iter::successors(Some(first_res), move |(consumed, _res)| { - slice = &slice[*consumed..]; - - if slice.len() < P::MIN_MSG_LEN { - return None; - } - - self.parse_item(slice, timestamp).ok() - }); - - Ok(iter) - } -} diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 91d21bbe89..71d24798de 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -1,5 +1,5 @@ mod options; -use crate::{parse_all, Error, LogMessage, ParseYield, Parser}; +use definitions::*; use std::{ borrow::Cow, cmp::Ordering, @@ -206,7 +206,7 @@ impl SomeipParser { fibex_metadata: Option<&FibexMetadata>, input: &[u8], timestamp: Option, - ) -> Result<(usize, SomeipLogMessage), Error> { + ) -> Result<(usize, SomeipLogMessage), ParserError> { let time = timestamp.unwrap_or(0); match Message::from_slice(input) { Ok(Message::Sd(header, payload)) => { @@ -274,11 +274,11 @@ impl SomeipParser { } Err(e) => match e { - someip_messages::Error::NotEnoughData { .. } => Err(Error::Incomplete), + someip_messages::Error::NotEnoughData { .. } => Err(ParserError::Incomplete), e => { let msg = e.to_string(); error!("at {time} : {msg}"); - Err(Error::Parse(msg)) + Err(ParserError::Parse(msg)) } }, } @@ -295,7 +295,7 @@ impl SingleParser for SomeipParser { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option>), Error> { + ) -> Result<(usize, Option>), ParserError> { SomeipParser::parse_message(self.fibex_metadata.as_ref(), input, timestamp) .map(|(rest, message)| (rest, Some(ParseYield::from(message)))) } @@ -309,7 +309,7 @@ impl Parser for SomeipParser { &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, Error> { + ) -> Result>)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) @@ -609,7 +609,7 @@ mod test { let result = parser.parse_item(input, None); match result { - Err(crate::Error::Incomplete) => {} + Err(ParserError::Incomplete) => {} Err(err) => panic!("unexpected error: {err}"), _ => panic!("unexpected parse result"), } @@ -628,7 +628,7 @@ mod test { let result = parser.parse_item(input, None); match result { - Err(crate::Error::Incomplete) => {} + Err(ParserError::Incomplete) => {} Err(err) => panic!("unexpected error: {err}"), _ => panic!("unexpected parse result"), } diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index e6045393e2..945a1b9202 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,5 +1,5 @@ mod options; -use crate::{parse_all, Error, LogMessage, ParseYield, Parser}; +use definitions::*; use serde::Serialize; use std::{fmt, io::Write}; @@ -32,7 +32,7 @@ where &mut self, input: &[u8], _timestamp: Option, - ) -> Result<(usize, Option>), Error> { + ) -> Result<(usize, Option>), ParserError> { // TODO: support non-utf8 encodings use memchr::memchr; if input.is_empty() { @@ -65,7 +65,7 @@ where &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, Error> { + ) -> Result>)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index dcbf16d1c5..3ecc7b5261 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -21,6 +21,7 @@ components = { path = "../components" } parsers = { path = "../parsers" } sources = { path = "../sources" } stypes = { path = "../stypes" } +definitions = { path = "../definitions" } # TODO : Introduce shared crates and move dir_checksum to it. dir_checksum = { path = "../../../../cli/development-cli/dir_checksum" } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 5e446e4c33..7a59d89508 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -137,39 +137,38 @@ impl PluginErrorLimits { const INCOMPLETE_ERROR_LIMIT: usize = 50; } -use parsers as p; -impl p::Parser for PluginsParser { +use definitions as defs; +impl defs::Parser for PluginsParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, p::Error> - { + ) -> Result>)>, defs::ParserError> { let res = match &mut self.parser { PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), }; // Check for consecutive errors. match &res { - Ok(_) | Err(p::Error::Unrecoverable(_)) | Err(p::Error::Eof) => { + Ok(_) | Err(defs::ParserError::Unrecoverable(_)) | Err(defs::ParserError::Eof) => { self.errors_counter = 0; } - Err(p::Error::Parse(err)) => { + Err(defs::ParserError::Parse(err)) => { self.errors_counter += 1; if self.errors_counter > PluginErrorLimits::PARSE_ERROR_LIMIT { self.errors_counter = 0; - return Err(p::Error::Unrecoverable(format!( + return Err(defs::ParserError::Unrecoverable(format!( "Plugin parser returned more than \ {} recoverable parse errors consecutively\n. Parse Error: {err}", PluginErrorLimits::PARSE_ERROR_LIMIT ))); } } - Err(p::Error::Incomplete) => { + Err(defs::ParserError::Incomplete) => { self.errors_counter += 1; if self.errors_counter > PluginErrorLimits::INCOMPLETE_ERROR_LIMIT { self.errors_counter = 0; - return Err(p::Error::Unrecoverable(format!( + return Err(defs::ParserError::Unrecoverable(format!( "Plugin parser returned more than \ {} recoverable incomplete errors consecutively", PluginErrorLimits::INCOMPLETE_ERROR_LIMIT diff --git a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs index e38996db1e..e1c3f1dfd1 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use parsers::LogMessage; +use definitions::LogMessage; use serde::Serialize; /// Represent the message of the parsed item returned by plugins. diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index b6089d8924..dc8cec3e1c 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -41,23 +41,23 @@ impl From<&stypes::PluginParserGeneralSettings> for ParserConfig { } } -use parsers as p; +use definitions as defs; -impl From for p::ParseYield { +impl From for defs::ParseYield { fn from(yld: ParseYield) -> Self { match yld { - ParseYield::Message(msg) => p::ParseYield::Message(msg.into()), - ParseYield::Attachment(att) => p::ParseYield::Attachment(att.into()), + ParseYield::Message(msg) => defs::ParseYield::Message(msg.into()), + ParseYield::Attachment(att) => defs::ParseYield::Attachment(att.into()), ParseYield::MessageAndAttachment((msg, att)) => { - p::ParseYield::MessageAndAttachment((msg.into(), att.into())) + defs::ParseYield::MessageAndAttachment((msg.into(), att.into())) } } } } -impl From for p::Attachment { +impl From for defs::Attachment { fn from(att: Attachment) -> Self { - p::Attachment { + defs::Attachment { data: att.data, name: att.name, size: att.size as usize, @@ -68,13 +68,13 @@ impl From for p::Attachment { } } -impl From for p::Error { +impl From for defs::ParserError { fn from(err: ParseError) -> Self { match err { - ParseError::Unrecoverable(msg) => p::Error::Unrecoverable(msg), - ParseError::Parse(msg) => p::Error::Parse(msg), - ParseError::Incomplete => p::Error::Incomplete, - ParseError::Eof => p::Error::Eof, + ParseError::Unrecoverable(msg) => defs::ParserError::Unrecoverable(msg), + ParseError::Parse(msg) => defs::ParserError::Parse(msg), + ParseError::Incomplete => defs::ParserError::Incomplete, + ParseError::Eof => defs::ParserError::Eof, } } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 9e6675d27d..94423f0249 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -131,8 +131,8 @@ impl PluginParser { } } -use parsers as p; -impl p::Parser for PluginParser { +use definitions as defs; +impl defs::Parser for PluginParser { fn parse( &mut self, input: &[u8], @@ -164,10 +164,10 @@ impl p::Parser for PluginParser { } }; - let res = parse_results + Ok(parse_results .into_iter() - .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))); - Ok(res) + .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))) + .collect()) } } diff --git a/application/apps/indexer/processor/Cargo.toml b/application/apps/indexer/processor/Cargo.toml index 2601c3aa45..8530482c1b 100644 --- a/application/apps/indexer/processor/Cargo.toml +++ b/application/apps/indexer/processor/Cargo.toml @@ -6,25 +6,26 @@ edition = "2024" [dependencies] bincode = "1.3" -bufread = { path = "../addons/bufread" } bytecount = "0.6" futures.workspace = true grep-regex.workspace = true grep-searcher.workspace = true -indexer_base = { path = "../indexer_base" } itertools = "0.13" lazy_static.workspace = true log.workspace = true -parsers = { path = "../parsers" } -sources = { path = "../sources" } regex.workspace = true serde = { workspace = true , features = ["derive"] } serde_json.workspace = true -text_grep = { path = "../addons/text_grep" } thiserror.workspace = true tokio-util.workspace = true uuid = { workspace = true , features = ["serde", "v4"] } +bufread = { path = "../addons/bufread" } +text_grep = { path = "../addons/text_grep" } stypes = { path = "../stypes", features=["rustcore"] } +parsers = { path = "../parsers" } +sources = { path = "../sources" } +definitions = { path = "../definitions" } +indexer_base = { path = "../indexer_base" } [dev-dependencies] criterion.workspace = true diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs index 6d5b13c228..b26db6d6db 100644 --- a/application/apps/indexer/processor/src/export/mod.rs +++ b/application/apps/indexer/processor/src/export/mod.rs @@ -3,9 +3,9 @@ use std::{ path::Path, }; +use definitions::{ByteSource, LogMessage, MessageStreamItem, ParseYield, Parser}; use indexer_base::config::IndexSection; -use parsers::{LogMessage, MessageStreamItem, ParseYield, Parser}; -use sources::{ByteSource, producer::MessageProducer}; +use sources::producer::MessageProducer; use thiserror::Error; use tokio_util::sync::CancellationToken; diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index c567c3c2d8..f0c7cc11d7 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -10,29 +10,31 @@ crossbeam-channel.workspace = true dirs.workspace = true dlt-core = { workspace = true, features = ["statistics", "serialization"] } envvars = { workspace = true } -file-tools = { path = "../addons/file-tools" } futures.workspace = true -indexer_base = { path = "../indexer_base" } lazy_static.workspace = true log.workspace = true -merging = { path = "../merging" } mime_guess = "2.0" -parsers = { path = "../parsers" } -processor = { path = "../processor" } rustc-hash = "2.1" -stypes = { path = "../stypes", features=["rustcore"] } serde = { workspace = true , features = ["derive"] } serde_json.workspace = true serialport.workspace = true -sources = { path = "../sources" } thiserror.workspace = true tokio = { workspace = true , features = ["full"] } tokio-stream.workspace = true tokio-util.workspace = true uuid = { workspace = true , features = ["serde", "v4"] } walkdir.workspace = true -plugins_host = {path = "../plugins_host/"} + +indexer_base = { path = "../indexer_base" } +plugins_host = { path = "../plugins_host/"} components = { path = "../components" } +definitions = { path = "../definitions" } +stypes = { path = "../stypes", features=["rustcore"] } +parsers = { path = "../parsers" } +processor = { path = "../processor" } +sources = { path = "../sources" } +merging = { path = "../merging" } +file-tools = { path = "../addons/file-tools" } [dev-dependencies] lazy_static.workspace = true diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/export_raw.rs index da1e3a2d58..91b83798f2 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/export_raw.rs @@ -1,4 +1,5 @@ use crate::{operations::OperationResult, state::SessionStateAPI}; +use definitions::{ByteSource, LogMessage, Parser}; use indexer_base::config::IndexSection; use log::debug; use parsers::{ @@ -156,8 +157,9 @@ async fn export( let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); let parser = DltParser::new( settings.filter_config.as_ref().map(|f| f.into()), - settings.fibex_metadata.as_ref(), - fmt_options.as_ref(), + // TODO: find a way to avoid clonning of MD + settings.fibex_metadata.as_ref().map(|md| md.clone()), + fmt_options, None, settings.with_storage_header, ); diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index d753dd8c8f..8a73c7ccd5 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -5,6 +5,7 @@ use crate::{ state::SessionStateAPI, tail, }; +use definitions::{ByteSource, LogMessage, MessageStreamItem, ParseYield, Parser}; use log::trace; use parsers::{ LogMessage, MessageStreamItem, ParseYield, Parser, @@ -110,9 +111,10 @@ async fn run_source_intern( }); let dlt_parser = DltParser::new( settings.filter_config.as_ref().map(|f| f.into()), - settings.fibex_metadata.as_ref(), - fmt_options.as_ref(), - someip_metadata.as_ref(), + // TODO: find a way to avoid clonning of MD + settings.fibex_metadata.as_ref().map(|md| md.clone()), + fmt_options, + someip_metadata, settings.with_storage_header, ); let producer = MessageProducer::new(dlt_parser, source); diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index a67f4af3ea..4233c6d9d7 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -7,7 +7,6 @@ use crate::{ tracker::OperationTrackerAPI, }; use log::error; -use parsers; use processor::{ grabber::LineRange, map::{FiltersStats, ScaledDistribution}, @@ -193,7 +192,7 @@ pub enum Api { SetDebugMode((bool, oneshot::Sender<()>)), NotifyCancelingOperation(Uuid), NotifyCanceledOperation(Uuid), - AddAttachment(parsers::Attachment), + AddAttachment(definitions::Attachment), GetAttachments(oneshot::Sender>), // Used for tests of error handeling ShutdownWithError, @@ -688,7 +687,10 @@ impl SessionStateAPI { }) } - pub fn add_attachment(&self, origin: parsers::Attachment) -> Result<(), stypes::NativeError> { + pub fn add_attachment( + &self, + origin: definitions::Attachment, + ) -> Result<(), stypes::NativeError> { self.tx_api.send(Api::AddAttachment(origin)).map_err(|e| { stypes::NativeError::channel( &format!("fail to send to Api::AddAttachment; error: {e}",), diff --git a/application/apps/indexer/session/src/state/attachments.rs b/application/apps/indexer/session/src/state/attachments.rs index 28872426bc..0d682f2014 100644 --- a/application/apps/indexer/session/src/state/attachments.rs +++ b/application/apps/indexer/session/src/state/attachments.rs @@ -1,5 +1,4 @@ use mime_guess; -use parsers::{self}; use std::{ collections::HashMap, fs::{File, create_dir}, @@ -100,7 +99,7 @@ impl Attachments { } pub fn get_attch_from( - origin: parsers::Attachment, + origin: definitions::Attachment, store_folder: &PathBuf, ) -> Result { if !store_folder.exists() { @@ -147,7 +146,7 @@ impl Attachments { pub fn add( &mut self, - attachment: parsers::Attachment, + attachment: definitions::Attachment, ) -> Result { if let Some(dest) = self.dest.as_ref() { let uuid = Uuid::new_v4(); diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index e329eda2fd..6b544922e9 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -1,5 +1,4 @@ use log::{debug, error}; -use parsers; use processor::{ grabber::LineRange, map::SearchMap, @@ -496,7 +495,7 @@ impl SessionState { fn handle_add_attachment( &mut self, - origin: parsers::Attachment, + origin: definitions::Attachment, tx_callback_events: UnboundedSender, ) -> Result<(), stypes::NativeError> { let attachment = self.attachments.add(origin)?; diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 3196892bea..051c83922e 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -8,7 +8,7 @@ edition = "2024" async-stream = "0.3" bufread = { path = "../addons/bufread" } bytes = "1.3" -etherparse = "0.16" +etherparse.workspace = true futures.workspace = true indexer_base = { path = "../indexer_base" } log.workspace = true @@ -23,12 +23,14 @@ serde = { workspace = true, features = ["derive"] } uuid = { workspace = true , features = ["serde", "v4"] } regex.workspace = true lazy_static.workspace = true +async-trait.workspace = true shellexpand = "3.1" -stypes = { path = "../stypes", features=["rustcore"] } socket2 = "0.5.8" -components = { path = "../components" } serialport.workspace = true file-tools = { path = "../addons/file-tools" } +stypes = { path = "../stypes", features=["rustcore"] } +components = { path = "../components" } +definitions = { path = "../definitions" } [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 2674c969b7..deb683ca48 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -1,7 +1,5 @@ -use crate::{ - ByteSource, Error as SourceError, ReloadInfo, SourceFilter, TransportProtocol, - binary::pcap::debug_block, -}; +use crate::{binary::pcap::debug_block}; +use definitions::*; use bufread::DeqBuffer; use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; @@ -10,6 +8,7 @@ use log::{debug, error, trace}; use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; use std::io::Read; use stypes::SourceOrigin; +use async_trait::async_trait; pub struct PcapLegacyByteSource { pcap_reader: LegacyPcapReader, @@ -30,6 +29,7 @@ impl PcapLegacyByteSource { } } +#[async_trait] impl ByteSource for PcapLegacyByteSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 0fe3420c9a..c6613a155f 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -1,7 +1,5 @@ -use crate::{ - ByteSource, Error as SourceError, ReloadInfo, SourceFilter, TransportProtocol, - binary::pcap::debug_block, -}; +use crate::{binary::pcap::debug_block}; +use definitions::*; use bufread::DeqBuffer; use components::ComponentDescriptor; use etherparse::{SlicedPacket, TransportSlice}; @@ -10,6 +8,7 @@ use log::{debug, error, trace}; use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; use std::io::Read; use stypes::SourceOrigin; +use async_trait::async_trait; pub struct PcapngByteSource { pcapng_reader: PcapNGReader, @@ -30,6 +29,7 @@ impl PcapngByteSource { } } +#[async_trait] impl ByteSource for PcapngByteSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 9893819d8f..14d041bef8 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -1,9 +1,7 @@ -use crate::{ - ByteSource, DEFAULT_MIN_BUFFER_SPACE, DEFAULT_READER_CAPACITY, Error as SourceError, - ReloadInfo, SourceFilter, -}; +use async_trait::async_trait; use bufread::BufReader; use components::ComponentDescriptor; +use definitions::*; use file_tools::is_binary; use std::io::{BufRead, Read}; use stypes::SourceOrigin; @@ -34,6 +32,7 @@ where } } +#[async_trait] impl ByteSource for BinaryByteSource { async fn load(&mut self, _: Option<&SourceFilter>) -> Result, SourceError> { let initial_buf_len = self.reader.len(); @@ -87,6 +86,16 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ struct Descriptor {} impl ComponentDescriptor for Descriptor { + fn to_source( + inner: R, + _origin: SourceOrigin, + _options: Vec, + ) -> Result>, stypes::NativeError> + where + Self: Sized, + { + Ok(Some(Box::new(BinaryByteSource::new(inner)))) + } fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 9e85384a94..19941cedaa 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,6 +1,7 @@ -use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; +use async_trait::async_trait; use bufread::DeqBuffer; use components::ComponentDescriptor; +use definitions::*; use regex::{Captures, Regex}; use shellexpand::tilde; use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; @@ -157,6 +158,7 @@ impl ProcessSource { } } +#[async_trait] impl ByteSource for ProcessSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 3317b802a3..bd74c83db8 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,8 +1,5 @@ // Rust can't currently distinguish between dev and none-dev dependencies at the moment. There is // an open issue for this case: "https://github.com/rust-lang/rust/issues/129637" - -use thiserror::Error; - #[macro_use] extern crate lazy_static; @@ -19,130 +16,3 @@ pub mod producer; pub mod sde; pub mod serial; pub mod socket; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TransportProtocol { - TCP, - UDP, - Unknown, -} - -impl From> for TransportProtocol { - fn from(tp_slice: etherparse::TransportSlice<'_>) -> Self { - match tp_slice { - etherparse::TransportSlice::Tcp(_) => TransportProtocol::TCP, - etherparse::TransportSlice::Udp(_) => TransportProtocol::UDP, - _ => TransportProtocol::Unknown, - } - } -} - -#[derive(Debug)] -pub struct SourceFilter { - transport: Option, -} - -#[derive(Debug)] -pub struct ReloadInfo { - pub newly_loaded_bytes: usize, - pub available_bytes: usize, - pub skipped_bytes: usize, - pub last_known_ts: Option, -} - -impl ReloadInfo { - pub fn new( - newly_loaded_bytes: usize, - available_bytes: usize, - skipped_bytes: usize, - last_known_ts: Option, - ) -> Self { - Self { - newly_loaded_bytes, - available_bytes, - skipped_bytes, - last_known_ts, - } - } -} - -#[derive(Error, Debug)] -pub enum Error { - #[error("Sources setup problem: {0}")] - Setup(String), - #[error("Unrecoverable source error: {0}")] - Unrecoverable(String), - #[error("IO error: {0}")] - Io(std::io::Error), - #[error("Not supported feature")] - NotSupported, -} - -impl From for stypes::NativeError { - fn from(err: Error) -> Self { - stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::ComputationFailed, - message: Some(format!("Fail create source: {err}")), - } - } -} - -pub(crate) const DEFAULT_READER_CAPACITY: usize = 10 * 1024 * 1024; -pub(crate) const DEFAULT_MIN_BUFFER_SPACE: usize = 10 * 1024; - -// Warning can be suppressed here because we are using this trait in our own codebase only. -#[allow(async_fn_in_trait)] -/// A `ByteSource` provides a way to read data from some underlying data source. But it does -/// not provide a simple read interface, rather it allows implementations to filter the data -/// while reading it from it's underlying source. -/// A good example is a network trace where complete ethernet frames are described. If we only -/// want to extract the data part from certain frames, the `relaod` method will load only the relevant -/// data into an internal buffer. -/// This data can then be accessed via the `current_slice` method. -pub trait ByteSource: Send + components::Component { - /// Indicate that we have consumed a certain amount of data from our internal - /// buffer and that this part can be discarded - fn consume(&mut self, offset: usize); - - /// Provide access to the filtered data that is currently loaded - fn current_slice(&self) -> &[u8]; - - /// count of currently loaded bytes - fn len(&self) -> usize; - - fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// will load more bytes from the underlying source - /// when the source has reached it's end, this function - /// will return Ok((None, _)) - /// - /// A successful reload operation will return the number - /// of bytes that were newly loaded `newly_loaded_bytes` - /// along with all currently available bytes `available_bytes` - /// In some cases it is possible that some bytes had to be skipped in order to - /// reach the next usable bytes, this is indicated in the `skipped_bytes` number - /// - /// - /// If the source has access to some timestamp (e.g. timestamp of network package), - /// this timestamp is passed on additionally (`last_known_ts`) - /// - /// # Note: - /// - /// This function must be **Cancel-Safe** - async fn load(&mut self, filter: Option<&SourceFilter>) -> Result, Error>; - - /// In case the ByteSource is some kind of connection that does not end, - /// cancel can be implemented that will give the ByteSource the chance to perform some - /// cleanup before the ByteSource is discarded - async fn cancel(&mut self) -> Result<(), Error> { - Ok(()) - } - - /// Append incoming (SDE) Source-Data-Exchange to the data. - async fn income(&mut self, _msg: stypes::SdeRequest) -> Result { - Err(Error::NotSupported) - } -} diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/sources/src/producer.rs index 2504c45747..c363888308 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/sources/src/producer.rs @@ -1,9 +1,8 @@ #[cfg(test)] mod tests; -use crate::{ByteSource, ReloadInfo, SourceFilter}; +use definitions::*; use log::warn; -use parsers::{Error as ParserError, LogMessage, MessageStreamItem, Parser}; use std::marker::PhantomData; /// Number of bytes to skip on initial parse errors before terminating the session. @@ -105,8 +104,8 @@ impl, D: ByteSource> MessageProducer { match self .parser .parse(self.byte_source.current_slice(), self.last_seen_ts) - .map(|iter| { - iter.for_each(|item| match item { + .map(|items| { + items.into_iter().for_each(|item| match item { (consumed, Some(m)) => { let total_used_bytes = consumed + skipped_bytes; // Reset skipped bytes since it had been counted here. @@ -312,7 +311,7 @@ impl, D: ByteSource> MessageProducer { pub async fn sde_income( &mut self, msg: stypes::SdeRequest, - ) -> Result { + ) -> Result { self.byte_source.income(msg).await } } diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index 68c79aca4f..af8dd77e03 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,6 +1,7 @@ -use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; +use async_trait::async_trait; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; +use definitions::*; use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, @@ -125,6 +126,7 @@ impl SerialSource { } } +#[async_trait] impl ByteSource for SerialSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index da4de54c7f..b2fa55a912 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,6 +1,7 @@ -use crate::{ByteSource, Error as SourceError, ReloadInfo, SourceFilter}; +use async_trait::async_trait; use bufread::DeqBuffer; use components::ComponentDescriptor; +use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{net::SocketAddr, time::Duration}; @@ -69,6 +70,7 @@ impl TcpSource { } } +#[async_trait] impl ByteSource for TcpSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 6ca9ef9f62..dc8a468451 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -1,17 +1,15 @@ +use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; +use crate::socket::{BuffCapacityState, handle_buff_capacity}; +use async_trait::async_trait; use bufread::DeqBuffer; use components::ComponentDescriptor; +use definitions::*; use log::trace; use std::net::{IpAddr, Ipv4Addr}; use stypes::SourceOrigin; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; -use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; -use crate::{ - ByteSource, Error as SourceError, ReloadInfo, SourceFilter, - socket::{BuffCapacityState, handle_buff_capacity}, -}; - #[derive(Error, Debug)] pub enum UdpSourceError { #[error("IO Error: {0}")] @@ -77,6 +75,7 @@ impl UdpSource { } } +#[async_trait] impl ByteSource for UdpSource { async fn load( &mut self, diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 1722fadb84..200e7bf7d8 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,6 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ComponentOptions } from "./options"; import type { PluginParserSettings } from "./plugins"; +export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; + export type ComponentType = "Parser" | "Source"; /** @@ -118,6 +121,8 @@ send_data_delay: number, */ exclusive: boolean, }; +export type SessionSetup = { origin: SourceOrigin, components: Array, }; + /** * Settings for the SomeIp parser. */ diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index be00bb20d9..60e07e9e55 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -28,6 +28,30 @@ pub enum SourceOrigin { Source, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub enum ComponentDef { + Source(ComponentOptions), + Parser(ComponentOptions), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +struct SessionSetup { + origin: SourceOrigin, + components: Vec, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index ee44764bec..240ad637c0 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2436,6 +2436,8 @@ dependencies = [ "chrono-tz", "components", "dlt-core", + "file-tools", + "humantime", "lazy_static", "log", "memchr", @@ -3234,6 +3236,7 @@ dependencies = [ "bytes", "components", "etherparse", + "file-tools", "futures", "indexer_base", "lazy_static", diff --git a/application/platform/types/observe/index.ts b/application/platform/types/observe/index.ts index 7cba9c3d76..1de23d3038 100644 --- a/application/platform/types/observe/index.ts +++ b/application/platform/types/observe/index.ts @@ -6,14 +6,28 @@ import { Signature } from '../env/types'; import * as Parser from './parser'; import * as Origin from './origin'; import * as Sde from './sde'; +import * as Bindings from '../bindings'; export * as Parser from './parser'; export * as Origin from './origin'; export * as Types from './types'; export * as Description from './description'; +export * as Bindings from '../bindings'; export { IList, IOriginDetails, IJob } from './description'; +export interface ISessionSetup { + origin: Bindings.SourceOrigin; + parser: { + uuid: string; + options: Bindings.Field[]; + }; + source: { + uuid: string; + options: Bindings.Field[]; + }; +} + export interface IObserve { origin: Origin.IConfiguration; parser: Parser.IConfiguration; diff --git a/cli/chipmunk-cli/Cargo.lock b/cli/chipmunk-cli/Cargo.lock index cb81f94e4f..3a973cb0f1 100644 --- a/cli/chipmunk-cli/Cargo.lock +++ b/cli/chipmunk-cli/Cargo.lock @@ -505,6 +505,13 @@ dependencies = [ "syn", ] +[[package]] +name = "file-tools" +version = "0.1.0" +dependencies = [ + "anyhow", +] + [[package]] name = "fnv" version = "1.0.7" @@ -967,6 +974,8 @@ dependencies = [ "chrono-tz", "components", "dlt-core", + "file-tools", + "humantime", "lazy_static", "log", "memchr", @@ -1389,6 +1398,7 @@ dependencies = [ "bytes", "components", "etherparse", + "file-tools", "futures", "indexer_base", "lazy_static", From 6c1f9e24a68c88c34d2c7e818284bfaf58e51207 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 9 May 2025 09:10:50 +0200 Subject: [PATCH 030/121] Switch LogMessage to enum --- .../apps/indexer/components/src/tys.rs | 4 +- .../apps/indexer/definitions/src/parser.rs | 68 +++++-- application/apps/indexer/parsers/Cargo.toml | 2 +- .../apps/indexer/parsers/src/dlt/fmt.rs | 17 +- .../apps/indexer/parsers/src/dlt/mod.rs | 182 ++++++++---------- .../apps/indexer/parsers/src/dlt/options.rs | 14 +- .../apps/indexer/parsers/src/someip/mod.rs | 28 +-- .../apps/indexer/parsers/src/text/mod.rs | 46 ++--- .../plugins_host/src/parser_shared/mod.rs | 7 +- .../src/parser_shared/plugin_parse_message.rs | 17 +- .../src/v0_1_0/parser/bindings.rs | 16 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 2 +- .../apps/indexer/processor/src/export/mod.rs | 9 +- .../session/src/handlers/export_raw.rs | 9 +- .../session/src/handlers/observing/mod.rs | 10 +- .../apps/indexer/sources/src/producer.rs | 14 +- 16 files changed, 215 insertions(+), 230 deletions(-) diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 2f5983aa53..5c11ba2bee 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -96,10 +96,10 @@ pub trait ComponentDescriptor { { Ok(None) } - fn to_parser( + fn to_parser( _options: Vec, _origin: SourceOrigin, - ) -> Result>>, stypes::NativeError> + ) -> Result>, stypes::NativeError> where Self: Sized, { diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index 150af0ee3c..ed752de40a 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -1,7 +1,10 @@ +use std::{fmt, io::Write}; + use serde::Serialize; -use std::{fmt::Display, io::Write}; use thiserror::Error; +pub const COLUMN_SENTINAL: char = '\u{0004}'; + #[derive(Error, Debug)] pub enum ParserError { #[error("Unrecoverable error, cannot continue: {0}")] @@ -15,21 +18,15 @@ pub enum ParserError { } #[derive(Debug)] -pub enum ParseYield { - Message(T), +pub enum ParseYield { + Message(LogMessage), Attachment(Attachment), - MessageAndAttachment((T, Attachment)), -} - -impl From for ParseYield { - fn from(item: T) -> Self { - Self::Message(item) - } + MessageAndAttachment((LogMessage, Attachment)), } /// Parser trait that needs to be implemented for any parser we support /// in chipmunk -pub trait Parser { +pub trait Parser { /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` /// @@ -47,7 +44,7 @@ pub trait Parser { &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, ParserError>; + ) -> Result)>, ParserError>; } #[derive(Debug, Clone, Serialize)] @@ -81,15 +78,44 @@ pub enum ByteRepresentation { Range((usize, usize)), } -pub trait LogMessage: Display + Serialize { - /// Serializes a message directly into a Writer - /// returns the size of the serialized message - fn to_writer(&self, writer: &mut W) -> Result; +#[derive(Debug)] +pub enum LogMessage { + Raw(Vec), + PlainText(String), + Columns(Vec), +} + +impl LogMessage { + pub fn to_writer(&self, writer: &mut W) -> Result { + match self { + Self::PlainText(msg) => writer.write(msg.as_bytes()), + Self::Columns(columns) => { + writer.write(columns.join(&COLUMN_SENTINAL.to_string()).as_bytes()) + } + Self::Raw(raw) => writer.write(&raw), + } + } +} + +impl fmt::Display for LogMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::PlainText(msg) => write!(f, "{}", msg), + Self::Columns(columns) => write!(f, "{}", columns.join(";")), + Self::Raw(raw) => write!(f, "[{:02X?}]", raw), + } + } +} + +impl From for ParseYield { + fn from(msg: LogMessage) -> Self { + ParseYield::Message(msg) + } } #[derive(Debug)] -pub enum MessageStreamItem { - Item(ParseYield), +pub enum MessageStreamItem { + Item(ParseYield), Skipped, Incomplete, Empty, @@ -179,14 +205,14 @@ pub enum MessageStreamItem { /// - The number of bytes consumed. /// - An optional parsed item [`ParseYield`]. /// - If parsing fails, it returns an [`ParserError`]. -pub fn parse_all( +pub fn parse_all( input: &[u8], timestamp: Option, min_bytes_count: usize, mut parse_fn: F, -) -> Result>)>, ParserError> +) -> Result)>, ParserError> where - F: FnMut(&[u8], Option) -> Result<(usize, Option>), ParserError>, + F: FnMut(&[u8], Option) -> Result<(usize, Option), ParserError>, { let mut slice = input; let mut results = Vec::new(); diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 6c3172e172..d0b8947df4 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -byteorder = "1.5" +# byteorder = "1.5" chrono = "0.4" chrono-tz = "0.10" dlt-core = { workspace = true, features = ["serialization", "fibex"] } diff --git a/application/apps/indexer/parsers/src/dlt/fmt.rs b/application/apps/indexer/parsers/src/dlt/fmt.rs index c7ee3f179e..8609fa1e9a 100644 --- a/application/apps/indexer/parsers/src/dlt/fmt.rs +++ b/application/apps/indexer/parsers/src/dlt/fmt.rs @@ -35,7 +35,6 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; use std::{ fmt::{self, Formatter}, str, - sync::Arc, }; /// Separator to used between the columns in DLT [`FormattableMessage`]. @@ -207,14 +206,14 @@ impl From> for FormatOptions { } /// A dlt message that can be formatted with optional FIBEX data support -pub struct FormattableMessage { +pub struct FormattableMessage<'a> { pub message: Message, - pub fibex_dlt_metadata: Option>, - pub fibex_someip_metadata: Option>, - pub options: Option>, + pub fibex_dlt_metadata: Option<&'a FibexDltMetadata>, + pub fibex_someip_metadata: Option<&'a FibexSomeipMetadata>, + pub options: Option<&'a FormatOptions>, } -impl Serialize for FormattableMessage { +impl<'a> Serialize for FormattableMessage<'a> { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -311,7 +310,7 @@ impl Serialize for FormattableMessage { } } -impl From for FormattableMessage { +impl<'a> From for FormattableMessage<'a> { fn from(message: Message) -> Self { FormattableMessage { message, @@ -345,7 +344,7 @@ impl<'a> PrintableMessage<'a> { } } -impl FormattableMessage { +impl<'a> FormattableMessage<'a> { pub fn printable_parts<'b>( &'b self, ext_h_app_id: &'b str, @@ -550,7 +549,7 @@ impl FormattableMessage { } } -impl fmt::Display for FormattableMessage { +impl<'a> fmt::Display for FormattableMessage<'a> { /// will format dlt Message with those fields: /// ********* storage-header ******** /// date-time diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index dcede6abf1..c324d67cfb 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -3,7 +3,7 @@ pub mod fmt; mod options; use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; -use byteorder::{BigEndian, WriteBytesExt}; +// use byteorder::{BigEndian, WriteBytesExt}; use dlt_core::{ dlt, parse::{DltParseError, dlt_consume_msg, dlt_message}, @@ -14,7 +14,7 @@ pub use dlt_core::{ filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; use serde::Serialize; -use std::{io::Write, ops::Range, sync::Arc}; +// use std::{io::Write, ops::Range, sync::Arc}; use self::{attachment::FtScanner, fmt::FormatOptions}; use definitions::*; @@ -22,66 +22,45 @@ use definitions::*; /// The most likely minimal bytes count needed to parse a DLT message. const MIN_MSG_LEN: usize = 20; -impl LogMessage for FormattableMessage { - fn to_writer(&self, writer: &mut W) -> Result { - let bytes = self.message.as_bytes(); - let len = bytes.len(); - writer.write_all(&bytes)?; - Ok(len) - } -} +// #[derive(Debug, Serialize)] +// pub struct RangeMessage { +// pub range: Range, +// } + +// impl std::fmt::Display for RangeMessage { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "({:?})", self.range) +// } +// } #[derive(Debug, Serialize)] pub struct RawMessage { pub content: Vec, } -#[derive(Debug, Serialize)] -pub struct RangeMessage { - pub range: Range, -} - -impl std::fmt::Display for RangeMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "({:?})", self.range) - } -} impl std::fmt::Display for RawMessage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "[{:02X?}]", &self.content) } } -impl LogMessage for RangeMessage { - /// A RangeMessage only has range information and cannot serialize to bytes - fn to_writer(&self, writer: &mut W) -> Result { - writer.write_u64::(self.range.start as u64)?; - writer.write_u64::(self.range.end as u64)?; - Ok(8 + 8) - } -} - -impl LogMessage for RawMessage { - fn to_writer(&self, writer: &mut W) -> Result { - let len = self.content.len(); - writer.write_all(&self.content)?; - Ok(len) - } -} +// impl LogMessage for RangeMessage { +// /// A RangeMessage only has range information and cannot serialize to bytes +// fn to_writer(&self, writer: &mut W) -> Result { +// writer.write_u64::(self.range.start as u64)?; +// writer.write_u64::(self.range.end as u64)?; +// Ok(8 + 8) +// } +// } #[derive(Default)] pub struct DltParser { pub filter_config: Option, - pub fibex_dlt_metadata: Option>, - pub fmt_options: Option>, + pub fibex_dlt_metadata: Option, + pub fmt_options: Option, pub with_storage_header: bool, ft_scanner: FtScanner, - fibex_someip_metadata: Option>, - offset: usize, -} - -#[derive(Default)] -pub struct DltRangeParser { + fibex_someip_metadata: Option, offset: usize, } @@ -100,44 +79,46 @@ impl DltRawParser { &mut self, input: &[u8], _timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { + ) -> Result<(usize, Option), ParserError> { let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; - let msg = consumed.map(|c| RawMessage { - content: Vec::from(&input[0..c as usize]), - }); + let msg = consumed.map(|c| LogMessage::Raw(Vec::from(&input[0..c as usize]))); let total_consumed = input.len() - rest.len(); let item = (total_consumed, msg.map(|m| m.into())); - Ok(item) } } -impl DltRangeParser { - pub fn new() -> Self { - Self { offset: 0 } - } - - fn parse_item( - &mut self, - input: &[u8], - _timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { - let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; - let msg = consumed.map(|c| { - self.offset += c as usize; - RangeMessage { - range: Range { - start: self.offset, - end: self.offset + c as usize, - }, - } - }); - let total_consumed = input.len() - rest.len(); - let item = (total_consumed, msg.map(|m| m.into())); - - Ok(item) - } -} +// #[derive(Default)] +// pub struct DltRangeParser { +// offset: usize, +// } + +// impl DltRangeParser { +// pub fn new() -> Self { +// Self { offset: 0 } +// } + +// fn parse_item( +// &mut self, +// input: &[u8], +// _timestamp: Option, +// ) -> Result<(usize, Option), ParserError> { +// let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; +// let msg = consumed.map(|c| { +// self.offset += c as usize; +// RangeMessage { +// range: Range { +// start: self.offset, +// end: self.offset + c as usize, +// }, +// } +// }); +// let total_consumed = input.len() - rest.len(); +// let item = (total_consumed, msg.map(|m| m.into())); + +// Ok(item) +// } +// } impl DltParser { pub fn new( @@ -149,11 +130,11 @@ impl DltParser { ) -> Self { Self { filter_config, - fibex_dlt_metadata: fibex_dlt_metadata.map(|md| Arc::new(md)), + fibex_dlt_metadata, with_storage_header, - fmt_options: fmt_options.map(|fo| Arc::new(fo)), + fmt_options, ft_scanner: FtScanner::new(), - fibex_someip_metadata: fibex_someip_metadata.map(|md| Arc::new(md)), + fibex_someip_metadata, offset: 0, } } @@ -177,7 +158,7 @@ impl<'m> SingleParser> for DltParser<'m> { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { + ) -> Result<(usize, Option), ParserError> { match dlt_message(input, self.filter_config.as_ref(), self.with_storage_header) .map_err(map_dlt_err)? { @@ -199,18 +180,21 @@ impl<'m> SingleParser> for DltParser<'m> { let msg = FormattableMessage { message: msg_with_storage_header, - fibex_dlt_metadata: self.fibex_dlt_metadata.clone(), - options: self.fmt_options.clone(), - fibex_someip_metadata: self.fibex_someip_metadata.clone(), + fibex_dlt_metadata: self.fibex_dlt_metadata.as_ref(), + options: self.fmt_options.as_ref(), + fibex_someip_metadata: self.fibex_someip_metadata.as_ref(), }; let consumed = input.len() - rest.len(); self.offset += consumed; let item = ( consumed, if let Some(attachment) = attachment { - Some(ParseYield::MessageAndAttachment((msg, attachment))) + Some(ParseYield::MessageAndAttachment(( + LogMessage::PlainText(msg.to_string()), + attachment, + ))) } else { - Some(ParseYield::Message(msg)) + Some(ParseYield::Message(LogMessage::PlainText(msg.to_string()))) }, ); @@ -229,36 +213,36 @@ fn map_dlt_err(err: DltParseError) -> ParserError { } } -impl Parser for DltParser { +impl Parser for DltParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, ParserError> { + ) -> Result)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) } } -impl Parser for DltRangeParser { - fn parse( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result>)>, ParserError> { - parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { - self.parse_item(input, timestamp) - }) - } -} +// impl Parser for DltRangeParser { +// fn parse( +// &mut self, +// input: &[u8], +// timestamp: Option, +// ) -> Result)>, ParserError> { +// parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { +// self.parse_item(input, timestamp) +// }) +// } +// } -impl Parser for DltRawParser { +impl Parser for DltRawParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, ParserError> { + ) -> Result)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 39d2edf947..06f99395ee 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -56,10 +56,10 @@ fn to_native_cfg_err(msg: S) -> NativeError { } } impl ComponentDescriptor for Descriptor { - fn to_parser( + fn to_parser( options: Vec, origin: SourceOrigin, - ) -> Result>>, NativeError> + ) -> Result>, NativeError> where Self: Sized, { @@ -293,11 +293,11 @@ impl components::Component for DltParser { } } -impl components::Component for DltRangeParser { - fn register(_components: &mut components::Components) -> Result<(), NativeError> { - Ok(()) - } -} +// impl components::Component for DltRangeParser { +// fn register(_components: &mut components::Components) -> Result<(), NativeError> { +// Ok(()) +// } +// } impl components::Component for DltRawParser { fn register(_components: &mut components::Components) -> Result<(), NativeError> { diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 71d24798de..b06d862d62 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -295,21 +295,27 @@ impl SingleParser for SomeipParser { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { - SomeipParser::parse_message(self.fibex_metadata.as_ref(), input, timestamp) - .map(|(rest, message)| (rest, Some(ParseYield::from(message)))) + ) -> Result<(usize, Option), ParserError> { + SomeipParser::parse_message(self.fibex_metadata.as_ref(), input, timestamp).map( + |(rest, message)| { + ( + rest, + Some(LogMessage::PlainText(message.to_string()).into()), + ) + }, + ) } } unsafe impl Send for SomeipParser {} unsafe impl Sync for SomeipParser {} -impl Parser for SomeipParser { +impl Parser for SomeipParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, ParserError> { + ) -> Result)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) @@ -512,12 +518,12 @@ impl SomeipLogMessage { } } -impl LogMessage for SomeipLogMessage { - fn to_writer(&self, writer: &mut W) -> Result { - writer.write_all(&self.bytes)?; - Ok(self.bytes.len()) - } -} +// impl LogMessage for SomeipLogMessage { +// fn to_writer(&self, writer: &mut W) -> Result { +// writer.write_all(&self.bytes)?; +// Ok(self.bytes.len()) +// } +// } impl Display for SomeipLogMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 945a1b9202..c87bcbcf71 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,7 +1,6 @@ mod options; use definitions::*; use serde::Serialize; -use std::{fmt, io::Write}; pub struct StringTokenizer {} @@ -10,19 +9,19 @@ pub struct StringMessage { content: String, } -impl fmt::Display for StringMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.content) - } -} +// impl fmt::Display for StringMessage { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// write!(f, "{}", self.content) +// } +// } -impl LogMessage for StringMessage { - fn to_writer(&self, writer: &mut W) -> Result { - let len = self.content.len(); - writer.write_all(self.content.as_bytes())?; - Ok(len) - } -} +// impl LogMessage for StringMessage { +// fn to_writer(&self, writer: &mut W) -> Result { +// let len = self.content.len(); +// writer.write_all(self.content.as_bytes())?; +// Ok(len) +// } +// } impl SingleParser for StringTokenizer where @@ -32,7 +31,7 @@ where &mut self, input: &[u8], _timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { + ) -> Result<(usize, Option), ParserError> { // TODO: support non-utf8 encodings use memchr::memchr; if input.is_empty() { @@ -40,16 +39,14 @@ where } let item = if let Some(msg_size) = memchr(b'\n', input) { let content = String::from_utf8_lossy(&input[..msg_size]); - let string_msg = StringMessage { - content: content.to_string(), - }; - (msg_size + 1, Some(string_msg.into())) + ( + msg_size + 1, + Some(LogMessage::PlainText(content.to_string()).into()), + ) } else { ( input.len(), - Some(ParseYield::from(StringMessage { - content: String::new(), - })), + Some(LogMessage::PlainText(String::new()).into()), ) }; @@ -57,15 +54,12 @@ where } } -impl Parser for StringTokenizer -where - StringMessage: LogMessage, -{ +impl Parser for StringTokenizer { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, ParserError> { + ) -> Result)>, ParserError> { parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { self.parse_item(input, timestamp) }) diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 7a59d89508..915972b0f7 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -15,9 +15,6 @@ use crate::{ pub mod plugin_parse_message; -/// Marker for a column separator in the output string. -pub const COLUMN_SEP: &str = "\u{0004}"; - /// Uses [`WasmHost`](crate::wasm_host::WasmHost) to communicate with WASM parser plugin. pub struct PluginsParser { /// The actual parser for each supported version in plugins API. @@ -138,12 +135,12 @@ impl PluginErrorLimits { } use definitions as defs; -impl defs::Parser for PluginsParser { +impl defs::Parser for PluginsParser { fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result>)>, defs::ParserError> { + ) -> Result)>, defs::ParserError> { let res = match &mut self.parser { PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), }; diff --git a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs index e1c3f1dfd1..74feec16e9 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs @@ -1,5 +1,3 @@ -use std::fmt::Display; - use definitions::LogMessage; use serde::Serialize; @@ -10,17 +8,8 @@ pub struct PluginParseMessage { pub content: String, } -impl Display for PluginParseMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.content) - } -} - -impl LogMessage for PluginParseMessage { - fn to_writer( - &self, - _writer: &mut W, - ) -> Result { - panic!("Parser plugins don't support export as binary"); +impl From for LogMessage { + fn from(msg: PluginParseMessage) -> Self { + LogMessage::PlainText(msg.content) } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index dc8cec3e1c..13bc784bbe 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -1,5 +1,3 @@ -use crate::{PluginParseMessage, parser_shared::COLUMN_SEP}; - pub use self::chipmunk::parser::parse_types::*; use stypes::ParserRenderOptions; @@ -43,7 +41,7 @@ impl From<&stypes::PluginParserGeneralSettings> for ParserConfig { use definitions as defs; -impl From for defs::ParseYield { +impl From for defs::ParseYield { fn from(yld: ParseYield) -> Self { match yld { ParseYield::Message(msg) => defs::ParseYield::Message(msg.into()), @@ -79,14 +77,12 @@ impl From for defs::ParserError { } } -impl From for PluginParseMessage { +impl From for defs::LogMessage { fn from(msg: ParsedMessage) -> Self { - let content = match msg { - ParsedMessage::Line(msg) => msg, - ParsedMessage::Columns(columns) => columns.join(COLUMN_SEP), - }; - - Self { content } + match msg { + ParsedMessage::Line(msg) => defs::LogMessage::PlainText(msg), + ParsedMessage::Columns(columns) => defs::LogMessage::Columns(columns), + } } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 94423f0249..3629464e97 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -132,7 +132,7 @@ impl PluginParser { } use definitions as defs; -impl defs::Parser for PluginParser { +impl defs::Parser for PluginParser { fn parse( &mut self, input: &[u8], diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs index b26db6d6db..eefccaa1b5 100644 --- a/application/apps/indexer/processor/src/export/mod.rs +++ b/application/apps/indexer/processor/src/export/mod.rs @@ -3,7 +3,7 @@ use std::{ path::Path, }; -use definitions::{ByteSource, LogMessage, MessageStreamItem, ParseYield, Parser}; +use definitions::{ByteSource, MessageStreamItem, ParseYield, Parser}; use indexer_base::config::IndexSection; use sources::producer::MessageProducer; use thiserror::Error; @@ -39,8 +39,8 @@ pub enum ExportError { /// /// # Errors /// In case of cancellation will return ExportError::Cancelled -pub async fn export_raw( - mut producer: MessageProducer, +pub async fn export_raw( + mut producer: MessageProducer, destination_path: &Path, sections: &Vec, read_to_end: bool, @@ -48,8 +48,7 @@ pub async fn export_raw( cancel: &CancellationToken, ) -> Result where - T: LogMessage + Sized, - P: Parser, + P: Parser, D: ByteSource, { trace!("export_raw, sections: {sections:?}"); diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/export_raw.rs index 91b83798f2..f19c4b2455 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/export_raw.rs @@ -1,5 +1,5 @@ use crate::{operations::OperationResult, state::SessionStateAPI}; -use definitions::{ByteSource, LogMessage, Parser}; +use definitions::{ByteSource, Parser}; use indexer_base::config::IndexSection; use log::debug; use parsers::{ @@ -173,8 +173,8 @@ async fn export( } } -pub async fn export_runner( - producer: MessageProducer, +pub async fn export_runner( + producer: MessageProducer, dest: &Path, sections: &Vec, read_to_end: bool, @@ -182,8 +182,7 @@ pub async fn export_runner( cancel: &CancellationToken, ) -> Result, stypes::NativeError> where - T: LogMessage + Sized, - P: Parser, + P: Parser, D: ByteSource, { export_raw(producer, dest, sections, read_to_end, text_file, cancel) diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 8a73c7ccd5..190d4e67dd 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -5,7 +5,7 @@ use crate::{ state::SessionStateAPI, tail, }; -use definitions::{ByteSource, LogMessage, MessageStreamItem, ParseYield, Parser}; +use definitions::{ByteSource, MessageStreamItem, ParseYield, Parser}; use log::trace; use parsers::{ LogMessage, MessageStreamItem, ParseYield, Parser, @@ -25,8 +25,8 @@ use tokio::{ time::{Duration, timeout}, }; -enum Next<'a, T: LogMessage> { - Items(&'a mut Vec<(usize, MessageStreamItem)>), +enum Next<'a> { + Items(&'a mut Vec<(usize, MessageStreamItem)>), Timeout, Waiting, Sde(SdeMsg), @@ -123,11 +123,11 @@ async fn run_source_intern( } } -async fn run_producer, S: ByteSource>( +async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, source_id: u16, - mut producer: MessageProducer, + mut producer: MessageProducer, mut rx_tail: Option>>, mut rx_sde: Option, ) -> OperationResult<()> { diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/sources/src/producer.rs index c363888308..dbad067f20 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/sources/src/producer.rs @@ -3,30 +3,27 @@ mod tests; use definitions::*; use log::warn; -use std::marker::PhantomData; /// Number of bytes to skip on initial parse errors before terminating the session. const INITIAL_PARSE_ERROR_LIMIT: usize = 1024; #[derive(Debug)] -pub struct MessageProducer +pub struct MessageProducer where - T: LogMessage, - P: Parser, + P: Parser, D: ByteSource, { byte_source: D, parser: P, filter: Option, last_seen_ts: Option, - _phantom_data: Option>, total_loaded: usize, total_skipped: usize, done: bool, - buffer: Vec<(usize, MessageStreamItem)>, + buffer: Vec<(usize, MessageStreamItem)>, } -impl, D: ByteSource> MessageProducer { +impl MessageProducer { /// create a new producer by plugging into a byte source pub fn new(parser: P, source: D) -> Self { MessageProducer { @@ -34,7 +31,6 @@ impl, D: ByteSource> MessageProducer { parser, filter: None, last_seen_ts: None, - _phantom_data: None, total_loaded: 0, total_skipped: 0, done: false, @@ -52,7 +48,7 @@ impl, D: ByteSource> MessageProducer { /// # Return: /// Return a mutable reference for the newly parsed items when there are more data available, /// otherwise it returns None when there are no more data available in the source. - pub async fn read_next_segment(&mut self) -> Option<&mut Vec<(usize, MessageStreamItem)>> { + pub async fn read_next_segment(&mut self) -> Option<&mut Vec<(usize, MessageStreamItem)>> { // ### Cancel Safety ###: // This function is cancel safe because: // * there is no await calls or any function causing yielding between filling the internal From e900940f99767c8fbe2ceda4b32e21ff636a8f3b Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 12 May 2025 13:14:09 +0200 Subject: [PATCH 031/121] Create parser/source from components --- .../apps/indexer/components/Cargo.toml | 1 - .../apps/indexer/components/src/lib.rs | 102 ++++++++++-- .../apps/indexer/components/src/tys.rs | 110 +++++++------ application/apps/indexer/parsers/Cargo.toml | 2 +- .../apps/indexer/parsers/src/dlt/mod.rs | 2 +- .../apps/indexer/parsers/src/dlt/options.rs | 47 ++---- application/apps/indexer/parsers/src/lib.rs | 29 ++++ .../apps/indexer/parsers/src/someip/mod.rs | 2 +- .../indexer/parsers/src/someip/options.rs | 23 +-- .../apps/indexer/parsers/src/text/mod.rs | 2 +- .../apps/indexer/parsers/src/text/options.rs | 23 +-- .../plugins_host/src/parser_shared/mod.rs | 11 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 11 +- .../indexer/session/src/components/mod.rs | 27 +--- .../indexer/session/src/handlers/observe.rs | 148 ++++++++++-------- .../session/src/handlers/observing/mod.rs | 2 +- .../apps/indexer/session/src/operations.rs | 21 ++- .../apps/indexer/session/src/session.rs | 18 ++- application/apps/indexer/sources/Cargo.toml | 3 +- .../indexer/sources/src/binary/pcap/legacy.rs | 22 +-- .../indexer/sources/src/binary/pcap/ng.rs | 23 +-- .../apps/indexer/sources/src/binary/raw.rs | 33 ++-- .../indexer/sources/src/command/process.rs | 25 +-- application/apps/indexer/sources/src/lib.rs | 110 +++++++++++++ .../indexer/sources/src/serial/options.rs | 25 +-- .../apps/indexer/sources/src/socket/tcp.rs | 25 +-- .../apps/indexer/sources/src/socket/udp.rs | 28 ++-- .../apps/indexer/stypes/src/observe/mod.rs | 7 +- 28 files changed, 564 insertions(+), 318 deletions(-) diff --git a/application/apps/indexer/components/Cargo.toml b/application/apps/indexer/components/Cargo.toml index ba7ba13946..bd1f854508 100644 --- a/application/apps/indexer/components/Cargo.toml +++ b/application/apps/indexer/components/Cargo.toml @@ -10,5 +10,4 @@ tokio-util.workspace = true tokio = { workspace = true , features = ["full"] } log.workspace = true -definitions = { path = "../definitions" } stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index ddabaf799c..96f6bbe1cb 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -1,20 +1,40 @@ mod scheme; mod tys; -use std::collections::HashMap; +use std::{collections::HashMap, fmt}; use tokio_util::sync::CancellationToken; use uuid::Uuid; pub use scheme::*; pub use tys::*; -#[derive(Default)] -pub struct Components { - components: HashMap>, +pub struct Components { + components: HashMap>, } -impl Components { - pub fn register( +impl Components { + pub fn new() -> Self { + Self { + components: HashMap::new(), + } + } + pub fn add_parser + 'static>( + &mut self, + descriptor: D, + ) -> Result<(), stypes::NativeError> { + let ident = descriptor.ident(); + if self.components.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.components + .insert(ident.uuid, Entry::Parser(Box::new(descriptor))); + Ok(()) + } + pub fn add_source + 'static>( &mut self, descriptor: D, ) -> Result<(), stypes::NativeError> { @@ -26,7 +46,8 @@ impl Components { message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.components.insert(ident.uuid, Box::new(descriptor)); + self.components + .insert(ident.uuid, Entry::Source(Box::new(descriptor))); Ok(()) } @@ -39,11 +60,28 @@ impl Components { Ok(self .components .iter() - .filter_map(|(_, desc)| { - if desc.is_ty(target) && desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None + .filter_map(|(_, desc)| match target { + stypes::ComponentType::Parser => { + if let Entry::Parser(desc) = desc { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + } else { + None + } + } + stypes::ComponentType::Source => { + if let Entry::Source(desc) = desc { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + } else { + None + } } }) .collect()) @@ -54,7 +92,7 @@ impl Components { origin: stypes::SourceOrigin, mut targets: Vec, ) -> Result, stypes::NativeError> { - let descriptors: Vec<&Box> = self + let descriptors: Vec<&Entry> = self .components .iter() .filter_map(|(uuid, desc)| { @@ -109,4 +147,42 @@ impl Components { })?; Ok(descriptor.validate(origin, fields)) } + + pub fn setup(&self, options: &stypes::SessionSetup) -> Result<(S, P), stypes::NativeError> { + let Some(Entry::Parser(parser)) = self.components.get(&options.parser.uuid) else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find parser {}", options.parser.uuid)), + }); + }; + let Some(Entry::Source(source)) = self.components.get(&options.source.uuid) else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find source {}", options.source.uuid)), + }); + }; + let Some(parser) = parser.create(&options.origin, &options.parser.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init parser {}", options.parser.uuid)), + }); + }; + let Some(source) = source.create(&options.origin, &options.source.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init source {}", options.source.uuid)), + }); + }; + Ok((source, parser)) + } +} + +impl fmt::Debug for Components { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Components") + } } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 5c11ba2bee..6557f8e0de 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -44,7 +44,6 @@ impl StaticFieldResult { /// * `Ok(Vec)` - A vector containing static field results. /// * `Err(stypes::NativeError)` - An error indicating a problem while retrieving static fields. pub type StaticFieldsResult = Result, stypes::NativeError>; - /// A type alias for an asynchronous task that yields a static fields result. /// /// Represents a future that will eventually produce a `StaticFieldsResult`. @@ -56,55 +55,76 @@ pub type StaticFieldsResult = Result, stypes::NativeError /// * `StaticFieldsResult` - A result containing static field data or a native error. pub type LazyFieldsTask = Pin + Send>>; -/// A trait responsible for registering a component within the system. -/// -/// Registration of a component is performed only once during the application's lifetime. -/// Implementors of this trait define the mechanism for adding their component to the system registry. -pub trait Component { - /// Registers the component in the system. - /// - /// This method is responsible for adding the component to the given collection of components. - /// It should be called only once per application runtime. - /// - /// # Arguments - /// - /// * `components` - A mutable reference to the system's component registry. - /// - /// # Returns - /// - /// * `Ok(())` if the registration was successful. - /// * `Err(stypes::NativeError)` if an error occurred during registration. - fn register(components: &mut Components) -> Result<(), stypes::NativeError>; +pub enum Entry { + Parser(Box>), + Source(Box>), } -/// A trait that defines the characteristics and behavior of a component. -/// -/// Each component (such as a parser, source, etc.) must specify its type and identifier. -/// Additional parameters are optional, meaning a component might have no settings at all -/// or only static settings. Similarly, the validation procedure for settings is optional. -/// -/// This trait provides a standardized way to describe a component, including its type, -/// identifier, field getters, lazy field loaders, and optional validation logic. -pub trait ComponentDescriptor { - fn to_source( - _inner: R, - _origin: SourceOrigin, - _options: Vec, - ) -> Result>, stypes::NativeError> - where - Self: Sized, - { - Ok(None) +impl MetadataDescriptor for Entry { + fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { + match self { + Self::Source(inner) => inner.fields_getter(origin), + Self::Parser(inner) => inner.fields_getter(origin), + } + } + + fn ident(&self) -> stypes::Ident { + match self { + Self::Source(inner) => inner.ident(), + Self::Parser(inner) => inner.ident(), + } + } + fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { + match self { + Self::Source(inner) => inner.is_compatible(origin), + Self::Parser(inner) => inner.is_compatible(origin), + } + } + fn is_ty(&self, ty: &stypes::ComponentType) -> bool { + match self { + Self::Source(inner) => inner.is_ty(ty), + Self::Parser(inner) => inner.is_ty(ty), + } } - fn to_parser( - _options: Vec, - _origin: SourceOrigin, - ) -> Result>, stypes::NativeError> - where - Self: Sized, - { + fn lazy_fields_getter( + &self, + origin: stypes::SourceOrigin, + cancel: CancellationToken, + ) -> LazyFieldsTask { + match self { + Self::Source(inner) => inner.lazy_fields_getter(origin, cancel), + Self::Parser(inner) => inner.lazy_fields_getter(origin, cancel), + } + } + fn ty(&self) -> stypes::ComponentType { + match self { + Self::Source(inner) => inner.ty(), + Self::Parser(inner) => inner.ty(), + } + } + fn validate( + &self, + origin: &stypes::SourceOrigin, + fields: &[stypes::Field], + ) -> HashMap { + match self { + Self::Source(inner) => inner.validate(origin, fields), + Self::Parser(inner) => inner.validate(origin, fields), + } + } +} + +pub trait ComponentDescriptor: MetadataDescriptor + Sync + Send { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { Ok(None) } +} + +pub trait MetadataDescriptor { /// Check is component is campatible with given origin /// /// * `origin` - The source origin diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index d0b8947df4..8c8dc59c6f 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -17,7 +17,6 @@ rand.workspace = true someip-messages = { git = "https://github.com/esrlabs/someip" } someip-payload = { git = "https://github.com/esrlabs/someip-payload" } uuid = { workspace = true , features = ["serde", "v4"] } -components = { path = "../components" } tokio-util.workspace = true tokio.workspace = true @@ -25,6 +24,7 @@ stypes = { path = "../stypes", features=["rustcore"] } definitions = { path = "../definitions" } file-tools = { path = "../addons/file-tools" } someip-tools = { path = "../addons/someip-tools" } +components = { path = "../components" } [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index c324d67cfb..18606cac87 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -1,6 +1,6 @@ pub mod attachment; pub mod fmt; -mod options; +pub mod options; use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; // use byteorder::{BigEndian, WriteBytesExt}; diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 06f99395ee..139446dbd9 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -1,5 +1,5 @@ use crate::dlt::*; -use components::{ComponentDescriptor, StaticFieldResult}; +use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; use dlt_core::{ read::DltMessageReader, statistics::{ @@ -26,7 +26,7 @@ const FIELD_STATISTICS: &str = "DLT_PARSER_FIELD_STATISTICS"; const FIELD_TZ: &str = "DLT_PARSER_FIELD_TIMEZONE"; #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} enum StatFields { AppIds, @@ -55,20 +55,19 @@ fn to_native_cfg_err(msg: S) -> NativeError { message: Some(msg.to_string()), } } -impl ComponentDescriptor for Descriptor { - fn to_parser( - options: Vec, - origin: SourceOrigin, - ) -> Result>, NativeError> - where - Self: Sized, - { + +impl ComponentDescriptor for Descriptor { + fn create( + &self, + origin: &SourceOrigin, + options: &[stypes::Field], + ) -> Result, stypes::NativeError> { let mut filter_config: Option = None; let mut someip_metadata: Option = None; let mut dlt_metadata: Option = None; for field in options.into_iter() { if field.id == FIELD_FIBEX_FILES { - let Value::Files(paths) = field.value else { + let Value::Files(paths) = &field.value else { return Err(to_native_cfg_err("Invalid settings for Fibex files paths")); }; someip_metadata = if paths.is_empty() { @@ -88,16 +87,17 @@ impl ComponentDescriptor for Descriptor { }; } } - let parser = Box::new(DltParser::new( + Ok(Some(crate::Parser::Dlt(DltParser::new( None, dlt_metadata, None, someip_metadata, // If it's source - no storage header expected !matches!(origin, SourceOrigin::Source), - )); - Ok(None) + )))) } +} +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -285,22 +285,3 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } - -impl components::Component for DltParser { - fn register(components: &mut components::Components) -> Result<(), NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - -// impl components::Component for DltRangeParser { -// fn register(_components: &mut components::Components) -> Result<(), NativeError> { -// Ok(()) -// } -// } - -impl components::Component for DltRawParser { - fn register(_components: &mut components::Components) -> Result<(), NativeError> { - Ok(()) - } -} diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index e9be47fcda..ee6abe84ac 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,5 +1,34 @@ #![deny(unused_crate_dependencies)] + +use components::Components; pub mod dlt; pub mod prelude; pub mod someip; pub mod text; + +pub enum Parser { + Dlt(dlt::DltParser), + SomeIp(someip::SomeipParser), + Text(text::StringTokenizer), +} + +impl definitions::Parser for Parser { + fn parse( + &mut self, + input: &[u8], + timestamp: Option, + ) -> Result)>, definitions::ParserError> { + match self { + Self::Dlt(inst) => inst.parse(input, timestamp), + Self::SomeIp(inst) => inst.parse(input, timestamp), + Self::Text(inst) => inst.parse(input, timestamp), + } + } +} + +pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { + components.add_parser(dlt::options::Descriptor::default())?; + components.add_parser(someip::options::Descriptor::default())?; + components.add_parser(text::options::Descriptor::default())?; + Ok(()) +} diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index b06d862d62..bdde380bbe 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -1,4 +1,4 @@ -mod options; +pub mod options; use definitions::*; use std::{ borrow::Cow, diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index be990a1020..ac52c435de 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use super::SomeipParser; -use components::{ComponentDescriptor, StaticFieldResult}; +use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; use stypes::SourceOrigin; use tokio::{ select, @@ -19,9 +19,19 @@ const FIELD_LOG_LEVEL: &str = "log_level"; const FIELD_STATISTICS: &str = "statistics"; #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -89,10 +99,3 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } - -impl components::Component for SomeipParser { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index c87bcbcf71..43d70586e7 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,4 +1,4 @@ -mod options; +pub mod options; use definitions::*; use serde::Serialize; diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/options.rs index a9166911d3..1b7e4e9fef 100644 --- a/application/apps/indexer/parsers/src/text/options.rs +++ b/application/apps/indexer/parsers/src/text/options.rs @@ -1,4 +1,4 @@ -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use file_tools::is_binary; use stypes::SourceOrigin; @@ -9,9 +9,19 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -37,10 +47,3 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } - -impl components::Component for StringTokenizer { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 915972b0f7..f0c00c31c9 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use stypes::{PluginInfo, SemanticVersion}; use wasmtime::component::Component; @@ -181,7 +181,7 @@ impl defs::Parser for PluginsParser { #[derive(Default)] struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { true } @@ -197,10 +197,3 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } - -impl components::Component for PluginsParser { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 3629464e97..a007a5313e 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -4,7 +4,7 @@ mod bindings; mod parser_plugin_state; -use components::ComponentDescriptor; +use components::MetadataDescriptor; use stypes::{ParserRenderOptions, RenderOptions, SemanticVersion}; use tokio::runtime::Handle; use wasmtime::{ @@ -174,7 +174,7 @@ impl defs::Parser for PluginParser { #[derive(Default)] struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { true } @@ -190,10 +190,3 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } - -impl components::Component for PluginParser { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index fd45943c8e..49c8322ed5 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -1,13 +1,11 @@ mod api; -use std::collections::HashMap; +use std::{collections::HashMap, io::Read}; use api::*; -use components::{Component, Components, LazyLoadingResult, LazyLoadingTaskMeta}; +use components::{Components, LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; -use parsers::prelude as parsers; -use sources::prelude as sources; use tokio::{ sync::{ mpsc::{error::SendError, unbounded_channel, UnboundedReceiver, UnboundedSender}, @@ -55,21 +53,12 @@ impl ComponentsSession { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components = Components::default(); - { - parsers::DltParser::register(&mut components)?; - parsers::SomeipParser::register(&mut components)?; - parsers::StringTokenizer::register(&mut components)?; - } - { - sources::BinaryByteSource::::register(&mut components)?; - sources::PcapLegacyByteSource::::register(&mut components)?; - sources::PcapngByteSource::::register(&mut components)?; - sources::TcpSource::register(&mut components)?; - sources::UdpSource::register(&mut components)?; - sources::SerialSource::register(&mut components)?; - sources::ProcessSource::register(&mut components)?; - } + let mut components: Components, parsers::Parser> = + Components::new(); + // Registre parsers + parsers::registration(&mut components)?; + // Registre sources + sources::registration(&mut components)?; let tx_api_inner = tx_api.clone(); let session = Self { tx_api }; task::spawn(async move { diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 863183399b..32ef81666e 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -1,82 +1,92 @@ +use std::sync::Arc; + use crate::{ handlers::observing, operations::{OperationAPI, OperationResult}, state::SessionStateAPI, }; +use components::Components; use log::error; -use sources::sde::SdeReceiver; +use sources::{producer::MessageProducer, sde::SdeReceiver}; + +use super::observing::run_producer; pub async fn start_observing( operation_api: OperationAPI, state: SessionStateAPI, - mut options: stypes::ObserveOptions, + options: stypes::SessionSetup, + components: Arc, parsers::Parser>>, rx_sde: Option, ) -> OperationResult<()> { - if let stypes::ParserType::Dlt(ref mut settings) = options.parser { - settings.load_fibex_metadata(); - }; - if let Err(err) = state.add_executed_observe(options.clone()).await { - error!("Fail to store observe options: {err:?}"); - } - match &options.origin { - stypes::ObserveOrigin::File(uuid, file_origin, filename) => { - let (is_text, session_file_origin) = ( - matches!(options.parser, stypes::ParserType::Text(())), - state.get_session_file_origin().await?, - ); - match session_file_origin { - Some(origin) if origin.is_linked() => Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(String::from( - "Cannot observe file, because session is linked to other text file", - )), - }), - Some(origin) if !origin.is_linked() && is_text => { - // Session file was created and some files/streams were opened already. We should check for text files - // to prevent attempt to link session with text file. Using concat instead - observing::concat::concat_files( - operation_api, - state, - &[(uuid.clone(), file_origin.clone(), filename.clone())], - &options.parser, - ) - .await - } - _ => { - observing::file::observe_file( - operation_api, - state, - uuid, - file_origin, - filename, - &options.parser, - ) - .await - } - } - } - stypes::ObserveOrigin::Concat(files) => { - if files.is_empty() { - Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(String::from("No files are defined for Concat operation")), - }) - } else { - observing::concat::concat_files(operation_api, state, files, &options.parser).await - } - } - stypes::ObserveOrigin::Stream(uuid, transport) => { - observing::stream::observe_stream( - operation_api, - state, - uuid, - transport, - &options.parser, - rx_sde, - ) - .await - } - } + let (source, parser) = components.setup(&options)?; + // let source_id = state.add_source(uuid).await?; + let producer = MessageProducer::new(parser, source); + run_producer(operation_api, state, 0, producer, None, rx_sde).await + // if let stypes::ParserType::Dlt(ref mut settings) = options.parser { + // settings.load_fibex_metadata(); + // }; + // if let Err(err) = state.add_executed_observe(options.clone()).await { + // error!("Fail to store observe options: {:?}", err); + // } + // match &options.origin { + // stypes::ObserveOrigin::File(uuid, file_origin, filename) => { + // let (is_text, session_file_origin) = ( + // matches!(options.parser, stypes::ParserType::Text(())), + // state.get_session_file_origin().await?, + // ); + // match session_file_origin { + // Some(origin) if origin.is_linked() => Err(stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Configuration, + // message: Some(String::from( + // "Cannot observe file, because session is linked to other text file", + // )), + // }), + // Some(origin) if !origin.is_linked() && is_text => { + // // Session file was created and some files/streams were opened already. We should check for text files + // // to prevent attempt to link session with text file. Using concat instead + // observing::concat::concat_files( + // operation_api, + // state, + // &[(uuid.clone(), file_origin.clone(), filename.clone())], + // &options.parser, + // ) + // .await + // } + // _ => { + // observing::file::observe_file( + // operation_api, + // state, + // uuid, + // file_origin, + // filename, + // &options.parser, + // ) + // .await + // } + // } + // } + // stypes::ObserveOrigin::Concat(files) => { + // if files.is_empty() { + // Err(stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Configuration, + // message: Some(String::from("No files are defined for Concat operation")), + // }) + // } else { + // observing::concat::concat_files(operation_api, state, files, &options.parser).await + // } + // } + // stypes::ObserveOrigin::Stream(uuid, transport) => { + // observing::stream::observe_stream( + // operation_api, + // state, + // uuid, + // transport, + // &options.parser, + // rx_sde, + // ) + // .await + // } + // } } diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 190d4e67dd..584ba75954 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -123,7 +123,7 @@ async fn run_source_intern( } } -async fn run_producer( +pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, source_id: u16, diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index b4ecfb4de4..57497ec663 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -1,4 +1,5 @@ use crate::{handlers, state::SessionStateAPI, tracker::OperationTrackerAPI}; +use components::Components; use log::{debug, error, warn}; use merging::merger::FileMergeOptions; use processor::search::filter::SearchFilter; @@ -7,6 +8,7 @@ use sources::sde::{SdeReceiver, SdeSender}; use std::{ ops::RangeInclusive, path::PathBuf, + sync::Arc, time::{SystemTime, UNIX_EPOCH}, }; @@ -72,7 +74,10 @@ impl Operation { #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum OperationKind { - Observe(stypes::ObserveOptions), + Observe( + stypes::SessionSetup, + Arc, parsers::Parser>>, + ), Search { filters: Vec, }, @@ -140,7 +145,7 @@ impl std::fmt::Display for OperationKind { f, "{}", match self { - OperationKind::Observe(_) => "Observing", + OperationKind::Observe(..) => "Observing", OperationKind::Search { .. } => "Searching", OperationKind::SearchValues { .. } => "Searching values", OperationKind::Export { .. } => "Exporting", @@ -305,10 +310,16 @@ impl OperationAPI { api.started(); let operation_str = &format!("{}", operation.kind); match operation.kind { - OperationKind::Observe(options) => { + OperationKind::Observe(options, components) => { api.finish( - handlers::observe::start_observing(api.clone(), state, options, rx_sde) - .await, + handlers::observe::start_observing( + api.clone(), + state, + options, + components, + rx_sde, + ) + .await, operation_str, ) .await; diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index 1e14be4437..f0fc229c0b 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -6,10 +6,11 @@ use crate::{ tracker, tracker::OperationTrackerAPI, }; +use components::Components; use futures::Future; use log::{debug, error, warn}; use processor::{grabber::LineRange, search::filter::SearchFilter}; -use std::{ops::RangeInclusive, path::PathBuf}; +use std::{ops::RangeInclusive, path::PathBuf, sync::Arc}; use tokio::{ join, sync::{ @@ -31,6 +32,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, + components: Arc, parsers::Parser>>, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -54,11 +56,21 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); + let mut components: Components, parsers::Parser> = + Components::new(); + // Registre parsers + parsers::registration(&mut components) + .map_err(|err| stypes::ComputationError::NativeError(err))?; + // Registre sources + sources::registration(&mut components) + .map_err(|err| stypes::ComputationError::NativeError(err))?; + // Create a session let session = Self { uuid, tx_operations: tx_operations.clone(), destroyed: CancellationToken::new(), destroying: CancellationToken::new(), + components: Arc::new(components), state: state_api.clone(), tracker: tracker_api.clone(), }; @@ -376,12 +388,12 @@ impl Session { pub fn observe( &self, operation_id: Uuid, - options: stypes::ObserveOptions, + options: stypes::SessionSetup, ) -> Result<(), stypes::ComputationError> { self.tx_operations .send(Operation::new( operation_id, - operations::OperationKind::Observe(options), + operations::OperationKind::Observe(options, self.components.clone()), )) .map_err(|e| stypes::ComputationError::Communication(e.to_string())) } diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 051c83922e..2ed101a69f 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -23,10 +23,11 @@ serde = { workspace = true, features = ["derive"] } uuid = { workspace = true , features = ["serde", "v4"] } regex.workspace = true lazy_static.workspace = true -async-trait.workspace = true shellexpand = "3.1" socket2 = "0.5.8" serialport.workspace = true +async-trait.workspace = true + file-tools = { path = "../addons/file-tools" } stypes = { path = "../stypes", features=["rustcore"] } components = { path = "../components" } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index deb683ca48..5875b9a816 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -1,7 +1,7 @@ use crate::{binary::pcap::debug_block}; use definitions::*; use bufread::DeqBuffer; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; @@ -163,9 +163,19 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -201,12 +211,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for PcapLegacyByteSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} #[cfg(test)] mod tests { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index c6613a155f..53e778cb87 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -1,7 +1,7 @@ use crate::{binary::pcap::debug_block}; use definitions::*; use bufread::DeqBuffer; -use components::ComponentDescriptor; +use components::{ComponentDescriptor,MetadataDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; @@ -167,9 +167,19 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -205,13 +215,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for PcapngByteSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 14d041bef8..412ce8e899 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use bufread::BufReader; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; use file_tools::is_binary; use std::io::{BufRead, Read}; @@ -83,19 +83,19 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} - -impl ComponentDescriptor for Descriptor { - fn to_source( - inner: R, - _origin: SourceOrigin, - _options: Vec, - ) -> Result>, stypes::NativeError> - where - Self: Sized, - { - Ok(Some(Box::new(BinaryByteSource::new(inner)))) +pub struct Descriptor {} + +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { @@ -124,13 +124,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for BinaryByteSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 19941cedaa..4367c2d0f6 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,10 +1,10 @@ use async_trait::async_trait; use bufread::DeqBuffer; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; use regex::{Captures, Regex}; use shellexpand::tilde; -use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; +use std::{collections::HashMap, ffi::OsString, io::Read, path::PathBuf, process::Stdio}; use stypes::SourceOrigin; use thiserror::Error; use tokio::{ @@ -235,9 +235,19 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) @@ -259,13 +269,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for ProcessSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index bd74c83db8..3f08c8b212 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,3 +1,9 @@ +use std::io::Read; + +use async_trait::async_trait; +use components::Components; +use definitions::{ReloadInfo, SourceError, SourceFilter}; + // Rust can't currently distinguish between dev and none-dev dependencies at the moment. There is // an open issue for this case: "https://github.com/rust-lang/rust/issues/129637" #[macro_use] @@ -16,3 +22,107 @@ pub mod producer; pub mod sde; pub mod serial; pub mod socket; + +pub enum Source { + Raw(binary::raw::BinaryByteSource), + Pcap(binary::pcap::legacy::PcapLegacyByteSource), + PcapNg(binary::pcap::ng::PcapngByteSource), + Tcp(socket::tcp::TcpSource), + Udp(socket::udp::UdpSource), + Serial(serial::serialport::SerialSource), + Process(command::process::ProcessSource), +} + +#[async_trait] +impl definitions::ByteSource for Source { + fn consume(&mut self, offset: usize) { + match self { + Self::Raw(inner) => inner.consume(offset), + Self::Pcap(inner) => inner.consume(offset), + Self::PcapNg(inner) => inner.consume(offset), + Self::Tcp(inner) => inner.consume(offset), + Self::Udp(inner) => inner.consume(offset), + Self::Serial(inner) => inner.consume(offset), + Self::Process(inner) => inner.consume(offset), + } + } + + fn current_slice(&self) -> &[u8] { + match self { + Self::Raw(inner) => inner.current_slice(), + Self::Pcap(inner) => inner.current_slice(), + Self::PcapNg(inner) => inner.current_slice(), + Self::Tcp(inner) => inner.current_slice(), + Self::Udp(inner) => inner.current_slice(), + Self::Serial(inner) => inner.current_slice(), + Self::Process(inner) => inner.current_slice(), + } + } + + fn len(&self) -> usize { + match self { + Self::Raw(inner) => inner.len(), + Self::Pcap(inner) => inner.len(), + Self::PcapNg(inner) => inner.len(), + Self::Tcp(inner) => inner.len(), + Self::Udp(inner) => inner.len(), + Self::Serial(inner) => inner.len(), + Self::Process(inner) => inner.len(), + } + } + + async fn load( + &mut self, + filter: Option<&SourceFilter>, + ) -> Result, SourceError> { + match self { + Self::Raw(inner) => inner.load(filter).await, + Self::Pcap(inner) => inner.load(filter).await, + Self::PcapNg(inner) => inner.load(filter).await, + Self::Tcp(inner) => inner.load(filter).await, + Self::Udp(inner) => inner.load(filter).await, + Self::Serial(inner) => inner.load(filter).await, + Self::Process(inner) => inner.load(filter).await, + } + } + + async fn cancel(&mut self) -> Result<(), SourceError> { + match self { + Self::Raw(inner) => inner.cancel().await, + Self::Pcap(inner) => inner.cancel().await, + Self::PcapNg(inner) => inner.cancel().await, + Self::Tcp(inner) => inner.cancel().await, + Self::Udp(inner) => inner.cancel().await, + Self::Serial(inner) => inner.cancel().await, + Self::Process(inner) => inner.cancel().await, + } + } + + async fn income( + &mut self, + msg: stypes::SdeRequest, + ) -> Result { + match self { + Self::Raw(inner) => inner.income(msg).await, + Self::Pcap(inner) => inner.income(msg).await, + Self::PcapNg(inner) => inner.income(msg).await, + Self::Tcp(inner) => inner.income(msg).await, + Self::Udp(inner) => inner.income(msg).await, + Self::Serial(inner) => inner.income(msg).await, + Self::Process(inner) => inner.income(msg).await, + } + } +} + +pub fn registration( + components: &mut Components, P>, +) -> Result<(), stypes::NativeError> { + components.add_source(binary::raw::Descriptor::default())?; + components.add_source(binary::pcap::legacy::Descriptor::default())?; + components.add_source(binary::pcap::ng::Descriptor::default())?; + components.add_source(socket::tcp::Descriptor::default())?; + components.add_source(socket::udp::Descriptor::default())?; + components.add_source(serial::options::Descriptor::default())?; + components.add_source(command::process::Descriptor::default())?; + Ok(()) +} diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index a3b757677a..0d63938fc7 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -1,6 +1,6 @@ use crate::serial::serialport::SerialSource; -use components::{ComponentDescriptor, StaticFieldResult}; -use std::{collections::HashMap, str}; +use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; +use std::{collections::HashMap, io::Read, str}; use stypes::{FieldDesc, LazyFieldDesc, SourceOrigin, StaticFieldDesc, Value, ValueInput}; const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -27,9 +27,19 @@ const FIELD_EXCLUSIVE: &str = "SERIAL_SOURCE_FIELD_EXCLUSIVE"; const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) @@ -186,10 +196,3 @@ impl ComponentDescriptor for Descriptor { errors } } - -impl components::Component for SerialSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index b2fa55a912..c6afceabe5 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,10 +1,10 @@ use async_trait::async_trait; use bufread::DeqBuffer; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; -use std::{net::SocketAddr, time::Duration}; +use std::{io::Read, net::SocketAddr, time::Duration}; use stypes::SourceOrigin; use tokio::net::TcpStream; @@ -176,9 +176,19 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} + +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} -impl ComponentDescriptor for Descriptor { +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) @@ -200,13 +210,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for TcpSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index dc8a468451..4462fa0b3e 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -2,10 +2,13 @@ use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; use async_trait::async_trait; use bufread::DeqBuffer; -use components::ComponentDescriptor; +use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; use log::trace; -use std::net::{IpAddr, Ipv4Addr}; +use std::{ + io::Read, + net::{IpAddr, Ipv4Addr}, +}; use stypes::SourceOrigin; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -136,9 +139,19 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); #[derive(Default)] -struct Descriptor {} +pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentDescriptor> for Descriptor { + fn create( + &self, + _origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result>, stypes::NativeError> { + Ok(None) + } +} + +impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) @@ -160,13 +173,6 @@ impl ComponentDescriptor for Descriptor { } } -impl components::Component for UdpSource { - fn register(components: &mut components::Components) -> Result<(), stypes::NativeError> { - components.register(Descriptor::default())?; - Ok(()) - } -} - #[cfg(test)] mod tests { use std::time::Duration; diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 60e07e9e55..51cb0f9f9b 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -47,9 +47,10 @@ pub enum ComponentDef { derive(TS), ts(export, export_to = "observe.ts") )] -struct SessionSetup { - origin: SourceOrigin, - components: Vec, +pub struct SessionSetup { + pub origin: SourceOrigin, + pub parser: ComponentOptions, + pub source: ComponentOptions, } #[derive(Debug, Serialize, Deserialize, Clone)] From 2b6a8cbf733a5677499802bfb936d6954611649a Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 12 May 2025 13:29:06 +0200 Subject: [PATCH 032/121] Remove async-trait to use allow(async_fn_in_trait) --- application/apps/indexer/Cargo.toml | 1 - .../apps/indexer/definitions/Cargo.toml | 1 - .../apps/indexer/definitions/src/source.rs | 73 +------------------ application/apps/indexer/sources/Cargo.toml | 1 - .../indexer/sources/src/binary/pcap/legacy.rs | 2 - .../indexer/sources/src/binary/pcap/ng.rs | 2 - .../apps/indexer/sources/src/binary/raw.rs | 2 - .../indexer/sources/src/command/process.rs | 2 - application/apps/indexer/sources/src/lib.rs | 2 - .../indexer/sources/src/serial/options.rs | 1 - .../indexer/sources/src/serial/serialport.rs | 2 - .../apps/indexer/sources/src/socket/tcp.rs | 2 - .../apps/indexer/sources/src/socket/udp.rs | 2 - application/apps/indexer/stypes/Cargo.toml | 2 +- 14 files changed, 2 insertions(+), 93 deletions(-) diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index 5cd2fc78d0..ea82b3c31d 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -46,7 +46,6 @@ toml = "0.8" blake3 = "1.8" serialport = "4.6" etherparse = "0.18" -async-trait = "0.1" ## Development Dependencies ## # Support for `html_reports` needs running the benchmarks via `cargo-criterion` tool. diff --git a/application/apps/indexer/definitions/Cargo.toml b/application/apps/indexer/definitions/Cargo.toml index 639dd592ad..88035096fe 100644 --- a/application/apps/indexer/definitions/Cargo.toml +++ b/application/apps/indexer/definitions/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" thiserror.workspace = true etherparse.workspace = true serde = { workspace = true , features = ["derive"] } -async-trait.workspace = true stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/definitions/src/source.rs b/application/apps/indexer/definitions/src/source.rs index 5cbb432c9c..a140b10c1d 100644 --- a/application/apps/indexer/definitions/src/source.rs +++ b/application/apps/indexer/definitions/src/source.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use thiserror::Error; #[derive(Debug, Clone, PartialEq, Eq)] @@ -75,76 +74,6 @@ pub const DEFAULT_MIN_BUFFER_SPACE: usize = 10 * 1024; /// Requirements to `ByteSource` inner reader pub trait InnerReader: std::io::Read + Send + Unpin + 'static {} -/// **Important NOTE** -/// -/// ### Restoring the Original Asynchronous Trait Design with `async_trait` -/// -/// Initially, we used the standard approach for implementing asynchronous methods in traits via `Pin>>`, which is a well-established and widely supported pattern in Rust. Later, the API was adapted to leverage the new experimental directive `#[allow(async_fn_in_trait)], which allows direct use of asynchronous methods in traits. -/// -/// However, it is important to note that Rust developers intentionally did not enable this feature by default, and it requires explicit activation for several critical reasons: -/// -/// 1. Partial Stabilization: -/// -/// * This feature is only partially stabilized, meaning it is still subject to significant changes in future releases. -/// * Relying on partially stabilized functionality in production code can lead to unexpected maintenance costs and compatibility issues. -/// -/// 2. Potential Breaking Changes: -/// -/// * Given the ongoing discussions within the Rust community, this feature may undergo significant modifications, potentially breaking backward compatibility. -/// -/// 3. Loss of Flexibility (Critical Issue): -/// -/// * Using `#[allow(async_fn_in_trait)]` completely blocks the ability to use `Box`, leading to the infamous compilation error: -/// -/// ```text -/// the trait is not dyn compatible because method `XXX` is `async` -/// error[E0038]: the trait `YYY` is not dyn compatible -/// ``` -/// -/// * This restriction completely eliminates the possibility of packing `ByteSource` instances into trait objects, which is a critical design flaw, as it significantly reduces architectural flexibility. -/// * Without this flexibility, it becomes impossible to extend the system with additional `ByteSource` and `Parser` implementations without direct modification of the core codebase. -/// -/// --- -/// -/// ### Why Boxing is Not a Real Problem -/// -/// The practice of boxing a `ByteSource` instance in `Box` is not inherently problematic, for several reasons: -/// -/// * Efficient Use in Hot Loops: -/// -/// * Once the boxed instance is unpacked at the level of the `MessageProducer`, the cost of indirection is eliminated, allowing direct access to the underlying object. -/// -/// * Negligible Allocation Overhead: -/// -/// * The cost of allocating a boxed instance is a one-time event, occurring only during the initialization of a session. -/// * Given that session objects are long-lived and only created once per session, this overhead is negligible compared to the benefits of a flexible architecture. -/// -/// --- -/// -/// ### Restoring Flexibility and Safety with `async_trait` -/// -/// Given these considerations, we have decided to revert to the "classic" and well-established approach of using `async_trait`, which wraps asynchronous methods in `Pin>>`. While this approach has a slightly higher runtime cost, it offers several critical advantages: -/// -/// 1. Technical Correctness and Safety: -/// -/// * We are aligning with the only officially supported and widely accepted Rust practice for asynchronous traits, which is both safe and fully optimized. -/// -/// 2. Avoiding Experimental Features: -/// -/// * We are removing the dependency on partially stabilized and potentially breaking features, reducing the risk of future maintenance headaches. -/// -/// 3. Preserving Full Architectural Flexibility: -/// -/// * This approach completely preserves the ability to separate `ByteSource` and `Parser` implementations from the core, making the architecture more modular and extensible. -/// * It also simplifies unit testing, as components can be developed, tested, and deployed independently of the core logic. -/// -/// 4. Realistic Performance Considerations: -/// -/// * We acknowledge a potential minor performance impact, but this is insignificant in real-world use cases. -/// * In a typical session, the initial "frame" of data is presented to the user immediately, while the full file is loaded in the background, making a 100-500ms delay on a 1GB file completely acceptable in terms of UI/UX. -/// -/// --- -/// /// In summary, this decision prioritizes architectural flexibility, reduces long-term maintenance risks, and ensures code stability, making the overall system more sustainable and extensible in the long run. /// /// A `ByteSource` provides a way to read data from some underlying data source. But it does @@ -154,7 +83,7 @@ pub trait InnerReader: std::io::Read + Send + Unpin + 'static {} /// want to extract the data part from certain frames, the `relaod` method will load only the relevant /// data into an internal buffer. /// This data can then be accessed via the `current_slice` method. -#[async_trait] +#[allow(async_fn_in_trait)] pub trait ByteSource: Send { /// Indicate that we have consumed a certain amount of data from our internal /// buffer and that this part can be discarded diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 2ed101a69f..dc1f4f883a 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -26,7 +26,6 @@ lazy_static.workspace = true shellexpand = "3.1" socket2 = "0.5.8" serialport.workspace = true -async-trait.workspace = true file-tools = { path = "../addons/file-tools" } stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 5875b9a816..b0a0067919 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -8,7 +8,6 @@ use log::{debug, error, trace}; use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; use std::io::Read; use stypes::SourceOrigin; -use async_trait::async_trait; pub struct PcapLegacyByteSource { pcap_reader: LegacyPcapReader, @@ -29,7 +28,6 @@ impl PcapLegacyByteSource { } } -#[async_trait] impl ByteSource for PcapLegacyByteSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index 53e778cb87..a24123eabe 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -8,7 +8,6 @@ use log::{debug, error, trace}; use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; use std::io::Read; use stypes::SourceOrigin; -use async_trait::async_trait; pub struct PcapngByteSource { pcapng_reader: PcapNGReader, @@ -29,7 +28,6 @@ impl PcapngByteSource { } } -#[async_trait] impl ByteSource for PcapngByteSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 412ce8e899..e56a059e5e 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use bufread::BufReader; use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; @@ -32,7 +31,6 @@ where } } -#[async_trait] impl ByteSource for BinaryByteSource { async fn load(&mut self, _: Option<&SourceFilter>) -> Result, SourceError> { let initial_buf_len = self.reader.len(); diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 4367c2d0f6..76b92e5c22 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use bufread::DeqBuffer; use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; @@ -158,7 +157,6 @@ impl ProcessSource { } } -#[async_trait] impl ByteSource for ProcessSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 3f08c8b212..f23289890a 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,6 +1,5 @@ use std::io::Read; -use async_trait::async_trait; use components::Components; use definitions::{ReloadInfo, SourceError, SourceFilter}; @@ -33,7 +32,6 @@ pub enum Source { Process(command::process::ProcessSource), } -#[async_trait] impl definitions::ByteSource for Source { fn consume(&mut self, offset: usize) { match self { diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 0d63938fc7..538c40584a 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -1,4 +1,3 @@ -use crate::serial::serialport::SerialSource; use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; use std::{collections::HashMap, io::Read, str}; use stypes::{FieldDesc, LazyFieldDesc, SourceOrigin, StaticFieldDesc, Value, ValueInput}; diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index af8dd77e03..ee50bb2ffd 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; use definitions::*; @@ -126,7 +125,6 @@ impl SerialSource { } } -#[async_trait] impl ByteSource for SerialSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index c6afceabe5..d428d1af8f 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use bufread::DeqBuffer; use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; @@ -70,7 +69,6 @@ impl TcpSource { } } -#[async_trait] impl ByteSource for TcpSource { async fn load( &mut self, diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 4462fa0b3e..afc15027c1 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -1,6 +1,5 @@ use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; -use async_trait::async_trait; use bufread::DeqBuffer; use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; @@ -78,7 +77,6 @@ impl UdpSource { } } -#[async_trait] impl ByteSource for UdpSource { async fn load( &mut self, diff --git a/application/apps/indexer/stypes/Cargo.toml b/application/apps/indexer/stypes/Cargo.toml index 7f761781ff..47314559bd 100644 --- a/application/apps/indexer/stypes/Cargo.toml +++ b/application/apps/indexer/stypes/Cargo.toml @@ -27,7 +27,7 @@ dlt-core = { workspace = true, features = ["fibex", "serialization"] } regex = { workspace = true, optional = true } bincode = "1.3" extend = { path = "../tools/extend"} -uuid = { workspace = true, features = ["serde"] } +uuid = { workspace = true, features = ["serde", "v4"] } tokio = { workspace = true, optional = true } node-bindgen = { git = "https://github.com/infinyon/node-bindgen.git", branch="master", optional = true} thiserror.workspace = true From 756bb4c77363627c6e769d8e8e363dfa7e3f2735 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 13 May 2025 22:49:07 +0200 Subject: [PATCH 033/121] Add test drive runner (testing updated interfaces) --- .../apps/indexer/definitions/src/parser.rs | 22 +++ .../apps/indexer/parsers/src/dlt/options.rs | 4 +- application/apps/indexer/parsers/src/lib.rs | 6 + .../indexer/session/src/handlers/observe.rs | 2 + .../indexer/sources/src/binary/pcap/legacy.rs | 8 +- .../apps/indexer/sources/src/binary/raw.rs | 64 ++++++++- application/apps/indexer/sources/src/lib.rs | 2 +- .../apps/indexer/sources/src/producer.rs | 2 + application/apps/indexer/stypes/Cargo.toml | 2 +- .../apps/indexer/stypes/bindings/observe.ts | 2 +- application/apps/list/Cargo.lock | 129 ++++++++++++++++++ application/apps/list/Cargo.toml | 8 ++ application/apps/list/src/main.rs | 10 ++ application/apps/protocol/src/lib.rs | 1 + .../apps/rustcore/rs-bindings/Cargo.lock | 26 +++- .../rs-bindings/src/js/session/mod.rs | 2 +- .../session.stream.observe.executor.ts | 14 +- .../ts-bindings/src/api/session.stream.ts | 4 +- .../apps/rustcore/ts-bindings/src/index.ts | 2 + .../ts-bindings/src/native/native.session.ts | 8 +- .../client/src/app/schema/render/tools.ts | 5 +- application/client/src/app/service/session.ts | 40 +++++- .../dependencies/observing/provider.ts | 6 +- .../service/session/dependencies/stream.ts | 79 +++++------ .../client/src/app/service/session/origin.ts | 44 +++++- .../client/src/app/ui/tabs/setup/component.ts | 23 +++- .../src/app/ui/tabs/setup/template.html | 4 +- .../sidebar/observe/lists/file/component.ts | 59 ++++---- .../holder/src/service/sessions/holder.ts | 114 ++++++++-------- .../sessions/requests/observe/start.ts | 2 +- .../platform/ipc/event/observe/finished.ts | 7 +- .../platform/ipc/event/observe/started.ts | 7 +- .../platform/ipc/request/observe/start.ts | 12 +- application/platform/package.json | 1 + .../platform/types/bindings/observe.ts | 9 ++ application/platform/types/index.ts | 1 + .../platform/types/sessionsetup/index.ts | 14 ++ cli/chipmunk-cli/Cargo.lock | 34 ++--- 38 files changed, 587 insertions(+), 192 deletions(-) create mode 100644 application/apps/list/Cargo.lock create mode 100644 application/apps/list/Cargo.toml create mode 100644 application/apps/list/src/main.rs create mode 100644 application/platform/types/sessionsetup/index.ts diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index ed752de40a..86b7ce44bd 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -47,6 +47,16 @@ pub trait Parser { ) -> Result)>, ParserError>; } +/** + * 1 - 10 message ? + * 100+ message ? + * Vec Aloc: |....|....|....|....| + * Iter : |....|xxx |....|xxxxxxxxx |....| + * Many items - vec is bad - ALOC + * Not many items - iter is bad - addition calls inside iter. + * + */ + #[derive(Debug, Clone, Serialize)] pub struct Attachment { pub name: String, @@ -78,6 +88,16 @@ pub enum ByteRepresentation { Range((usize, usize)), } +/* +TODO/NOTE: +Problems here: +- actually with return any fixed value - we are cloning data in fact and loosing performance +- additon allocation + +TODO: +- back to trait +- still remove 'lt but with Arc only for DLT parser +*/ #[derive(Debug)] pub enum LogMessage { Raw(Vec), @@ -205,6 +225,8 @@ pub enum MessageStreamItem { /// - The number of bytes consumed. /// - An optional parsed item [`ParseYield`]. /// - If parsing fails, it returns an [`ParserError`]. +/// + pub fn parse_all( input: &[u8], timestamp: Option, diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index 139446dbd9..d8b738436b 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -93,7 +93,8 @@ impl ComponentDescriptor for Descriptor { None, someip_metadata, // If it's source - no storage header expected - !matches!(origin, SourceOrigin::Source), + // !matches!(origin, SourceOrigin::Source), + true, )))) } } @@ -273,6 +274,7 @@ impl MetadataDescriptor for Descriptor { (String::from("log_debug"), String::from("DEBUG")), (String::from("log_verbose"), String::from("VERBOSE")), (String::from("log_invalid"), String::from("INVALID")), + (String::from("context_ids"), String::from("CONTEXT LIST")), ] .into_iter() .collect(), diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index ee6abe84ac..93e3a55f81 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -12,6 +12,12 @@ pub enum Parser { Text(text::StringTokenizer), } +/** + * NOTE/TODO: + * Using of the whole enum inside MessageProducer might give performance impact because it's massive. + * Into MessageProducer we should put exact instances of parsers instead enum-wrapper + */ + impl definitions::Parser for Parser { fn parse( &mut self, diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 32ef81666e..3e1df22046 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -7,6 +7,7 @@ use crate::{ }; use components::Components; use log::error; +use parsers::Parser; use sources::{producer::MessageProducer, sde::SdeReceiver}; use super::observing::run_producer; @@ -22,6 +23,7 @@ pub async fn start_observing( // let source_id = state.add_source(uuid).await?; let producer = MessageProducer::new(parser, source); run_producer(operation_api, state, 0, producer, None, rx_sde).await + // if let stypes::ParserType::Dlt(ref mut settings) = options.parser { // settings.load_fibex_metadata(); // }; diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index b0a0067919..6966f5d758 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -5,8 +5,8 @@ use components::{ComponentDescriptor, MetadataDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; -use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; -use std::io::Read; +use pcap_parser::{traits::PcapReaderIterator, LegacyPcapReader, PcapBlockOwned, PcapError}; +use std::{fs::File, io::Read}; use stypes::SourceOrigin; pub struct PcapLegacyByteSource { @@ -16,6 +16,10 @@ pub struct PcapLegacyByteSource { total: usize, } +pub struct PcapLegacyByteSourceFile { + inner: PcapLegacyByteSource +} + impl PcapLegacyByteSource { pub fn new(reader: R) -> Result { Ok(Self { diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index e56a059e5e..1790c5187a 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -2,7 +2,11 @@ use bufread::BufReader; use components::{ComponentDescriptor, MetadataDescriptor}; use definitions::*; use file_tools::is_binary; -use std::io::{BufRead, Read}; +use std::{ + fs::File, + io::{BufRead, Read}, + path::Path, +}; use stypes::SourceOrigin; pub struct BinaryByteSource @@ -76,6 +80,36 @@ impl ByteSource for BinaryByteSource { } } +pub struct BinaryByteSourceFromFile { + inner: BinaryByteSource, +} + +impl BinaryByteSourceFromFile { + pub fn new(inner: BinaryByteSource) -> Self { + Self { inner } + } +} + +impl ByteSource for BinaryByteSourceFromFile { + async fn load( + &mut self, + filter: Option<&SourceFilter>, + ) -> Result, SourceError> { + self.inner.load(filter).await + } + + fn current_slice(&self) -> &[u8] { + self.inner.current_slice() + } + fn consume(&mut self, offset: usize) { + self.inner.consume(offset) + } + + fn len(&self) -> usize { + self.inner.len() + } +} + const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, ]); @@ -86,10 +120,34 @@ pub struct Descriptor {} impl ComponentDescriptor> for Descriptor { fn create( &self, - _origin: &SourceOrigin, + origin: &SourceOrigin, _options: &[stypes::Field], ) -> Result>, stypes::NativeError> { - Ok(None) + fn input_file(filename: &Path) -> Result { + File::open(filename).map_err(|e| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(format!( + "Fail open file {}: {}", + filename.to_string_lossy(), + e + )), + }) + } + let filename = match origin { + SourceOrigin::File(filename) => filename, + _ => { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some( + "origin isn't supported by this source (BinaryByteSource)".to_owned(), + ), + }) + } + }; + let source = BinaryByteSourceFromFile::new(BinaryByteSource::new(input_file(filename)?)); + Ok(Some(crate::Source::Raw(source))) } } diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index f23289890a..38877d1668 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -23,7 +23,7 @@ pub mod serial; pub mod socket; pub enum Source { - Raw(binary::raw::BinaryByteSource), + Raw(binary::raw::BinaryByteSourceFromFile), Pcap(binary::pcap::legacy::PcapLegacyByteSource), PcapNg(binary::pcap::ng::PcapngByteSource), Tcp(socket::tcp::TcpSource), diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/sources/src/producer.rs index dbad067f20..3c48825c18 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/sources/src/producer.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests; +use crate::Source; +use definitions::Parser; use definitions::*; use log::warn; diff --git a/application/apps/indexer/stypes/Cargo.toml b/application/apps/indexer/stypes/Cargo.toml index 47314559bd..2537bb0321 100644 --- a/application/apps/indexer/stypes/Cargo.toml +++ b/application/apps/indexer/stypes/Cargo.toml @@ -27,7 +27,7 @@ dlt-core = { workspace = true, features = ["fibex", "serialization"] } regex = { workspace = true, optional = true } bincode = "1.3" extend = { path = "../tools/extend"} -uuid = { workspace = true, features = ["serde", "v4"] } +uuid = { workspace = true, features = ["serde", "v4", "js"] } tokio = { workspace = true, optional = true } node-bindgen = { git = "https://github.com/infinyon/node-bindgen.git", branch="master", optional = true} thiserror.workspace = true diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 200e7bf7d8..a8522e0ac9 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -121,7 +121,7 @@ send_data_delay: number, */ exclusive: boolean, }; -export type SessionSetup = { origin: SourceOrigin, components: Array, }; +export type SessionSetup = { origin: SourceOrigin, parser: ComponentOptions, source: ComponentOptions, }; /** * Settings for the SomeIp parser. diff --git a/application/apps/list/Cargo.lock b/application/apps/list/Cargo.lock new file mode 100644 index 0000000000..a3dc7fbf64 --- /dev/null +++ b/application/apps/list/Cargo.lock @@ -0,0 +1,129 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "list" +version = "0.1.0" +dependencies = [ + "uuid", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] diff --git a/application/apps/list/Cargo.toml b/application/apps/list/Cargo.toml new file mode 100644 index 0000000000..17c093cd96 --- /dev/null +++ b/application/apps/list/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "list" +description = "List" +version = "0.1.0" +edition = "2021" + +[dependencies] +uuid = { version="1", features = ["serde", "v4"] } diff --git a/application/apps/list/src/main.rs b/application/apps/list/src/main.rs new file mode 100644 index 0000000000..85cf6b7352 --- /dev/null +++ b/application/apps/list/src/main.rs @@ -0,0 +1,10 @@ +const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +]); +const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); +fn main() { + println!("{BIN_SOURCE_UUID}"); + println!("{DLT_PARSER_UUID}"); +} diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index dfbdc379b5..3d118e1d91 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -215,3 +215,4 @@ gen_encode_decode_fns!(CallbackOptionsEvent); gen_encode_decode_fns!(ComponentType); gen_encode_decode_fns!(FieldsValidationErrors); gen_encode_decode_fns!(ComponentOptions); +gen_encode_decode_fns!(SessionSetup); diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 240ad637c0..2d8676bf5c 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -882,6 +882,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "definitions" +version = "0.1.0" +dependencies = [ + "etherparse", + "serde", + "stypes", + "thiserror 2.0.3", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1128,9 +1138,9 @@ dependencies = [ [[package]] name = "etherparse" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d8a704b617484e9d867a0423cd45f7577f008c4068e2e33378f8d3860a6d73" +checksum = "3ff83a5facf1a7cbfef93cfb48d6d4fb6a1f42d8ac2341a96b3255acb4d4f860" dependencies = [ "arrayvec", ] @@ -1409,7 +1419,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", +<<<<<<< HEAD "wasi 0.11.0+wasi-snapshot-preview1", ] @@ -1423,6 +1435,10 @@ dependencies = [ "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", +======= + "wasi", + "wasm-bindgen", +>>>>>>> ce5e18605 (Add test drive runner (testing updated interfaces)) ] [[package]] @@ -2431,10 +2447,10 @@ dependencies = [ name = "parsers" version = "0.1.0" dependencies = [ - "byteorder", "chrono", "chrono-tz", "components", + "definitions", "dlt-core", "file-tools", "humantime", @@ -2545,6 +2561,7 @@ dependencies = [ "anyhow", "blake3", "components", + "definitions", "dir_checksum", "dirs 6.0.0", "futures", @@ -2624,6 +2641,7 @@ dependencies = [ "bincode", "bufread", "bytecount", + "definitions", "futures", "grep-regex", "grep-searcher", @@ -3090,6 +3108,7 @@ dependencies = [ "blake3", "components", "crossbeam-channel", + "definitions", "dirs 6.0.0", "dlt-core", "envvars", @@ -3235,6 +3254,7 @@ dependencies = [ "bufread", "bytes", "components", + "definitions", "etherparse", "file-tools", "futures", diff --git a/application/apps/rustcore/rs-bindings/src/js/session/mod.rs b/application/apps/rustcore/rs-bindings/src/js/session/mod.rs index 5c078103f9..649e617bd2 100644 --- a/application/apps/rustcore/rs-bindings/src/js/session/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/session/mod.rs @@ -447,7 +447,7 @@ impl RustSession { operation_id: String, ) -> Result<(), stypes::ComputationError> { let options = - stypes::ObserveOptions::decode(&options).map_err(stypes::ComputationError::Decoding)?; + stypes::SessionSetup::decode(&options).map_err(stypes::ComputationError::Decoding)?; self.session .as_ref() .ok_or(stypes::ComputationError::SessionUnavailable)? diff --git a/application/apps/rustcore/ts-bindings/src/api/executors/session.stream.observe.executor.ts b/application/apps/rustcore/ts-bindings/src/api/executors/session.stream.observe.executor.ts index 86253a1d43..d20e43d9ed 100644 --- a/application/apps/rustcore/ts-bindings/src/api/executors/session.stream.observe.executor.ts +++ b/application/apps/rustcore/ts-bindings/src/api/executors/session.stream.observe.executor.ts @@ -1,7 +1,7 @@ import { TExecutor, Logger, CancelablePromise, AsyncVoidConfirmedExecutor } from './executor'; import { RustSession } from '../../native/native.session'; import { EventProvider } from '../../api/session.provider'; -import { IObserve } from 'platform/types/observe'; +import { SessionSetup } from 'platform/types/bindings'; export interface IFileOptionsDLT {} @@ -12,19 +12,23 @@ export enum EFileOptionsRequirements { export type TFileOptions = IFileOptionsDLT | undefined; -export const executor: TExecutor = ( +export const executor: TExecutor = ( session: RustSession, provider: EventProvider, logger: Logger, - options: IObserve, + options: SessionSetup, ): CancelablePromise => { const debugInfo = JSON.stringify(options); - return AsyncVoidConfirmedExecutor( + return AsyncVoidConfirmedExecutor( session, provider, logger, options, - function (session: RustSession, options: IObserve, operationUuid: string): Promise { + function ( + session: RustSession, + options: SessionSetup, + operationUuid: string, + ): Promise { return session.observe(options, operationUuid); }, (): string => { diff --git a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts index ec67b4fa2b..4e314503f2 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts @@ -10,7 +10,7 @@ import { GrabbedElement } from 'platform/types/bindings/miscellaneous'; import { IRange } from 'platform/types/range'; import { ISourceLink } from 'platform/types/observe/types'; import { Attachment, IndexingMode } from 'platform/types/content'; -import { IObserve } from 'platform/types/observe'; +import { SessionSetup } from 'platform/types/bindings'; import { TextExportOptions } from 'platform/types/exporting'; export class SessionStream { @@ -81,7 +81,7 @@ export class SessionStream { return this._session.getSourcesDefinitions(); } - public observe(source: IObserve): ICancelablePromise { + public observe(source: SessionSetup): ICancelablePromise { return Executors.observe(this._session, this._provider, this._logger, source); } diff --git a/application/apps/rustcore/ts-bindings/src/index.ts b/application/apps/rustcore/ts-bindings/src/index.ts index c5bbbdb863..9265072db6 100644 --- a/application/apps/rustcore/ts-bindings/src/index.ts +++ b/application/apps/rustcore/ts-bindings/src/index.ts @@ -28,5 +28,7 @@ export { Components } from './api/components'; export { Units, Events, Interfaces }; export * as $ from 'platform/types/observe'; +export * as Bindings from 'platform/types/bindings'; +export * as Tys from 'platform/types'; setUuidGenerator(v4); diff --git a/application/apps/rustcore/ts-bindings/src/native/native.session.ts b/application/apps/rustcore/ts-bindings/src/native/native.session.ts index eeece0c673..a2e65986e6 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.session.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.session.ts @@ -14,7 +14,7 @@ import { ISourceLink } from 'platform/types/observe/types'; import { IndexingMode, Attachment } from 'platform/types/content'; import { Logger, utils } from 'platform/log'; import { scope } from 'platform/env/scope'; -import { IObserve } from 'platform/types/observe'; +import { SessionSetup } from 'platform/types/bindings'; import { TextExportOptions } from 'platform/types/exporting'; import * as protocol from 'protocol'; @@ -135,7 +135,7 @@ export abstract class RustSession extends RustSessionRequiered { * async operation. After TCanceler was called, @event destroy of @param emitter would be expected to * confirm cancelation. */ - public abstract observe(source: IObserve, operationUuid: string): Promise; + public abstract observe(options: SessionSetup, operationUuid: string): Promise; public abstract export( dest: string, @@ -741,12 +741,12 @@ export class RustSessionWrapper extends RustSession { return ''; } - public observe(source: IObserve, operationUuid: string): Promise { + public observe(options: SessionSetup, operationUuid: string): Promise { return new Promise((resolve, reject) => { try { this._provider.debug().emit.operation('observe', operationUuid); this._native - .observe(protocol.encodeObserveOptions(source), operationUuid) + .observe(protocol.encodeSessionSetup(options), operationUuid) .then(resolve) .catch((err: Error) => { reject(NativeError.from(err)); diff --git a/application/client/src/app/schema/render/tools.ts b/application/client/src/app/schema/render/tools.ts index c7df377451..4e9beff136 100644 --- a/application/client/src/app/schema/render/tools.ts +++ b/application/client/src/app/schema/render/tools.ts @@ -8,6 +8,7 @@ import { Observe } from '@platform/types/observe'; import { plugins } from '@service/plugins'; import * as Parsers from '@platform/types/observe/parser/index'; +import { Columns } from './columns'; const RENDERS: { [key: string]: RenderReference; @@ -16,7 +17,9 @@ const RENDERS: { [Parsers.Protocol.SomeIp]: SomeIp, [Parsers.Protocol.Text]: Text, }; - +export function getDltRender(): Render { + return new Dlt(); +} export async function getRender(observe: Observe): Promise | Error> { const protocol = observe.parser.instance.alias(); // Render options on plugins can't be static because they must be retrieved diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 6800837dc8..b77fb90c7c 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -18,7 +18,7 @@ import { unique } from '@platform/env/sequence'; import { history } from '@service/history'; import { Render } from '@schema/render'; import { File } from '@platform/types/files'; -import { getRender } from '@schema/render/tools'; +import { getRender, getDltRender } from '@schema/render/tools'; import { SetupObserve } from '@tabs/setup/component'; import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; @@ -292,8 +292,42 @@ export class Service extends Implementation { }); }, observe: async (origin: SessionSourceOrigin, existed?: Session): Promise => { - console.error(`Not implemented`); - return Promise.reject(new Error(`Not implemented`)); + const session = + existed !== undefined ? existed : await this.add(false).empty(getDltRender()); + return new Promise((resolve, reject) => { + session.stream + .observe() + .start(origin.getSessionSetup()) + .then((uuid: string) => { + const error = this.bind(session.uuid(), uuid, true); + if (error instanceof Error) { + this.log().error(`Fail to bind session: ${error.message}`); + } + // if (existed === undefined) { + // const error = this.bind( + // session.uuid(), + // observe.origin.desc().major, + // true, + // ); + // if (error instanceof Error) { + // this.log().error(`Fail to bind session: ${error.message}`); + // } + // } + resolve(uuid); + }) + .catch((err: Error) => { + if (existed !== undefined) { + return reject(err); + } + this.kill(session.uuid()) + .catch((closeErr: Error) => { + this.log().error(`Fail to close session: ${closeErr.message}`); + }) + .finally(() => { + reject(err); + }); + }); + }); // const render = await getRender(observe); // if (render instanceof Error) { diff --git a/application/client/src/app/service/session/dependencies/observing/provider.ts b/application/client/src/app/service/session/dependencies/observing/provider.ts index e798377d44..e178b72e23 100644 --- a/application/client/src/app/service/session/dependencies/observing/provider.ts +++ b/application/client/src/app/service/session/dependencies/observing/provider.ts @@ -94,7 +94,8 @@ export abstract class Provider { if (last !== undefined) { observe.parser.change(last.parser.instance); } - return this.session.stream.observe().start(observe.clone()); + // return this.session.stream.observe().start(observe.clone()); + return Promise.reject(new Error(`Not implemented`)); } public openAsNewOrigin(observe: Observe): Promise { @@ -104,7 +105,8 @@ export abstract class Provider { } else { return Promise.reject(new Error(`No data about current parser`)); } - return this.session.stream.observe().start(observe.clone()); + // return this.session.stream.observe().start(observe.clone()); + return Promise.reject(new Error(`Not implemented`)); } public openAsNew(source: ObserveSource | Observe): Promise { diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index 0408bb0eae..ab7527e287 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -11,6 +11,7 @@ import { Info } from './info'; import { lockers } from '@ui/service/lockers'; import { Sde } from './observing/sde'; import { TextExportOptions } from '@platform/types/exporting'; +import { SessionSetup } from '@platform/types/bindings'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; @@ -80,37 +81,39 @@ export class Stream extends Subscriber { if (event.session !== this._uuid) { return; } - const observe = Observe.from(event.source); - if (observe instanceof Error) { - this.log().error(`Fail to parse Observe: ${observe.message}`); - return; - } - this.observed.running.set( - event.operation, - new ObserveOperation( - event.operation, - observe, - this.sde.send.bind(this.sde, event.operation), - this.observe().restart.bind(this, event.operation), - this.observe().abort.bind(this, event.operation), - ), - ); - this.observe() - .descriptions.request() - .then((sources) => { - let updated = false; - sources.forEach((source) => { - if (!this.observed.map.has(source.id)) { - this.observed.map.set(source.id, source); - updated = true; - } - }); - updated && this.subjects.get().sources.emit(); - }) - .catch((err: Error) => { - this.log().error(`Fail get sources description: ${err.message}`); - }); - this.sde.overwrite(this.observed.running); + console.error(`Not implemented`); + // const observe = Observe.from(event.source); + // if (observe instanceof Error) { + // this.log().error(`Fail to parse Observe: ${observe.message}`); + // return; + // } + // this.observed.running.set( + // event.operation, + // new ObserveOperation( + // event.operation, + // observe, + // this.sde.send.bind(this.sde, event.operation), + // this.observe().restart.bind(this, event.operation), + // this.observe().abort.bind(this, event.operation), + // ), + // ); + // this.observe() + // .descriptions.request() + // .then((sources) => { + // let updated = false; + // sources.forEach((source) => { + // if (!this.observed.map.has(source.id)) { + // this.observed.map.set(source.id, source); + // updated = true; + // } + // }); + // updated && this.subjects.get().sources.emit(); + // }) + // .catch((err: Error) => { + // this.log().error(`Fail get sources description: ${err.message}`); + // }); + // this.sde.overwrite(this.observed.running); + let observe = Observe.new(); this.subjects.get().started.emit(observe); }), ); @@ -142,9 +145,9 @@ export class Stream extends Subscriber { } public observe(): { - start(observe: Observe): Promise; + start(options: SessionSetup): Promise; abort(uuid: string): Promise; - restart(uuid: string, source: Observe): Promise; + restart(uuid: string, options: SessionSetup): Promise; list(): Promise>; sources(): ObserveSource[]; isFileSource(): boolean; @@ -157,19 +160,19 @@ export class Stream extends Subscriber { }; } { return { - start: (observe: Observe): Promise => { + start: (options: SessionSetup): Promise => { return Requests.IpcRequest.send( Requests.Observe.Start.Response, new Requests.Observe.Start.Request({ session: this._uuid, - observe: observe.sterilized(), + options, }), ) .then((response) => { if (typeof response.error === 'string' && response.error !== '') { return Promise.reject(new Error(response.error)); } - this._info.fromObserveInfo(observe); + // this._info.fromObserveInfo(observe); return response.session; }) .finally(lockers.progress(`Creating session...`)); @@ -196,11 +199,11 @@ export class Stream extends Subscriber { }); }); }, - restart: (uuid: string, observe: Observe): Promise => { + restart: (uuid: string, options: SessionSetup): Promise => { return this.observe() .abort(uuid) .then(() => { - return this.observe().start(observe); + return this.observe().start(options); }); }, list: (): Promise> => { diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 07eb98ccd7..85d474d1a2 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -1,10 +1,23 @@ -import { SourceOrigin, Ident } from '@platform/types/bindings'; +import { SourceOrigin, Ident, SessionSetup, ComponentOptions } from '@platform/types/bindings'; -export class SessionOptionsPreset { +export class SessionComponents { public parser: Ident | undefined; public source: Ident | undefined; } +export class ComponentsOptions { + public parser: ComponentOptions | undefined; + public source: ComponentOptions | undefined; + public setParser(options: ComponentOptions): ComponentsOptions { + this.parser = options; + return this; + } + public setSource(options: ComponentOptions): ComponentsOptions { + this.source = options; + return this; + } +} + export class SessionSourceOrigin { static file(path: string): SessionSourceOrigin { return new SessionSourceOrigin({ File: path }, undefined); @@ -21,10 +34,33 @@ export class SessionSourceOrigin { static source(): SessionSourceOrigin { return new SessionSourceOrigin('Source', undefined); } + + public readonly options: ComponentsOptions; + constructor( public readonly origin: SourceOrigin, - public options: SessionOptionsPreset | undefined, - ) {} + public components: SessionComponents | undefined, + ) { + this.options = new ComponentsOptions(); + } + + public setComponents(components: SessionComponents) { + this.components = components; + } + + public getSessionSetup(): SessionSetup { + if (!this.options.parser) { + throw new Error(`Parser isn't defined`); + } + if (!this.options.source) { + throw new Error(`Source isn't defined`); + } + return { + origin: this.origin, + source: this.options.source, + parser: this.options.parser, + }; + } public getDef(): SourceOrigin { return this.origin; diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index 0f6c5a671e..1bccd58dc2 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -11,7 +11,8 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, IApi } from './state'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionComponents, SessionSourceOrigin } from '@service/session/origin'; +import { session } from '@service/session'; @Component({ selector: 'app-tabs-setup', @@ -57,5 +58,25 @@ export class SetupObserve }), ); } + + public test() { + this.origin.options.setSource({ + uuid: '08080808-0808-0808-0808-080808080808', + fields: [], + }); + this.origin.options.setParser({ + uuid: '01010101-0101-0101-0101-010101010101', + fields: [], + }); + session + .initialize() + .observe(this.origin) + .then((uuid: string) => { + console.log(`Session has been created: ${uuid}`); + }) + .catch((err: Error) => { + console.error(err.message); + }); + } } export interface SetupObserve extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index 6f08f05f44..e86a2dc3bb 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -28,8 +28,8 @@
- + +
diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts index a5ee4b3979..e73bfa3047 100644 --- a/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts +++ b/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts @@ -129,35 +129,36 @@ export class List extends ListBase implements AfterContentInit if (lastFile === undefined) { return; } - this.ilc() - .services.system.bridge.files() - .select.any() - .then((files: File[]) => { - if (files.length === 0) { - return; - } - this.provider.session.stream - .observe() - .start( - files.length === 1 - ? new Factory.File() - .type(lastFile.configuration[1]) - .file(files[0].filename) - .protocol(last.parser.instance.alias()) - .get() - : new Factory.Concat() - .type(lastFile.configuration[1]) - .files(files.map((f) => f.filename)) - .protocol(last.parser.instance.alias()) - .get(), - ) - .catch((err: Error) => { - this.log().error(`Fail to observe: ${err.message}`); - }); - }) - .catch((err: Error) => { - this.log().error(`Fail to select file(s): ${err.message}`); - }); + console.error(`Not implemented`); + // this.ilc() + // .services.system.bridge.files() + // .select.any() + // .then((files: File[]) => { + // if (files.length === 0) { + // return; + // } + // this.provider.session.stream + // .observe() + // .start( + // files.length === 1 + // ? new Factory.File() + // .type(lastFile.configuration[1]) + // .file(files[0].filename) + // .protocol(last.parser.instance.alias()) + // .get() + // : new Factory.Concat() + // .type(lastFile.configuration[1]) + // .files(files.map((f) => f.filename)) + // .protocol(last.parser.instance.alias()) + // .get(), + // ) + // .catch((err: Error) => { + // this.log().error(`Fail to observe: ${err.message}`); + // }); + // }) + // .catch((err: Error) => { + // this.log().error(`Fail to select file(s): ${err.message}`); + // }); } } export interface List extends IlcInterface {} diff --git a/application/holder/src/service/sessions/holder.ts b/application/holder/src/service/sessions/holder.ts index 6734ca811b..8a23791ce6 100644 --- a/application/holder/src/service/sessions/holder.ts +++ b/application/holder/src/service/sessions/holder.ts @@ -5,7 +5,7 @@ import { scope } from 'platform/env/scope'; import { Logger } from 'platform/log'; import { jobs } from '@service/jobs'; import { ICancelablePromise } from 'platform/env/promise'; -import { $ } from 'rustcore'; +import { Tys } from 'rustcore'; import * as Events from 'platform/ipc/event'; import * as path from 'path'; @@ -20,8 +20,8 @@ export class Holder { public readonly subscriber: Subscriber; protected readonly jobs: Map = new Map(); protected readonly observing: { - active: Map; - finished: Map; + active: Map; + finished: Map; } = { active: new Map(), finished: new Map(), @@ -69,17 +69,17 @@ export class Holder { } public observe(): { - start(observe: $.IObserve): Promise; + start(options: Tys.bindings.SessionSetup): Promise; cancel(uuid: string): Promise; list(): { [key: string]: string }; } { return { - start: (cfg: $.IObserve): Promise => { - const observe = new $.Observe(cfg); + start: (options: Tys.bindings.SessionSetup): Promise => { + const holder = new Tys.sessionsetup.SessionSetupHolder(options); if (this.shutdown) { return Promise.reject(new Error(`Session is closing`)); } - let jobDesc = observe.origin.asJob(); + let jobDesc = holder.asJob(); if (jobDesc instanceof Error) { this.logger.error(`Fail to get job description: ${jobDesc.message}`); jobDesc = { @@ -99,13 +99,13 @@ export class Holder { return new Promise((resolve, reject) => { const observer = this.session .getStream() - .observe(cfg) + .observe(options) .on('confirmed', () => { Events.IpcEvent.emit( new Events.Observe.Started.Event({ session: this.session.getUUID(), operation: observer.uuid(), - source: observe.json().to(), + options, }), ); }) @@ -119,22 +119,22 @@ export class Holder { .finally(() => { job.done(); this.observing.active.delete(observer.uuid()); - this.observing.finished.set(observer.uuid(), observe); + this.observing.finished.set(observer.uuid(), options); Events.IpcEvent.emit( new Events.Observe.Finished.Event({ session: this.session.getUUID(), operation: observer.uuid(), - source: observe.json().to(), + options, }), ); }); - this.observing.active.set(observer.uuid(), { source: observe, observer }); + this.observing.active.set(observer.uuid(), { options, observer }); }); }, cancel: (uuid: string): Promise => { const operation = this.observing.active.get(uuid); if (operation === undefined) { - return Promise.reject(new Error(`AAA Operation isn't found`)); + return Promise.reject(new Error(`Operation isn't found`)); } return new Promise((resolve) => { operation.observer @@ -146,55 +146,57 @@ export class Holder { }, list: (): { [key: string]: string } => { const list: { [key: string]: string } = {}; - this.observing.active.forEach((operation, uuid) => { - list[uuid] = operation.source.json().to(); - }); + console.error(`Not implemented`); + // this.observing.active.forEach((operation, uuid) => { + // list[uuid] = operation.source.json().to(); + // }); return list; }, }; } public getFileExt(): string | Error { - const all = [ - Array.from(this.observing.active.values()).map((o) => o.source), - Array.from(this.observing.finished.values()), - ].flat(); - const files: Array = all - .map((o) => o.origin.files()) - .filter((f) => f !== undefined) - .flat(); - if (files.filter((f) => f === undefined).length > 0) { - return new Error(`Streams arn't supported yet`); - } - const parsers: $.Parser.Protocol[] = []; - all.forEach((observe) => { - if (parsers.includes(observe.parser.alias())) { - return; - } - parsers.push(observe.parser.alias()); - }); - if (parsers.length > 1) { - return new Error(`Multiple parsers are used`); - } else if (parsers.length === 0) { - return new Error(`No parsers has been found`); - } - const exts: string[] = files - .map((f) => path.extname(f as string)) - .filter((ex) => ex.trim() !== ''); - switch (parsers[0]) { - case $.Parser.Protocol.Text: - return `.txt`; - case $.Parser.Protocol.Plugin: - return exts.length === 0 ? '.plg' : exts[0]; - case $.Parser.Protocol.Dlt: - case $.Parser.Protocol.SomeIp: - if (files.length === 0) { - return new Error( - `No assigned files are found. Exporting from stream into new session arn't supported`, - ); - } - return exts.length === 0 ? '' : exts[0]; - } + return new Error(`Not implemented`); + // const all = [ + // Array.from(this.observing.active.values()).map((o) => o.source), + // Array.from(this.observing.finished.values()), + // ].flat(); + // const files: Array = all + // .map((o) => o.origin.files()) + // .filter((f) => f !== undefined) + // .flat(); + // if (files.filter((f) => f === undefined).length > 0) { + // return new Error(`Streams arn't supported yet`); + // } + // const parsers: $.Parser.Protocol[] = []; + // all.forEach((observe) => { + // if (parsers.includes(observe.parser.alias())) { + // return; + // } + // parsers.push(observe.parser.alias()); + // }); + // if (parsers.length > 1) { + // return new Error(`Multiple parsers are used`); + // } else if (parsers.length === 0) { + // return new Error(`No parsers has been found`); + // } + // const exts: string[] = files + // .map((f) => path.extname(f as string)) + // .filter((ex) => ex.trim() !== ''); + // switch (parsers[0]) { + // case $.Parser.Protocol.Text: + // return `.txt`; + // case $.Parser.Protocol.Plugin: + // return exts.length === 0 ? '.plg' : exts[0]; + // case $.Parser.Protocol.Dlt: + // case $.Parser.Protocol.SomeIp: + // if (files.length === 0) { + // return new Error( + // `No assigned files are found. Exporting from stream into new session arn't supported`, + // ); + // } + // return exts.length === 0 ? '' : exts[0]; + // } } public isShutdowning(): boolean { diff --git a/application/holder/src/service/sessions/requests/observe/start.ts b/application/holder/src/service/sessions/requests/observe/start.ts index b84420d547..b97e7a2168 100644 --- a/application/holder/src/service/sessions/requests/observe/start.ts +++ b/application/holder/src/service/sessions/requests/observe/start.ts @@ -19,7 +19,7 @@ export const handler = Requests.InjectLogger< } stored .observe() - .start(request.observe) + .start(request.options) .then(() => { resolve( new Requests.Observe.Start.Response({ diff --git a/application/platform/ipc/event/observe/finished.ts b/application/platform/ipc/event/observe/finished.ts index 585aa14a59..7f5cf16c36 100644 --- a/application/platform/ipc/event/observe/finished.ts +++ b/application/platform/ipc/event/observe/finished.ts @@ -1,4 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; +import { SessionSetup } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @@ -6,14 +7,14 @@ import * as validator from '../../../env/obj'; export class Event extends SignatureRequirement { public session: string; public operation: string; - public source: string; + public options: SessionSetup; - constructor(input: { session: string; operation: string; source: string }) { + constructor(input: { session: string; operation: string; options: SessionSetup }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); this.operation = validator.getAsNotEmptyString(input, 'operation'); - this.source = validator.getAsNotEmptyString(input, 'source'); + this.options = validator.getAsObj(input, 'options'); } } diff --git a/application/platform/ipc/event/observe/started.ts b/application/platform/ipc/event/observe/started.ts index 422837356e..657716db70 100644 --- a/application/platform/ipc/event/observe/started.ts +++ b/application/platform/ipc/event/observe/started.ts @@ -1,4 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; +import { SessionSetup } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @@ -6,14 +7,14 @@ import * as validator from '../../../env/obj'; export class Event extends SignatureRequirement { public session: string; public operation: string; - public source: string; + public options: SessionSetup; - constructor(input: { session: string; operation: string; source: string }) { + constructor(input: { session: string; operation: string; options: SessionSetup }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); this.operation = validator.getAsNotEmptyString(input, 'operation'); - this.source = validator.getAsNotEmptyString(input, 'source'); + this.options = validator.getAsObj(input, 'options'); } } diff --git a/application/platform/ipc/request/observe/start.ts b/application/platform/ipc/request/observe/start.ts index b7516aa7ac..911203cc15 100644 --- a/application/platform/ipc/request/observe/start.ts +++ b/application/platform/ipc/request/observe/start.ts @@ -1,22 +1,18 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { IObserve, Observe } from '../../../types/observe/index'; +import { SessionSetup } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @Define({ name: 'StartObserveRequest' }) export class Request extends SignatureRequirement { public session: string; - public observe: IObserve; + public options: SessionSetup; - constructor(input: { session: string; observe: IObserve }) { + constructor(input: { session: string; options: SessionSetup }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); - this.observe = validator.getAsObj(input, 'observe'); - const error = Observe.validate(this.observe); - if (error instanceof Error) { - throw error; - } + this.options = validator.getAsObj(input, 'options'); } } diff --git a/application/platform/package.json b/application/platform/package.json index c49660dbb0..b57c5dcf52 100644 --- a/application/platform/package.json +++ b/application/platform/package.json @@ -95,6 +95,7 @@ "./types/comment": "./dist/types/comment.js", "./types/bookmark": "./dist/types/bookmark.js", "./types/exporting": "./dist/types/exporting.js", + "./types/sessionsetup": "./dist/types/sessionsetup/index.js", "./types/observe": "./dist/types/observe/index.js", "./types/observe/factory": "./dist/types/observe/factory.js", "./types/observe/description": "./dist/types/observe/description.js", diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index e92ba30818..332578f539 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -1,7 +1,10 @@ import { DltFilterConfig } from './dlt'; // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ComponentOptions } from './options'; import type { PluginParserSettings } from './plugins'; +export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; + export type ComponentType = 'Parser' | 'Source'; /** @@ -130,6 +133,12 @@ export type SerialTransportConfig = { exclusive: boolean; }; +export type SessionSetup = { + origin: SourceOrigin; + parser: ComponentOptions; + source: ComponentOptions; +}; + /** * Settings for the SomeIp parser. */ diff --git a/application/platform/types/index.ts b/application/platform/types/index.ts index 3568f72b52..f648d239a3 100644 --- a/application/platform/types/index.ts +++ b/application/platform/types/index.ts @@ -11,3 +11,4 @@ export * as bookmark from './bookmark'; export * as sde from './sde'; export * as bindings from './bindings'; export * as components from './components'; +export * as sessionsetup from './sessionsetup'; diff --git a/application/platform/types/sessionsetup/index.ts b/application/platform/types/sessionsetup/index.ts new file mode 100644 index 0000000000..772deadcfc --- /dev/null +++ b/application/platform/types/sessionsetup/index.ts @@ -0,0 +1,14 @@ +import { IJob } from '../observe'; +import { SessionSetup } from '../bindings'; + +export class SessionSetupHolder { + constructor(protected readonly options: SessionSetup) {} + + public asJob(): IJob { + return { + name: this.options.source.uuid, + desc: this.options.source.uuid, + icon: undefined, + }; + } +} diff --git a/cli/chipmunk-cli/Cargo.lock b/cli/chipmunk-cli/Cargo.lock index 3a973cb0f1..96d745c10d 100644 --- a/cli/chipmunk-cli/Cargo.lock +++ b/cli/chipmunk-cli/Cargo.lock @@ -398,6 +398,16 @@ dependencies = [ "syn", ] +[[package]] +name = "definitions" +version = "0.1.0" +dependencies = [ + "etherparse", + "serde", + "stypes", + "thiserror 2.0.11", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -489,9 +499,9 @@ dependencies = [ [[package]] name = "etherparse" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d8a704b617484e9d867a0423cd45f7577f008c4068e2e33378f8d3860a6d73" +checksum = "3ff83a5facf1a7cbfef93cfb48d6d4fb6a1f42d8ac2341a96b3255acb4d4f860" dependencies = [ "arrayvec", ] @@ -620,20 +630,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi", + "wasm-bindgen", ] [[package]] @@ -969,10 +969,10 @@ dependencies = [ name = "parsers" version = "0.1.0" dependencies = [ - "byteorder", "chrono", "chrono-tz", "components", + "definitions", "dlt-core", "file-tools", "humantime", @@ -986,7 +986,6 @@ dependencies = [ "someip-payload", "someip-tools", "stypes", - "thiserror 2.0.11", "tokio", "tokio-util", "uuid", @@ -1397,6 +1396,7 @@ dependencies = [ "bufread", "bytes", "components", + "definitions", "etherparse", "file-tools", "futures", From 2602469ca2710893b797c8bd081692081d9690c3 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 14 May 2025 14:16:59 +0200 Subject: [PATCH 034/121] Remove generic type from Source definition --- .../indexer/session/src/components/mod.rs | 3 +- .../indexer/session/src/handlers/observe.rs | 2 +- .../apps/indexer/session/src/operations.rs | 2 +- .../apps/indexer/session/src/session.rs | 5 +- .../indexer/sources/src/binary/pcap/legacy.rs | 60 ++++++++++++++++--- .../indexer/sources/src/binary/pcap/ng.rs | 56 +++++++++++++++-- .../apps/indexer/sources/src/binary/raw.rs | 37 ++++++------ .../indexer/sources/src/command/process.rs | 4 +- application/apps/indexer/sources/src/lib.rs | 12 ++-- .../indexer/sources/src/serial/options.rs | 4 +- .../apps/indexer/sources/src/socket/tcp.rs | 4 +- .../apps/indexer/sources/src/socket/udp.rs | 4 +- 12 files changed, 143 insertions(+), 50 deletions(-) diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 49c8322ed5..8f6409af65 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -53,8 +53,7 @@ impl ComponentsSession { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components, parsers::Parser> = - Components::new(); + let mut components: Components = Components::new(); // Registre parsers parsers::registration(&mut components)?; // Registre sources diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 3e1df22046..e34d76330c 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -16,7 +16,7 @@ pub async fn start_observing( operation_api: OperationAPI, state: SessionStateAPI, options: stypes::SessionSetup, - components: Arc, parsers::Parser>>, + components: Arc>, rx_sde: Option, ) -> OperationResult<()> { let (source, parser) = components.setup(&options)?; diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index 57497ec663..69c872f267 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -76,7 +76,7 @@ impl Operation { pub enum OperationKind { Observe( stypes::SessionSetup, - Arc, parsers::Parser>>, + Arc>, ), Search { filters: Vec, diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index f0fc229c0b..2c2dd8ea69 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -32,7 +32,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, - components: Arc, parsers::Parser>>, + components: Arc>, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -56,8 +56,7 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components, parsers::Parser> = - Components::new(); + let mut components: Components = Components::new(); // Registre parsers parsers::registration(&mut components) .map_err(|err| stypes::ComputationError::NativeError(err))?; diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 6966f5d758..536e72d6d3 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -6,9 +6,10 @@ use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{traits::PcapReaderIterator, LegacyPcapReader, PcapBlockOwned, PcapError}; -use std::{fs::File, io::Read}; +use std::{fs::File, io::Read, path::Path}; use stypes::SourceOrigin; + pub struct PcapLegacyByteSource { pcap_reader: LegacyPcapReader, buffer: DeqBuffer, @@ -16,10 +17,6 @@ pub struct PcapLegacyByteSource { total: usize, } -pub struct PcapLegacyByteSourceFile { - inner: PcapLegacyByteSource -} - impl PcapLegacyByteSource { pub fn new(reader: R) -> Result { Ok(Self { @@ -160,6 +157,55 @@ impl ByteSource for PcapLegacyByteSource { } } +pub struct PcapLegacyByteSourceFromFile { + inner: PcapLegacyByteSource, +} + +impl PcapLegacyByteSourceFromFile { + pub fn new>(filename: P) -> Result { + fn input_file(filename: &Path) -> Result { + File::open(filename).map_err(|e| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(format!( + "Fail open file {}: {}", + filename.to_string_lossy(), + e + )), + }) + } + Ok(Self { + inner: PcapLegacyByteSource::new(input_file(filename.as_ref())?).map_err(|err|stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(err.to_string()), + })?, + }) + } +} + + +impl ByteSource for PcapLegacyByteSourceFromFile { + async fn load( + &mut self, + filter: Option<&SourceFilter>, + ) -> Result, SourceError> { + self.inner.load(filter).await + } + + fn current_slice(&self) -> &[u8] { + self.inner.current_slice() + } + fn consume(&mut self, offset: usize) { + self.inner.consume(offset) + } + + fn len(&self) -> usize { + self.inner.len() + } +} + + const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, ]); @@ -167,12 +213,12 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index a24123eabe..ec3ac2b00d 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -5,8 +5,8 @@ use components::{ComponentDescriptor,MetadataDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; -use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; -use std::io::Read; +use pcap_parser::{traits::PcapReaderIterator, PcapBlockOwned, PcapError, PcapNGReader}; +use std::{fs::File, io::Read, path::Path}; use stypes::SourceOrigin; pub struct PcapngByteSource { @@ -160,6 +160,54 @@ impl ByteSource for PcapngByteSource { } } +pub struct PcapngByteSourceFromFile { + inner: PcapngByteSource, +} + +impl PcapngByteSourceFromFile { + pub fn new>(filename: P) -> Result { + fn input_file(filename: &Path) -> Result { + File::open(filename).map_err(|e| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(format!( + "Fail open file {}: {}", + filename.to_string_lossy(), + e + )), + }) + } + Ok(Self { + inner: PcapngByteSource::new(input_file(filename.as_ref())?).map_err(|err|stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(err.to_string()), + })?, + }) + } +} + + +impl ByteSource for PcapngByteSourceFromFile { + async fn load( + &mut self, + filter: Option<&SourceFilter>, + ) -> Result, SourceError> { + self.inner.load(filter).await + } + + fn current_slice(&self) -> &[u8] { + self.inner.current_slice() + } + fn consume(&mut self, offset: usize) { + self.inner.consume(offset) + } + + fn len(&self) -> usize { + self.inner.len() + } +} + const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, ]); @@ -167,12 +215,12 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 1790c5187a..94c2f795b3 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -85,8 +85,21 @@ pub struct BinaryByteSourceFromFile { } impl BinaryByteSourceFromFile { - pub fn new(inner: BinaryByteSource) -> Self { - Self { inner } + pub fn new>(filename: P) -> Result { + fn input_file(filename: &Path) -> Result { + File::open(filename).map_err(|e| stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(format!( + "Fail open file {}: {}", + filename.to_string_lossy(), + e + )), + }) + } + Ok(Self { + inner: BinaryByteSource::new(input_file(filename.as_ref())?), + }) } } @@ -117,23 +130,12 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { - fn input_file(filename: &Path) -> Result { - File::open(filename).map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(format!( - "Fail open file {}: {}", - filename.to_string_lossy(), - e - )), - }) - } + ) -> Result, stypes::NativeError> { let filename = match origin { SourceOrigin::File(filename) => filename, _ => { @@ -146,8 +148,9 @@ impl ComponentDescriptor> for Descriptor { }) } }; - let source = BinaryByteSourceFromFile::new(BinaryByteSource::new(input_file(filename)?)); - Ok(Some(crate::Source::Raw(source))) + Ok(Some(crate::Source::Raw(BinaryByteSourceFromFile::new( + filename, + )?))) } } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 76b92e5c22..52b48c55bc 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -235,12 +235,12 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 38877d1668..3356fa9811 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -22,17 +22,17 @@ pub mod sde; pub mod serial; pub mod socket; -pub enum Source { +pub enum Source { Raw(binary::raw::BinaryByteSourceFromFile), - Pcap(binary::pcap::legacy::PcapLegacyByteSource), - PcapNg(binary::pcap::ng::PcapngByteSource), + Pcap(binary::pcap::legacy::PcapLegacyByteSourceFromFile), + PcapNg(binary::pcap::ng::PcapngByteSourceFromFile), Tcp(socket::tcp::TcpSource), Udp(socket::udp::UdpSource), Serial(serial::serialport::SerialSource), Process(command::process::ProcessSource), } -impl definitions::ByteSource for Source { +impl definitions::ByteSource for Source { fn consume(&mut self, offset: usize) { match self { Self::Raw(inner) => inner.consume(offset), @@ -112,9 +112,7 @@ impl definitions::ByteSource for Source { } } -pub fn registration( - components: &mut Components, P>, -) -> Result<(), stypes::NativeError> { +pub fn registration

(components: &mut Components) -> Result<(), stypes::NativeError> { components.add_source(binary::raw::Descriptor::default())?; components.add_source(binary::pcap::legacy::Descriptor::default())?; components.add_source(binary::pcap::ng::Descriptor::default())?; diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 538c40584a..696c40018b 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -28,12 +28,12 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index d428d1af8f..d44d1afe35 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -176,12 +176,12 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index afc15027c1..7ed7fd1481 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -139,12 +139,12 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor> for Descriptor { +impl ComponentDescriptor for Descriptor { fn create( &self, _origin: &SourceOrigin, _options: &[stypes::Field], - ) -> Result>, stypes::NativeError> { + ) -> Result, stypes::NativeError> { Ok(None) } } From 02d0ac7e43c25d59442c678260aa19ed605359c5 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 16 May 2025 13:24:29 +0200 Subject: [PATCH 035/121] Using writer in parser --- .../apps/indexer/addons/dlt-tools/src/lib.rs | 102 +++---- .../apps/indexer/definitions/src/lib.rs | 2 + .../apps/indexer/definitions/src/parser.rs | 250 +++--------------- .../apps/indexer/definitions/src/record.rs | 54 ++++ .../apps/indexer/parsers/src/dlt/mod.rs | 228 +++++++++++----- application/apps/indexer/parsers/src/lib.rs | 12 +- .../apps/indexer/parsers/src/someip/mod.rs | 61 +++-- .../apps/indexer/parsers/src/text/mod.rs | 78 ++++-- .../plugins_host/src/parser_shared/mod.rs | 13 +- .../src/parser_shared/plugin_parse_message.rs | 8 +- .../src/v0_1_0/parser/bindings.rs | 38 +-- .../plugins_host/src/v0_1_0/parser/mod.rs | 12 +- .../apps/indexer/processor/src/export/mod.rs | 240 ++++++++--------- .../session/src/handlers/export_raw.rs | 84 +++--- .../indexer/session/src/handlers/observe.rs | 63 ++++- .../session/src/handlers/observing/concat.rs | 153 +++++------ .../session/src/handlers/observing/file.rs | 189 ++++++------- .../session/src/handlers/observing/mod.rs | 96 +++---- .../session/src/handlers/observing/stream.rs | 163 ++++++------ .../apps/indexer/sources/src/producer.rs | 119 +++++---- 20 files changed, 1040 insertions(+), 925 deletions(-) create mode 100644 application/apps/indexer/definitions/src/record.rs diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index 6df927034e..a6f0deff88 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(unused_crate_dependencies)] +// #![deny(unused_crate_dependencies)] // Copyright (c) 2019 E.S.R.Labs. All rights reserved. // // NOTICE: All information contained herein is, and remains @@ -15,7 +15,7 @@ extern crate indexer_base; #[macro_use] extern crate log; -use definitions::{Attachment, MessageStreamItem, ParseYield}; +use definitions::{Attachment, MessageStreamItem}; use dlt_core::filtering::DltFilterConfig; use parsers::dlt::DltParser; use sources::{binary::raw::BinaryByteSource, producer::MessageProducer}; @@ -34,55 +34,55 @@ pub async fn scan_dlt_ft( ) -> Result, String> { match File::open(input) { Ok(input) => { - let reader = BufReader::new(&input); - let source = BinaryByteSource::new(reader); - let parser = DltParser::new( - filter.map(|f| f.into()), - None, - None, - None, - with_storage_header, - ); - - let mut producer = MessageProducer::new(parser, source); - - let mut canceled = false; - - let mut attachments = vec![]; - loop { - tokio::select! { - // Check on events in current order ensuring cancel will be checked at first - // as it's defined in the current unit tests. - biased; - _ = cancel.cancelled() => { - debug!("scan canceled"); - canceled = true; - break; - } - items = producer.read_next_segment() => { - match items { - Some(items) => { - for (_, item) in items { - if let MessageStreamItem::Item(ParseYield::MessageAndAttachment((_msg, attachment))) = item { - attachments.push(attachment.to_owned()); - } else if let MessageStreamItem::Item(ParseYield::Attachment(attachment)) = item { - attachments.push(attachment.to_owned()); - } - } - } - _ => { - break; - } - } - } - } - } - - if canceled { - return Ok(Vec::new()); - } - - Ok(attachments) + todo!("Not implemented") + // let reader = BufReader::new(&input); + // let source = BinaryByteSource::new(reader); + // let parser = DltParser::new( + // filter.map(|f| f.into()), + // None, + // None, + // None, + // with_storage_header, + // ); + // let mut producer = MessageProducer::new(parser, source); + + // let mut canceled = false; + + // let mut attachments = vec![]; + // loop { + // tokio::select! { + // // Check on events in current order ensuring cancel will be checked at first + // // as it's defined in the current unit tests. + // biased; + // _ = cancel.cancelled() => { + // debug!("scan canceled"); + // canceled = true; + // break; + // } + // items = producer.read_next_segment() => { + // match items { + // Some(items) => { + // for (_, item) in items { + // if let MessageStreamItem::Item(ParseYield::MessageAndAttachment((_msg, attachment))) = item { + // attachments.push(attachment.to_owned()); + // } else if let MessageStreamItem::Item(ParseYield::Attachment(attachment)) = item { + // attachments.push(attachment.to_owned()); + // } + // } + // } + // _ => { + // break; + // } + // } + // } + // } + // } + + // if canceled { + // return Ok(Vec::new()); + // } + + // Ok(attachments) } Err(error) => Err(format!("failed to open file: {error}")), } diff --git a/application/apps/indexer/definitions/src/lib.rs b/application/apps/indexer/definitions/src/lib.rs index 138e0f2fac..10d85a6fb3 100644 --- a/application/apps/indexer/definitions/src/lib.rs +++ b/application/apps/indexer/definitions/src/lib.rs @@ -1,5 +1,7 @@ mod parser; +mod record; mod source; pub use parser::*; +pub use record::*; pub use source::*; diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index 86b7ce44bd..163807589c 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -1,10 +1,7 @@ -use std::{fmt, io::Write}; - -use serde::Serialize; +use crate::*; +use stypes::NativeError; use thiserror::Error; -pub const COLUMN_SENTINAL: char = '\u{0004}'; - #[derive(Error, Debug)] pub enum ParserError { #[error("Unrecoverable error, cannot continue: {0}")] @@ -15,17 +12,19 @@ pub enum ParserError { Incomplete, #[error("End of file reached")] Eof, + #[error("{0}")] + Native(NativeError), } -#[derive(Debug)] -pub enum ParseYield { - Message(LogMessage), - Attachment(Attachment), - MessageAndAttachment((LogMessage, Attachment)), +impl From for ParserError { + fn from(err: NativeError) -> Self { + ParserError::Native(err) + } } /// Parser trait that needs to be implemented for any parser we support /// in chipmunk +#[allow(async_fn_in_trait)] pub trait Parser { /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` @@ -40,38 +39,14 @@ pub trait Parser { /// If the parsers encounter any error while it already has parsed any items, then it must /// return those items without the error, then on the next call it can return the errors in /// case it was provided with the same slice of bytes. - fn parse( + /// Returns: + /// `usize` - consumed bytes + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, ParserError>; -} - -/** - * 1 - 10 message ? - * 100+ message ? - * Vec Aloc: |....|....|....|....| - * Iter : |....|xxx |....|xxxxxxxxx |....| - * Many items - vec is bad - ALOC - * Not many items - iter is bad - addition calls inside iter. - * - */ - -#[derive(Debug, Clone, Serialize)] -pub struct Attachment { - pub name: String, - pub size: usize, - pub created_date: Option, - pub modified_date: Option, - /// The indexes of the message within the original trace (0-based). - pub messages: Vec, - pub data: Vec, -} - -impl Attachment { - pub fn add_data(&mut self, new_data: &[u8]) { - self.data.extend_from_slice(new_data); - } + writer: &mut W, + ) -> Result; } pub trait Collector { @@ -88,178 +63,37 @@ pub enum ByteRepresentation { Range((usize, usize)), } -/* -TODO/NOTE: -Problems here: -- actually with return any fixed value - we are cloning data in fact and loosing performance -- additon allocation - -TODO: -- back to trait -- still remove 'lt but with Arc only for DLT parser -*/ -#[derive(Debug)] -pub enum LogMessage { - Raw(Vec), - PlainText(String), - Columns(Vec), -} - -impl LogMessage { - pub fn to_writer(&self, writer: &mut W) -> Result { - match self { - Self::PlainText(msg) => writer.write(msg.as_bytes()), - Self::Columns(columns) => { - writer.write(columns.join(&COLUMN_SENTINAL.to_string()).as_bytes()) - } - Self::Raw(raw) => writer.write(&raw), - } - } -} - -impl fmt::Display for LogMessage { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::PlainText(msg) => write!(f, "{}", msg), - Self::Columns(columns) => write!(f, "{}", columns.join(";")), - Self::Raw(raw) => write!(f, "[{:02X?}]", raw), - } - } -} - -impl From for ParseYield { - fn from(msg: LogMessage) -> Self { - ParseYield::Message(msg) - } -} - #[derive(Debug)] pub enum MessageStreamItem { - Item(ParseYield), + Parsed(ParseOperationResult), Skipped, - Incomplete, - Empty, Done, } -/// ### Historical Context -/// -/// * The initial parser design was intended to return only one element per parse call. -/// * It was later discovered that in certain scenarios, it is beneficial to return multiple elements if they are available within a single chunk of data. -/// * As a result, the parser interface was modified to return an iterator instead of a single item. -/// * However, this approach has proven to be overly complex and restrictive, and a simpler vector-based solution is now being considered. -/// -/// --- -/// -/// ### Rationale for Removing `impl Iterator>)>` -/// -/// Using an iterator for returning collections of messages has been rejected for the following reasons: -/// -/// 1. Misalignment with the Parser's Role: -/// -/// * A parser is a component that processes a chunk of data and returns fully parsed messages. -/// * Using an iterator is excessive, as the parser always produces a complete set of messages, not a stream of potentially infinite items. -/// * The natural output of a parser is a finite collection of messages, making `Vec` a more suitable container. -/// -/// --- -/// -/// 2. Improved Memory Management: -/// -/// * `Vec` allows for single memory allocation, reducing the risk of fragmentation. -/// * Iterators add overhead through internal state management and additional allocations for each step. -/// * `Vec` provides contiguous memory, which improves cache locality and overall access speed. -/// * Given that message batches are typically small (tens to hundreds of items), the fixed structure of `Vec` is generally more efficient. -/// -/// --- -/// -/// 3. Preserved Architectural Flexibility: -/// -/// * Using an iterator prevents the parser from being boxed as a `Box`, limiting the flexibility of the architecture. -/// * Vectors are fully compatible with dynamic objects, simplifying modular testing and isolation of components. -/// * Iterators implicitly create lifetime constraints, complicating the safe sharing of data. -/// -/// --- -/// -/// 4. Reduced Code Complexity: -/// -/// * Vectors are easier to use, test, and extend, as they can be compared directly and modified without reconstructing the iterator. -/// * Code using vectors is typically clearer, more maintainable, and less error-prone. -/// * Iterators in this context add unnecessary complexity without significant benefit. -/// -/// --- -/// -/// 5. Predictable Processing Flow: -/// -/// * Message producers always process the entire batch of messages, making streaming unnecessary. -/// * This simplifies profiling and debugging, as the data lifecycle is more straightforward. -/// -/// --- -/// -/// ### Conclusion: -/// -/// * Using `Vec` instead of iterators results in better performance, simpler code, and greater architectural flexibility. -/// * The iterator approach introduces unnecessary complexity without clear advantages, making it inappropriate for this context. -/// -/// Continuously applies the [`parse_fn`] function to the given [`input`] bytes, -/// extracting all possible items until no more can be parsed. -/// -/// This function serves as a helper for parsing methods that return only the first -/// successfully parsed item. It repeatedly invokes [`parse_fn`] as long as there -/// are enough bytes remaining in [`input`] to parse another message. -/// -/// # Behavior -/// -/// - If the first invocation of [`parse_fn`] fails, the function returns an error immediately. -/// - If the first invocation succeeds, parsing continues until either: -/// - There are not enough remaining bytes to parse another message. -/// - [`parse_fn`] returns an error, which will be ignored after the first success. -/// -/// # Arguments -/// -/// * `input`: A slice of bytes to be parsed. -/// * `timestamp`: An optional timestamp associated with the message. -/// * `min_bytes_count`: The minimum number of bytes likely required to parse a message. -/// * `parse_fn`: A function that attempts to parse a message from a byte slice. -/// - It takes a byte slice and an optional timestamp. -/// - It returns a result containing: -/// - The number of bytes consumed. -/// - An optional parsed item [`ParseYield`]. -/// - If parsing fails, it returns an [`ParserError`]. -/// - -pub fn parse_all( - input: &[u8], - timestamp: Option, - min_bytes_count: usize, - mut parse_fn: F, -) -> Result)>, ParserError> -where - F: FnMut(&[u8], Option) -> Result<(usize, Option), ParserError>, -{ - let mut slice = input; - let mut results = Vec::new(); - - // Parsing of the first item should be sensentive to errors - let (consumed, item) = parse_fn(slice, timestamp)?; - results.push((consumed, item)); - - // Continue parsing until end (or error) - loop { - slice = &slice[consumed..]; - - if slice.len() < min_bytes_count { - break; - } - - match parse_fn(slice, timestamp) { - Ok((consumed, item)) => { - results.push((consumed, item)); - if consumed == 0 { - break; - } - } - Err(_) => break, - } - } - Ok(results) -} +// fn parse_all( +// input: &[u8], +// timestamp: Option, +// min_bytes_count: usize, +// mut parse_fn: F, +// ) -> Result>)> + use<'_, F, T>, Error> +// where +// F: FnMut(&[u8], Option) -> Result<(usize, Option>), Error>, +// { +// let mut slice = input; + +// // return early if function errors on first parse call. +// let first_res = parse_fn(slice, timestamp)?; + +// // Otherwise keep parsing and stop on first error, returning the parsed items at the end. +// let iter = iter::successors(Some(first_res), move |(consumed, _res)| { +// slice = &slice[*consumed..]; + +// if slice.len() < min_bytes_count { +// return None; +// } + +// parse_fn(slice, timestamp).ok() +// }); + +// Ok(iter) +// } diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs new file mode 100644 index 0000000000..5880aee260 --- /dev/null +++ b/application/apps/indexer/definitions/src/record.rs @@ -0,0 +1,54 @@ +use std::borrow::Cow; + +use crate::*; +use serde::Serialize; + +pub const COLUMN_SENTINAL: char = '\u{0004}'; + +#[derive(Debug, Clone, Serialize)] +pub struct Attachment { + pub name: String, + pub size: usize, + pub created_date: Option, + pub modified_date: Option, + /// The indexes of the message within the original trace (0-based). + pub messages: Vec, + pub data: Vec, +} + +impl Attachment { + pub fn add_data(&mut self, new_data: &[u8]) { + self.data.extend_from_slice(new_data); + } +} + +#[derive(Debug)] +pub struct ParseOperationResult { + /// Number of consumed bytes + pub consumed: usize, + /// Count of parsed and sent messages + pub count: usize, +} + +impl ParseOperationResult { + pub fn new(consumed: usize, count: usize) -> Self { + Self { consumed, count } + } + pub fn parsed_any_msg(&self) -> bool { + self.count > 0 + } +} +pub enum LogRecordOutput<'a> { + Raw(&'a [u8]), + Str(&'a str), + Cow(Cow<'a, str>), + String(String), + Columns(&'a [&'a str]), + Attachment(Attachment), + Multiple(Vec>), +} + +#[allow(async_fn_in_trait)] +pub trait LogRecordWriter { + async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; +} diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 18606cac87..5848ae210c 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -2,6 +2,8 @@ pub mod attachment; pub mod fmt; pub mod options; +use std::io; + use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; // use byteorder::{BigEndian, WriteBytesExt}; use dlt_core::{ @@ -14,6 +16,7 @@ pub use dlt_core::{ filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; use serde::Serialize; +use stypes::NativeError; // use std::{io::Write, ops::Range, sync::Arc}; use self::{attachment::FtScanner, fmt::FormatOptions}; @@ -75,51 +78,19 @@ impl DltRawParser { } } - fn parse_item( + fn parse_item<'a>( &mut self, - input: &[u8], + input: &'a [u8], _timestamp: Option, - ) -> Result<(usize, Option), ParserError> { + ) -> Result<(usize, Option<&'a [u8]>), ParserError> { let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; - let msg = consumed.map(|c| LogMessage::Raw(Vec::from(&input[0..c as usize]))); + let msg = consumed.map(|c| &input[0..c as usize]); let total_consumed = input.len() - rest.len(); let item = (total_consumed, msg.map(|m| m.into())); Ok(item) } } -// #[derive(Default)] -// pub struct DltRangeParser { -// offset: usize, -// } - -// impl DltRangeParser { -// pub fn new() -> Self { -// Self { offset: 0 } -// } - -// fn parse_item( -// &mut self, -// input: &[u8], -// _timestamp: Option, -// ) -> Result<(usize, Option), ParserError> { -// let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; -// let msg = consumed.map(|c| { -// self.offset += c as usize; -// RangeMessage { -// range: Range { -// start: self.offset, -// end: self.offset + c as usize, -// }, -// } -// }); -// let total_consumed = input.len() - rest.len(); -// let item = (total_consumed, msg.map(|m| m.into())); - -// Ok(item) -// } -// } - impl DltParser { pub fn new( filter_config: Option, @@ -158,7 +129,7 @@ impl<'m> SingleParser> for DltParser<'m> { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option), ParserError> { + ) -> Result<(usize, Option<(FormattableMessage<'_>, Option)>), ParserError> { match dlt_message(input, self.filter_config.as_ref(), self.with_storage_header) .map_err(map_dlt_err)? { @@ -186,19 +157,7 @@ impl<'m> SingleParser> for DltParser<'m> { }; let consumed = input.len() - rest.len(); self.offset += consumed; - let item = ( - consumed, - if let Some(attachment) = attachment { - Some(ParseYield::MessageAndAttachment(( - LogMessage::PlainText(msg.to_string()), - attachment, - ))) - } else { - Some(ParseYield::Message(LogMessage::PlainText(msg.to_string()))) - }, - ); - - Ok(item) + Ok((consumed, Some((msg, attachment)))) } } } @@ -213,38 +172,163 @@ fn map_dlt_err(err: DltParseError) -> ParserError { } } +impl DltParser { + fn test<'a>( + &'a mut self, + input: &'a [u8], + timestamp: Option, + ) -> Result<(usize, Option>), ParserError> { + let (consumed, data) = self.parse_item(input, timestamp)?; + Ok(( + consumed, + data.map(|(msg, attachment)| { + if let Some(attachment) = attachment { + LogRecordOutput::Multiple(vec![ + LogRecordOutput::String(msg.to_string()), + LogRecordOutput::Attachment(attachment), + ]) + } else { + LogRecordOutput::String(msg.to_string()) + } + }), + )) + } +} + impl Parser for DltParser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, ParserError> { - parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { - self.parse_item(input, timestamp) - }) + writer: &mut W, + ) -> Result { + async fn write( + data: Option<(FormattableMessage<'_>, Option)>, + writer: &mut W, + ) -> Result { + if let Some((msg, attachment)) = data { + writer.write(LogRecordOutput::Str(&msg.to_string())).await?; + if let Some(attachment) = attachment { + writer + .write(LogRecordOutput::Attachment(attachment)) + .await?; + Ok(2) + } else { + Ok(1) + } + } else { + Ok(0) + } + } + let mut slice = input; + // Parsing of the first item should be sensentive to errors + let mut total_consumed = 0; + let (mut recently_consumed, data) = self.parse_item(slice, timestamp)?; + let mut count = write(data, writer).await?; + total_consumed += recently_consumed; + // Continue parsing until end (or error) + loop { + println!( + ">>>>>>>>>>>>>>>>>> DLT parser: Slice: {recently_consumed} / {}", + slice.len() + ); + slice = &slice[recently_consumed..]; + + if slice.len() < MIN_MSG_LEN { + break; + } + + match self.parse_item(slice, timestamp) { + Ok((consumed, data)) => { + recently_consumed = consumed; + total_consumed += recently_consumed; + println!( + ">>>>>>>>>>>>>>>>>> DLT parser: consumed: {consumed}/{total_consumed}" + ); + count += write(data, writer).await?; + if recently_consumed == 0 { + println!(">>>>>>>>>>>>>>>>>> DLT parser: 0004"); + break; + } + } + Err(_) => break, + } + } + println!(">>>>>>>>>>>>>>>>>> DLT parser: 0005"); + Ok(ParseOperationResult::new(total_consumed, count)) } } -// impl Parser for DltRangeParser { -// fn parse( -// &mut self, -// input: &[u8], -// timestamp: Option, -// ) -> Result)>, ParserError> { -// parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { -// self.parse_item(input, timestamp) -// }) -// } -// } - impl Parser for DltRawParser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, ParserError> { - parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { - self.parse_item(input, timestamp) - }) + writer: &mut W, + ) -> Result { + async fn write( + data: Option<&[u8]>, + writer: &mut W, + ) -> Result { + match data { + Some(buf) => writer.write(LogRecordOutput::Raw(buf)).await.map(|_| 1), + None => Ok(0), + } + } + let mut slice = input; + // Parsing of the first item should be sensentive to errors + let mut total_consumed = 0; + let (consumed, data) = self.parse_item(slice, timestamp)?; + let mut count = write(data, writer).await?; + total_consumed += consumed; + // Continue parsing until end (or error) + loop { + slice = &slice[consumed..]; + + if slice.len() < MIN_MSG_LEN { + break; + } + + match self.parse_item(slice, timestamp) { + Ok((consumed, data)) => { + total_consumed += consumed; + count += write(data, writer).await?; + if consumed == 0 { + break; + } + } + Err(_) => break, + } + } + Ok(ParseOperationResult::new(total_consumed, count)) } } + +// impl Parser for P +// where +// P: SingleParser, +// { +// fn parse<'a>( +// &mut self, +// input: &[u8], +// timestamp: Option, +// ) -> Result)>, ParserError> { +// let mut slice = input; + +// // return early if function errors on first parse call. +// let first_res = self.parse_item(slice, timestamp)?; + +// // Otherwise keep parsing and stop on first error, returning the parsed items at the end. +// let iter = iter::successors(Some(first_res), move |(consumed, _res)| { +// slice = &slice[*consumed..]; + +// if slice.len() < P::MIN_MSG_LEN { +// return None; +// } + +// self.parse_item(slice, timestamp).ok() +// }); + +// Ok(iter) +// } +// } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 93e3a55f81..ed30112b41 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,6 +1,7 @@ #![deny(unused_crate_dependencies)] use components::Components; +use definitions::{LogRecordWriter, ParseOperationResult, ParserError}; pub mod dlt; pub mod prelude; pub mod someip; @@ -19,15 +20,16 @@ pub enum Parser { */ impl definitions::Parser for Parser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, definitions::ParserError> { + writer: &mut W, + ) -> Result { match self { - Self::Dlt(inst) => inst.parse(input, timestamp), - Self::SomeIp(inst) => inst.parse(input, timestamp), - Self::Text(inst) => inst.parse(input, timestamp), + Self::Dlt(inst) => inst.parse(input, timestamp, writer).await, + Self::SomeIp(inst) => inst.parse(input, timestamp, writer).await, + Self::Text(inst) => inst.parse(input, timestamp, writer).await, } } } diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index bdde380bbe..0629a5ab23 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -5,10 +5,11 @@ use std::{ cmp::Ordering, collections::{HashMap, hash_map::Entry}, fmt::{self, Display}, - io::Write, + io::{self, Write}, path::PathBuf, sync::Mutex, }; +use stypes::NativeError; use someip_messages::*; use someip_payload::{ @@ -295,15 +296,9 @@ impl SingleParser for SomeipParser { &mut self, input: &[u8], timestamp: Option, - ) -> Result<(usize, Option), ParserError> { - SomeipParser::parse_message(self.fibex_metadata.as_ref(), input, timestamp).map( - |(rest, message)| { - ( - rest, - Some(LogMessage::PlainText(message.to_string()).into()), - ) - }, - ) + ) -> Result<(usize, Option), ParserError> { + SomeipParser::parse_message(self.fibex_metadata.as_ref(), input, timestamp) + .map(|(rest, message)| (rest, Some(message))) } } @@ -311,14 +306,50 @@ unsafe impl Send for SomeipParser {} unsafe impl Sync for SomeipParser {} impl Parser for SomeipParser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, ParserError> { - parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { - self.parse_item(input, timestamp) - }) + writer: &mut W, + ) -> Result { + async fn write( + data: Option, + writer: &mut W, + ) -> Result { + match data { + Some(msg) => writer + .write(LogRecordOutput::Str(&msg.to_string())) + .await + .map(|_| 1), + None => Ok(0), + } + } + let mut slice = input; + // Parsing of the first item should be sensentive to errors + let mut total_consumed = 0; + let (consumed, data) = self.parse_item(slice, timestamp)?; + let mut count = write(data, writer).await?; + total_consumed += consumed; + // Continue parsing until end (or error) + loop { + slice = &slice[consumed..]; + + if slice.len() < MIN_MSG_LEN { + break; + } + + match self.parse_item(slice, timestamp) { + Ok((consumed, data)) => { + total_consumed += consumed; + count += write(data, writer).await?; + if consumed == 0 { + break; + } + } + Err(_) => break, + } + } + Ok(ParseOperationResult::new(total_consumed, count)) } } diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 43d70586e7..20247afed8 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,6 +1,9 @@ pub mod options; +use std::{borrow::Cow, io}; + use definitions::*; use serde::Serialize; +use stypes::NativeError; pub struct StringTokenizer {} @@ -23,15 +26,12 @@ pub struct StringMessage { // } // } -impl SingleParser for StringTokenizer -where - StringMessage: LogMessage, -{ - fn parse_item( +impl StringTokenizer { + fn parse_item<'a>( &mut self, - input: &[u8], + input: &'a [u8], _timestamp: Option, - ) -> Result<(usize, Option), ParserError> { + ) -> Result<(usize, Option>), ParserError> { // TODO: support non-utf8 encodings use memchr::memchr; if input.is_empty() { @@ -39,30 +39,68 @@ where } let item = if let Some(msg_size) = memchr(b'\n', input) { let content = String::from_utf8_lossy(&input[..msg_size]); - ( - msg_size + 1, - Some(LogMessage::PlainText(content.to_string()).into()), - ) + (msg_size + 1, Some(content)) } else { - ( - input.len(), - Some(LogMessage::PlainText(String::new()).into()), - ) + (input.len(), Some(Cow::Borrowed(""))) }; Ok(item) } } +impl StringTokenizer { + fn test<'a>( + &'a mut self, + input: &'a [u8], + timestamp: Option, + ) -> Result<(usize, Option>), ParserError> { + let (consumed, data) = self.parse_item(input, timestamp)?; + Ok((consumed, data.map(|msg| LogRecordOutput::Cow(msg)))) + } +} + impl Parser for StringTokenizer { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, ParserError> { - parse_all(input, timestamp, MIN_MSG_LEN, |input, timestamp| { - self.parse_item(input, timestamp) - }) + writer: &mut W, + ) -> Result { + async fn write( + data: Option>, + writer: &mut W, + ) -> Result { + match data { + Some(msg) => writer.write(LogRecordOutput::Cow(msg)).await.map(|_| 1), + None => Ok(0), + } + } + let mut slice = input; + // Parsing of the first item should be sensentive to errors + let mut total_consumed = 0; + let (consumed, data) = self.parse_item(slice, timestamp)?; + let mut count = write(data, writer).await?; + total_consumed += consumed; + // Continue parsing until end (or error) + loop { + slice = &slice[consumed..]; + + if slice.len() < MIN_MSG_LEN { + break; + } + + match self.parse_item(slice, timestamp) { + Ok((consumed, data)) => { + total_consumed += consumed; + count += write(data, writer).await?; + if consumed == 0 { + break; + } + } + Err(_) => break, + } + } + Ok(ParseOperationResult::new(total_consumed, count)) } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index f0c00c31c9..9f2cfb673b 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -134,15 +134,17 @@ impl PluginErrorLimits { const INCOMPLETE_ERROR_LIMIT: usize = 50; } -use definitions as defs; +use definitions::{self as defs}; + impl defs::Parser for PluginsParser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, - ) -> Result)>, defs::ParserError> { + writer: &mut W, + ) -> Result { let res = match &mut self.parser { - PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), + PlugVerParser::Ver010(parser) => parser.parse(input, timestamp, writer).await, }; // Check for consecutive errors. @@ -172,6 +174,9 @@ impl defs::Parser for PluginsParser { ))); } } + Err(defs::ParserError::Native(err)) => { + todo!("Not implemented") + } } res diff --git a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs index 74feec16e9..a9cd0ebdf6 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs @@ -1,4 +1,4 @@ -use definitions::LogMessage; +use definitions::LogRecordOutput; use serde::Serialize; /// Represent the message of the parsed item returned by plugins. @@ -7,9 +7,3 @@ pub struct PluginParseMessage { /// The content of the message as string. pub content: String, } - -impl From for LogMessage { - fn from(msg: PluginParseMessage) -> Self { - LogMessage::PlainText(msg.content) - } -} diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index 13bc784bbe..0786dcfecd 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -41,17 +41,17 @@ impl From<&stypes::PluginParserGeneralSettings> for ParserConfig { use definitions as defs; -impl From for defs::ParseYield { - fn from(yld: ParseYield) -> Self { - match yld { - ParseYield::Message(msg) => defs::ParseYield::Message(msg.into()), - ParseYield::Attachment(att) => defs::ParseYield::Attachment(att.into()), - ParseYield::MessageAndAttachment((msg, att)) => { - defs::ParseYield::MessageAndAttachment((msg.into(), att.into())) - } - } - } -} +// impl From for defs::ParseYield { +// fn from(yld: ParseYield) -> Self { +// match yld { +// ParseYield::Message(msg) => defs::ParseYield::Message(msg.into()), +// ParseYield::Attachment(att) => defs::ParseYield::Attachment(att.into()), +// ParseYield::MessageAndAttachment((msg, att)) => { +// defs::ParseYield::MessageAndAttachment((msg.into(), att.into())) +// } +// } +// } +// } impl From for defs::Attachment { fn from(att: Attachment) -> Self { @@ -77,14 +77,14 @@ impl From for defs::ParserError { } } -impl From for defs::LogMessage { - fn from(msg: ParsedMessage) -> Self { - match msg { - ParsedMessage::Line(msg) => defs::LogMessage::PlainText(msg), - ParsedMessage::Columns(columns) => defs::LogMessage::Columns(columns), - } - } -} +// impl From for defs::LogMessage { +// fn from(msg: ParsedMessage) -> Self { +// match msg { +// ParsedMessage::Line(msg) => defs::LogMessage::PlainText(msg), +// ParsedMessage::Columns(columns) => defs::LogMessage::Columns(columns), +// } +// } +// } impl From for ParserRenderOptions { fn from(value: RenderOptions) -> Self { diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index a007a5313e..4c8d734d82 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -133,7 +133,7 @@ impl PluginParser { use definitions as defs; impl defs::Parser for PluginParser { - fn parse( + async fn parse( &mut self, input: &[u8], timestamp: Option, @@ -163,11 +163,13 @@ impl defs::Parser for PluginParser { ))); } }; + // TODO: write in iteration + todo!("Not implemented"); - Ok(parse_results - .into_iter() - .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))) - .collect()) + // Ok(parse_results + // .into_iter() + // .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))) + // .collect()) } } diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs index eefccaa1b5..331b1fe759 100644 --- a/application/apps/indexer/processor/src/export/mod.rs +++ b/application/apps/indexer/processor/src/export/mod.rs @@ -3,7 +3,7 @@ use std::{ path::Path, }; -use definitions::{ByteSource, MessageStreamItem, ParseYield, Parser}; +use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; use indexer_base::config::IndexSection; use sources::producer::MessageProducer; use thiserror::Error; @@ -39,8 +39,8 @@ pub enum ExportError { /// /// # Errors /// In case of cancellation will return ExportError::Cancelled -pub async fn export_raw( - mut producer: MessageProducer, +pub async fn export_raw( + mut producer: MessageProducer, destination_path: &Path, sections: &Vec, read_to_end: bool, @@ -50,125 +50,127 @@ pub async fn export_raw( where P: Parser, D: ByteSource, + W: LogRecordWriter, { - trace!("export_raw, sections: {sections:?}"); - if !sections_valid(sections) { - return Err(ExportError::Config("Invalid sections".to_string())); - } - let out_file = if destination_path.exists() { - std::fs::OpenOptions::new() - .append(true) - .open(destination_path)? - } else { - std::fs::File::create(destination_path)? - }; - let mut out_writer = BufWriter::new(out_file); - let mut section_index = 0usize; - let mut current_index = 0usize; - let mut inside = false; - let mut exported = 0usize; - if sections.is_empty() { - debug!("no sections configured"); - // export everything - 'outer: while let Some(items) = producer.read_next_segment().await { - if cancel.is_cancelled() { - return Err(ExportError::Cancelled); - } + todo!("Not implemented") + // trace!("export_raw, sections: {sections:?}"); + // if !sections_valid(sections) { + // return Err(ExportError::Config("Invalid sections".to_string())); + // } + // let out_file = if destination_path.exists() { + // std::fs::OpenOptions::new() + // .append(true) + // .open(destination_path)? + // } else { + // std::fs::File::create(destination_path)? + // }; + // let mut out_writer = BufWriter::new(out_file); + // let mut section_index = 0usize; + // let mut current_index = 0usize; + // let mut inside = false; + // let mut exported = 0usize; + // if sections.is_empty() { + // debug!("no sections configured"); + // // export everything + // 'outer: while let Some(items) = producer.read_next_segment().await { + // if cancel.is_cancelled() { + // return Err(ExportError::Cancelled); + // } - for (_, item) in items { - let written = match item { - MessageStreamItem::Item(ParseYield::Message(msg)) => { - msg.to_writer(&mut out_writer)?; - true - } - MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { - msg.to_writer(&mut out_writer)?; - true - } - MessageStreamItem::Done => break 'outer, - _ => false, - }; - if written && text_file { - out_writer.write_all("\n".as_bytes())?; - } - if written { - exported += 1; - } - } - } - return Ok(exported); - } + // for (_, item) in items { + // let written = match item { + // MessageStreamItem::Item(ParseYield::Message(msg)) => { + // msg.to_writer(&mut out_writer)?; + // true + // } + // MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { + // msg.to_writer(&mut out_writer)?; + // true + // } + // MessageStreamItem::Done => break 'outer, + // _ => false, + // }; + // if written && text_file { + // out_writer.write_all("\n".as_bytes())?; + // } + // if written { + // exported += 1; + // } + // } + // } + // return Ok(exported); + // } - 'outer: while let Some(items) = producer.read_next_segment().await { - if cancel.is_cancelled() { - return Err(ExportError::Cancelled); - } - for (_, item) in items { - if !inside { - if sections[section_index].first_line == current_index { - inside = true; - } - } else if sections[section_index].last_line < current_index { - inside = false; - section_index += 1; - if sections.len() <= section_index { - // no more sections - if matches!(item, MessageStreamItem::Item(_)) { - current_index += 1; - } - break 'outer; - } - // check if we are in next section again - if sections[section_index].first_line == current_index { - inside = true; - } - } - let written = match item { - MessageStreamItem::Item(ParseYield::Message(msg)) => { - if inside { - msg.to_writer(&mut out_writer)?; - } - current_index += 1; - inside - } - MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { - if inside { - msg.to_writer(&mut out_writer)?; - } - current_index += 1; - inside - } - MessageStreamItem::Done => { - debug!("No more messages to export"); - break 'outer; - } - _ => false, - }; - if written && text_file { - out_writer.write_all("\n".as_bytes())?; - } - } - } - if read_to_end { - 'outer: while let Some(items) = producer.read_next_segment().await { - if cancel.is_cancelled() { - return Err(ExportError::Cancelled); - } - for (_, item) in items { - match item { - MessageStreamItem::Item(_) => { - current_index += 1; - } - MessageStreamItem::Done => { - break 'outer; - } - _ => {} - } - } - } - } - debug!("export_raw done ({current_index} messages)"); - Ok(current_index) + // 'outer: while let Some(items) = producer.read_next_segment().await { + // if cancel.is_cancelled() { + // return Err(ExportError::Cancelled); + // } + // for (_, item) in items { + // if !inside { + // if sections[section_index].first_line == current_index { + // inside = true; + // } + // } else if sections[section_index].last_line < current_index { + // inside = false; + // section_index += 1; + // if sections.len() <= section_index { + // // no more sections + // if matches!(item, MessageStreamItem::Item(_)) { + // current_index += 1; + // } + // break 'outer; + // } + // // check if we are in next section again + // if sections[section_index].first_line == current_index { + // inside = true; + // } + // } + // let written = match item { + // MessageStreamItem::Item(ParseYield::Message(msg)) => { + // if inside { + // msg.to_writer(&mut out_writer)?; + // } + // current_index += 1; + // inside + // } + // MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { + // if inside { + // msg.to_writer(&mut out_writer)?; + // } + // current_index += 1; + // inside + // } + // MessageStreamItem::Done => { + // debug!("No more messages to export"); + // break 'outer; + // } + // _ => false, + // }; + // if written && text_file { + // out_writer.write_all("\n".as_bytes())?; + // } + // } + // } + // if read_to_end { + // 'outer: while let Some(items) = producer.read_next_segment().await { + // if cancel.is_cancelled() { + // return Err(ExportError::Cancelled); + // } + // for (_, item) in items { + // match item { + // MessageStreamItem::Item(_) => { + // current_index += 1; + // } + // MessageStreamItem::Done => { + // break 'outer; + // } + // _ => {} + // } + // } + // } + // } + // debug!("export_raw done ({current_index} messages)"); + // Ok(current_index) } fn sections_valid(sections: &[IndexSection]) -> bool { diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/export_raw.rs index f19c4b2455..3b2a900646 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/export_raw.rs @@ -1,5 +1,5 @@ use crate::{operations::OperationResult, state::SessionStateAPI}; -use definitions::{ByteSource, Parser}; +use definitions::{ByteSource, LogRecordWriter, Parser}; use indexer_base::config::IndexSection; use log::debug; use parsers::{ @@ -133,48 +133,49 @@ async fn export( read_to_end: bool, cancel: &CancellationToken, ) -> Result, stypes::NativeError> { - match parser { - stypes::ParserType::Plugin(settings) => { - let parser = PluginsParser::initialize( - &settings.plugin_path, - &settings.general_settings, - settings.plugin_configs.clone(), - ) - .await?; - let producer = MessageProducer::new(parser, source); - export_runner(producer, dest, sections, read_to_end, false, cancel).await - } - stypes::ParserType::SomeIp(settings) => { - let parser = if let Some(files) = settings.fibex_file_paths.as_ref() { - SomeipParser::from_fibex_files(files.iter().map(PathBuf::from).collect()) - } else { - SomeipParser::new() - }; - let producer = MessageProducer::new(parser, source); - export_runner(producer, dest, sections, read_to_end, false, cancel).await - } - stypes::ParserType::Dlt(settings) => { - let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); - let parser = DltParser::new( - settings.filter_config.as_ref().map(|f| f.into()), - // TODO: find a way to avoid clonning of MD - settings.fibex_metadata.as_ref().map(|md| md.clone()), - fmt_options, - None, - settings.with_storage_header, - ); - let producer = MessageProducer::new(parser, source); - export_runner(producer, dest, sections, read_to_end, false, cancel).await - } - stypes::ParserType::Text(()) => { - let producer = MessageProducer::new(StringTokenizer {}, source); - export_runner(producer, dest, sections, read_to_end, true, cancel).await - } - } + todo!("Not implemented"); + // match parser { + // stypes::ParserType::Plugin(settings) => { + // let parser = PluginsParser::initialize( + // &settings.plugin_path, + // &settings.general_settings, + // settings.plugin_configs.clone(), + // ) + // .await?; + // let producer = MessageProducer::new(parser, source); + // export_runner(producer, dest, sections, read_to_end, false, cancel).await + // } + // stypes::ParserType::SomeIp(settings) => { + // let parser = if let Some(files) = settings.fibex_file_paths.as_ref() { + // SomeipParser::from_fibex_files(files.iter().map(PathBuf::from).collect()) + // } else { + // SomeipParser::new() + // }; + // let producer = MessageProducer::new(parser, source); + // export_runner(producer, dest, sections, read_to_end, false, cancel).await + // } + // stypes::ParserType::Dlt(settings) => { + // let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); + // let parser = DltParser::new( + // settings.filter_config.as_ref().map(|f| f.into()), + // // TODO: find a way to avoid clonning of MD + // settings.fibex_metadata.as_ref().map(|md| md.clone()), + // fmt_options, + // None, + // settings.with_storage_header, + // ); + // let producer = MessageProducer::new(parser, source); + // export_runner(producer, dest, sections, read_to_end, false, cancel).await + // } + // stypes::ParserType::Text(()) => { + // let producer = MessageProducer::new(StringTokenizer {}, source); + // export_runner(producer, dest, sections, read_to_end, true, cancel).await + // } + // } } -pub async fn export_runner( - producer: MessageProducer, +pub async fn export_runner( + producer: MessageProducer, dest: &Path, sections: &Vec, read_to_end: bool, @@ -184,6 +185,7 @@ pub async fn export_runner( where P: Parser, D: ByteSource, + W: LogRecordWriter, { export_raw(producer, dest, sections, read_to_end, text_file, cancel) .await diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index e34d76330c..7103c58c81 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -6,12 +6,66 @@ use crate::{ state::SessionStateAPI, }; use components::Components; +use definitions::{LogRecordOutput, LogRecordWriter}; use log::error; use parsers::Parser; use sources::{producer::MessageProducer, sde::SdeReceiver}; +use stypes::NativeError; use super::observing::run_producer; +struct Writer { + state: SessionStateAPI, +} + +impl Writer { + pub fn new(state: SessionStateAPI) -> Self { + Self { state } + } +} + +impl LogRecordWriter for Writer { + async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + match record { + LogRecordOutput::Raw(inner) => { + // self.state + // .write_session_file(0, format!("{:02X}\n", inner)) + // .await?; + } + LogRecordOutput::Cow(inner) => { + self.state + .write_session_file(0, format!("{inner}\n")) + .await?; + } + LogRecordOutput::String(inner) => { + self.state + .write_session_file(0, format!("{inner}\n")) + .await?; + } + LogRecordOutput::Str(inner) => { + self.state + .write_session_file(0, format!("{inner}\n")) + .await?; + } + LogRecordOutput::Columns(inner) => { + self.state + .write_session_file( + 0, + format!( + "{}\n", + inner.join(&definitions::COLUMN_SENTINAL.to_string()) + ), + ) + .await?; + } + LogRecordOutput::Multiple(inner) => {} + LogRecordOutput::Attachment(inner) => { + self.state.add_attachment(inner)?; + } + } + Ok(()) + } +} pub async fn start_observing( operation_api: OperationAPI, state: SessionStateAPI, @@ -21,9 +75,12 @@ pub async fn start_observing( ) -> OperationResult<()> { let (source, parser) = components.setup(&options)?; // let source_id = state.add_source(uuid).await?; - let producer = MessageProducer::new(parser, source); - run_producer(operation_api, state, 0, producer, None, rx_sde).await - + println!(">>>>>>>>>>>>>>> 0000"); + let producer = MessageProducer::new(parser, source, Writer::new(state.clone())); + println!(">>>>>>>>>>>>>>> 0001"); + let result = run_producer(operation_api, state, 0, producer, None, rx_sde).await?; + println!(">>>>>>>>>>>>>>> 0002"); + Ok(result) // if let stypes::ParserType::Dlt(ref mut settings) = options.parser { // settings.load_fibex_metadata(); // }; diff --git a/application/apps/indexer/session/src/handlers/observing/concat.rs b/application/apps/indexer/session/src/handlers/observing/concat.rs index c12073962b..66545248fd 100644 --- a/application/apps/indexer/session/src/handlers/observing/concat.rs +++ b/application/apps/indexer/session/src/handlers/observing/concat.rs @@ -15,80 +15,81 @@ pub async fn concat_files( files: &[(String, stypes::FileFormat, PathBuf)], parser: &stypes::ParserType, ) -> OperationResult<()> { - for file in files.iter() { - let (uuid, _file_type, _filename) = file; - state.add_source(uuid).await?; - } - for file in files.iter() { - let (uuid, file_type, filename) = file; - let source_id = state.get_source(uuid).await?.ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(format!( - "Cannot find source id for file {} with alias {}", - filename.to_string_lossy(), - uuid, - )), - })?; - let input_file = File::open(filename).map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(format!( - "Fail open file {}: {}", - filename.to_string_lossy(), - e - )), - })?; - match file_type { - stypes::FileFormat::Binary => { - super::run_source( - operation_api.clone(), - state.clone(), - BinaryByteSource::new(input_file), - source_id, - parser, - None, - None, - ) - .await? - } - stypes::FileFormat::PcapLegacy => { - super::run_source( - operation_api.clone(), - state.clone(), - PcapLegacyByteSource::new(input_file)?, - source_id, - parser, - None, - None, - ) - .await? - } - stypes::FileFormat::PcapNG => { - super::run_source( - operation_api.clone(), - state.clone(), - PcapngByteSource::new(input_file)?, - source_id, - parser, - None, - None, - ) - .await? - } - stypes::FileFormat::Text => { - super::run_source( - operation_api.clone(), - state.clone(), - BinaryByteSource::new(input_file), - source_id, - parser, - None, - None, - ) - .await? - } - }; - } - Ok(Some(())) + todo!("Not implemented") + // for file in files.iter() { + // let (uuid, _file_type, _filename) = file; + // state.add_source(uuid).await?; + // } + // for file in files.iter() { + // let (uuid, file_type, filename) = file; + // let source_id = state.get_source(uuid).await?.ok_or(stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Io, + // message: Some(format!( + // "Cannot find source id for file {} with alias {}", + // filename.to_string_lossy(), + // uuid, + // )), + // })?; + // let input_file = File::open(filename).map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Io, + // message: Some(format!( + // "Fail open file {}: {}", + // filename.to_string_lossy(), + // e + // )), + // })?; + // match file_type { + // stypes::FileFormat::Binary => { + // super::run_source( + // operation_api.clone(), + // state.clone(), + // BinaryByteSource::new(input_file), + // source_id, + // parser, + // None, + // None, + // ) + // .await? + // } + // stypes::FileFormat::PcapLegacy => { + // super::run_source( + // operation_api.clone(), + // state.clone(), + // PcapLegacyByteSource::new(input_file)?, + // source_id, + // parser, + // None, + // None, + // ) + // .await? + // } + // stypes::FileFormat::PcapNG => { + // super::run_source( + // operation_api.clone(), + // state.clone(), + // PcapngByteSource::new(input_file)?, + // source_id, + // parser, + // None, + // None, + // ) + // .await? + // } + // stypes::FileFormat::Text => { + // super::run_source( + // operation_api.clone(), + // state.clone(), + // BinaryByteSource::new(input_file), + // source_id, + // parser, + // None, + // None, + // ) + // .await? + // } + // }; + // } + // Ok(Some(())) } diff --git a/application/apps/indexer/session/src/handlers/observing/file.rs b/application/apps/indexer/session/src/handlers/observing/file.rs index c3a85de0a6..5bdcdfa43d 100644 --- a/application/apps/indexer/session/src/handlers/observing/file.rs +++ b/application/apps/indexer/session/src/handlers/observing/file.rs @@ -22,100 +22,101 @@ pub async fn observe_file( filename: &Path, parser: &stypes::ParserType, ) -> OperationResult<()> { - let source_id = state.add_source(uuid).await?; - let (tx_tail, mut rx_tail): ( - Sender>, - Receiver>, - ) = channel(1); - match file_format { - stypes::FileFormat::Binary => { - let source = BinaryByteSource::new(input_file(filename)?); - let (_, listening) = join!( - tail::track(filename, tx_tail, operation_api.cancellation_token()), - super::run_source( - operation_api, - state, - source, - source_id, - parser, - None, - Some(rx_tail) - ) - ); - listening - } - stypes::FileFormat::PcapLegacy => { - let source = PcapLegacyByteSource::new(input_file(filename)?)?; - let (_, listening) = join!( - tail::track(filename, tx_tail, operation_api.cancellation_token()), - super::run_source( - operation_api, - state, - source, - source_id, - parser, - None, - Some(rx_tail) - ) - ); - listening - } - stypes::FileFormat::PcapNG => { - let source = PcapngByteSource::new(input_file(filename)?)?; - let (_, listening) = join!( - tail::track(filename, tx_tail, operation_api.cancellation_token()), - super::run_source( - operation_api, - state, - source, - source_id, - parser, - None, - Some(rx_tail) - ) - ); - listening - } - stypes::FileFormat::Text => { - state.set_session_file(Some(filename.to_path_buf())).await?; - // Grab main file content - state.update_session(source_id).await?; - operation_api.processing(); - // Confirm: main file content has been read - state.file_read().await?; - // Switching to tail - let cancel = operation_api.cancellation_token(); - let (result, tracker) = join!( - async { - let result = select! { - res = async move { - while let Some(update) = rx_tail.recv().await { - update.map_err(|err| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(err.to_string()), - })?; - state.update_session(source_id).await?; - } - Ok(()) - } => res, - _ = cancel.cancelled() => Ok(()) - }; - result - }, - tail::track(filename, tx_tail, operation_api.cancellation_token()), - ); - result - .and_then(|_| { - tracker.map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(format!("Tailing error: {e}")), - }) - }) - .map(|_| None) - } - } + todo!("Not implemented") + // let source_id = state.add_source(uuid).await?; + // let (tx_tail, mut rx_tail): ( + // Sender>, + // Receiver>, + // ) = channel(1); + // match file_format { + // stypes::FileFormat::Binary => { + // let source = BinaryByteSource::new(input_file(filename)?); + // let (_, listening) = join!( + // tail::track(filename, tx_tail, operation_api.cancellation_token()), + // super::run_source( + // operation_api, + // state, + // source, + // source_id, + // parser, + // None, + // Some(rx_tail) + // ) + // ); + // listening + // } + // stypes::FileFormat::PcapLegacy => { + // let source = PcapLegacyByteSource::new(input_file(filename)?)?; + // let (_, listening) = join!( + // tail::track(filename, tx_tail, operation_api.cancellation_token()), + // super::run_source( + // operation_api, + // state, + // source, + // source_id, + // parser, + // None, + // Some(rx_tail) + // ) + // ); + // listening + // } + // stypes::FileFormat::PcapNG => { + // let source = PcapngByteSource::new(input_file(filename)?)?; + // let (_, listening) = join!( + // tail::track(filename, tx_tail, operation_api.cancellation_token()), + // super::run_source( + // operation_api, + // state, + // source, + // source_id, + // parser, + // None, + // Some(rx_tail) + // ) + // ); + // listening + // } + // stypes::FileFormat::Text => { + // state.set_session_file(Some(filename.to_path_buf())).await?; + // // Grab main file content + // state.update_session(source_id).await?; + // operation_api.processing(); + // // Confirm: main file content has been read + // state.file_read().await?; + // // Switching to tail + // let cancel = operation_api.cancellation_token(); + // let (result, tracker) = join!( + // async { + // let result = select! { + // res = async move { + // while let Some(update) = rx_tail.recv().await { + // update.map_err(|err| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(err.to_string()), + // })?; + // state.update_session(source_id).await?; + // } + // Ok(()) + // } => res, + // _ = cancel.cancelled() => Ok(()) + // }; + // result + // }, + // tail::track(filename, tx_tail, operation_api.cancellation_token()), + // ); + // result + // .and_then(|_| { + // tracker.map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(format!("Tailing error: {e}")), + // }) + // }) + // .map(|_| None) + // } + // } } fn input_file(filename: &Path) -> Result { diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 584ba75954..9aca4c962a 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -5,7 +5,7 @@ use crate::{ state::SessionStateAPI, tail, }; -use definitions::{ByteSource, MessageStreamItem, ParseYield, Parser}; +use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; use log::trace; use parsers::{ LogMessage, MessageStreamItem, ParseYield, Parser, @@ -25,8 +25,8 @@ use tokio::{ time::{Duration, timeout}, }; -enum Next<'a> { - Items(&'a mut Vec<(usize, MessageStreamItem)>), +enum Next { + Parsed(usize, MessageStreamItem), Timeout, Waiting, Sde(SdeMsg), @@ -36,14 +36,16 @@ pub mod concat; pub mod file; pub mod stream; -pub const FLUSH_TIMEOUT_IN_MS: u128 = 500; +pub const FLUSH_TIMEOUT_IN_MS: u128 = 5000; -pub async fn run_source( +pub async fn run_source( operation_api: OperationAPI, state: SessionStateAPI, source: S, source_id: u16, parser: &stypes::ParserType, + writer: W, + rx_sde: Option, rx_tail: Option>>, ) -> OperationResult<()> { @@ -57,6 +59,7 @@ pub async fn run_source( source, source_id, parser, + writer, rx_sde, rx_tail, ) @@ -70,12 +73,13 @@ pub async fn run_source( } /// Contains all implementation details for running the source and the producer in the session -async fn run_source_intern( +async fn run_source_intern( operation_api: OperationAPI, state: SessionStateAPI, source: S, source_id: u16, parser: &stypes::ParserType, + writer: W, rx_sde: Option, rx_tail: Option>>, ) -> OperationResult<()> { @@ -87,7 +91,7 @@ async fn run_source_intern( settings.plugin_configs.clone(), ) .await?; - let producer = MessageProducer::new(parser, source); + let producer = MessageProducer::new(parser, source, writer); run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await } stypes::ParserType::SomeIp(settings) => { @@ -97,11 +101,11 @@ async fn run_source_intern( } None => SomeipParser::new(), }; - let producer = MessageProducer::new(someip_parser, source); + let producer = MessageProducer::new(someip_parser, source, writer); run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await } stypes::ParserType::Text(()) => { - let producer = MessageProducer::new(StringTokenizer {}, source); + let producer = MessageProducer::new(StringTokenizer {}, source, writer); run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await } stypes::ParserType::Dlt(settings) => { @@ -117,17 +121,17 @@ async fn run_source_intern( someip_metadata, settings.with_storage_header, ); - let producer = MessageProducer::new(dlt_parser, source); + let producer = MessageProducer::new(dlt_parser, source, writer); run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await } } } -pub async fn run_producer( +pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, source_id: u16, - mut producer: MessageProducer, + mut producer: MessageProducer, mut rx_tail: Option>>, mut rx_sde: Option, ) -> OperationResult<()> { @@ -139,9 +143,10 @@ pub async fn run_producer( while let Some(next) = select! { next_from_stream = async { match timeout(Duration::from_millis(FLUSH_TIMEOUT_IN_MS as u64), producer.read_next_segment()).await { - Ok(items) => { - if let Some(items) = items { - Some(Next::Items(items)) + Ok(result) => { + if let Some((consumed, results)) = result { + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0000"); + Some(Next::Parsed(consumed, results)) } else { Some(Next::Waiting) } @@ -160,56 +165,31 @@ pub async fn run_producer( _ = cancel.cancelled() => None, } { match next { - Next::Items(items) => { - // Iterating over references is more efficient than using `drain(..)`, even though - // we clone the attachments below. With ownership, `mem_copy()` would still be called - // to move the item into the attachment vector. Cloning avoids the overhead of - // `drain(..)`, especially since `items` is cleared on each iteration anyway. - for (_, item) in items { - match item { - MessageStreamItem::Item(ParseYield::Message(item)) => { - state - .write_session_file(source_id, format!("{item}\n")) - .await?; - } - MessageStreamItem::Item(ParseYield::MessageAndAttachment(( - item, - attachment, - ))) => { - state - .write_session_file(source_id, format!("{item}\n")) - .await?; - state.add_attachment(attachment.to_owned())?; - } - MessageStreamItem::Item(ParseYield::Attachment(attachment)) => { - state.add_attachment(attachment.to_owned())?; - } - MessageStreamItem::Done => { - trace!("observe, message stream is done"); - state.flush_session_file().await?; - state.file_read().await?; - } - // MessageStreamItem::FileRead => { - // state.file_read().await?; - // } - MessageStreamItem::Skipped => { - trace!("observe: skipped a message"); - } - MessageStreamItem::Incomplete => { - trace!("observe: incomplete message"); - } - MessageStreamItem::Empty => { - trace!("observe: empty message"); - } - } + Next::Parsed(_consumed, results) => match results { + MessageStreamItem::Parsed(_results) => { + //Just continue + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0001"); } - } + MessageStreamItem::Done => { + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0002"); + trace!("observe, message stream is done"); + state.flush_session_file().await?; + state.file_read().await?; + } + MessageStreamItem::Skipped => { + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0003"); + trace!("observe: skipped a message"); + } + }, Next::Timeout => { + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0004"); + if !state.is_closing() { state.flush_session_file().await?; } } Next::Waiting => { + println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0005"); if let Some(mut rx_tail) = rx_tail.take() { if select! { next_from_stream = rx_tail.recv() => { diff --git a/application/apps/indexer/session/src/handlers/observing/stream.rs b/application/apps/indexer/session/src/handlers/observing/stream.rs index 0a2e442018..50b84dd4ce 100644 --- a/application/apps/indexer/session/src/handlers/observing/stream.rs +++ b/application/apps/indexer/session/src/handlers/observing/stream.rs @@ -19,85 +19,86 @@ pub async fn observe_stream( parser: &stypes::ParserType, rx_sde: Option, ) -> OperationResult<()> { - let source_id = state.add_source(uuid).await?; - match transport { - stypes::Transport::UDP(settings) => { - let udp_source = UdpSource::new(&settings.bind_addr, settings.multicast.clone()) - .await - .map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(format!("{e}")), - })?; - observing::run_source( - operation_api, - state, - udp_source, - source_id, - parser, - rx_sde, - None, - ) - .await - } - stypes::Transport::TCP(settings) => { - let tcp_source = TcpSource::new(&settings.bind_addr, None, None) - .await - .map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(format!("{e}")), - })?; - observing::run_source( - operation_api, - state, - tcp_source, - source_id, - parser, - rx_sde, - None, - ) - .await - } - stypes::Transport::Serial(settings) => { - let serial_source = SerialSource::new(settings).map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(format!("{e}")), - })?; - observing::run_source( - operation_api, - state, - serial_source, - source_id, - parser, - rx_sde, - None, - ) - .await - } - stypes::Transport::Process(settings) => { - let process_source = ProcessSource::new( - settings.command.clone(), - settings.cwd.clone(), - settings.envs.clone(), - ) - .await - .map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Interrupted, - message: Some(format!("{e}")), - })?; - observing::run_source( - operation_api, - state, - process_source, - source_id, - parser, - rx_sde, - None, - ) - .await - } - } + todo!("Not implemented") + // let source_id = state.add_source(uuid).await?; + // match transport { + // stypes::Transport::UDP(settings) => { + // let udp_source = UdpSource::new(&settings.bind_addr, settings.multicast.clone()) + // .await + // .map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(format!("{e}")), + // })?; + // observing::run_source( + // operation_api, + // state, + // udp_source, + // source_id, + // parser, + // rx_sde, + // None, + // ) + // .await + // } + // stypes::Transport::TCP(settings) => { + // let tcp_source = TcpSource::new(&settings.bind_addr, None, None) + // .await + // .map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(format!("{e}")), + // })?; + // observing::run_source( + // operation_api, + // state, + // tcp_source, + // source_id, + // parser, + // rx_sde, + // None, + // ) + // .await + // } + // stypes::Transport::Serial(settings) => { + // let serial_source = SerialSource::new(settings).map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(format!("{e}")), + // })?; + // observing::run_source( + // operation_api, + // state, + // serial_source, + // source_id, + // parser, + // rx_sde, + // None, + // ) + // .await + // } + // stypes::Transport::Process(settings) => { + // let process_source = ProcessSource::new( + // settings.command.clone(), + // settings.cwd.clone(), + // settings.envs.clone(), + // ) + // .await + // .map_err(|e| stypes::NativeError { + // severity: stypes::Severity::ERROR, + // kind: stypes::NativeErrorKind::Interrupted, + // message: Some(format!("{e}")), + // })?; + // observing::run_source( + // operation_api, + // state, + // process_source, + // source_id, + // parser, + // rx_sde, + // None, + // ) + // .await + // } + // } } diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/sources/src/producer.rs index 3c48825c18..64634a27d9 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/sources/src/producer.rs @@ -10,10 +10,11 @@ use log::warn; const INITIAL_PARSE_ERROR_LIMIT: usize = 1024; #[derive(Debug)] -pub struct MessageProducer +pub struct MessageProducer where P: Parser, D: ByteSource, + W: LogRecordWriter, { byte_source: D, parser: P, @@ -23,11 +24,12 @@ where total_skipped: usize, done: bool, buffer: Vec<(usize, MessageStreamItem)>, + writer: W, } -impl MessageProducer { +impl MessageProducer { /// create a new producer by plugging into a byte source - pub fn new(parser: P, source: D) -> Self { + pub fn new(parser: P, source: D, writer: W) -> Self { MessageProducer { byte_source: source, parser, @@ -37,6 +39,7 @@ impl MessageProducer { total_skipped: 0, done: false, buffer: Vec::new(), + writer, } } @@ -50,7 +53,7 @@ impl MessageProducer { /// # Return: /// Return a mutable reference for the newly parsed items when there are more data available, /// otherwise it returns None when there are no more data available in the source. - pub async fn read_next_segment(&mut self) -> Option<&mut Vec<(usize, MessageStreamItem)>> { + pub async fn read_next_segment(&mut self) -> Option<(usize, MessageStreamItem)> { // ### Cancel Safety ###: // This function is cancel safe because: // * there is no await calls or any function causing yielding between filling the internal @@ -91,51 +94,63 @@ impl MessageProducer { if available == 0 { trace!("No more bytes available from source"); self.done = true; - self.buffer.push((0, MessageStreamItem::Done)); - return Some(&mut self.buffer); + // self.buffer.push((0, MessageStreamItem::Done)); + return Some((0, MessageStreamItem::Done)); } // we can call consume only after all parse results are collected because of its // reference to self. let mut total_consumed = 0; - match self .parser - .parse(self.byte_source.current_slice(), self.last_seen_ts) - .map(|items| { - items.into_iter().for_each(|item| match item { - (consumed, Some(m)) => { - let total_used_bytes = consumed + skipped_bytes; - // Reset skipped bytes since it had been counted here. - skipped_bytes = 0; - debug!( - "Extracted a valid message, consumed {consumed} bytes (total used {total_used_bytes} bytes)" - ); - total_consumed += consumed; - self.buffer - .push((total_used_bytes, MessageStreamItem::Item(m))); - } - (consumed, None) => { - total_consumed += consumed; - trace!("None, consumed {consumed} bytes"); - let total_used_bytes = consumed + skipped_bytes; - // Reset skipped bytes since it had been counted here. - skipped_bytes = 0; - self.buffer - .push((total_used_bytes, MessageStreamItem::Skipped)); - } - }) + .parse( + self.byte_source.current_slice(), + self.last_seen_ts, + &mut self.writer, + ) + .await + .map(|results| { + println!(">>>>>>>>>>>>>>>>>> parsing 00000"); + if results.parsed_any_msg() { + (results.consumed, MessageStreamItem::Parsed(results)) + } else { + (results.consumed, MessageStreamItem::Skipped) + } + // items.into_iter().for_each(|item| match item { + // (consumed, Some(m)) => { + // let total_used_bytes = consumed + skipped_bytes; + // // Reset skipped bytes since it had been counted here. + // skipped_bytes = 0; + // debug!( + // "Extracted a valid message, consumed {} bytes (total used {} bytes)", + // consumed, total_used_bytes + // ); + // total_consumed += consumed; + // self.buffer + // .push((total_used_bytes, MessageStreamItem::Item(m))); + // } + // (consumed, None) => { + // total_consumed += consumed; + // trace!("None, consumed {} bytes", consumed); + // let total_used_bytes = consumed + skipped_bytes; + // // Reset skipped bytes since it had been counted here. + // skipped_bytes = 0; + // self.buffer + // .push((total_used_bytes, MessageStreamItem::Skipped)); + // } + // }) }) { - Ok(()) => { + Ok((consumed, results)) => { + println!(">>>>>>>>>>>>>>>>>> parsing 00001"); // Ensure `did_produce_items()` correctness over time. - if cfg!(debug_assertions) && !self.buffer.is_empty() { - assert!(self.did_produce_items()); - } - - self.byte_source.consume(total_consumed); - return Some(&mut self.buffer); + // if cfg!(debug_assertions) && !self.buffer.is_empty() { + // assert!(self.did_produce_items()); + // } + self.byte_source.consume(consumed); + return Some((consumed, results)); } Err(ParserError::Incomplete) => { + println!(">>>>>>>>>>>>>>>>>> parsing ERR 00000"); trace!("not enough bytes to parse a message. Load more data"); let (newly_loaded, _available_bytes, skipped) = self.load().await?; @@ -153,18 +168,23 @@ impl MessageProducer { let unused = skipped_bytes + available; self.done = true; - self.buffer.push((unused, MessageStreamItem::Done)); - return Some(&mut self.buffer); + // self.buffer.push((unused, MessageStreamItem::Done)); + return Some((unused, MessageStreamItem::Done)); } } } Err(ParserError::Eof) => { - trace!("EOF reached...no more messages (skipped_bytes={skipped_bytes})"); + println!(">>>>>>>>>>>>>>>>>> parsing ERR 00001"); + trace!( + "EOF reached...no more messages (skipped_bytes={})", + skipped_bytes + ); self.done = true; return None; } Err(ParserError::Parse(s)) => { + println!(">>>>>>>>>>>>>>>>>> parsing ERR 00002"); // TODO: This is temporary solution. We need to inform the user each time we // hit the `INITIAL_PARSE_ERROR_LIMIT` and not break the session. // We may need the new item `MessageStreamItem::Skipped(bytes_count)` @@ -180,8 +200,8 @@ impl MessageProducer { let unused = skipped_bytes + available; self.done = true; - self.buffer.push((unused, MessageStreamItem::Done)); - return Some(&mut self.buffer); + // self.buffer.push((unused, MessageStreamItem::Done)); + return Some((unused, MessageStreamItem::Done)); } trace!("No parse possible, skip one byte and retry. Error: {s}"); @@ -194,20 +214,25 @@ impl MessageProducer { let unused = skipped_bytes + available; self.done = true; - self.buffer.push((unused, MessageStreamItem::Done)); - return Some(&mut self.buffer); + // self.buffer.push((unused, MessageStreamItem::Done)); + return Some((unused, MessageStreamItem::Done)); } } Err(ParserError::Unrecoverable(err)) => { + println!(">>>>>>>>>>>>>>>>>> parsing ERR 00003"); //TODO: Errors like this must be visible to users. // Current producer loop swallows the errors after logging them, // returning that the session is ended after encountering such errors. error!("Parsing failed: Error {err}"); eprintln!("Parsing failed: Error: {err}"); self.done = true; - self.buffer.push((0, MessageStreamItem::Done)); + // self.buffer.push((0, MessageStreamItem::Done)); - return Some(&mut self.buffer); + return Some((0, MessageStreamItem::Done)); + } + Err(ParserError::Native(err)) => { + println!(">>>>>>>>>>>>>>>>>> parsing ERR 00004"); + todo!("Not Implemented") } } } From 17edc13abbb03c46bf5e5a23dc579b334ebe7daf Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 16 May 2025 20:34:12 +0200 Subject: [PATCH 036/121] Introduce LogRecordOutput --- .../apps/indexer/definitions/src/parser.rs | 63 ++++++- .../apps/indexer/definitions/src/record.rs | 7 +- .../apps/indexer/parsers/src/dlt/mod.rs | 174 +++++++----------- application/apps/indexer/parsers/src/lib.rs | 15 +- .../apps/indexer/parsers/src/someip/mod.rs | 50 +---- .../apps/indexer/parsers/src/text/mod.rs | 51 +---- .../plugins_host/src/parser_shared/mod.rs | 9 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 4 +- .../indexer/session/src/handlers/observe.rs | 155 ++++++---------- .../apps/indexer/session/src/state/api.rs | 17 ++ .../apps/indexer/session/src/state/mod.rs | 13 ++ .../apps/indexer/sources/src/producer.rs | 104 +++++------ 12 files changed, 289 insertions(+), 373 deletions(-) diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index 163807589c..52a799b266 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -24,7 +24,6 @@ impl From for ParserError { /// Parser trait that needs to be implemented for any parser we support /// in chipmunk -#[allow(async_fn_in_trait)] pub trait Parser { /// Takes a slice of bytes and try to apply a parser. If it can parse any item of them, /// it will return iterator of items each with the consumed bytes count along with `Some(log_message)` @@ -41,12 +40,66 @@ pub trait Parser { /// case it was provided with the same slice of bytes. /// Returns: /// `usize` - consumed bytes - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, - writer: &mut W, - ) -> Result; + ) -> Result<(usize, Option>), ParserError>; +} + +pub trait ParserIteratorGetter: Parser { + fn iter<'a>( + &'a mut self, + input: &'a [u8], + timestamp: Option, + ) -> Result, ParserError> + where + Self: Sized, + { + // return early if function errors on first parse call. + let (consumed, first) = self.parse(input, timestamp)?; + Ok(ParserIterator { + parser: self, + input, + consumed, + first, + timestamp, + }) + } +} + +impl ParserIteratorGetter for T {} + +pub struct ParserIterator<'a, P: Parser> { + parser: &'a mut P, + input: &'a [u8], + consumed: usize, + first: Option>, + timestamp: Option, +} + +impl<'a, P: Parser> Iterator for ParserIterator<'a, P> { + type Item = (usize, Option>); + + fn next(&mut self) -> Option { + if self.first.is_some() { + return Some((self.consumed, self.first.take())); + } + if self.input.len() < 10 { + return None; + } + + match self + .parser + .parse(&self.input[self.consumed..], self.timestamp) + { + Ok((consumed, res)) => { + self.consumed += consumed; + Some((consumed, res)) + } + Err(_) => None, + } + } } pub trait Collector { diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 5880aee260..0e565dbc0f 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -48,7 +48,10 @@ pub enum LogRecordOutput<'a> { Multiple(Vec>), } -#[allow(async_fn_in_trait)] +// Write has to be sync to keep MessageProducer cancel-safe pub trait LogRecordWriter { - async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; + /// Writes data from buffer if buffer exists + fn finalize(&mut self) -> Result<(), stypes::NativeError>; + fn get_id(&self) -> u16; } diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 5848ae210c..50be80a278 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -172,9 +172,9 @@ fn map_dlt_err(err: DltParseError) -> ParserError { } } -impl DltParser { - fn test<'a>( - &'a mut self, +impl Parser for DltParser { + fn parse<'a>( + &mut self, input: &'a [u8], timestamp: Option, ) -> Result<(usize, Option>), ParserError> { @@ -195,112 +195,78 @@ impl DltParser { } } -impl Parser for DltParser { - async fn parse( - &mut self, - input: &[u8], - timestamp: Option, - writer: &mut W, - ) -> Result { - async fn write( - data: Option<(FormattableMessage<'_>, Option)>, - writer: &mut W, - ) -> Result { - if let Some((msg, attachment)) = data { - writer.write(LogRecordOutput::Str(&msg.to_string())).await?; - if let Some(attachment) = attachment { - writer - .write(LogRecordOutput::Attachment(attachment)) - .await?; - Ok(2) - } else { - Ok(1) - } - } else { - Ok(0) - } - } - let mut slice = input; - // Parsing of the first item should be sensentive to errors - let mut total_consumed = 0; - let (mut recently_consumed, data) = self.parse_item(slice, timestamp)?; - let mut count = write(data, writer).await?; - total_consumed += recently_consumed; - // Continue parsing until end (or error) - loop { - println!( - ">>>>>>>>>>>>>>>>>> DLT parser: Slice: {recently_consumed} / {}", - slice.len() - ); - slice = &slice[recently_consumed..]; - - if slice.len() < MIN_MSG_LEN { - break; - } +// impl Parser for DltParser { +// async fn parse( +// &mut self, +// input: &[u8], +// timestamp: Option, +// writer: &mut W, +// ) -> Result { +// async fn write( +// data: Option<(FormattableMessage<'_>, Option)>, +// writer: &mut W, +// ) -> Result { +// if let Some((msg, attachment)) = data { +// writer.write(LogRecordOutput::Str(&msg.to_string())).await?; +// if let Some(attachment) = attachment { +// writer +// .write(LogRecordOutput::Attachment(attachment)) +// .await?; +// Ok(2) +// } else { +// Ok(1) +// } +// } else { +// Ok(0) +// } +// } +// let mut slice = input; +// // Parsing of the first item should be sensentive to errors +// let mut total_consumed = 0; +// let (mut recently_consumed, data) = self.parse_item(slice, timestamp)?; +// let mut count = write(data, writer).await?; +// total_consumed += recently_consumed; +// // Continue parsing until end (or error) +// loop { +// println!( +// ">>>>>>>>>>>>>>>>>> DLT parser: Slice: {recently_consumed} / {}", +// slice.len() +// ); +// slice = &slice[recently_consumed..]; + +// if slice.len() < MIN_MSG_LEN { +// break; +// } - match self.parse_item(slice, timestamp) { - Ok((consumed, data)) => { - recently_consumed = consumed; - total_consumed += recently_consumed; - println!( - ">>>>>>>>>>>>>>>>>> DLT parser: consumed: {consumed}/{total_consumed}" - ); - count += write(data, writer).await?; - if recently_consumed == 0 { - println!(">>>>>>>>>>>>>>>>>> DLT parser: 0004"); - break; - } - } - Err(_) => break, - } - } - println!(">>>>>>>>>>>>>>>>>> DLT parser: 0005"); - Ok(ParseOperationResult::new(total_consumed, count)) - } -} +// match self.parse_item(slice, timestamp) { +// Ok((consumed, data)) => { +// recently_consumed = consumed; +// total_consumed += recently_consumed; +// println!( +// ">>>>>>>>>>>>>>>>>> DLT parser: consumed: {consumed}/{total_consumed}" +// ); +// count += write(data, writer).await?; +// if recently_consumed == 0 { +// println!(">>>>>>>>>>>>>>>>>> DLT parser: 0004"); +// break; +// } +// } +// Err(_) => break, +// } +// } +// println!(">>>>>>>>>>>>>>>>>> DLT parser: 0005"); +// Ok(ParseOperationResult::new(total_consumed, count)) +// } +// } impl Parser for DltRawParser { - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, - writer: &mut W, - ) -> Result { - async fn write( - data: Option<&[u8]>, - writer: &mut W, - ) -> Result { - match data { - Some(buf) => writer.write(LogRecordOutput::Raw(buf)).await.map(|_| 1), - None => Ok(0), - } - } - let mut slice = input; - // Parsing of the first item should be sensentive to errors - let mut total_consumed = 0; - let (consumed, data) = self.parse_item(slice, timestamp)?; - let mut count = write(data, writer).await?; - total_consumed += consumed; - // Continue parsing until end (or error) - loop { - slice = &slice[consumed..]; - - if slice.len() < MIN_MSG_LEN { - break; - } - - match self.parse_item(slice, timestamp) { - Ok((consumed, data)) => { - total_consumed += consumed; - count += write(data, writer).await?; - if consumed == 0 { - break; - } - } - Err(_) => break, - } - } - Ok(ParseOperationResult::new(total_consumed, count)) + ) -> Result<(usize, Option>), ParserError> { + let (consumed, data) = self.parse_item(input, timestamp)?; + Ok((consumed, data.map(LogRecordOutput::Raw))) } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index ed30112b41..064be435c1 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,7 +1,7 @@ #![deny(unused_crate_dependencies)] use components::Components; -use definitions::{LogRecordWriter, ParseOperationResult, ParserError}; +use definitions::{LogRecordOutput, LogRecordWriter, ParseOperationResult, ParserError}; pub mod dlt; pub mod prelude; pub mod someip; @@ -20,16 +20,15 @@ pub enum Parser { */ impl definitions::Parser for Parser { - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, - writer: &mut W, - ) -> Result { + ) -> Result<(usize, Option>), ParserError> { match self { - Self::Dlt(inst) => inst.parse(input, timestamp, writer).await, - Self::SomeIp(inst) => inst.parse(input, timestamp, writer).await, - Self::Text(inst) => inst.parse(input, timestamp, writer).await, + Self::Dlt(inst) => inst.parse(input, timestamp), + Self::SomeIp(inst) => inst.parse(input, timestamp), + Self::Text(inst) => inst.parse(input, timestamp), } } } diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 0629a5ab23..f0bf46ee92 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -306,50 +306,16 @@ unsafe impl Send for SomeipParser {} unsafe impl Sync for SomeipParser {} impl Parser for SomeipParser { - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, - writer: &mut W, - ) -> Result { - async fn write( - data: Option, - writer: &mut W, - ) -> Result { - match data { - Some(msg) => writer - .write(LogRecordOutput::Str(&msg.to_string())) - .await - .map(|_| 1), - None => Ok(0), - } - } - let mut slice = input; - // Parsing of the first item should be sensentive to errors - let mut total_consumed = 0; - let (consumed, data) = self.parse_item(slice, timestamp)?; - let mut count = write(data, writer).await?; - total_consumed += consumed; - // Continue parsing until end (or error) - loop { - slice = &slice[consumed..]; - - if slice.len() < MIN_MSG_LEN { - break; - } - - match self.parse_item(slice, timestamp) { - Ok((consumed, data)) => { - total_consumed += consumed; - count += write(data, writer).await?; - if consumed == 0 { - break; - } - } - Err(_) => break, - } - } - Ok(ParseOperationResult::new(total_consumed, count)) + ) -> Result<(usize, Option>), ParserError> { + let (consumed, data) = self.parse_item(input, timestamp)?; + Ok(( + consumed, + data.map(|msg| LogRecordOutput::String(msg.to_string())), + )) } } diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 20247afed8..7721ce9d33 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -48,9 +48,9 @@ impl StringTokenizer { } } -impl StringTokenizer { - fn test<'a>( - &'a mut self, +impl Parser for StringTokenizer { + fn parse<'a>( + &mut self, input: &'a [u8], timestamp: Option, ) -> Result<(usize, Option>), ParserError> { @@ -59,51 +59,6 @@ impl StringTokenizer { } } -impl Parser for StringTokenizer { - async fn parse( - &mut self, - input: &[u8], - timestamp: Option, - writer: &mut W, - ) -> Result { - async fn write( - data: Option>, - writer: &mut W, - ) -> Result { - match data { - Some(msg) => writer.write(LogRecordOutput::Cow(msg)).await.map(|_| 1), - None => Ok(0), - } - } - let mut slice = input; - // Parsing of the first item should be sensentive to errors - let mut total_consumed = 0; - let (consumed, data) = self.parse_item(slice, timestamp)?; - let mut count = write(data, writer).await?; - total_consumed += consumed; - // Continue parsing until end (or error) - loop { - slice = &slice[consumed..]; - - if slice.len() < MIN_MSG_LEN { - break; - } - - match self.parse_item(slice, timestamp) { - Ok((consumed, data)) => { - total_consumed += consumed; - count += write(data, writer).await?; - if consumed == 0 { - break; - } - } - Err(_) => break, - } - } - Ok(ParseOperationResult::new(total_consumed, count)) - } -} - #[cfg(test)] mod tests { use crate::Parser; diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 9f2cfb673b..bb8c94de64 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -137,14 +137,13 @@ impl PluginErrorLimits { use definitions::{self as defs}; impl defs::Parser for PluginsParser { - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, - writer: &mut W, - ) -> Result { + ) -> Result<(usize, Option>), defs::ParserError> { let res = match &mut self.parser { - PlugVerParser::Ver010(parser) => parser.parse(input, timestamp, writer).await, + PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), }; // Check for consecutive errors. diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 4c8d734d82..7dfcccd31c 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -133,9 +133,9 @@ impl PluginParser { use definitions as defs; impl defs::Parser for PluginParser { - async fn parse( + fn parse<'a>( &mut self, - input: &[u8], + input: &'a [u8], timestamp: Option, ) -> Result>)>, p::Error> { diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 7103c58c81..3b1493397c 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{mem, sync::Arc}; use crate::{ handlers::observing, @@ -6,26 +6,39 @@ use crate::{ state::SessionStateAPI, }; use components::Components; -use definitions::{LogRecordOutput, LogRecordWriter}; +use definitions::{Attachment, LogRecordOutput, LogRecordWriter}; use log::error; use parsers::Parser; use sources::{producer::MessageProducer, sde::SdeReceiver}; +use std::time::Instant; use stypes::NativeError; use super::observing::run_producer; +const SEND_DURATION: u128 = 500; + struct Writer { state: SessionStateAPI, + buffer: String, + attachments: Vec, + recent: Instant, + id: u16, } impl Writer { - pub fn new(state: SessionStateAPI) -> Self { - Self { state } + pub fn new(state: SessionStateAPI, id: u16) -> Self { + Self { + state, + buffer: String::new(), + attachments: Vec::new(), + recent: Instant::now(), + id, + } } } impl LogRecordWriter for Writer { - async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { match record { LogRecordOutput::Raw(inner) => { // self.state @@ -33,38 +46,58 @@ impl LogRecordWriter for Writer { // .await?; } LogRecordOutput::Cow(inner) => { - self.state - .write_session_file(0, format!("{inner}\n")) - .await?; + self.buffer.push_str(&inner); + self.buffer.push('\n'); } LogRecordOutput::String(inner) => { - self.state - .write_session_file(0, format!("{inner}\n")) - .await?; + self.buffer.push_str(&inner); + self.buffer.push('\n'); } LogRecordOutput::Str(inner) => { - self.state - .write_session_file(0, format!("{inner}\n")) - .await?; + self.buffer.push_str(inner); + self.buffer.push('\n'); } LogRecordOutput::Columns(inner) => { - self.state - .write_session_file( - 0, - format!( - "{}\n", - inner.join(&definitions::COLUMN_SENTINAL.to_string()) - ), - ) - .await?; + self.buffer + .push_str(&inner.join(&definitions::COLUMN_SENTINAL.to_string())); + self.buffer.push('\n'); + } + LogRecordOutput::Multiple(inner) => { + for record in inner.into_iter() { + self.write(record)?; + } } - LogRecordOutput::Multiple(inner) => {} LogRecordOutput::Attachment(inner) => { - self.state.add_attachment(inner)?; + self.attachments.push(inner); + } + } + if self.recent.elapsed().as_millis() > SEND_DURATION { + if !self.buffer.is_empty() { + self.state + .send_to_session_file(self.get_id(), std::mem::take(&mut self.buffer))?; } + for attachment in std::mem::take(&mut self.attachments) { + // TODO: send with 1 call + self.state.add_attachment(attachment)?; + } + self.recent = Instant::now(); + } + Ok(()) + } + fn finalize(&mut self) -> Result<(), stypes::NativeError> { + if !self.buffer.is_empty() { + self.state + .send_to_session_file(self.get_id(), std::mem::take(&mut self.buffer))?; + } + for attachment in std::mem::take(&mut self.attachments) { + // TODO: send with 1 call + self.state.add_attachment(attachment)?; } Ok(()) } + fn get_id(&self) -> u16 { + self.id + } } pub async fn start_observing( operation_api: OperationAPI, @@ -75,77 +108,7 @@ pub async fn start_observing( ) -> OperationResult<()> { let (source, parser) = components.setup(&options)?; // let source_id = state.add_source(uuid).await?; - println!(">>>>>>>>>>>>>>> 0000"); - let producer = MessageProducer::new(parser, source, Writer::new(state.clone())); - println!(">>>>>>>>>>>>>>> 0001"); + let producer = MessageProducer::new(parser, source, Writer::new(state.clone(), 0)); let result = run_producer(operation_api, state, 0, producer, None, rx_sde).await?; - println!(">>>>>>>>>>>>>>> 0002"); Ok(result) - // if let stypes::ParserType::Dlt(ref mut settings) = options.parser { - // settings.load_fibex_metadata(); - // }; - // if let Err(err) = state.add_executed_observe(options.clone()).await { - // error!("Fail to store observe options: {:?}", err); - // } - // match &options.origin { - // stypes::ObserveOrigin::File(uuid, file_origin, filename) => { - // let (is_text, session_file_origin) = ( - // matches!(options.parser, stypes::ParserType::Text(())), - // state.get_session_file_origin().await?, - // ); - // match session_file_origin { - // Some(origin) if origin.is_linked() => Err(stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Configuration, - // message: Some(String::from( - // "Cannot observe file, because session is linked to other text file", - // )), - // }), - // Some(origin) if !origin.is_linked() && is_text => { - // // Session file was created and some files/streams were opened already. We should check for text files - // // to prevent attempt to link session with text file. Using concat instead - // observing::concat::concat_files( - // operation_api, - // state, - // &[(uuid.clone(), file_origin.clone(), filename.clone())], - // &options.parser, - // ) - // .await - // } - // _ => { - // observing::file::observe_file( - // operation_api, - // state, - // uuid, - // file_origin, - // filename, - // &options.parser, - // ) - // .await - // } - // } - // } - // stypes::ObserveOrigin::Concat(files) => { - // if files.is_empty() { - // Err(stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Configuration, - // message: Some(String::from("No files are defined for Concat operation")), - // }) - // } else { - // observing::concat::concat_files(operation_api, state, files, &options.parser).await - // } - // } - // stypes::ObserveOrigin::Stream(uuid, transport) => { - // observing::stream::observe_stream( - // operation_api, - // state, - // uuid, - // transport, - // &options.parser, - // rx_sde, - // ) - // .await - // } - // } } diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index 4233c6d9d7..b7982713f5 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -39,6 +39,7 @@ pub enum Api { oneshot::Sender>, ), ), + SendToSessionFile(u16, String), FlushSessionFile(oneshot::Sender>), GetSessionFileOrigin(oneshot::Sender, stypes::NativeError>>), UpdateSession((u16, oneshot::Sender>)), @@ -208,6 +209,7 @@ impl Display for Api { Self::SetSessionFile(_) => "SetSessionFile", Self::GetSessionFile(_) => "GetSessionFile", Self::WriteSessionFile(_) => "WriteSessionFile", + Self::SendToSessionFile(..) => "SendToSessionFile", Self::FlushSessionFile(_) => "FlushSessionFile", Self::GetSessionFileOrigin(_) => "GetSessionFileOrigin", Self::UpdateSession(_) => "UpdateSession", @@ -293,6 +295,13 @@ impl SessionStateAPI { }) } + fn sync_exec_operation(&self, api: Api) -> Result<(), stypes::NativeError> { + let api_str = api.to_string(); + self.tx_api.send(api).map_err(|e| { + stypes::NativeError::channel(&format!("Failed to send to Api::{api_str}; error: {e}")) + }) + } + pub async fn grab(&self, range: LineRange) -> Result, stypes::NativeError> { let (tx, rx) = oneshot::channel(); self.exec_operation(Api::Grab((range.clone(), tx)), rx) @@ -446,6 +455,14 @@ impl SessionStateAPI { .await? } + pub fn send_to_session_file( + &self, + source_id: u16, + content: String, + ) -> Result<(), stypes::NativeError> { + self.sync_exec_operation(Api::SendToSessionFile(source_id, content)) + } + pub async fn flush_session_file(&self) -> Result<(), stypes::NativeError> { let (tx, rx) = oneshot::channel(); self.exec_operation(Api::FlushSessionFile(tx), rx).await? diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index 6b544922e9..b93800c1a7 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -550,6 +550,19 @@ pub async fn run( stypes::NativeError::channel("Failed to respond to Api::WriteSessionFile") })?; } + Api::SendToSessionFile(source_id, content) => { + if let Err(err) = state + .handle_write_session_file( + source_id, + state_cancellation_token.clone(), + tx_callback_events.clone(), + content, + ) + .await + { + error!("Fail to write session file: {err}") + } + } Api::FlushSessionFile(tx_response) => { let res = state .handle_flush_session_file( diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/sources/src/producer.rs index 64634a27d9..8f397b4489 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/sources/src/producer.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests; -use crate::Source; use definitions::Parser; use definitions::*; use log::warn; @@ -94,63 +93,47 @@ impl MessageProducer { if available == 0 { trace!("No more bytes available from source"); self.done = true; - // self.buffer.push((0, MessageStreamItem::Done)); + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } return Some((0, MessageStreamItem::Done)); } // we can call consume only after all parse results are collected because of its // reference to self. - let mut total_consumed = 0; match self .parser - .parse( - self.byte_source.current_slice(), - self.last_seen_ts, - &mut self.writer, - ) - .await - .map(|results| { - println!(">>>>>>>>>>>>>>>>>> parsing 00000"); - if results.parsed_any_msg() { - (results.consumed, MessageStreamItem::Parsed(results)) - } else { - (results.consumed, MessageStreamItem::Skipped) + .iter(self.byte_source.current_slice(), self.last_seen_ts) + { + Ok(mut iterator) => { + let mut bytes_processed = 0; + let mut messages_received = 0; + while let Some((consumed, msg)) = iterator.next() { + bytes_processed += consumed; + if let Some(msg) = msg { + if let Err(err) = self.writer.write(msg) { + error!("Fail to write data into writer: {err}"); + return None; + } + messages_received += 1; + } else { + self.byte_source.consume(bytes_processed); + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } + return None; + } } - // items.into_iter().for_each(|item| match item { - // (consumed, Some(m)) => { - // let total_used_bytes = consumed + skipped_bytes; - // // Reset skipped bytes since it had been counted here. - // skipped_bytes = 0; - // debug!( - // "Extracted a valid message, consumed {} bytes (total used {} bytes)", - // consumed, total_used_bytes - // ); - // total_consumed += consumed; - // self.buffer - // .push((total_used_bytes, MessageStreamItem::Item(m))); - // } - // (consumed, None) => { - // total_consumed += consumed; - // trace!("None, consumed {} bytes", consumed); - // let total_used_bytes = consumed + skipped_bytes; - // // Reset skipped bytes since it had been counted here. - // skipped_bytes = 0; - // self.buffer - // .push((total_used_bytes, MessageStreamItem::Skipped)); - // } - // }) - }) { - Ok((consumed, results)) => { - println!(">>>>>>>>>>>>>>>>>> parsing 00001"); - // Ensure `did_produce_items()` correctness over time. - // if cfg!(debug_assertions) && !self.buffer.is_empty() { - // assert!(self.did_produce_items()); - // } - self.byte_source.consume(consumed); - return Some((consumed, results)); + self.byte_source.consume(bytes_processed); + return Some(( + bytes_processed, + MessageStreamItem::Parsed(ParseOperationResult::new( + bytes_processed, + messages_received, + )), + )); } Err(ParserError::Incomplete) => { - println!(">>>>>>>>>>>>>>>>>> parsing ERR 00000"); trace!("not enough bytes to parse a message. Load more data"); let (newly_loaded, _available_bytes, skipped) = self.load().await?; @@ -167,24 +150,22 @@ impl MessageProducer { ); let unused = skipped_bytes + available; self.done = true; - - // self.buffer.push((unused, MessageStreamItem::Done)); + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } return Some((unused, MessageStreamItem::Done)); } } } Err(ParserError::Eof) => { - println!(">>>>>>>>>>>>>>>>>> parsing ERR 00001"); trace!( "EOF reached...no more messages (skipped_bytes={})", skipped_bytes ); self.done = true; - return None; } Err(ParserError::Parse(s)) => { - println!(">>>>>>>>>>>>>>>>>> parsing ERR 00002"); // TODO: This is temporary solution. We need to inform the user each time we // hit the `INITIAL_PARSE_ERROR_LIMIT` and not break the session. // We may need the new item `MessageStreamItem::Skipped(bytes_count)` @@ -199,8 +180,9 @@ impl MessageProducer { ); let unused = skipped_bytes + available; self.done = true; - - // self.buffer.push((unused, MessageStreamItem::Done)); + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } return Some((unused, MessageStreamItem::Done)); } @@ -213,25 +195,25 @@ impl MessageProducer { ); let unused = skipped_bytes + available; self.done = true; - - // self.buffer.push((unused, MessageStreamItem::Done)); + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } return Some((unused, MessageStreamItem::Done)); } } Err(ParserError::Unrecoverable(err)) => { - println!(">>>>>>>>>>>>>>>>>> parsing ERR 00003"); //TODO: Errors like this must be visible to users. // Current producer loop swallows the errors after logging them, // returning that the session is ended after encountering such errors. error!("Parsing failed: Error {err}"); eprintln!("Parsing failed: Error: {err}"); self.done = true; - // self.buffer.push((0, MessageStreamItem::Done)); - + if let Err(err) = self.writer.finalize() { + error!("Fail to write data into writer: {err}"); + } return Some((0, MessageStreamItem::Done)); } Err(ParserError::Native(err)) => { - println!(">>>>>>>>>>>>>>>>>> parsing ERR 00004"); todo!("Not Implemented") } } From f59cd97be14654c45e6aefb603062733d4dfa730 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 19 May 2025 14:07:35 +0200 Subject: [PATCH 037/121] Restore min_msg_len as parser method --- .../apps/indexer/definitions/src/parser.rs | 10 +- .../apps/indexer/parsers/src/dlt/mod.rs | 6 + application/apps/indexer/parsers/src/lib.rs | 7 + .../apps/indexer/parsers/src/someip/mod.rs | 3 + .../apps/indexer/parsers/src/text/mod.rs | 3 + .../plugins_host/src/parser_shared/mod.rs | 3 + .../plugins_host/src/v0_1_0/parser/mod.rs | 3 + .../indexer/session/src/handlers/observe.rs | 2 +- .../session/src/handlers/observing/mod.rs | 180 +++++++++--------- 9 files changed, 124 insertions(+), 93 deletions(-) diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index 52a799b266..a44ba66de9 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -45,6 +45,14 @@ pub trait Parser { input: &'a [u8], timestamp: Option, ) -> Result<(usize, Option>), ParserError>; + + /// The minimum number of bytes required to parse an item. + /// + /// # Notes: + /// - This value is used to prevent unnecessary parsing attempts when the remaining input + /// is too short to contain a valid message. + /// - The default value (`1`) indicates that the parser has no minimum length requirement. + fn min_msg_len(&self) -> usize; } pub trait ParserIteratorGetter: Parser { @@ -85,7 +93,7 @@ impl<'a, P: Parser> Iterator for ParserIterator<'a, P> { if self.first.is_some() { return Some((self.consumed, self.first.take())); } - if self.input.len() < 10 { + if self.input.len() < self.parser.min_msg_len() { return None; } diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 50be80a278..ec86e56588 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -193,6 +193,9 @@ impl Parser for DltParser { }), )) } + fn min_msg_len(&self) -> usize { + MIN_MSG_LEN + } } // impl Parser for DltParser { @@ -268,6 +271,9 @@ impl Parser for DltRawParser { let (consumed, data) = self.parse_item(input, timestamp)?; Ok((consumed, data.map(LogRecordOutput::Raw))) } + fn min_msg_len(&self) -> usize { + MIN_MSG_LEN + } } // impl Parser for P diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 064be435c1..a90f38edf4 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -31,6 +31,13 @@ impl definitions::Parser for Parser { Self::Text(inst) => inst.parse(input, timestamp), } } + fn min_msg_len(&self) -> usize { + match self { + Self::Dlt(inst) => inst.min_msg_len(), + Self::SomeIp(inst) => inst.min_msg_len(), + Self::Text(inst) => inst.min_msg_len(), + } + } } pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index f0bf46ee92..aa789ab38d 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -317,6 +317,9 @@ impl Parser for SomeipParser { data.map(|msg| LogRecordOutput::String(msg.to_string())), )) } + fn min_msg_len(&self) -> usize { + MIN_MSG_LEN + } } fn header_string(header: &Header) -> String { diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 7721ce9d33..191995943e 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -57,6 +57,9 @@ impl Parser for StringTokenizer { let (consumed, data) = self.parse_item(input, timestamp)?; Ok((consumed, data.map(|msg| LogRecordOutput::Cow(msg)))) } + fn min_msg_len(&self) -> usize { + MIN_MSG_LEN + } } #[cfg(test)] diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index bb8c94de64..4e06a4613f 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -180,6 +180,9 @@ impl defs::Parser for PluginsParser { res } + fn min_msg_len(&self) -> usize { + 1 + } } #[derive(Default)] diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 7dfcccd31c..65807e6284 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -171,6 +171,9 @@ impl defs::Parser for PluginParser { // .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))) // .collect()) } + fn min_msg_len(&self) -> usize { + 1 + } } #[derive(Default)] diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 3b1493397c..7643453786 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -109,6 +109,6 @@ pub async fn start_observing( let (source, parser) = components.setup(&options)?; // let source_id = state.add_source(uuid).await?; let producer = MessageProducer::new(parser, source, Writer::new(state.clone(), 0)); - let result = run_producer(operation_api, state, 0, producer, None, rx_sde).await?; + let result = run_producer(operation_api, state, producer, None, rx_sde).await?; Ok(result) } diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 9aca4c962a..7d328ca05d 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -6,7 +6,7 @@ use crate::{ tail, }; use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; -use log::trace; +use log::{trace, warn}; use parsers::{ LogMessage, MessageStreamItem, ParseYield, Parser, dlt::{DltParser, fmt::FormatOptions}, @@ -38,99 +38,99 @@ pub mod stream; pub const FLUSH_TIMEOUT_IN_MS: u128 = 5000; -pub async fn run_source( - operation_api: OperationAPI, - state: SessionStateAPI, - source: S, - source_id: u16, - parser: &stypes::ParserType, - writer: W, +// pub async fn run_source( +// operation_api: OperationAPI, +// state: SessionStateAPI, +// source: S, +// source_id: u16, +// parser: &stypes::ParserType, +// writer: W, - rx_sde: Option, - rx_tail: Option>>, -) -> OperationResult<()> { - let cancel = operation_api.cancellation_token(); +// rx_sde: Option, +// rx_tail: Option>>, +// ) -> OperationResult<()> { +// let cancel = operation_api.cancellation_token(); - // Actual function is wrapped here in order to react on errors and cancel other tasks - // running concurrently. - let operation_result = run_source_intern( - operation_api, - state, - source, - source_id, - parser, - writer, - rx_sde, - rx_tail, - ) - .await; +// // Actual function is wrapped here in order to react on errors and cancel other tasks +// // running concurrently. +// let operation_result = run_source_intern( +// operation_api, +// state, +// source, +// source_id, +// parser, +// writer, +// rx_sde, +// rx_tail, +// ) +// .await; - if operation_result.is_err() && !cancel.is_cancelled() { - cancel.cancel(); - } +// if operation_result.is_err() && !cancel.is_cancelled() { +// cancel.cancel(); +// } - operation_result -} +// operation_result +// } /// Contains all implementation details for running the source and the producer in the session -async fn run_source_intern( - operation_api: OperationAPI, - state: SessionStateAPI, - source: S, - source_id: u16, - parser: &stypes::ParserType, - writer: W, - rx_sde: Option, - rx_tail: Option>>, -) -> OperationResult<()> { - match parser { - stypes::ParserType::Plugin(settings) => { - let parser = PluginsParser::initialize( - &settings.plugin_path, - &settings.general_settings, - settings.plugin_configs.clone(), - ) - .await?; - let producer = MessageProducer::new(parser, source, writer); - run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await - } - stypes::ParserType::SomeIp(settings) => { - let someip_parser = match &settings.fibex_file_paths { - Some(paths) => { - SomeipParser::from_fibex_files(paths.iter().map(PathBuf::from).collect()) - } - None => SomeipParser::new(), - }; - let producer = MessageProducer::new(someip_parser, source, writer); - run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await - } - stypes::ParserType::Text(()) => { - let producer = MessageProducer::new(StringTokenizer {}, source, writer); - run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await - } - stypes::ParserType::Dlt(settings) => { - let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); - let someip_metadata = settings.fibex_file_paths.as_ref().and_then(|paths| { - FibexSomeipMetadata::from_fibex_files(paths.iter().map(PathBuf::from).collect()) - }); - let dlt_parser = DltParser::new( - settings.filter_config.as_ref().map(|f| f.into()), - // TODO: find a way to avoid clonning of MD - settings.fibex_metadata.as_ref().map(|md| md.clone()), - fmt_options, - someip_metadata, - settings.with_storage_header, - ); - let producer = MessageProducer::new(dlt_parser, source, writer); - run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await - } - } -} +// async fn run_source_intern( +// operation_api: OperationAPI, +// state: SessionStateAPI, +// source: S, +// source_id: u16, +// parser: &stypes::ParserType, +// writer: W, +// rx_sde: Option, +// rx_tail: Option>>, +// ) -> OperationResult<()> { +// match parser { +// stypes::ParserType::Plugin(settings) => { +// let parser = PluginsParser::initialize( +// &settings.plugin_path, +// &settings.general_settings, +// settings.plugin_configs.clone(), +// ) +// .await?; +// let producer = MessageProducer::new(parser, source, writer); +// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await +// } +// stypes::ParserType::SomeIp(settings) => { +// let someip_parser = match &settings.fibex_file_paths { +// Some(paths) => { +// SomeipParser::from_fibex_files(paths.iter().map(PathBuf::from).collect()) +// } +// None => SomeipParser::new(), +// }; +// let producer = MessageProducer::new(someip_parser, source, writer); +// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await +// } +// stypes::ParserType::Text(()) => { +// let producer = MessageProducer::new(StringTokenizer {}, source, writer); +// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await +// } +// stypes::ParserType::Dlt(settings) => { +// let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); +// let someip_metadata = settings.fibex_file_paths.as_ref().and_then(|paths| { +// FibexSomeipMetadata::from_fibex_files(paths.iter().map(PathBuf::from).collect()) +// }); +// let dlt_parser = DltParser::new( +// settings.filter_config.as_ref().map(|f| f.into()), +// // TODO: find a way to avoid clonning of MD +// settings.fibex_metadata.as_ref().map(|md| md.clone()), +// fmt_options, +// someip_metadata, +// settings.with_storage_header, +// ); +// let producer = MessageProducer::new(dlt_parser, source, writer); +// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await +// } +// } +// } +use std::time::Instant; pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, - source_id: u16, mut producer: MessageProducer, mut rx_tail: Option>>, mut rx_sde: Option, @@ -140,12 +140,12 @@ pub async fn run_producer( operation_api.processing(); let cancel = operation_api.cancellation_token(); let cancel_on_tail = cancel.clone(); + let started = Instant::now(); while let Some(next) = select! { next_from_stream = async { match timeout(Duration::from_millis(FLUSH_TIMEOUT_IN_MS as u64), producer.read_next_segment()).await { Ok(result) => { if let Some((consumed, results)) = result { - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0000"); Some(Next::Parsed(consumed, results)) } else { Some(Next::Waiting) @@ -168,28 +168,26 @@ pub async fn run_producer( Next::Parsed(_consumed, results) => match results { MessageStreamItem::Parsed(_results) => { //Just continue - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0001"); } MessageStreamItem::Done => { - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0002"); + warn!( + ">>>>>>>>>>>>>>>>>> MessageStreamItem::Done in {} ms", + started.elapsed().as_millis() + ); trace!("observe, message stream is done"); state.flush_session_file().await?; state.file_read().await?; } MessageStreamItem::Skipped => { - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0003"); trace!("observe: skipped a message"); } }, Next::Timeout => { - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0004"); - if !state.is_closing() { state.flush_session_file().await?; } } Next::Waiting => { - println!(">>>>>>>>>>>>>>>>>> UPPER LOOP: 0005"); if let Some(mut rx_tail) = rx_tail.take() { if select! { next_from_stream = rx_tail.recv() => { From 768e60a0fef6b450b5089de34bc1f62a19bba9c9 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 19 May 2025 14:23:17 +0200 Subject: [PATCH 038/121] Move MessageProducer out of sources --- application/apps/indexer/addons/dlt-tools/Cargo.toml | 1 + application/apps/indexer/addons/dlt-tools/src/lib.rs | 3 ++- application/apps/indexer/processor/Cargo.toml | 2 ++ application/apps/indexer/processor/src/export/mod.rs | 2 +- application/apps/indexer/processor/src/lib.rs | 1 + .../src/producer.rs => processor/src/producer/mod.rs} | 2 ++ .../src/producer}/producer/tests/cancel_safety.rs | 0 .../src/producer}/producer/tests/mock_byte_source.rs | 0 .../src/producer}/producer/tests/mock_parser.rs | 0 .../src/producer}/producer/tests/mod.rs | 0 .../src/producer}/producer/tests/multi_parse.rs | 0 .../src/producer}/producer/tests/single_parse.rs | 0 .../{sources/src => processor/src/producer}/sde.rs | 0 .../src => processor/src/producer}/tests/mock_read.rs | 0 .../src => processor/src/producer}/tests/mod.rs | 0 .../apps/indexer/session/src/handlers/export_raw.rs | 11 ++++------- .../apps/indexer/session/src/handlers/observe.rs | 2 +- .../indexer/session/src/handlers/observing/mod.rs | 6 +----- application/apps/indexer/session/src/operations.rs | 2 +- application/apps/indexer/session/src/tracker.rs | 2 +- application/apps/indexer/sources/src/lib.rs | 5 ----- application/apps/rustcore/rs-bindings/Cargo.lock | 2 ++ 22 files changed, 19 insertions(+), 22 deletions(-) rename application/apps/indexer/{sources/src/producer.rs => processor/src/producer/mod.rs} (99%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/cancel_safety.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/mock_byte_source.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/mock_parser.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/mod.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/multi_parse.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/producer/tests/single_parse.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/sde.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/tests/mock_read.rs (100%) rename application/apps/indexer/{sources/src => processor/src/producer}/tests/mod.rs (100%) diff --git a/application/apps/indexer/addons/dlt-tools/Cargo.toml b/application/apps/indexer/addons/dlt-tools/Cargo.toml index 6cd6510ffd..f6e035fbb2 100644 --- a/application/apps/indexer/addons/dlt-tools/Cargo.toml +++ b/application/apps/indexer/addons/dlt-tools/Cargo.toml @@ -12,5 +12,6 @@ definitions = { path = "../../definitions" } indexer_base = { path = "../../indexer_base" } parsers = { path = "../../parsers" } sources = { path = "../../sources" } +processor = { path = "../../processor" } [dev-dependencies] diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index a6f0deff88..94c8b7248b 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -18,7 +18,8 @@ extern crate log; use definitions::{Attachment, MessageStreamItem}; use dlt_core::filtering::DltFilterConfig; use parsers::dlt::DltParser; -use sources::{binary::raw::BinaryByteSource, producer::MessageProducer}; +use processor::producer::MessageProducer; +use sources::binary::raw::BinaryByteSource; use std::{ fs::File, io::{BufReader, BufWriter, Write}, diff --git a/application/apps/indexer/processor/Cargo.toml b/application/apps/indexer/processor/Cargo.toml index 8530482c1b..6ea4e562d2 100644 --- a/application/apps/indexer/processor/Cargo.toml +++ b/application/apps/indexer/processor/Cargo.toml @@ -18,6 +18,8 @@ serde = { workspace = true , features = ["derive"] } serde_json.workspace = true thiserror.workspace = true tokio-util.workspace = true +tokio = { version = "1", features = ["full"] } +tokio-stream = "0.1" uuid = { workspace = true , features = ["serde", "v4"] } bufread = { path = "../addons/bufread" } text_grep = { path = "../addons/text_grep" } diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs index 331b1fe759..c2e239a1f5 100644 --- a/application/apps/indexer/processor/src/export/mod.rs +++ b/application/apps/indexer/processor/src/export/mod.rs @@ -3,9 +3,9 @@ use std::{ path::Path, }; +use crate::producer::MessageProducer; use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; use indexer_base::config::IndexSection; -use sources::producer::MessageProducer; use thiserror::Error; use tokio_util::sync::CancellationToken; diff --git a/application/apps/indexer/processor/src/lib.rs b/application/apps/indexer/processor/src/lib.rs index 7c9e561b5f..4bd108f368 100644 --- a/application/apps/indexer/processor/src/lib.rs +++ b/application/apps/indexer/processor/src/lib.rs @@ -19,6 +19,7 @@ pub mod export; pub mod grabber; pub mod map; pub mod processor; +pub mod producer; pub mod search; pub mod text_source; diff --git a/application/apps/indexer/sources/src/producer.rs b/application/apps/indexer/processor/src/producer/mod.rs similarity index 99% rename from application/apps/indexer/sources/src/producer.rs rename to application/apps/indexer/processor/src/producer/mod.rs index 8f397b4489..3badaa6a4c 100644 --- a/application/apps/indexer/sources/src/producer.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests; +pub mod sde; + use definitions::Parser; use definitions::*; use log::warn; diff --git a/application/apps/indexer/sources/src/producer/tests/cancel_safety.rs b/application/apps/indexer/processor/src/producer/producer/tests/cancel_safety.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/cancel_safety.rs rename to application/apps/indexer/processor/src/producer/producer/tests/cancel_safety.rs diff --git a/application/apps/indexer/sources/src/producer/tests/mock_byte_source.rs b/application/apps/indexer/processor/src/producer/producer/tests/mock_byte_source.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/mock_byte_source.rs rename to application/apps/indexer/processor/src/producer/producer/tests/mock_byte_source.rs diff --git a/application/apps/indexer/sources/src/producer/tests/mock_parser.rs b/application/apps/indexer/processor/src/producer/producer/tests/mock_parser.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/mock_parser.rs rename to application/apps/indexer/processor/src/producer/producer/tests/mock_parser.rs diff --git a/application/apps/indexer/sources/src/producer/tests/mod.rs b/application/apps/indexer/processor/src/producer/producer/tests/mod.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/mod.rs rename to application/apps/indexer/processor/src/producer/producer/tests/mod.rs diff --git a/application/apps/indexer/sources/src/producer/tests/multi_parse.rs b/application/apps/indexer/processor/src/producer/producer/tests/multi_parse.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/multi_parse.rs rename to application/apps/indexer/processor/src/producer/producer/tests/multi_parse.rs diff --git a/application/apps/indexer/sources/src/producer/tests/single_parse.rs b/application/apps/indexer/processor/src/producer/producer/tests/single_parse.rs similarity index 100% rename from application/apps/indexer/sources/src/producer/tests/single_parse.rs rename to application/apps/indexer/processor/src/producer/producer/tests/single_parse.rs diff --git a/application/apps/indexer/sources/src/sde.rs b/application/apps/indexer/processor/src/producer/sde.rs similarity index 100% rename from application/apps/indexer/sources/src/sde.rs rename to application/apps/indexer/processor/src/producer/sde.rs diff --git a/application/apps/indexer/sources/src/tests/mock_read.rs b/application/apps/indexer/processor/src/producer/tests/mock_read.rs similarity index 100% rename from application/apps/indexer/sources/src/tests/mock_read.rs rename to application/apps/indexer/processor/src/producer/tests/mock_read.rs diff --git a/application/apps/indexer/sources/src/tests/mod.rs b/application/apps/indexer/processor/src/producer/tests/mod.rs similarity index 100% rename from application/apps/indexer/sources/src/tests/mod.rs rename to application/apps/indexer/processor/src/producer/tests/mod.rs diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/export_raw.rs index 3b2a900646..95a4f0bdd4 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/export_raw.rs @@ -10,13 +10,10 @@ use parsers::{ }; use plugins_host::PluginsParser; use processor::export::{ExportError, export_raw}; -use sources::{ - ByteSource, - binary::{ - pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, - raw::BinaryByteSource, - }, - producer::MessageProducer, +use processor::producer::MessageProducer; +use sources::binary::{ + pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, + raw::BinaryByteSource, }; use std::{ fs::File, diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 7643453786..6be8ffe143 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -9,7 +9,7 @@ use components::Components; use definitions::{Attachment, LogRecordOutput, LogRecordWriter}; use log::error; use parsers::Parser; -use sources::{producer::MessageProducer, sde::SdeReceiver}; +use processor::producer::{sde::*, MessageProducer}; use std::time::Instant; use stypes::NativeError; diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs index 7d328ca05d..cf247a83b2 100644 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ b/application/apps/indexer/session/src/handlers/observing/mod.rs @@ -14,11 +14,7 @@ use parsers::{ text::StringTokenizer, }; use plugins_host::PluginsParser; -use sources::{ - ByteSource, - producer::MessageProducer, - sde::{SdeMsg, SdeReceiver}, -}; +use processor::producer::{MessageProducer, sde::*}; use tokio::{ select, sync::mpsc::Receiver, diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index 69c872f267..d4dea6e1e9 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -2,9 +2,9 @@ use crate::{handlers, state::SessionStateAPI, tracker::OperationTrackerAPI}; use components::Components; use log::{debug, error, warn}; use merging::merger::FileMergeOptions; +use processor::producer::sde::{SdeReceiver, SdeSender}; use processor::search::filter::SearchFilter; use serde::Serialize; -use sources::sde::{SdeReceiver, SdeSender}; use std::{ ops::RangeInclusive, path::PathBuf, diff --git a/application/apps/indexer/session/src/tracker.rs b/application/apps/indexer/session/src/tracker.rs index 450a82ce06..f68b78c6b1 100644 --- a/application/apps/indexer/session/src/tracker.rs +++ b/application/apps/indexer/session/src/tracker.rs @@ -1,6 +1,6 @@ use crate::{operations::OperationStat, progress::ProgressProviderAPI, state::SessionStateAPI}; use log::{debug, error}; -use sources::sde::SdeSender; +use processor::producer::sde::SdeSender; use std::collections::{HashMap, hash_map::Entry}; use tokio::{ sync::{ diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 3356fa9811..74c9992598 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -11,14 +11,9 @@ extern crate lazy_static; #[macro_use] extern crate log; -#[cfg(test)] -mod tests; - pub mod binary; pub mod command; pub mod prelude; -pub mod producer; -pub mod sde; pub mod serial; pub mod socket; diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 2d8676bf5c..3354e4625b 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2657,6 +2657,8 @@ dependencies = [ "stypes", "text_grep", "thiserror 2.0.12", + "tokio", + "tokio-stream", "tokio-util", "uuid", ] From 667b1ead65b0a8d994131257528966ab899da6ea Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 19 May 2025 15:49:11 +0200 Subject: [PATCH 039/121] Change source description representation --- .../apps/indexer/components/src/lib.rs | 12 ++- .../apps/indexer/components/src/tys.rs | 33 +++++++- .../indexer/session/src/handlers/observe.rs | 9 ++- .../apps/indexer/session/src/state/api.rs | 22 ++---- .../apps/indexer/session/src/state/mod.rs | 11 +-- .../indexer/session/src/state/source_ids.rs | 18 ++--- .../indexer/stypes/bindings/miscellaneous.ts | 3 +- .../apps/indexer/stypes/bindings/observe.ts | 2 + .../indexer/stypes/src/miscellaneous/mod.rs | 4 +- .../stypes/src/miscellaneous/proptest.rs | 6 +- .../indexer/stypes/src/observe/extending.rs | 5 ++ .../apps/indexer/stypes/src/observe/mod.rs | 12 +++ .../indexer/stypes/src/observe/proptest.rs | 31 ++++++++ .../rs-bindings/src/js/converting/concat.rs | 46 ------------ .../rs-bindings/src/js/converting/merge.rs | 75 ------------------- .../rs-bindings/src/js/converting/mod.rs | 3 - .../rs-bindings/src/js/converting/source.rs | 20 ----- .../platform/types/bindings/miscellaneous.ts | 3 +- .../platform/types/bindings/observe.ts | 2 + 19 files changed, 124 insertions(+), 193 deletions(-) delete mode 100644 application/apps/rustcore/rs-bindings/src/js/converting/concat.rs delete mode 100644 application/apps/rustcore/rs-bindings/src/js/converting/merge.rs delete mode 100644 application/apps/rustcore/rs-bindings/src/js/converting/source.rs diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 96f6bbe1cb..f90da4d1e9 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -2,6 +2,7 @@ mod scheme; mod tys; use std::{collections::HashMap, fmt}; +use stypes::SessionDescriptor; use tokio_util::sync::CancellationToken; use uuid::Uuid; @@ -148,7 +149,10 @@ impl Components { Ok(descriptor.validate(origin, fields)) } - pub fn setup(&self, options: &stypes::SessionSetup) -> Result<(S, P), stypes::NativeError> { + pub fn setup( + &self, + options: &stypes::SessionSetup, + ) -> Result<(SessionDescriptor, S, P), stypes::NativeError> { let Some(Entry::Parser(parser)) = self.components.get(&options.parser.uuid) else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -163,6 +167,10 @@ impl Components { message: Some(format!("Fail to find source {}", options.source.uuid)), }); }; + let desciptor = SessionDescriptor::new( + source.bound_ident(&options.origin, &options.source.fields), + source.bound_ident(&options.origin, &options.parser.fields), + ); let Some(parser) = parser.create(&options.origin, &options.parser.fields)? else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -177,7 +185,7 @@ impl Components { message: Some(format!("Fail to init source {}", options.source.uuid)), }); }; - Ok((source, parser)) + Ok((desciptor, source, parser)) } } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 6557f8e0de..fad69f3ac3 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -135,13 +135,40 @@ pub trait MetadataDescriptor { /// * `false` - if component can not be used with origin fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool; - /// Retrieves the unique identifier of the component. + /// Returns a general description of the component as a static, standalone entity. /// - /// # Returns + /// This method provides a neutral, context-independent identifier that can be used + /// to list the component as an available option for the user. Since the actual usage + /// context is unknown at this stage, the returned description should be as general + /// as possible. /// - /// * `stypes::Ident` - The unique identifier for the component. + /// # Returns + /// A `stypes::Ident` representing the generic identity of the component. fn ident(&self) -> stypes::Ident; + /// Returns a context-specific description of the component, based on its usage origin and known fields. + /// + /// Unlike `ident`, this method considers how the component is being used, such as what + /// data source is involved and which fields are already known. For example, a `source::Raw` + /// component might normally describe itself as a "binary reader", but in the context of + /// a `SourceOrigin::File` and specific field values, it could instead describe itself + /// as a "filename" input. + /// + /// # Arguments + /// * `origin` - The `stypes::SourceOrigin` that indicates the usage context. + /// * `fields` - A slice of `stypes::Field` values representing known configuration fields. + /// + /// # Returns + /// An optional `stypes::Ident` with a contextualized description, or `None` if no + /// context-based identity is applicable. + fn bound_ident( + &self, + _origin: &stypes::SourceOrigin, + _fields: &[stypes::Field], + ) -> stypes::Ident { + self.ident() + } + /// Retrieves the type of the component. /// /// # Returns diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 6be8ffe143..7d929eebb3 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -106,9 +106,12 @@ pub async fn start_observing( components: Arc>, rx_sde: Option, ) -> OperationResult<()> { - let (source, parser) = components.setup(&options)?; - // let source_id = state.add_source(uuid).await?; - let producer = MessageProducer::new(parser, source, Writer::new(state.clone(), 0)); + let (desciptor, source, parser) = components.setup(&options)?; + let producer = MessageProducer::new( + parser, + source, + Writer::new(state.clone(), state.add_source(desciptor).await?), + ); let result = run_producer(operation_api, state, producer, None, rx_sde).await?; Ok(result) } diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index b7982713f5..b50d205d8f 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -16,7 +16,7 @@ use processor::{ }, }; use std::{collections::HashMap, fmt::Display, ops::RangeInclusive, path::PathBuf}; -use stypes::GrabbedElement; +use stypes::{GrabbedElement, SessionDescriptor}; use tokio::sync::{ mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}, oneshot, @@ -43,8 +43,7 @@ pub enum Api { FlushSessionFile(oneshot::Sender>), GetSessionFileOrigin(oneshot::Sender, stypes::NativeError>>), UpdateSession((u16, oneshot::Sender>)), - AddSource((String, oneshot::Sender)), - GetSource((String, oneshot::Sender>)), + AddSource(SessionDescriptor, oneshot::Sender), GetSourcesDefinitions(oneshot::Sender>), #[allow(clippy::large_enum_variant)] AddExecutedObserve((stypes::ObserveOptions, oneshot::Sender<()>)), @@ -213,8 +212,7 @@ impl Display for Api { Self::FlushSessionFile(_) => "FlushSessionFile", Self::GetSessionFileOrigin(_) => "GetSessionFileOrigin", Self::UpdateSession(_) => "UpdateSession", - Self::AddSource(_) => "AddSource", - Self::GetSource(_) => "GetSource", + Self::AddSource(..) => "AddSource", Self::GetSourcesDefinitions(_) => "GetSourcesDefinitions", Self::AddExecutedObserve(_) => "AddExecutedObserve", Self::GetExecutedHolder(_) => "GetExecutedHolder", @@ -482,16 +480,12 @@ impl SessionStateAPI { .await? } - pub async fn add_source(&self, uuid: &str) -> Result { - let (tx, rx) = oneshot::channel(); - self.exec_operation(Api::AddSource((uuid.to_owned(), tx)), rx) - .await - } - - pub async fn get_source(&self, uuid: &str) -> Result, stypes::NativeError> { + pub async fn add_source( + &self, + desciptor: SessionDescriptor, + ) -> Result { let (tx, rx) = oneshot::channel(); - self.exec_operation(Api::GetSource((uuid.to_owned(), tx)), rx) - .await + self.exec_operation(Api::AddSource(desciptor, tx), rx).await } pub async fn get_sources_definitions( diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index b93800c1a7..8691023b02 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -595,16 +595,9 @@ pub async fn run( stypes::NativeError::channel("Failed to respond to Api::UpdateSession") })?; } - Api::AddSource((uuid, tx_response)) => { + Api::AddSource(descriptor, tx_response) => { tx_response - .send(state.session_file.sources.add_source(uuid)) - .map_err(|_| { - stypes::NativeError::channel("Failed to respond to Api::AddSource") - })?; - } - Api::GetSource((uuid, tx_response)) => { - tx_response - .send(state.session_file.sources.get_source(uuid)) + .send(state.session_file.sources.add_source(descriptor)) .map_err(|_| { stypes::NativeError::channel("Failed to respond to Api::AddSource") })?; diff --git a/application/apps/indexer/session/src/state/source_ids.rs b/application/apps/indexer/session/src/state/source_ids.rs index 3eaeaf4106..6800f3bb2d 100644 --- a/application/apps/indexer/session/src/state/source_ids.rs +++ b/application/apps/indexer/session/src/state/source_ids.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, ops::RangeInclusive}; +use stypes::SessionDescriptor; + pub struct MappedRanges<'a> { ranges: Vec<&'a (RangeInclusive, u16)>, } @@ -22,7 +24,7 @@ impl<'a> MappedRanges<'a> { #[derive(Debug)] pub struct SourceIDs { - pub sources: HashMap, + pub sources: HashMap, pub map: Vec<(RangeInclusive, u16)>, pub recent: Option, } @@ -36,18 +38,12 @@ impl SourceIDs { } } - pub fn add_source(&mut self, alias: String) -> u16 { + pub fn add_source(&mut self, descriptor: SessionDescriptor) -> u16 { let key = self.sources.len() as u16; - self.sources.insert(key, alias); + self.sources.insert(key, descriptor); key } - pub fn get_source(&mut self, alias: String) -> Option { - self.sources - .iter() - .find_map(|(key, val)| if val == &alias { Some(*key) } else { None }) - } - pub fn is_source_same(&self, source_id: u16) -> bool { if let Some(id) = self.recent { id == source_id @@ -78,9 +74,9 @@ impl SourceIDs { pub fn get_sources_definitions(&self) -> Vec { self.sources .iter() - .map(|(id, alias)| stypes::SourceDefinition { + .map(|(id, descriptor)| stypes::SourceDefinition { id: *id, - alias: alias.to_string(), + descriptor: descriptor.clone(), }) .collect::>() } diff --git a/application/apps/indexer/stypes/bindings/miscellaneous.ts b/application/apps/indexer/stypes/bindings/miscellaneous.ts index 59ab592e7d..20b0702933 100644 --- a/application/apps/indexer/stypes/bindings/miscellaneous.ts +++ b/application/apps/indexer/stypes/bindings/miscellaneous.ts @@ -1,4 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SessionDescriptor } from "./observe"; /** * Data about indices (log entry numbers). Used to provide information about @@ -97,7 +98,7 @@ id: number, /** * The user-friendly name of the source for display purposes. */ -alias: string, }; +descriptor: SessionDescriptor, }; /** * A list of data sources. diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index a8522e0ac9..4cfc0bc801 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -121,6 +121,8 @@ send_data_delay: number, */ exclusive: boolean, }; +export type SessionDescriptor = { parser: Ident, source: Ident, }; + export type SessionSetup = { origin: SourceOrigin, parser: ComponentOptions, source: ComponentOptions, }; /** diff --git a/application/apps/indexer/stypes/src/miscellaneous/mod.rs b/application/apps/indexer/stypes/src/miscellaneous/mod.rs index df677ac90a..4a55b8021f 100644 --- a/application/apps/indexer/stypes/src/miscellaneous/mod.rs +++ b/application/apps/indexer/stypes/src/miscellaneous/mod.rs @@ -14,7 +14,7 @@ use crate::*; #[cfg_attr( all(test, feature = "test_and_gen"), derive(TS), - ts(export, export_to = "miscellaneous.ts") + ts(export, export_to = "miscellaneous.ts", type = "Map") )] pub struct MapKeyValue(pub HashMap); @@ -56,7 +56,7 @@ pub struct SourceDefinition { /// The unique identifier of the source. pub id: u16, /// The user-friendly name of the source for display purposes. - pub alias: String, + pub descriptor: SessionDescriptor, } /// A list of data sources. diff --git a/application/apps/indexer/stypes/src/miscellaneous/proptest.rs b/application/apps/indexer/stypes/src/miscellaneous/proptest.rs index e9b901fc1a..ddbe82b44b 100644 --- a/application/apps/indexer/stypes/src/miscellaneous/proptest.rs +++ b/application/apps/indexer/stypes/src/miscellaneous/proptest.rs @@ -39,13 +39,13 @@ impl Arbitrary for SourceDefinition { /// Implements the `Arbitrary` trait for `SourceDefinition` to generate random instances. /// /// # Details - /// - Generates random `id` (`u16`) and `alias` (`String`) values. + /// - Generates random `id` (`u16`) and `alias` (`SessionDescriptor`) values. type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - (any::(), any::()) - .prop_map(|(id, alias)| SourceDefinition { id, alias }) + (any::(), any::()) + .prop_map(|(id, descriptor)| SourceDefinition { id, descriptor }) .boxed() } } diff --git a/application/apps/indexer/stypes/src/observe/extending.rs b/application/apps/indexer/stypes/src/observe/extending.rs index 5e9415b68e..97e2f454b9 100644 --- a/application/apps/indexer/stypes/src/observe/extending.rs +++ b/application/apps/indexer/stypes/src/observe/extending.rs @@ -3,6 +3,11 @@ use std::net::IpAddr; use crate::*; use thiserror::Error; +impl SessionDescriptor { + pub fn new(source: Ident, parser: Ident) -> Self { + Self { parser, source } + } +} impl ObserveOptions { /// Creates a new `ObserveOptions` instance for a file. /// diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 51cb0f9f9b..2a8e68e926 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -53,6 +53,18 @@ pub struct SessionSetup { pub source: ComponentOptions, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub struct SessionDescriptor { + pub parser: Ident, + pub source: Ident, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/indexer/stypes/src/observe/proptest.rs b/application/apps/indexer/stypes/src/observe/proptest.rs index a899f9f30d..1fdf62e69a 100644 --- a/application/apps/indexer/stypes/src/observe/proptest.rs +++ b/application/apps/indexer/stypes/src/observe/proptest.rs @@ -1,6 +1,37 @@ use crate::*; use dlt_core::filtering::DltFilterConfig; +impl Arbitrary for SessionDescriptor { + /// Implements the `Arbitrary` trait for `SessionDescriptor` to generate random instances. + /// + /// # Details + /// - Generates random `id` (`u16`) and `alias` (`String`) values. + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + (any::(), any::()) + .prop_map(|(parser, source)| SessionDescriptor { parser, source }) + .boxed() + } +} + +impl Arbitrary for Ident { + /// Implements the `Arbitrary` trait for `Ident` to generate random instances. + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + (any::(), any::()) + .prop_map(|(name, desc)| Ident { + name, + desc, + uuid: Uuid::new_v4(), + }) + .boxed() + } +} + impl Arbitrary for MulticastInfo { type Parameters = (); type Strategy = BoxedStrategy; diff --git a/application/apps/rustcore/rs-bindings/src/js/converting/concat.rs b/application/apps/rustcore/rs-bindings/src/js/converting/concat.rs deleted file mode 100644 index 084bb20546..0000000000 --- a/application/apps/rustcore/rs-bindings/src/js/converting/concat.rs +++ /dev/null @@ -1,46 +0,0 @@ -// use merging::concatenator::ConcatenatorInput; -// use node_bindgen::{ -// core::{ -// val::{JsEnv, JsObject}, -// JSValue, NjError, -// }, -// sys::napi_value, -// }; -// use serde::Serialize; - -// #[derive(Serialize, Debug, Clone)] -// pub struct WrappedConcatenatorInput(ConcatenatorInput); - -// // impl WrappedConcatenatorInput { -// // pub fn as_concatenator_input(&self) -> ConcatenatorInput { -// // self.0.clone() -// // } -// // } - -// impl JSValue<'_> for WrappedConcatenatorInput { -// fn convert_to_rust(env: &JsEnv, n_value: napi_value) -> Result { -// if let Ok(js_obj) = env.convert_to_rust::(n_value) { -// let path: String = match js_obj.get_property("path") { -// Ok(Some(value)) => value.as_value()?, -// Ok(None) => { -// return Err(NjError::Other("[path] property is not found".to_owned())); -// } -// Err(e) => { -// return Err(e); -// } -// }; -// let tag: String = match js_obj.get_property("tag") { -// Ok(Some(value)) => value.as_value()?, -// Ok(None) => { -// return Err(NjError::Other("[tag] property is not found".to_owned())); -// } -// Err(e) => { -// return Err(e); -// } -// }; -// Ok(WrappedConcatenatorInput(ConcatenatorInput { path, tag })) -// } else { -// Err(NjError::Other("not valid format".to_owned())) -// } -// } -// } diff --git a/application/apps/rustcore/rs-bindings/src/js/converting/merge.rs b/application/apps/rustcore/rs-bindings/src/js/converting/merge.rs deleted file mode 100644 index 481d1cd49c..0000000000 --- a/application/apps/rustcore/rs-bindings/src/js/converting/merge.rs +++ /dev/null @@ -1,75 +0,0 @@ -use merging::merger::FileMergeOptions; -use node_bindgen::{ - core::{ - JSValue, NjError, - val::{JsEnv, JsObject}, - }, - sys::napi_value, -}; -use serde::Serialize; - -#[derive(Serialize, Debug, Clone)] -pub struct WrappedFileMergeOptions(FileMergeOptions); - -// impl WrappedFileMergeOptions { -// pub fn as_file_merge_options(&self) -> FileMergeOptions { -// self.0.clone() -// } -// } - -impl JSValue<'_> for WrappedFileMergeOptions { - fn convert_to_rust(env: &JsEnv, n_value: napi_value) -> Result { - if let Ok(js_obj) = env.convert_to_rust::(n_value) { - let path: String = match js_obj.get_property("path") { - Ok(Some(value)) => value.as_value()?, - Ok(None) => { - return Err(NjError::Other("[path] property is not found".to_owned())); - } - Err(e) => { - return Err(e); - } - }; - let tag: String = match js_obj.get_property("tag") { - Ok(Some(value)) => value.as_value()?, - Ok(None) => { - return Err(NjError::Other("[tag] property is not found".to_owned())); - } - Err(e) => { - return Err(e); - } - }; - let format: String = match js_obj.get_property("format") { - Ok(Some(value)) => value.as_value()?, - Ok(None) => { - return Err(NjError::Other("[format] property is not found".to_owned())); - } - Err(e) => { - return Err(e); - } - }; - let offset: Option = match js_obj.get_property("offset") { - Ok(Some(value)) => Some(value.as_value()?), - Ok(None) => None, - Err(e) => { - return Err(e); - } - }; - let year: Option = match js_obj.get_property("year") { - Ok(Some(value)) => Some(value.as_value()?), - Ok(None) => None, - Err(e) => { - return Err(e); - } - }; - Ok(WrappedFileMergeOptions(FileMergeOptions { - path, - offset, - year, - tag, - format, - })) - } else { - Err(NjError::Other("not valid format".to_owned())) - } - } -} diff --git a/application/apps/rustcore/rs-bindings/src/js/converting/mod.rs b/application/apps/rustcore/rs-bindings/src/js/converting/mod.rs index 3f8c5ad972..34d102d768 100644 --- a/application/apps/rustcore/rs-bindings/src/js/converting/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/converting/mod.rs @@ -1,4 +1 @@ -// pub mod concat; pub mod filter; -pub mod merge; -pub mod source; diff --git a/application/apps/rustcore/rs-bindings/src/js/converting/source.rs b/application/apps/rustcore/rs-bindings/src/js/converting/source.rs deleted file mode 100644 index 28ce5dd878..0000000000 --- a/application/apps/rustcore/rs-bindings/src/js/converting/source.rs +++ /dev/null @@ -1,20 +0,0 @@ -use node_bindgen::{ - core::{ - NjError, TryIntoJs, - val::{JsEnv, JsObject}, - }, - sys::napi_value, -}; -use serde::Serialize; - -#[derive(Serialize, Debug, Clone)] -pub struct WrappedSourceDefinition(pub stypes::SourceDefinition); - -impl TryIntoJs for WrappedSourceDefinition { - fn try_to_js(self, js_env: &JsEnv) -> Result { - let mut json = JsObject::new(*js_env, js_env.create_object()?); - json.set_property("id", js_env.create_int32(self.0.id as i32)?)?; - json.set_property("alias", js_env.create_string_utf8(&self.0.alias)?)?; - json.try_to_js(js_env) - } -} diff --git a/application/platform/types/bindings/miscellaneous.ts b/application/platform/types/bindings/miscellaneous.ts index 8b2df171f1..2c8c8c3cb0 100644 --- a/application/platform/types/bindings/miscellaneous.ts +++ b/application/platform/types/bindings/miscellaneous.ts @@ -1,4 +1,5 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SessionDescriptor } from './observe'; /** * Data about indices (log entry numbers). Used to provide information about @@ -100,7 +101,7 @@ export type SourceDefinition = { /** * The user-friendly name of the source for display purposes. */ - alias: string; + descriptor: SessionDescriptor; }; /** diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 332578f539..a2f77d9e12 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -133,6 +133,8 @@ export type SerialTransportConfig = { exclusive: boolean; }; +export type SessionDescriptor = { parser: Ident; source: Ident }; + export type SessionSetup = { origin: SourceOrigin; parser: ComponentOptions; From 9dd5c75713ce255458ff8a4427551f91c81da67f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 19 May 2025 19:32:13 +0200 Subject: [PATCH 040/121] Add documentation and cleanup --- .../apps/indexer/definitions/src/record.rs | 92 +++++++- .../apps/indexer/parsers/src/dlt/options.rs | 4 +- .../indexer/parsers/src/someip/options.rs | 4 +- .../apps/indexer/parsers/src/text/options.rs | 1 - .../indexer/session/src/components/mod.rs | 4 +- .../apps/indexer/session/src/handlers/mod.rs | 1 - .../indexer/session/src/handlers/observe.rs | 170 ++++++++++++-- .../session/src/handlers/observing/concat.rs | 95 -------- .../session/src/handlers/observing/file.rs | 132 ----------- .../session/src/handlers/observing/mod.rs | 214 ------------------ .../session/src/handlers/observing/stream.rs | 104 --------- .../indexer/sources/src/binary/pcap/legacy.rs | 3 - .../indexer/sources/src/binary/pcap/ng.rs | 3 - .../apps/indexer/sources/src/binary/raw.rs | 1 - .../indexer/sources/src/command/process.rs | 5 +- .../indexer/sources/src/serial/options.rs | 5 +- .../apps/indexer/sources/src/socket/tcp.rs | 5 +- .../apps/indexer/sources/src/socket/udp.rs | 5 +- .../indexer/stypes/src/observe/extending.rs | 9 + .../apps/indexer/stypes/src/observe/mod.rs | 2 - 20 files changed, 254 insertions(+), 605 deletions(-) delete mode 100644 application/apps/indexer/session/src/handlers/observing/concat.rs delete mode 100644 application/apps/indexer/session/src/handlers/observing/file.rs delete mode 100644 application/apps/indexer/session/src/handlers/observing/mod.rs delete mode 100644 application/apps/indexer/session/src/handlers/observing/stream.rs diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 0e565dbc0f..02dff2406f 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -1,7 +1,5 @@ -use std::borrow::Cow; - -use crate::*; use serde::Serialize; +use std::borrow::Cow; pub const COLUMN_SENTINAL: char = '\u{0004}'; @@ -38,20 +36,104 @@ impl ParseOperationResult { self.count > 0 } } + +/// Defines the shape of data that a parser can emit to a `MessageProducer`. +/// +/// # Design Philosophy +/// +/// Only the parser has full knowledge of the semantics and structure of the data +/// it processes. Therefore, the parser alone should decide how to best represent +/// that data. For example, a DLT parser naturally produces columnar data, while +/// another parser might emit raw binary content. +/// +/// Previous approaches attempted to force all parser outputs to conform to a trait +/// like `LogMessage`, requiring implementations of `to_string()` or `write()` methods. +/// This contradicts the role of the parser, which is **not** responsible for data +/// conversion or formatting. +/// +/// Instead, the parser emits data in its native structure, and it is the responsibility +/// of higher-level components (like `MessageProducer`) to format, serialize, or +/// store it as needed. For example, in the case of DLT: +/// +/// - The parser should emit structured columns. +/// - The `MessageProducer` should determine how to format those columns into lines +/// and write them to a session file. +/// +/// If different output is required (e.g., exporting DLT data as raw bytes), a different +/// parser implementation (such as `DltRaw`) should be used. pub enum LogRecordOutput<'a> { + /// A raw binary message. Raw(&'a [u8]), + + /// A borrowed UTF-8 string slice. Str(&'a str), + + /// A string wrapped in a [`Cow`] (Copy-on-Write), allowing either borrowed or owned data. Cow(Cow<'a, str>), + + /// An owned UTF-8 string. String(String), + + /// Structured columnar data. Typically used when the parser can extract + /// meaningful fields and present them as an array of strings. Columns(&'a [&'a str]), + + /// An attachment object, such as a binary blob or associated metadata. + /// These are handled separately from textual data and require special treatment + /// during output (e.g., saved as files, linked from logs, etc.). Attachment(Attachment), + + /// A compound message containing multiple outputs found during a single parsing iteration. + /// This is useful when the parser extracts several independent pieces of data at once. Multiple(Vec>), } -// Write has to be sync to keep MessageProducer cancel-safe +/// Interface for implementing a writer that handles log records. +/// +/// An instance of `LogRecordWriter` is passed to a `MessageProducer`. +/// Each time a new log record is available, the `MessageProducer` invokes the `write` method. +/// The specific behavior for writing records to the actual destination is left to the implementation. +/// +/// This allows implementors to define their own strategies for data caching, batching, and flushing. +/// When the `MessageProducer` has no more data to process, it calls `finalize`. At that point, +/// the writer should persist any buffered data, if applicable. +/// +/// **Important:** The `write` method must not be asynchronous. This is required to keep +/// the `MessageProducer` cancel-safe. +/// +/// # Examples +/// An implementation might buffer records in memory and write them in bulk +/// every few seconds, or directly write to a file, database, or network sink. pub trait LogRecordWriter { + /// Called for every new log record received from the `MessageProducer`. + /// + /// # Arguments + /// * `record` – A reference to a [`LogRecordOutput`] containing the data to write. + /// + /// # Returns + /// * `Ok(())` on success. + /// * `Err(NativeError)` if the write operation fails. fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; - /// Writes data from buffer if buffer exists + + /// Called once when the `MessageProducer` has no more data to provide. + /// + /// There is no guarantee that `write` won’t be called again after `finalize` + /// (e.g., in a file tailing scenario), but `finalize` itself is guaranteed + /// to be called only once. + /// + /// This is the opportunity to flush any buffered data and perform cleanup if necessary. + /// + /// # Returns + /// * `Ok(())` on success. + /// * `Err(NativeError)` if the finalization process fails. fn finalize(&mut self) -> Result<(), stypes::NativeError>; + + /// Returns the unique identifier of the associated data source. + /// + /// This ID can be used to correlate logs with their origin, + /// especially in multi-source scenarios. + /// + /// # Returns + /// A `u16` representing the unique source ID. fn get_id(&self) -> u16; } diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/options.rs index d8b738436b..f0f3c3b09b 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/options.rs @@ -105,9 +105,7 @@ impl MetadataDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { - return true - } + SourceOrigin::Source => return true, }; files.iter().any(|fp| { fp.extension() diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/options.rs index ac52c435de..637fd2f7ed 100644 --- a/application/apps/indexer/parsers/src/someip/options.rs +++ b/application/apps/indexer/parsers/src/someip/options.rs @@ -38,9 +38,7 @@ impl MetadataDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source | SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { - return true - } + SourceOrigin::Source => return true, }; files.iter().any(|fp| { fp.extension() diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/options.rs index 1b7e4e9fef..23dc4c6447 100644 --- a/application/apps/indexer/parsers/src/text/options.rs +++ b/application/apps/indexer/parsers/src/text/options.rs @@ -29,7 +29,6 @@ impl MetadataDescriptor for Descriptor { } SourceOrigin::Files(files) => files.iter().collect(), SourceOrigin::Source => return true, - SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => return false, }; // If at least some file doesn't exist or it's binary - do not recommend this source !files diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 8f6409af65..27328c3954 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -1,11 +1,9 @@ mod api; -use std::{collections::HashMap, io::Read}; - use api::*; - use components::{Components, LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; +use std::collections::HashMap; use tokio::{ sync::{ mpsc::{error::SendError, unbounded_channel, UnboundedReceiver, UnboundedSender}, diff --git a/application/apps/indexer/session/src/handlers/mod.rs b/application/apps/indexer/session/src/handlers/mod.rs index f0301268e7..11daf872c5 100644 --- a/application/apps/indexer/session/src/handlers/mod.rs +++ b/application/apps/indexer/session/src/handlers/mod.rs @@ -1,7 +1,6 @@ pub mod export_raw; pub mod extract; pub mod observe; -mod observing; pub mod search; pub mod search_values; pub mod sleep; diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 7d929eebb3..406fdd554f 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -1,27 +1,49 @@ -use std::{mem, sync::Arc}; - use crate::{ - handlers::observing, operations::{OperationAPI, OperationResult}, state::SessionStateAPI, + tail, }; use components::Components; -use definitions::{Attachment, LogRecordOutput, LogRecordWriter}; -use log::error; -use parsers::Parser; +use definitions::*; +use log::{trace, warn}; use processor::producer::{sde::*, MessageProducer}; +use std::sync::Arc; use std::time::Instant; -use stypes::NativeError; - -use super::observing::run_producer; +use stypes::{NativeError, SessionSetup, SourceOrigin}; +use tokio::{select, sync::mpsc::Receiver}; +/// Duration (in milliseconds) between flushes to the session file. const SEND_DURATION: u128 = 500; +/// A writer responsible for appending data to a session file. +/// +/// This writer is linked to a session file through the `SessionStateAPI`. +/// Instead of writing continuously, it accumulates updates and sends them +/// in periodic batches defined by `SEND_DURATION` (in milliseconds). +/// +/// To support this behavior, it uses two internal buffers: +/// - `buffer` accumulates textual log entries. +/// - `attachments` stores associated `Attachment` objects, which are sent +/// only after flushing the text buffer to ensure synchronization between +/// log lines and attachments. struct Writer { + /// Communication channel to the session file. state: SessionStateAPI, + + /// Text buffer for log messages. Since session files are text-based, + /// the buffered data is stored as a `String`. buffer: String, + + /// Buffer for received attachments. These are queued and sent only after + /// the buffered log messages have been flushed, preserving the logical + /// order between messages and attachments. attachments: Vec, + + /// Timestamp used to track the last flush to the session file. recent: Instant, + + /// Unique identifier for the data source. This is used on the client side + /// to visually group or distinguish data streams. id: u16, } @@ -40,7 +62,7 @@ impl Writer { impl LogRecordWriter for Writer { fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { match record { - LogRecordOutput::Raw(inner) => { + LogRecordOutput::Raw(_inner) => { // self.state // .write_session_file(0, format!("{:02X}\n", inner)) // .await?; @@ -102,16 +124,126 @@ impl LogRecordWriter for Writer { pub async fn start_observing( operation_api: OperationAPI, state: SessionStateAPI, - options: stypes::SessionSetup, + options: SessionSetup, components: Arc>, rx_sde: Option, ) -> OperationResult<()> { - let (desciptor, source, parser) = components.setup(&options)?; - let producer = MessageProducer::new( - parser, - source, - Writer::new(state.clone(), state.add_source(desciptor).await?), - ); - let result = run_producer(operation_api, state, producer, None, rx_sde).await?; - Ok(result) + match &options.origin { + SourceOrigin::File(..) => { + let (desciptor, source, parser) = components.setup(&options)?; + let producer = MessageProducer::new( + parser, + source, + Writer::new(state.clone(), state.add_source(desciptor).await?), + ); + Ok(run_producer(operation_api, state, producer, None, None).await?) + } + SourceOrigin::Source => { + let (desciptor, source, parser) = components.setup(&options)?; + let producer = MessageProducer::new( + parser, + source, + Writer::new(state.clone(), state.add_source(desciptor).await?), + ); + Ok(run_producer(operation_api, state, producer, None, rx_sde).await?) + } + SourceOrigin::Files(files) => { + // Replacement of concat feature + for file in files { + let (desciptor, source, parser) = + components.setup(&options.inherit(SourceOrigin::File(file.to_owned())))?; + let producer = MessageProducer::new( + parser, + source, + Writer::new(state.clone(), state.add_source(desciptor).await?), + ); + run_producer(operation_api.clone(), state.clone(), producer, None, None).await?; + } + Ok(Some(())) + } + } +} + +enum Next { + Parsed(usize, MessageStreamItem), + Waiting, + Sde(SdeMsg), +} + +pub async fn run_producer( + operation_api: OperationAPI, + state: SessionStateAPI, + mut producer: MessageProducer, + mut rx_tail: Option>>, + mut rx_sde: Option, +) -> OperationResult<()> { + use log::debug; + state.set_session_file(None).await?; + operation_api.processing(); + let cancel = operation_api.cancellation_token(); + let cancel_on_tail = cancel.clone(); + let started = Instant::now(); + while let Some(next) = select! { + next_from_stream = async { + producer.read_next_segment().await + .map(|(consumed, results)|Next::Parsed(consumed, results)) + .or_else(|| Some(Next::Waiting)) + } => next_from_stream, + Some(sde_msg) = async { + if let Some(rx_sde) = rx_sde.as_mut() { + rx_sde.recv().await + } else { + None + } + } => Some(Next::Sde(sde_msg)), + _ = cancel.cancelled() => None, + } { + match next { + Next::Parsed(_consumed, results) => match results { + MessageStreamItem::Parsed(_results) => { + //Just continue + } + MessageStreamItem::Done => { + state.flush_session_file().await?; + state.file_read().await?; + debug!( + "observe, message stream is done in {} ms", + started.elapsed().as_millis() + ); + } + MessageStreamItem::Skipped => { + trace!("observe: skipped a message"); + } + }, + Next::Waiting => { + if !state.is_closing() { + state.flush_session_file().await?; + } + if let Some(mut rx_tail) = rx_tail.take() { + if select! { + next_from_stream = rx_tail.recv() => { + if let Some(result) = next_from_stream { + result.is_err() + } else { + true + } + }, + _ = cancel_on_tail.cancelled() => true, + } { + break; + } + } else { + break; + } + } + Next::Sde((msg, tx_response)) => { + let sde_res = producer.sde_income(msg).await.map_err(|e| e.to_string()); + if tx_response.send(sde_res).is_err() { + warn!("Fail to send back message from source"); + } + } + } + } + debug!("listen done"); + Ok(None) } diff --git a/application/apps/indexer/session/src/handlers/observing/concat.rs b/application/apps/indexer/session/src/handlers/observing/concat.rs deleted file mode 100644 index 66545248fd..0000000000 --- a/application/apps/indexer/session/src/handlers/observing/concat.rs +++ /dev/null @@ -1,95 +0,0 @@ -use crate::{ - operations::{OperationAPI, OperationResult}, - state::SessionStateAPI, -}; -use sources::binary::{ - pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, - raw::BinaryByteSource, -}; -use std::{fs::File, path::PathBuf}; - -#[allow(clippy::type_complexity)] -pub async fn concat_files( - operation_api: OperationAPI, - state: SessionStateAPI, - files: &[(String, stypes::FileFormat, PathBuf)], - parser: &stypes::ParserType, -) -> OperationResult<()> { - todo!("Not implemented") - // for file in files.iter() { - // let (uuid, _file_type, _filename) = file; - // state.add_source(uuid).await?; - // } - // for file in files.iter() { - // let (uuid, file_type, filename) = file; - // let source_id = state.get_source(uuid).await?.ok_or(stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Io, - // message: Some(format!( - // "Cannot find source id for file {} with alias {}", - // filename.to_string_lossy(), - // uuid, - // )), - // })?; - // let input_file = File::open(filename).map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Io, - // message: Some(format!( - // "Fail open file {}: {}", - // filename.to_string_lossy(), - // e - // )), - // })?; - // match file_type { - // stypes::FileFormat::Binary => { - // super::run_source( - // operation_api.clone(), - // state.clone(), - // BinaryByteSource::new(input_file), - // source_id, - // parser, - // None, - // None, - // ) - // .await? - // } - // stypes::FileFormat::PcapLegacy => { - // super::run_source( - // operation_api.clone(), - // state.clone(), - // PcapLegacyByteSource::new(input_file)?, - // source_id, - // parser, - // None, - // None, - // ) - // .await? - // } - // stypes::FileFormat::PcapNG => { - // super::run_source( - // operation_api.clone(), - // state.clone(), - // PcapngByteSource::new(input_file)?, - // source_id, - // parser, - // None, - // None, - // ) - // .await? - // } - // stypes::FileFormat::Text => { - // super::run_source( - // operation_api.clone(), - // state.clone(), - // BinaryByteSource::new(input_file), - // source_id, - // parser, - // None, - // None, - // ) - // .await? - // } - // }; - // } - // Ok(Some(())) -} diff --git a/application/apps/indexer/session/src/handlers/observing/file.rs b/application/apps/indexer/session/src/handlers/observing/file.rs deleted file mode 100644 index 5bdcdfa43d..0000000000 --- a/application/apps/indexer/session/src/handlers/observing/file.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::{ - operations::{OperationAPI, OperationResult}, - state::SessionStateAPI, - tail, -}; -use sources::binary::{ - pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, - raw::BinaryByteSource, -}; -use std::{fs::File, path::Path}; -use tokio::{ - join, select, - sync::mpsc::{Receiver, Sender, channel}, -}; - -#[allow(clippy::type_complexity)] -pub async fn observe_file( - operation_api: OperationAPI, - state: SessionStateAPI, - uuid: &str, - file_format: &stypes::FileFormat, - filename: &Path, - parser: &stypes::ParserType, -) -> OperationResult<()> { - todo!("Not implemented") - // let source_id = state.add_source(uuid).await?; - // let (tx_tail, mut rx_tail): ( - // Sender>, - // Receiver>, - // ) = channel(1); - // match file_format { - // stypes::FileFormat::Binary => { - // let source = BinaryByteSource::new(input_file(filename)?); - // let (_, listening) = join!( - // tail::track(filename, tx_tail, operation_api.cancellation_token()), - // super::run_source( - // operation_api, - // state, - // source, - // source_id, - // parser, - // None, - // Some(rx_tail) - // ) - // ); - // listening - // } - // stypes::FileFormat::PcapLegacy => { - // let source = PcapLegacyByteSource::new(input_file(filename)?)?; - // let (_, listening) = join!( - // tail::track(filename, tx_tail, operation_api.cancellation_token()), - // super::run_source( - // operation_api, - // state, - // source, - // source_id, - // parser, - // None, - // Some(rx_tail) - // ) - // ); - // listening - // } - // stypes::FileFormat::PcapNG => { - // let source = PcapngByteSource::new(input_file(filename)?)?; - // let (_, listening) = join!( - // tail::track(filename, tx_tail, operation_api.cancellation_token()), - // super::run_source( - // operation_api, - // state, - // source, - // source_id, - // parser, - // None, - // Some(rx_tail) - // ) - // ); - // listening - // } - // stypes::FileFormat::Text => { - // state.set_session_file(Some(filename.to_path_buf())).await?; - // // Grab main file content - // state.update_session(source_id).await?; - // operation_api.processing(); - // // Confirm: main file content has been read - // state.file_read().await?; - // // Switching to tail - // let cancel = operation_api.cancellation_token(); - // let (result, tracker) = join!( - // async { - // let result = select! { - // res = async move { - // while let Some(update) = rx_tail.recv().await { - // update.map_err(|err| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(err.to_string()), - // })?; - // state.update_session(source_id).await?; - // } - // Ok(()) - // } => res, - // _ = cancel.cancelled() => Ok(()) - // }; - // result - // }, - // tail::track(filename, tx_tail, operation_api.cancellation_token()), - // ); - // result - // .and_then(|_| { - // tracker.map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(format!("Tailing error: {e}")), - // }) - // }) - // .map(|_| None) - // } - // } -} - -fn input_file(filename: &Path) -> Result { - File::open(filename).map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(format!( - "Fail open file {}: {}", - filename.to_string_lossy(), - e - )), - }) -} diff --git a/application/apps/indexer/session/src/handlers/observing/mod.rs b/application/apps/indexer/session/src/handlers/observing/mod.rs deleted file mode 100644 index cf247a83b2..0000000000 --- a/application/apps/indexer/session/src/handlers/observing/mod.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::path::PathBuf; - -use crate::{ - operations::{OperationAPI, OperationResult}, - state::SessionStateAPI, - tail, -}; -use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; -use log::{trace, warn}; -use parsers::{ - LogMessage, MessageStreamItem, ParseYield, Parser, - dlt::{DltParser, fmt::FormatOptions}, - someip::{FibexMetadata as FibexSomeipMetadata, SomeipParser}, - text::StringTokenizer, -}; -use plugins_host::PluginsParser; -use processor::producer::{MessageProducer, sde::*}; -use tokio::{ - select, - sync::mpsc::Receiver, - time::{Duration, timeout}, -}; - -enum Next { - Parsed(usize, MessageStreamItem), - Timeout, - Waiting, - Sde(SdeMsg), -} - -pub mod concat; -pub mod file; -pub mod stream; - -pub const FLUSH_TIMEOUT_IN_MS: u128 = 5000; - -// pub async fn run_source( -// operation_api: OperationAPI, -// state: SessionStateAPI, -// source: S, -// source_id: u16, -// parser: &stypes::ParserType, -// writer: W, - -// rx_sde: Option, -// rx_tail: Option>>, -// ) -> OperationResult<()> { -// let cancel = operation_api.cancellation_token(); - -// // Actual function is wrapped here in order to react on errors and cancel other tasks -// // running concurrently. -// let operation_result = run_source_intern( -// operation_api, -// state, -// source, -// source_id, -// parser, -// writer, -// rx_sde, -// rx_tail, -// ) -// .await; - -// if operation_result.is_err() && !cancel.is_cancelled() { -// cancel.cancel(); -// } - -// operation_result -// } - -/// Contains all implementation details for running the source and the producer in the session -// async fn run_source_intern( -// operation_api: OperationAPI, -// state: SessionStateAPI, -// source: S, -// source_id: u16, -// parser: &stypes::ParserType, -// writer: W, -// rx_sde: Option, -// rx_tail: Option>>, -// ) -> OperationResult<()> { -// match parser { -// stypes::ParserType::Plugin(settings) => { -// let parser = PluginsParser::initialize( -// &settings.plugin_path, -// &settings.general_settings, -// settings.plugin_configs.clone(), -// ) -// .await?; -// let producer = MessageProducer::new(parser, source, writer); -// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await -// } -// stypes::ParserType::SomeIp(settings) => { -// let someip_parser = match &settings.fibex_file_paths { -// Some(paths) => { -// SomeipParser::from_fibex_files(paths.iter().map(PathBuf::from).collect()) -// } -// None => SomeipParser::new(), -// }; -// let producer = MessageProducer::new(someip_parser, source, writer); -// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await -// } -// stypes::ParserType::Text(()) => { -// let producer = MessageProducer::new(StringTokenizer {}, source, writer); -// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await -// } -// stypes::ParserType::Dlt(settings) => { -// let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); -// let someip_metadata = settings.fibex_file_paths.as_ref().and_then(|paths| { -// FibexSomeipMetadata::from_fibex_files(paths.iter().map(PathBuf::from).collect()) -// }); -// let dlt_parser = DltParser::new( -// settings.filter_config.as_ref().map(|f| f.into()), -// // TODO: find a way to avoid clonning of MD -// settings.fibex_metadata.as_ref().map(|md| md.clone()), -// fmt_options, -// someip_metadata, -// settings.with_storage_header, -// ); -// let producer = MessageProducer::new(dlt_parser, source, writer); -// run_producer(operation_api, state, source_id, producer, rx_tail, rx_sde).await -// } -// } -// } -use std::time::Instant; - -pub async fn run_producer( - operation_api: OperationAPI, - state: SessionStateAPI, - mut producer: MessageProducer, - mut rx_tail: Option>>, - mut rx_sde: Option, -) -> OperationResult<()> { - use log::debug; - state.set_session_file(None).await?; - operation_api.processing(); - let cancel = operation_api.cancellation_token(); - let cancel_on_tail = cancel.clone(); - let started = Instant::now(); - while let Some(next) = select! { - next_from_stream = async { - match timeout(Duration::from_millis(FLUSH_TIMEOUT_IN_MS as u64), producer.read_next_segment()).await { - Ok(result) => { - if let Some((consumed, results)) = result { - Some(Next::Parsed(consumed, results)) - } else { - Some(Next::Waiting) - } - }, - Err(_) => Some(Next::Timeout), - } - } => next_from_stream, - Some(sde_msg) = async { - if let Some(rx_sde) = rx_sde.as_mut() { - rx_sde.recv().await - } else { - None - } - } => Some(Next::Sde(sde_msg)), - - _ = cancel.cancelled() => None, - } { - match next { - Next::Parsed(_consumed, results) => match results { - MessageStreamItem::Parsed(_results) => { - //Just continue - } - MessageStreamItem::Done => { - warn!( - ">>>>>>>>>>>>>>>>>> MessageStreamItem::Done in {} ms", - started.elapsed().as_millis() - ); - trace!("observe, message stream is done"); - state.flush_session_file().await?; - state.file_read().await?; - } - MessageStreamItem::Skipped => { - trace!("observe: skipped a message"); - } - }, - Next::Timeout => { - if !state.is_closing() { - state.flush_session_file().await?; - } - } - Next::Waiting => { - if let Some(mut rx_tail) = rx_tail.take() { - if select! { - next_from_stream = rx_tail.recv() => { - if let Some(result) = next_from_stream { - result.is_err() - } else { - true - } - }, - _ = cancel_on_tail.cancelled() => true, - } { - break; - } - } else { - break; - } - } - Next::Sde((msg, tx_response)) => { - let sde_res = producer.sde_income(msg).await.map_err(|e| e.to_string()); - if tx_response.send(sde_res).is_err() { - log::warn!("Fail to send back message from source"); - } - } - } - } - debug!("listen done"); - Ok(None) -} diff --git a/application/apps/indexer/session/src/handlers/observing/stream.rs b/application/apps/indexer/session/src/handlers/observing/stream.rs deleted file mode 100644 index 50b84dd4ce..0000000000 --- a/application/apps/indexer/session/src/handlers/observing/stream.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::{ - handlers::observing, - operations::{OperationAPI, OperationResult}, - state::SessionStateAPI, -}; -use sources::{ - command::process::ProcessSource, - serial::serialport::SerialSource, - socket::{tcp::TcpSource, udp::UdpSource}, -}; - -use super::SdeReceiver; - -pub async fn observe_stream( - operation_api: OperationAPI, - state: SessionStateAPI, - uuid: &str, - transport: &stypes::Transport, - parser: &stypes::ParserType, - rx_sde: Option, -) -> OperationResult<()> { - todo!("Not implemented") - // let source_id = state.add_source(uuid).await?; - // match transport { - // stypes::Transport::UDP(settings) => { - // let udp_source = UdpSource::new(&settings.bind_addr, settings.multicast.clone()) - // .await - // .map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(format!("{e}")), - // })?; - // observing::run_source( - // operation_api, - // state, - // udp_source, - // source_id, - // parser, - // rx_sde, - // None, - // ) - // .await - // } - // stypes::Transport::TCP(settings) => { - // let tcp_source = TcpSource::new(&settings.bind_addr, None, None) - // .await - // .map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(format!("{e}")), - // })?; - // observing::run_source( - // operation_api, - // state, - // tcp_source, - // source_id, - // parser, - // rx_sde, - // None, - // ) - // .await - // } - // stypes::Transport::Serial(settings) => { - // let serial_source = SerialSource::new(settings).map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(format!("{e}")), - // })?; - // observing::run_source( - // operation_api, - // state, - // serial_source, - // source_id, - // parser, - // rx_sde, - // None, - // ) - // .await - // } - // stypes::Transport::Process(settings) => { - // let process_source = ProcessSource::new( - // settings.command.clone(), - // settings.cwd.clone(), - // settings.envs.clone(), - // ) - // .await - // .map_err(|e| stypes::NativeError { - // severity: stypes::Severity::ERROR, - // kind: stypes::NativeErrorKind::Interrupted, - // message: Some(format!("{e}")), - // })?; - // observing::run_source( - // operation_api, - // state, - // process_source, - // source_id, - // parser, - // rx_sde, - // None, - // ) - // .await - // } - // } -} diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 536e72d6d3..2befbb7314 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -230,9 +230,6 @@ impl MetadataDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { - return true - } SourceOrigin::Source => { return false; } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index ec3ac2b00d..fdac35a98c 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -232,9 +232,6 @@ impl MetadataDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => { - return true - } SourceOrigin::Source => { return false; } diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 94c2f795b3..32e90b31ef 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -161,7 +161,6 @@ impl MetadataDescriptor for Descriptor { vec![filepath] } SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Folder(..) | SourceOrigin::Folders(..) => return false, SourceOrigin::Source => { return false; } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 52b48c55bc..7ecbcd0b47 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -248,10 +248,7 @@ impl ComponentDescriptor for Descriptor { impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { - SourceOrigin::File(..) - | SourceOrigin::Files(..) - | SourceOrigin::Folder(..) - | SourceOrigin::Folders(..) => false, + SourceOrigin::File(..) | SourceOrigin::Files(..) => false, SourceOrigin::Source => true, } } diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 696c40018b..aa32129a43 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -41,10 +41,7 @@ impl ComponentDescriptor for Descriptor { impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { - SourceOrigin::File(..) - | SourceOrigin::Files(..) - | SourceOrigin::Folder(..) - | SourceOrigin::Folders(..) => false, + SourceOrigin::File(..) | SourceOrigin::Files(..) => false, SourceOrigin::Source => true, } } diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index d44d1afe35..b964410942 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -189,10 +189,7 @@ impl ComponentDescriptor for Descriptor { impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { - SourceOrigin::File(..) - | SourceOrigin::Files(..) - | SourceOrigin::Folder(..) - | SourceOrigin::Folders(..) => false, + SourceOrigin::File(..) | SourceOrigin::Files(..) => false, SourceOrigin::Source => true, } } diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 7ed7fd1481..d31b06917a 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -152,10 +152,7 @@ impl ComponentDescriptor for Descriptor { impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { - SourceOrigin::File(..) - | SourceOrigin::Files(..) - | SourceOrigin::Folder(..) - | SourceOrigin::Folders(..) => false, + SourceOrigin::File(..) | SourceOrigin::Files(..) => false, SourceOrigin::Source => true, } } diff --git a/application/apps/indexer/stypes/src/observe/extending.rs b/application/apps/indexer/stypes/src/observe/extending.rs index 97e2f454b9..78bd3a942e 100644 --- a/application/apps/indexer/stypes/src/observe/extending.rs +++ b/application/apps/indexer/stypes/src/observe/extending.rs @@ -3,6 +3,15 @@ use std::net::IpAddr; use crate::*; use thiserror::Error; +impl SessionSetup { + pub fn inherit(&self, origin: SourceOrigin) -> Self { + Self { + origin, + parser: self.parser.clone(), + source: self.source.clone(), + } + } +} impl SessionDescriptor { pub fn new(source: Ident, parser: Ident) -> Self { Self { parser, source } diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 2a8e68e926..fbf2055a11 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -23,8 +23,6 @@ use dlt_core::filtering::DltFilterConfig; pub enum SourceOrigin { File(PathBuf), Files(Vec), - Folder(PathBuf), - Folders(Vec), Source, } From 74c7897a49580af3e432a2975818c181e4dd73a9 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 20 May 2025 11:27:03 +0200 Subject: [PATCH 041/121] Upgrade export function --- .../apps/indexer/components/src/lib.rs | 12 +- .../apps/indexer/components/src/tys.rs | 9 +- .../apps/indexer/definitions/src/parser.rs | 28 -- .../src/dlt/{options.rs => descriptor.rs} | 4 +- .../apps/indexer/parsers/src/dlt/mod.rs | 166 +--------- .../indexer/parsers/src/dlt/raw/descriptor.rs | 107 ++++++ .../apps/indexer/parsers/src/dlt/raw/mod.rs | 49 +++ application/apps/indexer/parsers/src/lib.rs | 10 +- .../src/someip/{options.rs => descriptor.rs} | 0 .../apps/indexer/parsers/src/someip/mod.rs | 2 +- .../src/text/{options.rs => descriptor.rs} | 0 .../apps/indexer/parsers/src/text/mod.rs | 2 +- .../apps/indexer/processor/src/export/mod.rs | 2 +- .../indexer/processor/src/producer/mod.rs | 8 +- .../session/src/handlers/export_raw.rs | 312 ++++++++---------- .../indexer/session/src/handlers/observe.rs | 37 +-- .../apps/indexer/session/src/operations.rs | 16 +- .../apps/indexer/session/src/session.rs | 14 +- .../apps/indexer/stypes/src/observe/mod.rs | 22 ++ 19 files changed, 388 insertions(+), 412 deletions(-) rename application/apps/indexer/parsers/src/dlt/{options.rs => descriptor.rs} (99%) create mode 100644 application/apps/indexer/parsers/src/dlt/raw/descriptor.rs create mode 100644 application/apps/indexer/parsers/src/dlt/raw/mod.rs rename application/apps/indexer/parsers/src/someip/{options.rs => descriptor.rs} (100%) rename application/apps/indexer/parsers/src/text/{options.rs => descriptor.rs} (100%) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index f90da4d1e9..a0eaba6e88 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -57,11 +57,21 @@ impl Components { target: &stypes::ComponentType, origin: stypes::SourceOrigin, ) -> Result, stypes::NativeError> { - // TODO: "ask" source/parser based on origin Ok(self .components .iter() .filter_map(|(_, desc)| match target { + stypes::ComponentType::RawParser => { + if let Entry::RawParser(desc) = desc { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + } else { + None + } + } stypes::ComponentType::Parser => { if let Entry::Parser(desc) = desc { if desc.is_compatible(&origin) { diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index fad69f3ac3..9a1a691121 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -56,6 +56,7 @@ pub type StaticFieldsResult = Result, stypes::NativeError pub type LazyFieldsTask = Pin + Send>>; pub enum Entry { + RawParser(Box>), Parser(Box>), Source(Box>), } @@ -65,25 +66,28 @@ impl MetadataDescriptor for Entry { match self { Self::Source(inner) => inner.fields_getter(origin), Self::Parser(inner) => inner.fields_getter(origin), + Self::RawParser(inner) => inner.fields_getter(origin), } } - fn ident(&self) -> stypes::Ident { match self { Self::Source(inner) => inner.ident(), Self::Parser(inner) => inner.ident(), + Self::RawParser(inner) => inner.ident(), } } fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { match self { Self::Source(inner) => inner.is_compatible(origin), Self::Parser(inner) => inner.is_compatible(origin), + Self::RawParser(inner) => inner.is_compatible(origin), } } fn is_ty(&self, ty: &stypes::ComponentType) -> bool { match self { Self::Source(inner) => inner.is_ty(ty), Self::Parser(inner) => inner.is_ty(ty), + Self::RawParser(inner) => inner.is_ty(ty), } } fn lazy_fields_getter( @@ -94,12 +98,14 @@ impl MetadataDescriptor for Entry { match self { Self::Source(inner) => inner.lazy_fields_getter(origin, cancel), Self::Parser(inner) => inner.lazy_fields_getter(origin, cancel), + Self::RawParser(inner) => inner.lazy_fields_getter(origin, cancel), } } fn ty(&self) -> stypes::ComponentType { match self { Self::Source(inner) => inner.ty(), Self::Parser(inner) => inner.ty(), + Self::RawParser(inner) => inner.ty(), } } fn validate( @@ -110,6 +116,7 @@ impl MetadataDescriptor for Entry { match self { Self::Source(inner) => inner.validate(origin, fields), Self::Parser(inner) => inner.validate(origin, fields), + Self::RawParser(inner) => inner.validate(origin, fields), } } } diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index a44ba66de9..5f443859f4 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -130,31 +130,3 @@ pub enum MessageStreamItem { Skipped, Done, } - -// fn parse_all( -// input: &[u8], -// timestamp: Option, -// min_bytes_count: usize, -// mut parse_fn: F, -// ) -> Result>)> + use<'_, F, T>, Error> -// where -// F: FnMut(&[u8], Option) -> Result<(usize, Option>), Error>, -// { -// let mut slice = input; - -// // return early if function errors on first parse call. -// let first_res = parse_fn(slice, timestamp)?; - -// // Otherwise keep parsing and stop on first error, returning the parsed items at the end. -// let iter = iter::successors(Some(first_res), move |(consumed, _res)| { -// slice = &slice[*consumed..]; - -// if slice.len() < min_bytes_count { -// return None; -// } - -// parse_fn(slice, timestamp).ok() -// }); - -// Ok(iter) -// } diff --git a/application/apps/indexer/parsers/src/dlt/options.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs similarity index 99% rename from application/apps/indexer/parsers/src/dlt/options.rs rename to application/apps/indexer/parsers/src/dlt/descriptor.rs index f0f3c3b09b..1bc9e9bc8d 100644 --- a/application/apps/indexer/parsers/src/dlt/options.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -93,11 +93,11 @@ impl ComponentDescriptor for Descriptor { None, someip_metadata, // If it's source - no storage header expected - // !matches!(origin, SourceOrigin::Source), - true, + !matches!(origin, SourceOrigin::Source), )))) } } + impl MetadataDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index ec86e56588..b52853d652 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -1,14 +1,14 @@ pub mod attachment; +pub mod descriptor; pub mod fmt; -pub mod options; - -use std::io; +pub mod raw; +use self::{attachment::FtScanner, fmt::FormatOptions}; use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; -// use byteorder::{BigEndian, WriteBytesExt}; +use definitions::*; use dlt_core::{ dlt, - parse::{DltParseError, dlt_consume_msg, dlt_message}, + parse::{DltParseError, dlt_message}, }; pub use dlt_core::{ dlt::LogLevel, @@ -16,26 +16,10 @@ pub use dlt_core::{ filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; use serde::Serialize; -use stypes::NativeError; -// use std::{io::Write, ops::Range, sync::Arc}; - -use self::{attachment::FtScanner, fmt::FormatOptions}; -use definitions::*; /// The most likely minimal bytes count needed to parse a DLT message. const MIN_MSG_LEN: usize = 20; -// #[derive(Debug, Serialize)] -// pub struct RangeMessage { -// pub range: Range, -// } - -// impl std::fmt::Display for RangeMessage { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// write!(f, "({:?})", self.range) -// } -// } - #[derive(Debug, Serialize)] pub struct RawMessage { pub content: Vec, @@ -47,15 +31,6 @@ impl std::fmt::Display for RawMessage { } } -// impl LogMessage for RangeMessage { -// /// A RangeMessage only has range information and cannot serialize to bytes -// fn to_writer(&self, writer: &mut W) -> Result { -// writer.write_u64::(self.range.start as u64)?; -// writer.write_u64::(self.range.end as u64)?; -// Ok(8 + 8) -// } -// } - #[derive(Default)] pub struct DltParser { pub filter_config: Option, @@ -67,30 +42,6 @@ pub struct DltParser { offset: usize, } -pub struct DltRawParser { - pub with_storage_header: bool, -} - -impl DltRawParser { - pub fn new(with_storage_header: bool) -> Self { - Self { - with_storage_header, - } - } - - fn parse_item<'a>( - &mut self, - input: &'a [u8], - _timestamp: Option, - ) -> Result<(usize, Option<&'a [u8]>), ParserError> { - let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; - let msg = consumed.map(|c| &input[0..c as usize]); - let total_consumed = input.len() - rest.len(); - let item = (total_consumed, msg.map(|m| m.into())); - Ok(item) - } -} - impl DltParser { pub fn new( filter_config: Option, @@ -197,110 +148,3 @@ impl Parser for DltParser { MIN_MSG_LEN } } - -// impl Parser for DltParser { -// async fn parse( -// &mut self, -// input: &[u8], -// timestamp: Option, -// writer: &mut W, -// ) -> Result { -// async fn write( -// data: Option<(FormattableMessage<'_>, Option)>, -// writer: &mut W, -// ) -> Result { -// if let Some((msg, attachment)) = data { -// writer.write(LogRecordOutput::Str(&msg.to_string())).await?; -// if let Some(attachment) = attachment { -// writer -// .write(LogRecordOutput::Attachment(attachment)) -// .await?; -// Ok(2) -// } else { -// Ok(1) -// } -// } else { -// Ok(0) -// } -// } -// let mut slice = input; -// // Parsing of the first item should be sensentive to errors -// let mut total_consumed = 0; -// let (mut recently_consumed, data) = self.parse_item(slice, timestamp)?; -// let mut count = write(data, writer).await?; -// total_consumed += recently_consumed; -// // Continue parsing until end (or error) -// loop { -// println!( -// ">>>>>>>>>>>>>>>>>> DLT parser: Slice: {recently_consumed} / {}", -// slice.len() -// ); -// slice = &slice[recently_consumed..]; - -// if slice.len() < MIN_MSG_LEN { -// break; -// } - -// match self.parse_item(slice, timestamp) { -// Ok((consumed, data)) => { -// recently_consumed = consumed; -// total_consumed += recently_consumed; -// println!( -// ">>>>>>>>>>>>>>>>>> DLT parser: consumed: {consumed}/{total_consumed}" -// ); -// count += write(data, writer).await?; -// if recently_consumed == 0 { -// println!(">>>>>>>>>>>>>>>>>> DLT parser: 0004"); -// break; -// } -// } -// Err(_) => break, -// } -// } -// println!(">>>>>>>>>>>>>>>>>> DLT parser: 0005"); -// Ok(ParseOperationResult::new(total_consumed, count)) -// } -// } - -impl Parser for DltRawParser { - fn parse<'a>( - &mut self, - input: &'a [u8], - timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { - let (consumed, data) = self.parse_item(input, timestamp)?; - Ok((consumed, data.map(LogRecordOutput::Raw))) - } - fn min_msg_len(&self) -> usize { - MIN_MSG_LEN - } -} - -// impl Parser for P -// where -// P: SingleParser, -// { -// fn parse<'a>( -// &mut self, -// input: &[u8], -// timestamp: Option, -// ) -> Result)>, ParserError> { -// let mut slice = input; - -// // return early if function errors on first parse call. -// let first_res = self.parse_item(slice, timestamp)?; - -// // Otherwise keep parsing and stop on first error, returning the parsed items at the end. -// let iter = iter::successors(Some(first_res), move |(consumed, _res)| { -// slice = &slice[*consumed..]; - -// if slice.len() < P::MIN_MSG_LEN { -// return None; -// } - -// self.parse_item(slice, timestamp).ok() -// }); - -// Ok(iter) -// } -// } diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs new file mode 100644 index 0000000000..472b4c3e3a --- /dev/null +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -0,0 +1,107 @@ +use super::*; +use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; +use dlt_core::{ + read::DltMessageReader, + statistics::{ + collect_statistics, + common::{StatisticInfo, StatisticInfoCollector}, + }, +}; +use std::fmt; +use std::fs::File; +use std::{collections::HashMap, path::PathBuf}; +use stypes::{ + FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, Value, + ValueInput, +}; +use tokio_util::sync::CancellationToken; + +const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +]); + +const FIELD_LOG_LEVEL: &str = "DLT_PARSER_FIELD_LOG_LEVEL"; +const FIELD_FIBEX_FILES: &str = "DLT_PARSER_FIELD_FIBEX_FILES"; +const FIELD_STATISTICS: &str = "DLT_PARSER_FIELD_STATISTICS"; +const FIELD_TZ: &str = "DLT_PARSER_FIELD_TIMEZONE"; + +#[derive(Default)] +pub struct Descriptor {} + +enum StatFields { + AppIds, + ContextIds, + EcuIds, +} + +impl fmt::Display for StatFields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::AppIds => "app_ids", + Self::ContextIds => "context_ids", + Self::EcuIds => "ecu_ids", + } + ) + } +} + +fn to_native_cfg_err(msg: S) -> NativeError { + NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Configuration, + message: Some(msg.to_string()), + } +} + +impl ComponentDescriptor for Descriptor { + fn create( + &self, + origin: &SourceOrigin, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(Some(crate::Parser::DltRaw(DltRawParser::new(!matches!( + origin, + SourceOrigin::Source + ))))) + } +} + +impl MetadataDescriptor for Descriptor { + fn is_compatible(&self, origin: &SourceOrigin) -> bool { + let files = match origin { + SourceOrigin::File(filepath) => { + vec![filepath] + } + SourceOrigin::Files(files) => files.iter().collect(), + SourceOrigin::Source => return true, + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "dlt") + .unwrap_or_default() + }) + } + fn fields_getter(&self, _origin: &SourceOrigin) -> components::FieldsResult { + Ok(Vec::new()) + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("DLT Export Parser"), + desc: String::from("DLT Export Parser"), + uuid: DLT_PARSER_UUID, + } + } + fn lazy_fields_getter( + &self, + _origin: SourceOrigin, + _cancel: CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async move { Ok(Vec::new()) }) + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::RawParser + } +} diff --git a/application/apps/indexer/parsers/src/dlt/raw/mod.rs b/application/apps/indexer/parsers/src/dlt/raw/mod.rs new file mode 100644 index 0000000000..65eac86e73 --- /dev/null +++ b/application/apps/indexer/parsers/src/dlt/raw/mod.rs @@ -0,0 +1,49 @@ +pub mod descriptor; + +use super::{map_dlt_err, MIN_MSG_LEN}; +use definitions::*; +pub use descriptor::*; +use dlt_core::parse::dlt_consume_msg; +pub use dlt_core::{ + dlt::LogLevel, + fibex::{gather_fibex_data, FibexConfig, FibexMetadata as FibexDltMetadata}, + filtering::{DltFilterConfig, ProcessedDltFilterConfig}, +}; + +pub struct DltRawParser { + pub with_storage_header: bool, +} + +impl DltRawParser { + pub fn new(with_storage_header: bool) -> Self { + Self { + with_storage_header, + } + } + + fn parse_item<'a>( + &mut self, + input: &'a [u8], + _timestamp: Option, + ) -> Result<(usize, Option<&'a [u8]>), ParserError> { + let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; + let msg = consumed.map(|c| &input[0..c as usize]); + let total_consumed = input.len() - rest.len(); + let item = (total_consumed, msg.map(|m| m.into())); + Ok(item) + } +} + +impl Parser for DltRawParser { + fn parse<'a>( + &mut self, + input: &'a [u8], + timestamp: Option, + ) -> Result<(usize, Option>), ParserError> { + let (consumed, data) = self.parse_item(input, timestamp)?; + Ok((consumed, data.map(LogRecordOutput::Raw))) + } + fn min_msg_len(&self) -> usize { + MIN_MSG_LEN + } +} diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index a90f38edf4..e1b240ecec 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -8,6 +8,7 @@ pub mod someip; pub mod text; pub enum Parser { + DltRaw(dlt::raw::DltRawParser), Dlt(dlt::DltParser), SomeIp(someip::SomeipParser), Text(text::StringTokenizer), @@ -27,6 +28,7 @@ impl definitions::Parser for Parser { ) -> Result<(usize, Option>), ParserError> { match self { Self::Dlt(inst) => inst.parse(input, timestamp), + Self::DltRaw(inst) => inst.parse(input, timestamp), Self::SomeIp(inst) => inst.parse(input, timestamp), Self::Text(inst) => inst.parse(input, timestamp), } @@ -34,6 +36,7 @@ impl definitions::Parser for Parser { fn min_msg_len(&self) -> usize { match self { Self::Dlt(inst) => inst.min_msg_len(), + Self::DltRaw(inst) => inst.min_msg_len(), Self::SomeIp(inst) => inst.min_msg_len(), Self::Text(inst) => inst.min_msg_len(), } @@ -41,8 +44,9 @@ impl definitions::Parser for Parser { } pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { - components.add_parser(dlt::options::Descriptor::default())?; - components.add_parser(someip::options::Descriptor::default())?; - components.add_parser(text::options::Descriptor::default())?; + components.add_parser(dlt::descriptor::Descriptor::default())?; + components.add_parser(dlt::raw::descriptor::Descriptor::default())?; + components.add_parser(someip::descriptor::Descriptor::default())?; + components.add_parser(text::descriptor::Descriptor::default())?; Ok(()) } diff --git a/application/apps/indexer/parsers/src/someip/options.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs similarity index 100% rename from application/apps/indexer/parsers/src/someip/options.rs rename to application/apps/indexer/parsers/src/someip/descriptor.rs diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index aa789ab38d..466fef09d7 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -1,4 +1,4 @@ -pub mod options; +pub mod descriptor; use definitions::*; use std::{ borrow::Cow, diff --git a/application/apps/indexer/parsers/src/text/options.rs b/application/apps/indexer/parsers/src/text/descriptor.rs similarity index 100% rename from application/apps/indexer/parsers/src/text/options.rs rename to application/apps/indexer/parsers/src/text/descriptor.rs diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 191995943e..38bcd40613 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,4 +1,4 @@ -pub mod options; +pub mod descriptor; use std::{borrow::Cow, io}; use definitions::*; diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs index c2e239a1f5..9cc669a9fb 100644 --- a/application/apps/indexer/processor/src/export/mod.rs +++ b/application/apps/indexer/processor/src/export/mod.rs @@ -40,7 +40,7 @@ pub enum ExportError { /// # Errors /// In case of cancellation will return ExportError::Cancelled pub async fn export_raw( - mut producer: MessageProducer, + mut producer: MessageProducer<'_, P, D, W>, destination_path: &Path, sections: &Vec, read_to_end: bool, diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 3badaa6a4c..f737c29f7d 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -11,7 +11,7 @@ use log::warn; const INITIAL_PARSE_ERROR_LIMIT: usize = 1024; #[derive(Debug)] -pub struct MessageProducer +pub struct MessageProducer<'a, P, D, W> where P: Parser, D: ByteSource, @@ -25,12 +25,12 @@ where total_skipped: usize, done: bool, buffer: Vec<(usize, MessageStreamItem)>, - writer: W, + writer: &'a mut W, } -impl MessageProducer { +impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, W> { /// create a new producer by plugging into a byte source - pub fn new(parser: P, source: D, writer: W) -> Self { + pub fn new(parser: P, source: D, writer: &'a mut W) -> Self { MessageProducer { byte_source: source, parser, diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/export_raw.rs index 95a4f0bdd4..67c6b6e17e 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/export_raw.rs @@ -1,200 +1,154 @@ -use crate::{operations::OperationResult, state::SessionStateAPI}; -use definitions::{ByteSource, LogRecordWriter, Parser}; -use indexer_base::config::IndexSection; +use crate::operations::{OperationAPI, OperationResult}; +use components::Components; +use definitions::{ByteSource, LogRecordOutput, LogRecordWriter, Parser}; use log::debug; -use parsers::{ - LogMessage, Parser, - dlt::{DltParser, fmt::FormatOptions}, - someip::SomeipParser, - text::StringTokenizer, -}; -use plugins_host::PluginsParser; -use processor::export::{ExportError, export_raw}; use processor::producer::MessageProducer; -use sources::binary::{ - pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, - raw::BinaryByteSource, -}; + use std::{ fs::File, + io::{self, BufWriter, Write}, path::{Path, PathBuf}, + sync::Arc, }; -use tokio_util::sync::CancellationToken; +use stypes::{NativeError, SessionSetup, SourceOrigin}; +use tokio::select; -pub async fn execute_export( - cancel: &CancellationToken, - state: SessionStateAPI, - out_path: PathBuf, +struct ExportWriter { + buffer: BufWriter, + index: usize, ranges: Vec>, -) -> OperationResult { - debug!("RUST: ExportRaw operation is requested"); - let observed = state.get_executed_holder().await?; - if !observed.is_file_based_export_possible() { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(String::from( - "For current collection of observing operation raw export isn't possible.", - )), - }); - } - let mut indexes = ranges - .iter() - .map(IndexSection::from) - .collect::>(); - let count = observed.get_files().len(); - for (i, (parser, file_format, filename)) in observed.get_files().iter().enumerate() { - if indexes.is_empty() { - break; - } - let read = if let Some(read) = assing_source( - filename, - &out_path, - parser, - file_format, - &indexes, - i != (count - 1), - cancel, - ) - .await? - { - read +} + +impl ExportWriter { + pub fn new>( + filename: P, + ranges: Vec>, + ) -> io::Result { + let filename = filename.as_ref(); + let out_file = if filename.exists() { + std::fs::OpenOptions::new().append(true).open(filename)? } else { - return Ok(Some(false)); + std::fs::File::create(filename)? }; - indexes.iter_mut().for_each(|index| index.left(read)); - indexes = indexes - .into_iter() - .filter(|index| !index.is_empty()) - .collect::>(); + Ok(Self { + buffer: BufWriter::new(out_file), + index: 0, + ranges, + }) } - Ok(Some(true)) } -async fn assing_source( - src: &PathBuf, - dest: &Path, - parser: &stypes::ParserType, - file_format: &stypes::FileFormat, - sections: &Vec, - read_to_end: bool, - cancel: &CancellationToken, -) -> Result, stypes::NativeError> { - let reader = File::open(src).map_err(|e| stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(format!("Fail open file {}: {}", src.to_string_lossy(), e)), - })?; - match file_format { - stypes::FileFormat::Binary | stypes::FileFormat::Text => { - export( - dest, - parser, - BinaryByteSource::new(reader), - sections, - read_to_end, - cancel, - ) - .await +impl LogRecordWriter for ExportWriter { + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + if !self.ranges.is_empty() { + // TODO: we can optimize index search + if !self + .ranges + .iter() + .any(|range| range.contains(&(self.index as u64))) + { + // Skip record because it's not in a range + self.index += 1; + return Ok(()); + } } - stypes::FileFormat::PcapNG => { - export( - dest, - parser, - PcapngByteSource::new(reader)?, - sections, - read_to_end, - cancel, - ) - .await - } - stypes::FileFormat::PcapLegacy => { - export( - dest, - parser, - PcapLegacyByteSource::new(reader)?, - sections, - read_to_end, - cancel, - ) - .await + self.index += 1; + match record { + LogRecordOutput::Raw(inner) => { + self.buffer.write_all(inner)?; + } + LogRecordOutput::Cow(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::String(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Str(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Columns(inner) => { + self.buffer.write_all( + inner + .join(&definitions::COLUMN_SENTINAL.to_string()) + .as_bytes(), + )?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Multiple(inner) => { + for record in inner.into_iter() { + self.write(record)?; + } + } + LogRecordOutput::Attachment(_) => { + // TODO: report error + } } + Ok(()) + } + fn finalize(&mut self) -> Result<(), stypes::NativeError> { + self.buffer.flush()?; + Ok(()) + } + fn get_id(&self) -> u16 { + 0 } } -async fn export( - dest: &Path, - parser: &stypes::ParserType, - source: S, - sections: &Vec, - read_to_end: bool, - cancel: &CancellationToken, -) -> Result, stypes::NativeError> { - todo!("Not implemented"); - // match parser { - // stypes::ParserType::Plugin(settings) => { - // let parser = PluginsParser::initialize( - // &settings.plugin_path, - // &settings.general_settings, - // settings.plugin_configs.clone(), - // ) - // .await?; - // let producer = MessageProducer::new(parser, source); - // export_runner(producer, dest, sections, read_to_end, false, cancel).await - // } - // stypes::ParserType::SomeIp(settings) => { - // let parser = if let Some(files) = settings.fibex_file_paths.as_ref() { - // SomeipParser::from_fibex_files(files.iter().map(PathBuf::from).collect()) - // } else { - // SomeipParser::new() - // }; - // let producer = MessageProducer::new(parser, source); - // export_runner(producer, dest, sections, read_to_end, false, cancel).await - // } - // stypes::ParserType::Dlt(settings) => { - // let fmt_options = Some(FormatOptions::from(settings.tz.as_ref())); - // let parser = DltParser::new( - // settings.filter_config.as_ref().map(|f| f.into()), - // // TODO: find a way to avoid clonning of MD - // settings.fibex_metadata.as_ref().map(|md| md.clone()), - // fmt_options, - // None, - // settings.with_storage_header, - // ); - // let producer = MessageProducer::new(parser, source); - // export_runner(producer, dest, sections, read_to_end, false, cancel).await - // } - // stypes::ParserType::Text(()) => { - // let producer = MessageProducer::new(StringTokenizer {}, source); - // export_runner(producer, dest, sections, read_to_end, true, cancel).await - // } - // } +pub async fn export( + operation_api: OperationAPI, + options: SessionSetup, + components: Arc>, + out_path: PathBuf, + ranges: Vec>, +) -> OperationResult { + match &options.origin { + SourceOrigin::File(..) => { + let (_, source, parser) = components.setup(&options)?; + let mut writer = ExportWriter::new(&out_path, ranges)?; + let producer = MessageProducer::new(parser, source, &mut writer); + Ok(run_producer(operation_api, producer).await?) + } + SourceOrigin::Source => { + let (_, source, parser) = components.setup(&options)?; + let mut writer = ExportWriter::new(&out_path, ranges)?; + let producer = MessageProducer::new(parser, source, &mut writer); + Ok(run_producer(operation_api, producer).await?) + } + SourceOrigin::Files(files) => { + // We are creating one single writer for all files to keep tracking ranges and current index + let mut writer = ExportWriter::new(&out_path, ranges)?; + for file in files { + if operation_api.cancellation_token().is_cancelled() { + return Ok(Some(false)); + } + let (_, source, parser) = + components.setup(&options.inherit(SourceOrigin::File(file.to_owned())))?; + let producer = MessageProducer::new(parser, source, &mut writer); + run_producer(operation_api.clone(), producer).await?; + } + Ok(Some(true)) + } + } } -pub async fn export_runner( - producer: MessageProducer, - dest: &Path, - sections: &Vec, - read_to_end: bool, - text_file: bool, - cancel: &CancellationToken, -) -> Result, stypes::NativeError> -where - P: Parser, - D: ByteSource, - W: LogRecordWriter, -{ - export_raw(producer, dest, sections, read_to_end, text_file, cancel) - .await - .map_or_else( - |err| match err { - ExportError::Cancelled => Ok(None), - _ => Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::UnsupportedFileType, - message: Some(format!("{err}")), - }), - }, - |read| Ok(Some(read)), - ) +pub async fn run_producer( + operation_api: OperationAPI, + mut producer: MessageProducer<'_, P, S, W>, +) -> OperationResult { + operation_api.processing(); + let cancel = operation_api.cancellation_token(); + while let Some(..) = select! { + next_from_stream = producer.read_next_segment() => next_from_stream, + _ = async { + cancel.cancelled().await; + debug!("exporting operation has been cancelled"); + } => None, + } { + // Do nothing. Writing happens on MessageProducer level + } + debug!("exporting is done"); + Ok(Some(!cancel.is_cancelled())) } diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 406fdd554f..9296fed204 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -62,10 +62,16 @@ impl Writer { impl LogRecordWriter for Writer { fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { match record { - LogRecordOutput::Raw(_inner) => { - // self.state - // .write_session_file(0, format!("{:02X}\n", inner)) - // .await?; + LogRecordOutput::Raw(inner) => { + // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs + // because during observe we do not expect raw data + self.buffer.push_str( + &inner + .iter() + .map(|b| format!("{:02X}", b)) + .collect::(), + ); + self.buffer.push('\n'); } LogRecordOutput::Cow(inner) => { self.buffer.push_str(&inner); @@ -131,20 +137,14 @@ pub async fn start_observing( match &options.origin { SourceOrigin::File(..) => { let (desciptor, source, parser) = components.setup(&options)?; - let producer = MessageProducer::new( - parser, - source, - Writer::new(state.clone(), state.add_source(desciptor).await?), - ); + let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); Ok(run_producer(operation_api, state, producer, None, None).await?) } SourceOrigin::Source => { let (desciptor, source, parser) = components.setup(&options)?; - let producer = MessageProducer::new( - parser, - source, - Writer::new(state.clone(), state.add_source(desciptor).await?), - ); + let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); Ok(run_producer(operation_api, state, producer, None, rx_sde).await?) } SourceOrigin::Files(files) => { @@ -152,11 +152,8 @@ pub async fn start_observing( for file in files { let (desciptor, source, parser) = components.setup(&options.inherit(SourceOrigin::File(file.to_owned())))?; - let producer = MessageProducer::new( - parser, - source, - Writer::new(state.clone(), state.add_source(desciptor).await?), - ); + let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); run_producer(operation_api.clone(), state.clone(), producer, None, None).await?; } Ok(Some(())) @@ -173,7 +170,7 @@ enum Next { pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, - mut producer: MessageProducer, + mut producer: MessageProducer<'_, P, S, W>, mut rx_tail: Option>>, mut rx_sde: Option, ) -> OperationResult<()> { diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index d4dea6e1e9..f2abd1a4f1 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -111,6 +111,8 @@ pub enum OperationKind { delimiter: Option, }, ExportRaw { + setup: stypes::SessionSetup, + components: Arc>, out_path: PathBuf, ranges: Vec>, }, @@ -364,11 +366,17 @@ impl OperationAPI { ) .await; } - OperationKind::ExportRaw { out_path, ranges } => { + OperationKind::ExportRaw { + setup, + components, + out_path, + ranges, + } => { api.finish( - handlers::export_raw::execute_export( - &api.cancellation_token(), - state, + handlers::export_raw::export( + api.clone(), + setup, + components, out_path, ranges, ) diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index 2c2dd8ea69..9ae64dd5d4 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -452,12 +452,14 @@ impl Session { out_path: PathBuf, ranges: Vec>, ) -> Result<(), stypes::ComputationError> { - self.tx_operations - .send(Operation::new( - operation_id, - operations::OperationKind::ExportRaw { out_path, ranges }, - )) - .map_err(|e| stypes::ComputationError::Communication(e.to_string())) + // We should not ask client for stypes::SessionSetup and we should take it from state + todo!("Not implemented") + // self.tx_operations + // .send(Operation::new( + // operation_id, + // operations::OperationKind::ExportRaw { out_path, ranges }, + // )) + // .map_err(|e| stypes::ComputationError::Communication(e.to_string())) } pub async fn is_raw_export_available(&self) -> Result { diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index fbf2055a11..beb6e1da70 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -85,6 +85,11 @@ pub struct Ident { pub uuid: Uuid, } +/// Represents the type of a component within the system. +/// +/// The component type indicates the general domain of responsibility and +/// functional role of the component. It is used to categorize components +/// according to their purpose in the data processing pipeline. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, Ord, Eq, Hash)] #[extend::encode_decode] #[cfg_attr( @@ -93,9 +98,26 @@ pub struct Ident { ts(export, export_to = "observe.ts") )] pub enum ComponentType { + /// A parser used specifically for exporting data. + /// + /// This kind of parser is not part of the live session processing flow, + /// but instead is used to convert raw or structured data into formats + /// suitable for external use (e.g., reports, backups, archives). + RawParser, + + /// A standard parser used during session processing. + /// + /// These parsers transform raw input data into a structured representation + /// that can be stored in session files and later viewed or analyzed by the user. Parser, + + /// A data source component. + /// + /// Responsible for providing input data to the system, such as reading from + /// a file, a network stream, or another external interface. Source, } + /// Multicast configuration information. /// - `multiaddr`: A valid multicast address. /// - `interface`: The address of the local interface used to join the multicast group. From 47e49f1b06a80c98c78909bcc4f680b0d3fa69fd Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 20 May 2025 14:48:54 +0200 Subject: [PATCH 042/121] Fix executing of application --- .../indexer/parsers/src/dlt/descriptor.rs | 10 +--- .../apps/indexer/parsers/src/dlt/mod.rs | 9 ++++ .../indexer/parsers/src/dlt/raw/descriptor.rs | 52 ++----------------- application/apps/indexer/parsers/src/lib.rs | 2 + application/apps/indexer/sources/src/lib.rs | 2 + .../indexer/stypes/bindings/miscellaneous.ts | 2 +- .../apps/indexer/stypes/bindings/observe.ts | 11 +++- application/apps/protocol/src/lib.rs | 1 + .../client/src/app/service/session/origin.ts | 22 -------- .../platform/types/bindings/observe.ts | 16 +++--- 10 files changed, 37 insertions(+), 90 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 1bc9e9bc8d..8e549708a5 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -7,9 +7,9 @@ use dlt_core::{ common::{StatisticInfo, StatisticInfoCollector}, }, }; +use std::collections::HashMap; use std::fmt; use std::fs::File; -use std::{collections::HashMap, path::PathBuf}; use stypes::{ FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, Value, ValueInput, @@ -48,14 +48,6 @@ impl fmt::Display for StatFields { } } -fn to_native_cfg_err(msg: S) -> NativeError { - NativeError { - severity: Severity::ERROR, - kind: NativeErrorKind::Configuration, - message: Some(msg.to_string()), - } -} - impl ComponentDescriptor for Descriptor { fn create( &self, diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index b52853d652..b023f0d81e 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -16,6 +16,7 @@ pub use dlt_core::{ filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; use serde::Serialize; +use stypes::{NativeError, NativeErrorKind, Severity}; /// The most likely minimal bytes count needed to parse a DLT message. const MIN_MSG_LEN: usize = 20; @@ -148,3 +149,11 @@ impl Parser for DltParser { MIN_MSG_LEN } } + +fn to_native_cfg_err(msg: S) -> NativeError { + NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Configuration, + message: Some(msg.to_string()), + } +} diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 472b4c3e3a..f686998dc9 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,61 +1,15 @@ use super::*; -use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; -use dlt_core::{ - read::DltMessageReader, - statistics::{ - collect_statistics, - common::{StatisticInfo, StatisticInfoCollector}, - }, -}; -use std::fmt; -use std::fs::File; -use std::{collections::HashMap, path::PathBuf}; -use stypes::{ - FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, Value, - ValueInput, -}; +use components::{ComponentDescriptor, MetadataDescriptor}; +use stypes::SourceOrigin; use tokio_util::sync::CancellationToken; const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, ]); -const FIELD_LOG_LEVEL: &str = "DLT_PARSER_FIELD_LOG_LEVEL"; -const FIELD_FIBEX_FILES: &str = "DLT_PARSER_FIELD_FIBEX_FILES"; -const FIELD_STATISTICS: &str = "DLT_PARSER_FIELD_STATISTICS"; -const FIELD_TZ: &str = "DLT_PARSER_FIELD_TIMEZONE"; - #[derive(Default)] pub struct Descriptor {} -enum StatFields { - AppIds, - ContextIds, - EcuIds, -} - -impl fmt::Display for StatFields { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::AppIds => "app_ids", - Self::ContextIds => "context_ids", - Self::EcuIds => "ecu_ids", - } - ) - } -} - -fn to_native_cfg_err(msg: S) -> NativeError { - NativeError { - severity: Severity::ERROR, - kind: NativeErrorKind::Configuration, - message: Some(msg.to_string()), - } -} - impl ComponentDescriptor for Descriptor { fn create( &self, diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index e1b240ecec..9af503abe7 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -43,6 +43,8 @@ impl definitions::Parser for Parser { } } +// TODO: this registration function will fail if some of parser would not be registred. That's wrong. +// If some of parsers are failed, another parsers should still be registred as well pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { components.add_parser(dlt::descriptor::Descriptor::default())?; components.add_parser(dlt::raw::descriptor::Descriptor::default())?; diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 74c9992598..e5a314f6ce 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -107,6 +107,8 @@ impl definitions::ByteSource for Source { } } +// TODO: this registration function will fail if some of source would not be registred. That's wrong. +// If some of sources are failed, another sources should still be registred as well pub fn registration

(components: &mut Components) -> Result<(), stypes::NativeError> { components.add_source(binary::raw::Descriptor::default())?; components.add_source(binary::pcap::legacy::Descriptor::default())?; diff --git a/application/apps/indexer/stypes/bindings/miscellaneous.ts b/application/apps/indexer/stypes/bindings/miscellaneous.ts index 20b0702933..2fa2be6197 100644 --- a/application/apps/indexer/stypes/bindings/miscellaneous.ts +++ b/application/apps/indexer/stypes/bindings/miscellaneous.ts @@ -57,7 +57,7 @@ nature: number, }; */ export type GrabbedElementList = Array; -export type MapKeyValue = { [key in string]?: string }; +export type MapKeyValue = Map; /** * Representation of ranges. We cannot use std ranges as soon as no way diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 4cfc0bc801..cf00b494ae 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -4,7 +4,14 @@ import type { PluginParserSettings } from "./plugins"; export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; -export type ComponentType = "Parser" | "Source"; +/** + * Represents the type of a component within the system. + * + * The component type indicates the general domain of responsibility and + * functional role of the component. It is used to categorize components + * according to their purpose in the data processing pipeline. + */ +export type ComponentType = "RawParser" | "Parser" | "Source"; /** * Settings for the DLT parser. @@ -134,7 +141,7 @@ export type SomeIpParserSettings = { */ fibex_file_paths: Array | null, }; -export type SourceOrigin = { "File": string } | { "Files": Array } | { "Folder": string } | { "Folders": Array } | "Source"; +export type SourceOrigin = { "File": string } | { "Files": Array } | "Source"; /** * Configuration for TCP connections. diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 3d118e1d91..e1fa2abd51 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -216,3 +216,4 @@ gen_encode_decode_fns!(ComponentType); gen_encode_decode_fns!(FieldsValidationErrors); gen_encode_decode_fns!(ComponentOptions); gen_encode_decode_fns!(SessionSetup); +gen_encode_decode_fns!(SessionDescriptor); diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 85d474d1a2..ffeccfbdd8 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -25,12 +25,6 @@ export class SessionSourceOrigin { static files(paths: string[]): SessionSourceOrigin { return new SessionSourceOrigin({ Files: paths }, undefined); } - static folder(path: string): SessionSourceOrigin { - return new SessionSourceOrigin({ Folder: path }, undefined); - } - static folders(paths: string[]): SessionSourceOrigin { - return new SessionSourceOrigin({ Folders: paths }, undefined); - } static source(): SessionSourceOrigin { return new SessionSourceOrigin('Source', undefined); } @@ -71,12 +65,8 @@ export class SessionSourceOrigin { return `Source`; } else if ((this.origin as { File: string }).File) { return (this.origin as { File: string }).File; - } else if ((this.origin as { Folder: string }).Folder) { - return (this.origin as { Folder: string }).Folder; } else if ((this.origin as { Files: string[] }).Files) { return `${(this.origin as { Files: string[] }).Files.length} files`; - } else if ((this.origin as { Folders: string[] }).Folders) { - return `${(this.origin as { Folders: string[] }).Folders.length} folders`; } else { return `unknown`; } @@ -90,23 +80,11 @@ export class SessionSourceOrigin { }; } else if ((this.origin as { File: string }).File) { return { title: `Selected File`, desctiption: (this.origin as { File: string }).File }; - } else if ((this.origin as { Folder: string }).Folder) { - return { - title: `Selected Folder`, - desctiption: (this.origin as { Folder: string }).Folder, - }; } else if ((this.origin as { Files: string[] }).Files) { return { title: `Collection of Files`, desctiption: `${(this.origin as { Files: string[] }).Files.length} files`, }; - } else if ((this.origin as { Folders: string[] }).Folders) { - return { - title: 'Collection of Folder', - desctiption: `${ - (this.origin as { Folders: string[] }).Folders.length - } folders selected`, - }; } else { return { title: 'Unknown', desctiption: undefined }; } diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index a2f77d9e12..b37165c47f 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -5,7 +5,14 @@ import type { PluginParserSettings } from './plugins'; export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; -export type ComponentType = 'Parser' | 'Source'; +/** + * Represents the type of a component within the system. + * + * The component type indicates the general domain of responsibility and + * functional role of the component. It is used to categorize components + * according to their purpose in the data processing pipeline. + */ +export type ComponentType = 'RawParser' | 'Parser' | 'Source'; /** * Settings for the DLT parser. @@ -151,12 +158,7 @@ export type SomeIpParserSettings = { fibex_file_paths: Array | null; }; -export type SourceOrigin = - | { File: string } - | { Files: Array } - | { Folder: string } - | { Folders: Array } - | 'Source'; +export type SourceOrigin = { File: string } | { Files: Array } | 'Source'; /** * Configuration for TCP connections. From 1d5beffca5ffca62190dda8edc5c2023f16cf871 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 21 May 2025 14:05:28 +0200 Subject: [PATCH 043/121] Corrections in the scope of rebasing to plugins branch (master) --- .../apps/indexer/parsers/src/dlt/mod.rs | 15 ----- .../apps/indexer/parsers/src/someip/mod.rs | 61 ++++++++++++------- .../apps/indexer/parsers/src/text/mod.rs | 11 ++-- .../plugins_host/src/v0_1_0/parser/mod.rs | 2 +- .../apps/rustcore/rs-bindings/Cargo.lock | 8 +-- .../nested_dictionary/structure/styles.less | 12 ++-- .../complex/nested_dictionary/styles.less | 6 +- .../app/ui/elements/scheme/entry/styles.less | 4 +- .../src/app/ui/tabs/setup/origin/styles.less | 4 +- .../client/src/app/ui/tabs/setup/styles.less | 10 +-- 10 files changed, 68 insertions(+), 65 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index b023f0d81e..17fa410f9a 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -61,21 +61,6 @@ impl DltParser { offset: 0, } } -} - -impl From for Error { - fn from(value: DltParseError) -> Self { - match value { - DltParseError::Unrecoverable(e) | DltParseError::ParsingHickup(e) => { - Error::Parse(e.to_string()) - } - DltParseError::IncompleteParse { needed: _ } => Error::Incomplete, - } - } -} - -impl<'m> SingleParser> for DltParser<'m> { - const MIN_MSG_LEN: usize = MIN_MSG_LEN; fn parse_item( &mut self, diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 466fef09d7..497a9828b9 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -284,13 +284,6 @@ impl SomeipParser { }, } } -} - -unsafe impl Send for SomeipParser {} -unsafe impl Sync for SomeipParser {} - -impl SingleParser for SomeipParser { - const MIN_MSG_LEN: usize = MIN_MSG_LEN; fn parse_item( &mut self, @@ -612,7 +605,7 @@ mod test { let input: &[u8] = &[]; let mut parser = SomeipParser::new(); - let result = parser.parse_item(input, None); + let result = parser.parse(input, None); match result { Err(ParserError::Incomplete) => {} @@ -631,7 +624,7 @@ mod test { ]; let mut parser = SomeipParser::new(); - let result = parser.parse_item(input, None); + let result = parser.parse(input, None); match result { Err(ParserError::Incomplete) => {} @@ -651,7 +644,9 @@ mod test { let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -673,7 +668,9 @@ mod test { let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -695,7 +692,9 @@ mod test { let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -726,7 +725,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -755,7 +756,9 @@ mod test { let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -787,7 +790,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -819,7 +824,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -851,7 +858,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -883,7 +892,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -915,7 +926,9 @@ mod test { fibex_metadata: Some(fibex_metadata), }; - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -945,7 +958,10 @@ mod test { ]; let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { @@ -992,7 +1008,10 @@ mod test { let mut parser = SomeipParser::new(); - let (consumed, message) = parser.parse_item(input, None).unwrap(); + let mut items: Vec<_> = parser.parse(input, None).unwrap().collect(); + assert_eq!(items.len(), 1); + + let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); if let ParseYield::Message(item) = message.unwrap() { diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 38bcd40613..60eb010f4e 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -5,6 +5,9 @@ use definitions::*; use serde::Serialize; use stypes::NativeError; +/// The most likely minimal bytes count needed to parse a text message. +const MIN_MSG_LEN: usize = 1; + pub struct StringTokenizer {} #[derive(Debug, PartialEq, Eq, Serialize)] @@ -64,28 +67,26 @@ impl Parser for StringTokenizer { #[cfg(test)] mod tests { - use crate::Parser; - use super::*; #[test] fn multiple_parse_calls() { let mut parser = StringTokenizer {}; let content = b"hello\nworld\n"; - let (consumed_1, first_msg) = parser.parse_item(content, None).unwrap(); + let (consumed_1, first_msg) = parser.parse(content, None).unwrap().next().unwrap(); match first_msg { Some(ParseYield::Message(StringMessage { content })) if content.eq("hello") => {} _ => panic!("First message did not match"), } let rest_1 = &content[consumed_1..]; println!("rest_1 = {:?}", String::from_utf8_lossy(rest_1)); - let (consumed_2, second_msg) = parser.parse_item(rest_1, None).unwrap(); + let (consumed_2, second_msg) = parser.parse(rest_1, None).unwrap().next().unwrap(); match second_msg { Some(ParseYield::Message(StringMessage { content })) if content.eq("world") => {} _ => panic!("Second message did not match"), } let rest_2 = &rest_1[consumed_2..]; - let (consumed_3, third_msg) = parser.parse_item(rest_2, None).unwrap(); + let (consumed_3, third_msg) = parser.parse(rest_2, None).unwrap().next().unwrap(); println!( "rest_3 = {:?}", String::from_utf8_lossy(&rest_2[consumed_3..]) diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 65807e6284..db2067f9d1 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -158,7 +158,7 @@ impl defs::Parser for PluginParser { Ok(results) => results?, Err(call_err) => { // Wasmtime uses anyhow error, which provides error context in debug print only. - return Err(p::Error::Unrecoverable(format!( + return Err(defs::ParserError::Unrecoverable(format!( "Call parse on the plugin failed. Error: {call_err:?}" ))); } diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 3354e4625b..810aec2fd1 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -564,7 +564,7 @@ version = "0.1.0" dependencies = [ "log", "stypes", - "thiserror 2.0.3", + "thiserror 2.0.12", "tokio", "tokio-util", "uuid", @@ -889,7 +889,7 @@ dependencies = [ "etherparse", "serde", "stypes", - "thiserror 2.0.3", + "thiserror 2.0.12", ] [[package]] @@ -2453,7 +2453,6 @@ dependencies = [ "definitions", "dlt-core", "file-tools", - "humantime", "lazy_static", "log", "memchr", @@ -2463,7 +2462,6 @@ dependencies = [ "someip-messages", "someip-payload", "someip-tools", - "thiserror 2.0.12", "stypes", "tokio", "tokio-util", @@ -2823,7 +2821,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom", "libredox", - "thiserror 2.0.3", + "thiserror 2.0.12", ] [[package]] diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less index 9ea4ddd588..771b4d60aa 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less @@ -7,34 +7,34 @@ app-settings-scheme-nested-dictionary-structure { width: 100%; & p.title { margin: 8px 0 0px 16px; - color: @scheme-color-2; + color: var(--scheme-color-2); font-size: 14px; font-weight: 500; } & tr{ position: relative; &:hover { - background-color: @scheme-color-4!important; + background-color: var(--scheme-color-4)!important; } } & tr[dataselected="true"] { - background: @scheme-color-active; + background: var(--scheme-color-active); &::after { position: absolute; display: block; content: ''; width: 4px; height: 100%; - background: @scheme-color-accent; + background: var(--scheme-color-accent); left: 0; } &:hover { - background-color: @scheme-color-active-75!important; + background-color: var(--scheme-color-active-75)!important; } } & span.id{ & > span { - background-color: @scheme-color-3; + background-color: var(--scheme-color-3); } } } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less index 88f9da640e..f2bc6e7681 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less +++ b/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less @@ -38,7 +38,7 @@ position: absolute; z-index: 1000; padding: 6px 12px; - background: @scheme-color-warning-light; + background: var(--scheme-color-warning-light); top: 6px; right: 6px; box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); @@ -83,7 +83,7 @@ position: sticky; bottom: 0; z-index: 1; - box-shadow: 0px 0px 16px @scheme-color-5; + box-shadow: 0px 0px 16px var(--scheme-color-5); } } & .table-fill { @@ -108,6 +108,6 @@ } } & .mat-progress-bar-fill::after { - background-color: @scheme-color-accent; + background-color: var(--scheme-color-accent); } } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index 6f7bc30f6d..141284eaa6 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -16,11 +16,11 @@ } p.title { display: block; - color: @scheme-color-2; + color: var(--scheme-color-2); padding: 3px 0px 0 16px; } p.error { - color: @scheme-color-error-light; + color: var(--scheme-color-error-light); } div.loading { position: relative; diff --git a/application/client/src/app/ui/tabs/setup/origin/styles.less b/application/client/src/app/ui/tabs/setup/origin/styles.less index be80a97242..384287f1fb 100644 --- a/application/client/src/app/ui/tabs/setup/origin/styles.less +++ b/application/client/src/app/ui/tabs/setup/origin/styles.less @@ -6,7 +6,7 @@ display: block; p.title { max-width: 200px; - color: @scheme-color-0; + color: var(--scheme-color-0); white-space: nowrap; text-overflow: ellipsis; position: relative; @@ -15,7 +15,7 @@ } p.description { max-width: 400px; - color: @scheme-color-2; + color: var(--scheme-color-2); white-space: nowrap; text-overflow: ellipsis; position: relative; diff --git a/application/client/src/app/ui/tabs/setup/styles.less b/application/client/src/app/ui/tabs/setup/styles.less index 7ccc097fce..da6c270193 100644 --- a/application/client/src/app/ui/tabs/setup/styles.less +++ b/application/client/src/app/ui/tabs/setup/styles.less @@ -22,8 +22,8 @@ left:0; right: 0; padding: 0 8px; - background: @scheme-color-5; - box-shadow: 0 3px 3px @scheme-color-7; + background: var(--scheme-color-5); + box-shadow: 0 3px 3px var(--scheme-color-7); height: 48px; overflow: hidden; & *.entity { @@ -33,7 +33,7 @@ left:0; right: 0; padding: 0 8px; - background: @scheme-color-5; + background: var(--scheme-color-5); height: 48px; &.filler { flex: auto; @@ -42,7 +42,7 @@ justify-content: end; } &.info { - color: @scheme-color-2; + color: var(--scheme-color-2); white-space: nowrap; align-items: center; flex: auto; @@ -63,7 +63,7 @@ } } & p.info { - color: @scheme-color-2; + color: var(--scheme-color-2); white-space: nowrap; } } From 624d4ca7b003bb2a949bc73a8d6468f4721b992f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 22 May 2025 09:50:00 +0200 Subject: [PATCH 044/121] Add documentation and reanaming --- .../apps/indexer/addons/dlt-tools/src/lib.rs | 2 +- .../apps/indexer/components/src/lib.rs | 4 +- .../apps/indexer/components/src/tys.rs | 46 +++- .../indexer/parsers/src/dlt/descriptor.rs | 6 +- .../indexer/parsers/src/dlt/raw/descriptor.rs | 6 +- .../indexer/parsers/src/someip/descriptor.rs | 6 +- .../indexer/parsers/src/text/descriptor.rs | 6 +- .../plugins_host/src/parser_shared/mod.rs | 4 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 4 +- .../apps/indexer/processor/src/export/mod.rs | 239 ------------------ application/apps/indexer/processor/src/lib.rs | 1 - .../indexer/processor/src/producer/mod.rs | 8 +- .../indexer/session/src/handlers/observe.rs | 2 +- .../indexer/sources/src/binary/pcap/legacy.rs | 6 +- .../indexer/sources/src/binary/pcap/ng.rs | 6 +- .../apps/indexer/sources/src/binary/raw.rs | 6 +- .../indexer/sources/src/command/process.rs | 6 +- .../indexer/sources/src/serial/options.rs | 6 +- .../apps/indexer/sources/src/socket/tcp.rs | 6 +- .../apps/indexer/sources/src/socket/udp.rs | 6 +- 20 files changed, 88 insertions(+), 288 deletions(-) delete mode 100644 application/apps/indexer/processor/src/export/mod.rs diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index 94c8b7248b..198cb99021 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -15,7 +15,7 @@ extern crate indexer_base; #[macro_use] extern crate log; -use definitions::{Attachment, MessageStreamItem}; +use definitions::Attachment; use dlt_core::filtering::DltFilterConfig; use parsers::dlt::DltParser; use processor::producer::MessageProducer; diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index a0eaba6e88..4a0ab5a575 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -19,7 +19,7 @@ impl Components { components: HashMap::new(), } } - pub fn add_parser + 'static>( + pub fn add_parser + 'static>( &mut self, descriptor: D, ) -> Result<(), stypes::NativeError> { @@ -35,7 +35,7 @@ impl Components { .insert(ident.uuid, Entry::Parser(Box::new(descriptor))); Ok(()) } - pub fn add_source + 'static>( + pub fn add_source + 'static>( &mut self, descriptor: D, ) -> Result<(), stypes::NativeError> { diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 9a1a691121..8627b9b44e 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -56,12 +56,12 @@ pub type StaticFieldsResult = Result, stypes::NativeError pub type LazyFieldsTask = Pin + Send>>; pub enum Entry { - RawParser(Box>), - Parser(Box>), - Source(Box>), + RawParser(Box>), + Parser(Box>), + Source(Box>), } -impl MetadataDescriptor for Entry { +impl ComponentDescriptor for Entry { fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { match self { Self::Source(inner) => inner.fields_getter(origin), @@ -121,7 +121,28 @@ impl MetadataDescriptor for Entry { } } -pub trait ComponentDescriptor: MetadataDescriptor + Sync + Send { +/// Defines a factory interface for creating instances of components. +/// +/// In the application architecture, components (such as parsers, sources, etc.) +/// are not instantiated directly. Instead, component instantiation is handled +/// by the `Components` controller, which creates component instances based on +/// registrations made during system initialization. +/// +/// This level of indirection is a key architectural decision, as it allows +/// components to remain decoupled from the application core. This makes it +/// possible to replace or update components without modifying the core logic. +pub trait ComponentFactory: ComponentDescriptor + Sync + Send { + /// Creates an instance of the component of type `T`. + /// + /// # Arguments + /// * `origin` - A reference to a `SourceOrigin` representing the creation context. + /// For example, it could point to a file, a group of files, or any other data source. + /// * `options` - A slice of `stypes::Field` values representing component options or settings. + /// + /// # Returns + /// * `Ok(Some(T))` if the component was successfully created. + /// * `Ok(None)` if the factory declined to create a component for the given context. + /// * `Err(stypes::NativeError)` if an error occurred during creation. fn create( &self, _origin: &SourceOrigin, @@ -131,7 +152,20 @@ pub trait ComponentDescriptor: MetadataDescriptor + Sync + Send { } } -pub trait MetadataDescriptor { +/// Describes a component in terms of its identity, configuration schema, +/// validation logic, support for lazy-loading configuration, and its type within the system. +/// +/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples +/// the core system from concrete component implementations. Instead of referring +/// to component types directly (e.g., a specific parser or source), the application +/// interacts with components through their descriptors. +/// +/// This design enables a fully modular architecture where, for example, a session +/// can be created using a parser and source identified solely by their UUIDs. +/// The actual parser and source implementations remain hidden behind the descriptor, +/// making it possible to swap, reconfigure, or isolate components without touching +/// the application core. +pub trait ComponentDescriptor { /// Check is component is campatible with given origin /// /// * `origin` - The source origin diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 8e549708a5..cf7cdab4b1 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -1,5 +1,5 @@ use crate::dlt::*; -use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; +use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; use dlt_core::{ read::DltMessageReader, statistics::{ @@ -48,7 +48,7 @@ impl fmt::Display for StatFields { } } -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, origin: &SourceOrigin, @@ -90,7 +90,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index f686998dc9..6238ce8dc2 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,5 +1,5 @@ use super::*; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use stypes::SourceOrigin; use tokio_util::sync::CancellationToken; @@ -10,7 +10,7 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, origin: &SourceOrigin, @@ -23,7 +23,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 637fd2f7ed..96373d9bad 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use super::SomeipParser; -use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; +use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; use stypes::SourceOrigin; use tokio::{ select, @@ -21,7 +21,7 @@ const FIELD_STATISTICS: &str = "statistics"; #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -31,7 +31,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 23dc4c6447..460dc16a50 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,4 +1,4 @@ -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use file_tools::is_binary; use stypes::SourceOrigin; @@ -11,7 +11,7 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -21,7 +21,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 4e06a4613f..18ebbabb8a 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use stypes::{PluginInfo, SemanticVersion}; use wasmtime::component::Component; @@ -188,7 +188,7 @@ impl defs::Parser for PluginsParser { #[derive(Default)] struct Descriptor {} -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { true } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index db2067f9d1..a6c757e901 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -4,7 +4,7 @@ mod bindings; mod parser_plugin_state; -use components::MetadataDescriptor; +use components::ComponentDescriptor; use stypes::{ParserRenderOptions, RenderOptions, SemanticVersion}; use tokio::runtime::Handle; use wasmtime::{ @@ -179,7 +179,7 @@ impl defs::Parser for PluginParser { #[derive(Default)] struct Descriptor {} -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { true } diff --git a/application/apps/indexer/processor/src/export/mod.rs b/application/apps/indexer/processor/src/export/mod.rs deleted file mode 100644 index 9cc669a9fb..0000000000 --- a/application/apps/indexer/processor/src/export/mod.rs +++ /dev/null @@ -1,239 +0,0 @@ -use std::{ - io::{BufWriter, Write}, - path::Path, -}; - -use crate::producer::MessageProducer; -use definitions::{ByteSource, LogRecordWriter, MessageStreamItem, Parser}; -use indexer_base::config::IndexSection; -use thiserror::Error; -use tokio_util::sync::CancellationToken; - -#[derive(Error, Debug)] -pub enum ExportError { - #[error("Configuration error ({0})")] - Config(String), - #[error("IO error: {0:?}")] - Io(#[from] std::io::Error), - #[error("Cancelled")] - Cancelled, -} - -/// Exporting data as raw into a given destination. Would be exported only data -/// defined in selections as indexes of rows (messages). -/// -/// Returns usize - count of read messages (not exported, but read messages) -/// -/// # Arguments -/// * `s` - instance of MessageProducer stream -/// * `destination_path` - destination path to a target file. If the file doesn't -/// exist it would be created; if exists - it will be opened to append new -/// content to the end of the file -/// * `sections` - an array of ranges, which should be written into destination -/// file -/// * `read_to_end` - in "true" will continue iterating stream after all ranges -/// are processed; in "false" will stop listening to a stream as soon as all ranges -/// are processed. It should be used in "true" for example if exporting applied -/// to concatenated files. -/// * `cancel` - cancellation token to stop operation -/// -/// # Errors -/// In case of cancellation will return ExportError::Cancelled -pub async fn export_raw( - mut producer: MessageProducer<'_, P, D, W>, - destination_path: &Path, - sections: &Vec, - read_to_end: bool, - text_file: bool, - cancel: &CancellationToken, -) -> Result -where - P: Parser, - D: ByteSource, - W: LogRecordWriter, -{ - todo!("Not implemented") - // trace!("export_raw, sections: {sections:?}"); - // if !sections_valid(sections) { - // return Err(ExportError::Config("Invalid sections".to_string())); - // } - // let out_file = if destination_path.exists() { - // std::fs::OpenOptions::new() - // .append(true) - // .open(destination_path)? - // } else { - // std::fs::File::create(destination_path)? - // }; - // let mut out_writer = BufWriter::new(out_file); - // let mut section_index = 0usize; - // let mut current_index = 0usize; - // let mut inside = false; - // let mut exported = 0usize; - // if sections.is_empty() { - // debug!("no sections configured"); - // // export everything - // 'outer: while let Some(items) = producer.read_next_segment().await { - // if cancel.is_cancelled() { - // return Err(ExportError::Cancelled); - // } - - // for (_, item) in items { - // let written = match item { - // MessageStreamItem::Item(ParseYield::Message(msg)) => { - // msg.to_writer(&mut out_writer)?; - // true - // } - // MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { - // msg.to_writer(&mut out_writer)?; - // true - // } - // MessageStreamItem::Done => break 'outer, - // _ => false, - // }; - // if written && text_file { - // out_writer.write_all("\n".as_bytes())?; - // } - // if written { - // exported += 1; - // } - // } - // } - // return Ok(exported); - // } - - // 'outer: while let Some(items) = producer.read_next_segment().await { - // if cancel.is_cancelled() { - // return Err(ExportError::Cancelled); - // } - // for (_, item) in items { - // if !inside { - // if sections[section_index].first_line == current_index { - // inside = true; - // } - // } else if sections[section_index].last_line < current_index { - // inside = false; - // section_index += 1; - // if sections.len() <= section_index { - // // no more sections - // if matches!(item, MessageStreamItem::Item(_)) { - // current_index += 1; - // } - // break 'outer; - // } - // // check if we are in next section again - // if sections[section_index].first_line == current_index { - // inside = true; - // } - // } - // let written = match item { - // MessageStreamItem::Item(ParseYield::Message(msg)) => { - // if inside { - // msg.to_writer(&mut out_writer)?; - // } - // current_index += 1; - // inside - // } - // MessageStreamItem::Item(ParseYield::MessageAndAttachment((msg, _))) => { - // if inside { - // msg.to_writer(&mut out_writer)?; - // } - // current_index += 1; - // inside - // } - // MessageStreamItem::Done => { - // debug!("No more messages to export"); - // break 'outer; - // } - // _ => false, - // }; - // if written && text_file { - // out_writer.write_all("\n".as_bytes())?; - // } - // } - // } - // if read_to_end { - // 'outer: while let Some(items) = producer.read_next_segment().await { - // if cancel.is_cancelled() { - // return Err(ExportError::Cancelled); - // } - // for (_, item) in items { - // match item { - // MessageStreamItem::Item(_) => { - // current_index += 1; - // } - // MessageStreamItem::Done => { - // break 'outer; - // } - // _ => {} - // } - // } - // } - // } - // debug!("export_raw done ({current_index} messages)"); - // Ok(current_index) -} - -fn sections_valid(sections: &[IndexSection]) -> bool { - let pairs = sections.iter().zip(sections.iter().skip(1)); - for p in pairs { - if p.0.last_line >= p.1.first_line { - // overlap - return false; - } - } - sections.iter().all(|s| s.first_line <= s.last_line) -} - -#[test] -fn test_sections_valid_valid_single_section() { - let sections = vec![IndexSection { - first_line: 0, - last_line: 2, - }]; - assert!(sections_valid(§ions)); -} -#[test] -fn test_sections_valid_validity_valid_sections_no_gap() { - let sections = vec![ - IndexSection { - first_line: 0, - last_line: 2, - }, - IndexSection { - first_line: 3, - last_line: 4, - }, - ]; - assert!(sections_valid(§ions)); -} -#[test] -fn test_sections_valid_invalidity_valid_single_section() { - let sections = vec![IndexSection { - first_line: 2, - last_line: 1, - }]; - assert!(!sections_valid(§ions)); -} -#[test] -fn test_sections_valid_empty_sections() { - let sections = vec![]; - assert!(sections_valid(§ions)); -} -#[test] -fn test_sections_valid_invalidity_overlapping() { - let sections = vec![ - IndexSection { - first_line: 1, - last_line: 4, - }, - IndexSection { - first_line: 9, - last_line: 11, - }, - IndexSection { - first_line: 11, - last_line: 15, - }, - ]; - assert!(!sections_valid(§ions)); -} diff --git a/application/apps/indexer/processor/src/lib.rs b/application/apps/indexer/processor/src/lib.rs index 4bd108f368..5c10804571 100644 --- a/application/apps/indexer/processor/src/lib.rs +++ b/application/apps/indexer/processor/src/lib.rs @@ -15,7 +15,6 @@ extern crate lazy_static; #[macro_use] extern crate log; -pub mod export; pub mod grabber; pub mod map; pub mod processor; diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index f737c29f7d..cf6e076e9f 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -3,13 +3,19 @@ mod tests; pub mod sde; -use definitions::Parser; use definitions::*; use log::warn; /// Number of bytes to skip on initial parse errors before terminating the session. const INITIAL_PARSE_ERROR_LIMIT: usize = 1024; +#[derive(Debug)] +pub enum MessageStreamItem { + Parsed(ParseOperationResult), + Skipped, + Done, +} + #[derive(Debug)] pub struct MessageProducer<'a, P, D, W> where diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe.rs index 9296fed204..52d2de3c01 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe.rs @@ -6,7 +6,7 @@ use crate::{ use components::Components; use definitions::*; use log::{trace, warn}; -use processor::producer::{sde::*, MessageProducer}; +use processor::producer::{sde::*, MessageProducer, MessageStreamItem}; use std::sync::Arc; use std::time::Instant; use stypes::{NativeError, SessionSetup, SourceOrigin}; diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index 2befbb7314..c2ccc5ff53 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -1,7 +1,7 @@ use crate::{binary::pcap::debug_block}; use definitions::*; use bufread::DeqBuffer; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; @@ -213,7 +213,7 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -223,7 +223,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index fdac35a98c..e4d298846d 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -1,7 +1,7 @@ use crate::{binary::pcap::debug_block}; use definitions::*; use bufread::DeqBuffer; -use components::{ComponentDescriptor,MetadataDescriptor}; +use components::{ComponentFactory,ComponentDescriptor}; use etherparse::{SlicedPacket, TransportSlice}; use file_tools::is_binary; use log::{debug, error, trace}; @@ -215,7 +215,7 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -225,7 +225,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 32e90b31ef..211b2d57c7 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -1,5 +1,5 @@ use bufread::BufReader; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use definitions::*; use file_tools::is_binary; use std::{ @@ -130,7 +130,7 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, origin: &SourceOrigin, @@ -154,7 +154,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { let files = match origin { SourceOrigin::File(filepath) => { diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 7ecbcd0b47..30aadc514a 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,5 +1,5 @@ use bufread::DeqBuffer; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use definitions::*; use regex::{Captures, Regex}; use shellexpand::tilde; @@ -235,7 +235,7 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -245,7 +245,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) | SourceOrigin::Files(..) => false, diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index aa32129a43..262596a07f 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -1,4 +1,4 @@ -use components::{ComponentDescriptor, MetadataDescriptor, StaticFieldResult}; +use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; use std::{collections::HashMap, io::Read, str}; use stypes::{FieldDesc, LazyFieldDesc, SourceOrigin, StaticFieldDesc, Value, ValueInput}; @@ -28,7 +28,7 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -38,7 +38,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) | SourceOrigin::Files(..) => false, diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index b964410942..28f0a7a8e2 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,5 +1,5 @@ use bufread::DeqBuffer; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; @@ -176,7 +176,7 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -186,7 +186,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) | SourceOrigin::Files(..) => false, diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index d31b06917a..83eac45219 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -1,7 +1,7 @@ use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; use bufread::DeqBuffer; -use components::{ComponentDescriptor, MetadataDescriptor}; +use components::{ComponentFactory, ComponentDescriptor}; use definitions::*; use log::trace; use std::{ @@ -139,7 +139,7 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl ComponentFactory for Descriptor { fn create( &self, _origin: &SourceOrigin, @@ -149,7 +149,7 @@ impl ComponentDescriptor for Descriptor { } } -impl MetadataDescriptor for Descriptor { +impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SourceOrigin) -> bool { match origin { SourceOrigin::File(..) | SourceOrigin::Files(..) => false, From 4e3cac707aa4611726e4b8caf0874d8445be0f24 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 22 May 2025 16:16:37 +0200 Subject: [PATCH 045/121] Merge observe operation and exporting raw operation --- .../apps/indexer/components/src/lib.rs | 17 +- .../apps/indexer/components/src/tys.rs | 36 +-- .../indexer/parsers/src/dlt/descriptor.rs | 27 +- .../indexer/parsers/src/dlt/raw/descriptor.rs | 23 +- .../indexer/parsers/src/someip/descriptor.rs | 19 +- .../indexer/parsers/src/text/descriptor.rs | 13 +- .../plugins_host/src/parser_shared/mod.rs | 2 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 2 +- .../indexer/session/src/components/api.rs | 12 +- .../indexer/session/src/components/mod.rs | 6 +- .../apps/indexer/session/src/handlers/mod.rs | 1 - .../src/handlers/{ => observe}/export_raw.rs | 45 +--- .../session/src/handlers/observe/mod.rs | 63 +++++ .../{observe.rs => observe/session.rs} | 39 +-- .../apps/indexer/session/src/operations.rs | 29 +-- .../indexer/sources/src/binary/pcap/legacy.rs | 12 +- .../indexer/sources/src/binary/pcap/ng.rs | 12 +- .../apps/indexer/sources/src/binary/raw.rs | 16 +- .../indexer/sources/src/command/process.rs | 14 +- .../indexer/sources/src/serial/options.rs | 20 +- .../apps/indexer/sources/src/socket/tcp.rs | 14 +- .../apps/indexer/sources/src/socket/udp.rs | 14 +- .../apps/indexer/stypes/bindings/observe.ts | 245 ++++++++++-------- .../indexer/stypes/src/observe/extending.rs | 2 +- .../apps/indexer/stypes/src/observe/mod.rs | 15 +- .../apps/indexer/stypes/src/observe/nodejs.rs | 2 +- application/apps/protocol/src/lib.rs | 2 +- .../rs-bindings/src/js/components/mod.rs | 6 +- .../spec/session.components.spec.ts | 6 +- .../ts-bindings/src/api/components.ts | 8 +- .../src/native/native.components.ts | 14 +- .../client/src/app/service/components.ts | 8 +- .../client/src/app/service/session/origin.ts | 6 +- .../client/src/app/ui/tabs/setup/provider.ts | 2 +- .../client/src/app/ui/tabs/setup/state.ts | 2 +- .../ipc/request/components/get_options.ts | 6 +- .../ipc/request/components/get_parsers.ts | 6 +- .../ipc/request/components/get_sources.ts | 6 +- .../ipc/request/components/validate.ts | 6 +- .../platform/types/bindings/observe.ts | 4 +- application/platform/types/observe/index.ts | 2 +- 41 files changed, 378 insertions(+), 406 deletions(-) rename application/apps/indexer/session/src/handlers/{ => observe}/export_raw.rs (66%) create mode 100644 application/apps/indexer/session/src/handlers/observe/mod.rs rename application/apps/indexer/session/src/handlers/{observe.rs => observe/session.rs} (81%) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 4a0ab5a575..3bbd310524 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -55,23 +55,12 @@ impl Components { pub fn get_components( &self, target: &stypes::ComponentType, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, ) -> Result, stypes::NativeError> { Ok(self .components .iter() .filter_map(|(_, desc)| match target { - stypes::ComponentType::RawParser => { - if let Entry::RawParser(desc) = desc { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - } else { - None - } - } stypes::ComponentType::Parser => { if let Entry::Parser(desc) = desc { if desc.is_compatible(&origin) { @@ -100,7 +89,7 @@ impl Components { pub fn get_options( &self, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, mut targets: Vec, ) -> Result, stypes::NativeError> { let descriptors: Vec<&Entry> = self @@ -147,7 +136,7 @@ impl Components { pub fn validate( &self, - origin: &stypes::SourceOrigin, + origin: &stypes::SessionAction, target: &Uuid, fields: &[stypes::Field], ) -> Result, stypes::NativeError> { diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 8627b9b44e..0507a504c4 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -1,7 +1,7 @@ use std::{future::Future, pin::Pin}; use crate::*; -use stypes::SourceOrigin; +use stypes::SessionAction; use tokio_util::sync::CancellationToken; /// A type alias for a result containing a vector of field descriptors or a native error. @@ -56,67 +56,59 @@ pub type StaticFieldsResult = Result, stypes::NativeError pub type LazyFieldsTask = Pin + Send>>; pub enum Entry { - RawParser(Box>), Parser(Box>), Source(Box>), } impl ComponentDescriptor for Entry { - fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { + fn fields_getter(&self, origin: &stypes::SessionAction) -> FieldsResult { match self { Self::Source(inner) => inner.fields_getter(origin), Self::Parser(inner) => inner.fields_getter(origin), - Self::RawParser(inner) => inner.fields_getter(origin), } } fn ident(&self) -> stypes::Ident { match self { Self::Source(inner) => inner.ident(), Self::Parser(inner) => inner.ident(), - Self::RawParser(inner) => inner.ident(), } } - fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { + fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { match self { Self::Source(inner) => inner.is_compatible(origin), Self::Parser(inner) => inner.is_compatible(origin), - Self::RawParser(inner) => inner.is_compatible(origin), } } fn is_ty(&self, ty: &stypes::ComponentType) -> bool { match self { Self::Source(inner) => inner.is_ty(ty), Self::Parser(inner) => inner.is_ty(ty), - Self::RawParser(inner) => inner.is_ty(ty), } } fn lazy_fields_getter( &self, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, cancel: CancellationToken, ) -> LazyFieldsTask { match self { Self::Source(inner) => inner.lazy_fields_getter(origin, cancel), Self::Parser(inner) => inner.lazy_fields_getter(origin, cancel), - Self::RawParser(inner) => inner.lazy_fields_getter(origin, cancel), } } fn ty(&self) -> stypes::ComponentType { match self { Self::Source(inner) => inner.ty(), Self::Parser(inner) => inner.ty(), - Self::RawParser(inner) => inner.ty(), } } fn validate( &self, - origin: &stypes::SourceOrigin, + origin: &stypes::SessionAction, fields: &[stypes::Field], ) -> HashMap { match self { Self::Source(inner) => inner.validate(origin, fields), Self::Parser(inner) => inner.validate(origin, fields), - Self::RawParser(inner) => inner.validate(origin, fields), } } } @@ -135,7 +127,7 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// Creates an instance of the component of type `T`. /// /// # Arguments - /// * `origin` - A reference to a `SourceOrigin` representing the creation context. + /// * `origin` - A reference to a `SessionAction` representing the creation context. /// For example, it could point to a file, a group of files, or any other data source. /// * `options` - A slice of `stypes::Field` values representing component options or settings. /// @@ -145,7 +137,7 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// * `Err(stypes::NativeError)` if an error occurred during creation. fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -174,7 +166,7 @@ pub trait ComponentDescriptor { /// /// * `true` - if component can be used with origin /// * `false` - if component can not be used with origin - fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool; + fn is_compatible(&self, origin: &stypes::SessionAction) -> bool; /// Returns a general description of the component as a static, standalone entity. /// @@ -192,11 +184,11 @@ pub trait ComponentDescriptor { /// Unlike `ident`, this method considers how the component is being used, such as what /// data source is involved and which fields are already known. For example, a `source::Raw` /// component might normally describe itself as a "binary reader", but in the context of - /// a `SourceOrigin::File` and specific field values, it could instead describe itself + /// a `SessionAction::File` and specific field values, it could instead describe itself /// as a "filename" input. /// /// # Arguments - /// * `origin` - The `stypes::SourceOrigin` that indicates the usage context. + /// * `origin` - The `stypes::SessionAction` that indicates the usage context. /// * `fields` - A slice of `stypes::Field` values representing known configuration fields. /// /// # Returns @@ -204,7 +196,7 @@ pub trait ComponentDescriptor { /// context-based identity is applicable. fn bound_ident( &self, - _origin: &stypes::SourceOrigin, + _origin: &stypes::SessionAction, _fields: &[stypes::Field], ) -> stypes::Ident { self.ident() @@ -244,7 +236,7 @@ pub trait ComponentDescriptor { /// /// * `FieldsResult` - A result containing a vector of field descriptors or a native error. #[allow(unused)] - fn fields_getter(&self, origin: &stypes::SourceOrigin) -> FieldsResult { + fn fields_getter(&self, origin: &stypes::SessionAction) -> FieldsResult { Ok(Vec::new()) } @@ -263,7 +255,7 @@ pub trait ComponentDescriptor { #[allow(unused)] fn lazy_fields_getter( &self, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, cancel: CancellationToken, ) -> LazyFieldsTask { Box::pin(async { Ok(Vec::new()) }) @@ -287,7 +279,7 @@ pub trait ComponentDescriptor { #[allow(unused)] fn validate( &self, - origin: &stypes::SourceOrigin, + origin: &stypes::SessionAction, fields: &[stypes::Field], ) -> HashMap { HashMap::new() diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index cf7cdab4b1..e7e0574fe6 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -1,5 +1,5 @@ use crate::dlt::*; -use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; +use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; use dlt_core::{ read::DltMessageReader, statistics::{ @@ -11,7 +11,7 @@ use std::collections::HashMap; use std::fmt; use std::fs::File; use stypes::{ - FieldDesc, NativeError, NativeErrorKind, Severity, SourceOrigin, StaticFieldDesc, Value, + FieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, StaticFieldDesc, Value, ValueInput, }; use tokio_util::sync::CancellationToken; @@ -51,7 +51,7 @@ impl fmt::Display for StatFields { impl ComponentFactory for Descriptor { fn create( &self, - origin: &SourceOrigin, + origin: &SessionAction, options: &[stypes::Field], ) -> Result, stypes::NativeError> { let mut filter_config: Option = None; @@ -85,19 +85,20 @@ impl ComponentFactory for Descriptor { None, someip_metadata, // If it's source - no storage header expected - !matches!(origin, SourceOrigin::Source), + !matches!(origin, SessionAction::Source), )))) } } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => return true, + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source => return true, + SessionAction::ExportRaw(..) => return false, }; files.iter().any(|fp| { fp.extension() @@ -105,7 +106,7 @@ impl ComponentDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, origin: &SourceOrigin) -> components::FieldsResult { + fn fields_getter(&self, origin: &SessionAction) -> components::FieldsResult { let mut options = vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), @@ -134,7 +135,7 @@ impl ComponentDescriptor for Descriptor { binding: None, }), ]; - if matches!(origin, SourceOrigin::File(..) | SourceOrigin::Files(..)) { + if matches!(origin, SessionAction::File(..) | SessionAction::Files(..)) { options.push(stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { id: FIELD_STATISTICS.to_owned(), name: String::from("Statistics"), @@ -153,15 +154,15 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - origin: SourceOrigin, + origin: SessionAction, cancel: CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { let file_paths = match origin { - SourceOrigin::File(fp) => { + SessionAction::File(fp) => { vec![fp] } - SourceOrigin::Files(fps) => fps, + SessionAction::Files(fps) => fps, _ => { return Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { id: FIELD_STATISTICS.to_owned(), diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 6238ce8dc2..d8df1c5c19 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,6 +1,6 @@ use super::*; -use components::{ComponentFactory, ComponentDescriptor}; -use stypes::SourceOrigin; +use components::{ComponentDescriptor, ComponentFactory}; +use stypes::SessionAction; use tokio_util::sync::CancellationToken; const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -13,24 +13,23 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - origin: &SourceOrigin, + origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(Some(crate::Parser::DltRaw(DltRawParser::new(!matches!( origin, - SourceOrigin::Source + SessionAction::Source ))))) } } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { - vec![filepath] + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::Source => { + return false } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => return true, + SessionAction::ExportRaw(files, ..) => files, }; files.iter().any(|fp| { fp.extension() @@ -38,7 +37,7 @@ impl ComponentDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, _origin: &SourceOrigin) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(Vec::new()) } fn ident(&self) -> stypes::Ident { @@ -50,12 +49,12 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: SourceOrigin, + _origin: SessionAction, _cancel: CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { Ok(Vec::new()) }) } fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::RawParser + stypes::ComponentType::Parser } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 96373d9bad..71e8de25c2 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use super::SomeipParser; -use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; -use stypes::SourceOrigin; +use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; +use stypes::SessionAction; use tokio::{ select, time::{sleep, Duration}, @@ -24,7 +24,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -32,13 +32,14 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => return true, + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source => return true, + SessionAction::ExportRaw(..) => return false, }; files.iter().any(|fp| { fp.extension() @@ -49,7 +50,7 @@ impl ComponentDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, _origin: &SourceOrigin) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), name: String::from("Log Level"), @@ -68,7 +69,7 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: SourceOrigin, + _origin: SessionAction, cancel: CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 460dc16a50..86da226756 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,6 +1,6 @@ use components::{ComponentDescriptor, ComponentFactory}; use file_tools::is_binary; -use stypes::SourceOrigin; +use stypes::SessionAction; use super::StringTokenizer; @@ -14,7 +14,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -22,13 +22,14 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &stypes::SourceOrigin) -> bool { + fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => return true, + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source => return true, + SessionAction::ExportRaw(..) => return false, }; // If at least some file doesn't exist or it's binary - do not recommend this source !files diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 18ebbabb8a..fce8eee039 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -189,7 +189,7 @@ impl defs::Parser for PluginsParser { struct Descriptor {} impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { + fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { true } /// **ATTANTION** That's placeholder. Should be another way to delivery data diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index a6c757e901..82425b7836 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -180,7 +180,7 @@ impl defs::Parser for PluginParser { struct Descriptor {} impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, _origin: &stypes::SourceOrigin) -> bool { + fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { true } /// **ATTANTION** That's placeholder. Should be another way to delivery data diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index b709686a06..953e916c8c 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use components::{LazyLoadingResult, LazyLoadingTaskMeta}; -use stypes::{Ident, NativeError, SourceOrigin}; +use stypes::{Ident, NativeError, SessionAction}; use tokio::sync::oneshot; use uuid::Uuid; @@ -61,7 +61,7 @@ pub enum Api { /// /// * `Result` - The result containing the list of component options or an error. GetOptions { - origin: SourceOrigin, + origin: SessionAction, targets: Vec, tx: oneshot::Sender>, }, @@ -72,11 +72,11 @@ pub enum Api { /// /// # Arguments /// - /// * `SourceOrigin` - The origin type indicating the context in which the components will be used. + /// * `SessionAction` - The origin type indicating the context in which the components will be used. /// * `stypes::ComponentType` - The type of components to retrieve (e.g., parser, source). /// * `oneshot::Sender, NativeError>>` - A sender channel for delivering the result back to the client. GetComponents( - SourceOrigin, + SessionAction, stypes::ComponentType, oneshot::Sender, NativeError>>, ), @@ -84,7 +84,7 @@ pub enum Api { /// /// # Arguments /// - /// * `SourceOrigin` - The origin type indicating the context in which the validation is performed. + /// * `SessionAction` - The origin type indicating the context in which the validation is performed. /// * `Uuid` - The identifier of the component (parser, source). /// * `Vec` - A list of configuration field values. /// * `oneshot::Sender, NativeError>>` - A sender channel for delivering validation results to the client. @@ -97,7 +97,7 @@ pub enum Api { /// /// If all fields are valid and have no errors, an empty `HashMap` will be returned. Validate( - SourceOrigin, + SessionAction, Uuid, Vec, oneshot::Sender, NativeError>>, diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 27328c3954..eadabedc56 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -214,7 +214,7 @@ impl ComponentsSession { pub async fn get_options( &self, targets: Vec, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, ) -> Result { let (tx, rx) = oneshot::channel(); send( @@ -250,7 +250,7 @@ impl ComponentsSession { /// - The response from the API could not be retrieved. pub async fn get_components( &self, - origin: stypes::SourceOrigin, + origin: stypes::SessionAction, ty: stypes::ComponentType, ) -> Result, stypes::NativeError> { let (tx, rx) = oneshot::channel(); @@ -305,7 +305,7 @@ impl ComponentsSession { /// * The response from the validation API cannot be retrieved. pub async fn validate( &self, - source: stypes::SourceOrigin, + source: stypes::SessionAction, target: Uuid, fields: Vec, ) -> Result, stypes::NativeError> { diff --git a/application/apps/indexer/session/src/handlers/mod.rs b/application/apps/indexer/session/src/handlers/mod.rs index 11daf872c5..2b3c96175f 100644 --- a/application/apps/indexer/session/src/handlers/mod.rs +++ b/application/apps/indexer/session/src/handlers/mod.rs @@ -1,4 +1,3 @@ -pub mod export_raw; pub mod extract; pub mod observe; pub mod search; diff --git a/application/apps/indexer/session/src/handlers/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs similarity index 66% rename from application/apps/indexer/session/src/handlers/export_raw.rs rename to application/apps/indexer/session/src/handlers/observe/export_raw.rs index 67c6b6e17e..3be8f51fd0 100644 --- a/application/apps/indexer/session/src/handlers/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -1,5 +1,4 @@ use crate::operations::{OperationAPI, OperationResult}; -use components::Components; use definitions::{ByteSource, LogRecordOutput, LogRecordWriter, Parser}; use log::debug; use processor::producer::MessageProducer; @@ -7,13 +6,12 @@ use processor::producer::MessageProducer; use std::{ fs::File, io::{self, BufWriter, Write}, - path::{Path, PathBuf}, - sync::Arc, + path::Path, }; -use stypes::{NativeError, SessionSetup, SourceOrigin}; +use stypes::NativeError; use tokio::select; -struct ExportWriter { +pub struct ExportWriter { buffer: BufWriter, index: usize, ranges: Vec>, @@ -97,43 +95,6 @@ impl LogRecordWriter for ExportWriter { } } -pub async fn export( - operation_api: OperationAPI, - options: SessionSetup, - components: Arc>, - out_path: PathBuf, - ranges: Vec>, -) -> OperationResult { - match &options.origin { - SourceOrigin::File(..) => { - let (_, source, parser) = components.setup(&options)?; - let mut writer = ExportWriter::new(&out_path, ranges)?; - let producer = MessageProducer::new(parser, source, &mut writer); - Ok(run_producer(operation_api, producer).await?) - } - SourceOrigin::Source => { - let (_, source, parser) = components.setup(&options)?; - let mut writer = ExportWriter::new(&out_path, ranges)?; - let producer = MessageProducer::new(parser, source, &mut writer); - Ok(run_producer(operation_api, producer).await?) - } - SourceOrigin::Files(files) => { - // We are creating one single writer for all files to keep tracking ranges and current index - let mut writer = ExportWriter::new(&out_path, ranges)?; - for file in files { - if operation_api.cancellation_token().is_cancelled() { - return Ok(Some(false)); - } - let (_, source, parser) = - components.setup(&options.inherit(SourceOrigin::File(file.to_owned())))?; - let producer = MessageProducer::new(parser, source, &mut writer); - run_producer(operation_api.clone(), producer).await?; - } - Ok(Some(true)) - } - } -} - pub async fn run_producer( operation_api: OperationAPI, mut producer: MessageProducer<'_, P, S, W>, diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs new file mode 100644 index 0000000000..1c046fc572 --- /dev/null +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -0,0 +1,63 @@ +mod export_raw; +mod session; + +use crate::{ + operations::{OperationAPI, OperationResult}, + state::SessionStateAPI, +}; +use components::Components; +use processor::producer::{sde::*, MessageProducer}; +use std::sync::Arc; +use stypes::{SessionAction, SessionSetup}; + +pub async fn observing( + operation_api: OperationAPI, + state: SessionStateAPI, + options: SessionSetup, + components: Arc>, + rx_sde: Option, +) -> OperationResult<()> { + match &options.origin { + SessionAction::File(..) => { + let (desciptor, source, parser) = components.setup(&options)?; + let mut writer = + session::Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); + Ok(session::run_producer(operation_api, state, producer, None, None).await?) + } + SessionAction::Source => { + let (desciptor, source, parser) = components.setup(&options)?; + let mut writer = + session::Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); + Ok(session::run_producer(operation_api, state, producer, None, rx_sde).await?) + } + SessionAction::Files(files) => { + // Replacement of concat feature + for file in files { + let (desciptor, source, parser) = + components.setup(&options.inherit(SessionAction::File(file.to_owned())))?; + let mut writer = + session::Writer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut writer); + session::run_producer(operation_api.clone(), state.clone(), producer, None, None) + .await?; + } + Ok(Some(())) + } + SessionAction::ExportRaw(files, ranges, output) => { + // We are creating one single writer for all files to keep tracking ranges and current index + let mut writer = export_raw::ExportWriter::new(output, ranges.clone())?; + for file in files { + if operation_api.cancellation_token().is_cancelled() { + return Ok(Some(())); + } + let (_, source, parser) = + components.setup(&options.inherit(SessionAction::File(file.to_owned())))?; + let producer = MessageProducer::new(parser, source, &mut writer); + export_raw::run_producer(operation_api.clone(), producer).await?; + } + Ok(Some(())) + } + } +} diff --git a/application/apps/indexer/session/src/handlers/observe.rs b/application/apps/indexer/session/src/handlers/observe/session.rs similarity index 81% rename from application/apps/indexer/session/src/handlers/observe.rs rename to application/apps/indexer/session/src/handlers/observe/session.rs index 52d2de3c01..8759168e1f 100644 --- a/application/apps/indexer/session/src/handlers/observe.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -3,13 +3,11 @@ use crate::{ state::SessionStateAPI, tail, }; -use components::Components; use definitions::*; use log::{trace, warn}; use processor::producer::{sde::*, MessageProducer, MessageStreamItem}; -use std::sync::Arc; use std::time::Instant; -use stypes::{NativeError, SessionSetup, SourceOrigin}; +use stypes::NativeError; use tokio::{select, sync::mpsc::Receiver}; /// Duration (in milliseconds) between flushes to the session file. @@ -26,7 +24,7 @@ const SEND_DURATION: u128 = 500; /// - `attachments` stores associated `Attachment` objects, which are sent /// only after flushing the text buffer to ensure synchronization between /// log lines and attachments. -struct Writer { +pub struct Writer { /// Communication channel to the session file. state: SessionStateAPI, @@ -127,39 +125,6 @@ impl LogRecordWriter for Writer { self.id } } -pub async fn start_observing( - operation_api: OperationAPI, - state: SessionStateAPI, - options: SessionSetup, - components: Arc>, - rx_sde: Option, -) -> OperationResult<()> { - match &options.origin { - SourceOrigin::File(..) => { - let (desciptor, source, parser) = components.setup(&options)?; - let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); - Ok(run_producer(operation_api, state, producer, None, None).await?) - } - SourceOrigin::Source => { - let (desciptor, source, parser) = components.setup(&options)?; - let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); - Ok(run_producer(operation_api, state, producer, None, rx_sde).await?) - } - SourceOrigin::Files(files) => { - // Replacement of concat feature - for file in files { - let (desciptor, source, parser) = - components.setup(&options.inherit(SourceOrigin::File(file.to_owned())))?; - let mut writer = Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); - run_producer(operation_api.clone(), state.clone(), producer, None, None).await?; - } - Ok(Some(())) - } - } -} enum Next { Parsed(usize, MessageStreamItem), diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index f2abd1a4f1..2d806d7710 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -110,12 +110,6 @@ pub enum OperationKind { /// An optional string used as the field delimiter within each record in output file. Defaults can be applied if `None`. delimiter: Option, }, - ExportRaw { - setup: stypes::SessionSetup, - components: Arc>, - out_path: PathBuf, - ranges: Vec>, - }, Extract { filters: Vec, }, @@ -151,7 +145,6 @@ impl std::fmt::Display for OperationKind { OperationKind::Search { .. } => "Searching", OperationKind::SearchValues { .. } => "Searching values", OperationKind::Export { .. } => "Exporting", - OperationKind::ExportRaw { .. } => "Exporting as Raw", OperationKind::Extract { .. } => "Extracting", OperationKind::Map { .. } => "Mapping", OperationKind::Values { .. } => "Values", @@ -314,7 +307,7 @@ impl OperationAPI { match operation.kind { OperationKind::Observe(options, components) => { api.finish( - handlers::observe::start_observing( + handlers::observe::observing( api.clone(), state, options, @@ -366,26 +359,6 @@ impl OperationAPI { ) .await; } - OperationKind::ExportRaw { - setup, - components, - out_path, - ranges, - } => { - api.finish( - handlers::export_raw::export( - api.clone(), - setup, - components, - out_path, - ranges, - ) - .await - .map(|v| v.map(stypes::ResultBool)), - operation_str, - ) - .await; - } OperationKind::Extract { filters } => { let session_file = if let Some(session_file) = session_file { session_file diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy.rs index c2ccc5ff53..a38b06216a 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy.rs @@ -7,7 +7,7 @@ use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{traits::PcapReaderIterator, LegacyPcapReader, PcapBlockOwned, PcapError}; use std::{fs::File, io::Read, path::Path}; -use stypes::SourceOrigin; +use stypes::SessionAction; pub struct PcapLegacyByteSource { @@ -216,7 +216,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -224,13 +224,13 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => { + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { return false; } }; diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng.rs index e4d298846d..d8e1a17980 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng.rs @@ -7,7 +7,7 @@ use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{traits::PcapReaderIterator, PcapBlockOwned, PcapError, PcapNGReader}; use std::{fs::File, io::Read, path::Path}; -use stypes::SourceOrigin; +use stypes::SessionAction; pub struct PcapngByteSource { pcapng_reader: PcapNGReader, @@ -218,7 +218,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -226,13 +226,13 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => { + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { return false; } }; diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw.rs index 211b2d57c7..7d6165d8d1 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw.rs @@ -1,5 +1,5 @@ use bufread::BufReader; -use components::{ComponentFactory, ComponentDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use file_tools::is_binary; use std::{ @@ -7,7 +7,7 @@ use std::{ io::{BufRead, Read}, path::Path, }; -use stypes::SourceOrigin; +use stypes::SessionAction; pub struct BinaryByteSource where @@ -133,11 +133,11 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - origin: &SourceOrigin, + origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { let filename = match origin { - SourceOrigin::File(filename) => filename, + SessionAction::File(filename) => filename, _ => { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -155,13 +155,13 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { - SourceOrigin::File(filepath) => { + SessionAction::File(filepath) => { vec![filepath] } - SourceOrigin::Files(files) => files.iter().collect(), - SourceOrigin::Source => { + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { return false; } }; diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 30aadc514a..8bff17d1ea 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,10 +1,10 @@ use bufread::DeqBuffer; -use components::{ComponentFactory, ComponentDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use regex::{Captures, Regex}; use shellexpand::tilde; use std::{collections::HashMap, ffi::OsString, io::Read, path::PathBuf, process::Stdio}; -use stypes::SourceOrigin; +use stypes::SessionAction; use thiserror::Error; use tokio::{ io::AsyncWriteExt, @@ -238,7 +238,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -246,10 +246,12 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { - SourceOrigin::File(..) | SourceOrigin::Files(..) => false, - SourceOrigin::Source => true, + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, } } fn ident(&self) -> stypes::Ident { diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 262596a07f..a9e48e1c16 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -1,6 +1,6 @@ -use components::{ComponentFactory, ComponentDescriptor, StaticFieldResult}; +use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; use std::{collections::HashMap, io::Read, str}; -use stypes::{FieldDesc, LazyFieldDesc, SourceOrigin, StaticFieldDesc, Value, ValueInput}; +use stypes::{FieldDesc, LazyFieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, @@ -31,7 +31,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -39,10 +39,12 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { - SourceOrigin::File(..) | SourceOrigin::Files(..) => false, - SourceOrigin::Source => true, + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, } } fn ident(&self) -> stypes::Ident { @@ -55,7 +57,7 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } - fn fields_getter(&self, _origin: &stypes::SourceOrigin) -> components::FieldsResult { + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_PATH.to_owned(), @@ -155,7 +157,7 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: stypes::SourceOrigin, + _origin: stypes::SessionAction, _cancel: tokio_util::sync::CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { @@ -176,7 +178,7 @@ impl ComponentDescriptor for Descriptor { } fn validate( &self, - _origin: &stypes::SourceOrigin, + _origin: &stypes::SessionAction, fields: &[stypes::Field], ) -> HashMap { let mut errors = HashMap::new(); diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 28f0a7a8e2..23c560ef65 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -1,10 +1,10 @@ use bufread::DeqBuffer; -use components::{ComponentFactory, ComponentDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{io::Read, net::SocketAddr, time::Duration}; -use stypes::SourceOrigin; +use stypes::SessionAction; use tokio::net::TcpStream; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; @@ -179,7 +179,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -187,10 +187,12 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { - SourceOrigin::File(..) | SourceOrigin::Files(..) => false, - SourceOrigin::Source => true, + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, } } fn ident(&self) -> stypes::Ident { diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 83eac45219..a866cd28d9 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -1,14 +1,14 @@ use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; use bufread::DeqBuffer; -use components::{ComponentFactory, ComponentDescriptor}; +use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use log::trace; use std::{ io::Read, net::{IpAddr, Ipv4Addr}, }; -use stypes::SourceOrigin; +use stypes::SessionAction; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -142,7 +142,7 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SourceOrigin, + _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { Ok(None) @@ -150,10 +150,12 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SourceOrigin) -> bool { + fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { - SourceOrigin::File(..) | SourceOrigin::Files(..) => false, - SourceOrigin::Source => true, + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, } } fn ident(&self) -> stypes::Ident { diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index cf00b494ae..39564d485e 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,8 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ComponentOptions } from "./options"; -import type { PluginParserSettings } from "./plugins"; +import type { ComponentOptions } from './options'; +import type { PluginParserSettings } from './plugins'; -export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; +export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; /** * Represents the type of a component within the system. @@ -11,35 +11,36 @@ export type ComponentDef = { "Source": ComponentOptions } | { "Parser": Componen * functional role of the component. It is used to categorize components * according to their purpose in the data processing pipeline. */ -export type ComponentType = "RawParser" | "Parser" | "Source"; +export type ComponentType = 'RawParser' | 'Parser' | 'Source'; /** * Settings for the DLT parser. */ -export type DltParserSettings = { -/** - * Configuration for filtering DLT messages. - */ -filter_config: DltFilterConfig, -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, -/** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ -with_storage_header: boolean, -/** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ -tz: string | null, }; +export type DltParserSettings = { + /** + * Configuration for filtering DLT messages. + */ + filter_config: DltFilterConfig; + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; + /** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ + with_storage_header: boolean; + /** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ + tz: string | null; +}; /** * Supported file formats for observation. */ -export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; +export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; -export type Ident = { name: string, desc: string, uuid: string, }; +export type Ident = { name: string; desc: string; uuid: string }; export type IdentList = Array; @@ -49,132 +50,154 @@ export type IdentList = Array; * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string, interface: string | null, }; +export type MulticastInfo = { multiaddr: string; interface: string | null }; /** * Options for observing data within a session. */ -export type ObserveOptions = { -/** - * The description of the data source. - */ -origin: ObserveOrigin, -/** - * The parser configuration to be applied. - */ -parser: ParserType, }; +export type ObserveOptions = { + /** + * The description of the data source. + */ + origin: ObserveOrigin; + /** + * The parser configuration to be applied. + */ + parser: ParserType; +}; /** * Describes the source of data for observation. */ -export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; +export type ObserveOrigin = + | { File: [string, FileFormat, string] } + | { Concat: Array<[string, FileFormat, string]> } + | { Stream: [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { "Plugin": PluginParserSettings }; +export type ParserType = + | { Dlt: DltParserSettings } + | { SomeIp: SomeIpParserSettings } + | { Text: null } + | { Plugin: PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { -/** - * The working directory for the command. - */ -cwd: string, -/** - * The command to execute. - */ -command: string, -/** - * Environment variables. If empty, the default environment variables are used. - */ -envs: Map, }; +export type ProcessTransportConfig = { + /** + * The working directory for the command. + */ + cwd: string; + /** + * The command to execute. + */ + command: string; + /** + * Environment variables. If empty, the default environment variables are used. + */ + envs: Map; +}; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { -/** - * The path to the serial port. - */ -path: string, -/** - * The baud rate for the connection. - */ -baud_rate: number, -/** - * The number of data bits per frame. - */ -data_bits: number, -/** - * The flow control setting. - */ -flow_control: number, -/** - * The parity setting. - */ -parity: number, -/** - * The number of stop bits. - */ -stop_bits: number, -/** - * The delay in sending data, in milliseconds. - */ -send_data_delay: number, -/** - * Whether the connection is exclusive. - */ -exclusive: boolean, }; - -export type SessionDescriptor = { parser: Ident, source: Ident, }; - -export type SessionSetup = { origin: SourceOrigin, parser: ComponentOptions, source: ComponentOptions, }; +export type SerialTransportConfig = { + /** + * The path to the serial port. + */ + path: string; + /** + * The baud rate for the connection. + */ + baud_rate: number; + /** + * The number of data bits per frame. + */ + data_bits: number; + /** + * The flow control setting. + */ + flow_control: number; + /** + * The parity setting. + */ + parity: number; + /** + * The number of stop bits. + */ + stop_bits: number; + /** + * The delay in sending data, in milliseconds. + */ + send_data_delay: number; + /** + * Whether the connection is exclusive. + */ + exclusive: boolean; +}; + +export type SessionDescriptor = { parser: Ident; source: Ident }; + +export type SessionSetup = { + origin: SessionAction; + parser: ComponentOptions; + source: ComponentOptions; +}; /** * Settings for the SomeIp parser. */ -export type SomeIpParserSettings = { -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, }; +export type SomeIpParserSettings = { + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; +}; -export type SourceOrigin = { "File": string } | { "Files": Array } | "Source"; +export type SessionAction = { File: string } | { Files: Array } | 'Source'; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { -/** - * The address to bind the TCP connection to. - */ -bind_addr: string, }; +export type TCPTransportConfig = { + /** + * The address to bind the TCP connection to. + */ + bind_addr: string; +}; /** * Describes the transport source for a session. */ -export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; +export type Transport = + | { Process: ProcessTransportConfig } + | { TCP: TCPTransportConfig } + | { UDP: UDPTransportConfig } + | { Serial: SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { -/** - * The address to bind the UDP connection to. - */ -bind_addr: string, -/** - * A list of multicast configurations. - */ -multicast: Array, }; +export type UDPTransportConfig = { + /** + * The address to bind the UDP connection to. + */ + bind_addr: string; + /** + * A list of multicast configurations. + */ + multicast: Array; +}; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { -/** - * A list of multicast addresses to listen on. - */ -multicast_addr: Array, }; +export type UdpConnectionInfo = { + /** + * A list of multicast addresses to listen on. + */ + multicast_addr: Array; +}; diff --git a/application/apps/indexer/stypes/src/observe/extending.rs b/application/apps/indexer/stypes/src/observe/extending.rs index 78bd3a942e..804bab8eae 100644 --- a/application/apps/indexer/stypes/src/observe/extending.rs +++ b/application/apps/indexer/stypes/src/observe/extending.rs @@ -4,7 +4,7 @@ use crate::*; use thiserror::Error; impl SessionSetup { - pub fn inherit(&self, origin: SourceOrigin) -> Self { + pub fn inherit(&self, origin: SessionAction) -> Self { Self { origin, parser: self.parser.clone(), diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index beb6e1da70..7940a10236 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -7,6 +7,8 @@ mod nodejs; #[cfg(test)] mod proptest; +use std::ops::RangeInclusive; + #[cfg(feature = "rustcore")] pub use extending::*; @@ -20,9 +22,11 @@ use dlt_core::filtering::DltFilterConfig; derive(TS), ts(export, export_to = "observe.ts") )] -pub enum SourceOrigin { +/// Описывает операцию, которая должна быть произведена +pub enum SessionAction { File(PathBuf), Files(Vec), + ExportRaw(Vec, Vec>, PathBuf), Source, } @@ -46,7 +50,7 @@ pub enum ComponentDef { ts(export, export_to = "observe.ts") )] pub struct SessionSetup { - pub origin: SourceOrigin, + pub origin: SessionAction, pub parser: ComponentOptions, pub source: ComponentOptions, } @@ -98,13 +102,6 @@ pub struct Ident { ts(export, export_to = "observe.ts") )] pub enum ComponentType { - /// A parser used specifically for exporting data. - /// - /// This kind of parser is not part of the live session processing flow, - /// but instead is used to convert raw or structured data into formats - /// suitable for external use (e.g., reports, backups, archives). - RawParser, - /// A standard parser used during session processing. /// /// These parsers transform raw input data into a structured representation diff --git a/application/apps/indexer/stypes/src/observe/nodejs.rs b/application/apps/indexer/stypes/src/observe/nodejs.rs index afdb5ab534..4fb005cb46 100644 --- a/application/apps/indexer/stypes/src/observe/nodejs.rs +++ b/application/apps/indexer/stypes/src/observe/nodejs.rs @@ -14,4 +14,4 @@ try_into_js!(ObserveOrigin); try_into_js!(ObserveOptions); try_into_js!(Ident); try_into_js!(IdentList); -try_into_js!(SourceOrigin); +try_into_js!(SessionAction); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index e1fa2abd51..a14b03f559 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -205,7 +205,7 @@ gen_encode_decode_fns!(Point); gen_encode_decode_fns!(ResultSearchValues); gen_encode_decode_fns!(ResultScaledDistribution); gen_encode_decode_fns!(DltLevelDistribution); -gen_encode_decode_fns!(SourceOrigin); +gen_encode_decode_fns!(SessionAction); gen_encode_decode_fns!(IdentList); gen_encode_decode_fns!(ComponentsOptionsList); gen_encode_decode_fns!(FieldDesc); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 66587d09f4..6bad76efbe 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -66,7 +66,7 @@ impl Components { ) -> Result { let ty = stypes::ComponentType::decode(&ty).map_err(stypes::ComputationError::Decoding)?; let origin = - stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; let session = self .session .as_ref() @@ -89,7 +89,7 @@ impl Components { .map(|uuid| Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)) .collect::>()?; let origin = - stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; let session = self .session .as_ref() @@ -107,7 +107,7 @@ impl Components { options: JSArrayBuffer, ) -> Result { let origin = - stypes::SourceOrigin::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; let options = stypes::ComponentOptions::decode(&options) .map_err(stypes::ComputationError::Decoding)?; let session = self diff --git a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts index ed319fa282..b08c1c1479 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.components.spec.ts @@ -8,7 +8,7 @@ initLogger(); import { Components } from '../src/index'; import { readConfigurationFile } from './config'; import { finish } from './common'; -import { SourceOrigin, FieldDesc, LazyFieldDesc, StaticFieldDesc } from 'platform/types/bindings'; +import { SessionAction, FieldDesc, LazyFieldDesc, StaticFieldDesc } from 'platform/types/bindings'; import * as runners from './runners'; import { error } from 'platform/log/utils'; @@ -25,7 +25,7 @@ describe('Jobs', function () { return runners.unbound(config.regular, 1, async (logger, done, collector) => { try { const components = collector(await Components.create()) as Components; - const origin: SourceOrigin = { File: 'somefile' }; + const origin: SessionAction = { File: 'somefile' }; // Request available parsers and sources let parsers = await components.get(origin).parsers(); let sources = await components.get(origin).sources(); @@ -91,7 +91,7 @@ describe('Jobs', function () { return runners.unbound(config.regular, 2, async (logger, done, collector) => { try { const components = collector(await Components.create()) as Components; - const origin: SourceOrigin = { File: 'somefile' }; + const origin: SessionAction = { File: 'somefile' }; // Request available parsers and sources let parsers = await components.get(origin).parsers(); let sources = await components.get(origin).sources(); diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 0605cb5169..149ff6edc7 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -10,7 +10,7 @@ import { FieldDesc, FieldsValidationErrors, Ident, - SourceOrigin, + SessionAction, } from 'platform/types/bindings'; export class Components extends Subscriber { @@ -57,7 +57,7 @@ export class Components extends Subscriber { return this.provider.getEvents(); } - public get(origin: SourceOrigin): { + public get(origin: SessionAction): { sources(): Promise; parsers(): Promise; } { @@ -71,14 +71,14 @@ export class Components extends Subscriber { }; } - public getOptions(origin: SourceOrigin, targets: string[]): Promise> { + public getOptions(origin: SessionAction, targets: string[]): Promise> { return this.native .getOptions(origin, targets) .then((list: ComponentsOptionsList) => list.options); } public validate( - origin: SourceOrigin, + origin: SessionAction, target: string, fields: Field[], ): Promise> { diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index f828b87d0a..aae38a42bb 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -2,7 +2,7 @@ import { scope } from 'platform/env/scope'; import { getNativeModule } from '../native/native'; import { Type, Source, NativeError } from '../interfaces/errors'; import { - SourceOrigin, + SessionAction, IdentList, ComponentsOptionsList, ComponentType, @@ -143,7 +143,7 @@ export class Base extends Subscriber { }); } - public getComponents(origin: SourceOrigin, ty: ComponentType): Promise { + public getComponents(origin: SessionAction, ty: ComponentType): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { return Promise.reject(err); @@ -151,7 +151,7 @@ export class Base extends Subscriber { return new Promise((resolve, reject) => { this.native .getComponents( - protocol.encodeSourceOrigin(origin), + protocol.encodeSessionAction(origin), protocol.encodeComponentType(ty), ) .then((buf: Uint8Array) => { @@ -174,14 +174,14 @@ export class Base extends Subscriber { }); } - public getOptions(origin: SourceOrigin, targets: string[]): Promise { + public getOptions(origin: SessionAction, targets: string[]): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { return Promise.reject(err); } return new Promise((resolve, reject) => { this.native - .getOptions(protocol.encodeSourceOrigin(origin), targets) + .getOptions(protocol.encodeSessionAction(origin), targets) .then((buf: Uint8Array) => { try { resolve(protocol.decodeComponentsOptionsList(buf)); @@ -203,7 +203,7 @@ export class Base extends Subscriber { } public validate( - origin: SourceOrigin, + origin: SessionAction, target: string, fields: Field[], ): Promise { @@ -215,7 +215,7 @@ export class Base extends Subscriber { return new Promise((resolve, reject) => { this.native .validate( - protocol.encodeSourceOrigin(origin), + protocol.encodeSessionAction(origin), protocol.encodeComponentOptions(options), ) .then((buf: Uint8Array) => { diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index b6210b2a23..f2324c6ee8 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -11,7 +11,7 @@ import { import * as Events from '@platform/ipc/event/index'; import * as Requests from '@platform/ipc/request/index'; -import { Field, FieldDesc, Ident, SourceOrigin } from '@platform/types/bindings'; +import { Field, FieldDesc, Ident, SessionAction } from '@platform/types/bindings'; @SetupService(services['components']) export class Service extends Implementation { @@ -77,7 +77,7 @@ export class Service extends Implementation { }); } - public getOptions(origin: SourceOrigin, targets: string[]): Promise> { + public getOptions(origin: SessionAction, targets: string[]): Promise> { return new Promise((resolve, reject) => { Requests.IpcRequest.send( Requests.Components.GetOptions.Response, @@ -94,7 +94,7 @@ export class Service extends Implementation { } public validate( - origin: SourceOrigin, + origin: SessionAction, target: string, fields: Field[], ): Promise> { @@ -113,7 +113,7 @@ export class Service extends Implementation { .catch(reject); }); } - public get(origin: SourceOrigin): { + public get(origin: SessionAction): { sources(): Promise; parsers(): Promise; } { diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index ffeccfbdd8..259a5942ed 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -1,4 +1,4 @@ -import { SourceOrigin, Ident, SessionSetup, ComponentOptions } from '@platform/types/bindings'; +import { SessionAction, Ident, SessionSetup, ComponentOptions } from '@platform/types/bindings'; export class SessionComponents { public parser: Ident | undefined; @@ -32,7 +32,7 @@ export class SessionSourceOrigin { public readonly options: ComponentsOptions; constructor( - public readonly origin: SourceOrigin, + public readonly origin: SessionAction, public components: SessionComponents | undefined, ) { this.options = new ComponentsOptions(); @@ -56,7 +56,7 @@ export class SessionSourceOrigin { }; } - public getDef(): SourceOrigin { + public getDef(): SessionAction { return this.origin; } public getTitle(): string { diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 69b23f4737..247836101c 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -1,6 +1,6 @@ import { SchemeProvider } from '@elements/scheme/provider'; import { Field, FieldDesc, Value } from '@platform/types/bindings'; -import { SourceOrigin, LazyFieldDesc } from '@platform/types/bindings'; +import { SessionAction, LazyFieldDesc } from '@platform/types/bindings'; import { components } from '@service/components'; import { Logger } from '@env/logs'; import { diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 3c604ac082..f743a6532a 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -1,4 +1,4 @@ -import { SourceOrigin, Ident } from '@platform/types/bindings'; +import { SessionAction, Ident } from '@platform/types/bindings'; import { components } from '@service/components'; import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; diff --git a/application/platform/ipc/request/components/get_options.ts b/application/platform/ipc/request/components/get_options.ts index e9008cc829..99878e111d 100644 --- a/application/platform/ipc/request/components/get_options.ts +++ b/application/platform/ipc/request/components/get_options.ts @@ -1,14 +1,14 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { FieldDesc, SourceOrigin } from '../../../types/bindings'; +import { FieldDesc, SessionAction } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @Define({ name: 'GetComponentsOptionsRequest' }) export class Request extends SignatureRequirement { public targets: string[]; - public origin: SourceOrigin; + public origin: SessionAction; - constructor(input: { origin: SourceOrigin; targets: string[] }) { + constructor(input: { origin: SessionAction; targets: string[] }) { super(); validator.isObject(input); this.targets = validator.getAsArray(input, 'targets'); diff --git a/application/platform/ipc/request/components/get_parsers.ts b/application/platform/ipc/request/components/get_parsers.ts index 8005dc6572..dce82db555 100644 --- a/application/platform/ipc/request/components/get_parsers.ts +++ b/application/platform/ipc/request/components/get_parsers.ts @@ -1,13 +1,13 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { SourceOrigin, Ident } from '../../../types/bindings'; +import { SessionAction, Ident } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @Define({ name: 'GetAvailableParsersRequest' }) export class Request extends SignatureRequirement { - public origin: SourceOrigin; + public origin: SessionAction; - constructor(input: { origin: SourceOrigin }) { + constructor(input: { origin: SessionAction }) { super(); validator.isObject(input); this.origin = diff --git a/application/platform/ipc/request/components/get_sources.ts b/application/platform/ipc/request/components/get_sources.ts index a72f94fcc6..46b9aa0989 100644 --- a/application/platform/ipc/request/components/get_sources.ts +++ b/application/platform/ipc/request/components/get_sources.ts @@ -1,13 +1,13 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { SourceOrigin, Ident } from '../../../types/bindings'; +import { SessionAction, Ident } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @Define({ name: 'GetAvailableSourcesRequest' }) export class Request extends SignatureRequirement { - public origin: SourceOrigin; + public origin: SessionAction; - constructor(input: { origin: SourceOrigin }) { + constructor(input: { origin: SessionAction }) { super(); validator.isObject(input); this.origin = diff --git a/application/platform/ipc/request/components/validate.ts b/application/platform/ipc/request/components/validate.ts index bc52b2b11f..45e4c37e8e 100644 --- a/application/platform/ipc/request/components/validate.ts +++ b/application/platform/ipc/request/components/validate.ts @@ -1,15 +1,15 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { Field, SourceOrigin } from '../../../types/bindings'; +import { Field, SessionAction } from '../../../types/bindings'; import * as validator from '../../../env/obj'; @Define({ name: 'ValidateComponentsOptionsRequest' }) export class Request extends SignatureRequirement { - public origin: SourceOrigin; + public origin: SessionAction; public target: string; public fields: Field[]; - constructor(input: { origin: SourceOrigin; target: string; fields: Field[] }) { + constructor(input: { origin: SessionAction; target: string; fields: Field[] }) { super(); validator.isObject(input); this.target = validator.getAsNotEmptyString(input, 'target'); diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index b37165c47f..8601ff9fd1 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -143,7 +143,7 @@ export type SerialTransportConfig = { export type SessionDescriptor = { parser: Ident; source: Ident }; export type SessionSetup = { - origin: SourceOrigin; + origin: SessionAction; parser: ComponentOptions; source: ComponentOptions; }; @@ -158,7 +158,7 @@ export type SomeIpParserSettings = { fibex_file_paths: Array | null; }; -export type SourceOrigin = { File: string } | { Files: Array } | 'Source'; +export type SessionAction = { File: string } | { Files: Array } | 'Source'; /** * Configuration for TCP connections. diff --git a/application/platform/types/observe/index.ts b/application/platform/types/observe/index.ts index 1de23d3038..1098b2d2c6 100644 --- a/application/platform/types/observe/index.ts +++ b/application/platform/types/observe/index.ts @@ -17,7 +17,7 @@ export * as Bindings from '../bindings'; export { IList, IOriginDetails, IJob } from './description'; export interface ISessionSetup { - origin: Bindings.SourceOrigin; + origin: Bindings.SessionAction; parser: { uuid: string; options: Bindings.Field[]; From 210eb5be7a8f068bedab0bd1451957a86a7bd5e1 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 27 May 2025 15:24:03 +0200 Subject: [PATCH 046/121] Add timezone selector --- .../indexer/parsers/src/dlt/descriptor.rs | 8 + .../apps/indexer/stypes/bindings/observe.ts | 250 ++++++++---------- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 1 + .../scheme/complex/timezone/component.ts | 72 +++++ .../scheme/complex/timezone/module.ts | 14 + .../scheme/complex/timezone/styles.less | 18 ++ .../scheme/complex/timezone/template.html | 7 + .../app/ui/elements/scheme/element/index.ts | 3 + .../ui/elements/scheme/element/timezone.ts | 30 +++ .../app/ui/elements/scheme/inner/component.ts | 5 + .../ui/elements/scheme/inner/template.html | 6 + .../src/app/ui/elements/scheme/module.ts | 2 + .../platform/types/bindings/options.ts | 1 + 14 files changed, 283 insertions(+), 136 deletions(-) create mode 100644 application/client/src/app/ui/elements/scheme/complex/timezone/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/timezone/module.ts create mode 100644 application/client/src/app/ui/elements/scheme/complex/timezone/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/complex/timezone/template.html create mode 100644 application/client/src/app/ui/elements/scheme/element/timezone.ts diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index e7e0574fe6..aa4cd6867b 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -134,6 +134,14 @@ impl ComponentDescriptor for Descriptor { interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), binding: None, }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_TZ.to_owned(), + name: "Timezone".to_owned(), + desc: "Timezone".to_owned(), + required: true, + interface: ValueInput::Timezone, + binding: None, + }), ]; if matches!(origin, SessionAction::File(..) | SessionAction::Files(..)) { options.push(stypes::FieldDesc::Lazy(stypes::LazyFieldDesc { diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 39564d485e..fee146048f 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,8 +1,8 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ComponentOptions } from './options'; -import type { PluginParserSettings } from './plugins'; +import type { ComponentOptions } from "./options"; +import type { PluginParserSettings } from "./plugins"; -export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; +export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; /** * Represents the type of a component within the system. @@ -11,36 +11,35 @@ export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOpt * functional role of the component. It is used to categorize components * according to their purpose in the data processing pipeline. */ -export type ComponentType = 'RawParser' | 'Parser' | 'Source'; +export type ComponentType = "Parser" | "Source"; /** * Settings for the DLT parser. */ -export type DltParserSettings = { - /** - * Configuration for filtering DLT messages. - */ - filter_config: DltFilterConfig; - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; - /** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ - with_storage_header: boolean; - /** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ - tz: string | null; -}; +export type DltParserSettings = { +/** + * Configuration for filtering DLT messages. + */ +filter_config: DltFilterConfig, +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, +/** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ +with_storage_header: boolean, +/** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ +tz: string | null, }; /** * Supported file formats for observation. */ -export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; +export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; -export type Ident = { name: string; desc: string; uuid: string }; +export type Ident = { name: string, desc: string, uuid: string, }; export type IdentList = Array; @@ -50,154 +49,135 @@ export type IdentList = Array; * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string; interface: string | null }; +export type MulticastInfo = { multiaddr: string, interface: string | null, }; /** * Options for observing data within a session. */ -export type ObserveOptions = { - /** - * The description of the data source. - */ - origin: ObserveOrigin; - /** - * The parser configuration to be applied. - */ - parser: ParserType; -}; +export type ObserveOptions = { +/** + * The description of the data source. + */ +origin: ObserveOrigin, +/** + * The parser configuration to be applied. + */ +parser: ParserType, }; /** * Describes the source of data for observation. */ -export type ObserveOrigin = - | { File: [string, FileFormat, string] } - | { Concat: Array<[string, FileFormat, string]> } - | { Stream: [string, Transport] }; +export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = - | { Dlt: DltParserSettings } - | { SomeIp: SomeIpParserSettings } - | { Text: null } - | { Plugin: PluginParserSettings }; +export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { "Plugin": PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { - /** - * The working directory for the command. - */ - cwd: string; - /** - * The command to execute. - */ - command: string; - /** - * Environment variables. If empty, the default environment variables are used. - */ - envs: Map; -}; +export type ProcessTransportConfig = { +/** + * The working directory for the command. + */ +cwd: string, +/** + * The command to execute. + */ +command: string, +/** + * Environment variables. If empty, the default environment variables are used. + */ +envs: Map, }; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { - /** - * The path to the serial port. - */ - path: string; - /** - * The baud rate for the connection. - */ - baud_rate: number; - /** - * The number of data bits per frame. - */ - data_bits: number; - /** - * The flow control setting. - */ - flow_control: number; - /** - * The parity setting. - */ - parity: number; - /** - * The number of stop bits. - */ - stop_bits: number; - /** - * The delay in sending data, in milliseconds. - */ - send_data_delay: number; - /** - * Whether the connection is exclusive. - */ - exclusive: boolean; -}; - -export type SessionDescriptor = { parser: Ident; source: Ident }; - -export type SessionSetup = { - origin: SessionAction; - parser: ComponentOptions; - source: ComponentOptions; -}; +export type SerialTransportConfig = { +/** + * The path to the serial port. + */ +path: string, +/** + * The baud rate for the connection. + */ +baud_rate: number, +/** + * The number of data bits per frame. + */ +data_bits: number, +/** + * The flow control setting. + */ +flow_control: number, +/** + * The parity setting. + */ +parity: number, +/** + * The number of stop bits. + */ +stop_bits: number, +/** + * The delay in sending data, in milliseconds. + */ +send_data_delay: number, +/** + * Whether the connection is exclusive. + */ +exclusive: boolean, }; /** - * Settings for the SomeIp parser. + * Описывает операцию, которая должна быть произведена */ -export type SomeIpParserSettings = { - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; -}; +export type SessionAction = { "File": string } | { "Files": Array } | { "ExportRaw": [Array, Array<{ start: bigint, end: bigint, }>, string] } | "Source"; -export type SessionAction = { File: string } | { Files: Array } | 'Source'; +export type SessionDescriptor = { parser: Ident, source: Ident, }; + +export type SessionSetup = { origin: SessionAction, parser: ComponentOptions, source: ComponentOptions, }; + +/** + * Settings for the SomeIp parser. + */ +export type SomeIpParserSettings = { +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, }; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { - /** - * The address to bind the TCP connection to. - */ - bind_addr: string; -}; +export type TCPTransportConfig = { +/** + * The address to bind the TCP connection to. + */ +bind_addr: string, }; /** * Describes the transport source for a session. */ -export type Transport = - | { Process: ProcessTransportConfig } - | { TCP: TCPTransportConfig } - | { UDP: UDPTransportConfig } - | { Serial: SerialTransportConfig }; +export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { - /** - * The address to bind the UDP connection to. - */ - bind_addr: string; - /** - * A list of multicast configurations. - */ - multicast: Array; -}; +export type UDPTransportConfig = { +/** + * The address to bind the UDP connection to. + */ +bind_addr: string, +/** + * A list of multicast configurations. + */ +multicast: Array, }; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { - /** - * A list of multicast addresses to listen on. - */ - multicast_addr: Array; -}; +export type UdpConnectionInfo = { +/** + * A list of multicast addresses to listen on. + */ +multicast_addr: Array, }; diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 7916bb79a8..b1836524b0 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -23,4 +23,4 @@ export type StaticFieldDesc = { id: string, name: string, desc: string, required export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | "Timezone" | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 9bfdeabf55..b12ee7f2a0 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -158,6 +158,7 @@ pub enum ValueInput { Files(Vec), File(Vec), Directory, + Timezone, Bound { output: Box, inputs: Vec, diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/component.ts b/application/client/src/app/ui/elements/scheme/complex/timezone/component.ts new file mode 100644 index 0000000000..f2317d6aa0 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/timezone/component.ts @@ -0,0 +1,72 @@ +import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { Element, TimezoneSelectorElement } from '../../element'; +import { bridge } from '@service/bridge'; +import { File } from '@platform/types/files'; +import { components } from '@env/decorators/initial'; +import { Timezone } from '@ui/elements/timezones/timezone'; + +interface Tz { + name: string; + utc: string; + offset: number; +} + +@Component({ + selector: 'app-settings-scheme-tz-selector', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class TimezoneSelector extends ChangesDetector implements AfterContentInit { + @Input() element!: Element; + @Input() inner!: TimezoneSelectorElement; + + public tz: Tz | undefined; + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void {} + + public ngOnSelect() { + const subscription = this.ilc() + .services.ui.popup.open({ + component: { + factory: components.get('app-elements-timezone-selector'), + inputs: { + selected: (timezone: Timezone): void => { + if (timezone.name.toLowerCase().startsWith('utc')) { + this.tz = undefined; + } else { + this.tz = { + name: timezone.name, + offset: timezone.offset, + utc: timezone.utc, + }; + } + this.detectChanges(); + }, + }, + }, + closeOnKey: 'Escape', + width: 350, + uuid: 'app-elements-timezone-selector', + }) + .subjects.get() + .closed.subscribe(() => { + subscription.unsubscribe(); + }); + } + + public ngOnDrop() { + this.tz = undefined; + this.detectChanges(); + } +} +export interface TimezoneSelector extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/module.ts b/application/client/src/app/ui/elements/scheme/complex/timezone/module.ts new file mode 100644 index 0000000000..433b2d0342 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/timezone/module.ts @@ -0,0 +1,14 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIcon } from '@angular/material/icon'; + +import { TimezoneSelector } from './component'; + +@NgModule({ + imports: [CommonModule, MatButtonModule, MatIcon], + declarations: [TimezoneSelector], + exports: [TimezoneSelector], + bootstrap: [TimezoneSelector], +}) +export class TimezoneSelectorModule {} diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/styles.less b/application/client/src/app/ui/elements/scheme/complex/timezone/styles.less new file mode 100644 index 0000000000..a522203066 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/timezone/styles.less @@ -0,0 +1,18 @@ +@import '../../../../styles/variables.less'; + + +:host { + position: relative; + display: flex; + flex-direction: row; + align-items: center; + & > * { + margin: 6px; + } + & > *:first-child { + margin-left: 12px; + } + & > *:last-child { + margin-right: 12px; + } +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/template.html b/application/client/src/app/ui/elements/scheme/complex/timezone/template.html new file mode 100644 index 0000000000..f5c7536580 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/complex/timezone/template.html @@ -0,0 +1,7 @@ + + +

Not selected

+

{{tz.name}} ({{tz.utc}})

diff --git a/application/client/src/app/ui/elements/scheme/element/index.ts b/application/client/src/app/ui/elements/scheme/element/index.ts index a2dd31b57c..92ee1c3f3f 100644 --- a/application/client/src/app/ui/elements/scheme/element/index.ts +++ b/application/client/src/app/ui/elements/scheme/element/index.ts @@ -7,6 +7,7 @@ import * as InputElement from './input_field'; import * as ListElement from './list'; import * as NamedValuesElement from './named'; import * as NestedDictionaryElement from './nested_dictionary'; +import * as TimezoneSelectorElement from './timezone'; export { CheckboxElement } from './checkbox'; export { FilesFolderSelectorElement } from './files_selector'; @@ -14,6 +15,7 @@ export { InputElement } from './input_field'; export { ListElement } from './list'; export { NamedValuesElement } from './named'; export { NestedDictionaryElement, NestedDictionaryStructure } from './nested_dictionary'; +export { TimezoneSelectorElement } from './timezone'; /** export type ValueInput = @@ -71,6 +73,7 @@ export class Element { ListElement, NamedValuesElement, NestedDictionaryElement, + TimezoneSelectorElement, ]) { let element = el.tryFromOrigin(origin); if (element !== undefined) { diff --git a/application/client/src/app/ui/elements/scheme/element/timezone.ts b/application/client/src/app/ui/elements/scheme/element/timezone.ts new file mode 100644 index 0000000000..0335e69262 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/element/timezone.ts @@ -0,0 +1,30 @@ +import { ElementInner } from './index'; +import { Value, ValueInput } from '@platform/types/bindings'; + +export class TimezoneSelectorElement implements ElementInner { + public value: number = -1; + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return false; + } + + public getValue(): Value { + return { Number: this.value }; + } +} + +export function tryFromOrigin(origin: ValueInput): TimezoneSelectorElement | undefined { + if (typeof origin === 'string' && origin === 'Timezone') { + return new TimezoneSelectorElement(); + } else { + return undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/inner/component.ts b/application/client/src/app/ui/elements/scheme/inner/component.ts index 425aff5738..6f2420ae56 100644 --- a/application/client/src/app/ui/elements/scheme/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/inner/component.ts @@ -31,6 +31,7 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit public elListElement: els.ListElement | undefined; public elNamedValuesElement: els.NamedValuesElement | undefined; public elNestedDictionaryElement: els.NestedDictionaryElement | undefined; + public elTimezoneSelectorElement: els.TimezoneSelectorElement | undefined; public field: boolean = false; @@ -56,6 +57,10 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit this.element.inner instanceof els.NestedDictionaryElement ? this.element.inner : undefined; + this.elTimezoneSelectorElement = + this.element.inner instanceof els.TimezoneSelectorElement + ? this.element.inner + : undefined; } public ngAfterViewInit(): void { diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/inner/template.html index b8978e4283..a98c24b300 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/inner/template.html @@ -26,7 +26,13 @@ + + + + + + diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts index 906ce4b25d..102b568d56 100644 --- a/application/client/src/app/ui/elements/scheme/module.ts +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -11,6 +11,7 @@ import { MatSelectModule } from '@angular/material/select'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { NestedDictionaryModule } from './complex/nested_dictionary/module'; import { FilesSelectorModule } from './complex/files_selector/module'; +import { TimezoneSelectorModule } from './complex/timezone/module'; import { SettingsScheme } from './component'; import { SchemeEntry } from './entry/component'; @@ -31,6 +32,7 @@ import { SchemeEntryElement } from './inner/component'; MatProgressBarModule, NestedDictionaryModule, FilesSelectorModule, + TimezoneSelectorModule, ], declarations: [SettingsScheme, SchemeEntry, SchemeEntryElement], exports: [SettingsScheme], diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 5081384256..1cb5c92ba4 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -67,4 +67,5 @@ export type ValueInput = | { Files: Array } | { File: Array } | 'Directory' + | 'Timezone' | { Bound: { output: ValueInput; inputs: Array } }; From 9a4ebe54a275e6de0b770608b8874e0ac3d7d1a1 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 27 May 2025 18:32:59 +0200 Subject: [PATCH 047/121] Update SomeIp descriptor --- application/apps/indexer/parsers/Cargo.toml | 1 - application/apps/indexer/parsers/src/lib.rs | 2 +- .../indexer/parsers/src/someip/descriptor.rs | 70 ++++++------------- .../apps/rustcore/rs-bindings/Cargo.lock | 1 - 4 files changed, 24 insertions(+), 50 deletions(-) diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 8c8dc59c6f..06c207e029 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -18,7 +18,6 @@ someip-messages = { git = "https://github.com/esrlabs/someip" } someip-payload = { git = "https://github.com/esrlabs/someip-payload" } uuid = { workspace = true , features = ["serde", "v4"] } tokio-util.workspace = true -tokio.workspace = true stypes = { path = "../stypes", features=["rustcore"] } definitions = { path = "../definitions" } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 9af503abe7..f1ec4e327a 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,7 +1,7 @@ #![deny(unused_crate_dependencies)] use components::Components; -use definitions::{LogRecordOutput, LogRecordWriter, ParseOperationResult, ParserError}; +use definitions::{LogRecordOutput, ParserError}; pub mod dlt; pub mod prelude; pub mod someip; diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 71e8de25c2..da5156bd9a 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,13 +1,5 @@ -use std::collections::HashMap; - -use super::SomeipParser; -use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; -use stypes::SessionAction; -use tokio::{ - select, - time::{sleep, Duration}, -}; -use tokio_util::sync::CancellationToken; +use components::{ComponentDescriptor, ComponentFactory}; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, @@ -15,8 +7,8 @@ const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ // TODO: fields IDs could be same for diff parser/source... on level of Options event // we should also provide a master of field to prevent conflicts. -const FIELD_LOG_LEVEL: &str = "log_level"; -const FIELD_STATISTICS: &str = "statistics"; +const FIELD_FIBEX_FILES: &str = "SOMEIP_PARSER_FIELD_FIBEX_FILES"; +const FIELD_TZ: &str = "SOMEIP_PARSER_FIELD_TIMEZONE"; #[derive(Default)] pub struct Descriptor {} @@ -51,15 +43,26 @@ impl ComponentDescriptor for Descriptor { }) } fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { - Ok(vec![stypes::FieldDesc::Static(stypes::StaticFieldDesc { - id: FIELD_LOG_LEVEL.to_owned(), - name: String::from("Log Level"), - desc: String::from("DLT Log Level"), - required: true, - interface: stypes::ValueInput::KeyString(HashMap::new()), - binding: None, - })]) + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_FIBEX_FILES.to_owned(), + name: "Fibex Files".to_owned(), + desc: "Fibex Files".to_owned(), + required: true, + interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_TZ.to_owned(), + name: "Timezone".to_owned(), + desc: "Timezone".to_owned(), + required: true, + interface: ValueInput::Timezone, + binding: None, + }), + ]) } + fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("SomeIP Parser"), @@ -67,33 +70,6 @@ impl ComponentDescriptor for Descriptor { uuid: SOMEIP_PARSER_UUID, } } - fn lazy_fields_getter( - &self, - _origin: SessionAction, - cancel: CancellationToken, - ) -> components::LazyFieldsTask { - Box::pin(async move { - // Sleep a little to emulate loading - let duration = Duration::from_millis(100); - select! { - _ = sleep(duration) => { - // no cancelation - }, - _ = cancel.cancelled() => { - // cancelled - return Ok(Vec::new()); - } - }; - Ok(vec![StaticFieldResult::Success(stypes::StaticFieldDesc { - id: FIELD_STATISTICS.to_owned(), - name: String::from("Example"), - desc: String::from("Example"), - required: true, - interface: stypes::ValueInput::KeyString(HashMap::new()), - binding: None, - })]) - }) - } fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Parser } diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 810aec2fd1..2b9aa6766f 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2463,7 +2463,6 @@ dependencies = [ "someip-payload", "someip-tools", "stypes", - "tokio", "tokio-util", "uuid", ] From 2f4e27cdd0458ba382ba2be4d73958a74bd7c101 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 29 May 2025 12:06:26 +0200 Subject: [PATCH 048/121] Allow collection of fields --- .../app/ui/elements/scheme/entry/component.ts | 30 +++++++- .../complex/fields_collection/component.ts | 44 +++++++++++ .../inner/complex/fields_collection/module.ts | 15 ++++ .../complex/fields_collection/provider.ts | 74 +++++++++++++++++++ .../complex/fields_collection/styles.less | 8 ++ .../complex/fields_collection/template.html | 15 ++++ .../complex/files_selector/component.ts | 0 .../inner}/complex/files_selector/module.ts | 0 .../inner/complex/files_selector/styles.less | 8 ++ .../complex/files_selector/template.html | 0 .../scheme/entry/inner/complex/module.ts | 20 +++++ .../complex/nested_dictionary/component.ts | 0 .../complex/nested_dictionary/module.ts | 0 .../inner}/complex/nested_dictionary/state.ts | 0 .../nested_dictionary/structure/component.ts | 0 .../nested_dictionary/structure/section.ts | 0 .../nested_dictionary/structure/statentity.ts | 0 .../nested_dictionary/structure/styles.less | 2 +- .../nested_dictionary/structure/template.html | 0 .../complex/nested_dictionary/styles.less | 2 +- .../complex/nested_dictionary/summary.ts | 0 .../complex/nested_dictionary/template.html | 0 .../inner}/complex/timezone/component.ts | 0 .../inner}/complex/timezone/module.ts | 0 .../inner}/complex/timezone/styles.less | 2 +- .../inner}/complex/timezone/template.html | 0 .../scheme/{ => entry}/inner/component.ts | 51 ++++++++++++- .../{ => entry/inner}/element/checkbox.ts | 0 .../inner/element/fields_collection/index.ts | 53 +++++++++++++ .../element/fields_collection/provider.ts | 74 +++++++++++++++++++ .../inner}/element/files_selector.ts | 0 .../scheme/{ => entry/inner}/element/index.ts | 8 +- .../{ => entry/inner}/element/input_field.ts | 20 +++-- .../scheme/{ => entry/inner}/element/list.ts | 0 .../scheme/{ => entry/inner}/element/named.ts | 0 .../inner}/element/nested_dictionary.ts | 0 .../{ => entry/inner}/element/timezone.ts | 0 .../ui/elements/scheme/entry/inner/module.ts | 35 +++++++++ .../inner}/styles.less | 1 - .../scheme/{ => entry}/inner/template.html | 31 ++++++++ .../app/ui/elements/scheme/inner/styles.less | 7 -- .../src/app/ui/elements/scheme/module.ts | 12 +-- .../src/app/ui/env/extentions/changes.ts | 21 +++++- 43 files changed, 496 insertions(+), 37 deletions(-) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/files_selector/component.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/files_selector/module.ts (100%) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/files_selector/template.html (100%) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/module.ts rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/component.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/module.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/state.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/structure/component.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/structure/section.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/structure/statentity.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/structure/styles.less (94%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/structure/template.html (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/styles.less (97%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/summary.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/nested_dictionary/template.html (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/timezone/component.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/timezone/module.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/timezone/styles.less (83%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/complex/timezone/template.html (100%) rename application/client/src/app/ui/elements/scheme/{ => entry}/inner/component.ts (61%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/checkbox.ts (100%) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/files_selector.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/index.ts (91%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/input_field.ts (75%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/list.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/named.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/nested_dictionary.ts (100%) rename application/client/src/app/ui/elements/scheme/{ => entry/inner}/element/timezone.ts (100%) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/module.ts rename application/client/src/app/ui/elements/scheme/{complex/files_selector => entry/inner}/styles.less (95%) rename application/client/src/app/ui/elements/scheme/{ => entry}/inner/template.html (59%) delete mode 100644 application/client/src/app/ui/elements/scheme/inner/styles.less diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts index 6c75c97ba2..342c6bf6c5 100644 --- a/application/client/src/app/ui/elements/scheme/entry/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -11,14 +11,40 @@ import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { SchemeProvider, ForcedValueChanges } from '../provider'; import { FieldDesc, LazyFieldDesc, StaticFieldDesc, ValueInput } from '@platform/types/bindings'; -import { ChangeEvent, Element } from '../element'; +import { ChangeEvent, Element } from './inner/element'; import { WrappedField } from '../field'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatSelectModule } from '@angular/material/select'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { SchemeEntryElement } from './inner/component'; + @Component({ selector: 'app-settings-scheme-entry', templateUrl: './template.html', styleUrls: ['./styles.less'], - standalone: false, + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatCheckboxModule, + MatCardModule, + MatDividerModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatSelectModule, + MatProgressBarModule, + SchemeEntryElement, + ], + standalone: true, }) @Initial() @Ilc() diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts new file mode 100644 index 0000000000..d0079f4f3d --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts @@ -0,0 +1,44 @@ +import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { Initial } from '@env/decorators/initial'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { Element, FieldsCollectionElement } from '../../element'; +import { FieldDesc } from '@platform/types/bindings'; +import { Proivder } from './provider'; + +import * as obj from '@platform/env/obj'; + +@Component({ + selector: 'app-settings-scheme-fields-collection-selector', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Initial() +@Ilc() +export class FieldsCollection extends ChangesDetector implements AfterContentInit { + @Input() element!: Element; + @Input() inner!: FieldsCollectionElement; + + public desc: FieldDesc[] = []; + public fields: FieldDesc[][] = []; + public provider: Proivder = new Proivder(); + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public ngAfterContentInit(): void { + // this.desc = this.inner.elements.map((el) => { + // return { Static: el }; + // }); + } + + public ngOnAdd() { + const fields = this.desc.map((field) => { + return obj.clone(field); + }); + this.fields.push(fields); + this.detectChanges(); + } +} +export interface FieldsCollection extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts new file mode 100644 index 0000000000..b865c60dc1 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIcon } from '@angular/material/icon'; + +import { FieldsCollection } from './component'; +import { SchemeEntry } from '../../../component'; + +@NgModule({ + imports: [CommonModule, MatButtonModule, MatIcon, SchemeEntry], + declarations: [FieldsCollection], + exports: [FieldsCollection], + bootstrap: [], +}) +export class FieldsCollectionModule {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts new file mode 100644 index 0000000000..28971a9568 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts @@ -0,0 +1,74 @@ +import { SchemeProvider } from '@elements/scheme/provider'; +import { Field, FieldDesc, Value } from '@platform/types/bindings'; +import { components } from '@service/components'; +import { Logger } from '@env/logs'; +import { + LoadingCancelledEvent, + LoadingDoneEvent, + LoadingErrorEvent, + LoadingErrorsEvent, +} from '@platform/types/components'; +import { WrappedField } from '@ui/elements/scheme/field'; +import { SessionSourceOrigin } from '@service/session/origin'; + +export class Proivder extends SchemeProvider { + protected pending: string[] = []; + protected readonly logger = new Logger(`ObserveSetupProivder`); + protected readonly values: Map = new Map(); + protected fields: string[] = []; + + constructor() { + super(); + // this.register( + // components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), + // components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), + // components.subjects.get().LoadingErrors.subscribe(this.onLoadingErrors.bind(this)), + // ); + } + + public override get(): Promise { + return Promise.resolve([]); + } + + public override setValue(uuid: string, value: Value): void { + if (!this.fields.includes(uuid)) { + this.logger.error(`Field ${uuid} doesn't belong to current provider`); + return; + } + this.values.set(uuid, value); + const fields: Field[] = []; + this.values.forEach((value, id) => { + fields.push({ id, value }); + }); + } + + public override destroy(): Promise { + this.unsubscribe(); + this.subjects.destroy(); + return Promise.resolve(); + } + + protected onLoadingDone(event: LoadingDoneEvent) {} + + protected onLoadingError(event: LoadingErrorEvent) { + const errors = new Map(); + event.fields.forEach((id) => { + if (!this.pending.includes(id)) { + return; + } + errors.set(id, event.error); + }); + errors.size > 0 && this.subjects.get().error.emit(errors); + } + protected onLoadingErrors(event: LoadingErrorsEvent) { + const errors = new Map(); + event.errors.forEach((error) => { + if (!this.pending.includes(error.id)) { + return; + } + errors.set(error.id, error.err); + }); + errors.size > 0 && this.subjects.get().error.emit(errors); + } + protected onLoadingCancelled(event: LoadingCancelledEvent) {} +} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less new file mode 100644 index 0000000000..48ac2ced20 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less @@ -0,0 +1,8 @@ +@import '../../../../../../styles/variables.less'; + + +:host { + position: relative; + display: block; + +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html new file mode 100644 index 0000000000..927351f554 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html @@ -0,0 +1,15 @@ + + + +
+ +
diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/files_selector/component.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/files_selector/module.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less new file mode 100644 index 0000000000..48ac2ced20 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less @@ -0,0 +1,8 @@ +@import '../../../../../../styles/variables.less'; + + +:host { + position: relative; + display: block; + +} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/files_selector/template.html rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/module.ts new file mode 100644 index 0000000000..24546ed890 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NestedDictionaryModule } from './nested_dictionary/module'; +import { FilesSelectorModule } from './files_selector/module'; +import { TimezoneSelectorModule } from './timezone/module'; + +@NgModule({ + imports: [ + CommonModule, + NestedDictionaryModule, + FilesSelectorModule, + TimezoneSelectorModule, + ], + exports: [ + NestedDictionaryModule, + FilesSelectorModule, + TimezoneSelectorModule, + ], +}) +export class ComplexFieldsModule {} diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/component.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/module.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/module.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/module.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/state.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/component.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/component.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/component.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/section.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/section.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/section.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/statentity.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/statentity.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/statentity.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/styles.less similarity index 94% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/styles.less index 771b4d60aa..36b0c8d9c7 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/styles.less @@ -1,4 +1,4 @@ -@import '../../../../../styles/variables.less'; +@import '../../../../../../../styles/variables.less'; app-settings-scheme-nested-dictionary-structure { diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/template.html similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/structure/template.html rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/structure/template.html diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/styles.less similarity index 97% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/styles.less index f2bc6e7681..7a70beb854 100644 --- a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/styles.less @@ -1,4 +1,4 @@ -@import '../../../../styles/variables.less'; +@import '../../../../../../styles/variables.less'; :host { diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/summary.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/summary.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/summary.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/template.html similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/nested_dictionary/template.html rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/template.html diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/timezone/component.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/module.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/timezone/module.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/module.ts diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/styles.less similarity index 83% rename from application/client/src/app/ui/elements/scheme/complex/timezone/styles.less rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/styles.less index a522203066..05e40f65dd 100644 --- a/application/client/src/app/ui/elements/scheme/complex/timezone/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/styles.less @@ -1,4 +1,4 @@ -@import '../../../../styles/variables.less'; +@import '../../../../../../styles/variables.less'; :host { diff --git a/application/client/src/app/ui/elements/scheme/complex/timezone/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html similarity index 100% rename from application/client/src/app/ui/elements/scheme/complex/timezone/template.html rename to application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html diff --git a/application/client/src/app/ui/elements/scheme/inner/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts similarity index 61% rename from application/client/src/app/ui/elements/scheme/inner/component.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/component.ts index 6f2420ae56..9baaf3b673 100644 --- a/application/client/src/app/ui/elements/scheme/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts @@ -8,16 +8,46 @@ import { import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import { SchemeProvider } from '../provider'; -import { Element } from '../element'; +import { SchemeProvider } from '../../provider'; +import { Element } from './element'; -import * as els from '../element'; +import { ComplexFieldsModule } from './complex/module'; + +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCardModule } from '@angular/material/card'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatButtonModule } from '@angular/material/button'; +import { MatSelectModule } from '@angular/material/select'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatIcon } from '@angular/material/icon'; + +import * as els from './element'; @Component({ selector: 'app-settings-scheme-element', templateUrl: './template.html', styleUrls: ['./styles.less'], - standalone: false, + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + MatCheckboxModule, + MatCardModule, + MatDividerModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatSelectModule, + MatProgressBarModule, + MatIcon, + ComplexFieldsModule, + SchemeEntryElement, + ], + standalone: true, }) @Initial() @Ilc() @@ -32,6 +62,7 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit public elNamedValuesElement: els.NamedValuesElement | undefined; public elNestedDictionaryElement: els.NestedDictionaryElement | undefined; public elTimezoneSelectorElement: els.TimezoneSelectorElement | undefined; + public elFieldsCollectionElement: els.FieldsCollectionElement | undefined; public field: boolean = false; @@ -61,6 +92,10 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit this.element.inner instanceof els.TimezoneSelectorElement ? this.element.inner : undefined; + this.elFieldsCollectionElement = + this.element.inner instanceof els.FieldsCollectionElement + ? this.element.inner + : undefined; } public ngAfterViewInit(): void { @@ -70,5 +105,13 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit }), ); } + + public ngOnAddCollection() { + if (!this.elFieldsCollectionElement) { + return; + } + this.elFieldsCollectionElement.add(); + this.detectChanges(); + } } export interface SchemeEntryElement extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/element/checkbox.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/checkbox.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts new file mode 100644 index 0000000000..2aae4c147b --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts @@ -0,0 +1,53 @@ +import { ElementInner, Element } from '../index'; +import { StaticFieldDesc, Value, ValueInput } from '@platform/types/bindings'; +import { Provider } from './provider'; + +import * as obj from '@platform/env/obj'; + +export class FieldsCollectionElement implements ElementInner { + public value: number = -1; + + public elements: Element[][] = []; + public provider: Provider = new Provider(); + + public constructor( + public readonly spec: StaticFieldDesc[], + public readonly addTitle: string | undefined, + ) {} + + public add(): void { + const collection = this.spec.map((field) => { + return new Element(field.id + '__', field.interface); + }); + this.elements.push(collection); + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public isField(): boolean { + return false; + } + + public getValue(): Value { + return { Number: this.value }; + } +} + +export function tryFromOrigin(origin: ValueInput): FieldsCollectionElement | undefined { + const vl = origin as { + FieldsCollection: { elements: StaticFieldDesc[]; add_title: string }; + }; + if (!vl.FieldsCollection) { + return undefined; + } + return new FieldsCollectionElement( + vl.FieldsCollection.elements, + vl.FieldsCollection.add_title.trim() === '' ? undefined : vl.FieldsCollection.add_title, + ); +} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts new file mode 100644 index 0000000000..3824574e42 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts @@ -0,0 +1,74 @@ +import { SchemeProvider } from '@elements/scheme/provider'; +import { Field, FieldDesc, Value } from '@platform/types/bindings'; +import { components } from '@service/components'; +import { Logger } from '@env/logs'; +import { + LoadingCancelledEvent, + LoadingDoneEvent, + LoadingErrorEvent, + LoadingErrorsEvent, +} from '@platform/types/components'; +import { WrappedField } from '@ui/elements/scheme/field'; +import { SessionSourceOrigin } from '@service/session/origin'; + +export class Provider extends SchemeProvider { + protected pending: string[] = []; + protected readonly logger = new Logger(`ObserveSetupProivder`); + protected readonly values: Map = new Map(); + protected fields: string[] = []; + + constructor() { + super(); + // this.register( + // components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), + // components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), + // components.subjects.get().LoadingErrors.subscribe(this.onLoadingErrors.bind(this)), + // ); + } + + public override get(): Promise { + return Promise.resolve([]); + } + + public override setValue(uuid: string, value: Value): void { + if (!this.fields.includes(uuid)) { + this.logger.error(`Field ${uuid} doesn't belong to current provider`); + return; + } + this.values.set(uuid, value); + const fields: Field[] = []; + this.values.forEach((value, id) => { + fields.push({ id, value }); + }); + } + + public override destroy(): Promise { + this.unsubscribe(); + this.subjects.destroy(); + return Promise.resolve(); + } + + protected onLoadingDone(event: LoadingDoneEvent) {} + + protected onLoadingError(event: LoadingErrorEvent) { + const errors = new Map(); + event.fields.forEach((id) => { + if (!this.pending.includes(id)) { + return; + } + errors.set(id, event.error); + }); + errors.size > 0 && this.subjects.get().error.emit(errors); + } + protected onLoadingErrors(event: LoadingErrorsEvent) { + const errors = new Map(); + event.errors.forEach((error) => { + if (!this.pending.includes(error.id)) { + return; + } + errors.set(error.id, error.err); + }); + errors.size > 0 && this.subjects.get().error.emit(errors); + } + protected onLoadingCancelled(event: LoadingCancelledEvent) {} +} diff --git a/application/client/src/app/ui/elements/scheme/element/files_selector.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/files_selector.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts diff --git a/application/client/src/app/ui/elements/scheme/element/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts similarity index 91% rename from application/client/src/app/ui/elements/scheme/element/index.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts index 92ee1c3f3f..cebf7a5067 100644 --- a/application/client/src/app/ui/elements/scheme/element/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts @@ -8,6 +8,7 @@ import * as ListElement from './list'; import * as NamedValuesElement from './named'; import * as NestedDictionaryElement from './nested_dictionary'; import * as TimezoneSelectorElement from './timezone'; +import * as FieldsCollectionElement from './fields_collection'; export { CheckboxElement } from './checkbox'; export { FilesFolderSelectorElement } from './files_selector'; @@ -16,12 +17,13 @@ export { ListElement } from './list'; export { NamedValuesElement } from './named'; export { NestedDictionaryElement, NestedDictionaryStructure } from './nested_dictionary'; export { TimezoneSelectorElement } from './timezone'; +export { FieldsCollectionElement } from './fields_collection'; /** export type ValueInput = | { Checkbox: boolean } | { Number: number } - | { String: string } + | { String: [string, string] } | { Numbers: [Array, number] } | { Strings: [Array, string] } | { NamedBools: Array<[string, boolean]> } @@ -37,6 +39,9 @@ export type ValueInput = | { Files: Array } | { File: Array } | 'Directory' + | 'Timezone' + | { InputsCollection: { elements: Array; add_title: string } } + | { FieldsCollection: { elements: Array; add_title: string } } | { Bound: { output: ValueInput; inputs: Array } }; */ @@ -74,6 +79,7 @@ export class Element { NamedValuesElement, NestedDictionaryElement, TimezoneSelectorElement, + FieldsCollectionElement, ]) { let element = el.tryFromOrigin(origin); if (element !== undefined) { diff --git a/application/client/src/app/ui/elements/scheme/element/input_field.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts similarity index 75% rename from application/client/src/app/ui/elements/scheme/element/input_field.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts index da4d66321f..c559f5f859 100644 --- a/application/client/src/app/ui/elements/scheme/element/input_field.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts @@ -11,6 +11,7 @@ export class InputElement implements ElementInner { constructor( public defaults: T, + public placeholder: string, protected readonly innerType: InnerType, protected readonly getter: (value: T) => Value, ) { @@ -44,18 +45,23 @@ export class InputElement implements ElementInner { export function tryFromOrigin(origin: ValueInput): InputElement | undefined { function as_string(origin: ValueInput): InputElement | undefined { - const vl = origin as { String: string }; - return typeof vl.String === 'string' - ? new InputElement(vl.String, InnerType.String, (value: string): Value => { - return { String: value }; - }) + const vl = origin as { String: [string, string] }; + return typeof vl.String[0] === 'string' + ? new InputElement( + vl.String[0], + vl.String[1], + InnerType.String, + (value: string): Value => { + return { String: value }; + }, + ) : undefined; } function as_number(origin: ValueInput): InputElement | undefined { const vl = origin as { Number: number }; return typeof vl.Number === 'number' - ? new InputElement(vl.Number, InnerType.Number, (value: number): Value => { + ? new InputElement(vl.Number, '', InnerType.Number, (value: number): Value => { return { Number: typeof value === 'number' @@ -69,7 +75,7 @@ export function tryFromOrigin(origin: ValueInput): InputElement } if ((origin as { Number: number }).Number !== undefined) { return as_number(origin) as InputElement; - } else if ((origin as { String: string }).String !== undefined) { + } else if ((origin as { String: [string, string] }).String !== undefined) { return as_string(origin) as InputElement; } else { return undefined; diff --git a/application/client/src/app/ui/elements/scheme/element/list.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/list.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts diff --git a/application/client/src/app/ui/elements/scheme/element/named.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/named.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts diff --git a/application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/nested_dictionary.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts diff --git a/application/client/src/app/ui/elements/scheme/element/timezone.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts similarity index 100% rename from application/client/src/app/ui/elements/scheme/element/timezone.ts rename to application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/module.ts new file mode 100644 index 0000000000..b46bf8815e --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/module.ts @@ -0,0 +1,35 @@ +// import { NgModule } from '@angular/core'; +// import { CommonModule } from '@angular/common'; +// import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +// import { MatCardModule } from '@angular/material/card'; +// import { MatDividerModule } from '@angular/material/divider'; +// import { MatFormFieldModule } from '@angular/material/form-field'; +// import { MatInputModule } from '@angular/material/input'; +// import { MatCheckboxModule } from '@angular/material/checkbox'; +// import { MatButtonModule } from '@angular/material/button'; +// import { MatSelectModule } from '@angular/material/select'; +// import { MatProgressBarModule } from '@angular/material/progress-bar'; + +// import { SchemeEntryElement } from './component'; +// import { ComplexFieldsModule } from './complex/module'; + +// @NgModule({ +// imports: [ +// CommonModule, +// FormsModule, +// ReactiveFormsModule, +// MatCheckboxModule, +// MatCardModule, +// MatDividerModule, +// MatFormFieldModule, +// MatInputModule, +// MatButtonModule, +// MatSelectModule, +// MatProgressBarModule, +// ComplexFieldsModule, +// ], +// declarations: [SchemeEntryElement], +// exports: [SchemeEntryElement], +// bootstrap: [], +// }) +// export class InnerModule {} diff --git a/application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/styles.less similarity index 95% rename from application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less rename to application/client/src/app/ui/elements/scheme/entry/inner/styles.less index d2f993c6ae..61cecebc28 100644 --- a/application/client/src/app/ui/elements/scheme/complex/files_selector/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/styles.less @@ -4,5 +4,4 @@ :host { position: relative; display: block; - } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/inner/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/template.html similarity index 59% rename from application/client/src/app/ui/elements/scheme/inner/template.html rename to application/client/src/app/ui/elements/scheme/entry/inner/template.html index a98c24b300..31dcc90598 100644 --- a/application/client/src/app/ui/elements/scheme/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/template.html @@ -16,6 +16,7 @@ + + +
+ + +
+ + + +
diff --git a/application/client/src/app/ui/elements/scheme/inner/styles.less b/application/client/src/app/ui/elements/scheme/inner/styles.less deleted file mode 100644 index f90559a55d..0000000000 --- a/application/client/src/app/ui/elements/scheme/inner/styles.less +++ /dev/null @@ -1,7 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: block; -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/module.ts b/application/client/src/app/ui/elements/scheme/module.ts index 102b568d56..0d6102c6ba 100644 --- a/application/client/src/app/ui/elements/scheme/module.ts +++ b/application/client/src/app/ui/elements/scheme/module.ts @@ -9,13 +9,9 @@ import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { MatSelectModule } from '@angular/material/select'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { NestedDictionaryModule } from './complex/nested_dictionary/module'; -import { FilesSelectorModule } from './complex/files_selector/module'; -import { TimezoneSelectorModule } from './complex/timezone/module'; import { SettingsScheme } from './component'; import { SchemeEntry } from './entry/component'; -import { SchemeEntryElement } from './inner/component'; @NgModule({ imports: [ @@ -30,12 +26,10 @@ import { SchemeEntryElement } from './inner/component'; MatButtonModule, MatSelectModule, MatProgressBarModule, - NestedDictionaryModule, - FilesSelectorModule, - TimezoneSelectorModule, + SchemeEntry, ], - declarations: [SettingsScheme, SchemeEntry, SchemeEntryElement], + declarations: [SettingsScheme], exports: [SettingsScheme], - bootstrap: [SettingsScheme, SchemeEntry, SchemeEntryElement], + bootstrap: [], }) export class SettingsSchemeModule {} diff --git a/application/client/src/app/ui/env/extentions/changes.ts b/application/client/src/app/ui/env/extentions/changes.ts index a3ce43ec6d..e065a9ff24 100644 --- a/application/client/src/app/ui/env/extentions/changes.ts +++ b/application/client/src/app/ui/env/extentions/changes.ts @@ -30,20 +30,35 @@ export class ChangesDetector { if (this._detauched) { return; } - this._changeDetectorRef.forEach((cdRef) => cdRef.detectChanges()); + this._changeDetectorRef.forEach((cdRef) => { + if (!cdRef) { + console.warn(`[detectChanges] Detected undefined cdRef in ChangesDetector`); + } + cdRef && cdRef.detectChanges(); + }); } public markChangesForCheck() { if (this._detauched) { return; } - this._changeDetectorRef.forEach((cdRef) => cdRef.markForCheck()); + this._changeDetectorRef.forEach((cdRef) => { + if (!cdRef) { + console.warn(`[markForCheck] Detected undefined cdRef in ChangesDetector`); + } + cdRef && cdRef.markForCheck(); + }); } public reattachChangesDetector() { if (this._detauched) { return; } - this._changeDetectorRef.forEach((cdRef) => cdRef.reattach()); + this._changeDetectorRef.forEach((cdRef) => { + if (!cdRef) { + console.warn(`[reattach] Detected undefined cdRef in ChangesDetector`); + } + cdRef && cdRef.reattach(); + }); } } From eef20f12d4b79ba7f375f2112fcde1862e3b8e06 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 29 May 2025 12:06:51 +0200 Subject: [PATCH 049/121] Update options of components --- .../indexer/sources/src/serial/options.rs | 2 +- .../apps/indexer/sources/src/socket/tcp.rs | 15 +++++- .../apps/indexer/sources/src/socket/udp.rs | 48 ++++++++++++++++++- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 14 +++++- .../platform/types/bindings/options.ts | 4 +- 6 files changed, 79 insertions(+), 6 deletions(-) diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index a9e48e1c16..991905e6bb 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -64,7 +64,7 @@ impl ComponentDescriptor for Descriptor { name: "Path to dev".to_owned(), desc: "Full path to serial device/port".to_owned(), required: true, - interface: ValueInput::String(String::new()), + interface: ValueInput::String(String::new(), "/dev/tty01".to_owned()), binding: None, }), FieldDesc::Lazy(LazyFieldDesc { diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 23c560ef65..648cbf72a4 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -4,7 +4,7 @@ use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{io::Read, net::SocketAddr, time::Duration}; -use stypes::SessionAction; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; use tokio::net::TcpStream; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; @@ -173,6 +173,9 @@ const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, ]); +// TODO: make fields ids more generic, for example prefix can be taken from source trait somehow +const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; + #[derive(Default)] pub struct Descriptor {} @@ -205,6 +208,16 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + Ok(vec![FieldDesc::Static(StaticFieldDesc { + id: FIELD_IP_ADDR.to_owned(), + name: "Address to connect".to_owned(), + desc: "IP address and port".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), + binding: None, + })]) + } } #[cfg(test)] diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index a866cd28d9..90530a80b0 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -8,7 +8,7 @@ use std::{ io::Read, net::{IpAddr, Ipv4Addr}, }; -use stypes::SessionAction; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -136,6 +136,9 @@ const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, ]); +const FIELD_IP_ADDR: &str = "UDP_SOURCE_FIELD_IP_ADDR"; +const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; + #[derive(Default)] pub struct Descriptor {} @@ -168,6 +171,49 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_IP_ADDR.to_owned(), + name: "Address to connect".to_owned(), + desc: "IP address and port".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_IP_ADDR.to_owned(), + name: "Multicast Addresses".to_owned(), + desc: "Multicast Addresses".to_owned(), + required: true, + interface: ValueInput::FieldsCollection { + elements: vec![ + StaticFieldDesc { + id: String::new(), + name: "Address".to_owned(), + desc: "Address to listen".to_owned(), + required: true, + interface: ValueInput::String( + String::new(), + "255.255.255.255".to_owned(), + ), + binding: None, + }, + StaticFieldDesc { + id: String::new(), + name: "Interface".to_owned(), + desc: "Interface".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0".to_owned()), + binding: None, + }, + ], + add_title: "Add Multicast".to_owned(), + }, + binding: None, + }), + ]) + } } #[cfg(test)] diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index b1836524b0..e5ffe53921 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -23,4 +23,4 @@ export type StaticFieldDesc = { id: string, name: string, desc: string, required export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": string } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | "Timezone" | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index b12ee7f2a0..7925d40b26 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -103,10 +103,14 @@ pub struct Field { ts(export, export_to = "options.ts") )] pub enum ValueInput { + /// * `bool` - default value Checkbox(bool), + /// * `i64` - default value #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "number"))] Number(i64), - String(String), + /// * `String` - default value + /// * `String` - placeholder + String(String, String), #[cfg_attr( all(test, feature = "test_and_gen"), ts(type = "[Array, number]") @@ -159,6 +163,14 @@ pub enum ValueInput { File(Vec), Directory, Timezone, + InputsCollection { + elements: Vec, + add_title: String, + }, + FieldsCollection { + elements: Vec, + add_title: String, + }, Bound { output: Box, inputs: Vec, diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 1cb5c92ba4..77288e5604 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -51,7 +51,7 @@ export type Value = export type ValueInput = | { Checkbox: boolean } | { Number: number } - | { String: string } + | { String: [string, string] } | { Numbers: [Array, number] } | { Strings: [Array, string] } | { NamedBools: Array<[string, boolean]> } @@ -68,4 +68,6 @@ export type ValueInput = | { File: Array } | 'Directory' | 'Timezone' + | { InputsCollection: { elements: Array; add_title: string } } + | { FieldsCollection: { elements: Array; add_title: string } } | { Bound: { output: ValueInput; inputs: Array } }; From de6bf45ece6b13a38826b3f22a9321455e53a578 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 29 May 2025 17:35:23 +0200 Subject: [PATCH 050/121] Update UDP options; link nested elements with parent --- .../apps/indexer/sources/src/socket/udp.rs | 77 ++++++++++++++++++- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../apps/indexer/stypes/src/options/mod.rs | 1 + .../complex/fields_collection/component.ts | 44 ----------- .../inner/complex/fields_collection/module.ts | 15 ---- .../complex/fields_collection/provider.ts | 74 ------------------ .../complex/fields_collection/styles.less | 8 -- .../complex/fields_collection/template.html | 15 ---- .../entry/inner/complex/timezone/component.ts | 2 - .../elements/scheme/entry/inner/component.ts | 18 ++++- .../scheme/entry/inner/element/checkbox.ts | 5 +- .../inner/element/fields_collection/index.ts | 69 +++++++++++++---- .../element/fields_collection/provider.ts | 38 --------- .../entry/inner/element/files_selector.ts | 8 +- .../scheme/entry/inner/element/index.ts | 11 +-- .../scheme/entry/inner/element/inner.ts | 12 +++ .../scheme/entry/inner/element/input_field.ts | 5 +- .../scheme/entry/inner/element/list.ts | 5 +- .../scheme/entry/inner/element/named.ts | 5 +- .../entry/inner/element/nested_dictionary.ts | 5 +- .../scheme/entry/inner/element/timezone.ts | 4 +- .../elements/scheme/entry/inner/template.html | 11 ++- .../platform/types/bindings/options.ts | 3 +- 23 files changed, 195 insertions(+), 242 deletions(-) delete mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts delete mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts delete mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts delete mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less delete mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 90530a80b0..3849645738 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -5,10 +5,11 @@ use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use log::trace; use std::{ + collections::HashMap, io::Read, net::{IpAddr, Ipv4Addr}, }; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -182,7 +183,7 @@ impl ComponentDescriptor for Descriptor { binding: None, }), FieldDesc::Static(StaticFieldDesc { - id: FIELD_IP_ADDR.to_owned(), + id: FIELD_MULTICAST_ADDR.to_owned(), name: "Multicast Addresses".to_owned(), desc: "Multicast Addresses".to_owned(), required: true, @@ -214,6 +215,78 @@ impl ComponentDescriptor for Descriptor { }), ]) } + + fn validate( + &self, + origin: &stypes::SessionAction, + fields: &[stypes::Field], + ) -> HashMap { + fn is_valid(addr: &str) -> bool { + let mut count = 0; + for part in addr.split(".") { + if part.parse::().is_err() { + return false; + } + count += 1; + } + count == 4 + } + let mut errors = HashMap::new(); + if let Some(field) = fields.iter().find(|field| field.id == FIELD_MULTICAST_ADDR) { + if let Value::Values(values) = &field.value { + for (idx, pair) in values.into_iter().enumerate() { + if let Value::Values(pair) = pair { + if pair.len() != 2 { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Invalid number of mutlicast settings".to_owned(), + ); + break; + } + if let (Value::String(addr), Value::String(interface)) = + (&pair[0], &pair[1]) + { + if !is_valid(addr) { + errors.insert( + format!("{idx}_0"), + "Expecting IP format 255.255.255.255".to_owned(), + ); + } + if !is_valid(interface) { + errors.insert( + format!("{idx}_1"), + "Expecting IP format 0.0.0.0".to_owned(), + ); + } + } else { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Invalid values of mutlicast settings".to_owned(), + ); + break; + } + } else { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Invalid mutlicast settings".to_owned(), + ); + break; + } + } + } else { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Invalid mutlicast settings".to_owned(), + ); + } + } else { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Fail to find mutlicast settings".to_owned(), + ); + } + errors + } } #[cfg(test)] diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index e5ffe53921..9b404c4fbf 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -21,6 +21,6 @@ export type LazyFieldDesc = { id: string, name: string, desc: string, binding: s export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, interface: ValueInput, binding: string | null, }; -export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map }; +export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "Values": Array }; export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 7925d40b26..fe2e1207a3 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -210,6 +210,7 @@ pub enum Value { ts(type = "Map") )] KeyStrings(HashMap>), + Values(Vec), } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts deleted file mode 100644 index d0079f4f3d..0000000000 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Element, FieldsCollectionElement } from '../../element'; -import { FieldDesc } from '@platform/types/bindings'; -import { Proivder } from './provider'; - -import * as obj from '@platform/env/obj'; - -@Component({ - selector: 'app-settings-scheme-fields-collection-selector', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class FieldsCollection extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - @Input() inner!: FieldsCollectionElement; - - public desc: FieldDesc[] = []; - public fields: FieldDesc[][] = []; - public provider: Proivder = new Proivder(); - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - // this.desc = this.inner.elements.map((el) => { - // return { Static: el }; - // }); - } - - public ngOnAdd() { - const fields = this.desc.map((field) => { - return obj.clone(field); - }); - this.fields.push(fields); - this.detectChanges(); - } -} -export interface FieldsCollection extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts deleted file mode 100644 index b865c60dc1..0000000000 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIcon } from '@angular/material/icon'; - -import { FieldsCollection } from './component'; -import { SchemeEntry } from '../../../component'; - -@NgModule({ - imports: [CommonModule, MatButtonModule, MatIcon, SchemeEntry], - declarations: [FieldsCollection], - exports: [FieldsCollection], - bootstrap: [], -}) -export class FieldsCollectionModule {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts deleted file mode 100644 index 28971a9568..0000000000 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/provider.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { SchemeProvider } from '@elements/scheme/provider'; -import { Field, FieldDesc, Value } from '@platform/types/bindings'; -import { components } from '@service/components'; -import { Logger } from '@env/logs'; -import { - LoadingCancelledEvent, - LoadingDoneEvent, - LoadingErrorEvent, - LoadingErrorsEvent, -} from '@platform/types/components'; -import { WrappedField } from '@ui/elements/scheme/field'; -import { SessionSourceOrigin } from '@service/session/origin'; - -export class Proivder extends SchemeProvider { - protected pending: string[] = []; - protected readonly logger = new Logger(`ObserveSetupProivder`); - protected readonly values: Map = new Map(); - protected fields: string[] = []; - - constructor() { - super(); - // this.register( - // components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), - // components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), - // components.subjects.get().LoadingErrors.subscribe(this.onLoadingErrors.bind(this)), - // ); - } - - public override get(): Promise { - return Promise.resolve([]); - } - - public override setValue(uuid: string, value: Value): void { - if (!this.fields.includes(uuid)) { - this.logger.error(`Field ${uuid} doesn't belong to current provider`); - return; - } - this.values.set(uuid, value); - const fields: Field[] = []; - this.values.forEach((value, id) => { - fields.push({ id, value }); - }); - } - - public override destroy(): Promise { - this.unsubscribe(); - this.subjects.destroy(); - return Promise.resolve(); - } - - protected onLoadingDone(event: LoadingDoneEvent) {} - - protected onLoadingError(event: LoadingErrorEvent) { - const errors = new Map(); - event.fields.forEach((id) => { - if (!this.pending.includes(id)) { - return; - } - errors.set(id, event.error); - }); - errors.size > 0 && this.subjects.get().error.emit(errors); - } - protected onLoadingErrors(event: LoadingErrorsEvent) { - const errors = new Map(); - event.errors.forEach((error) => { - if (!this.pending.includes(error.id)) { - return; - } - errors.set(error.id, error.err); - }); - errors.size > 0 && this.subjects.get().error.emit(errors); - } - protected onLoadingCancelled(event: LoadingCancelledEvent) {} -} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less deleted file mode 100644 index 48ac2ced20..0000000000 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/styles.less +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html deleted file mode 100644 index 927351f554..0000000000 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/fields_collection/template.html +++ /dev/null @@ -1,15 +0,0 @@ - - - -
- -
diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts index f2317d6aa0..3465bf94e6 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts @@ -3,8 +3,6 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { Element, TimezoneSelectorElement } from '../../element'; -import { bridge } from '@service/bridge'; -import { File } from '@platform/types/files'; import { components } from '@env/decorators/initial'; import { Timezone } from '@ui/elements/timezones/timezone'; diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts index 9baaf3b673..b1ebc62832 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts @@ -4,6 +4,7 @@ import { Input, AfterViewInit, AfterContentInit, + OnDestroy, } from '@angular/core'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; @@ -51,7 +52,10 @@ import * as els from './element'; }) @Initial() @Ilc() -export class SchemeEntryElement extends ChangesDetector implements AfterViewInit, AfterContentInit { +export class SchemeEntryElement + extends ChangesDetector + implements OnDestroy, AfterViewInit, AfterContentInit +{ @Input() provider!: SchemeProvider; @Input() element!: Element; @@ -70,6 +74,10 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit super(cdRef); } + public ngOnDestroy(): void { + this.elFieldsCollectionElement && this.elFieldsCollectionElement.destroy(); + } + public ngAfterContentInit(): void { this.field = this.element.isField(); this.elCheckboxElement = @@ -113,5 +121,13 @@ export class SchemeEntryElement extends ChangesDetector implements AfterViewInit this.elFieldsCollectionElement.add(); this.detectChanges(); } + + public ngOnRemoveCollection(idx: number) { + if (!this.elFieldsCollectionElement) { + return; + } + this.elFieldsCollectionElement.remove(idx); + this.detectChanges(); + } } export interface SchemeEntryElement extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts index c8ebe37ed5..0a337581f7 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts @@ -1,10 +1,11 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; -export class CheckboxElement implements ElementInner { +export class CheckboxElement extends ElementInner { public value: boolean; constructor(public defaults: boolean = false) { + super(); this.value = defaults; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts index 2aae4c147b..c69641dfc9 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts @@ -1,33 +1,70 @@ -import { ElementInner, Element } from '../index'; +import { Element } from '../index'; +import { ElementInner } from '../inner'; import { StaticFieldDesc, Value, ValueInput } from '@platform/types/bindings'; import { Provider } from './provider'; +import { Subscriber, Subject } from '@platform/env/subscription'; -import * as obj from '@platform/env/obj'; +class Collection extends Subscriber { + static from(spec: StaticFieldDesc[], idx: number, handler: () => void): Collection { + const elements: Element[] = spec.map( + (field, i) => new Element(`${idx}_${i}`, field.interface), + ); + return new Collection(elements, handler); + } + + constructor(public readonly elements: Element[], handler: () => void) { + super(); + this.register(...elements.map((el) => el.subjects.get().changed.subscribe(handler))); + } -export class FieldsCollectionElement implements ElementInner { - public value: number = -1; + public getValues(): Value[] { + return this.elements.map((el) => el.getValue()); + } +} + +export class FieldsCollectionElement extends ElementInner { + protected update() { + this.values = this.collections.map((collection) => collection.getValues()); + this.parent.subjects + .get() + .changed.emit({ uuid: this.parent.uuid, inner: undefined, value: this.getValue() }); + console.log(this.values); + } - public elements: Element[][] = []; + public parent!: Element; + public collections: Collection[] = []; + public values: Value[][] = []; public provider: Provider = new Provider(); public constructor( public readonly spec: StaticFieldDesc[], public readonly addTitle: string | undefined, - ) {} + ) { + super(); + this.update = this.update.bind(this); + } + + public override setParentRef(parent: Element): void { + this.parent = parent; + } + + public destroy() { + this.collections.forEach((collection) => collection.unsubscribe()); + } public add(): void { - const collection = this.spec.map((field) => { - return new Element(field.id + '__', field.interface); - }); - this.elements.push(collection); + this.collections.push(Collection.from(this.spec, this.collections.length, this.update)); + } + public remove(idx: number) { + this.collections.splice(idx, 1).forEach((collection) => collection.unsubscribe()); } public getInnerValue(): any { - return this.value; + return undefined; } - public setValue(value: any) { - this.value = value; + public setValue(_value: any) { + // Do nothing. This is just a wrapper } public isField(): boolean { @@ -35,7 +72,11 @@ export class FieldsCollectionElement implements ElementInner { } public getValue(): Value { - return { Number: this.value }; + return { + Values: this.collections.map((collection) => { + return { Values: collection.getValues() }; + }), + }; } } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts index 3824574e42..f541465717 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts @@ -1,15 +1,6 @@ import { SchemeProvider } from '@elements/scheme/provider'; import { Field, FieldDesc, Value } from '@platform/types/bindings'; -import { components } from '@service/components'; import { Logger } from '@env/logs'; -import { - LoadingCancelledEvent, - LoadingDoneEvent, - LoadingErrorEvent, - LoadingErrorsEvent, -} from '@platform/types/components'; -import { WrappedField } from '@ui/elements/scheme/field'; -import { SessionSourceOrigin } from '@service/session/origin'; export class Provider extends SchemeProvider { protected pending: string[] = []; @@ -19,11 +10,6 @@ export class Provider extends SchemeProvider { constructor() { super(); - // this.register( - // components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), - // components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), - // components.subjects.get().LoadingErrors.subscribe(this.onLoadingErrors.bind(this)), - // ); } public override get(): Promise { @@ -47,28 +33,4 @@ export class Provider extends SchemeProvider { this.subjects.destroy(); return Promise.resolve(); } - - protected onLoadingDone(event: LoadingDoneEvent) {} - - protected onLoadingError(event: LoadingErrorEvent) { - const errors = new Map(); - event.fields.forEach((id) => { - if (!this.pending.includes(id)) { - return; - } - errors.set(id, event.error); - }); - errors.size > 0 && this.subjects.get().error.emit(errors); - } - protected onLoadingErrors(event: LoadingErrorsEvent) { - const errors = new Map(); - event.errors.forEach((error) => { - if (!this.pending.includes(error.id)) { - return; - } - errors.set(error.id, error.err); - }); - errors.size > 0 && this.subjects.get().error.emit(errors); - } - protected onLoadingCancelled(event: LoadingCancelledEvent) {} } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts index be19da51ad..d138e4f197 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export enum FilesFoldersSelectorTarget { @@ -8,13 +8,15 @@ export enum FilesFoldersSelectorTarget { Folders, } -export class FilesFolderSelectorElement implements ElementInner { +export class FilesFolderSelectorElement extends ElementInner { public value: string[] = []; constructor( public readonly target: FilesFoldersSelectorTarget, public readonly exts: string[] = [], - ) {} + ) { + super(); + } public getInnerValue(): any { return this.value; diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts index cebf7a5067..abf6c05fb6 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts @@ -1,5 +1,6 @@ import { Value, ValueInput } from '@platform/types/bindings'; import { Subject, Subjects } from '@platform/env/subscription'; +import { ElementInner } from './inner'; import * as CheckboxElement from './checkbox'; import * as FilesFolderSelectorElement from './files_selector'; @@ -45,13 +46,6 @@ export type ValueInput = | { Bound: { output: ValueInput; inputs: Array } }; */ -export abstract class ElementInner { - public abstract getValue(): Value; - public abstract getInnerValue(): any; - public abstract setValue(value: any): void; - public abstract isField(): boolean; -} - export interface ChangeEvent { uuid: string; inner: any; @@ -69,7 +63,7 @@ export class Element { loaded: new Subject(), }); - constructor(protected readonly uuid: string, protected readonly origin: ValueInput) { + constructor(public readonly uuid: string, protected readonly origin: ValueInput) { let inner = undefined; for (let el of [ CheckboxElement, @@ -89,6 +83,7 @@ export class Element { } if (inner) { this.inner = inner; + this.inner.setParentRef(this); } else { throw new Error(`No controller for element ${JSON.stringify(origin)}`); } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts new file mode 100644 index 0000000000..c855897b19 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts @@ -0,0 +1,12 @@ +import { Value } from '@platform/types/bindings'; +import { Element } from './index'; + +export abstract class ElementInner { + public abstract getValue(): Value; + public abstract getInnerValue(): any; + public abstract setValue(value: any): void; + public abstract isField(): boolean; + public setParentRef(_parent: Element): void { + // This method is optional. That's why it has default implementation. + } +} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts index c559f5f859..adc0dd09ac 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; enum InnerType { @@ -6,7 +6,7 @@ enum InnerType { Number, } -export class InputElement implements ElementInner { +export class InputElement extends ElementInner { public value: T; constructor( @@ -15,6 +15,7 @@ export class InputElement implements ElementInner { protected readonly innerType: InnerType, protected readonly getter: (value: T) => Value, ) { + super(); this.value = defaults; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts index b5711184b7..45a7f3fa4a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; enum InnerType { @@ -6,7 +6,7 @@ enum InnerType { Number, } -export class ListElement implements ElementInner { +export class ListElement extends ElementInner { public value: T; public items: T[]; constructor( @@ -15,6 +15,7 @@ export class ListElement implements ElementInner { protected readonly innerType: InnerType, protected readonly getter: (value: T) => Value, ) { + super(); this.value = defaults; this.items = items; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts index 29c7e5cbcc..2e9069b58d 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts @@ -1,7 +1,7 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; -export class NamedValuesElement implements ElementInner { +export class NamedValuesElement extends ElementInner { public value: T; public items: { key: string; value: T }[]; constructor( @@ -9,6 +9,7 @@ export class NamedValuesElement implements ElementInner { items: { key: string; value: T }[], protected readonly getter: (value: T) => Value, ) { + super(); this.value = defaults; this.items = items; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts index f043988468..7fd3633505 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts @@ -1,9 +1,9 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export type NestedDictionaryStructure = Map>>; -export class NestedDictionaryElement implements ElementInner { +export class NestedDictionaryElement extends ElementInner { public value: V; constructor( public defaults: V, @@ -11,6 +11,7 @@ export class NestedDictionaryElement implements ElementInner { public readonly dictionary: Map, protected readonly getter: (value: V) => Value, ) { + super(); this.value = defaults; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts index 0335e69262..72777320b3 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts @@ -1,7 +1,7 @@ -import { ElementInner } from './index'; +import { ElementInner } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; -export class TimezoneSelectorElement implements ElementInner { +export class TimezoneSelectorElement extends ElementInner { public value: number = -1; public getInnerValue(): any { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/template.html index 31dcc90598..80d52c0705 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/template.html @@ -39,27 +39,30 @@ -
+
+
diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 22a84b9ebd..599f6b429b 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -47,7 +47,7 @@ export type Value = | { KeyNumbers: Map } | { KeyString: Map } | { KeyStrings: Map } - | { Values: Array }; + | { Fields: Array }; export type ValueInput = | { Checkbox: boolean } From 73711420bda3e7046952cbcf276c64414e03994e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 3 Jun 2025 18:32:35 +0200 Subject: [PATCH 052/121] Update layout of fields --- .../app/ui/elements/scheme/entry/component.ts | 5 +- .../inner/complex/timezone/template.html | 2 +- .../elements/scheme/entry/inner/component.ts | 5 +- .../scheme/entry/inner/element/checkbox.ts | 7 ++- .../inner/element/fields_collection/index.ts | 7 ++- .../entry/inner/element/files_selector.ts | 6 +- .../scheme/entry/inner/element/index.ts | 6 +- .../scheme/entry/inner/element/inner.ts | 7 ++- .../scheme/entry/inner/element/input_field.ts | 6 +- .../scheme/entry/inner/element/list.ts | 7 ++- .../scheme/entry/inner/element/named.ts | 6 +- .../entry/inner/element/nested_dictionary.ts | 6 +- .../scheme/entry/inner/element/timezone.ts | 6 +- .../elements/scheme/entry/inner/styles.less | 28 ++++++++++ .../elements/scheme/entry/inner/template.html | 55 +++++++++++-------- .../app/ui/elements/scheme/entry/styles.less | 13 +++-- .../ui/elements/scheme/entry/template.html | 1 + .../client/src/app/ui/styles/material.less | 8 ++- .../client/src/app/ui/tabs/setup/module.ts | 3 + .../client/src/app/ui/tabs/setup/state.ts | 21 +++++++ .../src/app/ui/tabs/setup/template.html | 44 ++++++++++++--- 21 files changed, 179 insertions(+), 70 deletions(-) diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts index fba8834056..213767b26b 100644 --- a/application/client/src/app/ui/elements/scheme/entry/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -26,6 +26,7 @@ import { MatButtonModule } from '@angular/material/button'; import { MatSelectModule } from '@angular/material/select'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { SchemeEntryElement } from './inner/component'; +import { FieldCategory } from './inner/element/inner'; @Component({ selector: 'app-settings-scheme-entry', @@ -56,7 +57,7 @@ export class SchemeEntry @Input() provider!: SchemeProvider; @Input() field!: FieldDesc; - @HostBinding('class') classes = 'field'; + @HostBinding('class') classes = ``; public pending: LazyFieldDesc | undefined; public loaded: StaticFieldDesc | undefined; @@ -112,7 +113,7 @@ export class SchemeEntry const value = this.element.getValue(); value && this.provider.setValue(this.loaded.id, value); this.element.loaded(); - this.classes = this.element.isField() ? 'field' : 'panel'; + this.classes = `field-${this.element.getFieldCategory()}`; return; } } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html index f5c7536580..2923a4d45a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/template.html @@ -1,5 +1,5 @@ diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts index d4206b8979..5272b9183e 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts @@ -27,6 +27,7 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatIcon } from '@angular/material/icon'; import * as els from './element'; +import { FieldCategory } from './element/inner'; @Component({ selector: 'app-settings-scheme-element', @@ -68,7 +69,7 @@ export class SchemeEntryElement public elTimezoneSelectorElement: els.TimezoneSelectorElement | undefined; public elFieldsCollectionElement: els.FieldsCollectionElement | undefined; - public field: boolean = false; + public inline: boolean = false; constructor(cdRef: ChangeDetectorRef) { super(cdRef); @@ -79,7 +80,7 @@ export class SchemeEntryElement } public ngAfterContentInit(): void { - this.field = this.element.isField(); + this.inline = this.element.getFieldCategory() == FieldCategory.Inline; this.elCheckboxElement = this.element.inner instanceof els.CheckboxElement ? this.element.inner : undefined; this.elFilesFolderSelectorElement = diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts index 0a337581f7..085594c18a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/checkbox.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export class CheckboxElement extends ElementInner { @@ -13,9 +13,10 @@ export class CheckboxElement extends ElementInner { return this.value; } - public isField(): boolean { - return true; + public getFieldCategory(): FieldCategory { + return FieldCategory.Inline; } + public setValue(value: any) { this.value = value; } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts index 703606ed2e..82d8131e74 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/index.ts @@ -1,5 +1,5 @@ import { Element } from '../index'; -import { ElementInner } from '../inner'; +import { ElementInner, FieldCategory } from '../inner'; import { StaticFieldDesc, Value, ValueInput, Field } from '@platform/types/bindings'; import { Provider } from './provider'; import { Subscriber, Subject } from '@platform/env/subscription'; @@ -95,6 +95,7 @@ export class FieldsCollectionElement extends ElementInner { this.collections.push( Collection.from(this.spec, this.parent.uuid, this.collections.length, this.update), ); + this.parent.change(); } public remove(idx: number) { this.collections.splice(idx, 1).forEach((collection) => collection.unsubscribe()); @@ -108,8 +109,8 @@ export class FieldsCollectionElement extends ElementInner { // Do nothing. This is just a wrapper } - public isField(): boolean { - return false; + public getFieldCategory(): FieldCategory { + return FieldCategory.Row; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts index d138e4f197..d2c09ef92a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export enum FilesFoldersSelectorTarget { @@ -26,8 +26,8 @@ export class FilesFolderSelectorElement extends ElementInner { this.value = value; } - public isField(): boolean { - return false; + public getFieldCategory(): FieldCategory { + return FieldCategory.Row; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts index b0bbd06fe7..0cf8151f72 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts @@ -1,6 +1,6 @@ import { Value, ValueInput } from '@platform/types/bindings'; import { Subject, Subjects } from '@platform/env/subscription'; -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import * as CheckboxElement from './checkbox'; import * as FilesFolderSelectorElement from './files_selector'; @@ -108,8 +108,8 @@ export class Element { this.subjects.get().loaded.emit(); } - public isField(): boolean { - return this.inner.isField(); + public getFieldCategory(): FieldCategory { + return this.inner.getFieldCategory(); } public change() { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts index fe47378e7b..9da2a9da56 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/inner.ts @@ -3,11 +3,16 @@ import { Element } from './index'; import { SchemeProvider } from '@ui/elements/scheme/provider'; import { Subscriber } from '@platform/env/subscription'; +export enum FieldCategory { + Block = 'block', + Inline = 'inline', + Row = 'row', +} export abstract class ElementInner extends Subscriber { public abstract getValue(): Value; public abstract getInnerValue(): any; public abstract setValue(value: any): void; - public abstract isField(): boolean; + public abstract getFieldCategory(): FieldCategory; public destroy(): void { this.unsubscribe(); // This method is optional. That's why it has default implementation. diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts index adc0dd09ac..c674979d99 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/input_field.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; enum InnerType { @@ -27,8 +27,8 @@ export class InputElement extends ElementInner { this.value = value; } - public isField(): boolean { - return true; + public getFieldCategory(): FieldCategory { + return FieldCategory.Inline; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts index 45a7f3fa4a..36a8e78c9a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/list.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; enum InnerType { @@ -28,9 +28,10 @@ export class ListElement extends ElementInner { this.value = value; } - public isField(): boolean { - return true; + public getFieldCategory(): FieldCategory { + return FieldCategory.Inline; } + public getValue(): Value { return this.getter(this.value); } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts index 2e9069b58d..cb9d8eb4e2 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/named.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export class NamedValuesElement extends ElementInner { @@ -22,8 +22,8 @@ export class NamedValuesElement extends ElementInner { this.value = value; } - public isField(): boolean { - return true; + public getFieldCategory(): FieldCategory { + return FieldCategory.Inline; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts index 7fd3633505..37c89d8f54 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export type NestedDictionaryStructure = Map>>; @@ -23,8 +23,8 @@ export class NestedDictionaryElement extends ElementInner { this.value = value; } - public isField(): boolean { - return false; + public getFieldCategory(): FieldCategory { + return FieldCategory.Block; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts index 72777320b3..2984efd0b2 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/timezone.ts @@ -1,4 +1,4 @@ -import { ElementInner } from './inner'; +import { ElementInner, FieldCategory } from './inner'; import { Value, ValueInput } from '@platform/types/bindings'; export class TimezoneSelectorElement extends ElementInner { @@ -12,8 +12,8 @@ export class TimezoneSelectorElement extends ElementInner { this.value = value; } - public isField(): boolean { - return false; + public getFieldCategory(): FieldCategory { + return FieldCategory.Row; } public getValue(): Value { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/styles.less index 8433ffc13a..7c74378305 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/styles.less @@ -4,7 +4,35 @@ :host { position: relative; display: block; + & div.collections { + position: relative; + display: flex; + justify-content: center; + flex-direction: column; + & div.collection { + position: relative; + display: flex; + flex-direction: row; + justify-content: flex-start; + & div.element { + margin-right: 12px; + & app-settings-scheme-element { + height: 42px; + } + } + } + & button { + color: var(--scheme-color-1) + } + & div.controls { + position: relative; + width: 100%; + display: flex; + + } + } & p.error { color: var(--scheme-color-error-light); } + } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/template.html index f4174495c9..2f23a47ba2 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/template.html @@ -1,4 +1,4 @@ - + @@ -38,33 +38,42 @@ - -
- +
+
+

{{element.error}}

- -
+
- - - +
+ + +
+
diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index 141284eaa6..8588e1d92c 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -3,13 +3,16 @@ :host { position: relative; - &.field { + padding: 2px; + margin: 6px; + &.field-inline { display: inline-block; - padding: 2px; - margin: 6px; - height: 42px; + height: 62px; } - &.panel { + &.field-row { + display: block; + } + &.field-block { display: block; width: auto; height: auto; diff --git a/application/client/src/app/ui/elements/scheme/entry/template.html b/application/client/src/app/ui/elements/scheme/entry/template.html index 608e1e1de6..e231492a04 100644 --- a/application/client/src/app/ui/elements/scheme/entry/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/template.html @@ -5,6 +5,7 @@
diff --git a/application/client/src/app/ui/styles/material.less b/application/client/src/app/ui/styles/material.less index 47fb5a0a24..bc1f729608 100644 --- a/application/client/src/app/ui/styles/material.less +++ b/application/client/src/app/ui/styles/material.less @@ -1,5 +1,11 @@ @import './variables.less'; - +*.mat-form-disable-background { + & mat-form-field { + & .mdc-text-field--filled:not(.mdc-text-field--disabled) { + background-color: unset; + } + } +} mat-form-field.material-mofication-normal { &.stretch { display: flex; diff --git a/application/client/src/app/ui/tabs/setup/module.ts b/application/client/src/app/ui/tabs/setup/module.ts index 1e26a350bd..7014f69412 100644 --- a/application/client/src/app/ui/tabs/setup/module.ts +++ b/application/client/src/app/ui/tabs/setup/module.ts @@ -4,6 +4,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; +import { MatCardModule } from '@angular/material/card'; + import { SettingsSchemeModule } from '@elements/scheme/module'; import { SetupObserve } from './component'; import { SourceOriginComponent } from './origin/component'; @@ -16,6 +18,7 @@ import { SourceOriginComponent } from './origin/component'; MatButtonModule, MatFormFieldModule, MatSelectModule, + MatCardModule, SettingsSchemeModule, ], declarations: [SetupObserve, SourceOriginComponent], diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index f743a6532a..7b52ca4f75 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -12,6 +12,11 @@ export interface IApi { tab(): TabControls; } +export interface ComponentDescription { + ident: Ident; + full: string; +} + export class State extends Subscriber { public sources: Ident[] = []; public parsers: Ident[] = []; @@ -33,6 +38,13 @@ export class State extends Subscriber { source: '', parser: '', }; + public description: { + source: ComponentDescription | undefined; + parser: ComponentDescription | undefined; + } = { + source: undefined, + parser: undefined, + }; public providers: { source: Proivder | undefined; parser: Proivder | undefined; @@ -97,6 +109,11 @@ export class State extends Subscriber { this.logger.error(`Fail to destroy source provider: ${err.message}`); }); } + this.description.source = undefined; + const ident = this.sources.find((ident) => ident.uuid == this.selected.source); + if (ident !== undefined) { + this.description.source = { full: '', ident }; + } this.providers.source = new Proivder(this.origin, this.selected.source); this.subjects.get().updated.emit(); }, @@ -109,6 +126,10 @@ export class State extends Subscriber { this.logger.error(`Fail to destroy parser provider: ${err.message}`); }); } + const ident = this.parsers.find((ident) => ident.uuid == this.selected.parser); + if (ident !== undefined) { + this.description.parser = { full: '', ident }; + } this.providers.parser = new Proivder(this.origin, this.selected.parser); this.subjects.get().updated.emit(); }, diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index e86a2dc3bb..27cc56b053 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -34,13 +34,41 @@
- - +
+ + {{state.description.source.ident.name}} + + + + + + {{state.description.parser.ident.name}} + + + + +
+
+ + {{state.description.source.ident.name}} + {{state.description.source.ident.desc}} + +

details

+
+
+ + {{state.description.parser.ident.name}} + {{state.description.parser.ident.desc}} + +

details

+
+
+
From 0091b034a1841e86b05fc42e5a211fe9a3b5f700 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 4 Jun 2025 15:50:20 +0200 Subject: [PATCH 053/121] Update descriptions --- .../indexer/parsers/src/dlt/descriptor.rs | 10 ++++----- .../indexer/parsers/src/someip/descriptor.rs | 8 +++---- .../indexer/parsers/src/text/descriptor.rs | 2 +- .../indexer/sources/src/command/process.rs | 4 ++-- .../indexer/sources/src/serial/options.rs | 22 +++++++++---------- .../apps/indexer/sources/src/socket/tcp.rs | 8 +++---- .../apps/indexer/sources/src/socket/udp.rs | 14 ++++++------ .../app/ui/elements/scheme/entry/component.ts | 18 ++++++++++++--- .../app/ui/elements/scheme/entry/styles.less | 5 +++++ .../ui/elements/scheme/entry/template.html | 3 ++- .../src/app/ui/elements/scheme/template.html | 3 +++ .../client/src/app/ui/tabs/setup/provider.ts | 4 ++++ .../client/src/app/ui/tabs/setup/styles.less | 4 ++++ .../src/app/ui/tabs/setup/template.html | 18 +++++++-------- 14 files changed, 75 insertions(+), 48 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index aa4cd6867b..92949c72b7 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -111,7 +111,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), name: "Log Level".to_owned(), - desc: "Log Level".to_owned(), + desc: "Log Level — defines the minimum severity level of log messages to be displayed. Messages with lower severity (e.g., Debug or Verbose) will be filtered out. For example, if \"Error\" is selected, only messages with level \"Error\" and above (e.g., Fatal) will be shown, while lower levels such as \"Warning\" or \"Debug\" will be ignored.".to_owned(), required: true, interface: ValueInput::Strings( vec![ @@ -129,7 +129,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_FIBEX_FILES.to_owned(), name: "Fibex Files".to_owned(), - desc: "Fibex Files".to_owned(), + desc: "Fibex Files — allows the user to attach one or more XML files containing the schema definitions for decoding the DLT payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), required: true, interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), binding: None, @@ -137,7 +137,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_TZ.to_owned(), name: "Timezone".to_owned(), - desc: "Timezone".to_owned(), + desc: "Timezone — a display-only setting used for visualizing timestamps. If a timezone is specified, all DLT timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), required: true, interface: ValueInput::Timezone, binding: None, @@ -155,8 +155,8 @@ impl ComponentDescriptor for Descriptor { } fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("DLT Parser"), - desc: String::from("DLT Parser"), + name: String::from("DLT"), + desc: String::from("DLT Parser is a binary parser for decoding AUTOSAR DLT (Diagnostic Log and Trace) messages. It processes raw binary input and extracts structured log information according to the DLT protocol specification. The parser can be applied both to files (typically containing a StorageHeader) and to live streams over TCP or UDP. It expects binary data as input and does not perform any framing or transport-level parsing."), uuid: DLT_PARSER_UUID, } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index da5156bd9a..7cb61ae25c 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -47,7 +47,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_FIBEX_FILES.to_owned(), name: "Fibex Files".to_owned(), - desc: "Fibex Files".to_owned(), + desc: "Fibex Files — allows the user to attach one or more XML files containing the schema definitions for decoding the payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), required: true, interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), binding: None, @@ -55,7 +55,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_TZ.to_owned(), name: "Timezone".to_owned(), - desc: "Timezone".to_owned(), + desc: "Timezone — a display-only setting used for visualizing timestamps. If a timezone is specified, all timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), required: true, interface: ValueInput::Timezone, binding: None, @@ -65,8 +65,8 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("SomeIP Parser"), - desc: String::from("SomeIP Parser"), + name: String::from("SOME/IP"), + desc: String::from("SomeIP Parser is a binary parser designed to decode messages conforming to the AUTOSAR SOME/IP (Scalable service-Oriented Middleware over IP) protocol. It processes raw binary input and extracts structured message data, including service ID, method ID, payload length, and more. The parser can be applied to both files and network streams (e.g., TCP/UDP). If a valid DLT message is detected within the SOME/IP payload, it will be automatically passed to the DLT parser and decoded accordingly."), uuid: SOMEIP_PARSER_UUID, } } diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 86da226756..ebfb94e88e 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -39,7 +39,7 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Text Parser"), - desc: String::from("Text Parser"), + desc: String::from("Text Parser is a minimal parser designed for processing plain text input. It performs no decoding or transformation and has no configuration options. Its sole purpose is to output incoming data line by line, making it suitable for logs or command outputs in textual format."), uuid: TEXT_PARSER_UUID, } } diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 8bff17d1ea..9e041f00b5 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -256,8 +256,8 @@ impl ComponentDescriptor for Descriptor { } fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("Terminal Source"), - desc: String::from("Terminal Source"), + name: String::from("Command Output"), + desc: String::from("Reads the standard output (stdout) of a launched command. The data is passed to the parser as lines of text."), uuid: TERM_SOURCE_UUID, } } diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/options.rs index 991905e6bb..7867731af6 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/options.rs @@ -49,8 +49,8 @@ impl ComponentDescriptor for Descriptor { } fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("Serial"), - desc: String::from("Access to Serial Devices"), + name: String::from("Serial Port Connection"), + desc: String::from("Connects to the specified serial port using user-defined settings. Data is received as a raw byte stream in a \"as-is\" mode - that is, the parser receives exactly what was read from the port, without any framing."), uuid: SERIAL_SOURCE_UUID, } } @@ -62,7 +62,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_PATH.to_owned(), name: "Path to dev".to_owned(), - desc: "Full path to serial device/port".to_owned(), + desc: String::new(), required: true, interface: ValueInput::String(String::new(), "/dev/tty01".to_owned()), binding: None, @@ -70,13 +70,13 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Lazy(LazyFieldDesc { id: FIELD_PORTS_LIST.to_string(), name: "Ports".to_string(), - desc: "List of available ports".to_string(), + desc: String::new(), binding: Some(FIELD_PATH.to_string()), }), FieldDesc::Static(StaticFieldDesc { id: FIELD_BAUD_RATE.to_owned(), name: "Boud rate".to_owned(), - desc: "Boud rate".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Numbers( vec![ @@ -91,7 +91,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_DATA_BITS.to_owned(), name: "Data bits".to_owned(), - desc: "Data bits".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Numbers(vec![8, 7, 6, 5], 8), binding: None, @@ -99,7 +99,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_STOP_BITS.to_owned(), name: "Stop bits".to_owned(), - desc: "Stop bits".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Numbers(vec![1, 2], 1), binding: None, @@ -107,7 +107,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_FLOW_CONTROL.to_owned(), name: "Flow control".to_owned(), - desc: "Flow control".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Strings( vec![ @@ -122,7 +122,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_PARITY.to_owned(), name: "Parity".to_owned(), - desc: "Parity".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Strings( vec![ @@ -137,7 +137,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_EXCLUSIVE.to_owned(), name: "Exclusive".to_owned(), - desc: "Exclusive".to_owned(), + desc: String::new(), required: true, interface: ValueInput::NamedBools(vec![ (String::from("Yes"), true), @@ -148,7 +148,7 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_SEND_DATA_DELAY.to_owned(), name: "Send delay, ms".to_owned(), - desc: "Data sending delay (between bytes), ms".to_owned(), + desc: String::new(), required: true, interface: ValueInput::Numbers(vec![0, 10, 20, 30, 40, 50], 0), binding: None, diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp.rs index 648cbf72a4..68346cbc30 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp.rs @@ -200,8 +200,8 @@ impl ComponentDescriptor for Descriptor { } fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("TCP Source"), - desc: String::from("TCP Source"), + name: String::from("TCP Connection"), + desc: String::from("Connects to the specified IP address and port to receive incoming messages. If the connection fails, an appropriate error is returned. Each individual TCP message is passed to the parser without any headers - only the payload is forwarded."), uuid: TCP_SOURCE_UUID, } } @@ -211,8 +211,8 @@ impl ComponentDescriptor for Descriptor { fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { Ok(vec![FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), - name: "Address to connect".to_owned(), - desc: "IP address and port".to_owned(), + name: "IP address and port".to_owned(), + desc: "Specifies the target address of the remote TCP server. The value must include both the IP address and the port, using the format IP:PORT — for example, 192.168.0.100:8888. This field is mandatory, and the parser will attempt to connect to the given endpoint exactly as specified. Use 0.0.0.0:PORT to bind to all local interfaces, if applicable.".to_owned(), required: true, interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), binding: None, diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp.rs index 98314408e1..d56e9cb536 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp.rs @@ -164,8 +164,8 @@ impl ComponentDescriptor for Descriptor { } fn ident(&self) -> stypes::Ident { stypes::Ident { - name: String::from("UDP Source"), - desc: String::from("UDP Source"), + name: String::from("UDP Connection"), + desc: String::from("Starts listening on the specified IP address and port, as well as on any user-defined multicast addresses. Each received UDP packet is passed to the parser as-is, excluding headers - only the payload is forwarded."), uuid: UDP_SOURCE_UUID, } } @@ -176,8 +176,8 @@ impl ComponentDescriptor for Descriptor { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), - name: "Address to connect".to_owned(), - desc: "IP address and port".to_owned(), + name: "IP address and port".to_owned(), + desc: "Specifies the local address and port on which the application should listen for incoming UDP packets. The value must include both the IP address and the port, using the format IP:PORT — for example, 0.0.0.0:9000. This field is mandatory, and determines the network interface and port bound by the listener. Use 0.0.0.0:PORT to listen on all available interfaces.".to_owned(), required: true, interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), binding: None, @@ -185,14 +185,14 @@ impl ComponentDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_MULTICAST_ADDR.to_owned(), name: "Multicast Addresses".to_owned(), - desc: "Multicast Addresses".to_owned(), + desc: "An optional list of UDP multicast groups to join. Each entry includes the group address (e.g., 239.0.0.1) and the local interface IP (e.g., 192.168.1.10) used for joining. This allows the source to receive packets sent to the specified multicast addresses in addition to those received on the main local socket (IP:PORT). If the list is empty, only unicast or broadcast messages will be received.".to_owned(), required: true, interface: ValueInput::FieldsCollection { elements: vec![ StaticFieldDesc { id: String::new(), name: "Address".to_owned(), - desc: "Address to listen".to_owned(), + desc: "".to_owned(), required: true, interface: ValueInput::String( String::new(), @@ -203,7 +203,7 @@ impl ComponentDescriptor for Descriptor { StaticFieldDesc { id: String::new(), name: "Interface".to_owned(), - desc: "Interface".to_owned(), + desc: "".to_owned(), required: true, interface: ValueInput::String(String::new(), "0.0.0.0".to_owned()), binding: None, diff --git a/application/client/src/app/ui/elements/scheme/entry/component.ts b/application/client/src/app/ui/elements/scheme/entry/component.ts index 213767b26b..9a1aae5f0a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/component.ts @@ -63,7 +63,7 @@ export class SchemeEntry public loaded: StaticFieldDesc | undefined; public element: Element | undefined; public name!: string; - public desc!: string; + public desc: string | undefined; public error: string | undefined; constructor(cdRef: ChangeDetectorRef) { @@ -79,7 +79,7 @@ export class SchemeEntry this.loaded = (this.field as { Static: StaticFieldDesc }).Static; const wrapped = new WrappedField(this.field); this.name = wrapped.name; - this.desc = wrapped.desc; + this.desc = wrapped.desc.trim() === '' ? undefined : wrapped.desc; this.init(); this.env().subscriber.register( this.provider.subjects.get().loaded.subscribe((loaded: StaticFieldDesc) => { @@ -113,7 +113,19 @@ export class SchemeEntry const value = this.element.getValue(); value && this.provider.setValue(this.loaded.id, value); this.element.loaded(); - this.classes = `field-${this.element.getFieldCategory()}`; + if (this.desc !== undefined) { + switch (this.element.getFieldCategory()) { + case FieldCategory.Inline: + this.classes = `field-${FieldCategory.Row}`; + break; + case FieldCategory.Block: + case FieldCategory.Row: + this.classes = `field-${this.element.getFieldCategory()}`; + break; + } + } else { + this.classes = `field-${this.element.getFieldCategory()}`; + } return; } } diff --git a/application/client/src/app/ui/elements/scheme/entry/styles.less b/application/client/src/app/ui/elements/scheme/entry/styles.less index 8588e1d92c..a2f6362e5c 100644 --- a/application/client/src/app/ui/elements/scheme/entry/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/styles.less @@ -18,6 +18,11 @@ height: auto; } p.title { + display: block; + color: var(--scheme-color-1); + padding: 3px 0px 0 16px; + } + p.desc { display: block; color: var(--scheme-color-2); padding: 3px 0px 0 16px; diff --git a/application/client/src/app/ui/elements/scheme/entry/template.html b/application/client/src/app/ui/elements/scheme/entry/template.html index e231492a04..2f40d6eca8 100644 --- a/application/client/src/app/ui/elements/scheme/entry/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/template.html @@ -1,4 +1,5 @@ -

{{name}}

+

{{name}}

+

{{desc}}

{{error}}

Loading...

diff --git a/application/client/src/app/ui/elements/scheme/template.html b/application/client/src/app/ui/elements/scheme/template.html index fec17fbb8b..afe26d0a1c 100644 --- a/application/client/src/app/ui/elements/scheme/template.html +++ b/application/client/src/app/ui/elements/scheme/template.html @@ -3,3 +3,6 @@ [field]="field" [provider]="provider" > +

+ This component doesn't have any settings or options. +

diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 247836101c..510c643e8d 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -30,6 +30,10 @@ export class Proivder extends SchemeProvider { ); } + public isEmpty(): boolean { + return this.fields.length === 0; + } + public override get(): Promise { this.pending.length === 0 && components.abort(this.pending); this.pending = []; diff --git a/application/client/src/app/ui/tabs/setup/styles.less b/application/client/src/app/ui/tabs/setup/styles.less index da6c270193..2ea7795447 100644 --- a/application/client/src/app/ui/tabs/setup/styles.less +++ b/application/client/src/app/ui/tabs/setup/styles.less @@ -71,4 +71,8 @@ margin: 0 8px; } } + p.desc { + color: var(--scheme-color-1); + font-size: 14px; + } } \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index 27cc56b053..c9e1f45172 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -8,7 +8,7 @@ [disabled]="state.sources.length <= 1" > - {{source.desc}} + {{source.name}} @@ -22,7 +22,7 @@ [disabled]="state.parsers.length <= 1" > - {{parser.desc}} + {{parser.name}} @@ -57,17 +57,15 @@
{{state.description.source.ident.name}} - {{state.description.source.ident.desc}} - -

details

-
+

{{state.description.source.ident.desc}}

{{state.description.parser.ident.name}} - {{state.description.parser.ident.desc}} - -

details

-
+

{{state.description.parser.ident.desc}}

From 5af5bfa7fc10dbf874b768e8d3a10632c024564e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 4 Jun 2025 21:37:46 +0200 Subject: [PATCH 054/121] Allow session starting (Test drive mode) --- .../src/app/ui/elements/scheme/component.ts | 8 ++-- .../element/fields_collection/provider.ts | 16 +++++++- .../src/app/ui/elements/scheme/provider.ts | 8 ++-- .../client/src/app/ui/tabs/setup/component.ts | 18 ++++----- .../client/src/app/ui/tabs/setup/provider.ts | 25 +++++++++++- .../client/src/app/ui/tabs/setup/state.ts | 40 +++++++++++++++++++ .../src/app/ui/tabs/setup/template.html | 9 ++++- 7 files changed, 104 insertions(+), 20 deletions(-) diff --git a/application/client/src/app/ui/elements/scheme/component.ts b/application/client/src/app/ui/elements/scheme/component.ts index f500c8b928..e85be2d95b 100644 --- a/application/client/src/app/ui/elements/scheme/component.ts +++ b/application/client/src/app/ui/elements/scheme/component.ts @@ -28,17 +28,17 @@ export class SettingsScheme public fields: FieldDesc[] = []; - protected id: string | undefined; + protected uuid: string | undefined; constructor(cdRef: ChangeDetectorRef) { super(cdRef); } public ngAfterContentChecked(): void { - if (this.id === undefined || this.provider === undefined) { + if (this.uuid === undefined || this.provider === undefined) { return; } - if (this.id === this.provider.id) { + if (this.uuid === this.provider.uuid) { return; } this.ngAfterViewInit(); @@ -46,7 +46,7 @@ export class SettingsScheme public ngAfterContentInit(): void {} public ngAfterViewInit(): void { - this.id = this.provider.id; + this.uuid = this.provider.uuid; this.provider .get() .then((fields) => { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts index f541465717..258e1f542e 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts @@ -1,6 +1,7 @@ import { SchemeProvider } from '@elements/scheme/provider'; import { Field, FieldDesc, Value } from '@platform/types/bindings'; import { Logger } from '@env/logs'; +import { unique } from '@platform/env/sequence'; export class Provider extends SchemeProvider { protected pending: string[] = []; @@ -9,7 +10,7 @@ export class Provider extends SchemeProvider { protected fields: string[] = []; constructor() { - super(); + super(unique()); } public override get(): Promise { @@ -33,4 +34,17 @@ export class Provider extends SchemeProvider { this.subjects.destroy(); return Promise.resolve(); } + + public override isValid(): boolean { + // This is dummy provider to broadcase event. No needs for validation + return true; + } + + public override getFields(): Field[] { + const fields: Field[] = []; + this.values.forEach((value, id) => { + fields.push({ id, value }); + }); + return fields; + } } diff --git a/application/client/src/app/ui/elements/scheme/provider.ts b/application/client/src/app/ui/elements/scheme/provider.ts index f5e687b061..920280d0ac 100644 --- a/application/client/src/app/ui/elements/scheme/provider.ts +++ b/application/client/src/app/ui/elements/scheme/provider.ts @@ -1,4 +1,4 @@ -import { FieldDesc, StaticFieldDesc, Value } from '@platform/types/bindings'; +import { Field, FieldDesc, StaticFieldDesc, Value } from '@platform/types/bindings'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; import { unique } from '@platform/env/sequence'; @@ -7,7 +7,6 @@ export interface ForcedValueChanges { value: any; } export abstract class SchemeProvider extends Subscriber { - public readonly id: string; public subjects: Subjects<{ loaded: Subject; forced: Subject; @@ -18,13 +17,14 @@ export abstract class SchemeProvider extends Subscriber { error: new Subject>(), }); - constructor() { + constructor(public readonly uuid: string) { super(); - this.id = unique(); } public abstract get(): Promise; public abstract destroy(): Promise; public abstract setValue(uuid: string, value: Value): void; + public abstract getFields(): Field[]; + public abstract isValid(): boolean; public force(target: string, value: any) { this.subjects.get().forced.emit({ target, value }); } diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index 1bccd58dc2..cb05191e51 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -56,18 +56,18 @@ export class SetupObserve this.state.subjects.get().updated.subscribe(() => { this.detectChanges(); }), + this.state.subjects.get().errorStateChange.subscribe(() => { + this.detectChanges(); + }), ); } - public test() { - this.origin.options.setSource({ - uuid: '08080808-0808-0808-0808-080808080808', - fields: [], - }); - this.origin.options.setParser({ - uuid: '01010101-0101-0101-0101-010101010101', - fields: [], - }); + public onStart() { + const error = this.state.setSourceOrigin(this.origin); + if (error instanceof Error) { + this.log().debug(`Cannot start session, because: ${error.message}`); + return; + } session .initialize() .observe(this.origin) diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 510c643e8d..940ec4e9f0 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -17,9 +17,10 @@ export class Proivder extends SchemeProvider { protected readonly logger = new Logger(`ObserveSetupProivder`); protected readonly values: Map = new Map(); protected fields: string[] = []; + protected hasErrors: boolean = false; constructor(protected readonly origin: SessionSourceOrigin, protected readonly target: string) { - super(); + super(target); this.register( components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), components.subjects.get().LoadingError.subscribe(this.onLoadingError.bind(this)), @@ -72,6 +73,7 @@ export class Proivder extends SchemeProvider { components .validate(this.origin.getDef(), this.target, fields) .then((errs: Map) => { + this.checkErrorState(errs.size > 0); // Always forward errors info, even no errors this.subjects.get().error.emit(errs); }) @@ -92,6 +94,25 @@ export class Proivder extends SchemeProvider { } } + public override isValid(): boolean { + return !this.hasErrors; + } + + public override getFields(): Field[] { + const fields: Field[] = []; + this.values.forEach((value, id) => { + fields.push({ id, value }); + }); + return fields; + } + + protected checkErrorState(updated: boolean) { + if (updated === this.hasErrors) { + return; + } + this.hasErrors = updated; + } + protected onLoadingDone(event: LoadingDoneEvent) { event.fields.forEach((field) => { if (!this.pending.includes(field.id)) { @@ -109,6 +130,7 @@ export class Proivder extends SchemeProvider { errors.set(id, event.error); }); errors.size > 0 && this.subjects.get().error.emit(errors); + this.checkErrorState(errors.size > 0); } protected onLoadingErrors(event: LoadingErrorsEvent) { const errors = new Map(); @@ -119,6 +141,7 @@ export class Proivder extends SchemeProvider { errors.set(error.id, error.err); }); errors.size > 0 && this.subjects.get().error.emit(errors); + this.checkErrorState(errors.size > 0); } protected onLoadingCancelled(event: LoadingCancelledEvent) {} } diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 7b52ca4f75..34581cfcac 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -24,11 +24,13 @@ export class State extends Subscriber { sources: Subject; parsers: Subject; error: Subject; + errorStateChange: Subject; updated: Subject; }> = new Subjects({ sources: new Subject(), parsers: new Subject(), error: new Subject(), + errorStateChange: new Subject(), updated: new Subject(), }); public selected: { @@ -52,6 +54,7 @@ export class State extends Subscriber { source: undefined, parser: undefined, }; + public locked: boolean = false; protected readonly logger = new Logger(`Setup`); @@ -62,6 +65,23 @@ export class State extends Subscriber { this.unsubscribe(); this.subjects.destroy(); } + public setSourceOrigin(origin: SessionSourceOrigin): Error | undefined { + if (!this.providers.parser || !this.providers.source) { + return new Error(`Source or parser isn't setup`); + } + if (this.locked) { + return new Error(`Not valid options`); + } + origin.options.setSource({ + uuid: this.providers.source.uuid, + fields: this.providers.source.getFields(), + }); + origin.options.setParser({ + uuid: this.providers.parser.uuid, + fields: this.providers.parser.getFields(), + }); + return undefined; + } public load() { components .get(this.origin.getDef()) @@ -115,6 +135,12 @@ export class State extends Subscriber { this.description.source = { full: '', ident }; } this.providers.source = new Proivder(this.origin, this.selected.source); + this.register( + this.providers.source.subjects.get().error.subscribe(() => { + this.checkErrors(); + this.subjects.get().errorStateChange.emit(); + }), + ); this.subjects.get().updated.emit(); }, parser: (): void => { @@ -131,8 +157,22 @@ export class State extends Subscriber { this.description.parser = { full: '', ident }; } this.providers.parser = new Proivder(this.origin, this.selected.parser); + this.register( + this.providers.parser.subjects.get().error.subscribe(() => { + this.checkErrors(); + this.subjects.get().errorStateChange.emit(); + }), + ); this.subjects.get().updated.emit(); }, }; } + + protected checkErrors() { + if (!this.providers.parser || !this.providers.source) { + this.locked = true; + } else { + this.locked = !this.providers.parser.isValid() || !this.providers.source.isValid(); + } + } } diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index c9e1f45172..ae6e6e0562 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -28,7 +28,14 @@
- +
From 2d78951f631ebfe3209fad1af06af0e67892829f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 5 Jun 2025 16:36:21 +0200 Subject: [PATCH 055/121] Add render and IO type options --- .../apps/indexer/components/src/tys.rs | 10 + .../indexer/parsers/src/dlt/descriptor.rs | 19 + .../indexer/parsers/src/dlt/raw/descriptor.rs | 4 + .../indexer/parsers/src/someip/descriptor.rs | 15 + .../indexer/parsers/src/text/descriptor.rs | 6 +- .../plugins_host/src/parser_shared/mod.rs | 1 + .../plugins_host/src/v0_1_0/parser/mod.rs | 1 + .../src/binary/pcap/legacy/descriptor.rs | 139 +++++++ .../binary/pcap/{legacy.rs => legacy/mod.rs} | 73 +--- .../sources/src/binary/pcap/ng/descriptor.rs | 55 +++ .../src/binary/pcap/{ng.rs => ng/mod.rs} | 72 +--- .../sources/src/binary/raw/descriptor.rs | 64 ++++ .../sources/src/binary/{raw.rs => raw/mod.rs} | 65 +--- .../indexer/sources/src/command/descriptor.rs | 87 +++++ .../apps/indexer/sources/src/command/mod.rs | 303 +++++++++++++++- .../indexer/sources/src/command/process.rs | 338 ------------------ application/apps/indexer/sources/src/lib.rs | 8 +- .../apps/indexer/sources/src/prelude.rs | 2 +- .../src/serial/{options.rs => descriptor.rs} | 1 + .../apps/indexer/sources/src/serial/mod.rs | 2 +- .../sources/src/socket/tcp/descriptor.rs | 54 +++ .../sources/src/socket/{tcp.rs => tcp/mod.rs} | 58 +-- .../sources/src/socket/udp/descriptor.rs | 154 ++++++++ .../sources/src/socket/{udp.rs => udp/mod.rs} | 162 +-------- .../apps/indexer/stypes/bindings/observe.ts | 3 +- .../apps/indexer/stypes/bindings/options.ts | 4 + .../apps/indexer/stypes/src/observe/mod.rs | 1 + .../indexer/stypes/src/observe/proptest.rs | 5 +- .../apps/indexer/stypes/src/options/mod.rs | 30 ++ .../indexer/stypes/src/options/proptest.rs | 26 ++ .../platform/types/bindings/observe.ts | 16 +- .../platform/types/bindings/options.ts | 9 + 32 files changed, 1038 insertions(+), 749 deletions(-) create mode 100644 application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs rename application/apps/indexer/sources/src/binary/pcap/{legacy.rs => legacy/mod.rs} (85%) create mode 100644 application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs rename application/apps/indexer/sources/src/binary/pcap/{ng.rs => ng/mod.rs} (84%) create mode 100644 application/apps/indexer/sources/src/binary/raw/descriptor.rs rename application/apps/indexer/sources/src/binary/{raw.rs => raw/mod.rs} (72%) create mode 100644 application/apps/indexer/sources/src/command/descriptor.rs rename application/apps/indexer/sources/src/serial/{options.rs => descriptor.rs} (99%) create mode 100644 application/apps/indexer/sources/src/socket/tcp/descriptor.rs rename application/apps/indexer/sources/src/socket/{tcp.rs => tcp/mod.rs} (90%) create mode 100644 application/apps/indexer/sources/src/socket/udp/descriptor.rs rename application/apps/indexer/sources/src/socket/{udp.rs => udp/mod.rs} (54%) create mode 100644 application/apps/indexer/stypes/src/options/proptest.rs diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 62a2b3d46c..cf9d240704 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -61,6 +61,12 @@ pub enum Entry { } impl ComponentDescriptor for Entry { + fn get_render(&self) -> stypes::OutputRender { + match self { + Self::Source(inner) => inner.get_render(), + Self::Parser(inner) => inner.get_render(), + } + } fn fields_getter(&self, origin: &stypes::SessionAction) -> FieldsResult { match self { Self::Source(inner) => inner.fields_getter(origin), @@ -158,6 +164,10 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// making it possible to swap, reconfigure, or isolate components without touching /// the application core. pub trait ComponentDescriptor { + fn get_render(&self) -> stypes::OutputRender { + stypes::OutputRender::NoRender + } + /// Check is component is campatible with given origin /// /// * `origin` - The source origin diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 92949c72b7..f9205739c4 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -91,6 +91,21 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { + fn get_render(&self) -> stypes::OutputRender { + stypes::OutputRender::Columns(vec![ + ("Datetime".to_owned(), 150), + ("ECUID".to_owned(), 80), + ("VERS".to_owned(), 80), + ("SID".to_owned(), 80), + ("MCNT".to_owned(), 80), + ("TMS".to_owned(), 80), + ("EID".to_owned(), 80), + ("APID".to_owned(), 80), + ("CTID".to_owned(), 80), + ("MSTP".to_owned(), 80), + ("PAYLOAD".to_owned(), 0), + ]) + } fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -157,6 +172,10 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("DLT"), desc: String::from("DLT Parser is a binary parser for decoding AUTOSAR DLT (Diagnostic Log and Trace) messages. It processes raw binary input and extracts structured log information according to the DLT protocol specification. The parser can be applied both to files (typically containing a StorageHeader) and to live streams over TCP or UDP. It expects binary data as input and does not perform any framing or transport-level parsing."), + io: stypes::IODataType::Multiple(vec![ + stypes::IODataType::NetworkFramePayload, + stypes::IODataType::Raw + ]), uuid: DLT_PARSER_UUID, } } diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index d8df1c5c19..386867e732 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -44,6 +44,10 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("DLT Export Parser"), desc: String::from("DLT Export Parser"), + io: stypes::IODataType::Multiple(vec![ + stypes::IODataType::NetworkFramePayload, + stypes::IODataType::Raw, + ]), uuid: DLT_PARSER_UUID, } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 7cb61ae25c..c3bf0616d6 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -24,6 +24,20 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { + fn get_render(&self) -> stypes::OutputRender { + stypes::OutputRender::Columns(vec![ + ("SOME/IP".to_owned(), 50), + ("SERV".to_owned(), 50), + ("METH".to_owned(), 50), + ("LENG".to_owned(), 30), + ("CLID".to_owned(), 30), + ("SEID".to_owned(), 30), + ("IVER".to_owned(), 30), + ("MSTP".to_owned(), 30), + ("RETC".to_owned(), 30), + ("PAYLOAD".to_owned(), 0), + ]) + } fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -67,6 +81,7 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("SOME/IP"), desc: String::from("SomeIP Parser is a binary parser designed to decode messages conforming to the AUTOSAR SOME/IP (Scalable service-Oriented Middleware over IP) protocol. It processes raw binary input and extracts structured message data, including service ID, method ID, payload length, and more. The parser can be applied to both files and network streams (e.g., TCP/UDP). If a valid DLT message is detected within the SOME/IP payload, it will be automatically passed to the DLT parser and decoded accordingly."), + io: stypes::IODataType::NetworkFramePayload, uuid: SOMEIP_PARSER_UUID, } } diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index ebfb94e88e..94cd182c1d 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -17,11 +17,14 @@ impl ComponentFactory for Descriptor { _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { - Ok(None) + Ok(Some(crate::Parser::Text(StringTokenizer {}))) } } impl ComponentDescriptor for Descriptor { + fn get_render(&self) -> stypes::OutputRender { + stypes::OutputRender::PlaitText + } fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -40,6 +43,7 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("Text Parser"), desc: String::from("Text Parser is a minimal parser designed for processing plain text input. It performs no decoding or transformation and has no configuration options. Its sole purpose is to output incoming data line by line, making it suitable for logs or command outputs in textual format."), + io: stypes::IODataType::PlaitText, uuid: TEXT_PARSER_UUID, } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index fce8eee039..f9bdfe584d 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -197,6 +197,7 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("Plugin"), desc: String::from("Plugin"), + io: stypes::IODataType::Any, uuid: uuid::Uuid::new_v4(), } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 82425b7836..73ee49ab21 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -188,6 +188,7 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("Plugin Parser"), desc: String::from("Plugin Parser"), + io: stypes::IODataType::Any, uuid: uuid::Uuid::new_v4(), } } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs new file mode 100644 index 0000000000..41d95f087f --- /dev/null +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -0,0 +1,139 @@ +use components::{ComponentFactory, ComponentDescriptor}; +use file_tools::is_binary; +use stypes::SessionAction; + +const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +]); + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + _origin: &SessionAction, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SessionAction) -> bool { + let files = match origin { + SessionAction::File(filepath) => { + vec![filepath] + } + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { + return false; + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "pcap") + .unwrap_or_default() + }) && + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("PCAP Source"), + desc: String::from("PCAP Source"), + io: stypes::IODataType::NetworkFramePayload, + uuid: PCAP_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + + +#[cfg(test)] +mod tests { + + use env_logger; + + use crate::{ + binary::pcap::legacy::PcapLegacyByteSource, tests::general_source_reload_test, ByteSource, + }; + + #[tokio::test] + async fn test_read_one_message_from_pcap() { + let _ = env_logger::try_init(); + + const SAMPLE_PCAP_DATA: &[u8] = &[ + 0xd4, 0xc3, 0xb2, 0xa1, // Magic Number (4 bytes) = d4 c3 b2 a1 + 0x02, 0x00, // Version Major (2 bytes) = 02 00 + 0x04, 0x00, // Version Minor (2 bytes) = 04 00 + 0x00, 0x00, 0x00, 0x00, // Timezone (4 bytes) = 00 00 00 00 + 0x00, 0x00, 0x00, 0x00, // Timestamp Accuracy (4 bytes) = 00 00 00 00 + 0x00, 0x00, 0x04, 0x00, // Snap Length (4 bytes) + 0x01, 0x00, 0x00, 0x00, // Link-Layer Type (4 bytes) + // Packet Header 16 byte + 0xeb, 0x15, 0x88, 0x60, 0xe6, 0x7f, 0x04, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, + 0x00, 0x00, // + // start of ethernet packet ------------------- + 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0xb8, 0x27, 0xeb, 0x98, 0x94, 0xfa, 0x08, 0x00, + 0x45, 0x00, 0x00, 0x54, 0xa0, 0x48, 0x40, 0x00, 0x40, 0x11, 0x29, 0x85, 0xac, 0x16, + 0x0c, 0x4f, 0xac, 0x16, 0x0c, + 0x50, // start of udp frame ------------------------- + 0xc3, 0x50, 0xc3, 0x50, 0x00, 0x40, 0x8e, 0xe3, // + // start of udp payload 56 bytes --------------------------- + 0xff, 0xff, 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x02, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x10, + 0x01, 0x03, 0x00, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x09, 0x04, 0x00, 0xac, 0x16, 0x0c, 0x4f, 0x00, 0x11, 0x75, 0x30, + ]; + + let udp_payload = &SAMPLE_PCAP_DATA[82..=137]; + let pcap_file = std::io::Cursor::new(SAMPLE_PCAP_DATA); + + let mut source = PcapLegacyByteSource::new(pcap_file).expect("cannot create source"); + let reload_info = source.load(None).await.expect("reload should work"); + println!("reload_info: {:?}", reload_info); + let slice = source.current_slice(); + println!("slice: {:x?}", slice); + assert_eq!(slice.len(), 56); + assert_eq!(slice, udp_payload); + } + + #[tokio::test] + async fn test_general_source_reload() { + // This is part of the file "chipmunk/application/developing/resources". + // In this test we just need enough bytes to call reload twice on it, and we will not + // call parse on any of this data. + const SAMPLE_PCAP_DATA: &[u8] = &[ + 0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x52, 0x90, 0x5a, 0x64, + 0xa4, 0xd8, 0x0e, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x5e, 0x40, 0xff, 0xfb, 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x60, 0x16, 0x99, 0x00, 0x00, 0x01, 0x11, 0x40, 0x17, 0xc0, 0xa8, 0xb2, 0x3a, + 0xef, 0xff, 0xff, 0xfa, 0x9c, 0x40, 0x9c, 0x40, 0x00, 0x4c, 0x63, 0x3b, 0xff, 0xff, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x20, 0x00, 0x7b, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x00, 0x10, + 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x06, 0x75, 0x30, 0x52, 0x90, 0x5a, 0x64, + 0x08, 0xda, 0x0e, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x5e, 0x40, 0xff, 0xfb, 0xb8, 0x27, 0xeb, 0x1d, 0x24, 0xc9, 0x08, 0x00, 0x45, 0x00, + 0x00, 0x54, 0x3a, 0xb4, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, + 0xc0, 0xa8, 0xb2, 0x3a, 0x9c, 0x40, 0x9c, 0x40, 0x00, 0x40, 0xe6, 0x17, 0xff, 0xff, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x10, 0x00, 0x7b, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x09, 0x04, 0x00, 0xc0, 0xa8, 0xb2, 0x3a, 0x00, 0x11, 0x75, 0x30, 0x52, 0x90, + ]; + + let pcap_file = std::io::Cursor::new(SAMPLE_PCAP_DATA); + + let mut source = PcapLegacyByteSource::new(pcap_file).expect("cannot create source"); + + general_source_reload_test(&mut source).await; + } +} diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs similarity index 85% rename from application/apps/indexer/sources/src/binary/pcap/legacy.rs rename to application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs index a38b06216a..eb7e9b607d 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs @@ -1,14 +1,14 @@ -use crate::{binary::pcap::debug_block}; -use definitions::*; +mod descriptor; + +use crate::binary::pcap::debug_block; use bufread::DeqBuffer; -use components::{ComponentFactory, ComponentDescriptor}; +use definitions::*; use etherparse::{SlicedPacket, TransportSlice}; -use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{traits::PcapReaderIterator, LegacyPcapReader, PcapBlockOwned, PcapError}; use std::{fs::File, io::Read, path::Path}; -use stypes::SessionAction; +pub use descriptor::*; pub struct PcapLegacyByteSource { pcap_reader: LegacyPcapReader, @@ -175,16 +175,17 @@ impl PcapLegacyByteSourceFromFile { }) } Ok(Self { - inner: PcapLegacyByteSource::new(input_file(filename.as_ref())?).map_err(|err|stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(err.to_string()), + inner: PcapLegacyByteSource::new(input_file(filename.as_ref())?).map_err(|err| { + stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(err.to_string()), + } })?, }) } } - impl ByteSource for PcapLegacyByteSourceFromFile { async fn load( &mut self, @@ -205,58 +206,6 @@ impl ByteSource for PcapLegacyByteSourceFromFile { } } - -const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, -]); - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - let files = match origin { - SessionAction::File(filepath) => { - vec![filepath] - } - SessionAction::Files(files) => files.iter().collect(), - SessionAction::Source | SessionAction::ExportRaw(..) => { - return false; - } - }; - files.iter().any(|fp| { - fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "pcap") - .unwrap_or_default() - }) && - // If at least some file doesn't exist or not binary - do not recommend this source - !files - .into_iter() - .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("PCAP Source"), - desc: String::from("PCAP Source"), - uuid: PCAP_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } -} - - #[cfg(test)] mod tests { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs new file mode 100644 index 0000000000..4ee150c54c --- /dev/null +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -0,0 +1,55 @@ +use components::{ComponentFactory,ComponentDescriptor}; +use file_tools::is_binary; +use stypes::SessionAction; + +const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, +]); + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + _origin: &SessionAction, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SessionAction) -> bool { + let files = match origin { + SessionAction::File(filepath) => { + vec![filepath] + } + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { + return false; + } + }; + files.iter().any(|fp| { + fp.extension() + .map(|ext| ext.to_ascii_lowercase() == "pcapng") + .unwrap_or_default() + }) && + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("PCAP NG Source"), + desc: String::from("PCAP NG Source"), + io: stypes::IODataType::NetworkFramePayload, + uuid: PCAPNG_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} + diff --git a/application/apps/indexer/sources/src/binary/pcap/ng.rs b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs similarity index 84% rename from application/apps/indexer/sources/src/binary/pcap/ng.rs rename to application/apps/indexer/sources/src/binary/pcap/ng/mod.rs index d8e1a17980..8f27cbb850 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs @@ -1,13 +1,14 @@ -use crate::{binary::pcap::debug_block}; -use definitions::*; +mod descriptor; + +use crate::binary::pcap::debug_block; use bufread::DeqBuffer; -use components::{ComponentFactory,ComponentDescriptor}; +use definitions::*; use etherparse::{SlicedPacket, TransportSlice}; -use file_tools::is_binary; use log::{debug, error, trace}; use pcap_parser::{traits::PcapReaderIterator, PcapBlockOwned, PcapError, PcapNGReader}; use std::{fs::File, io::Read, path::Path}; -use stypes::SessionAction; + +pub use descriptor::*; pub struct PcapngByteSource { pcapng_reader: PcapNGReader, @@ -178,16 +179,17 @@ impl PcapngByteSourceFromFile { }) } Ok(Self { - inner: PcapngByteSource::new(input_file(filename.as_ref())?).map_err(|err|stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Io, - message: Some(err.to_string()), + inner: PcapngByteSource::new(input_file(filename.as_ref())?).map_err(|err| { + stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Io, + message: Some(err.to_string()), + } })?, }) } } - impl ByteSource for PcapngByteSourceFromFile { async fn load( &mut self, @@ -208,56 +210,6 @@ impl ByteSource for PcapngByteSourceFromFile { } } -const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, -]); - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - let files = match origin { - SessionAction::File(filepath) => { - vec![filepath] - } - SessionAction::Files(files) => files.iter().collect(), - SessionAction::Source | SessionAction::ExportRaw(..) => { - return false; - } - }; - files.iter().any(|fp| { - fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "pcapng") - .unwrap_or_default() - }) && - // If at least some file doesn't exist or not binary - do not recommend this source - !files - .into_iter() - .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("PCAP NG Source"), - desc: String::from("PCAP NG Source"), - uuid: PCAPNG_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs new file mode 100644 index 0000000000..86805809bd --- /dev/null +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -0,0 +1,64 @@ +use components::{ComponentDescriptor, ComponentFactory}; +use file_tools::is_binary; + +use stypes::SessionAction; + +const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +]); + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + origin: &SessionAction, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + let filename = match origin { + SessionAction::File(filename) => filename, + _ => { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some( + "origin isn't supported by this source (BinaryByteSource)".to_owned(), + ), + }) + } + }; + Ok(Some(crate::Source::Raw( + super::BinaryByteSourceFromFile::new(filename)?, + ))) + } +} + +impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SessionAction) -> bool { + let files = match origin { + SessionAction::File(filepath) => { + vec![filepath] + } + SessionAction::Files(files) => files.iter().collect(), + SessionAction::Source | SessionAction::ExportRaw(..) => { + return false; + } + }; + // If at least some file doesn't exist or not binary - do not recommend this source + !files + .into_iter() + .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("Binary Source"), + desc: String::from("Binary Source"), + io: stypes::IODataType::Raw, + uuid: BIN_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } +} diff --git a/application/apps/indexer/sources/src/binary/raw.rs b/application/apps/indexer/sources/src/binary/raw/mod.rs similarity index 72% rename from application/apps/indexer/sources/src/binary/raw.rs rename to application/apps/indexer/sources/src/binary/raw/mod.rs index 7d6165d8d1..ad9cc4896e 100644 --- a/application/apps/indexer/sources/src/binary/raw.rs +++ b/application/apps/indexer/sources/src/binary/raw/mod.rs @@ -1,13 +1,13 @@ +mod descriptor; + use bufread::BufReader; -use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; -use file_tools::is_binary; +pub use descriptor::*; use std::{ fs::File, io::{BufRead, Read}, path::Path, }; -use stypes::SessionAction; pub struct BinaryByteSource where @@ -123,65 +123,6 @@ impl ByteSource for BinaryByteSourceFromFile { } } -const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, -]); - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let filename = match origin { - SessionAction::File(filename) => filename, - _ => { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some( - "origin isn't supported by this source (BinaryByteSource)".to_owned(), - ), - }) - } - }; - Ok(Some(crate::Source::Raw(BinaryByteSourceFromFile::new( - filename, - )?))) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - let files = match origin { - SessionAction::File(filepath) => { - vec![filepath] - } - SessionAction::Files(files) => files.iter().collect(), - SessionAction::Source | SessionAction::ExportRaw(..) => { - return false; - } - }; - // If at least some file doesn't exist or not binary - do not recommend this source - !files - .into_iter() - .any(|f| !f.exists() || !is_binary(f.to_string_lossy().to_string()).unwrap_or_default()) - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("Binary Source"), - desc: String::from("Binary Source"), - uuid: BIN_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } -} - #[cfg(test)] mod tests { use crate::{ diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs new file mode 100644 index 0000000000..bd28f8a257 --- /dev/null +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -0,0 +1,87 @@ +use super::ProcessSource; +use components::{ComponentDescriptor, ComponentFactory}; +use std::{collections::HashMap, env}; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; + +const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, +]); + +const FIELD_COMMAND: &str = "COMMAND_FIELD_COMMAND"; + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + _origin: &SessionAction, + options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + let Some(command) = options.iter().find_map(|field| { + if field.id == FIELD_COMMAND { + if let Value::String(command) = &field.value { + Some(command.clone()) + } else { + None + } + } else { + None + } + }) else { + return Ok(None); + }; + Ok(Some(crate::Source::Process( + ProcessSource::new(command, env::current_dir().unwrap(), HashMap::new()).unwrap(), + ))) + } +} + +impl ComponentDescriptor for Descriptor { + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + Ok(vec![FieldDesc::Static(StaticFieldDesc { + id: FIELD_COMMAND.to_owned(), + name: "Terminal Command".to_owned(), + desc: String::new(), + required: true, + interface: ValueInput::String(String::new(), "terminal command".to_owned()), + binding: None, + })]) + } + fn is_compatible(&self, origin: &SessionAction) -> bool { + match origin { + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, + } + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("Command Output"), + desc: String::from("Reads the standard output (stdout) of a launched command. The data is passed to the parser as lines of text."), + io: stypes::IODataType::PlaitText, + uuid: TERM_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } + fn validate( + &self, + _origin: &stypes::SessionAction, + fields: &[stypes::Field], + ) -> HashMap { + let mut errors = HashMap::new(); + fields.iter().for_each(|field| { + if field.id == FIELD_COMMAND { + if let Value::String(path) = &field.value { + if path.trim().is_empty() { + errors.insert(field.id.clone(), "command cannot be empty".to_owned()); + } + } + } + }); + errors + } +} diff --git a/application/apps/indexer/sources/src/command/mod.rs b/application/apps/indexer/sources/src/command/mod.rs index 80fe812c8b..f68a9a960c 100644 --- a/application/apps/indexer/sources/src/command/mod.rs +++ b/application/apps/indexer/sources/src/command/mod.rs @@ -1 +1,302 @@ -pub mod process; +mod descriptor; +pub use descriptor::*; + +use bufread::DeqBuffer; +use definitions::*; +use regex::{Captures, Regex}; +use shellexpand::tilde; +use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; +use thiserror::Error; +use tokio::{ + io::AsyncWriteExt, + process::{Child, ChildStderr, ChildStdin, ChildStdout, Command}, + select, +}; +use tokio_stream::StreamExt; +use tokio_util::codec::{self, FramedRead, LinesCodec}; + +lazy_static! { + static ref GROUP_RE: Regex = + Regex::new(r#"".*?""#).expect("Regex must compile (fail with GROUP_RE)"); + static ref QUOTE_RE: Regex = + Regex::new(r#"""#).expect("Regex must compile (fail with QUOTE_RE)"); + static ref ESC_RE: Regex = Regex::new(r"\\\s").expect("Regex must compile (fail with ESC_RE)"); +} + +#[derive(Error, Debug)] +pub enum ProcessError { + #[error("{0}")] + Setup(String), + #[error("Unrecoverable process error: {0}")] + Unrecoverable(String), +} + +pub struct ProcessSource { + process: Child, + buffer: DeqBuffer, + stdout: FramedRead, + stderr: FramedRead, + stdin: ChildStdin, +} + +impl Drop for ProcessSource { + fn drop(&mut self) { + let is_process_alive = self.process.try_wait().is_ok_and(|state| state.is_none()); + if is_process_alive { + if let Err(err) = self.process.start_kill() { + warn!("Fail to kill child process: {err}"); + } + } + } +} + +impl ProcessSource { + pub fn parse_command(command: &str) -> Result, ProcessError> { + let mut groups: Vec = vec![]; + let parsed = ESC_RE.replace_all(command, "==esc_space==").to_string(); + let parsed = GROUP_RE.replace_all(&parsed, |caps: &Captures| { + let index = groups.len(); + if caps.len() != 0 { + let group = caps[0].to_string(); + groups.push(QUOTE_RE.replace_all(&group, "").to_string()); + } + format!("==extraction:({index})==") + }); + Ok(parsed + .split(' ') + .map(|a| { + let mut str = a.to_string(); + for (i, g) in groups.iter().enumerate() { + let key = format!("==extraction:({i})=="); + str.replace(&key, g).clone_into(&mut str); + } + let restored = str.replace("==esc_space==", " "); + OsString::from(tilde(&restored).to_string()) + }) + .collect()) + } + + #[cfg(windows)] + fn spawn( + cmd: OsString, + args: Vec, + cwd: PathBuf, + envs: HashMap, + ) -> Result { + const CREATE_NO_WINDOW: u32 = 0x08000000; + Command::new(cmd) + .args(args) + .current_dir(OsString::from(cwd)) + .envs(envs) + .creation_flags(CREATE_NO_WINDOW) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .stdin(Stdio::piped()) + .kill_on_drop(true) + .spawn() + .map_err(|e| ProcessError::Setup(format!("{e}"))) + } + + #[cfg(not(windows))] + fn spawn( + cmd: OsString, + args: Vec, + cwd: PathBuf, + envs: HashMap, + ) -> Result { + Command::new(cmd) + .args(args) + .current_dir(OsString::from(cwd)) + .envs(envs) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .stdin(Stdio::piped()) + .kill_on_drop(true) + .spawn() + .map_err(|e| ProcessError::Setup(format!("{e}"))) + } + + pub fn new( + command: String, + cwd: PathBuf, + envs: HashMap, + ) -> Result { + let mut args = ProcessSource::parse_command(&command)?; + let cmd = if args.is_empty() { + return Err(ProcessError::Setup(format!( + "Not command has been found in \"{command}\"" + ))); + } else { + args.remove(0) + }; + let mut process = ProcessSource::spawn(cmd, args, cwd, envs)?; + let stdout = codec::FramedRead::new( + process + .stdout + .take() + .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stdout handle")))?, + LinesCodec::default(), + ); + let stderr = codec::FramedRead::new( + process + .stderr + .take() + .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stderr handle")))?, + LinesCodec::default(), + ); + let stdin = process + .stdin + .take() + .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stdin handle")))?; + Ok(Self { + process, + buffer: DeqBuffer::new(8192), + stdout, + stderr, + stdin, + }) + } +} + +impl ByteSource for ProcessSource { + async fn load( + &mut self, + _filter: Option<&SourceFilter>, + ) -> Result, SourceError> { + let mut closing = false; + let mut output; + // Implementation is cancel-safe here because there is no data gathered between to await + // calls. The only multiple await calls here, are actually while closing where there is + // no data to lose anyway. + loop { + if !closing { + output = select! { + res = self.stdout.next() => res, + res = self.stderr.next() => { + if res.is_none() { + closing = true; + } + res + }, + }; + if !closing { + break; + } + } else { + output = self.stdout.next().await; + break; + } + } + if let Some(Ok(line)) = output { + let stored = line.len() + 1; + self.buffer.write_from(line.as_bytes()); + self.buffer.write_from(b"\n"); + let available_bytes = self.buffer.read_available(); + Ok(Some(ReloadInfo::new(stored, available_bytes, 0, None))) + } else if let Some(Err(err)) = output { + Err(SourceError::Unrecoverable(format!("{err}"))) + } else { + Ok(None) + } + } + + fn current_slice(&self) -> &[u8] { + self.buffer.read_slice() + } + + fn consume(&mut self, offset: usize) { + self.buffer.read_done(offset); + } + + fn len(&self) -> usize { + self.buffer.read_available() + } + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + async fn income( + &mut self, + request: stypes::SdeRequest, + ) -> Result { + let bytes = match request { + stypes::SdeRequest::WriteText(ref str) => str.as_bytes(), + stypes::SdeRequest::WriteBytes(ref bytes) => bytes, + }; + self.stdin.write_all(bytes).await.map_err(SourceError::Io)?; + Ok(stypes::SdeResponse { bytes: bytes.len() }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::general_source_reload_test; + + #[tokio::test] + async fn test_process() -> Result<(), ProcessError> { + use std::env; + let mut command = ""; + if cfg!(windows) { + command = "help"; + } else if cfg!(unix) { + command = "ls -lsa"; + } + let envs = HashMap::new(); + match ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs).await { + Ok(mut process_source) => { + while process_source + .load(None) + .await + .expect("Reload data from process source failed") + .is_some() + { + assert!(!process_source.current_slice().is_empty()); + process_source.consume(process_source.current_slice().len()); + } + // By some reasons during test sometimes process stay alive and as result + let _ = process_source.process.kill().await; + Ok(()) + } + Err(err) => Err(err), + } + } + + #[tokio::test] + async fn test_parsing() -> Result<(), ProcessError> { + let parsed = + ProcessSource::parse_command(r#"cmd arg2 "some_path/with space or spaces" arg3"#)?; + assert_eq!(parsed.len(), 4); + assert_eq!(parsed[0], OsString::from("cmd")); + assert_eq!(parsed[1], OsString::from("arg2")); + assert_eq!(parsed[2], OsString::from("some_path/with space or spaces")); + assert_eq!(parsed[3], OsString::from("arg3")); + let parsed = + ProcessSource::parse_command(r"cmd arg2 some_path/with\ space\ or\ spaces arg3")?; + assert_eq!(parsed.len(), 4); + assert_eq!(parsed[0], OsString::from("cmd")); + assert_eq!(parsed[1], OsString::from("arg2")); + assert_eq!(parsed[2], OsString::from("some_path/with space or spaces")); + assert_eq!(parsed[3], OsString::from("arg3")); + Ok(()) + } + + #[tokio::test] + async fn test_source_reload() { + use std::env; + let mut command = ""; + if cfg!(windows) { + command = "help"; + } else if cfg!(unix) { + command = "ls -lsa"; + } + let envs = HashMap::new(); + let mut process_source = + ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs) + .await + .unwrap(); + + general_source_reload_test(&mut process_source).await; + } +} diff --git a/application/apps/indexer/sources/src/command/process.rs b/application/apps/indexer/sources/src/command/process.rs index 9e041f00b5..8b13789179 100644 --- a/application/apps/indexer/sources/src/command/process.rs +++ b/application/apps/indexer/sources/src/command/process.rs @@ -1,339 +1 @@ -use bufread::DeqBuffer; -use components::{ComponentDescriptor, ComponentFactory}; -use definitions::*; -use regex::{Captures, Regex}; -use shellexpand::tilde; -use std::{collections::HashMap, ffi::OsString, io::Read, path::PathBuf, process::Stdio}; -use stypes::SessionAction; -use thiserror::Error; -use tokio::{ - io::AsyncWriteExt, - process::{Child, ChildStderr, ChildStdin, ChildStdout, Command}, - select, -}; -use tokio_stream::StreamExt; -use tokio_util::codec::{self, FramedRead, LinesCodec}; -lazy_static! { - static ref GROUP_RE: Regex = - Regex::new(r#"".*?""#).expect("Regex must compile (fail with GROUP_RE)"); - static ref QUOTE_RE: Regex = - Regex::new(r#"""#).expect("Regex must compile (fail with QUOTE_RE)"); - static ref ESC_RE: Regex = Regex::new(r"\\\s").expect("Regex must compile (fail with ESC_RE)"); -} - -#[derive(Error, Debug)] -pub enum ProcessError { - #[error("{0}")] - Setup(String), - #[error("Unrecoverable process error: {0}")] - Unrecoverable(String), -} - -pub struct ProcessSource { - process: Child, - buffer: DeqBuffer, - stdout: FramedRead, - stderr: FramedRead, - stdin: ChildStdin, -} - -impl Drop for ProcessSource { - fn drop(&mut self) { - let is_process_alive = self.process.try_wait().is_ok_and(|state| state.is_none()); - if is_process_alive { - if let Err(err) = self.process.start_kill() { - warn!("Fail to kill child process: {err}"); - } - } - } -} - -impl ProcessSource { - pub fn parse_command(command: &str) -> Result, ProcessError> { - let mut groups: Vec = vec![]; - let parsed = ESC_RE.replace_all(command, "==esc_space==").to_string(); - let parsed = GROUP_RE.replace_all(&parsed, |caps: &Captures| { - let index = groups.len(); - if caps.len() != 0 { - let group = caps[0].to_string(); - groups.push(QUOTE_RE.replace_all(&group, "").to_string()); - } - format!("==extraction:({index})==") - }); - Ok(parsed - .split(' ') - .map(|a| { - let mut str = a.to_string(); - for (i, g) in groups.iter().enumerate() { - let key = format!("==extraction:({i})=="); - str.replace(&key, g).clone_into(&mut str); - } - let restored = str.replace("==esc_space==", " "); - OsString::from(tilde(&restored).to_string()) - }) - .collect()) - } - - #[cfg(windows)] - fn spawn( - cmd: OsString, - args: Vec, - cwd: PathBuf, - envs: HashMap, - ) -> Result { - const CREATE_NO_WINDOW: u32 = 0x08000000; - Command::new(cmd) - .args(args) - .current_dir(OsString::from(cwd)) - .envs(envs) - .creation_flags(CREATE_NO_WINDOW) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .stdin(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .map_err(|e| ProcessError::Setup(format!("{e}"))) - } - - #[cfg(not(windows))] - fn spawn( - cmd: OsString, - args: Vec, - cwd: PathBuf, - envs: HashMap, - ) -> Result { - Command::new(cmd) - .args(args) - .current_dir(OsString::from(cwd)) - .envs(envs) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .stdin(Stdio::piped()) - .kill_on_drop(true) - .spawn() - .map_err(|e| ProcessError::Setup(format!("{e}"))) - } - - pub async fn new( - command: String, - cwd: PathBuf, - envs: HashMap, - ) -> Result { - let mut args = ProcessSource::parse_command(&command)?; - let cmd = if args.is_empty() { - return Err(ProcessError::Setup(format!( - "Not command has been found in \"{command}\"" - ))); - } else { - args.remove(0) - }; - let mut process = ProcessSource::spawn(cmd, args, cwd, envs)?; - let stdout = codec::FramedRead::new( - process - .stdout - .take() - .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stdout handle")))?, - LinesCodec::default(), - ); - let stderr = codec::FramedRead::new( - process - .stderr - .take() - .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stderr handle")))?, - LinesCodec::default(), - ); - let stdin = process - .stdin - .take() - .ok_or_else(|| ProcessError::Setup(String::from("Fail to get stdin handle")))?; - Ok(Self { - process, - buffer: DeqBuffer::new(8192), - stdout, - stderr, - stdin, - }) - } -} - -impl ByteSource for ProcessSource { - async fn load( - &mut self, - _filter: Option<&SourceFilter>, - ) -> Result, SourceError> { - let mut closing = false; - let mut output; - // Implementation is cancel-safe here because there is no data gathered between to await - // calls. The only multiple await calls here, are actually while closing where there is - // no data to lose anyway. - loop { - if !closing { - output = select! { - res = self.stdout.next() => res, - res = self.stderr.next() => { - if res.is_none() { - closing = true; - } - res - }, - }; - if !closing { - break; - } - } else { - output = self.stdout.next().await; - break; - } - } - if let Some(Ok(line)) = output { - let stored = line.len() + 1; - self.buffer.write_from(line.as_bytes()); - self.buffer.write_from(b"\n"); - let available_bytes = self.buffer.read_available(); - Ok(Some(ReloadInfo::new(stored, available_bytes, 0, None))) - } else if let Some(Err(err)) = output { - Err(SourceError::Unrecoverable(format!("{err}"))) - } else { - Ok(None) - } - } - - fn current_slice(&self) -> &[u8] { - self.buffer.read_slice() - } - - fn consume(&mut self, offset: usize) { - self.buffer.read_done(offset); - } - - fn len(&self) -> usize { - self.buffer.read_available() - } - - fn is_empty(&self) -> bool { - self.len() == 0 - } - - async fn income( - &mut self, - request: stypes::SdeRequest, - ) -> Result { - let bytes = match request { - stypes::SdeRequest::WriteText(ref str) => str.as_bytes(), - stypes::SdeRequest::WriteBytes(ref bytes) => bytes, - }; - self.stdin.write_all(bytes).await.map_err(SourceError::Io)?; - Ok(stypes::SdeResponse { bytes: bytes.len() }) - } -} - -const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, -]); - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - match origin { - SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { - false - } - SessionAction::Source => true, - } - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("Command Output"), - desc: String::from("Reads the standard output (stdout) of a launched command. The data is passed to the parser as lines of text."), - uuid: TERM_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tests::general_source_reload_test; - - #[tokio::test] - async fn test_process() -> Result<(), ProcessError> { - use std::env; - let mut command = ""; - if cfg!(windows) { - command = "help"; - } else if cfg!(unix) { - command = "ls -lsa"; - } - let envs = HashMap::new(); - match ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs).await { - Ok(mut process_source) => { - while process_source - .load(None) - .await - .expect("Reload data from process source failed") - .is_some() - { - assert!(!process_source.current_slice().is_empty()); - process_source.consume(process_source.current_slice().len()); - } - // By some reasons during test sometimes process stay alive and as result - let _ = process_source.process.kill().await; - Ok(()) - } - Err(err) => Err(err), - } - } - - #[tokio::test] - async fn test_parsing() -> Result<(), ProcessError> { - let parsed = - ProcessSource::parse_command(r#"cmd arg2 "some_path/with space or spaces" arg3"#)?; - assert_eq!(parsed.len(), 4); - assert_eq!(parsed[0], OsString::from("cmd")); - assert_eq!(parsed[1], OsString::from("arg2")); - assert_eq!(parsed[2], OsString::from("some_path/with space or spaces")); - assert_eq!(parsed[3], OsString::from("arg3")); - let parsed = - ProcessSource::parse_command(r"cmd arg2 some_path/with\ space\ or\ spaces arg3")?; - assert_eq!(parsed.len(), 4); - assert_eq!(parsed[0], OsString::from("cmd")); - assert_eq!(parsed[1], OsString::from("arg2")); - assert_eq!(parsed[2], OsString::from("some_path/with space or spaces")); - assert_eq!(parsed[3], OsString::from("arg3")); - Ok(()) - } - - #[tokio::test] - async fn test_source_reload() { - use std::env; - let mut command = ""; - if cfg!(windows) { - command = "help"; - } else if cfg!(unix) { - command = "ls -lsa"; - } - let envs = HashMap::new(); - let mut process_source = - ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs) - .await - .unwrap(); - - general_source_reload_test(&mut process_source).await; - } -} diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index e5a314f6ce..77fe8f344a 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,5 +1,3 @@ -use std::io::Read; - use components::Components; use definitions::{ReloadInfo, SourceError, SourceFilter}; @@ -24,7 +22,7 @@ pub enum Source { Tcp(socket::tcp::TcpSource), Udp(socket::udp::UdpSource), Serial(serial::serialport::SerialSource), - Process(command::process::ProcessSource), + Process(command::ProcessSource), } impl definitions::ByteSource for Source { @@ -115,7 +113,7 @@ pub fn registration

(components: &mut Components) -> Result<(), sty components.add_source(binary::pcap::ng::Descriptor::default())?; components.add_source(socket::tcp::Descriptor::default())?; components.add_source(socket::udp::Descriptor::default())?; - components.add_source(serial::options::Descriptor::default())?; - components.add_source(command::process::Descriptor::default())?; + components.add_source(serial::descriptor::Descriptor::default())?; + components.add_source(command::Descriptor::default())?; Ok(()) } diff --git a/application/apps/indexer/sources/src/prelude.rs b/application/apps/indexer/sources/src/prelude.rs index 4c02944e7c..7568d8b130 100644 --- a/application/apps/indexer/sources/src/prelude.rs +++ b/application/apps/indexer/sources/src/prelude.rs @@ -3,7 +3,7 @@ pub use crate::{ pcap::{legacy::PcapLegacyByteSource, ng::PcapngByteSource}, raw::BinaryByteSource, }, - command::process::ProcessSource, + command::ProcessSource, serial::serialport::SerialSource, socket::{tcp::TcpSource, udp::UdpSource}, }; diff --git a/application/apps/indexer/sources/src/serial/options.rs b/application/apps/indexer/sources/src/serial/descriptor.rs similarity index 99% rename from application/apps/indexer/sources/src/serial/options.rs rename to application/apps/indexer/sources/src/serial/descriptor.rs index 7867731af6..0173c5d966 100644 --- a/application/apps/indexer/sources/src/serial/options.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -51,6 +51,7 @@ impl ComponentDescriptor for Descriptor { stypes::Ident { name: String::from("Serial Port Connection"), desc: String::from("Connects to the specified serial port using user-defined settings. Data is received as a raw byte stream in a \"as-is\" mode - that is, the parser receives exactly what was read from the port, without any framing."), + io: stypes::IODataType::PlaitText, uuid: SERIAL_SOURCE_UUID, } } diff --git a/application/apps/indexer/sources/src/serial/mod.rs b/application/apps/indexer/sources/src/serial/mod.rs index 9c23886d3a..5742c421a0 100644 --- a/application/apps/indexer/sources/src/serial/mod.rs +++ b/application/apps/indexer/sources/src/serial/mod.rs @@ -1,2 +1,2 @@ -pub mod options; +pub mod descriptor; pub mod serialport; diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs new file mode 100644 index 0000000000..3d3d49317b --- /dev/null +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -0,0 +1,54 @@ +use components::{ComponentDescriptor, ComponentFactory}; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; + +const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, +]); + +// TODO: make fields ids more generic, for example prefix can be taken from source trait somehow +const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + _origin: &SessionAction, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SessionAction) -> bool { + match origin { + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, + } + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("TCP Connection"), + desc: String::from("Connects to the specified IP address and port to receive incoming messages. If the connection fails, an appropriate error is returned. Each individual TCP message is passed to the parser without any headers - only the payload is forwarded."), + io: stypes::IODataType::NetworkFramePayload, + uuid: TCP_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + Ok(vec![FieldDesc::Static(StaticFieldDesc { + id: FIELD_IP_ADDR.to_owned(), + name: "IP address and port".to_owned(), + desc: "Specifies the target address of the remote TCP server. The value must include both the IP address and the port, using the format IP:PORT — for example, 192.168.0.100:8888. This field is mandatory, and the parser will attempt to connect to the given endpoint exactly as specified. Use 0.0.0.0:PORT to bind to all local interfaces, if applicable.".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), + binding: None, + })]) + } +} diff --git a/application/apps/indexer/sources/src/socket/tcp.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs similarity index 90% rename from application/apps/indexer/sources/src/socket/tcp.rs rename to application/apps/indexer/sources/src/socket/tcp/mod.rs index 68346cbc30..ea861b18a9 100644 --- a/application/apps/indexer/sources/src/socket/tcp.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -1,15 +1,16 @@ +mod descriptor; + use bufread::DeqBuffer; -use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; -use std::{io::Read, net::SocketAddr, time::Duration}; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; +use std::{net::SocketAddr, time::Duration}; use tokio::net::TcpStream; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; pub mod reconnect; +pub use descriptor::*; /// Configurations for keep-alive probes in TCP communication. #[derive(Debug, Clone)] @@ -169,57 +170,6 @@ impl ByteSource for TcpSource { } } -const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, -]); - -// TODO: make fields ids more generic, for example prefix can be taken from source trait somehow -const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - match origin { - SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { - false - } - SessionAction::Source => true, - } - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("TCP Connection"), - desc: String::from("Connects to the specified IP address and port to receive incoming messages. If the connection fails, an appropriate error is returned. Each individual TCP message is passed to the parser without any headers - only the payload is forwarded."), - uuid: TCP_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { - Ok(vec![FieldDesc::Static(StaticFieldDesc { - id: FIELD_IP_ADDR.to_owned(), - name: "IP address and port".to_owned(), - desc: "Specifies the target address of the remote TCP server. The value must include both the IP address and the port, using the format IP:PORT — for example, 192.168.0.100:8888. This field is mandatory, and the parser will attempt to connect to the given endpoint exactly as specified. Use 0.0.0.0:PORT to bind to all local interfaces, if applicable.".to_owned(), - required: true, - interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), - binding: None, - })]) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs new file mode 100644 index 0000000000..99cf267f93 --- /dev/null +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -0,0 +1,154 @@ +use components::{ComponentDescriptor, ComponentFactory}; +use std::collections::HashMap; +use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; + +const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, +]); + +const FIELD_IP_ADDR: &str = "UDP_SOURCE_FIELD_IP_ADDR"; +const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; + +#[derive(Default)] +pub struct Descriptor {} + +impl ComponentFactory for Descriptor { + fn create( + &self, + _origin: &SessionAction, + _options: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(None) + } +} + +impl ComponentDescriptor for Descriptor { + fn is_compatible(&self, origin: &SessionAction) -> bool { + match origin { + SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { + false + } + SessionAction::Source => true, + } + } + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("UDP Connection"), + desc: String::from("Starts listening on the specified IP address and port, as well as on any user-defined multicast addresses. Each received UDP packet is passed to the parser as-is, excluding headers - only the payload is forwarded."), + io: stypes::IODataType::NetworkFramePayload, + uuid: UDP_SOURCE_UUID, + } + } + fn ty(&self) -> stypes::ComponentType { + stypes::ComponentType::Source + } + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_IP_ADDR.to_owned(), + name: "IP address and port".to_owned(), + desc: "Specifies the local address and port on which the application should listen for incoming UDP packets. The value must include both the IP address and the port, using the format IP:PORT — for example, 0.0.0.0:9000. This field is mandatory, and determines the network interface and port bound by the listener. Use 0.0.0.0:PORT to listen on all available interfaces.".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_MULTICAST_ADDR.to_owned(), + name: "Multicast Addresses".to_owned(), + desc: "An optional list of UDP multicast groups to join. Each entry includes the group address (e.g., 239.0.0.1) and the local interface IP (e.g., 192.168.1.10) used for joining. This allows the source to receive packets sent to the specified multicast addresses in addition to those received on the main local socket (IP:PORT). If the list is empty, only unicast or broadcast messages will be received.".to_owned(), + required: true, + interface: ValueInput::FieldsCollection { + elements: vec![ + StaticFieldDesc { + id: String::new(), + name: "Address".to_owned(), + desc: "".to_owned(), + required: true, + interface: ValueInput::String( + String::new(), + "255.255.255.255".to_owned(), + ), + binding: None, + }, + StaticFieldDesc { + id: String::new(), + name: "Interface".to_owned(), + desc: "".to_owned(), + required: true, + interface: ValueInput::String(String::new(), "0.0.0.0".to_owned()), + binding: None, + }, + ], + add_title: "Add Multicast".to_owned(), + }, + binding: None, + }), + ]) + } + + fn validate( + &self, + _origin: &stypes::SessionAction, + fields: &[stypes::Field], + ) -> HashMap { + fn is_valid(addr: &str) -> bool { + let mut count = 0; + for part in addr.split(".") { + if part.parse::().is_err() { + return false; + } + count += 1; + } + count == 4 + } + let mut errors = HashMap::new(); + if let Some(field) = fields.iter().find(|field| field.id == FIELD_MULTICAST_ADDR) { + if let Value::Fields(fields) = &field.value { + for pair in fields.into_iter() { + if let Value::Fields(pair) = &pair.value { + if pair.len() != 2 { + errors.insert( + field.id.to_owned(), + "Invalid number of mutlicast settings".to_owned(), + ); + break; + } + if let (Value::String(addr), Value::String(interface)) = + (&pair[0].value, &pair[1].value) + { + if !is_valid(addr) { + errors.insert( + pair[0].id.to_owned(), + "Expecting IP format 255.255.255.255".to_owned(), + ); + } + if !is_valid(interface) { + errors.insert( + pair[1].id.to_owned(), + "Expecting IP format 0.0.0.0".to_owned(), + ); + } + } else { + errors.insert( + field.id.to_owned(), + "Invalid values of mutlicast settings".to_owned(), + ); + break; + } + } else { + errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); + break; + } + } + } else { + errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); + } + } else { + errors.insert( + FIELD_MULTICAST_ADDR.to_owned(), + "Fail to find mutlicast settings".to_owned(), + ); + } + errors + } +} diff --git a/application/apps/indexer/sources/src/socket/udp.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs similarity index 54% rename from application/apps/indexer/sources/src/socket/udp.rs rename to application/apps/indexer/sources/src/socket/udp/mod.rs index d56e9cb536..beb349a1c7 100644 --- a/application/apps/indexer/sources/src/socket/udp.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -1,18 +1,16 @@ +mod descriptor; + use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; use bufread::DeqBuffer; -use components::{ComponentDescriptor, ComponentFactory}; use definitions::*; use log::trace; -use std::{ - collections::HashMap, - io::Read, - net::{IpAddr, Ipv4Addr}, -}; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; +use std::net::{IpAddr, Ipv4Addr}; use thiserror::Error; use tokio::net::{ToSocketAddrs, UdpSocket}; +pub use descriptor::*; + #[derive(Error, Debug)] pub enum UdpSourceError { #[error("IO Error: {0}")] @@ -133,156 +131,6 @@ impl ByteSource for UdpSource { } } -const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, -]); - -const FIELD_IP_ADDR: &str = "UDP_SOURCE_FIELD_IP_ADDR"; -const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; - -#[derive(Default)] -pub struct Descriptor {} - -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) - } -} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, origin: &SessionAction) -> bool { - match origin { - SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { - false - } - SessionAction::Source => true, - } - } - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("UDP Connection"), - desc: String::from("Starts listening on the specified IP address and port, as well as on any user-defined multicast addresses. Each received UDP packet is passed to the parser as-is, excluding headers - only the payload is forwarded."), - uuid: UDP_SOURCE_UUID, - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { - Ok(vec![ - FieldDesc::Static(StaticFieldDesc { - id: FIELD_IP_ADDR.to_owned(), - name: "IP address and port".to_owned(), - desc: "Specifies the local address and port on which the application should listen for incoming UDP packets. The value must include both the IP address and the port, using the format IP:PORT — for example, 0.0.0.0:9000. This field is mandatory, and determines the network interface and port bound by the listener. Use 0.0.0.0:PORT to listen on all available interfaces.".to_owned(), - required: true, - interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), - binding: None, - }), - FieldDesc::Static(StaticFieldDesc { - id: FIELD_MULTICAST_ADDR.to_owned(), - name: "Multicast Addresses".to_owned(), - desc: "An optional list of UDP multicast groups to join. Each entry includes the group address (e.g., 239.0.0.1) and the local interface IP (e.g., 192.168.1.10) used for joining. This allows the source to receive packets sent to the specified multicast addresses in addition to those received on the main local socket (IP:PORT). If the list is empty, only unicast or broadcast messages will be received.".to_owned(), - required: true, - interface: ValueInput::FieldsCollection { - elements: vec![ - StaticFieldDesc { - id: String::new(), - name: "Address".to_owned(), - desc: "".to_owned(), - required: true, - interface: ValueInput::String( - String::new(), - "255.255.255.255".to_owned(), - ), - binding: None, - }, - StaticFieldDesc { - id: String::new(), - name: "Interface".to_owned(), - desc: "".to_owned(), - required: true, - interface: ValueInput::String(String::new(), "0.0.0.0".to_owned()), - binding: None, - }, - ], - add_title: "Add Multicast".to_owned(), - }, - binding: None, - }), - ]) - } - - fn validate( - &self, - origin: &stypes::SessionAction, - fields: &[stypes::Field], - ) -> HashMap { - fn is_valid(addr: &str) -> bool { - let mut count = 0; - for part in addr.split(".") { - if part.parse::().is_err() { - return false; - } - count += 1; - } - count == 4 - } - let mut errors = HashMap::new(); - if let Some(field) = fields.iter().find(|field| field.id == FIELD_MULTICAST_ADDR) { - if let Value::Fields(fields) = &field.value { - for pair in fields.into_iter() { - if let Value::Fields(pair) = &pair.value { - if pair.len() != 2 { - errors.insert( - field.id.to_owned(), - "Invalid number of mutlicast settings".to_owned(), - ); - break; - } - if let (Value::String(addr), Value::String(interface)) = - (&pair[0].value, &pair[1].value) - { - if !is_valid(addr) { - errors.insert( - pair[0].id.to_owned(), - "Expecting IP format 255.255.255.255".to_owned(), - ); - } - if !is_valid(interface) { - errors.insert( - pair[1].id.to_owned(), - "Expecting IP format 0.0.0.0".to_owned(), - ); - } - } else { - errors.insert( - field.id.to_owned(), - "Invalid values of mutlicast settings".to_owned(), - ); - break; - } - } else { - errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); - break; - } - } - } else { - errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); - } - } else { - errors.insert( - FIELD_MULTICAST_ADDR.to_owned(), - "Fail to find mutlicast settings".to_owned(), - ); - } - errors - } -} - #[cfg(test)] mod tests { use std::time::Duration; diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index fee146048f..08e9d3eb76 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,5 +1,6 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ComponentOptions } from "./options"; +import type { IODataType } from "./options"; import type { PluginParserSettings } from "./plugins"; export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; @@ -39,7 +40,7 @@ tz: string | null, }; */ export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; -export type Ident = { name: string, desc: string, uuid: string, }; +export type Ident = { name: string, desc: string, io: IODataType, uuid: string, }; export type IdentList = Array; diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 503f2c2e58..db47062e4f 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -17,8 +17,12 @@ export type FieldLoadingError = { id: string, err: string, }; export type FieldsValidationErrors = { errors: Map, }; +export type IODataType = "PlaitText" | "Raw" | "NetworkFramePayload" | { "Multiple": Array } | "Any"; + export type LazyFieldDesc = { id: string, name: string, desc: string, binding: string | null, }; +export type OutputRender = { "Columns": Array<[string, number]> } | "PlaitText" | "NoRender"; + export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, interface: ValueInput, binding: string | null, }; export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "Fields": Array }; diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 7940a10236..1f661fac52 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -86,6 +86,7 @@ pub struct IdentList(pub Vec); pub struct Ident { pub name: String, pub desc: String, + pub io: IODataType, pub uuid: Uuid, } diff --git a/application/apps/indexer/stypes/src/observe/proptest.rs b/application/apps/indexer/stypes/src/observe/proptest.rs index 1fdf62e69a..9d81261863 100644 --- a/application/apps/indexer/stypes/src/observe/proptest.rs +++ b/application/apps/indexer/stypes/src/observe/proptest.rs @@ -22,10 +22,11 @@ impl Arbitrary for Ident { type Strategy = BoxedStrategy; fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - (any::(), any::()) - .prop_map(|(name, desc)| Ident { + (any::(), any::(), any::()) + .prop_map(|(name, desc, io)| Ident { name, desc, + io, uuid: Uuid::new_v4(), }) .boxed() diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index e0b879e5f4..c00d068abe 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -1,8 +1,38 @@ #[cfg(feature = "nodejs")] mod nodejs; +#[cfg(test)] +mod proptest; use crate::*; +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum OutputRender { + Columns(Vec<(String, usize)>), + PlaitText, + NoRender, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub enum IODataType { + PlaitText, + Raw, + NetworkFramePayload, + Multiple(Vec), + Any, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/indexer/stypes/src/options/proptest.rs b/application/apps/indexer/stypes/src/options/proptest.rs new file mode 100644 index 0000000000..d4a642454c --- /dev/null +++ b/application/apps/indexer/stypes/src/options/proptest.rs @@ -0,0 +1,26 @@ +use crate::*; + +impl Arbitrary for IODataType { + type Parameters = bool; + type Strategy = BoxedStrategy; + + fn arbitrary_with(nested: Self::Parameters) -> Self::Strategy { + if !nested { + prop_oneof![ + Just(IODataType::Raw), + Just(IODataType::PlaitText), + Just(IODataType::NetworkFramePayload), + IODataType::arbitrary_with(true) + .prop_map(|inner| IODataType::Multiple(vec![inner])) + ] + .boxed() + } else { + prop_oneof![ + Just(IODataType::Raw), + Just(IODataType::PlaitText), + Just(IODataType::NetworkFramePayload), + ] + .boxed() + } + } +} diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 8601ff9fd1..f50e5b0e87 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -1,6 +1,7 @@ import { DltFilterConfig } from './dlt'; // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ComponentOptions } from './options'; +import type { IODataType } from './options'; import type { PluginParserSettings } from './plugins'; export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; @@ -12,7 +13,7 @@ export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOpt * functional role of the component. It is used to categorize components * according to their purpose in the data processing pipeline. */ -export type ComponentType = 'RawParser' | 'Parser' | 'Source'; +export type ComponentType = 'Parser' | 'Source'; /** * Settings for the DLT parser. @@ -41,7 +42,7 @@ export type DltParserSettings = { */ export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; -export type Ident = { name: string; desc: string; uuid: string }; +export type Ident = { name: string; desc: string; io: IODataType; uuid: string }; export type IdentList = Array; @@ -140,6 +141,15 @@ export type SerialTransportConfig = { exclusive: boolean; }; +/** + * Описывает операцию, которая должна быть произведена + */ +export type SessionAction = + | { File: string } + | { Files: Array } + | { ExportRaw: [Array, Array<{ start: bigint; end: bigint }>, string] } + | 'Source'; + export type SessionDescriptor = { parser: Ident; source: Ident }; export type SessionSetup = { @@ -158,8 +168,6 @@ export type SomeIpParserSettings = { fibex_file_paths: Array | null; }; -export type SessionAction = { File: string } | { Files: Array } | 'Source'; - /** * Configuration for TCP connections. */ diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 599f6b429b..e8f9d2de1e 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -22,8 +22,17 @@ export type FieldLoadingError = { id: string; err: string }; export type FieldsValidationErrors = { errors: Map }; +export type IODataType = + | 'PlaitText' + | 'Raw' + | 'NetworkFramePayload' + | { Multiple: Array } + | 'Any'; + export type LazyFieldDesc = { id: string; name: string; desc: string; binding: string | null }; +export type OutputRender = { Columns: Array<[string, number]> } | 'PlaitText' | 'NoRender'; + export type StaticFieldDesc = { id: string; name: string; From 640322ffe84b621d65b556b51919cc144090acdd Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 5 Jun 2025 20:38:36 +0200 Subject: [PATCH 056/121] Render related bindings --- .../apps/indexer/components/src/lib.rs | 21 +++++ .../apps/indexer/components/src/tys.rs | 6 +- .../indexer/parsers/src/dlt/descriptor.rs | 6 +- .../indexer/parsers/src/someip/descriptor.rs | 6 +- .../indexer/parsers/src/text/descriptor.rs | 4 +- .../indexer/session/src/components/api.rs | 20 +++++ .../indexer/session/src/components/mod.rs | 29 ++++++ .../apps/indexer/stypes/src/options/mod.rs | 1 - .../apps/indexer/stypes/src/options/nodejs.rs | 1 + application/apps/protocol/src/lib.rs | 1 + .../rs-bindings/src/js/components/mod.rs | 16 ++++ .../ts-bindings/src/api/components.ts | 5 ++ .../src/native/native.components.ts | 30 +++++++ .../client/src/app/schema/render/columns.ts | 89 +++++++++++-------- .../client/src/app/schema/render/dlt.ts | 3 +- .../client/src/app/schema/render/index.ts | 2 + .../client/src/app/schema/render/plugin.ts | 3 +- .../client/src/app/schema/render/someip.ts | 3 +- .../client/src/app/schema/render/text.ts | 2 +- .../client/src/app/schema/render/tools.ts | 4 +- .../client/src/app/service/components.ts | 17 +++- application/client/src/app/service/session.ts | 5 +- .../client/src/app/service/session/origin.ts | 43 ++++++++- application/holder/src/service/components.ts | 7 ++ .../service/components/get_output_render.ts | 25 ++++++ .../holder/src/service/components/index.ts | 1 + .../request/components/get_output_render.ts | 28 ++++++ .../platform/ipc/request/components/index.ts | 1 + 28 files changed, 322 insertions(+), 57 deletions(-) create mode 100644 application/holder/src/service/components/get_output_render.ts create mode 100644 application/platform/ipc/request/components/get_output_render.ts diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 3bbd310524..3f26cea0ae 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -134,6 +134,27 @@ impl Components { Ok(list) } + pub fn get_output_render( + &self, + uuid: &Uuid, + ) -> Result, stypes::NativeError> { + let Entry::Parser(descriptor) = self.components.get(&uuid).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {uuid}")), + })? + else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Fail to get render for {uuid}, because it isn't parser" + )), + }); + }; + Ok(descriptor.get_render()) + } + pub fn validate( &self, origin: &stypes::SessionAction, diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index cf9d240704..1cde1e2314 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -61,7 +61,7 @@ pub enum Entry { } impl ComponentDescriptor for Entry { - fn get_render(&self) -> stypes::OutputRender { + fn get_render(&self) -> Option { match self { Self::Source(inner) => inner.get_render(), Self::Parser(inner) => inner.get_render(), @@ -164,8 +164,8 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// making it possible to swap, reconfigure, or isolate components without touching /// the application core. pub trait ComponentDescriptor { - fn get_render(&self) -> stypes::OutputRender { - stypes::OutputRender::NoRender + fn get_render(&self) -> Option { + None } /// Check is component is campatible with given origin diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index f9205739c4..26ebdc5e42 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -91,8 +91,8 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> stypes::OutputRender { - stypes::OutputRender::Columns(vec![ + fn get_render(&self) -> Option { + Some(stypes::OutputRender::Columns(vec![ ("Datetime".to_owned(), 150), ("ECUID".to_owned(), 80), ("VERS".to_owned(), 80), @@ -104,7 +104,7 @@ impl ComponentDescriptor for Descriptor { ("CTID".to_owned(), 80), ("MSTP".to_owned(), 80), ("PAYLOAD".to_owned(), 0), - ]) + ])) } fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index c3bf0616d6..0a83410760 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -24,8 +24,8 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> stypes::OutputRender { - stypes::OutputRender::Columns(vec![ + fn get_render(&self) -> Option { + Some(stypes::OutputRender::Columns(vec![ ("SOME/IP".to_owned(), 50), ("SERV".to_owned(), 50), ("METH".to_owned(), 50), @@ -36,7 +36,7 @@ impl ComponentDescriptor for Descriptor { ("MSTP".to_owned(), 30), ("RETC".to_owned(), 30), ("PAYLOAD".to_owned(), 0), - ]) + ])) } fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 94cd182c1d..595e208cb8 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -22,8 +22,8 @@ impl ComponentFactory for Descriptor { } impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> stypes::OutputRender { - stypes::OutputRender::PlaitText + fn get_render(&self) -> Option { + Some(stypes::OutputRender::PlaitText) } fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { let files = match origin { diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index 953e916c8c..5f4b9474d6 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -66,6 +66,26 @@ pub enum Api { tx: oneshot::Sender>, }, + /// Requests the render supported by parser + /// + /// This API is used by the client to retrieve the render to use. + /// + /// # Arguments + /// + /// * `Uuid` - Uuid of parser. + /// * `tx` - A one-shot sender used to deliver the result back to the client. + /// + /// # Result + /// + /// * `Result, NativeError>` - The result containing the render or an error. + /// + /// # Note + /// If component doesn't have render, returns `None` + GetOutputRender( + Uuid, + oneshot::Sender, NativeError>>, + ), + /// Retrieves a list of components available in the system. /// /// This API is invoked by the client when it needs to get the list of available components of a specific type. diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index eadabedc56..1929fbb181 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -63,6 +63,9 @@ impl ComponentsSession { let mut tasks: HashMap)> = HashMap::new(); while let Some(msg) = rx_api.recv().await { match msg { + Api::GetOutputRender(uuid, tx) => { + log_if_err(tx.send(components.get_output_render(&uuid))); + } Api::GetOptions { origin, targets, @@ -228,6 +231,32 @@ impl ComponentsSession { response(rx.await, "Fail to get response from Api::GetOptions")? } + /// Requests the render supported by parser + /// + /// This API is used by the client to retrieve the render to use. + /// + /// # Arguments + /// + /// * `uuid` - Uuid of parser. + /// + /// # Result + /// + /// * `Result, NativeError>` - The result containing the render or an error. + /// + /// # Note + /// If component doesn't have render, returns `None` + pub async fn get_output_render( + &self, + uuid: Uuid, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetOutputRender(uuid, tx)), + "Fail to send Api::GetOutputRender", + )?; + response(rx.await, "Fail to get response from Api::GetOutputRender")? + } + /// Retrieves the list of available components of a specified type. /// /// This method sends an asynchronous request to get the list of components, filtered by type. diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index c00d068abe..51945ef403 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -15,7 +15,6 @@ use crate::*; pub enum OutputRender { Columns(Vec<(String, usize)>), PlaitText, - NoRender, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs index 7678486399..cc97da3336 100644 --- a/application/apps/indexer/stypes/src/options/nodejs.rs +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -8,3 +8,4 @@ try_into_js!(ValueInput); try_into_js!(CallbackOptionsEvent); try_into_js!(ComponentsOptionsList); try_into_js!(FieldsValidationErrors); +try_into_js!(OutputRender); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index a14b03f559..6178abc019 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -217,3 +217,4 @@ gen_encode_decode_fns!(FieldsValidationErrors); gen_encode_decode_fns!(ComponentOptions); gen_encode_decode_fns!(SessionSetup); gen_encode_decode_fns!(SessionDescriptor); +gen_encode_decode_fns!(OutputRender); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 6bad76efbe..bdb7a3dd8b 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -100,6 +100,22 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + #[node_bindgen] + async fn get_output_render( + &self, + uuid: String, + ) -> Result, stypes::ComputationError> { + let uuid = Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_output_render(uuid) + .await + .map_err(stypes::ComputationError::NativeError) + } + #[node_bindgen] async fn validate( &self, diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 149ff6edc7..1093f0dd31 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -10,6 +10,7 @@ import { FieldDesc, FieldsValidationErrors, Ident, + OutputRender, SessionAction, } from 'platform/types/bindings'; @@ -77,6 +78,10 @@ export class Components extends Subscriber { .then((list: ComponentsOptionsList) => list.options); } + public getOutputRender(uuid: string): Promise { + return this.native.getOutputRender(uuid); + } + public validate( origin: SessionAction, target: string, diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index aae38a42bb..81b86c2e8e 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -9,6 +9,7 @@ import { Field, ComponentOptions, FieldsValidationErrors, + OutputRender, } from 'platform/types/bindings'; import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; @@ -31,6 +32,8 @@ export abstract class ComponentsNative { public abstract getOptions(origin: Uint8Array, targets: string[]): Promise; + public abstract getOutputRender(uuid: String): Promise; + public abstract validate(origin: Uint8Array, fields: Uint8Array): Promise; public abstract abort(fields: string[]): void; @@ -202,6 +205,33 @@ export class Base extends Subscriber { }); } + public getOutputRender(uuid: String): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native.getOutputRender(uuid).then((buf: Uint8Array | null) => { + if (!buf) { + return resolve(null); + } + try { + resolve(protocol.decodeOutputRender(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.ComponentsOptions, + ), + ); + } + }); + }); + } + public validate( origin: SessionAction, target: string, diff --git a/application/client/src/app/schema/render/columns.ts b/application/client/src/app/schema/render/columns.ts index 5a00f98197..6e67c89f1f 100644 --- a/application/client/src/app/schema/render/columns.ts +++ b/application/client/src/app/schema/render/columns.ts @@ -4,8 +4,39 @@ import { Subject, Subjects } from '@platform/env/subscription'; import { error } from '@platform/log/utils'; import { bridge } from '@service/bridge'; import { LimittedValue } from '@ui/env/entities/value.limited'; +import { Render } from './index'; import * as num from '@platform/env/num'; +import { Protocol } from '@platform/types/observe/parser'; + +const MIN_COLUMN_WIDTH = 30; +const MAX_COLUMN_WIDTH = 600; + +export class ColumnsRender extends Render { + constructor(protected readonly headers: string[], sizes: number[]) { + super(); + this.setBoundEntity( + new Columns( + headers, + true, + sizes.map((w) => (w === 0 ? -1 : w)), + MIN_COLUMN_WIDTH, + MAX_COLUMN_WIDTH, + ), + ); + } + + public override protocol(): Protocol { + return Protocol.Dlt; + } + + public override columns(): number { + return this.headers.length; + } + public override delimiter(): string | undefined { + return `\u0004`; + } +} export interface Header { caption: string; @@ -20,10 +51,7 @@ export class Columns { protected readonly styles: Map = new Map(); protected readonly logger = scope.getLogger('Columns'); protected readonly defaults: { - headers: { - caption: string; - desc: string; - }[]; + headers: string[]; visability: boolean[] | boolean; widths: number[]; min: number[] | number; @@ -52,34 +80,26 @@ export class Columns { ) as number[]); this.headers.clear(); this.styles.clear(); - this.defaults.headers.forEach( - ( - desc: { - caption: string; - desc: string; - }, - index: number, - ) => { - const header = { - caption: desc.caption, - desc: desc.desc, - width: - this.defaults.widths[index] === -1 - ? undefined - : new LimittedValue( - `column_width_${index}`, - minWidths[index], - maxWidths[index], - this.defaults.widths[index], - ), - visible: headersVisability[index], - color: undefined, - index, - }; - this.headers.set(index, header); - this.styles.set(index, {}); - }, - ); + this.defaults.headers.forEach((caption, index: number) => { + const header = { + caption: caption, + desc: caption, + width: + this.defaults.widths[index] === -1 + ? undefined + : new LimittedValue( + `column_width_${index}`, + minWidths[index], + maxWidths[index], + this.defaults.widths[index], + ), + visible: headersVisability[index], + color: undefined, + index, + }; + this.headers.set(index, header); + this.styles.set(index, {}); + }); this.update().all(); this.hash = this.getHash(); } @@ -183,10 +203,7 @@ export class Columns { }); constructor( - headers: { - caption: string; - desc: string; - }[], + headers: string[], visability: boolean[] | boolean, widths: number[], min: number[] | number, diff --git a/application/client/src/app/schema/render/dlt.ts b/application/client/src/app/schema/render/dlt.ts index 5ca730c3f7..7970be44af 100644 --- a/application/client/src/app/schema/render/dlt.ts +++ b/application/client/src/app/schema/render/dlt.ts @@ -58,7 +58,8 @@ export class Implementation extends Render { super(); this.setBoundEntity( new Columns( - Implementation.HEADERS, + // Implementation.HEADERS, + [], true, [150, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1], MIN_COLUMN_WIDTH, diff --git a/application/client/src/app/schema/render/index.ts b/application/client/src/app/schema/render/index.ts index eb6013d96e..d57da7cc9c 100644 --- a/application/client/src/app/schema/render/index.ts +++ b/application/client/src/app/schema/render/index.ts @@ -3,9 +3,11 @@ import { Protocol } from '@platform/types/observe/parser/index'; export interface RenderReference { new (): Render; } + export abstract class Render { private _bound: T | undefined; + // TODO: not need any more this method abstract protocol(): Protocol; public delimiter(): string | undefined { diff --git a/application/client/src/app/schema/render/plugin.ts b/application/client/src/app/schema/render/plugin.ts index e00e4a2759..63fd573874 100644 --- a/application/client/src/app/schema/render/plugin.ts +++ b/application/client/src/app/schema/render/plugin.ts @@ -35,7 +35,8 @@ export class Implementation extends Render { this.setBoundEntity( new Columns( - headers, + // headers, + [], true, widths, columns_options.min_width, diff --git a/application/client/src/app/schema/render/someip.ts b/application/client/src/app/schema/render/someip.ts index 9fe995d46c..42aef54474 100644 --- a/application/client/src/app/schema/render/someip.ts +++ b/application/client/src/app/schema/render/someip.ts @@ -53,7 +53,8 @@ export class Implementation extends Render { super(); this.setBoundEntity( new Columns( - Implementation.HEADERS, + // Implementation.HEADERS, + [], true, [50, 50, 50, 30, 30, 30, 30, 30, 30, -1], MIN_COLUMN_WIDTH, diff --git a/application/client/src/app/schema/render/text.ts b/application/client/src/app/schema/render/text.ts index 59e8ba6e35..0c822a8db9 100644 --- a/application/client/src/app/schema/render/text.ts +++ b/application/client/src/app/schema/render/text.ts @@ -1,7 +1,7 @@ import { Render } from './index'; import { Protocol } from '@platform/types/observe/parser/index'; -export class Implementation extends Render { +export class TextRender extends Render { public override protocol(): Protocol { return Protocol.Text; } diff --git a/application/client/src/app/schema/render/tools.ts b/application/client/src/app/schema/render/tools.ts index 4e9beff136..e3463f9fa0 100644 --- a/application/client/src/app/schema/render/tools.ts +++ b/application/client/src/app/schema/render/tools.ts @@ -1,6 +1,6 @@ import { Implementation as Dlt } from './dlt'; import { Implementation as SomeIp } from './someip'; -import { Implementation as Text } from './text'; +import { TextRender } from './text'; import { Implementation as Plugin } from './plugin'; import { Render, RenderReference } from './index'; import { Session } from '@service/session/session'; @@ -15,7 +15,7 @@ const RENDERS: { } = { [Parsers.Protocol.Dlt]: Dlt, [Parsers.Protocol.SomeIp]: SomeIp, - [Parsers.Protocol.Text]: Text, + [Parsers.Protocol.Text]: TextRender, }; export function getDltRender(): Render { return new Dlt(); diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index f2324c6ee8..7372e62bea 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -11,7 +11,7 @@ import { import * as Events from '@platform/ipc/event/index'; import * as Requests from '@platform/ipc/request/index'; -import { Field, FieldDesc, Ident, SessionAction } from '@platform/types/bindings'; +import { Field, FieldDesc, Ident, OutputRender, SessionAction } from '@platform/types/bindings'; @SetupService(services['components']) export class Service extends Implementation { @@ -93,6 +93,21 @@ export class Service extends Implementation { }); } + public getOutputRender(uuid: string): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetOutputRender.Response, + new Requests.Components.GetOutputRender.Request({ + uuid, + }), + ) + .then((response) => { + resolve(response.render); + }) + .catch(reject); + }); + } + public validate( origin: SessionAction, target: string, diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index b77fb90c7c..24ff6fb41c 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -292,8 +292,11 @@ export class Service extends Implementation { }); }, observe: async (origin: SessionSourceOrigin, existed?: Session): Promise => { + debugger; + const render = await origin.getRender(); + const session = - existed !== undefined ? existed : await this.add(false).empty(getDltRender()); + existed !== undefined ? existed : await this.add(false).empty(render); return new Promise((resolve, reject) => { session.stream .observe() diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 259a5942ed..556dbba593 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -1,4 +1,14 @@ -import { SessionAction, Ident, SessionSetup, ComponentOptions } from '@platform/types/bindings'; +import { + SessionAction, + Ident, + SessionSetup, + ComponentOptions, + OutputRender, +} from '@platform/types/bindings'; +import { Render } from '@schema/render'; +import { ColumnsRender } from '@schema/render/columns'; +import { TextRender } from '@schema/render/text'; +import { components } from '@service/components'; export class SessionComponents { public parser: Ident | undefined; @@ -59,6 +69,37 @@ export class SessionSourceOrigin { public getDef(): SessionAction { return this.origin; } + + public getRender(): Promise> { + const parser = this.options.parser; + if (!parser) { + return Promise.reject(new Error(`No parsed defined`)); + } + return new Promise((resolve, reject) => { + components + .getOutputRender(parser.uuid) + .then((render: OutputRender | null | undefined) => { + if (!render) { + reject(new Error(`No output render for parser ${parser.uuid}`)); + return; + } + if (render === 'PlaitText') { + return resolve(new TextRender()); + } else if (typeof render === 'object') { + if ((render as { Columns: Array<[string, number]> }).Columns) { + const schema = (render as { Columns: Array<[string, number]> }).Columns; + return resolve( + new ColumnsRender( + schema.map((data: [string, number]) => data[0]), + schema.map((data: [string, number]) => data[1]), + ), + ); + } + } + }) + .catch(reject); + }); + } public getTitle(): string { if (this.origin === 'Source') { // TODO: Check idents diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index b76e52794d..a3777939ed 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -43,6 +43,13 @@ export class Service extends Implementation { Requests.Components.GetOptions.Request, RequestHandlers.GetOptions.handler, ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetOutputRender.Request, + RequestHandlers.GetOutputRender.handler, + ), electron .ipc() .respondent( diff --git a/application/holder/src/service/components/get_output_render.ts b/application/holder/src/service/components/get_output_render.ts new file mode 100644 index 0000000000..5663cad488 --- /dev/null +++ b/application/holder/src/service/components/get_output_render.ts @@ -0,0 +1,25 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { FieldDesc, OutputRender } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetOutputRender.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetOutputRender.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .getOutputRender(request.uuid) + .then((render: OutputRender | null) => { + resolve(new Requests.Components.GetOutputRender.Response({ render })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts index 92c414533d..262421c2d7 100644 --- a/application/holder/src/service/components/index.ts +++ b/application/holder/src/service/components/index.ts @@ -1,5 +1,6 @@ export * as GetSources from './get_sources'; export * as GetParsers from './get_parsers'; export * as GetOptions from './get_options'; +export * as GetOutputRender from './get_output_render'; export * as Abort from './abort'; export * as Validate from './validate'; diff --git a/application/platform/ipc/request/components/get_output_render.ts b/application/platform/ipc/request/components/get_output_render.ts new file mode 100644 index 0000000000..4a69cffbef --- /dev/null +++ b/application/platform/ipc/request/components/get_output_render.ts @@ -0,0 +1,28 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { FieldDesc, OutputRender, SessionAction } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetOutputRenderRequest' }) +export class Request extends SignatureRequirement { + public uuid: string; + + constructor(input: { uuid: string }) { + super(); + validator.isObject(input); + this.uuid = validator.getAsNotEmptyString(input, 'uuid'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetOutputRenderResponse' }) +export class Response extends SignatureRequirement { + public render: OutputRender | null | undefined; + + constructor(input: { render: OutputRender | null | undefined }) { + super(); + this.render = input.render; + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/index.ts b/application/platform/ipc/request/components/index.ts index 92c414533d..262421c2d7 100644 --- a/application/platform/ipc/request/components/index.ts +++ b/application/platform/ipc/request/components/index.ts @@ -1,5 +1,6 @@ export * as GetSources from './get_sources'; export * as GetParsers from './get_parsers'; export * as GetOptions from './get_options'; +export * as GetOutputRender from './get_output_render'; export * as Abort from './abort'; export * as Validate from './validate'; From 0b3401fc989a2ed0aaa699f6e8af46e40c98153e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 6 Jun 2025 15:15:14 +0200 Subject: [PATCH 057/121] Fix MessageProducer loop --- .../apps/indexer/parsers/src/text/mod.rs | 17 +---------------- .../apps/indexer/processor/src/producer/mod.rs | 8 ++++++-- application/client/src/app/service/session.ts | 1 - 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 60eb010f4e..88a3e65632 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,9 +1,8 @@ pub mod descriptor; -use std::{borrow::Cow, io}; +use std::borrow::Cow; use definitions::*; use serde::Serialize; -use stypes::NativeError; /// The most likely minimal bytes count needed to parse a text message. const MIN_MSG_LEN: usize = 1; @@ -15,20 +14,6 @@ pub struct StringMessage { content: String, } -// impl fmt::Display for StringMessage { -// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -// write!(f, "{}", self.content) -// } -// } - -// impl LogMessage for StringMessage { -// fn to_writer(&self, writer: &mut W) -> Result { -// let len = self.content.len(); -// writer.write_all(self.content.as_bytes())?; -// Ok(len) -// } -// } - impl StringTokenizer { fn parse_item<'a>( &mut self, diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index cf6e076e9f..f40c6de165 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -125,11 +125,15 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, } messages_received += 1; } else { - self.byte_source.consume(bytes_processed); if let Err(err) = self.writer.finalize() { error!("Fail to write data into writer: {err}"); } - return None; + if messages_received == 0 { + self.byte_source.consume(bytes_processed); + return None; + } else { + break; + } } } self.byte_source.consume(bytes_processed); diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 24ff6fb41c..1ab0fa725d 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -292,7 +292,6 @@ export class Service extends Implementation { }); }, observe: async (origin: SessionSourceOrigin, existed?: Session): Promise => { - debugger; const render = await origin.getRender(); const session = From d1f518f8ff2a34f4ac83c7fe3924611a91293f88 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 6 Jun 2025 17:04:02 +0200 Subject: [PATCH 058/121] Add IO compatibility checks --- .../src/app/ui/elements/scheme/component.ts | 19 +-- .../element/fields_collection/provider.ts | 7 +- .../src/app/ui/elements/scheme/provider.ts | 3 +- .../client/src/app/ui/styles/containers.less | 4 + .../client/src/app/ui/tabs/setup/provider.ts | 20 ++- .../client/src/app/ui/tabs/setup/state.ts | 130 +++++++++++++++--- .../src/app/ui/tabs/setup/template.html | 12 +- 7 files changed, 144 insertions(+), 51 deletions(-) diff --git a/application/client/src/app/ui/elements/scheme/component.ts b/application/client/src/app/ui/elements/scheme/component.ts index e85be2d95b..6e5a19f4a3 100644 --- a/application/client/src/app/ui/elements/scheme/component.ts +++ b/application/client/src/app/ui/elements/scheme/component.ts @@ -2,7 +2,6 @@ import { Component, ChangeDetectorRef, AfterContentInit, - AfterViewInit, AfterContentChecked, Input, } from '@angular/core'; @@ -22,7 +21,7 @@ import { FieldDesc } from '@platform/types/bindings'; @Ilc() export class SettingsScheme extends ChangesDetector - implements AfterContentInit, AfterContentChecked, AfterViewInit + implements AfterContentInit, AfterContentChecked { @Input() provider!: SchemeProvider; @@ -41,21 +40,11 @@ export class SettingsScheme if (this.uuid === this.provider.uuid) { return; } - this.ngAfterViewInit(); + this.ngAfterContentInit(); } - public ngAfterContentInit(): void {} - - public ngAfterViewInit(): void { + public ngAfterContentInit(): void { + this.fields = this.provider.getFieldDescs(); this.uuid = this.provider.uuid; - this.provider - .get() - .then((fields) => { - this.fields = fields; - this.detectChanges(); - }) - .catch((err: Error) => { - this.log().error(`Fail get fields: ${err.message}`); - }); } } export interface SettingsScheme extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts index 258e1f542e..90dad60518 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/fields_collection/provider.ts @@ -13,8 +13,11 @@ export class Provider extends SchemeProvider { super(unique()); } - public override get(): Promise { - return Promise.resolve([]); + public override getFieldDescs(): FieldDesc[] { + return []; + } + public override load(): Promise { + return Promise.resolve(); } public override setValue(uuid: string, value: Value): void { diff --git a/application/client/src/app/ui/elements/scheme/provider.ts b/application/client/src/app/ui/elements/scheme/provider.ts index 920280d0ac..bae0b46193 100644 --- a/application/client/src/app/ui/elements/scheme/provider.ts +++ b/application/client/src/app/ui/elements/scheme/provider.ts @@ -20,7 +20,8 @@ export abstract class SchemeProvider extends Subscriber { constructor(public readonly uuid: string) { super(); } - public abstract get(): Promise; + public abstract getFieldDescs(): FieldDesc[]; + public abstract load(): Promise; public abstract destroy(): Promise; public abstract setValue(uuid: string, value: Value): void; public abstract getFields(): Field[]; diff --git a/application/client/src/app/ui/styles/containers.less b/application/client/src/app/ui/styles/containers.less index e90cc44b80..a738f879d9 100644 --- a/application/client/src/app/ui/styles/containers.less +++ b/application/client/src/app/ui/styles/containers.less @@ -36,6 +36,10 @@ div.preset-container{ overflow: hidden; justify-content: stretch; justify-items: stretch; + &.vertical-scrolling { + overflow-y: auto; + overflow-x: hidden; + } } & div.preset-container-right { position: absolute; diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 940ec4e9f0..1be5556a23 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -16,7 +16,8 @@ export class Proivder extends SchemeProvider { protected pending: string[] = []; protected readonly logger = new Logger(`ObserveSetupProivder`); protected readonly values: Map = new Map(); - protected fields: string[] = []; + protected uuids: string[] = []; + protected fields: FieldDesc[] = []; protected hasErrors: boolean = false; constructor(protected readonly origin: SessionSourceOrigin, protected readonly target: string) { @@ -32,10 +33,10 @@ export class Proivder extends SchemeProvider { } public isEmpty(): boolean { - return this.fields.length === 0; + return this.uuids.length === 0; } - public override get(): Promise { + public override load(): Promise { this.pending.length === 0 && components.abort(this.pending); this.pending = []; return components @@ -43,9 +44,9 @@ export class Proivder extends SchemeProvider { .then((map: Map) => { const fields = map.get(this.target); if (!fields) { - return Promise.resolve([]); + return Promise.resolve(); } - this.fields = fields.map((field) => new WrappedField(field).id); + this.uuids = fields.map((field) => new WrappedField(field).id); this.pending = fields .map((field: FieldDesc) => { const lazy = field as { Lazy: LazyFieldDesc }; @@ -56,12 +57,17 @@ export class Proivder extends SchemeProvider { } }) .filter((id) => id !== undefined); - return Promise.resolve(fields === undefined ? [] : fields); + this.fields = fields; + return Promise.resolve(); }); } + public override getFieldDescs(): FieldDesc[] { + return this.fields; + } + public override setValue(uuid: string, value: Value): void { - if (!this.fields.includes(uuid)) { + if (!this.uuids.includes(uuid)) { this.logger.error(`Field ${uuid} doesn't belong to current provider`); return; } diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 34581cfcac..a91adcf205 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -1,4 +1,4 @@ -import { SessionAction, Ident } from '@platform/types/bindings'; +import { SessionAction, Ident, IODataType } from '@platform/types/bindings'; import { components } from '@service/components'; import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; @@ -34,11 +34,11 @@ export class State extends Subscriber { updated: new Subject(), }); public selected: { - source: string; - parser: string; + source: string | undefined; + parser: string | undefined; } = { - source: '', - parser: '', + source: undefined, + parser: undefined, }; public description: { source: ComponentDescription | undefined; @@ -106,7 +106,7 @@ export class State extends Subscriber { if (this.parsers.length > 0) { this.selected.parser = this.parsers[0].uuid; } - this.subjects.get().parsers.emit(this.sources); + this.subjects.get().parsers.emit(this.parsers); this.change().parser(); }) .catch((err: Error) => { @@ -129,19 +129,31 @@ export class State extends Subscriber { this.logger.error(`Fail to destroy source provider: ${err.message}`); }); } + this.providers.source = undefined; this.description.source = undefined; const ident = this.sources.find((ident) => ident.uuid == this.selected.source); if (ident !== undefined) { this.description.source = { full: '', ident }; } - this.providers.source = new Proivder(this.origin, this.selected.source); - this.register( - this.providers.source.subjects.get().error.subscribe(() => { - this.checkErrors(); - this.subjects.get().errorStateChange.emit(); - }), - ); - this.subjects.get().updated.emit(); + const provider = new Proivder(this.origin, this.selected.source); + provider + .load() + .then(() => { + this.register( + provider.subjects.get().error.subscribe(() => { + this.checkErrors(); + this.subjects.get().errorStateChange.emit(); + }), + ); + this.providers.source = provider; + }) + .catch((err: Error) => { + this.logger.error(`Fail load source options desc: ${err.message}`); + }) + .finally(() => { + this.subjects.get().updated.emit(); + this.checkCompatibility(); + }); }, parser: (): void => { if (this.selected.parser === undefined) { @@ -152,22 +164,43 @@ export class State extends Subscriber { this.logger.error(`Fail to destroy parser provider: ${err.message}`); }); } + this.providers.parser = undefined; + this.description.parser = undefined; const ident = this.parsers.find((ident) => ident.uuid == this.selected.parser); if (ident !== undefined) { this.description.parser = { full: '', ident }; } - this.providers.parser = new Proivder(this.origin, this.selected.parser); - this.register( - this.providers.parser.subjects.get().error.subscribe(() => { - this.checkErrors(); - this.subjects.get().errorStateChange.emit(); - }), - ); - this.subjects.get().updated.emit(); + const provider = new Proivder(this.origin, this.selected.parser); + provider + .load() + .then(() => { + this.register( + provider.subjects.get().error.subscribe(() => { + this.checkErrors(); + this.subjects.get().errorStateChange.emit(); + }), + ); + this.providers.parser = provider; + }) + .catch((err: Error) => { + this.logger.error(`Fail load parser options desc: ${err.message}`); + }) + .finally(() => { + this.subjects.get().updated.emit(); + }); }, }; } + public isParserIOCompatible(uuid: string): boolean { + const source = this.sources.find((source) => source.uuid === this.selected.source); + const parser = this.parsers.find((parser) => parser.uuid === uuid); + if (!source || !parser) { + return false; + } + return isIOCompatible(source.io, parser.io); + } + protected checkErrors() { if (!this.providers.parser || !this.providers.source) { this.locked = true; @@ -175,4 +208,57 @@ export class State extends Subscriber { this.locked = !this.providers.parser.isValid() || !this.providers.source.isValid(); } } + + protected checkCompatibility() { + const source = this.sources.find((source) => source.uuid === this.selected.source); + if (source === undefined || this.selected.parser === undefined) { + return; + } + const compatible = this.parsers + .filter((parser) => isIOCompatible(source.io, parser.io)) + .map((parser) => parser.uuid); + if (compatible.includes(this.selected.parser)) { + return; + } + this.selected.parser = compatible.length === 0 ? undefined : compatible[0]; + this.change().parser(); + } +} + +function isIOCompatible(a: IODataType, b: IODataType): boolean { + if (a === 'Any' || b === 'Any') { + return true; + } + if (typeof a === 'string' && typeof b === 'string') { + return a == b; + } + const aMtl: + | { + Multiple: Array; + } + | undefined = + typeof a === 'object' + ? (a as { + Multiple: Array; + }) + : undefined; + const bMtl: + | { + Multiple: Array; + } + | undefined = + typeof b === 'object' + ? (b as { + Multiple: Array; + }) + : undefined; + if (aMtl && bMtl) { + return aMtl.Multiple.find((a) => bMtl.Multiple.includes(a)) !== undefined; + } else if (aMtl) { + return aMtl.Multiple.includes(b); + } else if (bMtl) { + return bMtl.Multiple.includes(a); + } else { + return false; + } } diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index ae6e6e0562..7de78ed4e8 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -21,7 +21,11 @@ (ngModelChange)="state.change().parser()" [disabled]="state.parsers.length <= 1" > - + {{parser.name}} @@ -41,10 +45,10 @@

-
+
{{state.description.source.ident.name}} @@ -53,7 +57,7 @@ {{state.description.parser.ident.name}} From 691bcee11c328d8211cb32998d6b95924471430d Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 9 Jun 2025 16:03:24 +0200 Subject: [PATCH 059/121] Fix session writer (sync reading and UI updates) --- .../apps/indexer/definitions/src/record.rs | 4 +- .../indexer/processor/src/producer/mod.rs | 14 +-- .../src/handlers/observe/export_raw.rs | 74 ++++++++------- .../session/src/handlers/observe/session.rs | 91 +++++++++++-------- .../apps/indexer/session/src/state/api.rs | 10 -- .../apps/indexer/session/src/state/mod.rs | 13 --- 6 files changed, 105 insertions(+), 101 deletions(-) diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 02dff2406f..81a2a7fad1 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -113,7 +113,7 @@ pub trait LogRecordWriter { /// # Returns /// * `Ok(())` on success. /// * `Err(NativeError)` if the write operation fails. - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; + async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; /// Called once when the `MessageProducer` has no more data to provide. /// @@ -126,7 +126,7 @@ pub trait LogRecordWriter { /// # Returns /// * `Ok(())` on success. /// * `Err(NativeError)` if the finalization process fails. - fn finalize(&mut self) -> Result<(), stypes::NativeError>; + async fn finalize(&mut self) -> Result<(), stypes::NativeError>; /// Returns the unique identifier of the associated data source. /// diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index f40c6de165..33e3f4e62e 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -101,7 +101,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, if available == 0 { trace!("No more bytes available from source"); self.done = true; - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); @@ -119,13 +119,13 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, while let Some((consumed, msg)) = iterator.next() { bytes_processed += consumed; if let Some(msg) = msg { - if let Err(err) = self.writer.write(msg) { + if let Err(err) = self.writer.write(msg).await { error!("Fail to write data into writer: {err}"); return None; } messages_received += 1; } else { - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } if messages_received == 0 { @@ -162,7 +162,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -192,7 +192,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -207,7 +207,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -220,7 +220,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, error!("Parsing failed: Error {err}"); eprintln!("Parsing failed: Error: {err}"); self.done = true; - if let Err(err) = self.writer.finalize() { + if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index 3be8f51fd0..73dca1c8d4 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -37,7 +37,7 @@ impl ExportWriter { } impl LogRecordWriter for ExportWriter { - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { if !self.ranges.is_empty() { // TODO: we can optimize index search if !self @@ -51,42 +51,54 @@ impl LogRecordWriter for ExportWriter { } } self.index += 1; - match record { - LogRecordOutput::Raw(inner) => { - self.buffer.write_all(inner)?; - } - LogRecordOutput::Cow(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::String(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::Str(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::Columns(inner) => { - self.buffer.write_all( - inner - .join(&definitions::COLUMN_SENTINAL.to_string()) - .as_bytes(), - )?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::Multiple(inner) => { - for record in inner.into_iter() { - self.write(record)?; + fn fill<'a>( + writer: &mut ExportWriter, + record: LogRecordOutput<'a>, + ) -> Result>>, NativeError> { + match record { + LogRecordOutput::Raw(inner) => { + writer.buffer.write_all(inner)?; + Ok(None) + } + LogRecordOutput::Cow(inner) => { + writer.buffer.write_all(inner.as_bytes())?; + writer.buffer.write_all(&[b'\n'])?; + Ok(None) + } + LogRecordOutput::String(inner) => { + writer.buffer.write_all(inner.as_bytes())?; + writer.buffer.write_all(&[b'\n'])?; + Ok(None) + } + LogRecordOutput::Str(inner) => { + writer.buffer.write_all(inner.as_bytes())?; + writer.buffer.write_all(&[b'\n'])?; + Ok(None) + } + LogRecordOutput::Columns(inner) => { + writer.buffer.write_all( + inner + .join(&definitions::COLUMN_SENTINAL.to_string()) + .as_bytes(), + )?; + writer.buffer.write_all(&[b'\n'])?; + Ok(None) + } + LogRecordOutput::Multiple(inner) => Ok(Some(inner)), + LogRecordOutput::Attachment(_) => { + // TODO: report error + Ok(None) } } - LogRecordOutput::Attachment(_) => { - // TODO: report error + } + if let Some(records) = fill(self, record)? { + for record in records.into_iter() { + fill(self, record)?; } } Ok(()) } - fn finalize(&mut self) -> Result<(), stypes::NativeError> { + async fn finalize(&mut self) -> Result<(), stypes::NativeError> { self.buffer.flush()?; Ok(()) } diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index 8759168e1f..5459f51175 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -58,49 +58,63 @@ impl Writer { } impl LogRecordWriter for Writer { - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { - match record { - LogRecordOutput::Raw(inner) => { - // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs - // because during observe we do not expect raw data - self.buffer.push_str( - &inner - .iter() - .map(|b| format!("{:02X}", b)) - .collect::(), - ); - self.buffer.push('\n'); - } - LogRecordOutput::Cow(inner) => { - self.buffer.push_str(&inner); - self.buffer.push('\n'); - } - LogRecordOutput::String(inner) => { - self.buffer.push_str(&inner); - self.buffer.push('\n'); - } - LogRecordOutput::Str(inner) => { - self.buffer.push_str(inner); - self.buffer.push('\n'); - } - LogRecordOutput::Columns(inner) => { - self.buffer - .push_str(&inner.join(&definitions::COLUMN_SENTINAL.to_string())); - self.buffer.push('\n'); - } - LogRecordOutput::Multiple(inner) => { - for record in inner.into_iter() { - self.write(record)?; + async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + fn fill<'a>( + writer: &mut Writer, + record: LogRecordOutput<'a>, + ) -> Option>> { + match record { + LogRecordOutput::Raw(inner) => { + // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs + // because during observe we do not expect raw data + writer.buffer.push_str( + &inner + .iter() + .map(|b| format!("{:02X}", b)) + .collect::(), + ); + writer.buffer.push('\n'); + None + } + LogRecordOutput::Cow(inner) => { + writer.buffer.push_str(&inner); + writer.buffer.push('\n'); + None + } + LogRecordOutput::String(inner) => { + writer.buffer.push_str(&inner); + writer.buffer.push('\n'); + None + } + LogRecordOutput::Str(inner) => { + writer.buffer.push_str(inner); + writer.buffer.push('\n'); + None + } + LogRecordOutput::Columns(inner) => { + writer + .buffer + .push_str(&inner.join(&definitions::COLUMN_SENTINAL.to_string())); + writer.buffer.push('\n'); + None + } + LogRecordOutput::Multiple(inner) => Some(inner), + LogRecordOutput::Attachment(inner) => { + writer.attachments.push(inner); + None } } - LogRecordOutput::Attachment(inner) => { - self.attachments.push(inner); + } + if let Some(records) = fill(self, record) { + for record in records.into_iter() { + fill(self, record); } } if self.recent.elapsed().as_millis() > SEND_DURATION { if !self.buffer.is_empty() { self.state - .send_to_session_file(self.get_id(), std::mem::take(&mut self.buffer))?; + .write_session_file(self.get_id(), std::mem::take(&mut self.buffer)) + .await?; } for attachment in std::mem::take(&mut self.attachments) { // TODO: send with 1 call @@ -110,10 +124,11 @@ impl LogRecordWriter for Writer { } Ok(()) } - fn finalize(&mut self) -> Result<(), stypes::NativeError> { + async fn finalize(&mut self) -> Result<(), stypes::NativeError> { if !self.buffer.is_empty() { self.state - .send_to_session_file(self.get_id(), std::mem::take(&mut self.buffer))?; + .write_session_file(self.get_id(), std::mem::take(&mut self.buffer)) + .await?; } for attachment in std::mem::take(&mut self.attachments) { // TODO: send with 1 call diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index b50d205d8f..601b348335 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -39,7 +39,6 @@ pub enum Api { oneshot::Sender>, ), ), - SendToSessionFile(u16, String), FlushSessionFile(oneshot::Sender>), GetSessionFileOrigin(oneshot::Sender, stypes::NativeError>>), UpdateSession((u16, oneshot::Sender>)), @@ -208,7 +207,6 @@ impl Display for Api { Self::SetSessionFile(_) => "SetSessionFile", Self::GetSessionFile(_) => "GetSessionFile", Self::WriteSessionFile(_) => "WriteSessionFile", - Self::SendToSessionFile(..) => "SendToSessionFile", Self::FlushSessionFile(_) => "FlushSessionFile", Self::GetSessionFileOrigin(_) => "GetSessionFileOrigin", Self::UpdateSession(_) => "UpdateSession", @@ -453,14 +451,6 @@ impl SessionStateAPI { .await? } - pub fn send_to_session_file( - &self, - source_id: u16, - content: String, - ) -> Result<(), stypes::NativeError> { - self.sync_exec_operation(Api::SendToSessionFile(source_id, content)) - } - pub async fn flush_session_file(&self) -> Result<(), stypes::NativeError> { let (tx, rx) = oneshot::channel(); self.exec_operation(Api::FlushSessionFile(tx), rx).await? diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index 8691023b02..779eaf0a49 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -550,19 +550,6 @@ pub async fn run( stypes::NativeError::channel("Failed to respond to Api::WriteSessionFile") })?; } - Api::SendToSessionFile(source_id, content) => { - if let Err(err) = state - .handle_write_session_file( - source_id, - state_cancellation_token.clone(), - tx_callback_events.clone(), - content, - ) - .await - { - error!("Fail to write session file: {err}") - } - } Api::FlushSessionFile(tx_response) => { let res = state .handle_flush_session_file( From 246cb884c709801bfed7747e1df01402f0251d5f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 9 Jun 2025 18:58:39 +0200 Subject: [PATCH 060/121] Update setting of command source --- application/apps/indexer/sources/Cargo.toml | 1 + .../indexer/sources/src/command/descriptor.rs | 50 ++++++++-- .../apps/indexer/stypes/bindings/options.ts | 4 +- .../apps/indexer/stypes/src/options/mod.rs | 2 +- .../apps/rustcore/rs-bindings/Cargo.lock | 1 + .../inner/complex/files_selector/component.ts | 75 ++++++++++---- .../inner/complex/files_selector/module.ts | 5 + .../inner/complex/files_selector/styles.less | 15 ++- .../complex/files_selector/template.html | 44 ++++++--- .../elements/scheme/entry/inner/component.ts | 3 + .../entry/inner/element/files_selector.ts | 18 +++- .../scheme/entry/inner/element/index.ts | 4 +- .../scheme/entry/inner/element/key_value.ts | 97 +++++++++++++++++++ .../elements/scheme/entry/inner/template.html | 7 ++ .../platform/types/bindings/options.ts | 4 +- 15 files changed, 279 insertions(+), 51 deletions(-) create mode 100644 application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index dc1f4f883a..df80382885 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -26,6 +26,7 @@ lazy_static.workspace = true shellexpand = "3.1" socket2 = "0.5.8" serialport.workspace = true +envvars.workspace = true file-tools = { path = "../addons/file-tools" } stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index bd28f8a257..37b728233d 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -8,6 +8,8 @@ const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ ]); const FIELD_COMMAND: &str = "COMMAND_FIELD_COMMAND"; +const FIELD_CWD: &str = "COMMAND_FIELD_CWD"; +const FIELD_SHELLS: &str = "COMMAND_FIELD_SHELLS"; #[derive(Default)] pub struct Descriptor {} @@ -39,14 +41,46 @@ impl ComponentFactory for Descriptor { impl ComponentDescriptor for Descriptor { fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { - Ok(vec![FieldDesc::Static(StaticFieldDesc { - id: FIELD_COMMAND.to_owned(), - name: "Terminal Command".to_owned(), - desc: String::new(), - required: true, - interface: ValueInput::String(String::new(), "terminal command".to_owned()), - binding: None, - })]) + let mut shells = vec![("".to_owned(), "Default".to_owned())]; + if let Ok(profiles) = envvars::get_profiles() { + shells = profiles + .into_iter() + .map(|profile| { + let path = profile.path.to_string_lossy(); + (format!("{} ({path})", profile.name), path.to_string()) + }) + .collect(); + } + Ok(vec![ + FieldDesc::Static(StaticFieldDesc { + id: FIELD_COMMAND.to_owned(), + name: "Terminal Command".to_owned(), + desc: String::new(), + required: true, + interface: ValueInput::String(String::new(), "terminal command".to_owned()), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_CWD.to_owned(), + name: "Working Folder".to_owned(), + desc: String::new(), + required: true, + interface: ValueInput::Directory( + env::current_dir() + .map(|cwd| Some(cwd.to_string_lossy().to_string())) + .unwrap_or(None), + ), + binding: None, + }), + FieldDesc::Static(StaticFieldDesc { + id: FIELD_SHELLS.to_owned(), + name: "Shell".to_owned(), + desc: String::new(), + required: true, + interface: ValueInput::NamedStrings(shells), + binding: None, + }), + ]) } fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index db47062e4f..19019a952b 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -21,10 +21,10 @@ export type IODataType = "PlaitText" | "Raw" | "NetworkFramePayload" | { "Multip export type LazyFieldDesc = { id: string, name: string, desc: string, binding: string | null, }; -export type OutputRender = { "Columns": Array<[string, number]> } | "PlaitText" | "NoRender"; +export type OutputRender = { "Columns": Array<[string, number]> } | "PlaitText"; export type StaticFieldDesc = { id: string, name: string, desc: string, required: boolean, interface: ValueInput, binding: string | null, }; export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "Fields": Array }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | "Directory" | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | { "Directory": string | null } | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 51945ef403..5bb891a69c 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -190,7 +190,7 @@ pub enum ValueInput { Directories, Files(Vec), File(Vec), - Directory, + Directory(Option), Timezone, InputsCollection { elements: Vec, diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 2b9aa6766f..385c56e824 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -3254,6 +3254,7 @@ dependencies = [ "bytes", "components", "definitions", + "envvars", "etherparse", "file-tools", "futures", diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts index 202325f4be..a6e8a01bc4 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/component.ts @@ -5,6 +5,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { Element, FilesFolderSelectorElement } from '../../element'; import { bridge } from '@service/bridge'; import { File } from '@platform/types/files'; +import { FilesFoldersSelectorTarget } from '../../element/files_selector'; interface Path { name: string; @@ -20,6 +21,9 @@ interface Path { @Initial() @Ilc() export class FilesSelector extends ChangesDetector implements AfterContentInit { + public get FilesFoldersSelectorTarget(): typeof FilesFoldersSelectorTarget { + return FilesFoldersSelectorTarget; + } @Input() element!: Element; @Input() inner!: FilesFolderSelectorElement; @@ -32,30 +36,65 @@ export class FilesSelector extends ChangesDetector implements AfterContentInit { public ngAfterContentInit(): void {} public ngAddTarget() { - function dialog(exts: string): Promise { + function selectFile(exts: string): Promise { if (exts.trim() === '') { return bridge.files().select.any(); } else { return bridge.files().select.custom(exts); } } - dialog(this.inner.exts.join(',')) - .then((paths: File[]) => { - paths = paths.filter((added) => { - return this.paths.find((exist) => exist.path === added.filename) === undefined; - }); - this.paths = this.paths.concat( - paths.map((file) => { - return { path: file.filename, name: file.name }; - }), - ); - }) - .catch((err: Error) => { - this.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); + switch (this.inner.target) { + case FilesFoldersSelectorTarget.File: + case FilesFoldersSelectorTarget.Files: + selectFile(this.inner.exts.join(',')) + .then((paths: File[]) => { + paths = paths.filter((added) => { + return ( + this.paths.find((exist) => exist.path === added.filename) === + undefined + ); + }); + this.paths = this.paths.concat( + paths.map((file) => { + return { path: file.filename, name: file.name }; + }), + ); + }) + .catch((err: Error) => { + this.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); + }) + .finally(() => { + this.detectChanges(); + }); + + break; + case FilesFoldersSelectorTarget.Folders: + case FilesFoldersSelectorTarget.Folder: + bridge + .folders() + .select() + .then((paths: string[]) => { + if (this.inner.target === FilesFoldersSelectorTarget.Folder) { + if (paths.length > 0) { + this.inner.value = [paths[0]]; + this.element.change(); + } + } else { + this.paths = this.paths.concat( + paths.map((path) => { + return { path, name: path }; + }), + ); + } + }) + .catch((err: Error) => { + this.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); + }) + .finally(() => { + this.detectChanges(); + }); + break; + } } public ngOnRemovePath(path: string) { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts index 62a01de735..54fa7e8864 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/module.ts @@ -5,6 +5,8 @@ import { MatChipsModule } from '@angular/material/chips'; import { MatSelectModule } from '@angular/material/select'; import { MatListModule } from '@angular/material/list'; import { MatIcon } from '@angular/material/icon'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { FilesSelector } from './component'; @@ -16,6 +18,9 @@ import { FilesSelector } from './component'; MatSelectModule, MatListModule, MatIcon, + FormsModule, + ReactiveFormsModule, + MatFormFieldModule, ], declarations: [FilesSelector], exports: [FilesSelector], diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less index 48ac2ced20..643992b4c1 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/styles.less @@ -4,5 +4,18 @@ :host { position: relative; display: block; - + div.folder-input { + display: flex; + flex-direction: row; + justify-items: center; + & button { + margin-left: 12px; + } + & input { + background: var(--scheme-color-4); + border: thin solid var(--scheme-color-3); + color: var(--scheme-color-0); + outline: none; + } + } } \ No newline at end of file diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html index d69834e3ff..86ab0b3a09 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/files_selector/template.html @@ -1,14 +1,30 @@ - -
- - - {{path.name}} - - - -
+ +
+ + +
+
+ + +
+ + + {{path.name}} + + + +
+
diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts index 5272b9183e..bca6e1ba6f 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/component.ts @@ -64,6 +64,7 @@ export class SchemeEntryElement public elFilesFolderSelectorElement: els.FilesFolderSelectorElement | undefined; public elInputElement: els.InputElement | undefined; public elListElement: els.ListElement | undefined; + public elKeyValueElement: els.KeyValueElement | undefined; public elNamedValuesElement: els.NamedValuesElement | undefined; public elNestedDictionaryElement: els.NestedDictionaryElement | undefined; public elTimezoneSelectorElement: els.TimezoneSelectorElement | undefined; @@ -91,6 +92,8 @@ export class SchemeEntryElement this.element.inner instanceof els.InputElement ? this.element.inner : undefined; this.elListElement = this.element.inner instanceof els.ListElement ? this.element.inner : undefined; + this.elKeyValueElement = + this.element.inner instanceof els.KeyValueElement ? this.element.inner : undefined; this.elNamedValuesElement = this.element.inner instanceof els.NamedValuesElement ? this.element.inner : undefined; this.elNestedDictionaryElement = diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts index d2c09ef92a..2d439bf082 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/files_selector.ts @@ -13,9 +13,13 @@ export class FilesFolderSelectorElement extends ElementInner { constructor( public readonly target: FilesFoldersSelectorTarget, - public readonly exts: string[] = [], + public readonly exts: string[], + defaults: string | null | undefined, ) { super(); + if (target == FilesFoldersSelectorTarget.Folder) { + this.value = [defaults ? defaults : '']; + } } public getInnerValue(): any { @@ -44,18 +48,24 @@ export class FilesFolderSelectorElement extends ElementInner { export function tryFromOrigin(origin: ValueInput): FilesFolderSelectorElement | undefined { if (typeof origin === 'string' && origin === 'Directories') { - return new FilesFolderSelectorElement(FilesFoldersSelectorTarget.Folders); - } else if (typeof origin === 'string' && origin === 'Directory') { - return new FilesFolderSelectorElement(FilesFoldersSelectorTarget.Folder); + return new FilesFolderSelectorElement(FilesFoldersSelectorTarget.Folders, [], undefined); + } else if ((origin as { Directory: string | null }).Directory) { + return new FilesFolderSelectorElement( + FilesFoldersSelectorTarget.Folder, + [], + (origin as { Directory: string | null }).Directory, + ); } else if ((origin as { File: string[] }).File) { return new FilesFolderSelectorElement( FilesFoldersSelectorTarget.File, (origin as { File: string[] }).File, + undefined, ); } else if ((origin as { Files: string[] }).Files) { return new FilesFolderSelectorElement( FilesFoldersSelectorTarget.Files, (origin as { Files: string[] }).Files, + undefined, ); } else { return undefined; diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts index 0cf8151f72..2cc7816fa8 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/index.ts @@ -6,6 +6,7 @@ import * as CheckboxElement from './checkbox'; import * as FilesFolderSelectorElement from './files_selector'; import * as InputElement from './input_field'; import * as ListElement from './list'; +import * as KeyValueElement from './key_value'; import * as NamedValuesElement from './named'; import * as NestedDictionaryElement from './nested_dictionary'; import * as TimezoneSelectorElement from './timezone'; @@ -16,6 +17,7 @@ export { CheckboxElement } from './checkbox'; export { FilesFolderSelectorElement } from './files_selector'; export { InputElement } from './input_field'; export { ListElement } from './list'; +export { KeyValueElement } from './key_value'; export { NamedValuesElement } from './named'; export { NestedDictionaryElement, NestedDictionaryStructure } from './nested_dictionary'; export { TimezoneSelectorElement } from './timezone'; @@ -46,7 +48,6 @@ export type ValueInput = | { FieldsCollection: { elements: Array; add_title: string } } | { Bound: { output: ValueInput; inputs: Array } }; */ - export interface ChangeEvent { uuid: string; inner: any; @@ -79,6 +80,7 @@ export class Element { FilesFolderSelectorElement, InputElement, ListElement, + KeyValueElement, NamedValuesElement, NestedDictionaryElement, TimezoneSelectorElement, diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts new file mode 100644 index 0000000000..8530fc3cf5 --- /dev/null +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts @@ -0,0 +1,97 @@ +import { ElementInner, FieldCategory } from './inner'; +import { Value, ValueInput } from '@platform/types/bindings'; + +enum InnerType { + String, + Number, +} + +export class KeyValueElement extends ElementInner { + public value: V; + public items: [K, V][]; + constructor( + defaults: V, + items: [K, V][], + protected readonly innerType: InnerType, + protected readonly getter: (value: V) => Value, + ) { + super(); + this.value = defaults; + this.items = items; + } + + public getInnerValue(): any { + return this.value; + } + + public setValue(value: any) { + this.value = value; + } + + public getFieldCategory(): FieldCategory { + return FieldCategory.Inline; + } + + public getValue(): Value { + return this.getter(this.value); + } + public isNumeric(): boolean { + return this.innerType === InnerType.Number; + } + + public isString(): boolean { + return this.innerType === InnerType.String; + } +} + +export function tryFromOrigin(origin: ValueInput): KeyValueElement | undefined { + function as_named_numbers(origin: ValueInput): KeyValueElement | undefined { + const vl = origin as { NamedNumbers: [string, number][] }; + return vl.NamedNumbers + ? new KeyValueElement( + vl.NamedNumbers[0][1], + vl.NamedNumbers, + InnerType.Number, + (value: number): Value => { + return { + Number: + typeof value === 'number' + ? value + : typeof value === 'string' + ? parseInt(value, 10) + : 0, + }; + }, + ) + : undefined; + } + function as_named_strings(origin: ValueInput): KeyValueElement | undefined { + const vl = origin as { NamedStrings: [string, string][] }; + return vl.NamedStrings + ? new KeyValueElement( + vl.NamedStrings[0][1], + vl.NamedStrings, + InnerType.String, + (value: unknown): Value => { + return { + String: + typeof value === 'string' + ? value + : value + ? typeof value.toString === 'function' + ? value.toString() + : '' + : '', + }; + }, + ) + : undefined; + } + if ((origin as { NamedNumbers: [string, number][] }).NamedNumbers) { + return as_named_numbers(origin) as KeyValueElement; + } else if ((origin as { NamedStrings: [string, string][] }).NamedStrings) { + return as_named_strings(origin) as KeyValueElement; + } else { + return undefined; + } +} diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/template.html b/application/client/src/app/ui/elements/scheme/entry/inner/template.html index 2f23a47ba2..d053170077 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/template.html +++ b/application/client/src/app/ui/elements/scheme/entry/inner/template.html @@ -6,6 +6,13 @@ + + + + {{item[0]}} + + + diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index e8f9d2de1e..5eefc24a85 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -31,7 +31,7 @@ export type IODataType = export type LazyFieldDesc = { id: string; name: string; desc: string; binding: string | null }; -export type OutputRender = { Columns: Array<[string, number]> } | 'PlaitText' | 'NoRender'; +export type OutputRender = { Columns: Array<[string, number]> } | 'PlaitText'; export type StaticFieldDesc = { id: string; @@ -76,7 +76,7 @@ export type ValueInput = | 'Directories' | { Files: Array } | { File: Array } - | 'Directory' + | { Directory: string | null } | 'Timezone' | { InputsCollection: { elements: Array; add_title: string } } | { FieldsCollection: { elements: Array; add_title: string } } From 0f1cede0218bb9f785481cb7cb819f5a661daa30 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 10 Jun 2025 15:26:27 +0200 Subject: [PATCH 061/121] Add documentation to components and definitions --- .../apps/indexer/components/src/lib.rs | 156 ++++++++++++++++++ .../apps/indexer/components/src/tys.rs | 10 ++ .../apps/indexer/definitions/src/lib.rs | 5 + .../apps/indexer/definitions/src/record.rs | 28 +++- .../apps/indexer/definitions/src/source.rs | 4 +- 5 files changed, 197 insertions(+), 6 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 3f26cea0ae..f501b55213 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -9,16 +9,84 @@ use uuid::Uuid; pub use scheme::*; pub use tys::*; +/// The `Components` struct serves as a central registry and initialization hub for all components +/// used in the system, such as parsers and sources. It introduces a more flexible and modular +/// architecture compared to previous hardcoded solutions (prior to version 4.x.x). +/// +/// # Problem Statement +/// +/// In earlier versions, parsers and sources were explicitly enumerated and directly referenced +/// in the core system. This led to several architectural drawbacks: +/// +/// - Adding or removing a parser or source required changes to the core codebase. +/// - Component-related logic could not be easily isolated into self-contained modules. +/// - The frontend code had to mirror these changes, leading to tight coupling and redundancy. +/// +/// # Motivation +/// +/// Introducing the concept of a `Component` eliminates the need to hardcode system-wide knowledge +/// about available parsers and sources. Instead, it: +/// +/// - Allows independent development and isolation of each parser/source implementation. +/// - Ensures that adding, modifying, or removing a component does not impact the core, test suite, +/// or client code. +/// - Enables extensibility without requiring major refactoring of the core logic. +/// +/// # Implementation +/// +/// - At the session level, an instance of `Components` is created to register all available components. +/// - When initializing a session, the client provides a list of `Uuid`s representing the desired +/// components and their configurations. +/// - `Components` is responsible for initializing these components and handing them off for further use +/// (e.g., by `MessageProducer` or for data export). +/// +/// In addition, `Components` provides the following functionality: +/// +/// - Lists registered components (both parsers and sources). +/// - Provides the configuration schema for any component. +/// - Performs preliminary validation of the provided configuration. +/// +/// # Notes +/// +/// `Components` itself does not implement any teardown or destruction logic, as this is unnecessary: +/// +/// - It only stores `ComponentDescriptor` instances, which are lightweight and do not own the actual +/// component instances. +/// - Component instances are created on demand and immediately passed upwards for further use; they +/// are not retained by `Components`. +/// pub struct Components { components: HashMap>, } impl Components { + /// Creates an empty `Components` registry. + /// + /// This method initializes the `Components` structure without any registered parsers or sources. + /// Components should be registered manually using `add_parser` or `add_source` before use. + /// + /// # Returns + /// A new instance of `Components` with no components registered. pub fn new() -> Self { Self { components: HashMap::new(), } } + + /// Registers a parser component. + /// + /// This method adds a new parser to the registry. The parser is described by a `ComponentFactory`, + /// which defines how it should be instantiated and validated. + /// + /// # Arguments + /// * `descriptor` – A descriptor implementing `ComponentFactory

` that encapsulates parser metadata and behavior. + /// + /// # Returns + /// * `Ok(())` – If the parser was successfully registered. + /// * `Err(NativeError)` – If a component with the same UUID is already registered. + /// + /// # Errors + /// Returns a configuration error if a parser with the same UUID already exists in the registry. pub fn add_parser + 'static>( &mut self, descriptor: D, @@ -35,6 +103,21 @@ impl Components { .insert(ident.uuid, Entry::Parser(Box::new(descriptor))); Ok(()) } + + /// Registers a source component. + /// + /// Similar to `add_parser`, this method registers a data source implementation. + /// The descriptor provides construction logic and compatibility metadata. + /// + /// # Arguments + /// * `descriptor` – A descriptor implementing `ComponentFactory` for the source. + /// + /// # Returns + /// * `Ok(())` – If the source was successfully registered. + /// * `Err(NativeError)` – If a component with the same UUID is already registered. + /// + /// # Errors + /// Returns a configuration error if a source with the same UUID already exists in the registry. pub fn add_source + 'static>( &mut self, descriptor: D, @@ -52,6 +135,18 @@ impl Components { Ok(()) } + /// Returns a list of registered components of the specified type that are compatible with a given session. + /// + /// This is used to retrieve a filtered list of component identifiers for parsers or sources based on their compatibility + /// with the current session context. + /// + /// # Arguments + /// * `target` – The desired component type (`Parser` or `Source`). + /// * `origin` – The session action (e.g., export, view) to determine compatibility. + /// + /// # Returns + /// * `Ok(Vec)` – List of matching component identifiers. + /// * `Err(NativeError)` – On internal logic failure (should be rare). pub fn get_components( &self, target: &stypes::ComponentType, @@ -87,6 +182,21 @@ impl Components { .collect()) } + /// Returns configuration schemas for the specified component UUIDs. + /// + /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI + /// and optionally define lazy-loaded settings. + /// + /// # Arguments + /// * `origin` – Session action describing the context in which components will be used. + /// * `targets` – A list of component UUIDs to fetch configuration for. + /// + /// # Returns + /// * `Ok(HashMap)` – Mapping from component UUIDs to their configuration schemes. + /// * `Err(NativeError)` – If any of the UUIDs are not found in the registry. + /// + /// # Errors + /// Returns a configuration error with a list of UUIDs that were not found in the registry. pub fn get_options( &self, origin: stypes::SessionAction, @@ -134,6 +244,21 @@ impl Components { Ok(list) } + /// Returns the client-side render type for the specified component UUID. + /// + /// While typically associated with parsers (since they define the data format), + /// the system allows any component to provide rendering output hints. + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Ok(Some(OutputRender))` – If the parser provides render information. + /// * `Ok(None)` – If no render is defined. + /// * `Err(NativeError)` – If the UUID is not found or the component is not a parser. + /// + /// # Errors + /// Returns a configuration error if the component is missing or not a parser. pub fn get_output_render( &self, uuid: &Uuid, @@ -155,6 +280,22 @@ impl Components { Ok(descriptor.get_render()) } + /// Validates the configuration fields for a specified component within a session context. + /// + /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete + /// configurations before actual component instantiation. + /// + /// # Arguments + /// * `origin` – The session action context. + /// * `target` – The UUID of the component to validate. + /// * `fields` – A list of fields provided by the client. + /// + /// # Returns + /// * `Ok(HashMap)` – Empty if validation passed; otherwise, maps field UUIDs to error messages. + /// * `Err(NativeError)` – If the component UUID is not registered. + /// + /// # Errors + /// Returns a configuration error if the component is not found. pub fn validate( &self, origin: &stypes::SessionAction, @@ -169,6 +310,21 @@ impl Components { Ok(descriptor.validate(origin, fields)) } + /// Attempts to create instances of a parser and source component based on the session setup. + /// + /// This method creates fully initialized, ready-to-use instances of parser and source + /// based on the provided configuration. It also returns a `SessionDescriptor` + /// that contains display identifiers for tracking the session state. + /// + /// # Arguments + /// * `options` – Session setup data including parser/source UUIDs and user-defined field values. + /// + /// # Returns + /// * `Ok((SessionDescriptor, S, P))` – A tuple with session metadata, the source instance, and the parser instance. + /// * `Err(NativeError)` – If any of the components cannot be found or fail to initialize. + /// + /// # Errors + /// Returns configuration errors if components are missing or initialization fails. pub fn setup( &self, options: &stypes::SessionSetup, diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 1cde1e2314..2330d10e61 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -55,6 +55,16 @@ pub type StaticFieldsResult = Result, stypes::NativeError /// * `StaticFieldsResult` - A result containing static field data or a native error. pub type LazyFieldsTask = Pin + Send>>; +/// A wrapper for storing the full descriptor of a component. +/// +/// Although the internal value is stored as a `ComponentFactory`, it is important to note +/// that all factories are also required to implement `ComponentDescriptor`, i.e., +/// `ComponentFactory: ComponentDescriptor`. +/// Therefore, this enum represents a full component descriptor, not just a factory. +/// +/// Variants: +/// - `Parser`: A descriptor for a parser component. +/// - `Source`: A descriptor for a source component. pub enum Entry { Parser(Box>), Source(Box>), diff --git a/application/apps/indexer/definitions/src/lib.rs b/application/apps/indexer/definitions/src/lib.rs index 10d85a6fb3..5f48db9804 100644 --- a/application/apps/indexer/definitions/src/lib.rs +++ b/application/apps/indexer/definitions/src/lib.rs @@ -1,3 +1,8 @@ +/// The `definitions` crate describes the interfaces (traits) of the core system components: +/// - `Parser`: Responsible for interpreting incoming data. +/// - `Source`: Responsible for delivering data to the parser. +/// - `LogRecordOutput`: The result of parsing; represents the smallest unit of data in the system. +/// - `LogRecordWriter`: Responsible for writing data after processing; consumes `LogRecordOutput`. mod parser; mod record; mod source; diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 81a2a7fad1..7e9654f515 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -3,28 +3,46 @@ use std::borrow::Cow; pub const COLUMN_SENTINAL: char = '\u{0004}'; +/// Some log records (e.g., in binary formats like DLT) may include attached files. +/// This structure describes such attachments. #[derive(Debug, Clone, Serialize)] pub struct Attachment { + /// File name. pub name: String, + /// File size in bytes. pub size: usize, + /// File creation date, if available. pub created_date: Option, + /// File modification date, if available. pub modified_date: Option, - /// The indexes of the message within the original trace (0-based). + /// Indexes of the log messages (0-based) from which the file is composed. pub messages: Vec, + /// File content. pub data: Vec, } impl Attachment { + /// Appends additional data to the attachment. + /// + /// Since the content of a file may be split across multiple log messages, + /// this method allows merging the new chunk into the existing buffer. + /// + /// # Arguments + /// * `new_data` – A byte slice containing the next part of the file. pub fn add_data(&mut self, new_data: &[u8]) { self.data.extend_from_slice(new_data); } } +/// Represents the result of a single parsing step. +/// +/// This structure is used to report the outcome of log parsing from `MessageProducer` +/// to its controlling logic. #[derive(Debug)] pub struct ParseOperationResult { - /// Number of consumed bytes + /// Number of bytes successfully consumed from the input buffer. pub consumed: usize, - /// Count of parsed and sent messages + /// Number of messages that were parsed and forwarded. pub count: usize, } @@ -98,8 +116,8 @@ pub enum LogRecordOutput<'a> { /// When the `MessageProducer` has no more data to process, it calls `finalize`. At that point, /// the writer should persist any buffered data, if applicable. /// -/// **Important:** The `write` method must not be asynchronous. This is required to keep -/// the `MessageProducer` cancel-safe. +/// **Important:** (pending TODO) The `write` method must not be cancel-safe. This is required to keep +/// the `MessageProducer` cancel-safe. This problems isn't resolved yet. /// /// # Examples /// An implementation might buffer records in memory and write them in bulk diff --git a/application/apps/indexer/definitions/src/source.rs b/application/apps/indexer/definitions/src/source.rs index a140b10c1d..7480884f58 100644 --- a/application/apps/indexer/definitions/src/source.rs +++ b/application/apps/indexer/definitions/src/source.rs @@ -74,7 +74,9 @@ pub const DEFAULT_MIN_BUFFER_SPACE: usize = 10 * 1024; /// Requirements to `ByteSource` inner reader pub trait InnerReader: std::io::Read + Send + Unpin + 'static {} -/// In summary, this decision prioritizes architectural flexibility, reduces long-term maintenance risks, and ensures code stability, making the overall system more sustainable and extensible in the long run. +/// In summary, this decision prioritizes architectural flexibility, reduces long-term maintenance +/// risks, and ensures code stability, making the overall system more sustainable and extensible +/// in the long run. /// /// A `ByteSource` provides a way to read data from some underlying data source. But it does /// not provide a simple read interface, rather it allows implementations to filter the data From 1b978813b19ec2d7e2ba65d8f1ad85237253c581 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 12 Jun 2025 13:57:41 +0200 Subject: [PATCH 062/121] Fix merge conflicts --- application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 73ee49ab21..cc94473391 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -137,8 +137,7 @@ impl defs::Parser for PluginParser { &mut self, input: &'a [u8], timestamp: Option, - ) -> Result>)>, p::Error> - { + ) -> Result<(usize, Option>), defs::ParserError> { // Calls on plugins must be async. To solve that we got the following solutions: // - `futures::executor::block_on(plugin_call)`: Blocks the current Tokio worker with a local // executor. Risks are with blocking the whole runtime as Tokio isn't notified. From 8c6bd04627770cf1dc813d0c2c60d94351958cb7 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 12 Jun 2025 14:08:07 +0200 Subject: [PATCH 063/121] Remove unused lock file --- .../apps/rustcore/rs-bindings/Cargo.lock | 331 ++++++++++-------- 1 file changed, 177 insertions(+), 154 deletions(-) diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 385c56e824..2ac79b3e56 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -23,9 +23,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -258,7 +258,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -275,7 +275,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -302,7 +302,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -385,9 +385,9 @@ version = "0.1.1" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" dependencies = [ "allocator-api2", ] @@ -490,9 +490,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.25" +version = "1.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" dependencies = [ "jobserver", "libc", @@ -501,9 +501,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -835,7 +835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -859,7 +859,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -870,7 +870,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -921,7 +921,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -931,7 +931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -981,38 +981,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys 0.5.0", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users 0.4.6", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -1022,7 +990,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.1", ] [[package]] @@ -1044,7 +1012,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -1178,7 +1146,7 @@ version = "0.1.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -1347,7 +1315,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -1419,10 +1387,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", - "js-sys", "libc", -<<<<<<< HEAD - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -1435,10 +1401,6 @@ dependencies = [ "libc", "r-efi", "wasi 0.14.2+wasi-0.2.4", -======= - "wasi", - "wasm-bindgen", ->>>>>>> ce5e18605 (Add test drive runner (testing updated interfaces)) ] [[package]] @@ -1449,7 +1411,7 @@ checksum = "6a8661f18e73000bcea2f0d68262eb8ac1b1e951e10eb936d3f95b36201c136d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -1527,9 +1489,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "foldhash", "serde", @@ -1543,9 +1505,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "home" @@ -2057,9 +2019,9 @@ checksum = "4facc753ae494aeb6e3c22f839b158aebd4f9270f55cd3c79906c45476c47ab4" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memfd" @@ -2122,9 +2084,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] @@ -2137,7 +2099,7 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -2363,7 +2325,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -2431,7 +2393,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2560,8 +2522,7 @@ dependencies = [ "components", "definitions", "dir_checksum", - "dirs 6.0.0", - "futures", + "dirs", "log", "parsers", "rand 0.9.1", @@ -2817,17 +2778,6 @@ name = "redox_users" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" -dependencies = [ - "getrandom", - "libredox", - "thiserror 2.0.12", -] - -[[package]] -name = "regalloc2" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.16", "libredox", @@ -2894,9 +2844,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -3044,7 +2994,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3061,9 +3011,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -3108,7 +3058,7 @@ dependencies = [ "components", "crossbeam-channel", "definitions", - "dirs 6.0.0", + "dirs", "dlt-core", "envvars", "file-tools", @@ -3196,9 +3146,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] @@ -3266,7 +3216,7 @@ dependencies = [ "regex", "serde", "serialport", - "shellexpand 3.1.0", + "shellexpand", "socket2", "stypes", "thiserror 2.0.12", @@ -3333,9 +3283,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "f6397daf94fa90f058bd0fd88429dd9e5738999cca8d701813c80723add80462" dependencies = [ "proc-macro2", "quote", @@ -3350,7 +3300,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3437,7 +3387,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3448,7 +3398,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3527,7 +3477,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3574,9 +3524,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -3586,18 +3536,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", @@ -3609,9 +3559,9 @@ dependencies = [ [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tracing" @@ -3626,20 +3576,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -3682,7 +3632,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -3729,9 +3679,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -3836,9 +3786,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3871,7 +3821,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "wasm-bindgen-shared", ] @@ -3906,7 +3856,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3932,12 +3882,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.233.0" +version = "0.234.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9679ae3cf7cfa2ca3a327f7fab97f27f3294d402fd1a76ca8ab514e17973e4d3" +checksum = "170a0157eef517a179f2d20ed7c68df9c3f7f6c1c047782d488bf5a464174684" dependencies = [ "leb128fmt", - "wasmparser 0.233.0", + "wasmparser 0.234.0", ] [[package]] @@ -3955,9 +3905,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.233.0" +version = "0.234.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51cb03afce7964bbfce46602d6cb358726f36430b6ba084ac6020d8ce5bc102" +checksum = "be22e5a8f600afce671dd53c8d2dd26b4b7aa810fd18ae27dfc49737f3e02fc5" dependencies = [ "bitflags 2.9.1", "indexmap", @@ -4070,7 +4020,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -4197,7 +4147,7 @@ checksum = "55b39ffeda28be925babb2d45067d8ba2c67d2227328c5364d23b4152eba9950" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -4284,24 +4234,24 @@ dependencies = [ [[package]] name = "wast" -version = "233.0.0" +version = "234.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eaf4099d8d0c922b83bf3c90663f5666f0769db9e525184284ebbbdb1dd2180" +checksum = "f5fc6bea84cc3007ad3e68c6223f52095e6093eec4d7ebdff355f2c952fd9007" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width", - "wasm-encoder 0.233.0", + "wasm-encoder 0.234.0", ] [[package]] name = "wat" -version = "1.233.0" +version = "1.234.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d9bc80f5e4b25ea086ef41b91ccd244adde45d931c384d94a8ff64ab8bd7d87" +checksum = "1a4807d67cf6885965d2f118744fd0ad20ffb9082c875de532af37b499e65aa6" dependencies = [ - "wast 233.0.0", + "wast 234.0.0", ] [[package]] @@ -4339,7 +4289,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "witx", ] @@ -4351,7 +4301,7 @@ checksum = "71ac603ee46847d2e3c142ba715d326f1045155c7758f4e8dd001d5f92810c12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "wiggle-generate", ] @@ -4426,7 +4376,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -4437,14 +4387,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "d3bfe459f85da17560875b8bf1423d6f113b7a87a5d942e7da0ac71be7c61f8b" [[package]] name = "windows-result" @@ -4470,7 +4420,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -4479,7 +4429,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b36e9ed89376c545e20cbf5a13c306b49106b21b9d1d4f9cb9a1cb6b1e9ee06a" +dependencies = [ + "windows-targets 0.53.1", ] [[package]] @@ -4488,14 +4447,30 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30357ec391cde730f8fbfcdc29adc47518b06504528df977ab5af02ef23fdee9" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -4504,53 +4479,101 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -4630,7 +4653,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "synstructure", ] @@ -4651,7 +4674,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] @@ -4671,7 +4694,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", "synstructure", ] @@ -4705,7 +4728,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.102", ] [[package]] From d86ff19a3bd9424d8145ef796572392a8f9034b3 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 12 Jun 2025 15:45:47 +0200 Subject: [PATCH 064/121] Extend configuration --- .../indexer/sources/src/serial/descriptor.rs | 65 +++++- .../indexer/sources/src/serial/serialport.rs | 26 ++- .../sources/src/socket/udp/descriptor.rs | 13 +- .../indexer/stypes/src/options/extending.rs | 197 ++++++++++++++++++ .../apps/indexer/stypes/src/options/mod.rs | 5 + 5 files changed, 284 insertions(+), 22 deletions(-) create mode 100644 application/apps/indexer/stypes/src/options/extending.rs diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 0173c5d966..57bbbab663 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -1,6 +1,12 @@ use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; -use std::{collections::HashMap, io::Read, str}; -use stypes::{FieldDesc, LazyFieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; +use std::{collections::HashMap, str}; +use stypes::{ + ExtractByKey, FieldDesc, LazyFieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput, +}; + +use crate::prelude::SerialSource; + +use super::serialport::SerialConfig; const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, @@ -31,10 +37,55 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, - _options: &[stypes::Field], + origin: &SessionAction, + options: &[stypes::Field], ) -> Result, stypes::NativeError> { - Ok(None) + let missed = |field: &str| stypes::NativeError { + kind: stypes::NativeErrorKind::Configuration, + severity: stypes::Severity::ERROR, + message: Some(format!("Missed field {field} ")), + }; + let errors = self.validate(origin, options); + if !errors.is_empty() { + return Err(stypes::NativeError { + kind: stypes::NativeErrorKind::Configuration, + severity: stypes::Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let config = SerialConfig { + path: options + .extract_by_key(FIELD_PATH) + .ok_or(missed(FIELD_PATH))?, + baud_rate: options + .extract_by_key(FIELD_BAUD_RATE) + .ok_or(missed(FIELD_BAUD_RATE))?, + data_bits: options + .extract_by_key(FIELD_DATA_BITS) + .ok_or(missed(FIELD_DATA_BITS))?, + flow_control: options + .extract_by_key(FIELD_FLOW_CONTROL) + .ok_or(missed(FIELD_FLOW_CONTROL))?, + parity: options + .extract_by_key(FIELD_PARITY) + .ok_or(missed(FIELD_PARITY))?, + stop_bits: options + .extract_by_key(FIELD_STOP_BITS) + .ok_or(missed(FIELD_STOP_BITS))?, + send_data_delay: options + .extract_by_key(FIELD_SEND_DATA_DELAY) + .ok_or(missed(FIELD_SEND_DATA_DELAY))?, + exclusive: options + .extract_by_key(FIELD_EXCLUSIVE) + .ok_or(missed(FIELD_EXCLUSIVE))?, + }; + Ok(Some(crate::Source::Serial(SerialSource::new(config)?))) } } @@ -50,7 +101,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Serial Port Connection"), - desc: String::from("Connects to the specified serial port using user-defined settings. Data is received as a raw byte stream in a \"as-is\" mode - that is, the parser receives exactly what was read from the port, without any framing."), + desc: String::from( + "Connects to the specified serial port using user-defined settings. Data is received as a raw byte stream in a \"as-is\" mode - that is, the parser receives exactly what was read from the port, without any framing.", + ), io: stypes::IODataType::PlaitText, uuid: SERIAL_SOURCE_UUID, } diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index ee50bb2ffd..bd46098f0b 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -81,15 +81,27 @@ pub struct SerialSource { send_data_delay: u8, } -// Do we need to do some actions of destructor? -// impl Drop for SerialSource { -// fn drop(&mut self) { -// // Todo something good -// } -// } +pub struct SerialConfig { + /// The path to the serial port. + pub path: String, + /// The baud rate for the connection. + pub baud_rate: u32, + /// The number of data bits per frame. + pub data_bits: u8, + /// The flow control setting. + pub flow_control: u8, + /// The parity setting. + pub parity: u8, + /// The number of stop bits. + pub stop_bits: u8, + /// The delay in sending data, in milliseconds. + pub send_data_delay: u8, + /// Whether the connection is exclusive. + pub exclusive: bool, +} impl SerialSource { - pub fn new(config: &stypes::SerialTransportConfig) -> Result { + pub fn new(config: SerialConfig) -> Result { match tokio_serial::new(config.path.as_str(), config.baud_rate) .data_bits(data_bits(&config.data_bits)) .flow_control(flow_control(&config.flow_control)) diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 99cf267f93..62f3440abb 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -34,7 +34,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("UDP Connection"), - desc: String::from("Starts listening on the specified IP address and port, as well as on any user-defined multicast addresses. Each received UDP packet is passed to the parser as-is, excluding headers - only the payload is forwarded."), + desc: String::from( + "Starts listening on the specified IP address and port, as well as on any user-defined multicast addresses. Each received UDP packet is passed to the parser as-is, excluding headers - only the payload is forwarded.", + ), io: stypes::IODataType::NetworkFramePayload, uuid: UDP_SOURCE_UUID, } @@ -92,14 +94,7 @@ impl ComponentDescriptor for Descriptor { fields: &[stypes::Field], ) -> HashMap { fn is_valid(addr: &str) -> bool { - let mut count = 0; - for part in addr.split(".") { - if part.parse::().is_err() { - return false; - } - count += 1; - } - count == 4 + addr.parse::().is_ok() } let mut errors = HashMap::new(); if let Some(field) = fields.iter().find(|field| field.id == FIELD_MULTICAST_ADDR) { diff --git a/application/apps/indexer/stypes/src/options/extending.rs b/application/apps/indexer/stypes/src/options/extending.rs new file mode 100644 index 0000000000..d8527468d1 --- /dev/null +++ b/application/apps/indexer/stypes/src/options/extending.rs @@ -0,0 +1,197 @@ +use crate::*; + +pub trait ExtractByKey { + fn extract_by_key(&self, key: &str) -> Option; +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::String(value) => Some(value.to_owned()), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::Number(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => Some(*value), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} + +impl ExtractByKey for &[Field] { + fn extract_by_key(&self, key: &str) -> Option { + self.iter() + .find(|field| field.id == key) + .map(|field| match &field.value { + Value::Boolean(value) => Some(*value), + Value::Number(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + }) + .flatten() + } +} diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index 5bb891a69c..d02bc812da 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -1,8 +1,13 @@ +#[cfg(feature = "rustcore")] +mod extending; #[cfg(feature = "nodejs")] mod nodejs; #[cfg(test)] mod proptest; +#[cfg(feature = "rustcore")] +pub use extending::*; + use crate::*; #[derive(Debug, Serialize, Deserialize, Clone)] From 76e8a5b6c498486fa199a0530ed8a4ed67f2bbf6 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 12 Jun 2025 21:36:09 +0200 Subject: [PATCH 065/121] Update descriptors for parsers and sources --- .../apps/indexer/components/src/lib.rs | 2 +- .../apps/indexer/components/src/tys.rs | 9 +- .../indexer/parsers/src/someip/descriptor.rs | 8 +- .../src/binary/pcap/legacy/descriptor.rs | 18 +- .../sources/src/binary/pcap/ng/descriptor.rs | 18 +- .../sources/src/binary/raw/descriptor.rs | 4 +- .../indexer/sources/src/command/descriptor.rs | 72 +-- .../indexer/sources/src/serial/descriptor.rs | 71 +-- .../sources/src/socket/tcp/descriptor.rs | 61 ++- .../indexer/sources/src/socket/tcp/mod.rs | 12 +- .../sources/src/socket/tcp/reconnect.rs | 2 +- .../sources/src/socket/udp/descriptor.rs | 159 ++++--- .../indexer/sources/src/socket/udp/mod.rs | 23 +- .../indexer/stypes/src/options/extending.rs | 434 +++++++++++------- 14 files changed, 577 insertions(+), 316 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index f501b55213..fa109a1b18 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -307,7 +307,7 @@ impl Components { kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find component {target}")), })?; - Ok(descriptor.validate(origin, fields)) + descriptor.validate(origin, fields) } /// Attempts to create instances of a parser and source component based on the session setup. diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 2330d10e61..26f023b628 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -121,7 +121,7 @@ impl ComponentDescriptor for Entry { &self, origin: &stypes::SessionAction, fields: &[stypes::Field], - ) -> HashMap { + ) -> Result, stypes::NativeError> { match self { Self::Source(inner) => inner.validate(origin, fields), Self::Parser(inner) => inner.validate(origin, fields), @@ -296,13 +296,16 @@ pub trait ComponentDescriptor { /// /// * `HashMap` - A map where keys are field names and values are /// error messages, if any. An empty map means no validation errors. + /// + /// # Errors + /// Expected that returns error if some of fields aren't found. #[allow(unused)] fn validate( &self, origin: &stypes::SessionAction, fields: &[stypes::Field], - ) -> HashMap { - HashMap::new() + ) -> Result, stypes::NativeError> { + Ok(HashMap::new()) } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 0a83410760..678a05a71b 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,6 +1,8 @@ use components::{ComponentDescriptor, ComponentFactory}; use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; +use super::SomeipParser; + const SOMEIP_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, ]); @@ -19,7 +21,7 @@ impl ComponentFactory for Descriptor { _origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { - Ok(None) + Ok(Some(crate::Parser::SomeIp(SomeipParser::new()))) } } @@ -80,7 +82,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("SOME/IP"), - desc: String::from("SomeIP Parser is a binary parser designed to decode messages conforming to the AUTOSAR SOME/IP (Scalable service-Oriented Middleware over IP) protocol. It processes raw binary input and extracts structured message data, including service ID, method ID, payload length, and more. The parser can be applied to both files and network streams (e.g., TCP/UDP). If a valid DLT message is detected within the SOME/IP payload, it will be automatically passed to the DLT parser and decoded accordingly."), + desc: String::from( + "SomeIP Parser is a binary parser designed to decode messages conforming to the AUTOSAR SOME/IP (Scalable service-Oriented Middleware over IP) protocol. It processes raw binary input and extracts structured message data, including service ID, method ID, payload length, and more. The parser can be applied to both files and network streams (e.g., TCP/UDP). If a valid DLT message is detected within the SOME/IP payload, it will be automatically passed to the DLT parser and decoded accordingly.", + ), io: stypes::IODataType::NetworkFramePayload, uuid: SOMEIP_PARSER_UUID, } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 41d95f087f..665a7fa58d 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -1,6 +1,8 @@ use components::{ComponentFactory, ComponentDescriptor}; use file_tools::is_binary; -use stypes::SessionAction; +use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; + +use super::PcapLegacyByteSourceFromFile; const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, @@ -12,10 +14,20 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, + origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { - Ok(None) + let filepath = match origin { + SessionAction::File(file) => file, + SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(.. ) => { + return Err(NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Configuration, + message: Some("Pcap Legacy Source cannot be applied in this context".to_owned()) + }) + } + }; + Ok(Some(crate::Source::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?))) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 4ee150c54c..905e4225eb 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -1,6 +1,8 @@ use components::{ComponentFactory,ComponentDescriptor}; use file_tools::is_binary; -use stypes::SessionAction; +use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; + +use super::PcapngByteSourceFromFile; const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, @@ -12,10 +14,20 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, + origin: &SessionAction, _options: &[stypes::Field], ) -> Result, stypes::NativeError> { - Ok(None) + let filepath = match origin { + SessionAction::File(file) => file, + SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { + return Err(NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Configuration, + message: Some("Pcap NG Source cannot be applied in this context".to_owned()) + }) + } + }; + Ok(Some(crate::Source::PcapNg(PcapngByteSourceFromFile::new(filepath)?))) } } diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 86805809bd..9737d0720d 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -18,14 +18,14 @@ impl ComponentFactory for Descriptor { ) -> Result, stypes::NativeError> { let filename = match origin { SessionAction::File(filename) => filename, - _ => { + SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some( "origin isn't supported by this source (BinaryByteSource)".to_owned(), ), - }) + }); } }; Ok(Some(crate::Source::Raw( diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 37b728233d..525c633ec4 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -1,7 +1,10 @@ use super::ProcessSource; use components::{ComponentDescriptor, ComponentFactory}; use std::{collections::HashMap, env}; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; +use stypes::{ + ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, +}; const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, @@ -17,30 +20,34 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, - options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let Some(command) = options.iter().find_map(|field| { - if field.id == FIELD_COMMAND { - if let Value::String(command) = &field.value { - Some(command.clone()) - } else { - None - } - } else { - None - } - }) else { - return Ok(None); - }; + origin: &SessionAction, + options: &[Field], + ) -> Result, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let command: Extracted = options + .extract_by_key(FIELD_COMMAND) + .ok_or(missed(FIELD_COMMAND))?; Ok(Some(crate::Source::Process( - ProcessSource::new(command, env::current_dir().unwrap(), HashMap::new()).unwrap(), + ProcessSource::new(command.value, env::current_dir().unwrap(), HashMap::new()).unwrap(), ))) } } impl ComponentDescriptor for Descriptor { - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { let mut shells = vec![("".to_owned(), "Default".to_owned())]; if let Ok(profiles) = envvars::get_profiles() { shells = profiles @@ -93,7 +100,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Command Output"), - desc: String::from("Reads the standard output (stdout) of a launched command. The data is passed to the parser as lines of text."), + desc: String::from( + "Reads the standard output (stdout) of a launched command. The data is passed to the parser as lines of text.", + ), io: stypes::IODataType::PlaitText, uuid: TERM_SOURCE_UUID, } @@ -103,19 +112,16 @@ impl ComponentDescriptor for Descriptor { } fn validate( &self, - _origin: &stypes::SessionAction, - fields: &[stypes::Field], - ) -> HashMap { + _origin: &SessionAction, + fields: &[Field], + ) -> Result, NativeError> { let mut errors = HashMap::new(); - fields.iter().for_each(|field| { - if field.id == FIELD_COMMAND { - if let Value::String(path) = &field.value { - if path.trim().is_empty() { - errors.insert(field.id.clone(), "command cannot be empty".to_owned()); - } - } - } - }); - errors + let command: Extracted = fields + .extract_by_key(FIELD_COMMAND) + .ok_or(missed(FIELD_COMMAND))?; + if command.value.trim().is_empty() { + errors.insert(command.id.to_owned(), "command cannot be empty".to_owned()); + } + Ok(errors) } } diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 57bbbab663..4652261e80 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -1,7 +1,8 @@ use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; use std::{collections::HashMap, str}; use stypes::{ - ExtractByKey, FieldDesc, LazyFieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput, + ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, NativeErrorKind, + SessionAction, Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; use crate::prelude::SerialSource; @@ -38,18 +39,13 @@ impl ComponentFactory for Descriptor { fn create( &self, origin: &SessionAction, - options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let missed = |field: &str| stypes::NativeError { - kind: stypes::NativeErrorKind::Configuration, - severity: stypes::Severity::ERROR, - message: Some(format!("Missed field {field} ")), - }; - let errors = self.validate(origin, options); + options: &[Field], + ) -> Result, NativeError> { + let errors = self.validate(origin, options)?; if !errors.is_empty() { - return Err(stypes::NativeError { - kind: stypes::NativeErrorKind::Configuration, - severity: stypes::Severity::ERROR, + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, message: Some( errors .values() @@ -62,28 +58,36 @@ impl ComponentFactory for Descriptor { let config = SerialConfig { path: options .extract_by_key(FIELD_PATH) - .ok_or(missed(FIELD_PATH))?, + .ok_or(missed(FIELD_PATH))? + .value, baud_rate: options .extract_by_key(FIELD_BAUD_RATE) - .ok_or(missed(FIELD_BAUD_RATE))?, + .ok_or(missed(FIELD_BAUD_RATE))? + .value, data_bits: options .extract_by_key(FIELD_DATA_BITS) - .ok_or(missed(FIELD_DATA_BITS))?, + .ok_or(missed(FIELD_DATA_BITS))? + .value, flow_control: options .extract_by_key(FIELD_FLOW_CONTROL) - .ok_or(missed(FIELD_FLOW_CONTROL))?, + .ok_or(missed(FIELD_FLOW_CONTROL))? + .value, parity: options .extract_by_key(FIELD_PARITY) - .ok_or(missed(FIELD_PARITY))?, + .ok_or(missed(FIELD_PARITY))? + .value, stop_bits: options .extract_by_key(FIELD_STOP_BITS) - .ok_or(missed(FIELD_STOP_BITS))?, + .ok_or(missed(FIELD_STOP_BITS))? + .value, send_data_delay: options .extract_by_key(FIELD_SEND_DATA_DELAY) - .ok_or(missed(FIELD_SEND_DATA_DELAY))?, + .ok_or(missed(FIELD_SEND_DATA_DELAY))? + .value, exclusive: options .extract_by_key(FIELD_EXCLUSIVE) - .ok_or(missed(FIELD_EXCLUSIVE))?, + .ok_or(missed(FIELD_EXCLUSIVE))? + .value, }; Ok(Some(crate::Source::Serial(SerialSource::new(config)?))) } @@ -111,7 +115,7 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_PATH.to_owned(), @@ -211,7 +215,7 @@ impl ComponentDescriptor for Descriptor { } fn lazy_fields_getter( &self, - _origin: stypes::SessionAction, + _origin: SessionAction, _cancel: tokio_util::sync::CancellationToken, ) -> components::LazyFieldsTask { Box::pin(async move { @@ -232,19 +236,16 @@ impl ComponentDescriptor for Descriptor { } fn validate( &self, - _origin: &stypes::SessionAction, - fields: &[stypes::Field], - ) -> HashMap { + _origin: &SessionAction, + fields: &[Field], + ) -> Result, NativeError> { + let path: Extracted = fields + .extract_by_key(FIELD_PATH) + .ok_or(missed(FIELD_PATH))?; let mut errors = HashMap::new(); - fields.iter().for_each(|field| { - if field.id == FIELD_PATH { - if let Value::String(path) = &field.value { - if path.trim().is_empty() { - errors.insert(field.id.clone(), "Path cannot be empty".to_owned()); - } - } - } - }); - errors + if path.value.trim().is_empty() { + errors.insert(path.id.to_owned(), "Path cannot be empty".to_owned()); + } + Ok(errors) } } diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index 3d3d49317b..2f2f2f4d15 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -1,5 +1,12 @@ +use std::collections::HashMap; + use components::{ComponentDescriptor, ComponentFactory}; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; +use stypes::{ + ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, +}; + +use crate::prelude::TcpSource; const TCP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, @@ -14,10 +21,28 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) + origin: &SessionAction, + options: &[Field], + ) -> Result, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + Ok(Some(crate::Source::Tcp(TcpSource::new(&addr, None, None)?))) } } @@ -33,7 +58,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("TCP Connection"), - desc: String::from("Connects to the specified IP address and port to receive incoming messages. If the connection fails, an appropriate error is returned. Each individual TCP message is passed to the parser without any headers - only the payload is forwarded."), + desc: String::from( + "Connects to the specified IP address and port to receive incoming messages. If the connection fails, an appropriate error is returned. Each individual TCP message is passed to the parser without any headers - only the payload is forwarded.", + ), io: stypes::IODataType::NetworkFramePayload, uuid: TCP_SOURCE_UUID, } @@ -41,7 +68,7 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), name: "IP address and port".to_owned(), @@ -51,4 +78,24 @@ impl ComponentDescriptor for Descriptor { binding: None, })]) } + fn validate( + &self, + _origin: &SessionAction, + fields: &[Field], + ) -> Result, NativeError> { + fn is_valid(addr: &str) -> bool { + addr.parse::().is_ok() + } + let addr: Extracted = fields + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))?; + let mut errors = HashMap::new(); + if !is_valid(&addr.value) { + errors.insert( + addr.id.to_owned(), + "Expecting IP format 0.0.0.0::8888 (port is required)".to_owned(), + ); + } + Ok(errors) + } } diff --git a/application/apps/indexer/sources/src/socket/tcp/mod.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs index ea861b18a9..ab307a1557 100644 --- a/application/apps/indexer/sources/src/socket/tcp/mod.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -5,7 +5,7 @@ use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{net::SocketAddr, time::Duration}; -use tokio::net::TcpStream; +use tokio::{net::TcpStream, runtime::Handle}; use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; @@ -36,13 +36,13 @@ pub struct TcpSource { } impl TcpSource { - pub async fn new( + pub fn new( addr: &str, keepalive: Option, reconnect_info: Option, ) -> Result { let binding_address = addr.parse().map_err(std::io::Error::other)?; - let socket = Self::create_socket(binding_address, keepalive.as_ref()).await?; + let socket = Self::create_socket(binding_address, keepalive.as_ref())?; let reconnecter = reconnect_info.map(|rec| TcpReconnecter::new(rec, binding_address, keepalive)); Ok(Self { @@ -53,11 +53,13 @@ impl TcpSource { }) } - async fn create_socket( + fn create_socket( binding_address: SocketAddr, keep_alive: Option<&KeepAliveConfig>, ) -> std::io::Result { - let socket = TcpStream::connect(binding_address).await?; + let socket = tokio::task::block_in_place(|| { + Handle::current().block_on(TcpStream::connect(binding_address)) + })?; if let Some(keepalive_config) = keep_alive { let socket_ref = SockRef::from(&socket); let keepalive = TcpKeepalive::new() diff --git a/application/apps/indexer/sources/src/socket/tcp/reconnect.rs b/application/apps/indexer/sources/src/socket/tcp/reconnect.rs index ffe2b35bae..df30041797 100644 --- a/application/apps/indexer/sources/src/socket/tcp/reconnect.rs +++ b/application/apps/indexer/sources/src/socket/tcp/reconnect.rs @@ -119,7 +119,7 @@ async fn reconnect( } log::info!("Reconnecting to TCP server. Attempt: {attempts}"); - match super::TcpSource::create_socket(binding_address, keep_alive.as_ref()).await { + match super::TcpSource::create_socket(binding_address, keep_alive.as_ref()) { Ok(socket) => { if let Some(sender) = &reconnect_info.state_sender { if let Err(err) = sender.send(ReconnectStateMsg::Connected) { diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 62f3440abb..a19c2a0902 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -1,6 +1,11 @@ use components::{ComponentDescriptor, ComponentFactory}; use std::collections::HashMap; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, Value, ValueInput}; +use stypes::{ + ExtractAs, ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, + SessionAction, Severity, StaticFieldDesc, Value, ValueInput, missed_field_err as missed, +}; + +use super::{MulticastInfo, UdpSource}; const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, @@ -15,10 +20,65 @@ pub struct Descriptor {} impl ComponentFactory for Descriptor { fn create( &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(None) + origin: &SessionAction, + options: &[Field], + ) -> Result, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + let multicast: &Vec = options + .extract_by_key(FIELD_MULTICAST_ADDR) + .ok_or(missed(FIELD_MULTICAST_ADDR))? + .value; + let mut multicasts = Vec::new(); + for field in multicast { + let pair: &Vec = field.extract_as().ok_or(missed("Multicast Addr"))?; + if pair.len() != 2 { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some("Invalid settings of multicast address".to_owned()), + }); + } + let addr: String = (&pair[0]).extract_as().ok_or(missed("Multicast"))?; + let interface: String = (&pair[1]).extract_as().ok_or(missed("Interface"))?; + multicasts.push(MulticastInfo { + multiaddr: addr + .parse::() + .map_err(|err| NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some(format!("Fail to parse IP: {err}")), + })?, + interface: if interface.is_empty() { + None + } else { + Some(interface) + }, + }) + } + Ok(Some(crate::Source::Udp( + UdpSource::new(addr, multicasts).map_err(|err| NativeError { + kind: NativeErrorKind::Io, + severity: Severity::ERROR, + message: Some(err.to_string()), + })?, + ))) } } @@ -44,7 +104,7 @@ impl ComponentDescriptor for Descriptor { fn ty(&self) -> stypes::ComponentType { stypes::ComponentType::Source } - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), @@ -90,60 +150,55 @@ impl ComponentDescriptor for Descriptor { fn validate( &self, - _origin: &stypes::SessionAction, - fields: &[stypes::Field], - ) -> HashMap { + _origin: &SessionAction, + fields: &[Field], + ) -> Result, NativeError> { fn is_valid(addr: &str) -> bool { addr.parse::().is_ok() } + let multicast: Extracted<&Vec> = fields + .extract_by_key(FIELD_MULTICAST_ADDR) + .ok_or(missed(FIELD_MULTICAST_ADDR))?; let mut errors = HashMap::new(); - if let Some(field) = fields.iter().find(|field| field.id == FIELD_MULTICAST_ADDR) { - if let Value::Fields(fields) = &field.value { - for pair in fields.into_iter() { - if let Value::Fields(pair) = &pair.value { - if pair.len() != 2 { - errors.insert( - field.id.to_owned(), - "Invalid number of mutlicast settings".to_owned(), - ); - break; - } - if let (Value::String(addr), Value::String(interface)) = - (&pair[0].value, &pair[1].value) - { - if !is_valid(addr) { - errors.insert( - pair[0].id.to_owned(), - "Expecting IP format 255.255.255.255".to_owned(), - ); - } - if !is_valid(interface) { - errors.insert( - pair[1].id.to_owned(), - "Expecting IP format 0.0.0.0".to_owned(), - ); - } - } else { - errors.insert( - field.id.to_owned(), - "Invalid values of mutlicast settings".to_owned(), - ); - break; - } - } else { - errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); - break; + for pair in multicast.value.into_iter() { + if let Value::Fields(pair) = &pair.value { + if pair.len() != 2 { + errors.insert( + multicast.id.to_owned(), + "Invalid number of mutlicast settings".to_owned(), + ); + break; + } + if let (Value::String(addr), Value::String(interface)) = + (&pair[0].value, &pair[1].value) + { + if !is_valid(addr) { + errors.insert( + pair[0].id.to_owned(), + "Expecting IP format 255.255.255.255".to_owned(), + ); + } + if !interface.is_empty() && !is_valid(interface) { + errors.insert( + pair[1].id.to_owned(), + "Expecting IP format 0.0.0.0".to_owned(), + ); } + } else { + errors.insert( + multicast.id.to_owned(), + "Invalid values of mutlicast settings".to_owned(), + ); + break; } } else { - errors.insert(field.id.to_owned(), "Invalid mutlicast settings".to_owned()); + errors.insert( + multicast.id.to_owned(), + "Invalid mutlicast settings".to_owned(), + ); + break; } - } else { - errors.insert( - FIELD_MULTICAST_ADDR.to_owned(), - "Fail to find mutlicast settings".to_owned(), - ); } - errors + Ok(errors) } } diff --git a/application/apps/indexer/sources/src/socket/udp/mod.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs index beb349a1c7..d833724384 100644 --- a/application/apps/indexer/sources/src/socket/udp/mod.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -7,7 +7,10 @@ use definitions::*; use log::trace; use std::net::{IpAddr, Ipv4Addr}; use thiserror::Error; -use tokio::net::{ToSocketAddrs, UdpSocket}; +use tokio::{ + net::{ToSocketAddrs, UdpSocket}, + runtime::Handle, +}; pub use descriptor::*; @@ -25,6 +28,11 @@ pub enum UdpSourceError { Config(stypes::NetError), } +pub struct MulticastInfo { + pub multiaddr: IpAddr, + pub interface: Option, +} + pub struct UdpSource { buffer: DeqBuffer, socket: UdpSocket, @@ -32,16 +40,15 @@ pub struct UdpSource { } impl UdpSource { - pub async fn new( + pub fn new( addr: A, - multicast: Vec, + multicast: Vec, ) -> Result { - let socket = UdpSocket::bind(addr).await.map_err(UdpSourceError::Io)?; + let socket = + tokio::task::block_in_place(|| Handle::current().block_on(UdpSocket::bind(addr))) + .map_err(UdpSourceError::Io)?; for multicast_info in &multicast { - let multi_addr = multicast_info - .multicast_addr() - .map_err(UdpSourceError::Config)?; - match multi_addr { + match multicast_info.multiaddr { IpAddr::V4(addr) => { let inter: Ipv4Addr = match multicast_info.interface.as_ref() { Some(s) => s.parse().map_err(UdpSourceError::ParseAddr)?, diff --git a/application/apps/indexer/stypes/src/options/extending.rs b/application/apps/indexer/stypes/src/options/extending.rs index d8527468d1..6916acb9a4 100644 --- a/application/apps/indexer/stypes/src/options/extending.rs +++ b/application/apps/indexer/stypes/src/options/extending.rs @@ -1,197 +1,309 @@ +/// A set of utility traits designed to simplify working with component configuration fields. +/// +/// While the use of these traits is entirely optional, they can significantly reduce boilerplate code +/// and improve overall readability when extracting typed values from field definitions. use crate::*; +/// Helper function that creates a `NativeError` indicating a missing field in configuration. +pub fn missed_field_err(field: &str) -> NativeError { + NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some(format!("Missed field {field} ")), + } +} + +/// A wrapper structure that holds an extracted field value along with its ID. +pub struct Extracted<'a, T> { + /// The extracted value. + pub value: T, + /// The identifier of the field from which the value was extracted. + pub id: &'a str, +} + +impl<'a, T> Extracted<'a, T> { + /// Creates a new `Extracted` instance with the provided field ID and value. + pub fn new(id: &'a str, value: T) -> Self { + Self { value, id } + } +} + +/// Internal helper function used to avoid code duplication. +/// Searches for a field by its key and attempts to extract a value of the specified type. +fn extract_from_fields_by_key<'a, T>(fields: &'a [Field], key: &str) -> Option> +where + &'a Field: ExtractAs, +{ + fields + .iter() + .find(|field| field.id == key) + .map(|field| { + field + .extract_as() + .map(|v| Extracted::new(field.id.as_str(), v)) + }) + .flatten() +} + +/// Trait that provides typed extraction from a collection of `Field` objects using a string key. pub trait ExtractByKey { - fn extract_by_key(&self, key: &str) -> Option; + /// Attempts to extract a value of type `T` by field ID. + /// + /// Returns `Some(Extracted)` if the field exists and the value can be extracted, + /// otherwise returns `None`. + fn extract_by_key<'a>(&'a self, key: &str) -> Option>; } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::String(value) => Some(value.to_owned()), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::Number(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => Some(*value), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => (*value).try_into().ok(), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => (*value).try_into().ok(), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => (*value).try_into().ok(), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => (*value).try_into().ok(), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Number(value) => (*value).try_into().ok(), - Value::Boolean(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) } } impl ExtractByKey for &[Field] { - fn extract_by_key(&self, key: &str) -> Option { - self.iter() - .find(|field| field.id == key) - .map(|field| match &field.value { - Value::Boolean(value) => Some(*value), - Value::Number(..) - | Value::Directories(..) - | Value::Directory(..) - | Value::Fields(..) - | Value::File(..) - | Value::Files(..) - | Value::KeyNumber(..) - | Value::KeyNumbers(..) - | Value::KeyString(..) - | Value::KeyStrings(..) - | Value::String(..) - | Value::Numbers(..) - | Value::Strings(..) => None, - }) - .flatten() + fn extract_by_key<'a>(&'a self, key: &str) -> Option> { + extract_from_fields_by_key(&self, key) + } +} + +impl<'l> ExtractByKey<&'l Vec> for &'l [Field] { + fn extract_by_key<'a>(&'a self, key: &str) -> Option>> { + extract_from_fields_by_key(&self, key) + } +} + +/// A trait that provides type-specific extraction logic from a `Field`. +/// +/// This trait allows extracting a value of type `T` from a `Field` +/// if the internal representation supports it. +/// +/// Returns `Some(T)` if the conversion is successful, otherwise `None`. +pub trait ExtractAs { + /// Attempts to extract a value of type `T` from the field. + fn extract_as(&self) -> Option; +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::String(value) => Some(value.to_owned()), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::Number(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => Some(*value), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => (*value).try_into().map(|v| v).ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Number(value) => (*value).try_into().ok(), + Value::Boolean(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl ExtractAs for &Field { + fn extract_as(&self) -> Option { + match &self.value { + Value::Boolean(value) => Some(*value), + Value::Number(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Fields(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl<'l> ExtractAs<&'l Vec> for &'l Field { + fn extract_as(&self) -> Option<&'l Vec> { + match &self.value { + Value::Fields(value) => Some(value), + Value::Number(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Boolean(..) + | Value::File(..) + | Value::Files(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } } } From 508f89b5ab5d087fb241b4a4559318edd2e1a4f2 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 13 Jun 2025 15:35:02 +0200 Subject: [PATCH 066/121] Update dicionary config components --- .../indexer/parsers/src/dlt/descriptor.rs | 123 ++++++++++++------ .../apps/indexer/parsers/src/someip/mod.rs | 4 +- .../indexer/sources/src/command/descriptor.rs | 2 +- .../apps/indexer/stypes/bindings/options.ts | 2 +- .../indexer/stypes/src/options/extending.rs | 56 ++++++++ .../apps/indexer/stypes/src/options/mod.rs | 6 +- .../complex/nested_dictionary/component.ts | 5 +- .../inner/complex/nested_dictionary/state.ts | 41 +++--- .../entry/inner/complex/timezone/component.ts | 2 + .../scheme/entry/inner/element/key_value.ts | 16 +-- .../entry/inner/element/nested_dictionary.ts | 12 +- .../platform/types/bindings/options.ts | 4 +- 12 files changed, 179 insertions(+), 94 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 26ebdc5e42..ff07c8b1fd 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -7,12 +7,12 @@ use dlt_core::{ common::{StatisticInfo, StatisticInfoCollector}, }, }; -use std::collections::HashMap; -use std::fmt; use std::fs::File; +use std::{collections::HashMap, path::PathBuf}; +use std::{collections::HashSet, fmt}; use stypes::{ - FieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, StaticFieldDesc, Value, - ValueInput, + ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; use tokio_util::sync::CancellationToken; @@ -52,35 +52,60 @@ impl ComponentFactory for Descriptor { fn create( &self, origin: &SessionAction, - options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let mut filter_config: Option = None; - let mut someip_metadata: Option = None; - let mut dlt_metadata: Option = None; - for field in options.into_iter() { - if field.id == FIELD_FIBEX_FILES { - let Value::Files(paths) = &field.value else { - return Err(to_native_cfg_err("Invalid settings for Fibex files paths")); - }; - someip_metadata = if paths.is_empty() { - None - } else { - FibexSomeipMetadata::from_fibex_files(paths.clone()) - }; - dlt_metadata = if paths.is_empty() { - None - } else { - dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { - fibex_file_paths: paths - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect(), - }) - }; - } + options: &[Field], + ) -> Result, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); } + let fibex_file_paths: &Vec = options + .extract_by_key(FIELD_FIBEX_FILES) + .ok_or(missed(FIELD_FIBEX_FILES))? + .value; + let dlt_metadata = dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { + fibex_file_paths: fibex_file_paths + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect(), + }); + let someip_metadata = FibexSomeipMetadata::from_fibex_files(fibex_file_paths); + let min_log_level = u8_to_log_level( + options + .extract_by_key(FIELD_LOG_LEVEL) + .ok_or(missed(FIELD_LOG_LEVEL))? + .value, + ) + .ok_or(missed(FIELD_LOG_LEVEL))?; + let stats: &HashMap> = options + .extract_by_key(FIELD_STATISTICS) + .ok_or(missed(FIELD_STATISTICS))? + .value; + let filter_config: ProcessedDltFilterConfig = ProcessedDltFilterConfig { + min_log_level: Some(min_log_level), + app_ids: stats + .get(&StatFields::AppIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + ecu_ids: stats + .get(&StatFields::EcuIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + context_ids: stats + .get(&StatFields::ContextIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + app_id_count: 0, + context_id_count: 0, + }; Ok(Some(crate::Parser::Dlt(DltParser::new( - None, + Some(filter_config), dlt_metadata, None, someip_metadata, @@ -128,16 +153,16 @@ impl ComponentDescriptor for Descriptor { name: "Log Level".to_owned(), desc: "Log Level — defines the minimum severity level of log messages to be displayed. Messages with lower severity (e.g., Debug or Verbose) will be filtered out. For example, if \"Error\" is selected, only messages with level \"Error\" and above (e.g., Fatal) will be shown, while lower levels such as \"Warning\" or \"Debug\" will be ignored.".to_owned(), required: true, - interface: ValueInput::Strings( + interface: ValueInput::NamedNumbers( vec![ - "Fatal".to_owned(), - "Error".to_owned(), - "Warn".to_owned(), - "Info".to_owned(), - "Debug".to_owned(), - "Verbose".to_owned(), + ("Fatal".to_owned(), 1), + ("Error".to_owned(), 2), + ("Warn".to_owned(), 3), + ("Info".to_owned(), 4), + ("Debug".to_owned(), 5), + ("Verbose".to_owned(), 6), ], - "Verbose".to_owned(), + 6, ), binding: None, }), @@ -171,10 +196,12 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("DLT"), - desc: String::from("DLT Parser is a binary parser for decoding AUTOSAR DLT (Diagnostic Log and Trace) messages. It processes raw binary input and extracts structured log information according to the DLT protocol specification. The parser can be applied both to files (typically containing a StorageHeader) and to live streams over TCP or UDP. It expects binary data as input and does not perform any framing or transport-level parsing."), + desc: String::from( + "DLT Parser is a binary parser for decoding AUTOSAR DLT (Diagnostic Log and Trace) messages. It processes raw binary input and extracts structured log information according to the DLT protocol specification. The parser can be applied both to files (typically containing a StorageHeader) and to live streams over TCP or UDP. It expects binary data as input and does not perform any framing or transport-level parsing.", + ), io: stypes::IODataType::Multiple(vec![ stypes::IODataType::NetworkFramePayload, - stypes::IODataType::Raw + stypes::IODataType::Raw, ]), uuid: DLT_PARSER_UUID, } @@ -198,7 +225,7 @@ impl ComponentDescriptor for Descriptor { required: true, interface: ValueInput::KeyString(HashMap::new()), binding: None, - })]) + })]); } }; let mut stat = StatisticInfo::new(); @@ -305,3 +332,15 @@ impl ComponentDescriptor for Descriptor { stypes::ComponentType::Parser } } + +fn u8_to_log_level(level: u8) -> Option { + match level { + 1 => Some(dlt::LogLevel::Fatal), + 2 => Some(dlt::LogLevel::Error), + 3 => Some(dlt::LogLevel::Warn), + 4 => Some(dlt::LogLevel::Info), + 5 => Some(dlt::LogLevel::Debug), + 6 => Some(dlt::LogLevel::Verbose), + _ => None, + } +} diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 497a9828b9..1b18a67e3e 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -40,7 +40,7 @@ pub struct FibexMetadata { impl FibexMetadata { /// Returns a new meta-data from the given fibex-files. - pub fn from_fibex_files(paths: Vec) -> Option { + pub fn from_fibex_files(paths: &Vec) -> Option { let readers: Vec<_> = paths .iter() .filter_map(|path| FibexReader::from_file(path).ok()) @@ -196,7 +196,7 @@ impl SomeipParser { } /// Creates a new parser with the given files. - pub fn from_fibex_files(paths: Vec) -> Self { + pub fn from_fibex_files(paths: &Vec) -> Self { SomeipParser { fibex_metadata: FibexMetadata::from_fibex_files(paths), } diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 525c633ec4..1eefd4c26a 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -84,7 +84,7 @@ impl ComponentDescriptor for Descriptor { name: "Shell".to_owned(), desc: String::new(), required: true, - interface: ValueInput::NamedStrings(shells), + interface: ValueInput::NamedStrings(shells, String::new()), binding: None, }), ]) diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index 19019a952b..c44794ee53 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -27,4 +27,4 @@ export type StaticFieldDesc = { id: string, name: string, desc: string, required export type Value = { "Boolean": boolean } | { "Number": number } | { "Numbers": Array } | { "String": string } | { "Strings": Array } | { "Directories": Array } | { "Files": Array } | { "File": string } | { "Directory": string } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "Fields": Array }; -export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": Array<[string, number]> } | { "NamedStrings": Array<[string, string]> } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | { "Directory": string | null } | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; +export type ValueInput = { "Checkbox": boolean } | { "Number": number } | { "String": [string, string] } | { "Numbers": [Array, number] } | { "Strings": [Array, string] } | { "NamedBools": Array<[string, boolean]> } | { "NamedNumbers": [Array<[string, number]>, number] } | { "NamedStrings": [Array<[string, string]>, string] } | { "KeyNumber": Map } | { "KeyNumbers": Map } | { "KeyString": Map } | { "KeyStrings": Map } | { "NestedNumbersMap": [Map>>, Map] } | { "NestedStringsMap": [Map>>, Map] } | "Directories" | { "Files": Array } | { "File": Array } | { "Directory": string | null } | "Timezone" | { "InputsCollection": { elements: Array, add_title: string, } } | { "FieldsCollection": { elements: Array, add_title: string, } } | { "Bound": { output: ValueInput, inputs: Array, } }; diff --git a/application/apps/indexer/stypes/src/options/extending.rs b/application/apps/indexer/stypes/src/options/extending.rs index 6916acb9a4..84f75d2820 100644 --- a/application/apps/indexer/stypes/src/options/extending.rs +++ b/application/apps/indexer/stypes/src/options/extending.rs @@ -108,6 +108,20 @@ impl<'l> ExtractByKey<&'l Vec> for &'l [Field] { } } +impl<'l> ExtractByKey<&'l Vec> for &'l [Field] { + fn extract_by_key<'a>(&'a self, key: &str) -> Option>> { + extract_from_fields_by_key(&self, key) + } +} + +impl<'l> ExtractByKey<&'l HashMap>> for &'l [Field] { + fn extract_by_key<'a>( + &'a self, + key: &str, + ) -> Option>>> { + extract_from_fields_by_key(&self, key) + } +} /// A trait that provides type-specific extraction logic from a `Field`. /// /// This trait allows extracting a value of type `T` from a `Field` @@ -307,3 +321,45 @@ impl<'l> ExtractAs<&'l Vec> for &'l Field { } } } + +impl<'l> ExtractAs<&'l Vec> for &'l Field { + fn extract_as(&self) -> Option<&'l Vec> { + match &self.value { + Value::Files(value) => Some(value), + Value::Number(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Boolean(..) + | Value::File(..) + | Value::Fields(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::KeyStrings(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} + +impl<'l> ExtractAs<&'l HashMap>> for &'l Field { + fn extract_as(&self) -> Option<&'l HashMap>> { + match &self.value { + Value::KeyStrings(value) => Some(value), + Value::Number(..) + | Value::Directories(..) + | Value::Directory(..) + | Value::Boolean(..) + | Value::File(..) + | Value::Fields(..) + | Value::KeyNumber(..) + | Value::KeyNumbers(..) + | Value::KeyString(..) + | Value::Files(..) + | Value::String(..) + | Value::Numbers(..) + | Value::Strings(..) => None, + } + } +} diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index d02bc812da..f8af259af4 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -154,10 +154,10 @@ pub enum ValueInput { NamedBools(Vec<(String, bool)>), #[cfg_attr( all(test, feature = "test_and_gen"), - ts(type = "Array<[string, number]>") + ts(type = "[Array<[string, number]>, number]") )] - NamedNumbers(Vec<(String, i64)>), - NamedStrings(Vec<(String, String)>), + NamedNumbers(Vec<(String, i64)>, i64), + NamedStrings(Vec<(String, String)>, String), #[cfg_attr(all(test, feature = "test_and_gen"), ts(type = "Map"))] KeyNumber(HashMap), #[cfg_attr( diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts index 697f004ee2..714b82d5ff 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/component.ts @@ -2,12 +2,9 @@ import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import { bytesToStr, timestampToUTC } from '@env/str'; import { State } from './state'; import { Element, NestedDictionaryElement } from '../../element'; -import * as els from '../../element'; - @Component({ selector: 'app-settings-scheme-nested-dictionary', templateUrl: './template.html', @@ -27,7 +24,7 @@ export class NestedDictionary extends ChangesDetector implements AfterContentIni } public ngAfterContentInit(): void { - this.state = new State(this.inner.items, this.inner.dictionary); + this.state = new State(this.inner.items, this.inner.dictionary, this.element); } public ngOnEntitySelect() { diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts index 626fbf0225..d8fc76c2d9 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/nested_dictionary/state.ts @@ -1,9 +1,9 @@ import { Section } from './structure/section'; import { Summary } from './summary'; import { DictionaryEntities } from './structure/statentity'; -import { getTypedProp } from '@platform/env/obj'; import { NestedDictionaryStructure } from '../../element'; import { Holder as MatcherHolder } from '@module/matcher'; +import { Element } from '../../element'; export const ENTITIES = { app_ids: 'app_ids', @@ -29,6 +29,7 @@ export class State extends MatcherHolder { constructor( protected readonly data: NestedDictionaryStructure, protected readonly dictionary: Map, + protected readonly element: Element, ) { super(); this.struct().build(new Map()); @@ -118,35 +119,23 @@ export class State extends MatcherHolder { return; } selected.reset(); + const data: Map = new Map(); this.structure.forEach((structure) => { structure.entities.forEach((entity) => { - entity.selected && selected.inc(entity); + if (!entity.selected) { + return; + } + selected.inc(entity); + let entries = data.get(structure.key); + if (!entries) { + entries = []; + } + entries.push(entity.id); + data.set(structure.key, entries); }); }); - // const conf = this.observe.parser.as(Dlt.Configuration); - // if (conf === undefined) { - // return; - // } - // conf.setDefaultsFilterConfig(); - // let app_id_count = 0; - // let context_id_count = 0; - // this.structure.forEach((structure) => { - // if (structure.key === ENTITIES.app_ids) { - // app_id_count = structure.entities.length; - // } else if (structure.key === ENTITIES.context_ids) { - // context_id_count = structure.entities.length; - // } - // const selected = structure.getSelected().map((f) => f.id); - // if (selected.length === 0) { - // (conf.configuration.filter_config as any)[structure.key] = undefined; - // } else { - // (conf.configuration.filter_config as any)[structure.key] = selected; - // } - // }); - // if (conf.configuration.filter_config) { - // conf.configuration.filter_config.app_id_count = app_id_count; - // conf.configuration.filter_config.context_id_count = context_id_count; - // } + this.element.setValue(data); + this.element.change(); }, all: (): void => { this.buildSummary().total(); diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts index 3465bf94e6..1ac8ca7305 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts @@ -48,6 +48,8 @@ export class TimezoneSelector extends ChangesDetector implements AfterContentIni utc: timezone.utc, }; } + this.element.setValue(this.tz ? this.tz.offset : -1); + this.element.change(); this.detectChanges(); }, }, diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts index 8530fc3cf5..dc748dd41e 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/key_value.ts @@ -46,11 +46,11 @@ export class KeyValueElement extends ElementInner { export function tryFromOrigin(origin: ValueInput): KeyValueElement | undefined { function as_named_numbers(origin: ValueInput): KeyValueElement | undefined { - const vl = origin as { NamedNumbers: [string, number][] }; + const vl = origin as { NamedNumbers: [[string, number][], number] }; return vl.NamedNumbers ? new KeyValueElement( - vl.NamedNumbers[0][1], - vl.NamedNumbers, + vl.NamedNumbers[1], + vl.NamedNumbers[0], InnerType.Number, (value: number): Value => { return { @@ -66,11 +66,11 @@ export function tryFromOrigin(origin: ValueInput): KeyValueElement : undefined; } function as_named_strings(origin: ValueInput): KeyValueElement | undefined { - const vl = origin as { NamedStrings: [string, string][] }; + const vl = origin as { NamedStrings: [[string, string][], string] }; return vl.NamedStrings ? new KeyValueElement( - vl.NamedStrings[0][1], - vl.NamedStrings, + vl.NamedStrings[1], + vl.NamedStrings[0], InnerType.String, (value: unknown): Value => { return { @@ -87,9 +87,9 @@ export function tryFromOrigin(origin: ValueInput): KeyValueElement ) : undefined; } - if ((origin as { NamedNumbers: [string, number][] }).NamedNumbers) { + if ((origin as { NamedNumbers: [[string, number][], number] }).NamedNumbers) { return as_named_numbers(origin) as KeyValueElement; - } else if ((origin as { NamedStrings: [string, string][] }).NamedStrings) { + } else if ((origin as { NamedStrings: [[string, string][], string] }).NamedStrings) { return as_named_strings(origin) as KeyValueElement; } else { return undefined; diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts index 37c89d8f54..0d7809ce4a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/element/nested_dictionary.ts @@ -32,17 +32,19 @@ export class NestedDictionaryElement extends ElementInner { } } -export function tryFromOrigin(origin: ValueInput): NestedDictionaryElement | undefined { +export function tryFromOrigin( + origin: ValueInput, +): NestedDictionaryElement> | undefined { const vl = origin as { NestedNumbersMap: [Map>>, Map]; }; return vl.NestedNumbersMap instanceof Array && vl.NestedNumbersMap.length === 2 - ? new NestedDictionaryElement( - [], + ? new NestedDictionaryElement>( + new Map(), vl.NestedNumbersMap[0], vl.NestedNumbersMap[1], - (selections: string[]) => { - return { Strings: selections }; + (selections: Map) => { + return { KeyStrings: selections }; }, ) : undefined; diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index 5eefc24a85..dca267db4d 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -65,8 +65,8 @@ export type ValueInput = | { Numbers: [Array, number] } | { Strings: [Array, string] } | { NamedBools: Array<[string, boolean]> } - | { NamedNumbers: Array<[string, number]> } - | { NamedStrings: Array<[string, string]> } + | { NamedNumbers: [Array<[string, number]>, number] } + | { NamedStrings: [Array<[string, string]>, string] } | { KeyNumber: Map } | { KeyNumbers: Map } | { KeyString: Map } From a6450da0f9dd8da52333b4d65c66212e1a68274f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 18 Jun 2025 18:29:49 +0200 Subject: [PATCH 067/121] Fix error on parser iterator --- .../apps/indexer/definitions/src/parser.rs | 14 +++-- .../indexer/parsers/src/dlt/descriptor.rs | 6 +-- .../indexer/processor/src/producer/mod.rs | 53 ++++++++++++------- .../session/src/handlers/observe/session.rs | 20 +++++-- cli/chipmunk-cli/Cargo.lock | 23 +++++--- 5 files changed, 80 insertions(+), 36 deletions(-) diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index 5f443859f4..b2106ba94f 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -64,13 +64,16 @@ pub trait ParserIteratorGetter: Parser { where Self: Sized, { + if input.len() < self.min_msg_len() { + return Err(ParserError::Incomplete); + } // return early if function errors on first parse call. let (consumed, first) = self.parse(input, timestamp)?; Ok(ParserIterator { parser: self, input, consumed, - first, + first: Some((consumed, first)), timestamp, }) } @@ -82,7 +85,7 @@ pub struct ParserIterator<'a, P: Parser> { parser: &'a mut P, input: &'a [u8], consumed: usize, - first: Option>, + first: Option<(usize, Option>)>, timestamp: Option, } @@ -90,10 +93,11 @@ impl<'a, P: Parser> Iterator for ParserIterator<'a, P> { type Item = (usize, Option>); fn next(&mut self) -> Option { - if self.first.is_some() { - return Some((self.consumed, self.first.take())); + if let Some(first) = self.first.take() { + return Some(first); } - if self.input.len() < self.parser.min_msg_len() { + + if self.input[self.consumed..].len() < self.parser.min_msg_len() { return None; } diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index ff07c8b1fd..70c7a56cf5 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -7,12 +7,12 @@ use dlt_core::{ common::{StatisticInfo, StatisticInfoCollector}, }, }; +use std::fmt; use std::fs::File; use std::{collections::HashMap, path::PathBuf}; -use std::{collections::HashSet, fmt}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, - Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, + ExtractByKey, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, + StaticFieldDesc, ValueInput, missed_field_err as missed, }; use tokio_util::sync::CancellationToken; diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 33e3f4e62e..6d09b321ca 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -6,6 +6,25 @@ pub mod sde; use definitions::*; use log::warn; +/// ********************************************************************************************** +/// For Ammar: +/// +/// It makes sense to revisit the design of MessageProducer with the following considerations: +/// Remove the inner loop, which no longer appears necessary after all recent refactorings. +/// This would allow read_next_segment to simply read an available data fragment without +/// performing iteration, which should be handled at a higher level. +/// +/// Introduce an enum ProducerAction to group the logic by intent. Currently, in several +/// error-handling branches, we effectively duplicate code. Representing the different "actions" +/// explicitly would help make the logic inside read_next_segment clearer. For example: +/// +/// enum ProducerAction { +/// Return(..), // Return results +/// DropAndLoad(), // Attempt to load more data +/// ... +/// } +/// ********************************************************************************************** + /// Number of bytes to skip on initial parse errors before terminating the session. const INITIAL_PARSE_ERROR_LIMIT: usize = 1024; @@ -106,37 +125,35 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, } return Some((0, MessageStreamItem::Done)); } - // we can call consume only after all parse results are collected because of its // reference to self. - match self - .parser - .iter(self.byte_source.current_slice(), self.last_seen_ts) - { + match self.parser.iter(current_slice, self.last_seen_ts) { Ok(mut iterator) => { - let mut bytes_processed = 0; + let mut bytes_processed = skipped_bytes; let mut messages_received = 0; while let Some((consumed, msg)) = iterator.next() { bytes_processed += consumed; if let Some(msg) = msg { if let Err(err) = self.writer.write(msg).await { error!("Fail to write data into writer: {err}"); - return None; } messages_received += 1; - } else { - if let Err(err) = self.writer.finalize().await { - error!("Fail to write data into writer: {err}"); - } - if messages_received == 0 { - self.byte_source.consume(bytes_processed); - return None; - } else { - break; - } } } - self.byte_source.consume(bytes_processed); + if messages_received > 0 { + if let Err(err) = self.writer.finalize().await { + error!("Fail to write data into writer: {err}"); + } + } + bytes_processed = if bytes_processed > 0 { + // Some bytes had been processed or skipped + self.byte_source.consume(bytes_processed); + bytes_processed + } else { + // All bytes were ignored + self.byte_source.consume(available); + available + }; return Some(( bytes_processed, MessageStreamItem::Parsed(ParseOperationResult::new( diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index 5459f51175..a925ea627f 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -5,7 +5,7 @@ use crate::{ }; use definitions::*; use log::{trace, warn}; -use processor::producer::{sde::*, MessageProducer, MessageStreamItem}; +use processor::producer::{MessageProducer, MessageStreamItem, sde::*}; use std::time::Instant; use stypes::NativeError; use tokio::{select, sync::mpsc::Receiver}; @@ -147,6 +147,19 @@ enum Next { Sde(SdeMsg), } +/// ********************************************************************************************** +/// For Ammar: +/// +/// Currently, the use of select! requires careful handling of cancel-safety in all methods involved. +/// Instead of solving the rather complex problem of full cancel-safety, I suggest localizing the issue: +/// +/// * Remove all non-essential functionality from the select! block and use it only to listen for +/// messages. +/// * File reading can be performed in a loop that regularly checks the state of the SDE queue. +/// * Once the end of the file is reached, we can begin listening with select! for changes in the data +/// source, updates from SDE, and termination signals. +/// ********************************************************************************************** + pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, @@ -160,6 +173,7 @@ pub async fn run_producer( let cancel = operation_api.cancellation_token(); let cancel_on_tail = cancel.clone(); let started = Instant::now(); + while let Some(next) = select! { next_from_stream = async { producer.read_next_segment().await @@ -177,13 +191,13 @@ pub async fn run_producer( } { match next { Next::Parsed(_consumed, results) => match results { - MessageStreamItem::Parsed(_results) => { + MessageStreamItem::Parsed(results) => { //Just continue } MessageStreamItem::Done => { state.flush_session_file().await?; state.file_read().await?; - debug!( + warn!( "observe, message stream is done in {} ms", started.elapsed().as_millis() ); diff --git a/cli/chipmunk-cli/Cargo.lock b/cli/chipmunk-cli/Cargo.lock index 96d745c10d..ee4fb6aefb 100644 --- a/cli/chipmunk-cli/Cargo.lock +++ b/cli/chipmunk-cli/Cargo.lock @@ -335,7 +335,7 @@ version = "0.1.0" dependencies = [ "log", "stypes", - "thiserror 2.0.11", + "thiserror 2.0.12", "tokio", "tokio-util", "uuid", @@ -405,7 +405,7 @@ dependencies = [ "etherparse", "serde", "stypes", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] @@ -630,10 +630,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", - "js-sys", "libc", - "wasi", - "wasm-bindgen", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -975,7 +985,6 @@ dependencies = [ "definitions", "dlt-core", "file-tools", - "humantime", "lazy_static", "log", "memchr", @@ -986,7 +995,6 @@ dependencies = [ "someip-payload", "someip-tools", "stypes", - "tokio", "tokio-util", "uuid", ] @@ -1397,6 +1405,7 @@ dependencies = [ "bytes", "components", "definitions", + "envvars", "etherparse", "file-tools", "futures", From 4302fce312761c8e60d6d702d2f9ee3f3aff4be4 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 23 Jun 2025 14:05:22 +0200 Subject: [PATCH 068/121] Extend components API --- .../apps/indexer/components/src/lib.rs | 12 ++ .../indexer/session/src/components/api.rs | 17 ++ .../indexer/session/src/components/mod.rs | 36 +++- application/apps/protocol/src/lib.rs | 3 +- .../rs-bindings/src/js/components/mod.rs | 160 ++++++++++++++++++ .../ts-bindings/src/api/components.ts | 4 + .../ts-bindings/src/interfaces/errors.ts | 1 + .../src/native/native.components.ts | 30 ++++ .../client/src/app/service/components.ts | 117 +++++++++++++ application/holder/src/service/components.ts | 7 + .../src/service/components/get_ident.ts | 29 ++++ .../holder/src/service/components/index.ts | 1 + .../ipc/request/components/get_ident.ts | 29 ++++ .../platform/ipc/request/components/index.ts | 1 + 14 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 application/holder/src/service/components/get_ident.ts create mode 100644 application/platform/ipc/request/components/get_ident.ts diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index fa109a1b18..5e74d9ad32 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -280,6 +280,18 @@ impl Components { Ok(descriptor.get_render()) } + /// Returns the Ident of component (parser or source). + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Some(Ident)` – If the parser or source has been found. + /// * `None` – If no component is defined. + pub fn get_ident(&self, uuid: &Uuid) -> Option { + self.components.get(&uuid).map(|entity| entity.ident()) + } + /// Validates the configuration fields for a specified component within a session context. /// /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index 5f4b9474d6..f75b4db229 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -86,6 +86,23 @@ pub enum Api { oneshot::Sender, NativeError>>, ), + /// Requests the ident of component + /// + /// This API is used by the client to retrieve the identification of component parser or source. + /// + /// # Arguments + /// + /// * `Uuid` - Uuid of component (parser / source). + /// * `tx` - A one-shot sender used to deliver the result back to the client. + /// + /// # Result + /// + /// * `Option` - Ident of target component. + /// + /// # Note + /// If component doesn't exist, returns `None` + GetIdent(Uuid, oneshot::Sender>), + /// Retrieves a list of components available in the system. /// /// This API is invoked by the client when it needs to get the list of available components of a specific type. diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 1929fbb181..81d4b56bca 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -6,7 +6,7 @@ use log::{debug, error}; use std::collections::HashMap; use tokio::{ sync::{ - mpsc::{error::SendError, unbounded_channel, UnboundedReceiver, UnboundedSender}, + mpsc::{UnboundedReceiver, UnboundedSender, error::SendError, unbounded_channel}, oneshot::{self, error::RecvError}, }, task::{self, JoinHandle}, @@ -43,8 +43,8 @@ impl ComponentsSession { /// /// This method is asynchronous and should be awaited when called. The returned receiver can be used /// to listen for callback events related to the components' operations. - pub async fn new( - ) -> Result<(Self, UnboundedReceiver), stypes::NativeError> { + pub async fn new() + -> Result<(Self, UnboundedReceiver), stypes::NativeError> { let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = unbounded_channel(); let (tx_callback_events, rx_callback_events): ( @@ -66,6 +66,9 @@ impl ComponentsSession { Api::GetOutputRender(uuid, tx) => { log_if_err(tx.send(components.get_output_render(&uuid))); } + Api::GetIdent(uuid, tx) => { + log_if_err(tx.send(components.get_ident(&uuid))); + } Api::GetOptions { origin, targets, @@ -257,6 +260,33 @@ impl ComponentsSession { response(rx.await, "Fail to get response from Api::GetOutputRender")? } + /// Requests the ident of component + /// + /// This API is used by the client to retrieve the identification of component parser or source. + /// + /// # Arguments + /// + /// * `Uuid` - Uuid of component (parser / source). + /// * `tx` - A one-shot sender used to deliver the result back to the client. + /// + /// # Result + /// + /// * `Result, NativeError>` - Ident of target component. + /// + /// # Note + /// If component doesn't exist, returns `None` + pub async fn get_ident( + &self, + uuid: Uuid, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetIdent(uuid, tx)), + "Fail to send Api::GetIdent", + )?; + response(rx.await, "Fail to get response from Api::GetIdent") + } + /// Retrieves the list of available components of a specified type. /// /// This method sends an asynchronous request to get the list of components, filtered by type. diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 6178abc019..84048a05d7 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -107,7 +107,7 @@ mod generate; pub(crate) use err::*; pub(crate) use serde::Serialize; -pub(crate) use serde_wasm_bindgen::{Serializer, from_value}; +pub(crate) use serde_wasm_bindgen::{from_value, Serializer}; pub(crate) use stypes::*; pub(crate) use wasm_bindgen::prelude::*; @@ -206,6 +206,7 @@ gen_encode_decode_fns!(ResultSearchValues); gen_encode_decode_fns!(ResultScaledDistribution); gen_encode_decode_fns!(DltLevelDistribution); gen_encode_decode_fns!(SessionAction); +gen_encode_decode_fns!(Ident); gen_encode_decode_fns!(IdentList); gen_encode_decode_fns!(ComponentsOptionsList); gen_encode_decode_fns!(FieldDesc); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index bdb7a3dd8b..897a7d2eb8 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -16,6 +16,30 @@ impl Components { Self { session: None } } + /// Initializes a new session bound to the `Components` instance at the core level. + /// On the client side, there should only be a single global session; multiple instances of + /// `Components` are not required—only one is necessary. + /// + /// The `Components` structure maintains the list of all available components in the system, + /// specifically parsers and data sources. It is responsible for: + /// - Providing the list of available components + /// - Providing the configuration schema for a component + /// - Validating component configuration + /// - Supporting lazy loading of "heavy" configuration fields + /// + /// Note: Some components may have configuration fields that cannot be resolved or rendered + /// immediately (e.g., DLT file statistics). Such configuration fields are referred to as **lazy**. + /// `Components` first returns a general description of the lazy field, and once the field + /// is fully resolved, it provides the full configuration metadata. + /// While waiting, the client displays all static options along with a spinner or loading indicator + /// for the lazy ones. + /// + /// The `abort` method can be used to cancel the loading of a lazy configuration field. This is + /// particularly important if the user cancels the session before it is fully initialized. + /// + /// # Arguments + /// * `callback: F` - A callback function for communicating with the TypeScript client. It allows + /// Rust code to emit events back to the frontend. #[node_bindgen(mt)] async fn init( &mut self, @@ -58,6 +82,26 @@ impl Components { Ok(()) } + /// Retrieves the list of available components of a specified type. + /// + /// This method sends an asynchronous request to get the list of components, filtered by type. + /// It uses the `Api::GetComponents` command to query the system. + /// + /// # Arguments + /// + /// * `origin` - The source origin within which the components are being used. + /// * `ty` - The type of components to retrieve (e.g., parser, source). + /// + /// # Returns + /// + /// * `Ok(stypes::IdentList)` - A vector of component identifiers. + /// * `Err(stypes::ComputationError)` - An error if the request fails or the response is not received. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. #[node_bindgen] async fn get_components( &self, @@ -78,6 +122,26 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + /// Retrieves the configuration options for a list of components. + /// + /// This method sends an asynchronous request to obtain the settings of the specified components. + /// It uses the `Api::GetOptions` command to communicate with the underlying system. + /// + /// # Arguments + /// + /// * `targets` - A vector of UUIDs representing the components whose settings are being requested. + /// * `origin` - The source origin within which the components are being used. + /// + /// # Returns + /// + /// * `Ok(stypes::ComponentsOptionsList)` - The list of component configuration options. + /// * `Err(stypes::ComputationError)` - An error if the request fails or the response is not received. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. #[node_bindgen] async fn get_options( &self, @@ -100,6 +164,20 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + /// Requests the render supported by parser + /// + /// This API is used by the client to retrieve the render to use. + /// + /// # Arguments + /// + /// * `uuid` - Uuid of parser. + /// + /// # Result + /// + /// * `Result, ComputationError>` - The result containing the render or an error. + /// + /// # Note + /// If component doesn't have render, returns `None` #[node_bindgen] async fn get_output_render( &self, @@ -116,6 +194,60 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + /// Requests the ident of component + /// + /// This API is used by the client to retrieve the identification of component parser or source. + /// + /// # Arguments + /// + /// * `Uuid` - Uuid of component (parser / source). + /// * `tx` - A one-shot sender used to deliver the result back to the client. + /// + /// # Result + /// + /// * `Result, ComputationError>` - Ident of target component. + /// + /// # Note + /// If component doesn't exist, returns `None` + #[node_bindgen] + async fn get_ident( + &self, + uuid: String, + ) -> Result, stypes::ComputationError> { + let uuid = Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_ident(uuid) + .await + .map_err(stypes::ComputationError::NativeError) + } + + /// Asynchronously validates the configuration of a specific component. + /// + /// This method sends a validation request to the system and waits for the response asynchronously. + /// + /// # Arguments + /// + /// * `source` - The origin type indicating the context in which the validation is performed. + /// * `target` - The identifier of the component (parser, source). + /// * `fields` - A list of configuration field values to be validated. + /// + /// # Returns + /// + /// `Result`: + /// * On success: A `HashMap` (wrapped into `stypes::FieldsValidationErrors`) where the key is the field's + /// identifier (`String`) and the value is the error message (`String`). + /// * If all fields are valid and have no errors, an empty `HashMap` is returned. + /// * On failure: A `stypes::ComputationError` indicating the reason for the validation failure. + /// + /// # Errors + /// + /// Returns an error if: + /// * The API call to validate the configuration fails to send. + /// * The response from the validation API cannot be retrieved. #[node_bindgen] async fn validate( &self, @@ -138,6 +270,19 @@ impl Components { }) } + /// Aborts the lazy loading tasks for the specified fields. + /// + /// This method sends a cancellation request for ongoing lazy loading tasks associated with the given field IDs. + /// It uses the `Api::CancelLoading` command to perform the operation. + /// + /// # Arguments + /// + /// * `fields` - A vector of field IDs for which the lazy loading should be aborted. + /// + /// # Returns + /// + /// * `Ok(())` - If the cancellation request was successfully sent. + /// * `Err(stypes::ComputationError)` - An error if the API request could not be sent. #[node_bindgen] fn abort(&self, fields: Vec) -> Result<(), stypes::ComputationError> { let session = self @@ -150,6 +295,21 @@ impl Components { Ok(()) } + /// Initiates the shutdown process for the components session. + /// + /// This method sends a shutdown command to gracefully terminate the session. + /// It uses the `Api::Shutdown` command to initiate the shutdown. + /// + /// # Returns + /// + /// * `Ok(())` - If the shutdown request was successfully sent and acknowledged. + /// * `Err(stypes::ComputationError)` - An error if the shutdown process fails. + /// + /// # Errors + /// + /// The method returns an error if: + /// - The API request could not be sent. + /// - The response from the API could not be retrieved. #[node_bindgen] async fn destroy(&self) -> Result<(), stypes::ComputationError> { self.session diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 1093f0dd31..19fef5a670 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -82,6 +82,10 @@ export class Components extends Subscriber { return this.native.getOutputRender(uuid); } + public getIdent(uuid: string): Promise { + return this.native.getIdent(uuid); + } + public validate( origin: SessionAction, target: string, diff --git a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts index ba3d61e458..950a669bb9 100644 --- a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts +++ b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts @@ -74,6 +74,7 @@ export enum Source { GetIndexedLen = 'GetIndexedLen', getAroundIndexes = 'getAroundIndexes', ComponentsOptions = 'ComponentsOptions', + GetIdent = 'GetIdent', ComponentsValidate = 'ComponentsValidate', IdentList = 'IdentList', Native = 'Native', diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 81b86c2e8e..c59c7ae068 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -10,6 +10,7 @@ import { ComponentOptions, FieldsValidationErrors, OutputRender, + Ident, } from 'platform/types/bindings'; import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; @@ -34,6 +35,8 @@ export abstract class ComponentsNative { public abstract getOutputRender(uuid: String): Promise; + public abstract getIdent(uuid: String): Promise; + public abstract validate(origin: Uint8Array, fields: Uint8Array): Promise; public abstract abort(fields: string[]): void; @@ -232,6 +235,33 @@ export class Base extends Subscriber { }); } + public getIdent(uuid: String): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native.getIdent(uuid).then((buf: Uint8Array | null) => { + if (!buf) { + return resolve(null); + } + try { + resolve(protocol.decodeIdent(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.GetIdent, + ), + ); + } + }); + }); + } + public validate( origin: SessionAction, target: string, diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index 7372e62bea..fdc27c6ea3 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -15,10 +15,33 @@ import { Field, FieldDesc, Ident, OutputRender, SessionAction } from '@platform/ @SetupService(services['components']) export class Service extends Implementation { + /** + * A collection of subjects used for broadcasting events related to component settings loading and validation. + * These subjects are used to notify the client about the progress and results of loading static and lazy settings. + */ public subjects: Subjects<{ + /** + * Emitted when lazy settings have finished loading. + * Contains the fully resolved settings as if they were static. + */ LoadingDone: Subject; + + /** + * Emitted when an error occurs while retrieving the component's configuration schema. + * Contains identifiers of the fields for which schema retrieval failed. + */ LoadingErrors: Subject; + + /** + * Emitted in response to a validation request. + * Contains field-level validation errors for the submitted configuration data. + */ LoadingError: Subject; + + /** + * Emitted when lazy loading of settings has been cancelled. + * Typically triggered when the user aborts the session before completion. + */ LoadingCancelled: Subject; }> = new Subjects({ LoadingDone: new Subject(), @@ -62,6 +85,17 @@ export class Service extends Implementation { return Promise.resolve(); } + /** + * Aborts the lazy loading process for the specified configuration fields. + * + * The client receives the identifiers of fields marked for lazy loading + * along with the `getOptions` call. This method can be used to cancel + * loading for any of those fields, for example, if the user closes the session + * or navigates away before the data is fully loaded. + * + * @param fields - An array of field identifiers for which lazy loading should be cancelled. + * @returns A promise that resolves when the cancellation has been processed. + */ public abort(fields: string[]): Promise { return new Promise((resolve, reject) => { Requests.IpcRequest.send( @@ -77,6 +111,19 @@ export class Service extends Implementation { }); } + /** + * Retrieves the configuration schema (field descriptions) for the specified target components, + * such as a parser and a data source. + * + * Note that the returned schema may vary depending on the `SessionAction` context. + * For example, the DLT parser will include a lazy setting for "DLT Statistics" when the session + * is based on a DLT file. However, the same parser will not include this setting if the session + * refers to a stream instead of a file. + * + * @param origin - The session context (`SessionAction`) indicating the data source type (e.g., file or stream). + * @param targets - An array of component identifiers (usually parser and source) whose configuration schemas should be retrieved. + * @returns A promise that resolves to a map of component IDs to arrays of `FieldDesc` describing the configuration fields. + */ public getOptions(origin: SessionAction, targets: string[]): Promise> { return new Promise((resolve, reject) => { Requests.IpcRequest.send( @@ -93,6 +140,16 @@ export class Service extends Implementation { }); } + /** + * Retrieves the output render definition for displaying parsed data. + * + * The provided `uuid` is expected to refer to a parser, since the parser + * is responsible for defining how the data should be visually represented. + * + * @param uuid - The unique identifier of the parser component. + * @returns A promise that resolves to the `OutputRender` definition used for rendering output, + * or `null`/`undefined` if no render is available. + */ public getOutputRender(uuid: string): Promise { return new Promise((resolve, reject) => { Requests.IpcRequest.send( @@ -108,6 +165,19 @@ export class Service extends Implementation { }); } + /** + * Validates the provided configuration fields for the specified component. + * + * Validation is context-aware and may depend on the `SessionAction` (e.g., file vs. stream). + * The result is a map where each key corresponds to a field identifier, and the value + * is a human-readable error message describing the validation issue. + * + * @param origin - The session context (`SessionAction`) indicating the source type (e.g., file, stream). + * @param target - The identifier of the component whose settings are being validated. + * @param fields - An array of field values to be validated. + * @returns A promise that resolves to a map of field IDs to validation error messages. + * If a field is valid, it will not appear in the result. + */ public validate( origin: SessionAction, target: string, @@ -128,8 +198,29 @@ export class Service extends Implementation { .catch(reject); }); } + + /** + * Provides access to the list of available components for the given session context. + * + * This includes both sources and parsers. The list may vary depending on the `SessionAction`, + * which defines the context (e.g., file-based vs. stream-based sessions). + * + * @param origin - The session context (`SessionAction`) determining which components are applicable. + * @returns An object with methods to retrieve available component identifiers. + */ public get(origin: SessionAction): { + /** + * Retrieves a list of available source components. + * + * @returns A promise that resolves to an array of `Ident` objects representing source components. + */ sources(): Promise; + + /** + * Retrieves a list of available parser components. + * + * @returns A promise that resolves to an array of `Ident` objects representing parser components. + */ parsers(): Promise; } { return { @@ -163,6 +254,32 @@ export class Service extends Implementation { }, }; } + + /** + * Retrieves the identifier metadata for the specified component. + * + * The provided `uuid` should correspond to a registered component (e.g., parser or source). + * If the component exists, its `Ident` information will be returned; otherwise, the result + * will be `undefined`. + * + * @param uuid - The unique identifier of the component. + * @returns A promise that resolves to the `Ident` object describing the component, + * or `undefined` if no matching component is found. + */ + public getIdent(uuid: string): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetIdent.Response, + new Requests.Components.GetIdent.Request({ + target: uuid, + }), + ) + .then((response) => { + resolve(response.ident); + }) + .catch(reject); + }); + } } export interface Service extends Interface {} export const components = register(new Service()); diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index a3777939ed..c980dd0563 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -43,6 +43,13 @@ export class Service extends Implementation { Requests.Components.GetOptions.Request, RequestHandlers.GetOptions.handler, ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetIdent.Request, + RequestHandlers.GetIdent.handler, + ), electron .ipc() .respondent( diff --git a/application/holder/src/service/components/get_ident.ts b/application/holder/src/service/components/get_ident.ts new file mode 100644 index 0000000000..b7cbe54aa4 --- /dev/null +++ b/application/holder/src/service/components/get_ident.ts @@ -0,0 +1,29 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { Ident } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetIdent.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetIdent.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .getIdent(request.target) + .then((ident: Ident | undefined | null) => { + resolve( + new Requests.Components.GetIdent.Response({ + ident: ident ? ident : undefined, + }), + ); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts index 262421c2d7..628a27ac52 100644 --- a/application/holder/src/service/components/index.ts +++ b/application/holder/src/service/components/index.ts @@ -2,5 +2,6 @@ export * as GetSources from './get_sources'; export * as GetParsers from './get_parsers'; export * as GetOptions from './get_options'; export * as GetOutputRender from './get_output_render'; +export * as GetIdent from './get_ident'; export * as Abort from './abort'; export * as Validate from './validate'; diff --git a/application/platform/ipc/request/components/get_ident.ts b/application/platform/ipc/request/components/get_ident.ts new file mode 100644 index 0000000000..ef7232ac4e --- /dev/null +++ b/application/platform/ipc/request/components/get_ident.ts @@ -0,0 +1,29 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { FieldDesc, Ident, SessionAction } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetIdentRequest' }) +export class Request extends SignatureRequirement { + public target: string; + + constructor(input: { target: string }) { + super(); + validator.isObject(input); + this.target = validator.getAsNotEmptyString(input, 'target'); + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetIdentResponse' }) +export class Response extends SignatureRequirement { + public ident: Ident | undefined; + + constructor(input: { ident: Ident | undefined }) { + super(); + validator.isObject(input); + this.ident = validator.getAsObjOrUndefined(input, 'ident'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/index.ts b/application/platform/ipc/request/components/index.ts index 262421c2d7..c00c9be07c 100644 --- a/application/platform/ipc/request/components/index.ts +++ b/application/platform/ipc/request/components/index.ts @@ -1,6 +1,7 @@ export * as GetSources from './get_sources'; export * as GetParsers from './get_parsers'; export * as GetOptions from './get_options'; +export * as GetIdent from './get_ident'; export * as GetOutputRender from './get_output_render'; export * as Abort from './abort'; export * as Validate from './validate'; From aa0fbe70847e5f27e818a2261fd33d6051dae656 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 25 Jun 2025 13:51:03 +0200 Subject: [PATCH 069/121] Extend SessionDescriptor with more data --- .../apps/indexer/components/src/lib.rs | 10 +++++---- .../apps/indexer/components/src/tys.rs | 5 +++-- .../indexer/parsers/src/dlt/descriptor.rs | 21 +++++++++++-------- .../indexer/parsers/src/dlt/raw/descriptor.rs | 12 +++++------ .../indexer/parsers/src/someip/descriptor.rs | 7 +++++-- .../indexer/parsers/src/text/descriptor.rs | 8 ++++--- .../src/binary/pcap/legacy/descriptor.rs | 4 ++-- .../sources/src/binary/pcap/ng/descriptor.rs | 4 ++-- .../sources/src/binary/raw/descriptor.rs | 7 ++++--- .../indexer/sources/src/command/descriptor.rs | 14 ++++++++----- .../apps/indexer/sources/src/command/mod.rs | 2 +- .../indexer/sources/src/serial/descriptor.rs | 16 ++++++++------ .../sources/src/socket/tcp/descriptor.rs | 7 +++++-- .../sources/src/socket/udp/descriptor.rs | 17 ++++++++------- .../apps/indexer/stypes/bindings/observe.ts | 2 +- .../indexer/stypes/src/observe/extending.rs | 13 +++++++++++- .../apps/indexer/stypes/src/observe/mod.rs | 2 ++ .../indexer/stypes/src/observe/proptest.rs | 9 ++++++-- 18 files changed, 102 insertions(+), 58 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 5e74d9ad32..c2bd685395 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -355,25 +355,27 @@ impl Components { message: Some(format!("Fail to find source {}", options.source.uuid)), }); }; - let desciptor = SessionDescriptor::new( + let mut descriptor = SessionDescriptor::new( source.bound_ident(&options.origin, &options.source.fields), source.bound_ident(&options.origin, &options.parser.fields), ); - let Some(parser) = parser.create(&options.origin, &options.parser.fields)? else { + let Some((parser, desc)) = parser.create(&options.origin, &options.parser.fields)? else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to init parser {}", options.parser.uuid)), }); }; - let Some(source) = source.create(&options.origin, &options.source.fields)? else { + descriptor.set_parser_desc(desc); + let Some((source, desc)) = source.create(&options.origin, &options.source.fields)? else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to init source {}", options.source.uuid)), }); }; - Ok((desciptor, source, parser)) + descriptor.set_parser_desc(desc); + Ok((descriptor, source, parser)) } } diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys.rs index 26f023b628..483a738546 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys.rs @@ -148,14 +148,15 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// * `options` - A slice of `stypes::Field` values representing component options or settings. /// /// # Returns - /// * `Ok(Some(T))` if the component was successfully created. + /// * `Ok(Some(T, Option))` if the component was successfully created, where `T` - an + /// instance of component; `Option` - self description for client (e.g. "TCP on 0.0.0.0:8888"). /// * `Ok(None)` if the factory declined to create a component for the given context. /// * `Err(stypes::NativeError)` if an error occurred during creation. fn create( &self, _origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { Ok(None) } } diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 70c7a56cf5..4475f32260 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -53,7 +53,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, options: &[Field], - ) -> Result, NativeError> { + ) -> Result)>, NativeError> { let errors = self.validate(origin, options)?; if !errors.is_empty() { return Err(NativeError { @@ -104,14 +104,17 @@ impl ComponentFactory for Descriptor { app_id_count: 0, context_id_count: 0, }; - Ok(Some(crate::Parser::Dlt(DltParser::new( - Some(filter_config), - dlt_metadata, - None, - someip_metadata, - // If it's source - no storage header expected - !matches!(origin, SessionAction::Source), - )))) + Ok(Some(( + crate::Parser::Dlt(DltParser::new( + Some(filter_config), + dlt_metadata, + None, + someip_metadata, + // If it's source - no storage header expected + !matches!(origin, SessionAction::Source), + )), + Some("DLT".to_owned()), + ))) } } diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 386867e732..c8ded05999 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -15,11 +15,11 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(Some(crate::Parser::DltRaw(DltRawParser::new(!matches!( - origin, - SessionAction::Source - ))))) + ) -> Result)>, stypes::NativeError> { + Ok(Some(( + crate::Parser::DltRaw(DltRawParser::new(!matches!(origin, SessionAction::Source))), + Some("DLT".to_string()), + ))) } } @@ -27,7 +27,7 @@ impl ComponentDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(..) | SessionAction::Files(..) | SessionAction::Source => { - return false + return false; } SessionAction::ExportRaw(files, ..) => files, }; diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 678a05a71b..6728ff2913 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -20,8 +20,11 @@ impl ComponentFactory for Descriptor { &self, _origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(Some(crate::Parser::SomeIp(SomeipParser::new()))) + ) -> Result)>, stypes::NativeError> { + Ok(Some(( + crate::Parser::SomeIp(SomeipParser::new()), + Some("SomeIp".to_owned()), + ))) } } diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 595e208cb8..3bd71dee9f 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -16,8 +16,8 @@ impl ComponentFactory for Descriptor { &self, _origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { - Ok(Some(crate::Parser::Text(StringTokenizer {}))) + ) -> Result)>, stypes::NativeError> { + Ok(Some((crate::Parser::Text(StringTokenizer {}), None))) } } @@ -42,7 +42,9 @@ impl ComponentDescriptor for Descriptor { fn ident(&self) -> stypes::Ident { stypes::Ident { name: String::from("Text Parser"), - desc: String::from("Text Parser is a minimal parser designed for processing plain text input. It performs no decoding or transformation and has no configuration options. Its sole purpose is to output incoming data line by line, making it suitable for logs or command outputs in textual format."), + desc: String::from( + "Text Parser is a minimal parser designed for processing plain text input. It performs no decoding or transformation and has no configuration options. Its sole purpose is to output incoming data line by line, making it suitable for logs or command outputs in textual format.", + ), io: stypes::IODataType::PlaitText, uuid: TEXT_PARSER_UUID, } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 665a7fa58d..a17f74d596 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -16,7 +16,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { let filepath = match origin { SessionAction::File(file) => file, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(.. ) => { @@ -27,7 +27,7 @@ impl ComponentFactory for Descriptor { }) } }; - Ok(Some(crate::Source::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?))) + Ok(Some((crate::Source::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?), Some("Pcap".to_owned())))) } } diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 905e4225eb..3456ee4e3f 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -16,7 +16,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { let filepath = match origin { SessionAction::File(file) => file, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { @@ -27,7 +27,7 @@ impl ComponentFactory for Descriptor { }) } }; - Ok(Some(crate::Source::PcapNg(PcapngByteSourceFromFile::new(filepath)?))) + Ok(Some((crate::Source::PcapNg(PcapngByteSourceFromFile::new(filepath)?), Some("PcapNg".to_owned())))) } } diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 9737d0720d..948a6f3c15 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -15,7 +15,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, _options: &[stypes::Field], - ) -> Result, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { let filename = match origin { SessionAction::File(filename) => filename, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { @@ -28,8 +28,9 @@ impl ComponentFactory for Descriptor { }); } }; - Ok(Some(crate::Source::Raw( - super::BinaryByteSourceFromFile::new(filename)?, + Ok(Some(( + crate::Source::Raw(super::BinaryByteSourceFromFile::new(filename)?), + None, ))) } } diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 1eefd4c26a..cd08dd77a2 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -22,7 +22,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, options: &[Field], - ) -> Result, NativeError> { + ) -> Result)>, NativeError> { let errors = self.validate(origin, options)?; if !errors.is_empty() { return Err(NativeError { @@ -37,11 +37,15 @@ impl ComponentFactory for Descriptor { ), }); } - let command: Extracted = options + let command: String = options .extract_by_key(FIELD_COMMAND) - .ok_or(missed(FIELD_COMMAND))?; - Ok(Some(crate::Source::Process( - ProcessSource::new(command.value, env::current_dir().unwrap(), HashMap::new()).unwrap(), + .ok_or(missed(FIELD_COMMAND))? + .value; + Ok(Some(( + crate::Source::Process( + ProcessSource::new(&command, env::current_dir().unwrap(), HashMap::new()).unwrap(), + ), + Some(command), ))) } } diff --git a/application/apps/indexer/sources/src/command/mod.rs b/application/apps/indexer/sources/src/command/mod.rs index f68a9a960c..951795e5da 100644 --- a/application/apps/indexer/sources/src/command/mod.rs +++ b/application/apps/indexer/sources/src/command/mod.rs @@ -117,7 +117,7 @@ impl ProcessSource { } pub fn new( - command: String, + command: &str, cwd: PathBuf, envs: HashMap, ) -> Result { diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 4652261e80..8675a36ed9 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -40,7 +40,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, options: &[Field], - ) -> Result, NativeError> { + ) -> Result)>, NativeError> { let errors = self.validate(origin, options)?; if !errors.is_empty() { return Err(NativeError { @@ -55,11 +55,12 @@ impl ComponentFactory for Descriptor { ), }); } + let path: String = options + .extract_by_key(FIELD_PATH) + .ok_or(missed(FIELD_PATH))? + .value; let config = SerialConfig { - path: options - .extract_by_key(FIELD_PATH) - .ok_or(missed(FIELD_PATH))? - .value, + path: path.clone(), baud_rate: options .extract_by_key(FIELD_BAUD_RATE) .ok_or(missed(FIELD_BAUD_RATE))? @@ -89,7 +90,10 @@ impl ComponentFactory for Descriptor { .ok_or(missed(FIELD_EXCLUSIVE))? .value, }; - Ok(Some(crate::Source::Serial(SerialSource::new(config)?))) + Ok(Some(( + crate::Source::Serial(SerialSource::new(config)?), + Some(path), + ))) } } diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index 2f2f2f4d15..c0883fa8eb 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -23,7 +23,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, options: &[Field], - ) -> Result, NativeError> { + ) -> Result)>, NativeError> { let errors = self.validate(origin, options)?; if !errors.is_empty() { return Err(NativeError { @@ -42,7 +42,10 @@ impl ComponentFactory for Descriptor { .extract_by_key(FIELD_IP_ADDR) .ok_or(missed(FIELD_IP_ADDR))? .value; - Ok(Some(crate::Source::Tcp(TcpSource::new(&addr, None, None)?))) + Ok(Some(( + crate::Source::Tcp(TcpSource::new(&addr, None, None)?), + Some(format!("TCP on {}", addr)), + ))) } } diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index a19c2a0902..269e9b1f32 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -22,7 +22,7 @@ impl ComponentFactory for Descriptor { &self, origin: &SessionAction, options: &[Field], - ) -> Result, NativeError> { + ) -> Result)>, NativeError> { let errors = self.validate(origin, options)?; if !errors.is_empty() { return Err(NativeError { @@ -72,12 +72,15 @@ impl ComponentFactory for Descriptor { }, }) } - Ok(Some(crate::Source::Udp( - UdpSource::new(addr, multicasts).map_err(|err| NativeError { - kind: NativeErrorKind::Io, - severity: Severity::ERROR, - message: Some(err.to_string()), - })?, + Ok(Some(( + crate::Source::Udp( + UdpSource::new(&addr, multicasts).map_err(|err| NativeError { + kind: NativeErrorKind::Io, + severity: Severity::ERROR, + message: Some(err.to_string()), + })?, + ), + Some(format!("UDP on {addr}")), ))) } } diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 08e9d3eb76..b3eaf72c32 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -134,7 +134,7 @@ exclusive: boolean, }; */ export type SessionAction = { "File": string } | { "Files": Array } | { "ExportRaw": [Array, Array<{ start: bigint, end: bigint, }>, string] } | "Source"; -export type SessionDescriptor = { parser: Ident, source: Ident, }; +export type SessionDescriptor = { parser: Ident, source: Ident, p_desc: string | null, s_desc: string | null, }; export type SessionSetup = { origin: SessionAction, parser: ComponentOptions, source: ComponentOptions, }; diff --git a/application/apps/indexer/stypes/src/observe/extending.rs b/application/apps/indexer/stypes/src/observe/extending.rs index 804bab8eae..fe5e4851fd 100644 --- a/application/apps/indexer/stypes/src/observe/extending.rs +++ b/application/apps/indexer/stypes/src/observe/extending.rs @@ -14,7 +14,18 @@ impl SessionSetup { } impl SessionDescriptor { pub fn new(source: Ident, parser: Ident) -> Self { - Self { parser, source } + Self { + parser, + source, + p_desc: None, + s_desc: None, + } + } + pub fn set_parser_desc(&mut self, desc: Option) { + self.p_desc = desc; + } + pub fn set_source_desc(&mut self, desc: Option) { + self.s_desc = desc; } } impl ObserveOptions { diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 1f661fac52..9fd827e624 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -65,6 +65,8 @@ pub struct SessionSetup { pub struct SessionDescriptor { pub parser: Ident, pub source: Ident, + pub p_desc: Option, + pub s_desc: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] diff --git a/application/apps/indexer/stypes/src/observe/proptest.rs b/application/apps/indexer/stypes/src/observe/proptest.rs index 9d81261863..d6e0d10e92 100644 --- a/application/apps/indexer/stypes/src/observe/proptest.rs +++ b/application/apps/indexer/stypes/src/observe/proptest.rs @@ -10,8 +10,13 @@ impl Arbitrary for SessionDescriptor { type Strategy = BoxedStrategy; fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - (any::(), any::()) - .prop_map(|(parser, source)| SessionDescriptor { parser, source }) + (any::(), any::(), any::()) + .prop_map(|(parser, source, desc)| SessionDescriptor { + parser, + source, + p_desc: Some(desc.clone()), + s_desc: Some(desc), + }) .boxed() } } From cac281766e5a37f4d64ddcb4b573711b5125fddb Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 25 Jun 2025 13:51:34 +0200 Subject: [PATCH 070/121] Renaming --- .../src/app/service/actions/file.any.ts | 8 +++--- .../src/app/service/actions/file.dlt.ts | 4 +-- .../app/service/actions/file.parserPlugin.ts | 4 +-- .../src/app/service/actions/file.pcap.ts | 4 +-- .../src/app/service/actions/file.pcapng.ts | 4 +-- .../src/app/service/actions/file.text.ts | 4 +-- .../src/app/service/actions/folder.any.ts | 8 +++--- .../src/app/service/actions/folder.dlt.ts | 4 +-- .../service/actions/folder.parserPlugin.ts | 4 +-- .../src/app/service/actions/folder.pcap.ts | 4 +-- .../src/app/service/actions/folder.pcapng.ts | 4 +-- .../src/app/service/actions/folder.plugins.ts | 4 +-- .../src/app/service/actions/folder.text.ts | 4 +-- .../service/actions/serial.parserPlugin.ts | 4 +-- .../src/app/service/actions/serial.text.ts | 4 +-- .../src/app/service/actions/stdout.plugin.ts | 4 +-- .../src/app/service/actions/stdout.text.ts | 4 +-- .../client/src/app/service/actions/tcp.dlt.ts | 4 +-- .../app/service/actions/tcp.parserPlugin.ts | 4 +-- .../client/src/app/service/actions/udp.dlt.ts | 4 +-- .../app/service/actions/udp.parserPlugin.ts | 4 +-- application/client/src/app/service/session.ts | 26 +++++++------------ .../observing/implementations/processes.ts | 4 +-- .../observing/implementations/serial.ts | 4 +-- .../observing/implementations/tcp.ts | 4 +-- .../observing/implementations/udp.ts | 4 +-- .../client/src/app/service/session/origin.ts | 14 +++++----- .../elements/menu.attachsource/component.ts | 10 +++---- .../navigator/providers/provider.files.ts | 16 ++++++------ .../src/app/ui/elements/tree/component.ts | 12 ++++----- .../src/app/ui/tabs/multiplefiles/state.ts | 22 ++++++++-------- .../client/src/app/ui/tabs/setup/component.ts | 4 +-- .../src/app/ui/tabs/setup/origin/component.ts | 4 +-- .../client/src/app/ui/tabs/setup/provider.ts | 4 +-- .../client/src/app/ui/tabs/setup/state.ts | 6 ++--- .../platform/types/bindings/observe.ts | 7 ++++- 36 files changed, 116 insertions(+), 117 deletions(-) diff --git a/application/client/src/app/service/actions/file.any.ts b/application/client/src/app/service/actions/file.any.ts index ddcd06c9b8..f42ad8d00e 100644 --- a/application/client/src/app/service/actions/file.any.ts +++ b/application/client/src/app/service/actions/file.any.ts @@ -6,7 +6,7 @@ import { File } from '@platform/types/files'; import { FileType } from '@platform/types/observe/types/file'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_file'; @@ -40,13 +40,13 @@ export class Action extends Base { case FileType.Binary: case FileType.PcapNG: case FileType.PcapLegacy: - session.initialize().configure(SessionSourceOrigin.file(file.filename)); + session.initialize().configure(SessionOrigin.file(file.filename)); break; case FileType.Text: - session.initialize().observe(SessionSourceOrigin.file(file.filename)); + session.initialize().observe(SessionOrigin.file(file.filename)); break; case FileType.ParserPlugin: - session.initialize().observe(SessionSourceOrigin.file(file.filename)); + session.initialize().observe(SessionOrigin.file(file.filename)); break; } } diff --git a/application/client/src/app/service/actions/file.dlt.ts b/application/client/src/app/service/actions/file.dlt.ts index 472d27b87f..ed2fb6fe79 100644 --- a/application/client/src/app/service/actions/file.dlt.ts +++ b/application/client/src/app/service/actions/file.dlt.ts @@ -4,7 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_file'; @@ -36,7 +36,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.parserPlugin.ts b/application/client/src/app/service/actions/file.parserPlugin.ts index 2791df8f5e..656e613202 100644 --- a/application/client/src/app/service/actions/file.parserPlugin.ts +++ b/application/client/src/app/service/actions/file.parserPlugin.ts @@ -4,7 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_file_parser_plugins'; @@ -37,7 +37,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.pcap.ts b/application/client/src/app/service/actions/file.pcap.ts index 5b1df2f3f8..12a17a1fce 100644 --- a/application/client/src/app/service/actions/file.pcap.ts +++ b/application/client/src/app/service/actions/file.pcap.ts @@ -4,7 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_file'; @@ -36,7 +36,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.pcapng.ts b/application/client/src/app/service/actions/file.pcapng.ts index 2e02890132..d8b67cb164 100644 --- a/application/client/src/app/service/actions/file.pcapng.ts +++ b/application/client/src/app/service/actions/file.pcapng.ts @@ -4,7 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_file'; @@ -36,7 +36,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.text.ts b/application/client/src/app/service/actions/file.text.ts index 53031fa453..45dd6f98d1 100644 --- a/application/client/src/app/service/actions/file.text.ts +++ b/application/client/src/app/service/actions/file.text.ts @@ -4,7 +4,7 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_file'; @@ -36,7 +36,7 @@ export class Action extends Base { }, }); } else { - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.any.ts b/application/client/src/app/service/actions/folder.any.ts index 4a77b972ff..533f526e33 100644 --- a/application/client/src/app/service/actions/folder.any.ts +++ b/application/client/src/app/service/actions/folder.any.ts @@ -6,7 +6,7 @@ import { FileType } from '@platform/types/observe/types/file'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_folder'; @@ -48,13 +48,13 @@ export class Action extends Base { case FileType.Binary: case FileType.PcapNG: case FileType.PcapLegacy: - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); break; case FileType.Text: - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); break; case FileType.ParserPlugin: - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); break; } } diff --git a/application/client/src/app/service/actions/folder.dlt.ts b/application/client/src/app/service/actions/folder.dlt.ts index 54ccdef4ba..feacfb1f6c 100644 --- a/application/client/src/app/service/actions/folder.dlt.ts +++ b/application/client/src/app/service/actions/folder.dlt.ts @@ -5,7 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_folder'; @@ -44,7 +44,7 @@ export class Action extends Base { }); return Promise.resolve(); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.parserPlugin.ts b/application/client/src/app/service/actions/folder.parserPlugin.ts index d26a95f729..738e6aa7fd 100644 --- a/application/client/src/app/service/actions/folder.parserPlugin.ts +++ b/application/client/src/app/service/actions/folder.parserPlugin.ts @@ -5,7 +5,7 @@ import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; @@ -45,7 +45,7 @@ export class Action extends Base { }, }); } else { - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); diff --git a/application/client/src/app/service/actions/folder.pcap.ts b/application/client/src/app/service/actions/folder.pcap.ts index e53361a8fd..a346019ce4 100644 --- a/application/client/src/app/service/actions/folder.pcap.ts +++ b/application/client/src/app/service/actions/folder.pcap.ts @@ -5,7 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_folder'; @@ -43,7 +43,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.pcapng.ts b/application/client/src/app/service/actions/folder.pcapng.ts index 528ceb6826..a727470d84 100644 --- a/application/client/src/app/service/actions/folder.pcapng.ts +++ b/application/client/src/app/service/actions/folder.pcapng.ts @@ -5,7 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_folder'; @@ -43,7 +43,7 @@ export class Action extends Base { }, }); } else { - session.initialize().configure(SessionSourceOrigin.file(files[0].filename)); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/folder.plugins.ts b/application/client/src/app/service/actions/folder.plugins.ts index d26a95f729..738e6aa7fd 100644 --- a/application/client/src/app/service/actions/folder.plugins.ts +++ b/application/client/src/app/service/actions/folder.plugins.ts @@ -5,7 +5,7 @@ import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; @@ -45,7 +45,7 @@ export class Action extends Base { }, }); } else { - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); diff --git a/application/client/src/app/service/actions/folder.text.ts b/application/client/src/app/service/actions/folder.text.ts index e8359bbdba..f52952cef2 100644 --- a/application/client/src/app/service/actions/folder.text.ts +++ b/application/client/src/app/service/actions/folder.text.ts @@ -5,7 +5,7 @@ import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_folder'; @@ -43,7 +43,7 @@ export class Action extends Base { }, }); } else { - session.initialize().observe(SessionSourceOrigin.file(files[0].filename)); + session.initialize().observe(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/serial.parserPlugin.ts b/application/client/src/app/service/actions/serial.parserPlugin.ts index 0d18919bff..14f0a78526 100644 --- a/application/client/src/app/service/actions/serial.parserPlugin.ts +++ b/application/client/src/app/service/actions/serial.parserPlugin.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_serial'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/serial.text.ts b/application/client/src/app/service/actions/serial.text.ts index a91e173b21..c22903a5f0 100644 --- a/application/client/src/app/service/actions/serial.text.ts +++ b/application/client/src/app/service/actions/serial.text.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_serial'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/stdout.plugin.ts b/application/client/src/app/service/actions/stdout.plugin.ts index bf45b98892..4577877f96 100644 --- a/application/client/src/app/service/actions/stdout.plugin.ts +++ b/application/client/src/app/service/actions/stdout.plugin.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_plugin'; @@ -18,7 +18,7 @@ export class Action extends Base { return 'Execute command with plugins'; } public override apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/stdout.text.ts b/application/client/src/app/service/actions/stdout.text.ts index 55dea4bfdc..ae968ea9b8 100644 --- a/application/client/src/app/service/actions/stdout.text.ts +++ b/application/client/src/app/service/actions/stdout.text.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_stdout'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/tcp.dlt.ts b/application/client/src/app/service/actions/tcp.dlt.ts index 429efdd594..df15f878de 100644 --- a/application/client/src/app/service/actions/tcp.dlt.ts +++ b/application/client/src/app/service/actions/tcp.dlt.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_tcp'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/tcp.parserPlugin.ts b/application/client/src/app/service/actions/tcp.parserPlugin.ts index df9b9a4f9c..5bb6aaa392 100644 --- a/application/client/src/app/service/actions/tcp.parserPlugin.ts +++ b/application/client/src/app/service/actions/tcp.parserPlugin.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_tcp'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/udp.dlt.ts b/application/client/src/app/service/actions/udp.dlt.ts index f659f1d38c..d5eef7660b 100644 --- a/application/client/src/app/service/actions/udp.dlt.ts +++ b/application/client/src/app/service/actions/udp.dlt.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_udp'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/actions/udp.parserPlugin.ts b/application/client/src/app/service/actions/udp.parserPlugin.ts index 381608020e..ebc38d326d 100644 --- a/application/client/src/app/service/actions/udp.parserPlugin.ts +++ b/application/client/src/app/service/actions/udp.parserPlugin.ts @@ -2,7 +2,7 @@ import { Base } from './action'; import { session } from '@service/session'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_udp'; @@ -19,7 +19,7 @@ export class Action extends Base { } public async apply(): Promise { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); return Promise.resolve(); } } diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 1ab0fa725d..dd4346de8e 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -22,7 +22,7 @@ import { getRender, getDltRender } from '@schema/render/tools'; import { SetupObserve } from '@tabs/setup/component'; import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; -import { SessionSourceOrigin } from './session/origin'; +import { SessionOrigin } from './session/origin'; import * as Factory from '@platform/types/observe/factory'; @@ -226,9 +226,9 @@ export class Service extends Implementation { public initialize(): { suggest(filename: string, session?: Session): Promise; - auto(origin: SessionSourceOrigin, session?: Session): Promise; - configure(origin: SessionSourceOrigin, session?: Session): Promise; - observe(origin: SessionSourceOrigin, session?: Session): Promise; + auto(origin: SessionOrigin, session?: Session): Promise; + configure(origin: SessionOrigin, session?: Session): Promise; + observe(origin: SessionOrigin, session?: Session): Promise; multiple(files: File[]): Promise; } { return { @@ -238,23 +238,17 @@ export class Service extends Implementation { .isBinary(filename) .then((_binary: boolean) => { // TODO: considering binary or not should happen on rustcore now - return this.initialize().observe( - SessionSourceOrigin.file(filename), - session, - ); + return this.initialize().observe(SessionOrigin.file(filename), session); }); }, - auto: (origin: SessionSourceOrigin, session?: Session): Promise => { + auto: (origin: SessionOrigin, session?: Session): Promise => { console.error(`Not implemented`); return Promise.reject(new Error(`Not implemented`)); // return observe.locker().is() // ? this.initialize().observe(observe, session) // : this.initialize().configure(observe, session); }, - configure: ( - origin: SessionSourceOrigin, - session?: Session, - ): Promise => { + configure: (origin: SessionOrigin, session?: Session): Promise => { return new Promise((resolve) => { const api = this.add().tab({ name: origin.getTitle(), @@ -263,7 +257,7 @@ export class Service extends Implementation { inputs: { origin, api: { - finish: (origin: SessionSourceOrigin): Promise => { + finish: (origin: SessionOrigin): Promise => { return new Promise((success, failed) => { this.initialize() .observe(origin, session) @@ -291,7 +285,7 @@ export class Service extends Implementation { }); }); }, - observe: async (origin: SessionSourceOrigin, existed?: Session): Promise => { + observe: async (origin: SessionOrigin, existed?: Session): Promise => { const render = await origin.getRender(); const session = @@ -384,7 +378,7 @@ export class Service extends Implementation { setTitle: (title: string) => { api?.setTitle(title); }, - done: (origin: SessionSourceOrigin) => { + done: (origin: SessionOrigin) => { console.error(`Not implemented`); // .observe(observe) // .then((session) => { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts b/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts index b923c79027..fccba46d41 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts @@ -7,7 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -47,7 +47,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); } public sources(): ObserveSource[] { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts b/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts index 43c97148ee..dc967b88af 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts @@ -7,7 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -40,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts index 4937a27049..60fd5c7e40 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts @@ -7,7 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -40,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts index a97de8d960..9de239dfe2 100644 --- a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts +++ b/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts @@ -7,7 +7,7 @@ import { session } from '@service/session'; import * as $ from '@platform/types/observe'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export class Provider extends Base { private _sources: ObserveSource[] = []; @@ -40,7 +40,7 @@ export class Provider extends Base { } public openNewSessionOptions() { - session.initialize().configure(SessionSourceOrigin.source()); + session.initialize().configure(SessionOrigin.source()); } public update(sources: ObserveSource[]): Provider { diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 556dbba593..092f3f9bae 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -28,15 +28,15 @@ export class ComponentsOptions { } } -export class SessionSourceOrigin { - static file(path: string): SessionSourceOrigin { - return new SessionSourceOrigin({ File: path }, undefined); +export class SessionOrigin { + static file(path: string): SessionOrigin { + return new SessionOrigin({ File: path }, undefined); } - static files(paths: string[]): SessionSourceOrigin { - return new SessionSourceOrigin({ Files: paths }, undefined); + static files(paths: string[]): SessionOrigin { + return new SessionOrigin({ Files: paths }, undefined); } - static source(): SessionSourceOrigin { - return new SessionSourceOrigin('Source', undefined); + static source(): SessionOrigin { + return new SessionOrigin('Source', undefined); } public readonly options: ComponentsOptions; diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index 70603fcff7..247f3f90f6 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -11,7 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { ChangesDetector } from '@ui/env/extentions/changes'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-attach-new-source-menu', @@ -40,7 +40,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.source(), this.session); + .configure(SessionOrigin.source(), this.session); }, }, { @@ -49,7 +49,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.source(), this.session); + .configure(SessionOrigin.source(), this.session); }, }, { @@ -58,7 +58,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.source(), this.session); + .configure(SessionOrigin.source(), this.session); }, }, null, @@ -68,7 +68,7 @@ export class AttachSourceMenu extends ChangesDetector { handler: () => { this.ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.source(), this.session); + .configure(SessionOrigin.source(), this.session); }, }, ]; diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts index 23311cdc6a..22ff3e2eab 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts @@ -8,7 +8,7 @@ import { FolderEntityType } from '@platform/types/bindings'; import * as Factory from '@platform/types/observe/factory'; import { IMenuItem } from '@ui/service/contextmenu'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; const DEFAULT_DEEP = 5; const DEFAULT_LEN = 20000; @@ -84,7 +84,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(item.filename)) + .configure(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -93,7 +93,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(item.filename)) + .configure(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -102,7 +102,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(item.filename)) + .configure(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -111,7 +111,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(item.filename)) + .observe(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -120,7 +120,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(item.filename)) + .observe(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() @@ -141,7 +141,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(item.filename)) + .observe(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() @@ -151,7 +151,7 @@ export class Provider extends Base { this.ilc .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(item.filename)) + .configure(SessionOrigin.file(item.filename)) .catch((err: Error) => { this.ilc .log() diff --git a/application/client/src/app/ui/elements/tree/component.ts b/application/client/src/app/ui/elements/tree/component.ts index 95610a03fe..0478456fb1 100644 --- a/application/client/src/app/ui/elements/tree/component.ts +++ b/application/client/src/app/ui/elements/tree/component.ts @@ -16,7 +16,7 @@ import { stop } from '@ui/env/dom'; import * as Scheme from './scheme'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-elements-tree', @@ -111,7 +111,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(entity.getPath())) + .observe(SessionOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open text file; error: ${err.message}`); }); @@ -122,7 +122,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(entity.getPath())) + .observe(SessionOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open dlt file; error: ${err.message}`); }); @@ -133,7 +133,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(entity.getPath())) + .observe(SessionOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open pcapng file; error: ${err.message}`); }); @@ -144,7 +144,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(entity.getPath())) + .observe(SessionOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error(`Fail to open pcapng file; error: ${err.message}`); }); @@ -155,7 +155,7 @@ export class ElementsTreeSelector handler: () => { this.ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(entity.getPath())) + .observe(SessionOrigin.file(entity.getPath())) .catch((err: Error) => { this.log().error( `Fail to open file with plugins: errord ${err.message}`, diff --git a/application/client/src/app/ui/tabs/multiplefiles/state.ts b/application/client/src/app/ui/tabs/multiplefiles/state.ts index 646b2ad0fd..c8f6413dd8 100644 --- a/application/client/src/app/ui/tabs/multiplefiles/state.ts +++ b/application/client/src/app/ui/tabs/multiplefiles/state.ts @@ -13,7 +13,7 @@ import { Sort } from '@angular/material/sort'; import { ChangesDetector } from '@ui/env/extentions/changes'; import * as Factory from '@platform/types/observe/factory'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export interface IMultifile { usedColors: string[]; @@ -148,27 +148,27 @@ export class State extends Holder { return this._ref .ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.files(files)); + .observe(SessionOrigin.files(files)); case FileType.PcapNG: return this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.files(files)); + .configure(SessionOrigin.files(files)); case FileType.PcapLegacy: return this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.files(files)); + .configure(SessionOrigin.files(files)); case FileType.Binary: return this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.files(files)); + .configure(SessionOrigin.files(files)); case FileType.ParserPlugin: return this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.files(files)); + .configure(SessionOrigin.files(files)); default: return Promise.reject( new Error(`Unsupported type ${this.files[0].type}`), @@ -201,7 +201,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .observe(SessionSourceOrigin.file(file.filename)) + .observe(SessionOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -214,7 +214,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(file.filename)) + .configure(SessionOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -227,7 +227,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(file.filename)) + .configure(SessionOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -240,7 +240,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(file.filename)) + .configure(SessionOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() @@ -253,7 +253,7 @@ export class State extends Holder { this._ref .ilc() .services.system.session.initialize() - .configure(SessionSourceOrigin.file(file.filename)) + .configure(SessionOrigin.file(file.filename)) .catch((err: Error) => { this._ref .ilc() diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index cb05191e51..e257d3ff0f 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -11,7 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, IApi } from './state'; -import { SessionComponents, SessionSourceOrigin } from '@service/session/origin'; +import { SessionComponents, SessionOrigin } from '@service/session/origin'; import { session } from '@service/session'; @Component({ @@ -28,7 +28,7 @@ export class SetupObserve implements AfterViewInit, AfterContentInit, OnDestroy { @Input() api!: IApi; - @Input() origin!: SessionSourceOrigin; + @Input() origin!: SessionOrigin; public state!: State; diff --git a/application/client/src/app/ui/tabs/setup/origin/component.ts b/application/client/src/app/ui/tabs/setup/origin/component.ts index faba0ff184..583f450cfd 100644 --- a/application/client/src/app/ui/tabs/setup/origin/component.ts +++ b/application/client/src/app/ui/tabs/setup/origin/component.ts @@ -10,7 +10,7 @@ import { import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-tabs-setup-origin', @@ -22,7 +22,7 @@ import { SessionSourceOrigin } from '@service/session/origin'; @Initial() @Ilc() export class SourceOriginComponent implements AfterContentInit, OnDestroy { - @Input() origin!: SessionSourceOrigin; + @Input() origin!: SessionOrigin; public title: string = ''; public description: string | undefined; diff --git a/application/client/src/app/ui/tabs/setup/provider.ts b/application/client/src/app/ui/tabs/setup/provider.ts index 1be5556a23..ac16c2a108 100644 --- a/application/client/src/app/ui/tabs/setup/provider.ts +++ b/application/client/src/app/ui/tabs/setup/provider.ts @@ -10,7 +10,7 @@ import { LoadingErrorsEvent, } from '@platform/types/components'; import { WrappedField } from '@ui/elements/scheme/field'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export class Proivder extends SchemeProvider { protected pending: string[] = []; @@ -20,7 +20,7 @@ export class Proivder extends SchemeProvider { protected fields: FieldDesc[] = []; protected hasErrors: boolean = false; - constructor(protected readonly origin: SessionSourceOrigin, protected readonly target: string) { + constructor(protected readonly origin: SessionOrigin, protected readonly target: string) { super(target); this.register( components.subjects.get().LoadingDone.subscribe(this.onLoadingDone.bind(this)), diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index a91adcf205..2a28097080 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -4,7 +4,7 @@ import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; import { TabControls } from '@service/session'; import { Proivder } from './provider'; -import { SessionSourceOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; export interface IApi { finish(): Promise; @@ -58,14 +58,14 @@ export class State extends Subscriber { protected readonly logger = new Logger(`Setup`); - constructor(protected readonly origin: SessionSourceOrigin) { + constructor(protected readonly origin: SessionOrigin) { super(); } public destroy() { this.unsubscribe(); this.subjects.destroy(); } - public setSourceOrigin(origin: SessionSourceOrigin): Error | undefined { + public setSourceOrigin(origin: SessionOrigin): Error | undefined { if (!this.providers.parser || !this.providers.source) { return new Error(`Source or parser isn't setup`); } diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index f50e5b0e87..1842c6010f 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -150,7 +150,12 @@ export type SessionAction = | { ExportRaw: [Array, Array<{ start: bigint; end: bigint }>, string] } | 'Source'; -export type SessionDescriptor = { parser: Ident; source: Ident }; +export type SessionDescriptor = { + parser: Ident; + source: Ident; + p_desc: string | null; + s_desc: string | null; +}; export type SessionSetup = { origin: SessionAction; From c6cf5577f795bce843b0c1dabd5c25d1e25715a9 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 25 Jun 2025 14:48:07 +0200 Subject: [PATCH 071/121] Add SessionDescriptor event --- .../apps/indexer/components/src/lib.rs | 2 +- .../apps/indexer/session/src/state/mod.rs | 3 ++- .../sources/src/binary/raw/descriptor.rs | 3 ++- .../indexer/stypes/src/callback/formating.rs | 3 +++ .../apps/indexer/stypes/src/callback/mod.rs | 4 ++++ .../apps/indexer/stypes/src/observe/mod.rs | 5 ++++- .../ts-bindings/src/api/session.provider.ts | 20 ++++++++++++++++++- 7 files changed, 35 insertions(+), 5 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index c2bd685395..6f9e048bda 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -374,7 +374,7 @@ impl Components { message: Some(format!("Fail to init source {}", options.source.uuid)), }); }; - descriptor.set_parser_desc(desc); + descriptor.set_source_desc(desc); Ok((descriptor, source, parser)) } } diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index 779eaf0a49..51bce9bf5e 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -584,10 +584,11 @@ pub async fn run( } Api::AddSource(descriptor, tx_response) => { tx_response - .send(state.session_file.sources.add_source(descriptor)) + .send(state.session_file.sources.add_source(descriptor.clone())) .map_err(|_| { stypes::NativeError::channel("Failed to respond to Api::AddSource") })?; + tx_callback_events.send(stypes::CallbackEvent::SessionDescriptor(descriptor))?; } Api::GetSourcesDefinitions(tx_response) => { tx_response diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 948a6f3c15..523aa2b943 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -28,9 +28,10 @@ impl ComponentFactory for Descriptor { }); } }; + Ok(Some(( crate::Source::Raw(super::BinaryByteSourceFromFile::new(filename)?), - None, + Some(filename.to_string_lossy().to_string()), ))) } } diff --git a/application/apps/indexer/stypes/src/callback/formating.rs b/application/apps/indexer/stypes/src/callback/formating.rs index 617973972d..d1148f407c 100644 --- a/application/apps/indexer/stypes/src/callback/formating.rs +++ b/application/apps/indexer/stypes/src/callback/formating.rs @@ -37,6 +37,9 @@ impl std::fmt::Display for CallbackEvent { progress: _, } => write!(f, "Progress"), Self::SessionError(err) => write!(f, "SessionError: {err:?}"), + Self::SessionDescriptor(desc) => { + write!(f, "SessionDescriptor: {:?}/{:?}", desc.s_desc, desc.p_desc) + } Self::OperationError { uuid, error } => { write!(f, "OperationError: {uuid}: {error:?}") } diff --git a/application/apps/indexer/stypes/src/callback/mod.rs b/application/apps/indexer/stypes/src/callback/mod.rs index 849408ab09..82bfe6cc15 100644 --- a/application/apps/indexer/stypes/src/callback/mod.rs +++ b/application/apps/indexer/stypes/src/callback/mod.rs @@ -70,6 +70,10 @@ pub enum CallbackEvent { /// - `Option`: The list of matches with log entry indices. SearchMapUpdated(Option), + /// Triggers as soon as new observe operation has been started and SessionDescriptor has been + /// registred in the list of sources. + SessionDescriptor(SessionDescriptor), + /// Triggered when the "value map" is updated. The "value map" is used to build charts /// from search results. Always triggered immediately after `SearchUpdated`. /// - `Option>`: The value map. diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 9fd827e624..667fc91677 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -63,12 +63,15 @@ pub struct SessionSetup { ts(export, export_to = "observe.ts") )] pub struct SessionDescriptor { + /// Identifier of the parser being used. Provides a general description of the parser. pub parser: Ident, + /// Identifier of the source being used. Provides a general description of the source. pub source: Ident, + /// Parser description in the context of the current session. pub p_desc: Option, + /// Source description in the context of the current session. For example, instead of "UDP", it might be "UDP on 0.0.0.0::8888". pub s_desc: Option, } - #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts index e561b8cc98..2854842ef1 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts @@ -3,7 +3,7 @@ import { ISearchUpdated } from 'platform/types/filter'; import { Computation } from '../provider/provider'; import { EErrorKind, EErrorSeverity } from '../provider/provider.errors'; import { IMapEntity, IMatchEntity, FilterMatch } from 'platform/types/filter'; -import { AttachmentInfo } from 'platform/types/bindings'; +import { AttachmentInfo, SessionDescriptor } from 'platform/types/bindings'; import * as protocol from 'protocol'; @@ -64,6 +64,7 @@ export interface ISessionEvents { SessionError: Subject; OperationError: Subject; SessionDestroyed: Subject; + SessionDescriptor: Subject; OperationStarted: Subject; OperationProcessing: Subject; OperationDone: Subject; @@ -86,6 +87,7 @@ interface ISessionEventsSignatures { OperationError: 'OperationError'; SessionDestroyed: 'SessionDestroyed'; OperationStarted: 'OperationStarted'; + SessionDescriptor: 'SessionDescriptor'; OperationProcessing: 'OperationProcessing'; OperationDone: 'OperationDone'; } @@ -105,6 +107,7 @@ const SessionEventsSignatures: ISessionEventsSignatures = { OperationError: 'OperationError', SessionDestroyed: 'SessionDestroyed', OperationStarted: 'OperationStarted', + SessionDescriptor: 'SessionDescriptor', OperationProcessing: 'OperationProcessing', OperationDone: 'OperationDone', }; @@ -135,6 +138,13 @@ interface ISessionEventsInterfaces { }; SessionDestroyed: { self: null }; OperationStarted: { self: 'string' }; + SessionDescriptor: { + self: 'object'; + parser: 'object'; + source: 'object'; + s_desc: ['string', null, undefined]; + p_desc: ['string', null, undefined]; + }; OperationProcessing: { self: 'string' }; OperationDone: { self: 'object'; uuid: 'string'; result: 'any' }; } @@ -165,6 +175,13 @@ const SessionEventsInterfaces: ISessionEventsInterfaces = { }, SessionDestroyed: { self: null }, OperationStarted: { self: 'string' }, + SessionDescriptor: { + self: 'object', + parser: 'object', + source: 'object', + s_desc: ['string', null, undefined], + p_desc: ['string', null, undefined], + }, OperationProcessing: { self: 'string' }, OperationDone: { self: 'object', uuid: 'string', result: 'any' }, }; @@ -190,6 +207,7 @@ export class EventProvider extends Computation< SessionDestroyed: new Subject(), OperationStarted: new Subject(), OperationProcessing: new Subject(), + SessionDescriptor: new Subject(), OperationDone: new Subject(), }; From 029f8f742d43fef78d38a7feda05daa1f95cddf7 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Wed, 25 Jun 2025 16:51:24 +0200 Subject: [PATCH 072/121] Change Parser definitions & Plugins integrations * Provide new parser definitions taken from the current master solution but with dynamic dispatch. * Optimize writer performance + Remove async from write method + Refactoring by removing nested function * Improve error handling in producer loop. * Remove internal buffer from producer --- .../apps/indexer/definitions/src/parser.rs | 109 +++++------ .../apps/indexer/definitions/src/record.rs | 5 +- .../apps/indexer/parsers/src/dlt/mod.rs | 9 +- .../apps/indexer/parsers/src/dlt/raw/mod.rs | 13 +- application/apps/indexer/parsers/src/lib.rs | 18 +- .../apps/indexer/parsers/src/someip/mod.rs | 177 +++++++++--------- .../apps/indexer/parsers/src/text/mod.rs | 12 +- .../plugins_host/src/parser_shared/mod.rs | 19 +- .../src/v0_1_0/parser/bindings.rs | 48 ++--- .../plugins_host/src/v0_1_0/parser/mod.rs | 27 ++- .../indexer/processor/src/producer/mod.rs | 72 +++---- .../src/handlers/observe/export_raw.rs | 77 ++++---- .../session/src/handlers/observe/session.rs | 115 +++++------- 13 files changed, 325 insertions(+), 376 deletions(-) diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/definitions/src/parser.rs index b2106ba94f..62e8b8a00b 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/definitions/src/parser.rs @@ -2,6 +2,9 @@ use crate::*; use stypes::NativeError; use thiserror::Error; +pub type ParseReturnIterator<'a> = + Result>)> + 'a>, ParserError>; + #[derive(Error, Debug)] pub enum ParserError { #[error("Unrecoverable error, cannot continue: {0}")] @@ -38,82 +41,72 @@ pub trait Parser { /// If the parsers encounter any error while it already has parsed any items, then it must /// return those items without the error, then on the next call it can return the errors in /// case it was provided with the same slice of bytes. - /// Returns: - /// `usize` - consumed bytes - fn parse<'a>( - &mut self, - input: &'a [u8], - timestamp: Option, - ) -> Result<(usize, Option>), ParserError>; + fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a>; +} +/// A trait for parsers that extract one item at a time from a byte slice. +/// +/// Any type implementing this trait will automatically implement the [`Parser`] trait +/// due to a blanket implementation. This means that such types will support extracting +/// all available items from an input slice by repeatedly calling [`SingleParser::parse_item()`] +/// until no more items can be parsed. +/// +/// # Behavior +/// +/// - The blanket implementation of [`Parser`] will repeatedly invoke `parse_item()`, +/// extracting as many items as possible. +/// - If `parse_item()` fails on the first call, an error is returned immediately. +/// - If `parse_item()` succeeds, parsing continues until: +/// - The remaining input is too short to parse another item. +/// - `parse_item()` returns an error, which is ignored after the first successful parse. +pub trait SingleParser { /// The minimum number of bytes required to parse an item. /// /// # Notes: /// - This value is used to prevent unnecessary parsing attempts when the remaining input /// is too short to contain a valid message. /// - The default value (`1`) indicates that the parser has no minimum length requirement. - fn min_msg_len(&self) -> usize; -} + const MIN_MSG_LEN: usize = 1; -pub trait ParserIteratorGetter: Parser { - fn iter<'a>( - &'a mut self, + /// Parses a single item from the given byte slice. + /// + /// in case we could parse a message but the message was filtered out, `None` is returned on + /// that item. + fn parse_item<'a>( + &mut self, input: &'a [u8], timestamp: Option, - ) -> Result, ParserError> - where - Self: Sized, - { - if input.len() < self.min_msg_len() { - return Err(ParserError::Incomplete); - } - // return early if function errors on first parse call. - let (consumed, first) = self.parse(input, timestamp)?; - Ok(ParserIterator { - parser: self, - input, - consumed, - first: Some((consumed, first)), - timestamp, - }) - } + ) -> Result<(usize, Option>), ParserError>; } -impl ParserIteratorGetter for T {} +/// This blanket implementation repeatedly applies [`SingleParser::parse_item()`] function, +/// extracting as many items as possible from the provided input until no more can be parsed. +impl

Parser for P +where + P: SingleParser, +{ + fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { + let mut slice = input; -pub struct ParserIterator<'a, P: Parser> { - parser: &'a mut P, - input: &'a [u8], - consumed: usize, - first: Option<(usize, Option>)>, - timestamp: Option, -} + // return early if function errors on first parse call. + let first_res = self.parse_item(slice, timestamp)?; + + // Otherwise keep parsing and stop on first error, returning the parsed items at the end. + let iter = std::iter::successors(Some(first_res), move |(consumed, _res)| { + slice = &slice[*consumed..]; -impl<'a, P: Parser> Iterator for ParserIterator<'a, P> { - type Item = (usize, Option>); - - fn next(&mut self) -> Option { - if let Some(first) = self.first.take() { - return Some(first); - } - - if self.input[self.consumed..].len() < self.parser.min_msg_len() { - return None; - } - - match self - .parser - .parse(&self.input[self.consumed..], self.timestamp) - { - Ok((consumed, res)) => { - self.consumed += consumed; - Some((consumed, res)) + if slice.len() < P::MIN_MSG_LEN { + return None; } - Err(_) => None, - } + + self.parse_item(slice, timestamp).ok() + }); + + Ok(Box::new(iter)) } } +//TODO AAZ: This could be removed? pub trait Collector { fn register_message(&mut self, offset: usize, msg: &T); fn attachment_indexes(&self) -> Vec; diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 7e9654f515..6348ad79a6 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -79,6 +79,7 @@ impl ParseOperationResult { /// /// If different output is required (e.g., exporting DLT data as raw bytes), a different /// parser implementation (such as `DltRaw`) should be used. +#[derive(Debug)] pub enum LogRecordOutput<'a> { /// A raw binary message. Raw(&'a [u8]), @@ -94,7 +95,7 @@ pub enum LogRecordOutput<'a> { /// Structured columnar data. Typically used when the parser can extract /// meaningful fields and present them as an array of strings. - Columns(&'a [&'a str]), + Columns(Vec>), /// An attachment object, such as a binary blob or associated metadata. /// These are handled separately from textual data and require special treatment @@ -131,7 +132,7 @@ pub trait LogRecordWriter { /// # Returns /// * `Ok(())` on success. /// * `Err(NativeError)` if the write operation fails. - async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; /// Called once when the `MessageProducer` has no more data to provide. /// diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 17fa410f9a..08990481d9 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -109,8 +109,10 @@ fn map_dlt_err(err: DltParseError) -> ParserError { } } -impl Parser for DltParser { - fn parse<'a>( +impl SingleParser for DltParser { + const MIN_MSG_LEN: usize = MIN_MSG_LEN; + + fn parse_item<'a>( &mut self, input: &'a [u8], timestamp: Option, @@ -130,9 +132,6 @@ impl Parser for DltParser { }), )) } - fn min_msg_len(&self) -> usize { - MIN_MSG_LEN - } } fn to_native_cfg_err(msg: S) -> NativeError { diff --git a/application/apps/indexer/parsers/src/dlt/raw/mod.rs b/application/apps/indexer/parsers/src/dlt/raw/mod.rs index 65eac86e73..b4084abc41 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/mod.rs @@ -1,12 +1,12 @@ pub mod descriptor; -use super::{map_dlt_err, MIN_MSG_LEN}; +use super::{MIN_MSG_LEN, map_dlt_err}; use definitions::*; pub use descriptor::*; use dlt_core::parse::dlt_consume_msg; pub use dlt_core::{ dlt::LogLevel, - fibex::{gather_fibex_data, FibexConfig, FibexMetadata as FibexDltMetadata}, + fibex::{FibexConfig, FibexMetadata as FibexDltMetadata, gather_fibex_data}, filtering::{DltFilterConfig, ProcessedDltFilterConfig}, }; @@ -34,8 +34,10 @@ impl DltRawParser { } } -impl Parser for DltRawParser { - fn parse<'a>( +impl SingleParser for DltRawParser { + const MIN_MSG_LEN: usize = MIN_MSG_LEN; + + fn parse_item<'a>( &mut self, input: &'a [u8], timestamp: Option, @@ -43,7 +45,4 @@ impl Parser for DltRawParser { let (consumed, data) = self.parse_item(input, timestamp)?; Ok((consumed, data.map(LogRecordOutput::Raw))) } - fn min_msg_len(&self) -> usize { - MIN_MSG_LEN - } } diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index f1ec4e327a..e00cfcfe4e 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,7 +1,7 @@ -#![deny(unused_crate_dependencies)] +// #![deny(unused_crate_dependencies)] use components::Components; -use definitions::{LogRecordOutput, ParserError}; +use definitions::{LogRecordOutput, ParseReturnIterator, ParserError}; pub mod dlt; pub mod prelude; pub mod someip; @@ -21,11 +21,7 @@ pub enum Parser { */ impl definitions::Parser for Parser { - fn parse<'a>( - &mut self, - input: &'a [u8], - timestamp: Option, - ) -> Result<(usize, Option>), ParserError> { + fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { match self { Self::Dlt(inst) => inst.parse(input, timestamp), Self::DltRaw(inst) => inst.parse(input, timestamp), @@ -33,14 +29,6 @@ impl definitions::Parser for Parser { Self::Text(inst) => inst.parse(input, timestamp), } } - fn min_msg_len(&self) -> usize { - match self { - Self::Dlt(inst) => inst.min_msg_len(), - Self::DltRaw(inst) => inst.min_msg_len(), - Self::SomeIp(inst) => inst.min_msg_len(), - Self::Text(inst) => inst.min_msg_len(), - } - } } // TODO: this registration function will fail if some of parser would not be registred. That's wrong. diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 1b18a67e3e..6129628c59 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -298,8 +298,10 @@ impl SomeipParser { unsafe impl Send for SomeipParser {} unsafe impl Sync for SomeipParser {} -impl Parser for SomeipParser { - fn parse<'a>( +impl SingleParser for SomeipParser { + const MIN_MSG_LEN: usize = MIN_MSG_LEN; + + fn parse_item<'a>( &mut self, input: &'a [u8], timestamp: Option, @@ -310,9 +312,6 @@ impl Parser for SomeipParser { data.map(|msg| LogRecordOutput::String(msg.to_string())), )) } - fn min_msg_len(&self) -> usize { - MIN_MSG_LEN - } } fn header_string(header: &Header) -> String { @@ -548,49 +547,49 @@ mod test { fn test_metadata() -> FibexMetadata { let xml = r#" - - TestService - 259 - - - 1 - 2 - - - - EmptyEvent - 32772 - FIRE_AND_FORGET - - - TestEvent - 32773 - FIRE_AND_FORGET - - - Value1 - - - false - - 0 - - - Value2 - - - false - - 1 - - - - - - - UINT8 - - "#; + + TestService + 259 + + + 1 + 2 + + + + EmptyEvent + 32772 + FIRE_AND_FORGET + + + TestEvent + 32773 + FIRE_AND_FORGET + + + Value1 + + + false + + 0 + + + Value2 + + + false + + 1 + + + + + + + UINT8 + + "#; FibexMetadata::new( FibexParser::parse(vec![ @@ -1031,47 +1030,47 @@ mod test { #[test] fn service_lookup() { let xml = r#" - - Foo - 123 - - 1 - 0 - - - - Foo - 321 - - 1 - 0 - - - - Foo - 123 - - 2 - 1 - - - - Foo - 123 - - 1 - 1 - - - - Foo - 123 - - 2 - 3 - - - "#; + + Foo + 123 + + 1 + 0 + + + + Foo + 321 + + 1 + 0 + + + + Foo + 123 + + 2 + 1 + + + + Foo + 123 + + 1 + 1 + + + + Foo + 123 + + 2 + 3 + + + "#; let meta_data = FibexMetadata::new( FibexParser::parse(vec![ diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 88a3e65632..e6913fe154 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,4 +1,5 @@ pub mod descriptor; + use std::borrow::Cow; use definitions::*; @@ -36,17 +37,16 @@ impl StringTokenizer { } } -impl Parser for StringTokenizer { - fn parse<'a>( +impl SingleParser for StringTokenizer { + const MIN_MSG_LEN: usize = MIN_MSG_LEN; + + fn parse_item<'a>( &mut self, input: &'a [u8], timestamp: Option, ) -> Result<(usize, Option>), ParserError> { let (consumed, data) = self.parse_item(input, timestamp)?; - Ok((consumed, data.map(|msg| LogRecordOutput::Cow(msg)))) - } - fn min_msg_len(&self) -> usize { - MIN_MSG_LEN + Ok((consumed, data.map(LogRecordOutput::Cow))) } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index f9bdfe584d..d664095f3c 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -1,11 +1,13 @@ use std::path::{Path, PathBuf}; -use components::{ComponentDescriptor, ComponentFactory}; -use stypes::{PluginInfo, SemanticVersion}; use wasmtime::component::Component; +use components::ComponentDescriptor; +use definitions::{self as defs, ParseReturnIterator}; +use stypes::{PluginInfo, SemanticVersion}; + use crate::{ - PluginHostError, PluginParseMessage, PluginType, WasmPlugin, + PluginHostError, PluginType, WasmPlugin, plugins_shared::{ load::{WasmComponentInfo, load_and_inspect}, plugin_errors::PluginError, @@ -134,14 +136,8 @@ impl PluginErrorLimits { const INCOMPLETE_ERROR_LIMIT: usize = 50; } -use definitions::{self as defs}; - impl defs::Parser for PluginsParser { - fn parse<'a>( - &mut self, - input: &'a [u8], - timestamp: Option, - ) -> Result<(usize, Option>), defs::ParserError> { + fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { let res = match &mut self.parser { PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), }; @@ -180,9 +176,6 @@ impl defs::Parser for PluginsParser { res } - fn min_msg_len(&self) -> usize { - 1 - } } #[derive(Default)] diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index 0786dcfecd..30808fae1e 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -1,7 +1,10 @@ -pub use self::chipmunk::parser::parse_types::*; +use std::borrow::Cow; +use definitions::{self as defs, LogRecordOutput}; use stypes::ParserRenderOptions; +pub use self::chipmunk::parser::parse_types::*; + wasmtime::component::bindgen!({ path: "../../../../plugins/plugins_api/wit/v0.1.0", world: "chipmunk:parser/parse", @@ -39,19 +42,18 @@ impl From<&stypes::PluginParserGeneralSettings> for ParserConfig { } } -use definitions as defs; - -// impl From for defs::ParseYield { -// fn from(yld: ParseYield) -> Self { -// match yld { -// ParseYield::Message(msg) => defs::ParseYield::Message(msg.into()), -// ParseYield::Attachment(att) => defs::ParseYield::Attachment(att.into()), -// ParseYield::MessageAndAttachment((msg, att)) => { -// defs::ParseYield::MessageAndAttachment((msg.into(), att.into())) -// } -// } -// } -// } +impl From for LogRecordOutput<'_> { + fn from(value: ParseYield) -> Self { + match value { + ParseYield::Message(parsed_message) => parsed_message.into(), + ParseYield::Attachment(attachment) => LogRecordOutput::Attachment(attachment.into()), + ParseYield::MessageAndAttachment((msg, attachment)) => LogRecordOutput::Multiple(vec![ + msg.into(), + LogRecordOutput::Attachment(attachment.into()), + ]), + } + } +} impl From for defs::Attachment { fn from(att: Attachment) -> Self { @@ -77,14 +79,16 @@ impl From for defs::ParserError { } } -// impl From for defs::LogMessage { -// fn from(msg: ParsedMessage) -> Self { -// match msg { -// ParsedMessage::Line(msg) => defs::LogMessage::PlainText(msg), -// ParsedMessage::Columns(columns) => defs::LogMessage::Columns(columns), -// } -// } -// } +impl From for LogRecordOutput<'_> { + fn from(msg: ParsedMessage) -> Self { + match msg { + ParsedMessage::Line(msg) => LogRecordOutput::String(msg), + ParsedMessage::Columns(columns) => { + LogRecordOutput::Columns(columns.into_iter().map(Cow::Owned).collect()) + } + } + } +} impl From for ParserRenderOptions { fn from(value: RenderOptions) -> Self { diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index cc94473391..d29000b91c 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -14,7 +14,7 @@ use wasmtime::{ use wasmtime_wasi::{ResourceTable, p2::WasiCtx}; use crate::{ - PluginGuestError, PluginHostError, PluginParseMessage, + PluginGuestError, PluginHostError, plugins_shared::{PluginInfo, get_wasi_ctx_builder, plugin_errors::PluginError}, wasm_host::get_wasm_host, }; @@ -131,13 +131,9 @@ impl PluginParser { } } -use definitions as defs; +use definitions::{self as defs, LogRecordOutput, ParseReturnIterator}; impl defs::Parser for PluginParser { - fn parse<'a>( - &mut self, - input: &'a [u8], - timestamp: Option, - ) -> Result<(usize, Option>), defs::ParserError> { + fn parse<'a>(&mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { // Calls on plugins must be async. To solve that we got the following solutions: // - `futures::executor::block_on(plugin_call)`: Blocks the current Tokio worker with a local // executor. Risks are with blocking the whole runtime as Tokio isn't notified. @@ -162,16 +158,15 @@ impl defs::Parser for PluginParser { ))); } }; - // TODO: write in iteration - todo!("Not implemented"); - // Ok(parse_results - // .into_iter() - // .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))) - // .collect()) - } - fn min_msg_len(&self) -> usize { - 1 + let items = parse_results + .into_iter() + .map(|item| (item.consumed as usize, item.value.map(|v| v.into()))); + + let iter = Box::new(items) + as Box<(dyn Iterator>)> + 'a)>; + + Ok(iter) } } diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 6d09b321ca..95d92f6641 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -5,6 +5,7 @@ pub mod sde; use definitions::*; use log::warn; +use stypes::NativeError; /// ********************************************************************************************** /// For Ammar: @@ -49,8 +50,8 @@ where total_loaded: usize, total_skipped: usize, done: bool, - buffer: Vec<(usize, MessageStreamItem)>, writer: &'a mut W, + total_produced_items: usize, } impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, W> { @@ -64,8 +65,8 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, total_loaded: 0, total_skipped: 0, done: false, - buffer: Vec::new(), writer, + total_produced_items: 0, } } @@ -87,8 +88,6 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, // * Byte source will keep the loaded data in its internal buffer ensuring there // is no data loss when cancelling happen between load calls. - self.buffer.clear(); - if self.done { debug!("done...no next segment"); return None; @@ -127,37 +126,49 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, } // we can call consume only after all parse results are collected because of its // reference to self. - match self.parser.iter(current_slice, self.last_seen_ts) { - Ok(mut iterator) => { - let mut bytes_processed = skipped_bytes; - let mut messages_received = 0; - while let Some((consumed, msg)) = iterator.next() { - bytes_processed += consumed; - if let Some(msg) = msg { - if let Err(err) = self.writer.write(msg).await { - error!("Fail to write data into writer: {err}"); - } - messages_received += 1; + let mut total_consumed = 0; + let mut messages_received = 0; + + match self + .parser + .parse(current_slice, self.last_seen_ts) + .map(|mut iter| { + iter.try_for_each(|item| -> Result<(), NativeError> {match item { + (consumed, Some(m)) => { + let total_used_bytes = consumed + skipped_bytes; + // Reset skipped bytes since it had been counted here. + skipped_bytes = 0; + debug!( + "Extracted a valid message, consumed {} bytes (total used {} bytes)", + consumed, total_used_bytes + ); + total_consumed += consumed; + messages_received += 1; + + self.writer.write(m) } - } + (consumed, None) => { + total_consumed += consumed; + trace!("None, consumed {} bytes", consumed); + // Reset skipped bytes since it had been counted here. + skipped_bytes = 0; + + Ok(()) + } + }}) + }) { + Ok(Ok(())) => { + self.total_produced_items += messages_received; if messages_received > 0 { if let Err(err) = self.writer.finalize().await { error!("Fail to write data into writer: {err}"); } } - bytes_processed = if bytes_processed > 0 { - // Some bytes had been processed or skipped - self.byte_source.consume(bytes_processed); - bytes_processed - } else { - // All bytes were ignored - self.byte_source.consume(available); - available - }; + self.byte_source.consume(total_consumed); return Some(( - bytes_processed, + total_consumed, MessageStreamItem::Parsed(ParseOperationResult::new( - bytes_processed, + total_consumed, messages_received, )), )); @@ -242,7 +253,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, } return Some((0, MessageStreamItem::Done)); } - Err(ParserError::Native(err)) => { + Err(ParserError::Native(err)) | Ok(Err(err)) => { todo!("Not Implemented") } } @@ -335,10 +346,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, /// Checks if the producer have already produced any parsed items in the current session. fn did_produce_items(&self) -> bool { - // Produced items will be added to the internal buffer increasing its capacity. - // This isn't straight forward way, but used to avoid having to introduce a new field - // and keep track on its state. - self.buffer.capacity() > 0 + self.total_produced_items > 0 } /// Append incoming (SDE) Source-Data-Exchange to the underline byte source data. diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index 73dca1c8d4..8a73dd76cd 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -37,7 +37,7 @@ impl ExportWriter { } impl LogRecordWriter for ExportWriter { - async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { if !self.ranges.is_empty() { // TODO: we can optimize index search if !self @@ -51,49 +51,44 @@ impl LogRecordWriter for ExportWriter { } } self.index += 1; - fn fill<'a>( - writer: &mut ExportWriter, - record: LogRecordOutput<'a>, - ) -> Result>>, NativeError> { - match record { - LogRecordOutput::Raw(inner) => { - writer.buffer.write_all(inner)?; - Ok(None) - } - LogRecordOutput::Cow(inner) => { - writer.buffer.write_all(inner.as_bytes())?; - writer.buffer.write_all(&[b'\n'])?; - Ok(None) - } - LogRecordOutput::String(inner) => { - writer.buffer.write_all(inner.as_bytes())?; - writer.buffer.write_all(&[b'\n'])?; - Ok(None) - } - LogRecordOutput::Str(inner) => { - writer.buffer.write_all(inner.as_bytes())?; - writer.buffer.write_all(&[b'\n'])?; - Ok(None) - } - LogRecordOutput::Columns(inner) => { - writer.buffer.write_all( - inner - .join(&definitions::COLUMN_SENTINAL.to_string()) - .as_bytes(), - )?; - writer.buffer.write_all(&[b'\n'])?; - Ok(None) + match record { + LogRecordOutput::Raw(inner) => { + self.buffer.write_all(inner)?; + } + LogRecordOutput::Cow(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::String(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Str(inner) => { + self.buffer.write_all(inner.as_bytes())?; + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Columns(inner) => { + let mut items = inner.into_iter(); + if let Some(first_item) = items.next() { + self.buffer.write_all(first_item.as_bytes())?; + for item in items { + self.buffer + .write_all(&[definitions::COLUMN_SENTINAL as u8])?; + self.buffer.write_all(item.as_bytes())?; + } } - LogRecordOutput::Multiple(inner) => Ok(Some(inner)), - LogRecordOutput::Attachment(_) => { - // TODO: report error - Ok(None) + self.buffer.write_all(&[b'\n'])?; + } + LogRecordOutput::Multiple(inner) => { + for rec in inner { + self.write(rec)?; } } - } - if let Some(records) = fill(self, record)? { - for record in records.into_iter() { - fill(self, record)?; + LogRecordOutput::Attachment(att) => { + log::error!( + "Log attachments provided in raw export. Attachment name: {}", + att.name + ); } } Ok(()) diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index a925ea627f..ca5e123faf 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -10,9 +10,6 @@ use std::time::Instant; use stypes::NativeError; use tokio::{select, sync::mpsc::Receiver}; -/// Duration (in milliseconds) between flushes to the session file. -const SEND_DURATION: u128 = 500; - /// A writer responsible for appending data to a session file. /// /// This writer is linked to a session file through the `SessionStateAPI`. @@ -37,9 +34,6 @@ pub struct Writer { /// order between messages and attachments. attachments: Vec, - /// Timestamp used to track the last flush to the session file. - recent: Instant, - /// Unique identifier for the data source. This is used on the client side /// to visually group or distinguish data streams. id: u16, @@ -51,86 +45,67 @@ impl Writer { state, buffer: String::new(), attachments: Vec::new(), - recent: Instant::now(), id, } } } impl LogRecordWriter for Writer { - async fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { - fn fill<'a>( - writer: &mut Writer, - record: LogRecordOutput<'a>, - ) -> Option>> { - match record { - LogRecordOutput::Raw(inner) => { - // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs - // because during observe we do not expect raw data - writer.buffer.push_str( - &inner - .iter() - .map(|b| format!("{:02X}", b)) - .collect::(), - ); - writer.buffer.push('\n'); - None - } - LogRecordOutput::Cow(inner) => { - writer.buffer.push_str(&inner); - writer.buffer.push('\n'); - None - } - LogRecordOutput::String(inner) => { - writer.buffer.push_str(&inner); - writer.buffer.push('\n'); - None - } - LogRecordOutput::Str(inner) => { - writer.buffer.push_str(inner); - writer.buffer.push('\n'); - None - } - LogRecordOutput::Columns(inner) => { - writer - .buffer - .push_str(&inner.join(&definitions::COLUMN_SENTINAL.to_string())); - writer.buffer.push('\n'); - None - } - LogRecordOutput::Multiple(inner) => Some(inner), - LogRecordOutput::Attachment(inner) => { - writer.attachments.push(inner); - None - } + fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + match record { + LogRecordOutput::Raw(inner) => { + // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs + // because during observe we do not expect raw data + self.buffer.push_str( + &inner + .iter() + .map(|b| format!("{:02X}", b)) + .collect::(), + ); + self.buffer.push('\n'); } - } - if let Some(records) = fill(self, record) { - for record in records.into_iter() { - fill(self, record); + LogRecordOutput::Cow(inner) => { + self.buffer.push_str(&inner); + self.buffer.push('\n'); } - } - if self.recent.elapsed().as_millis() > SEND_DURATION { - if !self.buffer.is_empty() { - self.state - .write_session_file(self.get_id(), std::mem::take(&mut self.buffer)) - .await?; + LogRecordOutput::String(inner) => { + self.buffer.push_str(&inner); + self.buffer.push('\n'); + } + LogRecordOutput::Str(inner) => { + self.buffer.push_str(inner); + self.buffer.push('\n'); + } + LogRecordOutput::Columns(inner) => { + let mut items = inner.into_iter(); + if let Some(first_item) = items.next() { + self.buffer.push_str(&first_item); + for item in items { + self.buffer.push(definitions::COLUMN_SENTINAL); + self.buffer.push_str(&item); + } + } + self.buffer.push('\n'); + } + LogRecordOutput::Multiple(inner) => { + for rec in inner { + self.write(rec)?; + } } - for attachment in std::mem::take(&mut self.attachments) { - // TODO: send with 1 call - self.state.add_attachment(attachment)?; + LogRecordOutput::Attachment(inner) => { + self.attachments.push(inner); } - self.recent = Instant::now(); } Ok(()) } async fn finalize(&mut self) -> Result<(), stypes::NativeError> { if !self.buffer.is_empty() { - self.state - .write_session_file(self.get_id(), std::mem::take(&mut self.buffer)) - .await?; + let mut buf = String::with_capacity(self.buffer.len()); + buf.push_str(&self.buffer); + self.state.write_session_file(self.get_id(), buf).await?; + self.buffer.clear(); } - for attachment in std::mem::take(&mut self.attachments) { + for attachment in self.attachments.drain(..) { // TODO: send with 1 call self.state.add_attachment(attachment)?; } From d7d6d61f5cff2f49eae2687134a72c76ce20b58e Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Fri, 27 Jun 2025 16:29:21 +0200 Subject: [PATCH 073/121] Adjust LogRecordWriter definitions & implementations * Change methods on LogRecordWriter trait with method to append the messages to the internal memory and other method to flush the written data to the underline target. * Reduce log messages enums since Cow already covers String and &str * Change implementation in Binary exporter providing an internal bytes buffer making append method fill the internal memory only. --- .../apps/indexer/definitions/src/record.rs | 34 +++----------- .../apps/indexer/parsers/src/dlt/mod.rs | 4 +- .../apps/indexer/parsers/src/someip/mod.rs | 4 +- .../apps/indexer/parsers/src/text/mod.rs | 2 +- .../src/v0_1_0/parser/bindings.rs | 2 +- .../indexer/processor/src/producer/mod.rs | 27 +++++------ .../src/handlers/observe/export_raw.rs | 47 +++++++++---------- .../session/src/handlers/observe/session.rs | 29 +++++------- 8 files changed, 55 insertions(+), 94 deletions(-) diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 6348ad79a6..82851a2c01 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -84,17 +84,11 @@ pub enum LogRecordOutput<'a> { /// A raw binary message. Raw(&'a [u8]), - /// A borrowed UTF-8 string slice. - Str(&'a str), - - /// A string wrapped in a [`Cow`] (Copy-on-Write), allowing either borrowed or owned data. - Cow(Cow<'a, str>), - - /// An owned UTF-8 string. - String(String), + /// Log message line + Message(Cow<'a, str>), /// Structured columnar data. Typically used when the parser can extract - /// meaningful fields and present them as an array of strings. + /// meaningful fields and present them as collection of texts. Columns(Vec>), /// An attachment object, such as a binary blob or associated metadata. @@ -124,28 +118,14 @@ pub enum LogRecordOutput<'a> { /// An implementation might buffer records in memory and write them in bulk /// every few seconds, or directly write to a file, database, or network sink. pub trait LogRecordWriter { - /// Called for every new log record received from the `MessageProducer`. + /// Appends the provided `record` to the writer intermediate memory. /// /// # Arguments /// * `record` – A reference to a [`LogRecordOutput`] containing the data to write. - /// - /// # Returns - /// * `Ok(())` on success. - /// * `Err(NativeError)` if the write operation fails. - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), stypes::NativeError>; + fn append(&mut self, record: LogRecordOutput<'_>); - /// Called once when the `MessageProducer` has no more data to provide. - /// - /// There is no guarantee that `write` won’t be called again after `finalize` - /// (e.g., in a file tailing scenario), but `finalize` itself is guaranteed - /// to be called only once. - /// - /// This is the opportunity to flush any buffered data and perform cleanup if necessary. - /// - /// # Returns - /// * `Ok(())` on success. - /// * `Err(NativeError)` if the finalization process fails. - async fn finalize(&mut self) -> Result<(), stypes::NativeError>; + /// Flushes the data from the internal buffer to the underline target + async fn flush(&mut self) -> Result<(), stypes::NativeError>; /// Returns the unique identifier of the associated data source. /// diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 08990481d9..1fcebac618 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -123,11 +123,11 @@ impl SingleParser for DltParser { data.map(|(msg, attachment)| { if let Some(attachment) = attachment { LogRecordOutput::Multiple(vec![ - LogRecordOutput::String(msg.to_string()), + LogRecordOutput::Message(msg.to_string().into()), LogRecordOutput::Attachment(attachment), ]) } else { - LogRecordOutput::String(msg.to_string()) + LogRecordOutput::Message(msg.to_string().into()) } }), )) diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 6129628c59..cf6fab702a 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -5,11 +5,9 @@ use std::{ cmp::Ordering, collections::{HashMap, hash_map::Entry}, fmt::{self, Display}, - io::{self, Write}, path::PathBuf, sync::Mutex, }; -use stypes::NativeError; use someip_messages::*; use someip_payload::{ @@ -309,7 +307,7 @@ impl SingleParser for SomeipParser { let (consumed, data) = self.parse_item(input, timestamp)?; Ok(( consumed, - data.map(|msg| LogRecordOutput::String(msg.to_string())), + data.map(|msg| LogRecordOutput::Message(msg.to_string().into())), )) } } diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index e6913fe154..8cb98dec3b 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -46,7 +46,7 @@ impl SingleParser for StringTokenizer { timestamp: Option, ) -> Result<(usize, Option>), ParserError> { let (consumed, data) = self.parse_item(input, timestamp)?; - Ok((consumed, data.map(LogRecordOutput::Cow))) + Ok((consumed, data.map(LogRecordOutput::Message))) } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index 30808fae1e..16df7936eb 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -82,7 +82,7 @@ impl From for defs::ParserError { impl From for LogRecordOutput<'_> { fn from(msg: ParsedMessage) -> Self { match msg { - ParsedMessage::Line(msg) => LogRecordOutput::String(msg), + ParsedMessage::Line(msg) => LogRecordOutput::Message(msg.into()), ParsedMessage::Columns(columns) => { LogRecordOutput::Columns(columns.into_iter().map(Cow::Owned).collect()) } diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 95d92f6641..9f9c65c6d1 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -5,7 +5,6 @@ pub mod sde; use definitions::*; use log::warn; -use stypes::NativeError; /// ********************************************************************************************** /// For Ammar: @@ -119,7 +118,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, if available == 0 { trace!("No more bytes available from source"); self.done = true; - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); @@ -132,8 +131,8 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, match self .parser .parse(current_slice, self.last_seen_ts) - .map(|mut iter| { - iter.try_for_each(|item| -> Result<(), NativeError> {match item { + .map(|iter| { + iter.for_each(|item| {match item { (consumed, Some(m)) => { let total_used_bytes = consumed + skipped_bytes; // Reset skipped bytes since it had been counted here. @@ -145,22 +144,18 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, total_consumed += consumed; messages_received += 1; - self.writer.write(m) + self.writer.append(m) } (consumed, None) => { total_consumed += consumed; trace!("None, consumed {} bytes", consumed); - // Reset skipped bytes since it had been counted here. - skipped_bytes = 0; - - Ok(()) } }}) }) { - Ok(Ok(())) => { + Ok(()) => { self.total_produced_items += messages_received; if messages_received > 0 { - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } } @@ -190,7 +185,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -220,7 +215,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -235,7 +230,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -248,12 +243,12 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, error!("Parsing failed: Error {err}"); eprintln!("Parsing failed: Error: {err}"); self.done = true; - if let Err(err) = self.writer.finalize().await { + if let Err(err) = self.writer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); } - Err(ParserError::Native(err)) | Ok(Err(err)) => { + Err(ParserError::Native(err)) => { todo!("Not Implemented") } } diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index 8a73dd76cd..ad69e699fa 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -8,11 +8,11 @@ use std::{ io::{self, BufWriter, Write}, path::Path, }; -use stypes::NativeError; use tokio::select; pub struct ExportWriter { - buffer: BufWriter, + file_buffer: BufWriter, + bytes_buffer: Vec, index: usize, ranges: Vec>, } @@ -29,7 +29,8 @@ impl ExportWriter { std::fs::File::create(filename)? }; Ok(Self { - buffer: BufWriter::new(out_file), + file_buffer: BufWriter::new(out_file), + bytes_buffer: Vec::new(), index: 0, ranges, }) @@ -37,7 +38,7 @@ impl ExportWriter { } impl LogRecordWriter for ExportWriter { - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + fn append(&mut self, record: LogRecordOutput<'_>) { if !self.ranges.is_empty() { // TODO: we can optimize index search if !self @@ -47,41 +48,31 @@ impl LogRecordWriter for ExportWriter { { // Skip record because it's not in a range self.index += 1; - return Ok(()); } } self.index += 1; match record { LogRecordOutput::Raw(inner) => { - self.buffer.write_all(inner)?; + self.bytes_buffer.extend_from_slice(inner); } - LogRecordOutput::Cow(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::String(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; - } - LogRecordOutput::Str(inner) => { - self.buffer.write_all(inner.as_bytes())?; - self.buffer.write_all(&[b'\n'])?; + LogRecordOutput::Message(inner) => { + self.bytes_buffer.extend_from_slice(inner.as_bytes()); + self.bytes_buffer.push(b'\n'); } LogRecordOutput::Columns(inner) => { let mut items = inner.into_iter(); if let Some(first_item) = items.next() { - self.buffer.write_all(first_item.as_bytes())?; + self.bytes_buffer.extend_from_slice(first_item.as_bytes()); for item in items { - self.buffer - .write_all(&[definitions::COLUMN_SENTINAL as u8])?; - self.buffer.write_all(item.as_bytes())?; + self.bytes_buffer.push(definitions::COLUMN_SENTINAL as u8); + self.bytes_buffer.extend_from_slice(item.as_bytes()); } } - self.buffer.write_all(&[b'\n'])?; + self.bytes_buffer.push(b'\n'); } LogRecordOutput::Multiple(inner) => { for rec in inner { - self.write(rec)?; + self.append(rec); } } LogRecordOutput::Attachment(att) => { @@ -91,10 +82,14 @@ impl LogRecordWriter for ExportWriter { ); } } - Ok(()) } - async fn finalize(&mut self) -> Result<(), stypes::NativeError> { - self.buffer.flush()?; + async fn flush(&mut self) -> Result<(), stypes::NativeError> { + if !self.bytes_buffer.is_empty() { + self.file_buffer.write_all(&self.bytes_buffer)?; + self.bytes_buffer.clear() + } + self.file_buffer.flush()?; + Ok(()) } fn get_id(&self) -> u16 { diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index ca5e123faf..e2fd76e31e 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -7,7 +7,6 @@ use definitions::*; use log::{trace, warn}; use processor::producer::{MessageProducer, MessageStreamItem, sde::*}; use std::time::Instant; -use stypes::NativeError; use tokio::{select, sync::mpsc::Receiver}; /// A writer responsible for appending data to a session file. @@ -51,7 +50,7 @@ impl Writer { } impl LogRecordWriter for Writer { - fn write(&mut self, record: LogRecordOutput<'_>) -> Result<(), NativeError> { + fn append(&mut self, record: LogRecordOutput<'_>) { match record { LogRecordOutput::Raw(inner) => { // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs @@ -64,16 +63,8 @@ impl LogRecordWriter for Writer { ); self.buffer.push('\n'); } - LogRecordOutput::Cow(inner) => { - self.buffer.push_str(&inner); - self.buffer.push('\n'); - } - LogRecordOutput::String(inner) => { - self.buffer.push_str(&inner); - self.buffer.push('\n'); - } - LogRecordOutput::Str(inner) => { - self.buffer.push_str(inner); + LogRecordOutput::Message(msg) => { + self.buffer.push_str(&msg); self.buffer.push('\n'); } LogRecordOutput::Columns(inner) => { @@ -89,21 +80,23 @@ impl LogRecordWriter for Writer { } LogRecordOutput::Multiple(inner) => { for rec in inner { - self.write(rec)?; + self.append(rec); } } LogRecordOutput::Attachment(inner) => { self.attachments.push(inner); } } - Ok(()) } - async fn finalize(&mut self) -> Result<(), stypes::NativeError> { + + async fn flush(&mut self) -> Result<(), stypes::NativeError> { if !self.buffer.is_empty() { - let mut buf = String::with_capacity(self.buffer.len()); - buf.push_str(&self.buffer); - self.state.write_session_file(self.get_id(), buf).await?; + // Capacity of cloned item is equal to its length, making this clone + // produces one mem_copy command for the needed bytes only on the cloned + // string while preserving the capacity of the intermediate buffer. + let msgs = self.buffer.clone(); self.buffer.clear(); + self.state.write_session_file(self.get_id(), msgs).await?; } for attachment in self.attachments.drain(..) { // TODO: send with 1 call From f003f36161dee68cf02b9e2a000086e05c5295de Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Tue, 24 Jun 2025 15:46:49 +0200 Subject: [PATCH 074/121] Run CI checks on PRs into master only Skip CI checks for PRs targeting development branches. --- .github/workflows/pullrequest_check.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pullrequest_check.yml b/.github/workflows/pullrequest_check.yml index 55f8ba3a10..1157ea14b1 100644 --- a/.github/workflows/pullrequest_check.yml +++ b/.github/workflows/pullrequest_check.yml @@ -12,7 +12,8 @@ on: jobs: ts_and_rust_lint: - if: github.event.pull_request.draft == false + # TODO AAZ: Make sure this check works upon merging into master. + if: github.event.pull_request.draft == false && github.event.pull_request.base.ref == 'master' runs-on: ubuntu-latest steps: - name: Checkout @@ -63,7 +64,8 @@ jobs: run: yarn run check integration_and_unit_tests: - if: github.event.pull_request.draft == false + # TODO AAZ: Make sure this check works upon merging into master. + if: github.event.pull_request.draft == false && github.event.pull_request.base.ref == 'master' runs-on: ubuntu-latest steps: - name: Checkout From 060e0cbedbae1459d022b725e1c0138b11cca522 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Tue, 1 Jul 2025 09:45:12 +0200 Subject: [PATCH 075/121] After Review: LogRecordWriter -> LogRecordsBuffer * Change creating new string from the previous to the most performant solution with copying the data, since moving the data safely forces allocating the string capacity instead of length on each iteration. * Change function signature as requested * Rename LogRecordWriter to LogRecordsBuffer * Fix String to str --- .../apps/indexer/definitions/src/record.rs | 27 ++++---- .../indexer/processor/src/producer/mod.rs | 26 +++---- .../src/handlers/observe/export_raw.rs | 14 ++-- .../session/src/handlers/observe/mod.rs | 26 +++---- .../session/src/handlers/observe/session.rs | 67 ++++++++++--------- 5 files changed, 81 insertions(+), 79 deletions(-) diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/definitions/src/record.rs index 82851a2c01..1fd4193c0b 100644 --- a/application/apps/indexer/definitions/src/record.rs +++ b/application/apps/indexer/definitions/src/record.rs @@ -89,7 +89,7 @@ pub enum LogRecordOutput<'a> { /// Structured columnar data. Typically used when the parser can extract /// meaningful fields and present them as collection of texts. - Columns(Vec>), + Columns(Vec>), /// An attachment object, such as a binary blob or associated metadata. /// These are handled separately from textual data and require special treatment @@ -101,23 +101,20 @@ pub enum LogRecordOutput<'a> { Multiple(Vec>), } -/// Interface for implementing a writer that handles log records. +/// Defines an interface for buffering log records before they are persisted. /// -/// An instance of `LogRecordWriter` is passed to a `MessageProducer`. -/// Each time a new log record is available, the `MessageProducer` invokes the `write` method. -/// The specific behavior for writing records to the actual destination is left to the implementation. +/// An instance of a type implementing `LogRecordsBuffer` is passed to a `MessageProducer`. +/// For each new log record, the `MessageProducer` calls the `append` method. The `flush` +/// method is used to write the buffered records to their final destination. /// -/// This allows implementors to define their own strategies for data caching, batching, and flushing. -/// When the `MessageProducer` has no more data to process, it calls `finalize`. At that point, -/// the writer should persist any buffered data, if applicable. -/// -/// **Important:** (pending TODO) The `write` method must not be cancel-safe. This is required to keep -/// the `MessageProducer` cancel-safe. This problems isn't resolved yet. +/// This design allows for custom strategies regarding caching, batching, and flushing. +/// When processing is complete, `flush` should be called to ensure all buffered data +/// is persisted. /// /// # Examples -/// An implementation might buffer records in memory and write them in bulk -/// every few seconds, or directly write to a file, database, or network sink. -pub trait LogRecordWriter { +/// An implementation might buffer records in memory and flush them in bulk +/// to a file, database, or network sink. +pub trait LogRecordsBuffer { /// Appends the provided `record` to the writer intermediate memory. /// /// # Arguments @@ -134,5 +131,5 @@ pub trait LogRecordWriter { /// /// # Returns /// A `u16` representing the unique source ID. - fn get_id(&self) -> u16; + fn get_source_id(&self) -> u16; } diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 9f9c65c6d1..3149ea4990 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -36,11 +36,11 @@ pub enum MessageStreamItem { } #[derive(Debug)] -pub struct MessageProducer<'a, P, D, W> +pub struct MessageProducer<'a, P, D, B> where P: Parser, D: ByteSource, - W: LogRecordWriter, + B: LogRecordsBuffer, { byte_source: D, parser: P, @@ -49,13 +49,13 @@ where total_loaded: usize, total_skipped: usize, done: bool, - writer: &'a mut W, + logs_buffer: &'a mut B, total_produced_items: usize, } -impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, W> { +impl<'a, P: Parser, D: ByteSource, B: LogRecordsBuffer> MessageProducer<'a, P, D, B> { /// create a new producer by plugging into a byte source - pub fn new(parser: P, source: D, writer: &'a mut W) -> Self { + pub fn new(parser: P, source: D, logs_buffer: &'a mut B) -> Self { MessageProducer { byte_source: source, parser, @@ -64,7 +64,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, total_loaded: 0, total_skipped: 0, done: false, - writer, + logs_buffer, total_produced_items: 0, } } @@ -118,7 +118,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, if available == 0 { trace!("No more bytes available from source"); self.done = true; - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); @@ -144,7 +144,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, total_consumed += consumed; messages_received += 1; - self.writer.append(m) + self.logs_buffer.append(m) } (consumed, None) => { total_consumed += consumed; @@ -155,7 +155,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, Ok(()) => { self.total_produced_items += messages_received; if messages_received > 0 { - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } } @@ -185,7 +185,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -215,7 +215,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -230,7 +230,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, ); let unused = skipped_bytes + available; self.done = true; - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((unused, MessageStreamItem::Done)); @@ -243,7 +243,7 @@ impl<'a, P: Parser, D: ByteSource, W: LogRecordWriter> MessageProducer<'a, P, D, error!("Parsing failed: Error {err}"); eprintln!("Parsing failed: Error: {err}"); self.done = true; - if let Err(err) = self.writer.flush().await { + if let Err(err) = self.logs_buffer.flush().await { error!("Fail to write data into writer: {err}"); } return Some((0, MessageStreamItem::Done)); diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index ad69e699fa..4a01ade22e 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -1,5 +1,5 @@ use crate::operations::{OperationAPI, OperationResult}; -use definitions::{ByteSource, LogRecordOutput, LogRecordWriter, Parser}; +use definitions::{ByteSource, LogRecordOutput, LogRecordsBuffer, Parser}; use log::debug; use processor::producer::MessageProducer; @@ -10,14 +10,14 @@ use std::{ }; use tokio::select; -pub struct ExportWriter { +pub struct ExportLogsBuffer { file_buffer: BufWriter, bytes_buffer: Vec, index: usize, ranges: Vec>, } -impl ExportWriter { +impl ExportLogsBuffer { pub fn new>( filename: P, ranges: Vec>, @@ -37,7 +37,7 @@ impl ExportWriter { } } -impl LogRecordWriter for ExportWriter { +impl LogRecordsBuffer for ExportLogsBuffer { fn append(&mut self, record: LogRecordOutput<'_>) { if !self.ranges.is_empty() { // TODO: we can optimize index search @@ -92,14 +92,14 @@ impl LogRecordWriter for ExportWriter { Ok(()) } - fn get_id(&self) -> u16 { + fn get_source_id(&self) -> u16 { 0 } } -pub async fn run_producer( +pub async fn run_producer( operation_api: OperationAPI, - mut producer: MessageProducer<'_, P, S, W>, + mut producer: MessageProducer<'_, P, S, B>, ) -> OperationResult { operation_api.processing(); let cancel = operation_api.cancellation_token(); diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index 1c046fc572..8ea6381404 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -6,7 +6,7 @@ use crate::{ state::SessionStateAPI, }; use components::Components; -use processor::producer::{sde::*, MessageProducer}; +use processor::producer::{MessageProducer, sde::*}; use std::sync::Arc; use stypes::{SessionAction, SessionSetup}; @@ -20,16 +20,16 @@ pub async fn observing( match &options.origin { SessionAction::File(..) => { let (desciptor, source, parser) = components.setup(&options)?; - let mut writer = - session::Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); + let mut logs_buffer = + session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut logs_buffer); Ok(session::run_producer(operation_api, state, producer, None, None).await?) } SessionAction::Source => { let (desciptor, source, parser) = components.setup(&options)?; - let mut writer = - session::Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); + let mut logs_buffer = + session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut logs_buffer); Ok(session::run_producer(operation_api, state, producer, None, rx_sde).await?) } SessionAction::Files(files) => { @@ -37,24 +37,24 @@ pub async fn observing( for file in files { let (desciptor, source, parser) = components.setup(&options.inherit(SessionAction::File(file.to_owned())))?; - let mut writer = - session::Writer::new(state.clone(), state.add_source(desciptor).await?); - let producer = MessageProducer::new(parser, source, &mut writer); + let mut logs_buffer = + session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let producer = MessageProducer::new(parser, source, &mut logs_buffer); session::run_producer(operation_api.clone(), state.clone(), producer, None, None) .await?; } Ok(Some(())) } SessionAction::ExportRaw(files, ranges, output) => { - // We are creating one single writer for all files to keep tracking ranges and current index - let mut writer = export_raw::ExportWriter::new(output, ranges.clone())?; + // We are creating one single buffer for all files to keep tracking ranges and current index + let mut logs_buffer = export_raw::ExportLogsBuffer::new(output, ranges.clone())?; for file in files { if operation_api.cancellation_token().is_cancelled() { return Ok(Some(())); } let (_, source, parser) = components.setup(&options.inherit(SessionAction::File(file.to_owned())))?; - let producer = MessageProducer::new(parser, source, &mut writer); + let producer = MessageProducer::new(parser, source, &mut logs_buffer); export_raw::run_producer(operation_api.clone(), producer).await?; } Ok(Some(())) diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index e2fd76e31e..81dc352255 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -9,24 +9,22 @@ use processor::producer::{MessageProducer, MessageStreamItem, sde::*}; use std::time::Instant; use tokio::{select, sync::mpsc::Receiver}; -/// A writer responsible for appending data to a session file. +/// A buffer for accumulating log data before writing to a session file. /// -/// This writer is linked to a session file through the `SessionStateAPI`. -/// Instead of writing continuously, it accumulates updates and sends them -/// in periodic batches defined by `SEND_DURATION` (in milliseconds). +/// This buffer is linked to a session file via the `SessionStateAPI`. +/// It accumulates updates and sends them when `flush()` is called. /// -/// To support this behavior, it uses two internal buffers: +/// It uses two internal stores to manage data: /// - `buffer` accumulates textual log entries. -/// - `attachments` stores associated `Attachment` objects, which are sent -/// only after flushing the text buffer to ensure synchronization between -/// log lines and attachments. -pub struct Writer { +/// - `attachments` stores associated `Attachment` objects. These are sent +/// only after the text buffer is flushed to ensure synchronization. +pub struct LogsBuffer { /// Communication channel to the session file. state: SessionStateAPI, /// Text buffer for log messages. Since session files are text-based, /// the buffered data is stored as a `String`. - buffer: String, + text_buffer: String, /// Buffer for received attachments. These are queued and sent only after /// the buffered log messages have been flushed, preserving the logical @@ -38,45 +36,45 @@ pub struct Writer { id: u16, } -impl Writer { +impl LogsBuffer { pub fn new(state: SessionStateAPI, id: u16) -> Self { Self { state, - buffer: String::new(), + text_buffer: String::new(), attachments: Vec::new(), id, } } } -impl LogRecordWriter for Writer { +impl LogRecordsBuffer for LogsBuffer { fn append(&mut self, record: LogRecordOutput<'_>) { match record { LogRecordOutput::Raw(inner) => { // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs // because during observe we do not expect raw data - self.buffer.push_str( + self.text_buffer.push_str( &inner .iter() .map(|b| format!("{:02X}", b)) .collect::(), ); - self.buffer.push('\n'); + self.text_buffer.push('\n'); } LogRecordOutput::Message(msg) => { - self.buffer.push_str(&msg); - self.buffer.push('\n'); + self.text_buffer.push_str(&msg); + self.text_buffer.push('\n'); } LogRecordOutput::Columns(inner) => { let mut items = inner.into_iter(); if let Some(first_item) = items.next() { - self.buffer.push_str(&first_item); + self.text_buffer.push_str(&first_item); for item in items { - self.buffer.push(definitions::COLUMN_SENTINAL); - self.buffer.push_str(&item); + self.text_buffer.push(definitions::COLUMN_SENTINAL); + self.text_buffer.push_str(&item); } } - self.buffer.push('\n'); + self.text_buffer.push('\n'); } LogRecordOutput::Multiple(inner) => { for rec in inner { @@ -90,13 +88,19 @@ impl LogRecordWriter for Writer { } async fn flush(&mut self) -> Result<(), stypes::NativeError> { - if !self.buffer.is_empty() { - // Capacity of cloned item is equal to its length, making this clone - // produces one mem_copy command for the needed bytes only on the cloned - // string while preserving the capacity of the intermediate buffer. - let msgs = self.buffer.clone(); - self.buffer.clear(); - self.state.write_session_file(self.get_id(), msgs).await?; + if !self.text_buffer.is_empty() { + // Creates an owned string from current buffer then clean the current. This operation + // produces one mem_copy command for the needed bytes only while preserving + // the capacity of the intermediate buffer. + // Rust doesn't provide safe way to move bytes between strings without replacing + // the whole string, forcing us to allocate the full capacity of the buffer on each + // iteration (which could backfire in the internal buffer gets too long in one of the + // iterations). + let msgs = String::from(&self.text_buffer); + self.text_buffer.clear(); + self.state + .write_session_file(self.get_source_id(), msgs) + .await?; } for attachment in self.attachments.drain(..) { // TODO: send with 1 call @@ -104,7 +108,8 @@ impl LogRecordWriter for Writer { } Ok(()) } - fn get_id(&self) -> u16 { + + fn get_source_id(&self) -> u16 { self.id } } @@ -128,10 +133,10 @@ enum Next { /// source, updates from SDE, and termination signals. /// ********************************************************************************************** -pub async fn run_producer( +pub async fn run_producer( operation_api: OperationAPI, state: SessionStateAPI, - mut producer: MessageProducer<'_, P, S, W>, + mut producer: MessageProducer<'_, P, S, B>, mut rx_tail: Option>>, mut rx_sde: Option, ) -> OperationResult<()> { From 9279da4f79bd65b77e0195d3052d1b68c76ad22a Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Tue, 1 Jul 2025 14:56:47 +0200 Subject: [PATCH 076/121] Components: Move Plugins Manger to component session --- .../indexer/session/src/components/api.rs | 34 ++++ .../indexer/session/src/components/mod.rs | 184 +++++++++++++++++- .../commands => components}/plugins.rs | 76 +++----- .../apps/indexer/session/src/unbound/api.rs | 129 ------------ .../session/src/unbound/commands/mod.rs | 117 +---------- .../apps/indexer/session/src/unbound/mod.rs | 17 +- 6 files changed, 247 insertions(+), 310 deletions(-) rename application/apps/indexer/session/src/{unbound/commands => components}/plugins.rs (65%) diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index f75b4db229..74b24c8889 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -139,4 +139,38 @@ pub enum Api { Vec, oneshot::Sender, NativeError>>, ), + + /// Get all information of the installed plugins . + InstalledPluginsList(oneshot::Sender>), + /// Get all information of invalid plugins . + InvalidPluginsList(oneshot::Sender>), + /// Get the directory paths (considered ID) for installed plugins. + InstalledPluginsPaths(oneshot::Sender>), + /// Get the directory paths (considered ID) for invalid plugins. + InvalidPluginsPaths(oneshot::Sender>), + /// Get all info for the installed plugin with provided directory path (considered ID) + InstalledPluginInfo( + String, + oneshot::Sender, NativeError>>, + ), + /// Get all info for the invalid plugin with provided directory path (considered ID) + InvalidPluginInfo( + String, + oneshot::Sender, NativeError>>, + ), + /// Retrieves runtime data for a plugin located at the specified path. + PluginRunData( + String, + oneshot::Sender, NativeError>>, + ), + /// Reload all the plugins from their directory. + ReloadPlugins(oneshot::Sender>), + /// Adds a plugin with the given directory path and optional plugin type. + AddPlugin( + String, + Option, + oneshot::Sender>, + ), + /// Removes the plugin with the given directory path. + RemovePlugin(String, oneshot::Sender>), } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 81d4b56bca..9e53139a20 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -1,11 +1,10 @@ -mod api; - use api::*; use components::{Components, LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; use tokio::{ sync::{ + RwLock, mpsc::{UnboundedReceiver, UnboundedSender, error::SendError, unbounded_channel}, oneshot::{self, error::RecvError}, }, @@ -13,6 +12,9 @@ use tokio::{ }; use uuid::Uuid; +mod api; +mod plugins; + /// A controller responsible for managing all available components in the system (such as parsers, sources, etc.). /// /// This structure acts as a central registry that stores and manages the state of all components throughout @@ -45,6 +47,14 @@ impl ComponentsSession { /// to listen for callback events related to the components' operations. pub async fn new() -> Result<(Self, UnboundedReceiver), stypes::NativeError> { + // TODO: Plugins manager is used temporally here in initial phase and we should consider + // moving it to its own module. Reasons: + // * It doesn't need parallelism for most of task. + // * It'll need different state and locking management for downloading plugins, Updating + // caches etc... + let plugins_manager = plugins::load_manager().await?; + let plugins_manager = Arc::new(RwLock::new(plugins_manager)); + let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = unbounded_channel(); let (tx_callback_events, rx_callback_events): ( @@ -190,6 +200,38 @@ impl ComponentsSession { log_if_err(tx.send(())); break; } + + Api::InstalledPluginsList(tx) => { + // let plugs_ref_clone = Arc::clone(&plugins_manager); + log_if_err(tx.send(plugins::installed_plugins_list(&plugins_manager).await)) + } + Api::InvalidPluginsList(tx) => { + log_if_err(tx.send(plugins::invalid_plugins_list(&plugins_manager).await)) + } + Api::InstalledPluginsPaths(tx) => log_if_err( + tx.send(plugins::installed_plugins_paths(&plugins_manager).await), + ), + Api::InvalidPluginsPaths(tx) => { + log_if_err(tx.send(plugins::invalid_plugins_paths(&plugins_manager).await)) + } + Api::InstalledPluginInfo(path, tx) => log_if_err( + tx.send(plugins::installed_plugins_info(path, &plugins_manager).await), + ), + Api::InvalidPluginInfo(path, tx) => log_if_err( + tx.send(plugins::invalid_plugins_info(path, &plugins_manager).await), + ), + Api::PluginRunData(path, tx) => log_if_err( + tx.send(plugins::get_plugin_run_data(path, &plugins_manager).await), + ), + Api::ReloadPlugins(tx) => { + log_if_err(tx.send(plugins::reload_plugins(&plugins_manager).await)) + } + Api::AddPlugin(path, typ, tx) => { + log_if_err(tx.send(plugins::add_plugin(path, typ, &plugins_manager).await)) + } + Api::RemovePlugin(path, tx) => { + log_if_err(tx.send(plugins::remove_plugin(path, &plugins_manager).await)) + } } } debug!("Session task is finished"); @@ -399,6 +441,142 @@ impl ComponentsSession { )?; response(rx.await, "Fail to get response from Api::Shutdown") } + + /// Get all information of installed plugins . + pub async fn installed_plugins_list(&self) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InstalledPluginsList(tx)), + "Fail to send Api::InstalledPluginsList", + )?; + response( + rx.await, + "Fail to get response from Api::InstalledPluginsList", + )? + } + + /// Get all information of invalid plugins . + pub async fn invalid_plugins_list( + &self, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InvalidPluginsList(tx)), + "Fail to send Api::InvalidPluginsList", + )?; + response( + rx.await, + "Fail to get response from Api::InvalidPluginsList", + )? + } + + /// Get the directory paths (considered ID) for installed plugins. + pub async fn installed_plugins_paths( + &self, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InstalledPluginsPaths(tx)), + "Fail to send Api::InstalledPluginsPaths", + )?; + response( + rx.await, + "Fail to get response from Api::InstalledPluginsPaths", + )? + } + + /// Get the directory paths (considered ID) for invalid plugins. + pub async fn invalid_plugins_paths( + &self, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InvalidPluginsPaths(tx)), + "Fail to send Api::InvalidPluginsPaths", + )?; + response( + rx.await, + "Fail to get response from Api::InvalidPluginsPaths", + )? + } + + /// Get all info for the installed plugin with provided directory path (considered ID) + pub async fn installed_plugin_info( + &self, + plugin_path: String, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InstalledPluginInfo(plugin_path, tx)), + "Fail to send Api::InstalledPluginInfo", + )?; + response( + rx.await, + "Fail to get response from Api::InstalledPluginInfo", + )? + } + + /// Get all info for the invalid plugin with provided directory path (considered ID) + pub async fn invalid_plugin_info( + &self, + plugin_path: String, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::InvalidPluginInfo(plugin_path, tx)), + "Fail to send Api::InvalidPluginInfo", + )?; + response(rx.await, "Fail to get response from Api::InvalidPluginInfo")? + } + + /// Retrieves runtime data for a plugin located at the specified path. + pub async fn get_plugin_run_data( + &self, + plugin_path: String, + ) -> Result, stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::PluginRunData(plugin_path, tx)), + "Fail to send Api::PluginRunData", + )?; + response(rx.await, "Fail to get response from Api::PluginRunData")? + } + + /// Reload the plugin directory. + pub async fn reload_plugins(&self) -> Result<(), stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::ReloadPlugins(tx)), + "Fail to send Api::ReloadPlugins", + )?; + response(rx.await, "Fail to get response from Api::ReloadPlugins")? + //TODO AAZ: Reload components on reload plugins + } + + /// Adds a plugin with the given directory path and optional plugin type. + pub async fn add_plugin( + &self, + plugin_path: String, + plugin_type: Option, + ) -> Result<(), stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api + .send(Api::AddPlugin(plugin_path, plugin_type, tx)), + "Fail to send Api::AddPlugin", + )?; + response(rx.await, "Fail to get response from Api::AddPlugin")? + } + + /// Removes the plugin with the given directory path. + pub async fn remove_plugin(&self, plugin_path: String) -> Result<(), stypes::NativeError> { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::RemovePlugin(plugin_path, tx)), + "Fail to send Api::RemovePlugin", + )?; + response(rx.await, "Fail to get response from Api::RemovePlugin")? + } } /// Logs an error if the given result contains an error. diff --git a/application/apps/indexer/session/src/unbound/commands/plugins.rs b/application/apps/indexer/session/src/components/plugins.rs similarity index 65% rename from application/apps/indexer/session/src/unbound/commands/plugins.rs rename to application/apps/indexer/session/src/components/plugins.rs index 9ff8a896d5..0cb58bc2f3 100644 --- a/application/apps/indexer/session/src/unbound/commands/plugins.rs +++ b/application/apps/indexer/session/src/components/plugins.rs @@ -1,53 +1,47 @@ use std::path::PathBuf; -use crate::unbound::signal::Signal; use plugins_host::plugins_manager::PluginsManager; use stypes::{ - CommandOutcome, ComputationError, InvalidPluginEntity, InvalidPluginsList, PluginEntity, - PluginRunData, PluginType, PluginsList, PluginsPathsList, + InvalidPluginEntity, InvalidPluginsList, NativeError, PluginEntity, PluginRunData, PluginType, + PluginsList, PluginsPathsList, }; use tokio::sync::RwLock; /// Initialize the plugin manager loading all the plugins from their directory. -pub async fn load_manager() -> Result { - PluginsManager::load() - .await - .map_err(|err| ComputationError::NativeError(err.into())) +pub async fn load_manager() -> Result { + PluginsManager::load().await.map_err(|err| err.into()) } /// Get all information of installed plugins . pub async fn installed_plugins_list( plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result { let manager = plugins_manager.read().await; let installed_plugins = manager.installed_plugins().cloned().collect(); let plugins = PluginsList(installed_plugins); - Ok(CommandOutcome::Finished(plugins)) + Ok(plugins) } /// Get all information of invalid plugins . pub async fn invalid_plugins_list( plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result { let manager = plugins_manager.read().await; let invalid_plugins = manager.invalid_plugins().cloned().collect(); let plugins = InvalidPluginsList(invalid_plugins); - Ok(CommandOutcome::Finished(plugins)) + Ok(plugins) } /// Get the directory paths (considered ID) of installed plugins . pub async fn installed_plugins_paths( plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result { let manager = plugins_manager.read().await; let installed_paths: Vec<_> = manager @@ -57,14 +51,13 @@ pub async fn installed_plugins_paths( let plugins = PluginsPathsList(installed_paths); - Ok(CommandOutcome::Finished(plugins)) + Ok(plugins) } /// Get the directory paths (considered ID) of invalid plugins . pub async fn invalid_plugins_paths( plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result { let manager = plugins_manager.read().await; let invalid_paths: Vec<_> = manager @@ -74,37 +67,35 @@ pub async fn invalid_plugins_paths( let plugins = PluginsPathsList(invalid_paths); - Ok(CommandOutcome::Finished(plugins)) + Ok(plugins) } /// Get all info for the installed plugin with provided directory path (considered ID) pub async fn installed_plugins_info( plugin_path: String, plugins_manager: &RwLock, - _signal: Signal, -) -> Result>, ComputationError> { +) -> Result, NativeError> { let manager = plugins_manager.read().await; let plugin = manager .get_installed_plugin(&PathBuf::from(plugin_path)) .cloned(); - Ok(CommandOutcome::Finished(plugin)) + Ok(plugin) } /// Get all info for the invalid plugin with provided directory path (considered ID) pub async fn invalid_plugins_info( plugin_path: String, plugins_manager: &RwLock, - _signal: Signal, -) -> Result>, ComputationError> { +) -> Result, NativeError> { let manager = plugins_manager.read().await; let invalid_plug = manager .get_invalid_plugin(&PathBuf::from(plugin_path)) .cloned(); - Ok(CommandOutcome::Finished(invalid_plug)) + Ok(invalid_plug) } /// Retrieves runtime data for a plugin located at the specified path. @@ -121,30 +112,23 @@ pub async fn invalid_plugins_info( pub async fn get_plugin_run_data( plugin_path: String, plugins_manager: &RwLock, - _signal: Signal, -) -> Result>, ComputationError> { +) -> Result, NativeError> { let manager = plugins_manager.read().await; let invalid_plug = manager .get_plugin_run_data(PathBuf::from(plugin_path)) .cloned(); - Ok(CommandOutcome::Finished(invalid_plug)) + Ok(invalid_plug) } /// Reload plugins from the plugins directory. -pub async fn reload_plugins( - plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +pub async fn reload_plugins(plugins_manager: &RwLock) -> Result<(), NativeError> { let mut manager = plugins_manager.write().await; - manager - .reload() - .await - .map_err(|err| ComputationError::NativeError(err.into()))?; + manager.reload().await?; - Ok(CommandOutcome::Finished(())) + Ok(()) } /// Adds a plugin with the given directory path and the optional plugin type. @@ -156,16 +140,12 @@ pub async fn add_plugin( plugin_path: String, plugin_type: Option, plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result<(), NativeError> { let mut manager = plugins_manager.write().await; - manager - .add_plugin(plugin_path.into(), plugin_type) - .await - .map_err(|err| ComputationError::NativeError(err.into()))?; + manager.add_plugin(plugin_path.into(), plugin_type).await?; - Ok(CommandOutcome::Finished(())) + Ok(()) } /// Removes the plugin with the given directory path. @@ -174,14 +154,12 @@ pub async fn add_plugin( pub async fn remove_plugin( plugin_path: String, plugins_manager: &RwLock, - _signal: Signal, -) -> Result, ComputationError> { +) -> Result<(), NativeError> { let mut manager = plugins_manager.write().await; manager .remove_plugin(PathBuf::from(plugin_path).as_path()) - .await - .map_err(|err| ComputationError::NativeError(err.into()))?; + .await?; - Ok(CommandOutcome::Finished(())) + Ok(()) } diff --git a/application/apps/indexer/session/src/unbound/api.rs b/application/apps/indexer/session/src/unbound/api.rs index 4b51efaccc..dc646d8a18 100644 --- a/application/apps/indexer/session/src/unbound/api.rs +++ b/application/apps/indexer/session/src/unbound/api.rs @@ -209,133 +209,4 @@ impl UnboundSessionAPI { self.process_command(id, rx_results, Command::Sleep(ms, tx_results)) .await } - - /// Get all information of installed plugins . - pub async fn installed_plugins_list( - &self, - id: u64, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command(id, rx_results, Command::InstalledPluginsList(tx_results)) - .await - } - - /// Get all information of invalid plugins . - pub async fn invalid_plugins_list( - &self, - id: u64, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command(id, rx_results, Command::InvalidPluginsList(tx_results)) - .await - } - - /// Get the directory paths (considered ID) for installed plugins. - pub async fn installed_plugins_paths( - &self, - id: u64, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command(id, rx_results, Command::InstalledPluginsPaths(tx_results)) - .await - } - - /// Get the directory paths (considered ID) for invalid plugins. - pub async fn invalid_plugins_paths( - &self, - id: u64, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command(id, rx_results, Command::InvalidPluginsPaths(tx_results)) - .await - } - - /// Get all info for the installed plugin with provided directory path (considered ID) - pub async fn installed_plugin_info( - &self, - id: u64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command( - id, - rx_results, - Command::InstalledPluginInfo(plugin_path, tx_results), - ) - .await - } - - /// Get all info for the invalid plugin with provided directory path (considered ID) - pub async fn invalid_plugin_info( - &self, - id: u64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command( - id, - rx_results, - Command::InvalidPluginInfo(plugin_path, tx_results), - ) - .await - } - - /// Retrieves runtime data for a plugin located at the specified path. - pub async fn get_plugin_run_data( - &self, - id: u64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command( - id, - rx_results, - Command::PluginRunData(plugin_path, tx_results), - ) - .await - } - - /// Reload the plugin directory. - pub async fn reload_plugins( - &self, - id: u64, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command(id, rx_results, Command::ReloadPlugins(tx_results)) - .await - } - - /// Adds a plugin with the given directory path and optional plugin type. - pub async fn add_plugin( - &self, - id: u64, - plugin_path: String, - plugin_type: Option, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command( - id, - rx_results, - Command::AddPlugin(plugin_path, plugin_type, tx_results), - ) - .await - } - - /// Removes the plugin with the given directory path. - pub async fn remove_plugin( - &self, - id: u64, - plugin_path: String, - ) -> Result, stypes::ComputationError> { - let (tx_results, rx_results) = oneshot::channel(); - self.process_command( - id, - rx_results, - Command::RemovePlugin(plugin_path, tx_results), - ) - .await - } } diff --git a/application/apps/indexer/session/src/unbound/commands/mod.rs b/application/apps/indexer/session/src/unbound/commands/mod.rs index fc4762b79f..403f73e52b 100644 --- a/application/apps/indexer/session/src/unbound/commands/mod.rs +++ b/application/apps/indexer/session/src/unbound/commands/mod.rs @@ -3,7 +3,6 @@ mod checksum; mod dlt; mod file; mod folder; -pub mod plugins; mod process; mod regex; mod serial; @@ -12,8 +11,7 @@ mod sleep; mod someip; use crate::unbound::commands::someip::get_someip_statistic; -use plugins_host::plugins_manager::PluginsManager; -use tokio::sync::{RwLock, oneshot}; +use tokio::sync::oneshot; use super::signal::Signal; use log::{debug, error}; @@ -83,67 +81,6 @@ pub enum Command { i64, oneshot::Sender, stypes::ComputationError>>, ), - /// Get all information of the installed plugins . - InstalledPluginsList( - oneshot::Sender< - Result, stypes::ComputationError>, - >, - ), - /// Get all information of invalid plugins . - InvalidPluginsList( - oneshot::Sender< - Result, stypes::ComputationError>, - >, - ), - /// Get the directory paths (considered ID) for installed plugins. - InstalledPluginsPaths( - oneshot::Sender< - Result, stypes::ComputationError>, - >, - ), - /// Get the directory paths (considered ID) for invalid plugins. - InvalidPluginsPaths( - oneshot::Sender< - Result, stypes::ComputationError>, - >, - ), - /// Get all info for the installed plugin with provided directory path (considered ID) - InstalledPluginInfo( - String, - oneshot::Sender< - Result>, stypes::ComputationError>, - >, - ), - /// Get all info for the invalid plugin with provided directory path (considered ID) - InvalidPluginInfo( - String, - oneshot::Sender< - Result< - stypes::CommandOutcome>, - stypes::ComputationError, - >, - >, - ), - /// Retrieves runtime data for a plugin located at the specified path. - PluginRunData( - String, - oneshot::Sender< - Result>, stypes::ComputationError>, - >, - ), - /// Reload all the plugins from their directory. - ReloadPlugins(oneshot::Sender, stypes::ComputationError>>), - /// Adds a plugin with the given directory path and optional plugin type. - AddPlugin( - String, - Option, - oneshot::Sender, stypes::ComputationError>>, - ), - /// Removes the plugin with the given directory path. - RemovePlugin( - String, - oneshot::Sender, stypes::ComputationError>>, - ), } impl std::fmt::Display for Command { @@ -164,22 +101,12 @@ impl std::fmt::Display for Command { Command::GetSomeipStatistic(_, _) => "Getting someip statistic", Command::GetRegexError(_, _) => "Checking regex", Command::IsFileBinary(_, _) => "Checking if file is binary", - Command::InstalledPluginsList(..) => "Getting installed plugins", - Command::InvalidPluginsList(..) => "Getting invalid plugins", - Command::InstalledPluginsPaths(..) => "Getting installed plugins paths", - Command::InvalidPluginsPaths(..) => "Getting invaild plugins paths", - Command::InstalledPluginInfo(..) => "Getting installed plugin info", - Command::InvalidPluginInfo(..) => "Getting invalid plugin info", - Command::PluginRunData(..) => "Getting plugin run data", - Command::ReloadPlugins(..) => "Reloading plugins' information", - Command::AddPlugin(..) => "Adding plugin", - Command::RemovePlugin(..) => "Removing plugin", } ) } } -pub async fn process(command: Command, signal: Signal, plugins_manager: &RwLock) { +pub async fn process(command: Command, signal: Signal) { let cmd = command.to_string(); debug!("Processing command: {cmd}"); if match command { @@ -212,36 +139,6 @@ pub async fn process(command: Command, signal: Signal, plugins_manager: &RwLock< Command::CancelTest(a, b, tx) => tx .send(cancel_test::cancel_test(a, b, signal).await) .is_err(), - Command::InstalledPluginsList(tx) => tx - .send(plugins::installed_plugins_list(plugins_manager, signal).await) - .is_err(), - Command::InvalidPluginsList(tx) => tx - .send(plugins::invalid_plugins_list(plugins_manager, signal).await) - .is_err(), - Command::InstalledPluginsPaths(tx) => tx - .send(plugins::installed_plugins_paths(plugins_manager, signal).await) - .is_err(), - Command::InvalidPluginsPaths(tx) => tx - .send(plugins::invalid_plugins_paths(plugins_manager, signal).await) - .is_err(), - Command::InstalledPluginInfo(path, tx) => tx - .send(plugins::installed_plugins_info(path, plugins_manager, signal).await) - .is_err(), - Command::InvalidPluginInfo(path, tx) => tx - .send(plugins::invalid_plugins_info(path, plugins_manager, signal).await) - .is_err(), - Command::PluginRunData(path, tx) => tx - .send(plugins::get_plugin_run_data(path, plugins_manager, signal).await) - .is_err(), - Command::ReloadPlugins(tx) => tx - .send(plugins::reload_plugins(plugins_manager, signal).await) - .is_err(), - Command::AddPlugin(path, typ, tx) => tx - .send(plugins::add_plugin(path, typ, plugins_manager, signal).await) - .is_err(), - Command::RemovePlugin(path, tx) => tx - .send(plugins::remove_plugin(path, plugins_manager, signal).await) - .is_err(), } { error!("Fail to send response for command: {cmd}"); } @@ -262,16 +159,6 @@ pub fn err(command: Command, err: stypes::ComputationError) { Command::SerialPortsList(tx) => tx.send(Err(err)).is_err(), Command::IsFileBinary(_filepath, tx) => tx.send(Err(err)).is_err(), Command::CancelTest(_a, _b, tx) => tx.send(Err(err)).is_err(), - Command::InstalledPluginsList(tx) => tx.send(Err(err)).is_err(), - Command::InvalidPluginsList(tx) => tx.send(Err(err)).is_err(), - Command::InstalledPluginsPaths(tx) => tx.send(Err(err)).is_err(), - Command::InvalidPluginsPaths(tx) => tx.send(Err(err)).is_err(), - Command::InstalledPluginInfo(_, tx) => tx.send(Err(err)).is_err(), - Command::InvalidPluginInfo(_, tx) => tx.send(Err(err)).is_err(), - Command::PluginRunData(_, tx) => tx.send(Err(err)).is_err(), - Command::ReloadPlugins(tx) => tx.send(Err(err)).is_err(), - Command::AddPlugin(_, _, tx) => tx.send(Err(err)).is_err(), - Command::RemovePlugin(_, tx) => tx.send(Err(err)).is_err(), } { error!("Fail to send error response for command: {cmd}"); } diff --git a/application/apps/indexer/session/src/unbound/mod.rs b/application/apps/indexer/session/src/unbound/mod.rs index 2471e51350..409fce379f 100644 --- a/application/apps/indexer/session/src/unbound/mod.rs +++ b/application/apps/indexer/session/src/unbound/mod.rs @@ -12,12 +12,9 @@ use crate::{ }; use cleanup::cleanup_temp_dir; use log::{debug, error, warn}; -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; use tokio::{ - sync::{ - RwLock, - mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}, - }, + sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}, time::{Duration, timeout}, }; use tokio_util::sync::CancellationToken; @@ -46,13 +43,6 @@ impl UnboundSession { } pub async fn init(&mut self) -> Result<(), stypes::ComputationError> { - // TODO: Plugins manager is used temporally here in initial phase and we should consider - // moving it to its own module. Reasons: - // * It doesn't need parallelism for most of task. - // * It'll need different state and locking management for downloading plugins, Updating - // caches etc... - let plugins_manager = commands::plugins::load_manager().await?; - let plugins_manager = Arc::new(RwLock::new(plugins_manager)); let finished = self.finished.clone(); let mut rx = self .rx @@ -86,10 +76,9 @@ impl UnboundSession { jobs.insert(id, signal.clone()); UnboundSession::started(&progress, job.to_string(), &mut uuids, &id); let api = session_api.clone(); - let plugs_ref_clone = Arc::clone(&plugins_manager); tokio::spawn(async move { debug!("Job {job} has been called"); - commands::process(job, signal.clone(), plugs_ref_clone.as_ref()).await; + commands::process(job, signal.clone()).await; signal.confirm(); let _ = api.remove_command(id); }); From aa9af8cb94362e3964f33dee1bd586b2cf528779 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Tue, 1 Jul 2025 15:17:12 +0200 Subject: [PATCH 077/121] Components: Move plugins API calls in bindings API calls are part of component session instead of unbound session * RS-Bindings * TS-Bindings + Tests * Protocol --- .../apps/indexer/stypes/src/plugins/nodejs.rs | 1 + application/apps/protocol/src/lib.rs | 8 +- .../rs-bindings/src/js/components/mod.rs | 117 ++++++++ .../rustcore/rs-bindings/src/js/jobs/mod.rs | 128 --------- .../ts-bindings/spec/session.protocol.spec.ts | 7 - .../ts-bindings/src/api/components.ts | 49 ++++ .../apps/rustcore/ts-bindings/src/api/jobs.ts | 170 ------------ .../ts-bindings/src/interfaces/errors.ts | 1 + .../src/native/native.components.ts | 251 ++++++++++++++++++ .../ts-bindings/src/native/native.jobs.ts | 13 - 10 files changed, 420 insertions(+), 325 deletions(-) diff --git a/application/apps/indexer/stypes/src/plugins/nodejs.rs b/application/apps/indexer/stypes/src/plugins/nodejs.rs index 575b3c23b4..a5802be10e 100644 --- a/application/apps/indexer/stypes/src/plugins/nodejs.rs +++ b/application/apps/indexer/stypes/src/plugins/nodejs.rs @@ -19,3 +19,4 @@ try_into_js!(ParserRenderOptions); try_into_js!(PluginsList); try_into_js!(InvalidPluginsList); try_into_js!(PluginsPathsList); +try_into_js!(PluginRunData); diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 84048a05d7..74ed010a1c 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -107,7 +107,7 @@ mod generate; pub(crate) use err::*; pub(crate) use serde::Serialize; -pub(crate) use serde_wasm_bindgen::{from_value, Serializer}; +pub(crate) use serde_wasm_bindgen::{Serializer, from_value}; pub(crate) use stypes::*; pub(crate) use wasm_bindgen::prelude::*; @@ -152,12 +152,6 @@ gen_encode_decode_fns!(ColumnInfo); gen_encode_decode_fns!(PluginsList); gen_encode_decode_fns!(InvalidPluginsList); gen_encode_decode_fns!(PluginsPathsList); -gen_encode_decode_fns!(CommandOutcome); -gen_encode_decode_fns!(CommandOutcome); -gen_encode_decode_fns!(CommandOutcome); -gen_encode_decode_fns!(CommandOutcome>); -gen_encode_decode_fns!(CommandOutcome>); -gen_encode_decode_fns!(CommandOutcome>); gen_encode_decode_fns!(CommandOutcome); gen_encode_decode_fns!(CommandOutcome); gen_encode_decode_fns!(CommandOutcome); diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 897a7d2eb8..01d630b0c0 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -319,4 +319,121 @@ impl Components { .await .map_err(stypes::ComputationError::NativeError) } + + #[node_bindgen] + async fn installed_plugins_list( + &self, + ) -> Result { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .installed_plugins_list() + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn invalid_plugins_list( + &self, + ) -> Result { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .invalid_plugins_list() + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn installed_plugins_paths( + &self, + ) -> Result { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .installed_plugins_paths() + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn invalid_plugins_paths( + &self, + ) -> Result { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .invalid_plugins_paths() + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn installed_plugin_info( + &self, + plugin_path: String, + ) -> Result, stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .installed_plugin_info(plugin_path) + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn invalid_plugin_info( + &self, + plugin_path: String, + ) -> Result, stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .invalid_plugin_info(plugin_path) + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn get_plugin_run_data( + &self, + plugin_path: String, + ) -> Result, stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .get_plugin_run_data(plugin_path) + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn reload_plugins(&self) -> Result<(), stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .reload_plugins() + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn add_plugin(&self, plugin_path: String) -> Result<(), stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .add_plugin(plugin_path, None) + .await + .map_err(stypes::ComputationError::NativeError) + } + + #[node_bindgen] + async fn remove_plugin(&self, plugin_path: String) -> Result<(), stypes::ComputationError> { + self.session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)? + .remove_plugin(plugin_path) + .await + .map_err(stypes::ComputationError::NativeError) + } } diff --git a/application/apps/rustcore/rs-bindings/src/js/jobs/mod.rs b/application/apps/rustcore/rs-bindings/src/js/jobs/mod.rs index 95c1f4bcda..73f9c9c971 100644 --- a/application/apps/rustcore/rs-bindings/src/js/jobs/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/jobs/mod.rs @@ -220,134 +220,6 @@ impl UnboundJobs { .await } - #[node_bindgen] - async fn installed_plugins_list( - &self, - id: i64, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .installed_plugins_list(u64_from_i64(id)?) - .await - } - - #[node_bindgen] - async fn invalid_plugins_list( - &self, - id: i64, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .invalid_plugins_list(u64_from_i64(id)?) - .await - } - - #[node_bindgen] - async fn installed_plugins_paths( - &self, - id: i64, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .installed_plugins_paths(u64_from_i64(id)?) - .await - } - - #[node_bindgen] - async fn invalid_plugins_paths( - &self, - id: i64, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .invalid_plugins_paths(u64_from_i64(id)?) - .await - } - - #[node_bindgen] - async fn installed_plugin_info( - &self, - id: i64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .installed_plugin_info(u64_from_i64(id)?, plugin_path) - .await - } - - #[node_bindgen] - async fn invalid_plugin_info( - &self, - id: i64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .invalid_plugin_info(u64_from_i64(id)?, plugin_path) - .await - } - - #[node_bindgen] - async fn get_plugin_run_data( - &self, - id: i64, - plugin_path: String, - ) -> Result>, stypes::ComputationError> - { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .get_plugin_run_data(u64_from_i64(id)?, plugin_path) - .await - } - - #[node_bindgen] - async fn reload_plugins( - &self, - id: i64, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .reload_plugins(u64_from_i64(id)?) - .await - } - - #[node_bindgen] - async fn add_plugin( - &self, - id: i64, - plugin_path: String, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .add_plugin(u64_from_i64(id)?, plugin_path, None) - .await - } - - #[node_bindgen] - async fn remove_plugin( - &self, - id: i64, - plugin_path: String, - ) -> Result, stypes::ComputationError> { - self.api - .as_ref() - .ok_or(stypes::ComputationError::SessionUnavailable)? - .remove_plugin(u64_from_i64(id)?, plugin_path) - .await - } - #[node_bindgen] async fn job_cancel_test( &self, diff --git a/application/apps/rustcore/ts-bindings/spec/session.protocol.spec.ts b/application/apps/rustcore/ts-bindings/spec/session.protocol.spec.ts index 002da5e7d8..6efeb034e8 100644 --- a/application/apps/rustcore/ts-bindings/spec/session.protocol.spec.ts +++ b/application/apps/rustcore/ts-bindings/spec/session.protocol.spec.ts @@ -134,13 +134,6 @@ const MAP: { [key: string]: (buf: Uint8Array) => any } = { PluginsList: protocol.decodePluginsList, InvalidPluginsList: protocol.decodeInvalidPluginsList, PluginsPathsList: protocol.decodePluginsPathsList, - CommandOutcome_PluginsList: protocol.decodeCommandOutcomeWithPluginsList, - CommandOutcome_InvalidPluginsList: protocol.decodeCommandOutcomeWithInvalidPluginsList, - CommandOutcome_PluginsPathsList: protocol.decodeCommandOutcomeWithPluginsPathsList, - CommandOutcome_Option_PluginEntity: protocol.decodeCommandOutcomeWithOptionPluginEntity, - CommandOutcome_Option_InvalidPluginEntity: - protocol.decodeCommandOutcomeWithOptionInvalidPluginEntity, - CommandOutcome_Option_PluginRunData: protocol.decodeCommandOutcomeWithOptionPluginRunData, }; const OUTPUT_PATH_ENVVAR = 'CHIPMUNK_PROTOCOL_TEST_OUTPUT'; diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 19fef5a670..6b4d51a986 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -14,6 +14,15 @@ import { SessionAction, } from 'platform/types/bindings'; +import { + InvalidPluginEntity, + InvalidPluginsList, + PluginEntity, + PluginsList, + PluginsPathsList, + PluginRunData, +} from 'platform/types/bindings/plugins'; + export class Components extends Subscriber { private readonly native: Native; private readonly provider: ComponentsEventProvider; @@ -99,4 +108,44 @@ export class Components extends Subscriber { public abort(fields: string[]): Error | undefined { return this.native.abort(fields); } + + public installedPluginsList(): Promise { + return this.native.installedPluginsList(); + } + + public invalidPluginsList(): Promise { + return this.native.invalidPluginsList(); + } + + public installedPluginsPaths(): Promise { + return this.native.installedPluginsPaths(); + } + + public invalidPluginsPaths(): Promise { + return this.native.invalidPluginsPaths(); + } + + public installedPluginsInfo(plugin_path: string): Promise { + return this.native.installedPluginsInfo(plugin_path); + } + + public invalidPluginsInfo(plugin_path: string): Promise { + return this.native.invalidPluginsInfo(plugin_path); + } + + public getPluginRunData(plugin_path: string): Promise { + return this.native.getPluginRunData(plugin_path); + } + + public reloadPlugins(): Promise { + return this.native.reloadPlugins(); + } + + public addPlugin(plugin_path: string): Promise { + return this.native.addPlugin(plugin_path); + } + + public removePlugin(plugin_path: string): Promise { + return this.native.removePlugin(plugin_path); + } } diff --git a/application/apps/rustcore/ts-bindings/src/api/jobs.ts b/application/apps/rustcore/ts-bindings/src/api/jobs.ts index 1c9da55635..4095fee5cd 100644 --- a/application/apps/rustcore/ts-bindings/src/api/jobs.ts +++ b/application/apps/rustcore/ts-bindings/src/api/jobs.ts @@ -10,14 +10,6 @@ import { ProfileList, MapKeyValue, } from 'platform/types/bindings'; -import { - InvalidPluginEntity, - InvalidPluginsList, - PluginEntity, - PluginsList, - PluginsPathsList, - PluginRunData, -} from 'platform/types/bindings/plugins'; import * as protocol from 'protocol'; @@ -248,166 +240,4 @@ export class Jobs extends Base { ); return job; } - - public installedPluginsList(): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): PluginEntity[] | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithPluginsList, - ); - return decoded; - }, - this.native.installedPluginsList(sequence), - sequence, - 'installedPluginsList', - ); - return job; - } - - public invalidPluginsList(): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): InvalidPluginEntity[] | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithInvalidPluginsList, - ); - return decoded; - }, - this.native.invalidPluginsList(sequence), - sequence, - 'invalidPluginsList', - ); - return job; - } - - public installedPluginsPaths(): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): string[] | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithPluginsPathsList, - ); - return decoded; - }, - this.native.installedPluginsPaths(sequence), - sequence, - 'installedPluginsPaths', - ); - return job; - } - - public invalidPluginsPaths(): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): string[] | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithPluginsPathsList, - ); - return decoded; - }, - this.native.invalidPluginsPaths(sequence), - sequence, - 'invalidPluginsPaths', - ); - return job; - } - - public installedPluginsInfo(plugin_path: string): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): PluginEntity | undefined | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithOptionPluginEntity, - ); - return decoded; - }, - this.native.installedPluginsInfo(sequence, plugin_path), - sequence, - 'installedPluginsInfo', - ); - return job; - } - - public invalidPluginsInfo( - plugin_path: string, - ): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): InvalidPluginEntity | undefined | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithOptionInvalidPluginEntity, - ); - return decoded; - }, - this.native.invalidPluginsInfo(sequence, plugin_path), - sequence, - 'invalidPluginsInfo', - ); - return job; - } - - public getPluginRunData(plugin_path: string): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): PluginRunData | undefined | Error => { - const decoded = decode( - buf, - protocol.decodeCommandOutcomeWithOptionPluginRunData, - ); - return decoded; - }, - this.native.getPluginRunData(sequence, plugin_path), - sequence, - 'getPluginRunData', - ); - return job; - } - - public reloadPlugins(): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): any | Error => { - return decode(buf, protocol.decodeCommandOutcomeWithVoid); - }, - this.native.reloadPlugins(sequence), - sequence, - 'reloadPlugins', - ); - return job; - } - - public addPlugin(plugin_path: string): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): any | Error => { - return decode(buf, protocol.decodeCommandOutcomeWithVoid); - }, - this.native.addPlugin(sequence, plugin_path), - sequence, - 'addPlugin', - ); - - return job; - } - - public removePlugin(plugin_path: string): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): any | Error => { - return decode(buf, protocol.decodeCommandOutcomeWithVoid); - }, - this.native.removePlugin(sequence, plugin_path), - sequence, - `removePlugin`, - ); - - return job; - } } diff --git a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts index 950a669bb9..d57af3dcb6 100644 --- a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts +++ b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts @@ -78,6 +78,7 @@ export enum Source { ComponentsValidate = 'ComponentsValidate', IdentList = 'IdentList', Native = 'Native', + Plugins = 'Plugins', Other = 'Other', } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index c59c7ae068..30461a9155 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -22,6 +22,15 @@ import { IComponentsEventsInterfaces, } from '../api/components.provider'; +import { + InvalidPluginEntity, + InvalidPluginsList, + PluginEntity, + PluginsList, + PluginsPathsList, + PluginRunData, +} from 'platform/types/bindings/plugins'; + import * as protocol from 'protocol'; export abstract class ComponentsNative { @@ -40,6 +49,26 @@ export abstract class ComponentsNative { public abstract validate(origin: Uint8Array, fields: Uint8Array): Promise; public abstract abort(fields: string[]): void; + + public abstract installedPluginsList(): Promise; + + public abstract invalidPluginsList(): Promise; + + public abstract installedPluginsPaths(): Promise; + + public abstract invalidPluginsPaths(): Promise; + + public abstract installedPluginsInfo(plugin_path: string): Promise; + + public abstract invalidPluginsInfo(plugin_path: string): Promise; + + public abstract getPluginRunData(plugin_path: string): Promise; + + public abstract reloadPlugins(): Promise; + + public abstract addPlugin(plugin_path: string): Promise; + + public abstract removePlugin(plugin_path: string): Promise; } const DESTROY_TIMEOUT = 5000; @@ -325,4 +354,226 @@ export class Base extends Subscriber { return undefined; } } + + public installedPluginsList(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native.installedPluginsList().then((buf: Uint8Array) => { + try { + resolve(protocol.decodePluginsList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public invalidPluginsList(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.invalidPluginsList().then((buf: Uint8Array) => { + try { + resolve(protocol.decodeInvalidPluginsList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public installedPluginsPaths(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.installedPluginsPaths().then((buf: Uint8Array) => { + try { + resolve(protocol.decodePluginsPathsList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public invalidPluginsPaths(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.invalidPluginsPaths().then((buf: Uint8Array) => { + try { + resolve(protocol.decodePluginsPathsList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public installedPluginsInfo(plugin_path: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.installedPluginsInfo(plugin_path).then((buf: Uint8Array | undefined) => { + if (!buf) { + return resolve(undefined); + } + try { + resolve(protocol.decodePluginEntity(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public invalidPluginsInfo(plugin_path: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.invalidPluginsInfo(plugin_path).then((buf: Uint8Array | undefined) => { + if (!buf) { + return resolve(undefined); + } + try { + resolve(protocol.decodeInvalidPluginEntity(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public getPluginRunData(plugin_path: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.getPluginRunData(plugin_path).then((buf: Uint8Array | undefined) => { + if (!buf) { + return resolve(undefined); + } + try { + resolve(protocol.decodePluginRunData(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error(`Fail to decode message: ${utils.error(err)}`), + ), + Type.InvalidOutput, + Source.Plugins, + ), + ); + } + }); + }); + } + + public reloadPlugins(): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.reloadPlugins().catch((err: Error) => { + reject(err); + }); + }); + } + + public addPlugin(plugin_path: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.addPlugin(plugin_path).catch((err: Error) => { + reject(err); + }); + }); + } + + public removePlugin(plugin_path: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + + return new Promise((resolve, reject) => { + this.native.removePlugin(plugin_path).catch((err: Error) => { + reject(err); + }); + }); + } } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.jobs.ts b/application/apps/rustcore/ts-bindings/src/native/native.jobs.ts index 354e1a6574..0904a6b71f 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.jobs.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.jobs.ts @@ -50,19 +50,6 @@ export abstract class JobsNative { is_word: boolean; }, ): Promise; - public abstract installedPluginsList(sequence: number): Promise; - public abstract invalidPluginsList(sequence: number): Promise; - public abstract installedPluginsPaths(sequence: number): Promise; - public abstract invalidPluginsPaths(sequence: number): Promise; - public abstract installedPluginsInfo( - sequence: number, - plugin_path: string, - ): Promise; - public abstract invalidPluginsInfo(sequence: number, plugin_path: string): Promise; - public abstract getPluginRunData(sequence: number, plugin_path: string): Promise; - public abstract reloadPlugins(sequence: number): Promise; - public abstract addPlugin(sequence: number, plugin_path: string): Promise; - public abstract removePlugin(sequence: number, plugin_path: string): Promise; } interface Job { From ef6350658177895ebdb99e76542f159bef3898f4 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Tue, 1 Jul 2025 16:45:14 +0200 Subject: [PATCH 078/121] Components: Integrate Plugins in Electron --- application/holder/src/service/components.ts | 70 +++++++++++++++ .../holder/src/service/components/index.ts | 1 + .../plugins/add_plugin.ts | 4 +- .../{unbound => components}/plugins/index.ts | 0 .../plugins/installed_plugin_info.ts | 4 +- .../plugins/invalid_plugin_info.ts | 4 +- .../plugins/list_installed.ts | 4 +- .../plugins/list_installed_paths.ts | 4 +- .../plugins/list_invalid.ts | 4 +- .../plugins/list_invalid_paths.ts | 4 +- .../{unbound => components}/plugins/reload.ts | 4 +- .../plugins/remove_plugin.ts | 4 +- .../plugins/rundata.ts | 4 +- application/holder/src/service/unbound.ts | 90 ------------------- .../holder/src/service/unbound/index.ts | 1 - 15 files changed, 91 insertions(+), 111 deletions(-) rename application/holder/src/service/{unbound => components}/plugins/add_plugin.ts (89%) rename application/holder/src/service/{unbound => components}/plugins/index.ts (100%) rename application/holder/src/service/{unbound => components}/plugins/installed_plugin_info.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/invalid_plugin_info.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/list_installed.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/list_installed_paths.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/list_invalid.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/list_invalid_paths.ts (90%) rename application/holder/src/service/{unbound => components}/plugins/reload.ts (89%) rename application/holder/src/service/{unbound => components}/plugins/remove_plugin.ts (89%) rename application/holder/src/service/{unbound => components}/plugins/rundata.ts (90%) diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index c980dd0563..23304d4607 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -78,6 +78,76 @@ export class Service extends Implementation { Requests.Components.Validate.Request, RequestHandlers.Validate.handler, ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.ListInstalled.Request, + RequestHandlers.Plugins.ListInstalled.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.ListInvalid.Request, + RequestHandlers.Plugins.ListInvalid.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.ListInstalledPaths.Request, + RequestHandlers.Plugins.ListInstalledPaths.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.ListInvalidPaths.Request, + RequestHandlers.Plugins.ListInvalidPaths.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.InstalledPluginInfo.Request, + RequestHandlers.Plugins.InstalledPluginInfo.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.InvalidPluginInfo.Request, + RequestHandlers.Plugins.InvalidPluginInfo.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.PluginRunData.Request, + RequestHandlers.Plugins.PluginRunData.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.Reload.Request, + RequestHandlers.Plugins.Relaod.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.AddPlugin.Request, + RequestHandlers.Plugins.AddPlugin.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Plugins.RemovePlugin.Request, + RequestHandlers.Plugins.RemovePlugin.handler, + ), this.components.getEvents().LoadingDone.subscribe((event: LoadingDoneEvent) => { Events.IpcEvent.emit( new Events.Components.LoadingDone.Event({ diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts index 628a27ac52..fdecafd385 100644 --- a/application/holder/src/service/components/index.ts +++ b/application/holder/src/service/components/index.ts @@ -5,3 +5,4 @@ export * as GetOutputRender from './get_output_render'; export * as GetIdent from './get_ident'; export * as Abort from './abort'; export * as Validate from './validate'; +export * as Plugins from './plugins'; diff --git a/application/holder/src/service/unbound/plugins/add_plugin.ts b/application/holder/src/service/components/plugins/add_plugin.ts similarity index 89% rename from application/holder/src/service/unbound/plugins/add_plugin.ts rename to application/holder/src/service/components/plugins/add_plugin.ts index 7c75f5d614..b47d7a8683 100644 --- a/application/holder/src/service/unbound/plugins/add_plugin.ts +++ b/application/holder/src/service/components/plugins/add_plugin.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< request: Requests.Plugins.AddPlugin.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .addPlugin(request.pluginPath) .then(() => { reslove(new Requests.Plugins.AddPlugin.Response({})); diff --git a/application/holder/src/service/unbound/plugins/index.ts b/application/holder/src/service/components/plugins/index.ts similarity index 100% rename from application/holder/src/service/unbound/plugins/index.ts rename to application/holder/src/service/components/plugins/index.ts diff --git a/application/holder/src/service/unbound/plugins/installed_plugin_info.ts b/application/holder/src/service/components/plugins/installed_plugin_info.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/installed_plugin_info.ts rename to application/holder/src/service/components/plugins/installed_plugin_info.ts index c5bf5b4192..9f91f12dcf 100644 --- a/application/holder/src/service/unbound/plugins/installed_plugin_info.ts +++ b/application/holder/src/service/components/plugins/installed_plugin_info.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< request: Requests.Plugins.InstalledPluginInfo.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .installedPluginsInfo(request.pluginPath) .then((plugin) => { reslove(new Requests.Plugins.InstalledPluginInfo.Response({ plugin })); diff --git a/application/holder/src/service/unbound/plugins/invalid_plugin_info.ts b/application/holder/src/service/components/plugins/invalid_plugin_info.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/invalid_plugin_info.ts rename to application/holder/src/service/components/plugins/invalid_plugin_info.ts index 937b4f9728..47ceb1c372 100644 --- a/application/holder/src/service/unbound/plugins/invalid_plugin_info.ts +++ b/application/holder/src/service/components/plugins/invalid_plugin_info.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< request: Requests.Plugins.InvalidPluginInfo.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .invalidPluginsInfo(request.pluginPath) .then((invalidPlugin) => { reslove(new Requests.Plugins.InvalidPluginInfo.Response({ invalidPlugin })); diff --git a/application/holder/src/service/unbound/plugins/list_installed.ts b/application/holder/src/service/components/plugins/list_installed.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/list_installed.ts rename to application/holder/src/service/components/plugins/list_installed.ts index 0b376cf101..9c7ab85df7 100644 --- a/application/holder/src/service/unbound/plugins/list_installed.ts +++ b/application/holder/src/service/components/plugins/list_installed.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< _request: Requests.Plugins.ListInstalled.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .installedPluginsList() .then((plugins) => { reslove(new Requests.Plugins.ListInstalled.Response({ plugins })); diff --git a/application/holder/src/service/unbound/plugins/list_installed_paths.ts b/application/holder/src/service/components/plugins/list_installed_paths.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/list_installed_paths.ts rename to application/holder/src/service/components/plugins/list_installed_paths.ts index 07ca8639d3..0edf3689d8 100644 --- a/application/holder/src/service/unbound/plugins/list_installed_paths.ts +++ b/application/holder/src/service/components/plugins/list_installed_paths.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< _request: Requests.Plugins.ListInstalledPaths.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .installedPluginsPaths() .then((paths) => { reslove(new Requests.Plugins.ListInstalledPaths.Response({ paths })); diff --git a/application/holder/src/service/unbound/plugins/list_invalid.ts b/application/holder/src/service/components/plugins/list_invalid.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/list_invalid.ts rename to application/holder/src/service/components/plugins/list_invalid.ts index b48945c75c..84cedcecef 100644 --- a/application/holder/src/service/unbound/plugins/list_invalid.ts +++ b/application/holder/src/service/components/plugins/list_invalid.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< _request: Requests.Plugins.ListInvalid.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .invalidPluginsList() .then((invalidPlugins) => { reslove(new Requests.Plugins.ListInvalid.Response({ invalidPlugins })); diff --git a/application/holder/src/service/unbound/plugins/list_invalid_paths.ts b/application/holder/src/service/components/plugins/list_invalid_paths.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/list_invalid_paths.ts rename to application/holder/src/service/components/plugins/list_invalid_paths.ts index 33c91c2913..8e179cdd62 100644 --- a/application/holder/src/service/unbound/plugins/list_invalid_paths.ts +++ b/application/holder/src/service/components/plugins/list_invalid_paths.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< _request: Requests.Plugins.ListInvalidPaths.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .invalidPluginsPaths() .then((paths) => { reslove(new Requests.Plugins.ListInvalidPaths.Response({ paths })); diff --git a/application/holder/src/service/unbound/plugins/reload.ts b/application/holder/src/service/components/plugins/reload.ts similarity index 89% rename from application/holder/src/service/unbound/plugins/reload.ts rename to application/holder/src/service/components/plugins/reload.ts index efcc7a2a0b..e212c7426a 100644 --- a/application/holder/src/service/unbound/plugins/reload.ts +++ b/application/holder/src/service/components/plugins/reload.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< _request: Requests.Plugins.Reload.Request, ): CancelablePromise => { return new CancelablePromise((resolve, reject) => { - unbound.jobs + components.components .reloadPlugins() .then(() => { resolve(new Requests.Plugins.Reload.Response({})); diff --git a/application/holder/src/service/unbound/plugins/remove_plugin.ts b/application/holder/src/service/components/plugins/remove_plugin.ts similarity index 89% rename from application/holder/src/service/unbound/plugins/remove_plugin.ts rename to application/holder/src/service/components/plugins/remove_plugin.ts index b3893d4883..a9faa96c09 100644 --- a/application/holder/src/service/unbound/plugins/remove_plugin.ts +++ b/application/holder/src/service/components/plugins/remove_plugin.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< request: Requests.Plugins.RemovePlugin.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .removePlugin(request.pluginPath) .then(() => { reslove(new Requests.Plugins.RemovePlugin.Response({})); diff --git a/application/holder/src/service/unbound/plugins/rundata.ts b/application/holder/src/service/components/plugins/rundata.ts similarity index 90% rename from application/holder/src/service/unbound/plugins/rundata.ts rename to application/holder/src/service/components/plugins/rundata.ts index a8d9ea88e9..4b6288533a 100644 --- a/application/holder/src/service/unbound/plugins/rundata.ts +++ b/application/holder/src/service/components/plugins/rundata.ts @@ -1,6 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; -import { unbound } from '@service/unbound'; +import { components } from '@service/components'; import * as Requests from 'platform/ipc/request'; @@ -13,7 +13,7 @@ export const handler = Requests.InjectLogger< request: Requests.Plugins.PluginRunData.Request, ): CancelablePromise => { return new CancelablePromise((reslove, reject) => { - unbound.jobs + components.components .getPluginRunData(request.pluginPath) .then((data) => { reslove(new Requests.Plugins.PluginRunData.Response({ data })); diff --git a/application/holder/src/service/unbound.ts b/application/holder/src/service/unbound.ts index f101b40db2..a09f35c13b 100644 --- a/application/holder/src/service/unbound.ts +++ b/application/holder/src/service/unbound.ts @@ -83,96 +83,6 @@ export class Service extends Implementation { RequestHandlers.Serial.Ports.handler, ), ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.ListInstalled.Request, - RequestHandlers.Plugins.ListInstalled.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.ListInvalid.Request, - RequestHandlers.Plugins.ListInvalid.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.ListInstalledPaths.Request, - RequestHandlers.Plugins.ListInstalledPaths.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.ListInvalidPaths.Request, - RequestHandlers.Plugins.ListInvalidPaths.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.InstalledPluginInfo.Request, - RequestHandlers.Plugins.InstalledPluginInfo.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.InvalidPluginInfo.Request, - RequestHandlers.Plugins.InvalidPluginInfo.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.PluginRunData.Request, - RequestHandlers.Plugins.PluginRunData.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.Reload.Request, - RequestHandlers.Plugins.Relaod.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.AddPlugin.Request, - RequestHandlers.Plugins.AddPlugin.handler, - ), - ); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Plugins.RemovePlugin.Request, - RequestHandlers.Plugins.RemovePlugin.handler, - ), - ); return Promise.resolve(); } diff --git a/application/holder/src/service/unbound/index.ts b/application/holder/src/service/unbound/index.ts index 6d30af468a..b688947940 100644 --- a/application/holder/src/service/unbound/index.ts +++ b/application/holder/src/service/unbound/index.ts @@ -3,4 +3,3 @@ export * as Os from './os'; export * as File from './file'; export * as Serial from './serial'; export * as Someip from './someip'; -export * as Plugins from './plugins'; From 42ef787e419c201819c153c6964308b64804cb55 Mon Sep 17 00:00:00 2001 From: Ammar Abou Zor Date: Thu, 3 Jul 2025 17:37:04 +0200 Subject: [PATCH 079/121] Components: Plugins Integrations in system * Implement descriptor for plugins in plugins manager. * Some workarounds were added to the code to avoid circular references between `plugins_manager` and `parsers` crates. --- application/apps/indexer/parsers/src/lib.rs | 7 +- .../apps/indexer/plugins_host/Cargo.toml | 2 + .../plugins_host/src/parser_shared/mod.rs | 22 -- .../src/plugins_manager/descriptor.rs | 210 ++++++++++++++++++ .../plugins_host/src/plugins_manager/mod.rs | 22 ++ .../indexer/session/src/components/mod.rs | 5 +- .../apps/rustcore/rs-bindings/Cargo.lock | 1 + 7 files changed, 245 insertions(+), 24 deletions(-) create mode 100644 application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index e00cfcfe4e..30ccb801b1 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,7 +1,7 @@ // #![deny(unused_crate_dependencies)] use components::Components; -use definitions::{LogRecordOutput, ParseReturnIterator, ParserError}; +use definitions::ParseReturnIterator; pub mod dlt; pub mod prelude; pub mod someip; @@ -12,6 +12,10 @@ pub enum Parser { Dlt(dlt::DltParser), SomeIp(someip::SomeipParser), Text(text::StringTokenizer), + // NOTE: We can't reference plugins parser directly because of circular + // references between parsers and plugins_host library. + // TODO AAZ: This is a workaround until we find a proper solution. + Plugin(Box), } /** @@ -27,6 +31,7 @@ impl definitions::Parser for Parser { Self::DltRaw(inst) => inst.parse(input, timestamp), Self::SomeIp(inst) => inst.parse(input, timestamp), Self::Text(inst) => inst.parse(input, timestamp), + Self::Plugin(inst) => inst.parse(input, timestamp), } } } diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index 3ecc7b5261..9e0526a402 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -18,6 +18,8 @@ wasmtime = "33.0" wasmtime-wasi = "33.0" uuid = { workspace = true , features = ["serde", "v4"] } components = { path = "../components" } +# TODO AAZ: Remove when `DescriptorGetter::create` function is async. +tokio-util.workspace = true parsers = { path = "../parsers" } sources = { path = "../sources" } stypes = { path = "../stypes" } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index d664095f3c..4b6983e038 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -2,7 +2,6 @@ use std::path::{Path, PathBuf}; use wasmtime::component::Component; -use components::ComponentDescriptor; use definitions::{self as defs, ParseReturnIterator}; use stypes::{PluginInfo, SemanticVersion}; @@ -177,24 +176,3 @@ impl defs::Parser for PluginsParser { res } } - -#[derive(Default)] -struct Descriptor {} - -impl ComponentDescriptor for Descriptor { - fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { - true - } - /// **ATTANTION** That's placeholder. Should be another way to delivery data - fn ident(&self) -> stypes::Ident { - stypes::Ident { - name: String::from("Plugin"), - desc: String::from("Plugin"), - io: stypes::IODataType::Any, - uuid: uuid::Uuid::new_v4(), - } - } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser - } -} diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs new file mode 100644 index 0000000000..c356553d66 --- /dev/null +++ b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs @@ -0,0 +1,210 @@ +use components::{ComponentDescriptor, ComponentFactory}; +use stypes::{ + Field, NativeError, NativeErrorKind, PluginConfigItem, PluginEntity, PluginParserSettings, + SessionAction, Severity, StaticFieldDesc, +}; +use tokio::runtime::Handle; +use tokio_util::sync::CancellationToken; + +use crate::PluginsParser; + +#[derive(Debug)] +pub struct PluginDescriptor { + entity: PluginEntity, +} + +impl PluginDescriptor { + pub fn new(entity: PluginEntity) -> Self { + Self { entity } + } +} + +impl ComponentDescriptor for PluginDescriptor { + fn ident(&self) -> stypes::Ident { + let desc = self + .entity + .metadata + .description + .as_ref() + .map_or_else(|| String::from("Parser Plugin"), |desc| desc.to_owned()); + stypes::Ident { + name: self.entity.metadata.title.to_owned(), + desc, + io: stypes::IODataType::Any, + uuid: uuid::Uuid::new_v4(), + } + } + + fn ty(&self) -> stypes::ComponentType { + match self.entity.plugin_type { + stypes::PluginType::Parser => stypes::ComponentType::Parser, + stypes::PluginType::ByteSource => stypes::ComponentType::Source, + } + } + + fn get_render(&self) -> Option { + // TODO AAZ: API on plugins should change to match this changes. + match &self.entity.info.render_options { + stypes::RenderOptions::Parser(parser_render_options) => { + if let Some(opts) = parser_render_options.columns_options.as_ref() { + let cols: Vec<_> = opts + .columns + .iter() + .map(|col| (col.caption.to_owned(), col.width as usize)) + .collect(); + Some(stypes::OutputRender::Columns(cols)) + } else { + Some(stypes::OutputRender::PlaitText) + } + } + stypes::RenderOptions::ByteSource => None, + } + } + + fn bound_ident( + &self, + _origin: &stypes::SessionAction, + _fields: &[stypes::Field], + ) -> stypes::Ident { + self.ident() + } + + fn is_ty(&self, ty: &stypes::ComponentType) -> bool { + &self.ty() == ty + } + + fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + let fields: Vec<_> = self + .entity + .info + .config_schemas + .iter() + .map(|conf| { + use stypes::PluginConfigSchemaType as SchType; + use stypes::ValueInput as InType; + let interface = match &conf.input_type { + SchType::Boolean(bool) => InType::Checkbox(*bool), + SchType::Integer(num) => InType::Number(*num as i64), + //TODO AAZ: Clarify why we don't have float numbers. + SchType::Float(num) => InType::Number(*num as i64), + SchType::Text(def_txt) => InType::String(def_txt.to_owned(), String::new()), + SchType::Directories => InType::Directories, + SchType::Files(items) => InType::Files(items.to_vec()), + SchType::Dropdown(items) => { + InType::Strings(items.0.to_owned(), items.1.to_owned()) + } + }; + let static_desc = StaticFieldDesc { + id: conf.id.to_owned(), + name: conf.title.to_owned(), + desc: conf + .description + .as_ref() + .map_or_else(|| String::new(), |d| d.to_owned()), + //TODO AAZ: Map this to plugins API + required: true, + interface, + binding: None, + }; + stypes::FieldDesc::Static(static_desc) + }) + .collect(); + + Ok(fields) + } + + fn lazy_fields_getter( + &self, + _origin: stypes::SessionAction, + _cancel: CancellationToken, + ) -> components::LazyFieldsTask { + Box::pin(async { Ok(Vec::new()) }) + } + + fn validate( + &self, + _origin: &stypes::SessionAction, + _fields: &[stypes::Field], + ) -> Result, stypes::NativeError> { + Ok(std::collections::HashMap::new()) + } + + fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { + true + } +} + +impl ComponentFactory for PluginDescriptor { + fn create( + &self, + origin: &SessionAction, + options: &[Field], + ) -> Result)>, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + + let configs: Vec = options + .iter() + .map(|opt| { + use stypes::PluginConfigValue as PlVal; + + let val = match opt.value.clone() { + stypes::Value::Boolean(val) => PlVal::Boolean(val), + stypes::Value::Number(num) => PlVal::Integer(num as i32), + stypes::Value::String(txt) => PlVal::Text(txt), + stypes::Value::Directories(path_bufs) => PlVal::Directories(path_bufs), + stypes::Value::Files(path_bufs) => PlVal::Files(path_bufs), + unsupported => panic!("Config {unsupported:?} is unsupported by plugins"), + }; + PluginConfigItem::new(opt.id.to_owned(), val) + }) + .collect(); + + let settings = + PluginParserSettings::new(self.entity.dir_path.to_owned(), Default::default(), configs); + + //TODO AAZ: Temp solution by blocking here. + //Create function should be async + let parse_res = tokio::task::block_in_place(move || { + Handle::current().block_on(async move { + PluginsParser::initialize( + &settings.plugin_path, + &settings.general_settings, + settings.plugin_configs.clone(), + ) + .await + }) + })?; + + let parser = parsers::Parser::Plugin(Box::new(parse_res)); + + Ok(Some((parser, Some(self.entity.metadata.title.to_owned())))) + } +} + +impl ComponentFactory for PluginDescriptor { + fn create( + &self, + _origin: &SessionAction, + _options: &[Field], + ) -> Result)>, NativeError> { + //TODO: Update once sources are implemented + Err(NativeError { + severity: Severity::WARNING, + kind: NativeErrorKind::NotYetImplemented, + message: Some("Support for sources isn't implemented yet".into()), + }) + } +} diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs index 86808cb246..cdc6ee30c5 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs @@ -2,6 +2,7 @@ //! providing plugins state, infos and metadata. mod cache; +mod descriptor; mod errors; mod load; pub mod paths; @@ -13,6 +14,8 @@ use std::path::{Path, PathBuf}; use crate::plugins_shared::load::{WasmComponentInfo, load_and_inspect}; use cache::CacheManager; +use components::Components; +use descriptor::PluginDescriptor; use load::{PluginEntityState, load_plugin}; use paths::extract_plugin_file_paths; use stypes::{ @@ -314,6 +317,25 @@ impl PluginsManager { Ok(()) } + + pub fn register_plugins( + &self, + components: &mut Components, + ) -> Result<(), stypes::NativeError> { + for plugin in &self.installed_plugins { + let plug_info = plugin.entity.clone(); + match plugin.entity.plugin_type { + PluginType::Parser => { + components.add_parser(PluginDescriptor::new(plug_info))?; + } + PluginType::ByteSource => { + components.add_source(PluginDescriptor::new(plug_info))?; + } + } + } + + Ok(()) + } } /// Provides the root path of the plugins directory for the provided plugin type. diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 9e53139a20..3b60c3f048 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -53,7 +53,6 @@ impl ComponentsSession { // * It'll need different state and locking management for downloading plugins, Updating // caches etc... let plugins_manager = plugins::load_manager().await?; - let plugins_manager = Arc::new(RwLock::new(plugins_manager)); let (tx_api, mut rx_api): (UnboundedSender, UnboundedReceiver) = unbounded_channel(); @@ -66,6 +65,10 @@ impl ComponentsSession { parsers::registration(&mut components)?; // Registre sources sources::registration(&mut components)?; + + plugins_manager.register_plugins(&mut components)?; + + let plugins_manager = Arc::new(RwLock::new(plugins_manager)); let tx_api_inner = tx_api.clone(); let session = Self { tx_api }; task::spawn(async move { diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 2ac79b3e56..0f58236e6e 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2532,6 +2532,7 @@ dependencies = [ "stypes", "thiserror 2.0.12", "tokio", + "tokio-util", "toml", "uuid", "wasmtime", From 8d4544a950b7dd76f8599bcbcacaeb46e0075804 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 25 Jun 2025 16:36:11 +0200 Subject: [PATCH 080/121] Add SessionDescriptor into session (client) --- .../service/session/dependencies/stream.ts | 11 +++++++- .../client/src/app/service/session/session.ts | 27 +++++++++++++++++++ .../sessions/requests/session/create.ts | 13 +++++++++ .../platform/ipc/event/stream/index.ts | 1 + .../ipc/event/stream/session_descriptor.ts | 19 +++++++++++++ 5 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 application/platform/ipc/event/stream/session_descriptor.ts diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index ab7527e287..610c23c6de 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -11,7 +11,7 @@ import { Info } from './info'; import { lockers } from '@ui/service/lockers'; import { Sde } from './observing/sde'; import { TextExportOptions } from '@platform/types/exporting'; -import { SessionSetup } from '@platform/types/bindings'; +import { SessionSetup, SessionDescriptor } from '@platform/types/bindings'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; @@ -34,6 +34,8 @@ export class Stream extends Subscriber { rank: Subject; // Grabber is inited readable: Subject; + // New session descriptor has been gotten (describe a observe operation in human readable way) + descriptor: Subject; }> = new Subjects({ updated: new Subject(), started: new Subject(), @@ -41,6 +43,7 @@ export class Stream extends Subscriber { sources: new Subject(), rank: new Subject(), readable: new Subject(), + descriptor: new Subject(), }); private _len: number = 0; private _uuid!: string; @@ -77,6 +80,12 @@ export class Stream extends Subscriber { }), ); this.register( + Events.IpcEvent.subscribe(Events.Stream.SessionDescriptor.Event, (event) => { + if (event.session !== this._uuid) { + return; + } + this.subjects.get().descriptor.emit(event.descriptor); + }), Events.IpcEvent.subscribe(Events.Observe.Started.Event, (event) => { if (event.session !== this._uuid) { return; diff --git a/application/client/src/app/service/session/session.ts b/application/client/src/app/service/session/session.ts index ac055b645b..7bd04c5777 100644 --- a/application/client/src/app/service/session/session.ts +++ b/application/client/src/app/service/session/session.ts @@ -27,6 +27,7 @@ import { DisabledRequest } from './dependencies/search/disabled/request'; import { StoredEntity } from './dependencies/search/store'; import { Notification, notifications } from '@ui/service/notifications'; import { error } from '@platform/log/utils'; +import { SessionDescriptor } from '@platform/types/bindings'; import * as ids from '@schema/ids'; import * as Requests from '@platform/ipc/request'; @@ -64,6 +65,7 @@ export class Session extends Base { private _uuid!: string; private _tab!: ITabAPI; + private _descriptor: SessionDescriptor | undefined; private readonly _toolbar: TabsService = new TabsService(); private readonly _sidebar: TabsService = new TabsService({ options: new TabsOptions({ direction: ETabsListDirection.left }), @@ -215,6 +217,17 @@ export class Session extends Base { this.teamwork.init(this); this.cli.init(this); this.inited = true; + this.register( + this.stream.subjects + .get() + .descriptor.subscribe((descriptor: SessionDescriptor) => { + if (this._descriptor) { + return; + } + this._descriptor = descriptor; + this.title().check(); + }), + ); resolve(this._uuid); }) .catch(reject); @@ -261,6 +274,7 @@ export class Session extends Base { public bind(tab: ITabAPI) { this._tab = tab; + this.title().check(); } public uuid(): string { @@ -352,6 +366,7 @@ export class Session extends Base { public title(): { set(title: string): Error | undefined; get(): Error | string; + check(): void; } { return { set: (title: string): Error | undefined => { @@ -360,6 +375,18 @@ export class Session extends Base { get: (): Error | string => { return this._tab.getTitle(); }, + check: () => { + if (!this._descriptor) { + return; + } + if (!this._tab) { + this.log().error(`Tab API isn't available on session instance`); + return; + } + if (this._descriptor.s_desc && this._descriptor.p_desc) { + this._tab.setTitle(`${this._descriptor.s_desc} (${this._descriptor.p_desc})`); + } + }, }; } diff --git a/application/holder/src/service/sessions/requests/session/create.ts b/application/holder/src/service/sessions/requests/session/create.ts index 3d600b23c9..ce4cc78a3d 100644 --- a/application/holder/src/service/sessions/requests/session/create.ts +++ b/application/holder/src/service/sessions/requests/session/create.ts @@ -105,6 +105,19 @@ export const handler = Requests.InjectLogger< ); }), ); + subscriber.register( + session.getEvents().SessionDescriptor.subscribe((descriptor) => { + if (!sessions.exists(uuid)) { + return; + } + Events.IpcEvent.emit( + new Events.Stream.SessionDescriptor.Event({ + descriptor, + session: uuid, + }), + ); + }), + ); subscriber.register( session.getEvents().FileRead.subscribe(() => { if (!sessions.exists(uuid)) { diff --git a/application/platform/ipc/event/stream/index.ts b/application/platform/ipc/event/stream/index.ts index 8be40dd7bd..7a4644f1f6 100644 --- a/application/platform/ipc/event/stream/index.ts +++ b/application/platform/ipc/event/stream/index.ts @@ -1,3 +1,4 @@ export * as Updated from './updated'; export * as IndexedMapUpdated from './indexed_map_updated'; export * as Attachment from './attachment'; +export * as SessionDescriptor from './session_descriptor'; diff --git a/application/platform/ipc/event/stream/session_descriptor.ts b/application/platform/ipc/event/stream/session_descriptor.ts new file mode 100644 index 0000000000..9195764729 --- /dev/null +++ b/application/platform/ipc/event/stream/session_descriptor.ts @@ -0,0 +1,19 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { SessionDescriptor } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'SessionDescriptorUpdated' }) +export class Event extends SignatureRequirement { + public descriptor: SessionDescriptor; + public session: string; + + constructor(input: { descriptor: SessionDescriptor; session: string }) { + super(); + validator.isObject(input); + this.descriptor = validator.getAsObj(input, 'descriptor'); + this.session = validator.getAsNotEmptyString(input, 'session'); + } +} + +export interface Event extends Interface {} From e2dc3902861a2b571acf0d637d3bdfda6d2abd93 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 1 Jul 2025 14:25:30 +0200 Subject: [PATCH 081/121] Remove SourceObserve and Observe from session --- .../plugins_host/src/v0_1_0/parser/mod.rs | 2 + .../apps/indexer/session/src/state/api.rs | 6 +- .../apps/indexer/session/src/state/mod.rs | 14 +- .../indexer/session/src/state/source_ids.rs | 9 +- .../apps/indexer/stypes/bindings/callback.ts | 11 +- .../apps/indexer/stypes/bindings/observe.ts | 296 +++++++++++------- .../indexer/stypes/src/callback/formating.rs | 8 +- .../apps/indexer/stypes/src/callback/mod.rs | 9 +- .../apps/indexer/stypes/src/observe/mod.rs | 11 + .../ts-bindings/src/api/session.provider.ts | 33 +- .../client/src/app/service/history/session.ts | 42 +-- application/client/src/app/service/session.ts | 3 +- .../app/service/session/dependencies/cli.ts | 42 +-- .../observing/implementations/files.ts | 144 --------- .../observing/implementations/index.ts | 7 - .../observing/implementations/processes.ts | 111 ------- .../observing/implementations/serial.ts | 113 ------- .../observing/implementations/tcp.ts | 112 ------- .../observing/implementations/udp.ts | 112 ------- .../dependencies/observing/operation.ts | 112 +++++-- .../dependencies/observing/provider.ts | 75 +++-- .../dependencies/observing/providers.ts | 41 ++- .../session/dependencies/observing/sde.ts | 27 +- .../session/dependencies/observing/source.ts | 27 -- .../service/session/dependencies/stream.ts | 183 ++++++----- .../service/session/dependencies/teamwork.ts | 34 +- .../client/src/app/service/session/origin.ts | 23 ++ .../client/src/app/service/session/session.ts | 87 +++-- .../elements/menu.attachsource/component.ts | 10 +- .../app/ui/elements/navigator/component.ts | 25 +- .../app/ui/views/sidebar/observe/component.ts | 36 ++- .../app/ui/views/sidebar/observe/module.ts | 20 +- .../ui/views/sidebar/observe/template.html | 33 +- .../src/app/ui/views/workspace/sde/state.ts | 4 - .../app/ui/views/workspace/sde/template.html | 31 +- .../src/app/ui/views/workspace/title/state.ts | 22 +- .../holder/src/service/sessions/holder.ts | 28 +- .../service/sessions/requests/observe/list.ts | 2 +- .../sessions/requests/observe/start.ts | 4 +- .../sessions/requests/session/create.ts | 5 +- .../ipc/event/stream/session_descriptor.ts | 4 +- .../platform/ipc/request/observe/list.ts | 6 +- .../platform/ipc/request/observe/start.ts | 6 +- .../platform/types/bindings/callback.ts | 147 ++++++--- .../platform/types/bindings/observe.ts | 35 ++- cli/development-cli/src/release/mod.rs | 43 +-- 46 files changed, 938 insertions(+), 1217 deletions(-) delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/files.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/index.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/processes.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/serial.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/implementations/udp.ts delete mode 100644 application/client/src/app/service/session/dependencies/observing/source.ts diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index d29000b91c..33314f4b25 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -24,6 +24,7 @@ use self::{bindings::Parse, parser_plugin_state::ParserPluginState}; /// Host of the parser plugin for plugins API version 0.1.0 pub struct PluginParser { store: Store, + plugin_bindings: Parse, } @@ -132,6 +133,7 @@ impl PluginParser { } use definitions::{self as defs, LogRecordOutput, ParseReturnIterator}; + impl defs::Parser for PluginParser { fn parse<'a>(&mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { // Calls on plugins must be async. To solve that we got the following solutions: diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index 601b348335..78f589a333 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -42,7 +42,7 @@ pub enum Api { FlushSessionFile(oneshot::Sender>), GetSessionFileOrigin(oneshot::Sender, stypes::NativeError>>), UpdateSession((u16, oneshot::Sender>)), - AddSource(SessionDescriptor, oneshot::Sender), + AddSource(Uuid, SessionDescriptor, oneshot::Sender), GetSourcesDefinitions(oneshot::Sender>), #[allow(clippy::large_enum_variant)] AddExecutedObserve((stypes::ObserveOptions, oneshot::Sender<()>)), @@ -472,10 +472,12 @@ impl SessionStateAPI { pub async fn add_source( &self, + operation_uuid: Uuid, desciptor: SessionDescriptor, ) -> Result { let (tx, rx) = oneshot::channel(); - self.exec_operation(Api::AddSource(desciptor, tx), rx).await + self.exec_operation(Api::AddSource(operation_uuid, desciptor, tx), rx) + .await } pub async fn get_sources_definitions( diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index 51bce9bf5e..93606cce5a 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -582,13 +582,21 @@ pub async fn run( stypes::NativeError::channel("Failed to respond to Api::UpdateSession") })?; } - Api::AddSource(descriptor, tx_response) => { + Api::AddSource(uuid, descriptor, tx_response) => { tx_response - .send(state.session_file.sources.add_source(descriptor.clone())) + .send( + state + .session_file + .sources + .add_source(uuid, descriptor.clone()), + ) .map_err(|_| { stypes::NativeError::channel("Failed to respond to Api::AddSource") })?; - tx_callback_events.send(stypes::CallbackEvent::SessionDescriptor(descriptor))?; + tx_callback_events.send(stypes::CallbackEvent::SessionDescriptor { + uuid, + desc: descriptor, + })?; } Api::GetSourcesDefinitions(tx_response) => { tx_response diff --git a/application/apps/indexer/session/src/state/source_ids.rs b/application/apps/indexer/session/src/state/source_ids.rs index 6800f3bb2d..5ad1e09482 100644 --- a/application/apps/indexer/session/src/state/source_ids.rs +++ b/application/apps/indexer/session/src/state/source_ids.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, ops::RangeInclusive}; use stypes::SessionDescriptor; +use uuid::Uuid; pub struct MappedRanges<'a> { ranges: Vec<&'a (RangeInclusive, u16)>, @@ -24,7 +25,7 @@ impl<'a> MappedRanges<'a> { #[derive(Debug)] pub struct SourceIDs { - pub sources: HashMap, + pub sources: HashMap, pub map: Vec<(RangeInclusive, u16)>, pub recent: Option, } @@ -38,9 +39,9 @@ impl SourceIDs { } } - pub fn add_source(&mut self, descriptor: SessionDescriptor) -> u16 { + pub fn add_source(&mut self, uuid: Uuid, descriptor: SessionDescriptor) -> u16 { let key = self.sources.len() as u16; - self.sources.insert(key, descriptor); + self.sources.insert(key, (uuid, descriptor)); key } @@ -74,7 +75,7 @@ impl SourceIDs { pub fn get_sources_definitions(&self) -> Vec { self.sources .iter() - .map(|(id, descriptor)| stypes::SourceDefinition { + .map(|(id, (_uuid, descriptor))| stypes::SourceDefinition { id: *id, descriptor: descriptor.clone(), }) diff --git a/application/apps/indexer/stypes/bindings/callback.ts b/application/apps/indexer/stypes/bindings/callback.ts index 96d5e7d479..d5ad045574 100644 --- a/application/apps/indexer/stypes/bindings/callback.ts +++ b/application/apps/indexer/stypes/bindings/callback.ts @@ -3,6 +3,7 @@ import type { AttachmentInfo } from "./attachment"; import type { FilterMatchList } from "./miscellaneous"; import type { NativeError } from "./error"; import type { Progress } from "./progress"; +import type { SessionDescriptor } from "./observe"; /** * Represents events sent to the client. @@ -21,7 +22,15 @@ stat: Map, } } | { "IndexedMapUpdated": { /** * The number of log entries from search results available for reading. */ -len: number, } } | { "SearchMapUpdated": FilterMatchList | null } | { "SearchValuesUpdated": Map } | { "AttachmentsUpdated": { +len: number, } } | { "SearchMapUpdated": FilterMatchList | null } | { "SessionDescriptor": { +/** + * Uuid of the observe operation, which adds new `SessionDescriptor` + */ +uuid: string, +/** + * Added `SessionDescriptor` + */ +desc: SessionDescriptor, } } | { "SearchValuesUpdated": Map } | { "AttachmentsUpdated": { /** * The size of the attachment in bytes. */ diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index b3eaf72c32..09fea880a8 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,9 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ComponentOptions } from "./options"; -import type { IODataType } from "./options"; -import type { PluginParserSettings } from "./plugins"; +import type { ComponentOptions } from './options'; +import type { IODataType } from './options'; +import type { PluginParserSettings } from './plugins'; -export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; +export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; /** * Represents the type of a component within the system. @@ -12,35 +12,57 @@ export type ComponentDef = { "Source": ComponentOptions } | { "Parser": Componen * functional role of the component. It is used to categorize components * according to their purpose in the data processing pipeline. */ -export type ComponentType = "Parser" | "Source"; +export type ComponentType = 'Parser' | 'Source'; /** * Settings for the DLT parser. */ -export type DltParserSettings = { -/** - * Configuration for filtering DLT messages. - */ -filter_config: DltFilterConfig, -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, -/** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ -with_storage_header: boolean, -/** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ -tz: string | null, }; +export type DltParserSettings = { + /** + * Configuration for filtering DLT messages. + */ + filter_config: DltFilterConfig; + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; + /** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ + with_storage_header: boolean; + /** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ + tz: string | null; +}; /** * Supported file formats for observation. */ -export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; - -export type Ident = { name: string, desc: string, io: IODataType, uuid: string, }; +export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; + +export type Ident = { + /** + * A short, human-readable name of the component (parser/source), independent of context. + * Used on the client side to display available components. + */ + name: string; + /** + * A more detailed description of the component. This information is displayed in a separate + * area and can contain an extended explanation. + */ + desc: string; + /** + * The type of data the component produces or expects. + * For sources, this is the type of data they emit. + * For parsers, this is the type of data they expect as input. + */ + io: IODataType; + /** + * A unique identifier of the component. + */ + uuid: string; +}; export type IdentList = Array; @@ -50,135 +72,175 @@ export type IdentList = Array; * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string, interface: string | null, }; +export type MulticastInfo = { multiaddr: string; interface: string | null }; /** * Options for observing data within a session. */ -export type ObserveOptions = { -/** - * The description of the data source. - */ -origin: ObserveOrigin, -/** - * The parser configuration to be applied. - */ -parser: ParserType, }; +export type ObserveOptions = { + /** + * The description of the data source. + */ + origin: ObserveOrigin; + /** + * The parser configuration to be applied. + */ + parser: ParserType; +}; /** * Describes the source of data for observation. */ -export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; +export type ObserveOrigin = + | { File: [string, FileFormat, string] } + | { Concat: Array<[string, FileFormat, string]> } + | { Stream: [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { "Plugin": PluginParserSettings }; +export type ParserType = + | { Dlt: DltParserSettings } + | { SomeIp: SomeIpParserSettings } + | { Text: null } + | { Plugin: PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { -/** - * The working directory for the command. - */ -cwd: string, -/** - * The command to execute. - */ -command: string, -/** - * Environment variables. If empty, the default environment variables are used. - */ -envs: Map, }; +export type ProcessTransportConfig = { + /** + * The working directory for the command. + */ + cwd: string; + /** + * The command to execute. + */ + command: string; + /** + * Environment variables. If empty, the default environment variables are used. + */ + envs: Map; +}; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { -/** - * The path to the serial port. - */ -path: string, -/** - * The baud rate for the connection. - */ -baud_rate: number, -/** - * The number of data bits per frame. - */ -data_bits: number, -/** - * The flow control setting. - */ -flow_control: number, -/** - * The parity setting. - */ -parity: number, -/** - * The number of stop bits. - */ -stop_bits: number, -/** - * The delay in sending data, in milliseconds. - */ -send_data_delay: number, -/** - * Whether the connection is exclusive. - */ -exclusive: boolean, }; - -/** - * Описывает операцию, которая должна быть произведена - */ -export type SessionAction = { "File": string } | { "Files": Array } | { "ExportRaw": [Array, Array<{ start: bigint, end: bigint, }>, string] } | "Source"; - -export type SessionDescriptor = { parser: Ident, source: Ident, p_desc: string | null, s_desc: string | null, }; - -export type SessionSetup = { origin: SessionAction, parser: ComponentOptions, source: ComponentOptions, }; +export type SerialTransportConfig = { + /** + * The path to the serial port. + */ + path: string; + /** + * The baud rate for the connection. + */ + baud_rate: number; + /** + * The number of data bits per frame. + */ + data_bits: number; + /** + * The flow control setting. + */ + flow_control: number; + /** + * The parity setting. + */ + parity: number; + /** + * The number of stop bits. + */ + stop_bits: number; + /** + * The delay in sending data, in milliseconds. + */ + send_data_delay: number; + /** + * Whether the connection is exclusive. + */ + exclusive: boolean; +}; + +export type SessionAction = + | { File: string } + | { Files: Array } + | { ExportRaw: [Array, Array<{ start: bigint; end: bigint }>, string] } + | 'Source'; + +export type SessionDescriptor = { + /** + * Identifier of the parser being used. Provides a general description of the parser. + */ + parser: Ident; + /** + * Identifier of the source being used. Provides a general description of the source. + */ + source: Ident; + /** + * Parser description in the context of the current session. + */ + p_desc: string | null; + /** + * Source description in the context of the current session. For example, instead of "UDP", it might be "UDP on 0.0.0.0::8888". + */ + s_desc: string | null; +}; + +export type SessionSetup = { + origin: SessionAction; + parser: ComponentOptions; + source: ComponentOptions; +}; /** * Settings for the SomeIp parser. */ -export type SomeIpParserSettings = { -/** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ -fibex_file_paths: Array | null, }; +export type SomeIpParserSettings = { + /** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ + fibex_file_paths: Array | null; +}; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { -/** - * The address to bind the TCP connection to. - */ -bind_addr: string, }; +export type TCPTransportConfig = { + /** + * The address to bind the TCP connection to. + */ + bind_addr: string; +}; /** * Describes the transport source for a session. */ -export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; +export type Transport = + | { Process: ProcessTransportConfig } + | { TCP: TCPTransportConfig } + | { UDP: UDPTransportConfig } + | { Serial: SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { -/** - * The address to bind the UDP connection to. - */ -bind_addr: string, -/** - * A list of multicast configurations. - */ -multicast: Array, }; +export type UDPTransportConfig = { + /** + * The address to bind the UDP connection to. + */ + bind_addr: string; + /** + * A list of multicast configurations. + */ + multicast: Array; +}; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { -/** - * A list of multicast addresses to listen on. - */ -multicast_addr: Array, }; +export type UdpConnectionInfo = { + /** + * A list of multicast addresses to listen on. + */ + multicast_addr: Array; +}; diff --git a/application/apps/indexer/stypes/src/callback/formating.rs b/application/apps/indexer/stypes/src/callback/formating.rs index d1148f407c..77c8195a98 100644 --- a/application/apps/indexer/stypes/src/callback/formating.rs +++ b/application/apps/indexer/stypes/src/callback/formating.rs @@ -37,8 +37,12 @@ impl std::fmt::Display for CallbackEvent { progress: _, } => write!(f, "Progress"), Self::SessionError(err) => write!(f, "SessionError: {err:?}"), - Self::SessionDescriptor(desc) => { - write!(f, "SessionDescriptor: {:?}/{:?}", desc.s_desc, desc.p_desc) + Self::SessionDescriptor { uuid, desc } => { + write!( + f, + "SessionDescriptor for {uuid}: {:?}/{:?}", + desc.s_desc, desc.p_desc + ) } Self::OperationError { uuid, error } => { write!(f, "OperationError: {uuid}: {error:?}") diff --git a/application/apps/indexer/stypes/src/callback/mod.rs b/application/apps/indexer/stypes/src/callback/mod.rs index 82bfe6cc15..c82e361d24 100644 --- a/application/apps/indexer/stypes/src/callback/mod.rs +++ b/application/apps/indexer/stypes/src/callback/mod.rs @@ -70,9 +70,12 @@ pub enum CallbackEvent { /// - `Option`: The list of matches with log entry indices. SearchMapUpdated(Option), - /// Triggers as soon as new observe operation has been started and SessionDescriptor has been - /// registred in the list of sources. - SessionDescriptor(SessionDescriptor), + SessionDescriptor { + /// Uuid of the observe operation, which adds new `SessionDescriptor` + uuid: Uuid, + /// Added `SessionDescriptor` + desc: SessionDescriptor, + }, /// Triggered when the "value map" is updated. The "value map" is used to build charts /// from search results. Always triggered immediately after `SearchUpdated`. diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 667fc91677..561e84e13d 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -89,9 +89,20 @@ pub struct IdentList(pub Vec); ts(export, export_to = "observe.ts") )] pub struct Ident { + /// A short, human-readable name of the component (parser/source), independent of context. + /// Used on the client side to display available components. pub name: String, + + /// A more detailed description of the component. This information is displayed in a separate + /// area and can contain an extended explanation. pub desc: String, + + /// The type of data the component produces or expects. + /// For sources, this is the type of data they emit. + /// For parsers, this is the type of data they expect as input. pub io: IODataType, + + /// A unique identifier of the component. pub uuid: Uuid, } diff --git a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts index 2854842ef1..66ba376b50 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts @@ -50,6 +50,11 @@ export interface IAttachmentsUpdatedUpdated { attachment: AttachmentInfo; } +export interface SessionDescriptorEvent { + uuid: string; + desc: SessionDescriptor; +} + export interface ISessionEvents { StreamUpdated: Subject; FileRead: Subject; @@ -64,7 +69,7 @@ export interface ISessionEvents { SessionError: Subject; OperationError: Subject; SessionDestroyed: Subject; - SessionDescriptor: Subject; + SessionDescriptor: Subject; OperationStarted: Subject; OperationProcessing: Subject; OperationDone: Subject; @@ -140,10 +145,14 @@ interface ISessionEventsInterfaces { OperationStarted: { self: 'string' }; SessionDescriptor: { self: 'object'; - parser: 'object'; - source: 'object'; - s_desc: ['string', null, undefined]; - p_desc: ['string', null, undefined]; + uuid: 'string'; + desc: { + self: 'object'; + parser: 'object'; + source: 'object'; + s_desc: ['string', null, undefined]; + p_desc: ['string', null, undefined]; + }; }; OperationProcessing: { self: 'string' }; OperationDone: { self: 'object'; uuid: 'string'; result: 'any' }; @@ -177,10 +186,14 @@ const SessionEventsInterfaces: ISessionEventsInterfaces = { OperationStarted: { self: 'string' }, SessionDescriptor: { self: 'object', - parser: 'object', - source: 'object', - s_desc: ['string', null, undefined], - p_desc: ['string', null, undefined], + uuid: 'string', + desc: { + self: 'object', + parser: 'object', + source: 'object', + s_desc: ['string', null, undefined], + p_desc: ['string', null, undefined], + }, }, OperationProcessing: { self: 'string' }, OperationDone: { self: 'object', uuid: 'string', result: 'any' }, @@ -207,7 +220,7 @@ export class EventProvider extends Computation< SessionDestroyed: new Subject(), OperationStarted: new Subject(), OperationProcessing: new Subject(), - SessionDescriptor: new Subject(), + SessionDescriptor: new Subject(), OperationDone: new Subject(), }; diff --git a/application/client/src/app/service/history/session.ts b/application/client/src/app/service/history/session.ts index cef3e929e8..18e4f4225b 100644 --- a/application/client/src/app/service/history/session.ts +++ b/application/client/src/app/service/history/session.ts @@ -10,8 +10,7 @@ import { Subjects, Subject } from '@platform/env/subscription'; import { Suitable, SuitableGroup } from './suitable'; import { LockToken } from '@platform/env/lock.token'; import { cli } from '@service/cli'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; export { Suitable, SuitableGroup }; @@ -25,7 +24,7 @@ export class HistorySession extends Subscriber { protected readonly sources: string[] = []; protected readonly globals: Subscriber = new Subscriber(); protected readonly locker: LockToken = new LockToken(true); - protected readonly pendings: $.Observe[] = []; + protected readonly pendings: ObserveOperation[] = []; protected checked: boolean = false; public readonly definitions: Definitions; @@ -62,27 +61,28 @@ export class HistorySession extends Subscriber { ); } - protected handleNewSource(source: $.Observe) { + protected handleNewSource(operation: ObserveOperation) { if (this.locker.isLocked()) { - this.pendings.push(source); + this.pendings.push(operation); return; } - Definition.fromDataSource(source) - .then((definition) => { - definition = this.storage.definitions.update(definition); - this.definitions.add(definition); - this.collections.bind(definition); - if (cli.isFiltersImported(this.session.uuid())) { - // Filters are imported from CLI. Recent filters/charts should not - // be applied - return; - } - this.save(); - this.check().all(); - }) - .catch((err: Error) => { - this.log().error(`Fail to get definition of source: ${err.message}`); - }); + console.error(`Not implemented`); + // Definition.fromDataSource(operation) + // .then((definition) => { + // definition = this.storage.definitions.update(definition); + // this.definitions.add(definition); + // this.collections.bind(definition); + // if (cli.isFiltersImported(this.session.uuid())) { + // // Filters are imported from CLI. Recent filters/charts should not + // // be applied + // return; + // } + // this.save(); + // this.check().all(); + // }) + // .catch((err: Error) => { + // this.log().error(`Fail to get definition of source: ${err.message}`); + // }); } protected save() { diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index dd4346de8e..1618474f38 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -287,13 +287,12 @@ export class Service extends Implementation { }, observe: async (origin: SessionOrigin, existed?: Session): Promise => { const render = await origin.getRender(); - const session = existed !== undefined ? existed : await this.add(false).empty(render); return new Promise((resolve, reject) => { session.stream .observe() - .start(origin.getSessionSetup()) + .start(origin) .then((uuid: string) => { const error = this.bind(session.uuid(), uuid, true); if (error instanceof Error) { diff --git a/application/client/src/app/service/session/dependencies/cli.ts b/application/client/src/app/service/session/dependencies/cli.ts index f48da0d90e..668afb6bbf 100644 --- a/application/client/src/app/service/session/dependencies/cli.ts +++ b/application/client/src/app/service/session/dependencies/cli.ts @@ -226,26 +226,28 @@ ${udp.configuration.multicast[0].interface};"`; } protected arguments(): string | undefined { - const observed = this.session.stream - .observe() - .sources() - .map((s) => s.observe); - if (observed.length === 0) { - return undefined; - } - if (this.source().cx(observed) === undefined) { - // Not the same origin - return undefined; - } - const source = this.source().from(observed); - if (source === undefined) { - return undefined; - } - const parser = this.parser().from(observed[0]); - if (parser === undefined) { - return undefined; - } - return `${source} ${parser} ${this.filters()}`; + console.error(`Not implemented`); + // const operations = this.session.stream + // .observe() + // .operations() + // .map((s) => s.isRunning()); + // if (operations.length === 0) { + // return undefined; + // } + // if (this.source().cx(operations) === undefined) { + // // Not the same origin + // return undefined; + // } + // const source = this.source().from(operations); + // if (source === undefined) { + // return undefined; + // } + // const parser = this.parser().from(operations[0]); + // if (parser === undefined) { + // return undefined; + // } + // return `${source} ${parser} ${this.filters()}`; + return; } public init(session: Session) { diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/files.ts b/application/client/src/app/service/session/dependencies/observing/implementations/files.ts deleted file mode 100644 index cb25503f4b..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/files.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { Provider as Base } from '../provider'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { IComponentDesc } from '@elements/containers/dynamic/component'; -import { List } from '@ui/views/sidebar/observe/lists/file/component'; -import { IMenuItem } from '@ui/service/contextmenu'; - -import * as $ from '@platform/types/observe'; - -export class Provider extends Base { - private _sources: ObserveSource[] = []; - private _processed: string[] = []; - - public contextMenu(source: ObserveSource): IMenuItem[] { - const items: IMenuItem[] = []; - const observer = source.observer; - if (observer !== undefined) { - items.push({ - caption: 'Stop tailing', - handler: () => { - observer.abort().catch((err: Error) => { - this.logger.error(`Fail to abort observing (file tailing): ${err.message}`); - }); - }, - }); - } - return items; - } - - public update(sources: ObserveSource[]): Provider { - const added = sources.filter((s) => !this._processed.includes(s.uuid())); - // Add files - this._sources.push( - ...added.filter( - (source) => source.observe.origin.nature() instanceof $.Origin.File.Configuration, - ), - ); - // Add files from concat-container - added.map((source) => { - const concat = source.observe.origin.as<$.Origin.Concat.Configuration>( - $.Origin.Concat.Configuration, - ); - if (concat === undefined) { - return; - } - const origins = concat.asFileOrigins(); - const parser = source.observe.parser.sterilized(); - const files = origins.map((origin) => { - return new ObserveSource(new $.Observe({ origin, parser })).asChild(); - }); - this._sources.push(...files); - }); - this._processed.push(...added.map((a) => a.uuid())); - Base.overwrite(sources, this._sources); - return this; - } - - public sources(): ObserveSource[] { - return this._sources; - } - - public getPanels(): { - list(): { - name(): string; - desc(): string; - comp(): IComponentDesc; - }; - nocontent(): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - }; - } { - return { - list: (): { - name(): string; - desc(): string; - comp(): IComponentDesc; - } => { - return { - name: (): string => { - return `Files`; - }, - desc: (): string => { - return this._sources.length > 0 ? this._sources.length.toString() : ''; - }, - comp: (): IComponentDesc => { - return { - factory: List, - inputs: { - provider: this, - }, - }; - }, - }; - }, - - nocontent: (): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - } => { - return { - name: (): string | undefined => { - return undefined; - }, - desc: (): string | undefined => { - return undefined; - }, - comp: (): IComponentDesc | undefined => { - return undefined; - }, - }; - }, - }; - } - - public override getNewSourceError(): Error | undefined { - const active = this._sources.find((s) => s.observer !== undefined); - if (active !== undefined) { - return new Error(`Cannot attach new source while file is tailing`); - } - if (this._sources.length !== 1) { - return undefined; - } - const singleTextFile = this._sources.find((s) => { - const file = s.observe.origin.as<$.Origin.File.Configuration>( - $.Origin.File.Configuration, - ); - if (file === undefined) { - return false; - } - if (file.filetype() === $.Types.File.FileType.Text && !s.child) { - return true; - } - return false; - }); - if (singleTextFile !== undefined) { - return new Error( - `Single file has been opened. In this case you cannot attach new source(s).`, - ); - } - return undefined; - } -} diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/index.ts b/application/client/src/app/service/session/dependencies/observing/implementations/index.ts deleted file mode 100644 index 20d15fb76a..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Provider as Files } from './files'; -import { Provider as Processes } from './processes'; -import { Provider as Serial } from './serial'; -import { Provider as Tcp } from './tcp'; -import { Provider as Udp } from './udp'; - -export const PROVIDERS = [Files, Processes, Serial, Tcp, Udp]; diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts b/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts deleted file mode 100644 index fccba46d41..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/processes.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { Provider as Base } from '../provider'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { IComponentDesc } from '@elements/containers/dynamic/component'; -import { List } from '@ui/views/sidebar/observe/lists/process/component'; -import { IMenuItem } from '@ui/service/contextmenu'; -import { session } from '@service/session'; - -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; -import { SessionOrigin } from '@service/session/origin'; - -export class Provider extends Base { - private _sources: ObserveSource[] = []; - - public contextMenu(source: ObserveSource): IMenuItem[] { - const items: IMenuItem[] = []; - const observer = source.observer; - if (observer !== undefined) { - items.push({ - caption: 'Stop', - handler: () => { - observer.abort().catch((err: Error) => { - this.logger.error(`Fail to abort observing (spawning): ${err.message}`); - }); - }, - }); - } else { - items.push({ - caption: 'Repeat Command', - handler: () => { - this.clone(source.observe).catch((err: Error) => { - this.logger.error(`Fail to repeat: ${err.message}`); - }); - }, - }); - } - return items; - } - - public update(sources: ObserveSource[]): Provider { - this._sources = sources.filter( - (source) => - source.observe.origin.nature() instanceof - $.Origin.Stream.Stream.Process.Configuration, - ); - return this; - } - - public openNewSessionOptions() { - session.initialize().configure(SessionOrigin.source()); - } - - public sources(): ObserveSource[] { - return this._sources; - } - - public getPanels(): { - list(): { - name(): string; - desc(): string; - comp(): IComponentDesc; - }; - nocontent(): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - }; - } { - return { - list: (): { - name(): string; - desc(): string; - comp(): IComponentDesc; - } => { - return { - name: (): string => { - return `Commands`; - }, - desc: (): string => { - return this._sources.length > 0 ? this._sources.length.toString() : ''; - }, - comp: (): IComponentDesc => { - return { - factory: List, - inputs: { - provider: this, - }, - }; - }, - }; - }, - nocontent: (): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - } => { - return { - name: (): string | undefined => { - return undefined; - }, - desc: (): string | undefined => { - return undefined; - }, - comp: (): IComponentDesc | undefined => { - return undefined; - }, - }; - }, - }; - } -} diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts b/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts deleted file mode 100644 index dc967b88af..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/serial.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Provider as Base } from '../provider'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { IComponentDesc } from '@elements/containers/dynamic/component'; -import { List } from '@ui/views/sidebar/observe/lists/serial/component'; -import { IMenuItem } from '@ui/service/contextmenu'; -import { session } from '@service/session'; - -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; -import { SessionOrigin } from '@service/session/origin'; - -export class Provider extends Base { - private _sources: ObserveSource[] = []; - - public contextMenu(source: ObserveSource): IMenuItem[] { - const items: IMenuItem[] = []; - const observer = source.observer; - if (observer !== undefined) { - items.push({ - caption: 'Disconnect', - handler: () => { - observer.abort().catch((err: Error) => { - this.logger.error( - `Fail to abort observing (serial listening): ${err.message}`, - ); - }); - }, - }); - } else { - items.push({ - caption: 'Connect', - handler: () => { - this.clone(source.observe).catch((err: Error) => { - this.logger.error(`Fail to repeat: ${err.message}`); - }); - }, - }); - } - return items; - } - - public openNewSessionOptions() { - session.initialize().configure(SessionOrigin.source()); - } - - public update(sources: ObserveSource[]): Provider { - this._sources = sources.filter( - (source) => - source.observe.origin.nature() instanceof - $.Origin.Stream.Stream.Serial.Configuration, - ); - return this; - } - - public sources(): ObserveSource[] { - return this._sources; - } - - public getPanels(): { - list(): { - name(): string; - desc(): string; - comp(): IComponentDesc; - }; - nocontent(): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - }; - } { - return { - list: (): { - name(): string; - desc(): string; - comp(): IComponentDesc; - } => { - return { - name: (): string => { - return `Serial Connections`; - }, - desc: (): string => { - return this._sources.length > 0 ? this._sources.length.toString() : ''; - }, - comp: (): IComponentDesc => { - return { - factory: List, - inputs: { - provider: this, - }, - }; - }, - }; - }, - nocontent: (): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - } => { - return { - name: (): string | undefined => { - return undefined; - }, - desc: (): string | undefined => { - return undefined; - }, - comp: (): IComponentDesc | undefined => { - return undefined; - }, - }; - }, - }; - } -} diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts deleted file mode 100644 index 60fd5c7e40..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/tcp.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Provider as Base } from '../provider'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { IComponentDesc } from '@elements/containers/dynamic/component'; -import { List } from '@ui/views/sidebar/observe/lists/tcp/component'; -import { IMenuItem } from '@ui/service/contextmenu'; -import { session } from '@service/session'; - -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; -import { SessionOrigin } from '@service/session/origin'; - -export class Provider extends Base { - private _sources: ObserveSource[] = []; - - public contextMenu(source: ObserveSource): IMenuItem[] { - const items: IMenuItem[] = []; - const observer = source.observer; - if (observer !== undefined) { - items.push({ - caption: 'Disconnect', - handler: () => { - observer.abort().catch((err: Error) => { - this.logger.error( - `Fail to abort observing (tcp listening): ${err.message}`, - ); - }); - }, - }); - } else { - items.push({ - caption: 'Connect', - handler: () => { - this.clone(source.observe).catch((err: Error) => { - this.logger.error(`Fail to repeat: ${err.message}`); - }); - }, - }); - } - return items; - } - - public openNewSessionOptions() { - session.initialize().configure(SessionOrigin.source()); - } - - public update(sources: ObserveSource[]): Provider { - this._sources = sources.filter( - (source) => - source.observe.origin.nature() instanceof $.Origin.Stream.Stream.TCP.Configuration, - ); - return this; - } - - public sources(): ObserveSource[] { - return this._sources; - } - - public getPanels(): { - list(): { - name(): string; - desc(): string; - comp(): IComponentDesc; - }; - nocontent(): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - }; - } { - return { - list: (): { - name(): string; - desc(): string; - comp(): IComponentDesc; - } => { - return { - name: (): string => { - return `TCP Connections`; - }, - desc: (): string => { - return this._sources.length > 0 ? this._sources.length.toString() : ''; - }, - comp: (): IComponentDesc => { - return { - factory: List, - inputs: { - provider: this, - }, - }; - }, - }; - }, - nocontent: (): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - } => { - return { - name: (): string | undefined => { - return undefined; - }, - desc: (): string | undefined => { - return undefined; - }, - comp: (): IComponentDesc | undefined => { - return undefined; - }, - }; - }, - }; - } -} diff --git a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts b/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts deleted file mode 100644 index 9de239dfe2..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/implementations/udp.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Provider as Base } from '../provider'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { IComponentDesc } from '@elements/containers/dynamic/component'; -import { List } from '@ui/views/sidebar/observe/lists/udp/component'; -import { IMenuItem } from '@ui/service/contextmenu'; -import { session } from '@service/session'; - -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; -import { SessionOrigin } from '@service/session/origin'; - -export class Provider extends Base { - private _sources: ObserveSource[] = []; - - public contextMenu(source: ObserveSource): IMenuItem[] { - const items: IMenuItem[] = []; - const observer = source.observer; - if (observer !== undefined) { - items.push({ - caption: 'Disconnect', - handler: () => { - observer.abort().catch((err: Error) => { - this.logger.error( - `Fail to abort observing (udp listening): ${err.message}`, - ); - }); - }, - }); - } else { - items.push({ - caption: 'Connect', - handler: () => { - this.clone(source.observe).catch((err: Error) => { - this.logger.error(`Fail to repeat: ${err.message}`); - }); - }, - }); - } - return items; - } - - public openNewSessionOptions() { - session.initialize().configure(SessionOrigin.source()); - } - - public update(sources: ObserveSource[]): Provider { - this._sources = sources.filter( - (source) => - source.observe.origin.nature() instanceof $.Origin.Stream.Stream.UDP.Configuration, - ); - return this; - } - - public sources(): ObserveSource[] { - return this._sources; - } - - public getPanels(): { - list(): { - name(): string; - desc(): string; - comp(): IComponentDesc; - }; - nocontent(): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - }; - } { - return { - list: (): { - name(): string; - desc(): string; - comp(): IComponentDesc; - } => { - return { - name: (): string => { - return `UPD Connections`; - }, - desc: (): string => { - return this._sources.length > 0 ? this._sources.length.toString() : ''; - }, - comp: (): IComponentDesc => { - return { - factory: List, - inputs: { - provider: this, - }, - }; - }, - }; - }, - nocontent: (): { - name(): string | undefined; - desc(): string | undefined; - comp(): IComponentDesc | undefined; - } => { - return { - name: (): string | undefined => { - return undefined; - }, - desc: (): string | undefined => { - return undefined; - }, - comp: (): IComponentDesc | undefined => { - return undefined; - }, - }; - }, - }; - } -} diff --git a/application/client/src/app/service/session/dependencies/observing/operation.ts b/application/client/src/app/service/session/dependencies/observing/operation.ts index 2c7089c9d5..a228787476 100644 --- a/application/client/src/app/service/session/dependencies/observing/operation.ts +++ b/application/client/src/app/service/session/dependencies/observing/operation.ts @@ -1,33 +1,86 @@ -import { Observe } from '@platform/types/observe'; import { SdeRequest, SdeResponse } from '@platform/types/sde'; +import { SessionSetup, SessionDescriptor } from '@platform/types/bindings'; +import { SessionOrigin } from '@service/session/origin'; -import * as $ from '@platform/types/observe'; +type SdeAPIFunc = (msg: SdeRequest) => Promise; +type RestartingAPIFunc = (setup: SessionOrigin) => Promise; +type CancelAPIFunc = () => Promise; + +export enum State { + Started, + Running, + Done, +} export class ObserveOperation { - private _sdeTasksCount: number = 0; + protected _sdeTasksCount: number = 0; + + protected descriptor: SessionDescriptor | undefined; + protected sde: SdeAPIFunc | undefined; + protected restarting: RestartingAPIFunc | undefined; + protected cancel: CancelAPIFunc | undefined; + protected origin: SessionOrigin | undefined; + + public state: State = State.Started; + + constructor(public readonly uuid: string) {} - constructor( - public readonly uuid: string, - protected readonly observe: Observe, - protected readonly sde: (msg: SdeRequest) => Promise, - protected readonly restarting: (observe: Observe) => Promise, - protected readonly cancel: () => Promise, - ) {} + public bind( + origin: SessionOrigin, + sde: SdeAPIFunc, + restarting: RestartingAPIFunc, + cancel: CancelAPIFunc, + ) { + this.origin = origin; + this.sde = sde; + this.restarting = restarting; + this.cancel = cancel; + this.state = State.Running; + } public abort(): Promise { + if (!this.cancel) { + return Promise.reject( + new Error(`"abort" cannot be called, because ObserveOperation isn't bound`), + ); + } return this.cancel(); } public restart(): Promise { - return this.restarting(this.observe); + if (!this.restarting || !this.origin) { + return Promise.reject( + new Error(`"restart" cannot be called, because ObserveOperation isn't bound`), + ); + } + return this.restarting(this.origin); + } + + public finish() { + this.state = State.Done; + } + + public isRunning(): boolean { + return this.state === State.Running; } - public asObserve(): Observe { - return this.observe; + public isSame(operation: ObserveOperation): boolean { + return this.uuid === operation.uuid; } - public asOrigin(): $.Origin.Configuration { - return this.observe.origin; + public getOrigin(): SessionOrigin { + if (!this.origin) { + throw new Error(`Operation ${this.uuid} isn't bound yet.`); + } + return this.origin; + } + + public setDescriptor(descriptor: SessionDescriptor) { + this.descriptor = descriptor; + } + + public getDescriptor(): SessionDescriptor | undefined { + return this.descriptor; } public send(): { @@ -35,14 +88,27 @@ export class ObserveOperation { bytes(data: number[]): Promise; } { const send = (request: SdeRequest): Promise => { + if (!this.sde) { + return Promise.reject( + new Error(`"sde" cannot be called, because ObserveOperation isn't bound`), + ); + } this._sdeTasksCount += 1; return this.sde(request).finally(() => { this._sdeTasksCount -= 1; }); }; return { - text: (data: string): Promise => { - if (!this.observe.origin.isSdeSupported()) { + text: async (data: string): Promise => { + if (!this.origin) { + return Promise.reject( + new Error( + `"sde::send::text" cannot be called, because ObserveOperation isn't bound`, + ), + ); + } + const support = await this.origin.isSdeSupported(); + if (!support) { return Promise.reject( new Error(`Observed origin doesn't support SDE protocol`), ); @@ -51,8 +117,16 @@ export class ObserveOperation { WriteText: `${data}\n\r`, }); }, - bytes: (data: number[]): Promise => { - if (!this.observe.origin.isSdeSupported()) { + bytes: async (data: number[]): Promise => { + if (!this.origin) { + return Promise.reject( + new Error( + `"sde::send::bytes" cannot be called, because ObserveOperation isn't bound`, + ), + ); + } + const support = await this.origin.isSdeSupported(); + if (!support) { return Promise.reject( new Error(`Observed origin doesn't support SDE protocol`), ); diff --git a/application/client/src/app/service/session/dependencies/observing/provider.ts b/application/client/src/app/service/session/dependencies/observing/provider.ts index e178b72e23..6f9f0dfc05 100644 --- a/application/client/src/app/service/session/dependencies/observing/provider.ts +++ b/application/client/src/app/service/session/dependencies/observing/provider.ts @@ -1,7 +1,6 @@ import { IComponentDesc } from '@elements/containers/dynamic/component'; import { Logger } from '@platform/log'; import { Session } from '@service/session/session'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; import { unique } from '@platform/env/sequence'; import { Subject, Subjects } from '@platform/env/subscription'; import { IMenuItem } from '@ui/service/contextmenu'; @@ -9,22 +8,24 @@ import { Observe } from '@platform/types/observe'; import { session } from '@service/session'; import { components } from '@env/decorators/initial'; import { popup, Vertical, Horizontal } from '@ui/service/popup'; +import { ObserveOperation } from './operation'; import * as $ from '@platform/types/observe'; +import { SessionOrigin } from '@service/session/origin'; export interface ProviderConstructor { new (session: Session, logger: Logger): Provider; } export abstract class Provider { - static overwrite(src: ObserveSource[], dest: ObserveSource[]) { - src.filter((src) => src.observer === undefined).forEach((src) => { - const index = dest.findIndex((s) => s.observe.uuid === src.observe.uuid); - if (index !== -1) { - dest.splice(index, 1, src); - } - }); - } + // static overwrite(src: ObserveOperation[], dest: ObserveOperation[]) { + // src.filter((src) => !src.isRunning()).forEach((src) => { + // const index = dest.findIndex((s) => s.uuid === src.uuid); + // if (index !== -1) { + // dest.splice(index, 1, src); + // } + // }); + // } public readonly session: Session; public readonly logger: Logger; public readonly subjects: Subjects<{ @@ -55,25 +56,25 @@ export abstract class Provider { this.subjects.destroy(); } - public last(): Observe | undefined { - const sources = this.sources(); - if (sources.length === 0) { + public getLastOperation(): ObserveOperation | undefined { + const operations = this.operations(); + if (operations.length === 0) { return undefined; } - const observe = sources[sources.length - 1]; - return observe.observe; + const operation = operations[operations.length - 1]; + return operation; } public recent() { - const observe = this.last(); - if (observe === undefined) { + const operation = this.getLastOperation(); + if (operation === undefined) { return; } popup.open({ component: { factory: components.get('app-navigator'), inputs: { - observe, + operation, }, }, position: { @@ -90,31 +91,27 @@ export abstract class Provider { if (!(observe.origin.instance instanceof $.Origin.Stream.Configuration)) { return Promise.reject(new Error(`Only Origin.Stream can be repeated`)); } - const last = this.last(); - if (last !== undefined) { - observe.parser.change(last.parser.instance); - } + const last = this.getLastOperation(); + // if (last !== undefined) { + // observe.parser.change(last.parser.instance); + // } // return this.session.stream.observe().start(observe.clone()); return Promise.reject(new Error(`Not implemented`)); } - public openAsNewOrigin(observe: Observe): Promise { - const last = this.last(); - if (last !== undefined) { - observe.parser.change(last.parser.instance); - } else { - return Promise.reject(new Error(`No data about current parser`)); - } + public openAsNewOrigin(origin: SessionOrigin): Promise { + // const last = this.last(); + // if (last !== undefined) { + // observe.parser.change(last.parser.instance); + // } else { + // return Promise.reject(new Error(`No data about current parser`)); + // } // return this.session.stream.observe().start(observe.clone()); return Promise.reject(new Error(`Not implemented`)); } - public openAsNew(source: ObserveSource | Observe): Promise { - console.error(`Not implemented`); - return Promise.reject(new Error(`Not implemented`)); - // return session - // .initialize() - // .configure(source instanceof ObserveSource ? source.observe : source); + public openAsNew(origin: SessionOrigin): Promise { + return session.initialize().configure(origin); } public setPanels(): Provider { @@ -134,14 +131,14 @@ export abstract class Provider { } public isEmpty(): boolean { - return this.sources().length === 0; + return this.operations().length === 0; } - public abstract contextMenu(source: ObserveSource): IMenuItem[]; + public abstract contextMenu(operations: ObserveOperation): IMenuItem[]; - public abstract update(sources: ObserveSource[]): Provider; + public abstract update(operations: ObserveOperation[]): Provider; - public abstract sources(): ObserveSource[]; + public abstract operations(): ObserveOperation[]; public abstract getPanels(): { list(): { @@ -161,6 +158,6 @@ export abstract class Provider { } public count(): number { - return this.sources().length; + return this.operations().length; } } diff --git a/application/client/src/app/service/session/dependencies/observing/providers.ts b/application/client/src/app/service/session/dependencies/observing/providers.ts index 894edcee5c..3d0e3b74c7 100644 --- a/application/client/src/app/service/session/dependencies/observing/providers.ts +++ b/application/client/src/app/service/session/dependencies/observing/providers.ts @@ -3,7 +3,6 @@ import { Provider, ProviderConstructor } from './provider'; import { Session } from '@service/session/session'; import { Subscriber } from '@platform/env/subscription'; import { Mutable } from '@platform/types/unity/mutable'; -import { PROVIDERS } from './implementations'; import { cutUuid } from '@log/index'; @SetupLogger() @@ -13,24 +12,24 @@ export class Providers extends Subscriber { public readonly list: Map = new Map(); public init(session: Session) { - this.setLoggerName(`Providers: ${cutUuid(session.uuid())}`); - (this as Mutable).session = session; - PROVIDERS.forEach((providerConstructor: ProviderConstructor) => { - const provider = new providerConstructor(session, this.log()); - this.list.set(provider.uuid, provider.setPanels()); - }); - this.register( - session.stream.subjects.get().sources.subscribe(() => { - this.update(); - }), - session.stream.subjects.get().finished.subscribe(() => { - this.update(); - }), - session.stream.sde.subjects.get().selected.subscribe(() => { - this.update(); - }), - ); - this.update(); + // this.setLoggerName(`Providers: ${cutUuid(session.uuid())}`); + // (this as Mutable).session = session; + // PROVIDERS.forEach((providerConstructor: ProviderConstructor) => { + // const provider = new providerConstructor(session, this.log()); + // this.list.set(provider.uuid, provider.setPanels()); + // }); + // this.register( + // session.stream.subjects.get().sources.subscribe(() => { + // this.update(); + // }), + // session.stream.subjects.get().finished.subscribe(() => { + // this.update(); + // }), + // session.stream.sde.subjects.get().selected.subscribe(() => { + // this.update(); + // }), + // ); + // this.update(); } public destroy() { @@ -60,9 +59,9 @@ export class Providers extends Subscriber { } protected update() { - const sources = this.session.stream.observe().sources(); + const operations = this.session.stream.observe().operations(); this.list.forEach((provider, _uuid) => { - provider.update(sources).subjects.get().updated.emit(); + provider.update(operations).subjects.get().updated.emit(); }); } } diff --git a/application/client/src/app/service/session/dependencies/observing/sde.ts b/application/client/src/app/service/session/dependencies/observing/sde.ts index 031afb7fb2..3787c62f20 100644 --- a/application/client/src/app/service/session/dependencies/observing/sde.ts +++ b/application/client/src/app/service/session/dependencies/observing/sde.ts @@ -59,8 +59,25 @@ export class Sde { }); } - public overwrite(running: Map): void { - this.operations = Array.from(running.values()).filter((s) => s.asOrigin().isSdeSupported()); + public async overwrite(all: Map): Promise { + const running = Array.from(all.values()).filter((s) => s.isRunning()); + this.operations = []; + for (let operation of running) { + const sde: boolean = await operation + .getOrigin() + .isSdeSupported() + .catch((err: Error) => { + this.log().error( + `Fail to get SDE support info for ${operation.getOrigin().getTitle()} (${ + operation.uuid + }): ${err.message}`, + ); + return false; + }); + if (sde) { + this.operations.push(operation); + } + } this.subjects.get().updated.emit(); if (this.selected !== undefined) { const target = this.selected.uuid; @@ -110,7 +127,7 @@ export class Sde { is: (uuid: string): boolean => { return this.selected === undefined ? false - : this.selected.uuid === uuid || this.selected.asObserve().uuid === uuid; + : this.selected.uuid === uuid || this.selected.uuid === uuid; }, first: (): void => { if (this.operations.length === 0) { @@ -134,9 +151,7 @@ export class Sde { if (this.selected !== undefined && this.selected.uuid === uuid) { return false; } - const candidate = this.operations.find( - (o) => o.uuid === uuid || o.asObserve().uuid === uuid, - ); + const candidate = this.operations.find((o) => o.uuid === uuid || o.uuid === uuid); if (candidate === undefined) { return false; } diff --git a/application/client/src/app/service/session/dependencies/observing/source.ts b/application/client/src/app/service/session/dependencies/observing/source.ts deleted file mode 100644 index 55ccd622e5..0000000000 --- a/application/client/src/app/service/session/dependencies/observing/source.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ObserveOperation } from './operation'; -import { Observe } from '@platform/types/observe'; -import { Mutable } from '@platform/types/unity/mutable'; - -export class ObserveSource { - public readonly observe: Observe; - public readonly observer: ObserveOperation | undefined; - public readonly child: boolean = false; - - constructor(observe: Observe, observer?: ObserveOperation) { - this.observe = observe; - this.observer = observer; - } - - public uuid(): string { - return this.observe.uuid; - } - - public isSame(source: ObserveSource): boolean { - return this.uuid() === source.uuid(); - } - - public asChild(): ObserveSource { - (this as Mutable).child = true; - return this; - } -} diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index 610c23c6de..479a832b9a 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -4,9 +4,7 @@ import { Range, IRange } from '@platform/types/range'; import { cutUuid } from '@log/index'; import { Rank } from './rank'; import { GrabbedElement } from '@platform/types/bindings/miscellaneous'; -import { Observe } from '@platform/types/observe'; import { ObserveOperation } from './observing/operation'; -import { ObserveSource } from './observing/source'; import { Info } from './info'; import { lockers } from '@ui/service/lockers'; import { Sde } from './observing/sde'; @@ -16,6 +14,7 @@ import { SessionSetup, SessionDescriptor } from '@platform/types/bindings'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; import * as $ from '@platform/types/observe'; +import { SessionOrigin } from '../origin'; export { ObserveOperation }; @@ -25,9 +24,9 @@ export class Stream extends Subscriber { // Stream is updated (new rows came) updated: Subject; // New observe operation is started - started: Subject; + started: Subject; // Observe operation for source is finished - finished: Subject; + finished: Subject; // List of sources (observed operations has been changed) sources: Subject; // Session rank is changed @@ -38,8 +37,8 @@ export class Stream extends Subscriber { descriptor: Subject; }> = new Subjects({ updated: new Subject(), - started: new Subject(), - finished: new Subject(), + started: new Subject(), + finished: new Subject(), sources: new Subject(), rank: new Subject(), readable: new Subject(), @@ -50,12 +49,10 @@ export class Stream extends Subscriber { private _info!: Info; public readonly observed: { - running: Map; - done: Map; + operations: Map; map: Map; } = { - running: new Map(), - done: new Map(), + operations: new Map(), map: new Map(), }; public readonly rank: Rank = new Rank(); @@ -85,45 +82,23 @@ export class Stream extends Subscriber { return; } this.subjects.get().descriptor.emit(event.descriptor); + const operation = this.observed.operations.get(event.operation); + if (!operation) { + this.log().error( + `Event "Stream.SessionDescriptor" emmited for operation "${event.operation}", but there no started operation with same uuid.`, + ); + return; + } + operation.setDescriptor(event.descriptor); }), Events.IpcEvent.subscribe(Events.Observe.Started.Event, (event) => { if (event.session !== this._uuid) { return; } - console.error(`Not implemented`); - // const observe = Observe.from(event.source); - // if (observe instanceof Error) { - // this.log().error(`Fail to parse Observe: ${observe.message}`); - // return; - // } - // this.observed.running.set( - // event.operation, - // new ObserveOperation( - // event.operation, - // observe, - // this.sde.send.bind(this.sde, event.operation), - // this.observe().restart.bind(this, event.operation), - // this.observe().abort.bind(this, event.operation), - // ), - // ); - // this.observe() - // .descriptions.request() - // .then((sources) => { - // let updated = false; - // sources.forEach((source) => { - // if (!this.observed.map.has(source.id)) { - // this.observed.map.set(source.id, source); - // updated = true; - // } - // }); - // updated && this.subjects.get().sources.emit(); - // }) - // .catch((err: Error) => { - // this.log().error(`Fail get sources description: ${err.message}`); - // }); - // this.sde.overwrite(this.observed.running); - let observe = Observe.new(); - this.subjects.get().started.emit(observe); + this.observed.operations.set( + event.operation, + new ObserveOperation(event.operation), + ); }), ); this.register( @@ -131,14 +106,16 @@ export class Stream extends Subscriber { if (event.session !== this._uuid) { return; } - const stored = this.observed.running.get(event.operation); - if (stored === undefined) { + const operation = this.observed.operations.get(event.operation); + if (!operation) { + this.log().error( + `Event "Observe.Finished" emmited for operation "${event.operation}", but there no started operation with same uuid.`, + ); return; } - this.observed.done.set(event.operation, stored.asObserve()); - this.observed.running.delete(event.operation); - this.sde.overwrite(this.observed.running); - this.subjects.get().finished.emit(stored.asObserve()); + operation.finish(); + this.sde.overwrite(this.observed.operations); + this.subjects.get().finished.emit(operation); }), ); } @@ -154,11 +131,11 @@ export class Stream extends Subscriber { } public observe(): { - start(options: SessionSetup): Promise; + start(options: SessionOrigin): Promise; abort(uuid: string): Promise; - restart(uuid: string, options: SessionSetup): Promise; - list(): Promise>; - sources(): ObserveSource[]; + restart(uuid: string, options: SessionOrigin): Promise; + list(): Promise>; + operations(): ObserveOperation[]; isFileSource(): boolean; getSourceFileName(): string | undefined; descriptions: { @@ -169,19 +146,66 @@ export class Stream extends Subscriber { }; } { return { - start: (options: SessionSetup): Promise => { + start: (origin: SessionOrigin): Promise => { return Requests.IpcRequest.send( Requests.Observe.Start.Response, new Requests.Observe.Start.Request({ session: this._uuid, - options, + options: origin.getSessionSetup(), }), ) .then((response) => { if (typeof response.error === 'string' && response.error !== '') { return Promise.reject(new Error(response.error)); } - // this._info.fromObserveInfo(observe); + if (typeof response.uuid !== 'string' || response.uuid.trim() === '') { + return Promise.reject( + new Error(`Invalid session start operation UUID`), + ); + } + const operationUuid = response.uuid; + if (!operationUuid) { + return Promise.reject( + new Error( + `No operation UUID has been recieved: ${JSON.stringify( + response, + )}`, + ), + ); + } + const operation = this.observed.operations.get(operationUuid); + if (!operation) { + return Promise.reject( + new Error( + `Operation ${operationUuid} didn't sent Started event: ${JSON.stringify( + response, + )}`, + ), + ); + } + operation.bind( + origin, + this.sde.send.bind(this.sde, operationUuid), + this.observe().restart.bind(this, operationUuid), + this.observe().abort.bind(this, operationUuid), + ); + this.observe() + .descriptions.request() + .then((sources) => { + let updated = false; + sources.forEach((source) => { + if (!this.observed.map.has(source.id)) { + this.observed.map.set(source.id, source); + updated = true; + } + }); + updated && this.subjects.get().sources.emit(); + }) + .catch((err: Error) => { + this.log().error(`Fail get sources description: ${err.message}`); + }); + this.sde.overwrite(this.observed.operations); + this.subjects.get().started.emit(operation); return response.session; }) .finally(lockers.progress(`Creating session...`)); @@ -208,14 +232,14 @@ export class Stream extends Subscriber { }); }); }, - restart: (uuid: string, options: SessionSetup): Promise => { + restart: (uuid: string, options: SessionOrigin): Promise => { return this.observe() .abort(uuid) .then(() => { return this.observe().start(options); }); }, - list: (): Promise> => { + list: (): Promise> => { return new Promise((resolve) => { Requests.IpcRequest.send( Requests.Observe.List.Response, @@ -224,16 +248,18 @@ export class Stream extends Subscriber { }), ) .then((response: Requests.Observe.List.Response) => { - const sources: Map = new Map(); - Object.keys(response.sources).forEach((uuid: string) => { - const source = Observe.from(response.sources[uuid]); - if (source instanceof Error) { - this.log().error(`Fail to parse Observe: ${source.message}`); - return; + const operations: Map = new Map(); + response.operations.forEach((uuid: string) => { + const operation = this.observed.operations.get(uuid); + if (!operation) { + this.log().error( + `Fail to find operation ${uuid} in local scope`, + ); + } else { + operations.set(uuid, operation); } - sources.set(uuid, source); }); - resolve(sources); + resolve(operations); }) .catch((error: Error) => { this.log().error( @@ -242,33 +268,22 @@ export class Stream extends Subscriber { }); }); }, - sources: (): ObserveSource[] => { - const sources: ObserveSource[] = []; - Array.from(this.observed.running.values()).forEach((observed: ObserveOperation) => { - sources.push(new ObserveSource(observed.asObserve(), observed)); - }); - Array.from(this.observed.done.values()).forEach((source: Observe) => { - sources.push(new ObserveSource(source)); - }); - return sources; + operations: (): ObserveOperation[] => { + return Array.from(this.observed.operations.values()); }, isFileSource: (): boolean => { - const sources = this.observe().sources(); + const sources = this.observe().operations(); if (sources.length !== 1) { return false; } - return sources[0].observe.origin.files() !== undefined; + return sources[0].getOrigin().getFirstFilename() !== undefined; }, getSourceFileName: (): string | undefined => { - const sources = this.observe().sources(); + const sources = this.observe().operations(); if (sources.length !== 1) { return undefined; } - const files = sources[0].observe.origin.files(); - if (files === undefined || (files instanceof Array && files.length === 0)) { - return undefined; - } - return files instanceof Array ? files[0] : files; + return sources[0].getOrigin().getFirstFilename(); }, descriptions: { get: (id: number): $.Types.ISourceLink | undefined => { diff --git a/application/client/src/app/service/session/dependencies/teamwork.ts b/application/client/src/app/service/session/dependencies/teamwork.ts index edcfb59bce..8cb413b2e0 100644 --- a/application/client/src/app/service/session/dependencies/teamwork.ts +++ b/application/client/src/app/service/session/dependencies/teamwork.ts @@ -17,6 +17,7 @@ import * as utils from '@platform/log/utils'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; import * as moment from 'moment'; +import { ObserveOperation } from './stream'; export interface GitHubError { time: string; @@ -386,25 +387,26 @@ export class TeamWork extends Subscriber { ); }, ), - this.session.stream.subjects.get().started.subscribe((observe: Observe) => { + this.session.stream.subjects.get().started.subscribe((operation: ObserveOperation) => { if (this.checksum === null) { return; } - FileDesc.fromDataSource(observe) - .then((desc) => { - if (desc === undefined) { - this.checksum = null; - } else if (this.checksum === undefined) { - this.checksum = desc.checksum; - this.file().check(); - } else { - this.checksum = null; - } - }) - .catch((err: Error) => { - this.checksum = null; - this.error().add(`Fail get chechsum of file: ${err.message}`); - }); + console.error(`Not implemented`); + // FileDesc.fromDataSource(operation) + // .then((desc) => { + // if (desc === undefined) { + // this.checksum = null; + // } else if (this.checksum === undefined) { + // this.checksum = desc.checksum; + // this.file().check(); + // } else { + // this.checksum = null; + // } + // }) + // .catch((err: Error) => { + // this.checksum = null; + // this.error().add(`Fail get chechsum of file: ${err.message}`); + // }); }), history.subjects.get().created.subscribe((uuid: string) => { const session = history.get(uuid); diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 092f3f9bae..41101359d3 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -100,6 +100,7 @@ export class SessionOrigin { .catch(reject); }); } + public getTitle(): string { if (this.origin === 'Source') { // TODO: Check idents @@ -112,6 +113,7 @@ export class SessionOrigin { return `unknown`; } } + public getDescription(): { title: string; desctiption: string | undefined } { if (this.origin === 'Source') { // TODO: Check idents @@ -130,4 +132,25 @@ export class SessionOrigin { return { title: 'Unknown', desctiption: undefined }; } } + + public isSdeSupported(): Promise { + /// TODO: add API implementation + console.error(`Not Implemented`); + return Promise.resolve(true); + } + + public isFile(): boolean { + return typeof (this.origin as { File: string }).File === 'string'; + } + + public getFirstFilename(): string | undefined { + if ((this.origin as { File: string }).File) { + return (this.origin as { File: string }).File; + } else if ((this.origin as { Files: string[] }).Files) { + const files = (this.origin as { Files: string[] }).Files; + return files.length > 0 ? files[0] : undefined; + } else { + return undefined; + } + } } diff --git a/application/client/src/app/service/session/session.ts b/application/client/src/app/service/session/session.ts index 7bd04c5777..bd4c10c13e 100644 --- a/application/client/src/app/service/session/session.ts +++ b/application/client/src/app/service/session/session.ts @@ -14,7 +14,6 @@ import { Bookmarks } from './dependencies/bookmarks'; import { Comments } from './dependencies/comments'; import { Exporter } from './dependencies/exporter'; import { IRange, fromIndexes } from '@platform/types/range'; -import { Providers } from './dependencies/observing/providers'; import { Attachments } from './dependencies/attachments'; import { Info } from './dependencies/info'; import { session } from '@service/session'; @@ -57,7 +56,6 @@ export class Session extends Base { public readonly highlights: Highlights = new Highlights(); public readonly exporter: Exporter = new Exporter(); public readonly render: Render; - public readonly observed: Providers = new Providers(); public readonly attachments: Attachments = new Attachments(); public readonly info: Info = new Info(); public readonly teamwork: TeamWork = new TeamWork(); @@ -210,7 +208,6 @@ export class Session extends Base { this.comments.init(this); this.search.init(this); this.exporter.init(this._uuid, this.stream, this.indexed); - this.observed.init(this); this.attachments.init(this._uuid); this.charts.init(this._uuid, this.stream, this.search); this.highlights.init(this); @@ -244,7 +241,6 @@ export class Session extends Base { this.comments.destroy(); this.cursor.destroy(); this.exporter.destroy(); - this.observed.destroy(); this.attachments.destroy(); this.charts.destroy(); this.info.destroy(); @@ -376,11 +372,10 @@ export class Session extends Base { return this._tab.getTitle(); }, check: () => { - if (!this._descriptor) { + if (!this._tab) { return; } - if (!this._tab) { - this.log().error(`Tab API isn't available on session instance`); + if (!this._descriptor) { return; } if (this._descriptor.s_desc && this._descriptor.p_desc) { @@ -484,48 +479,48 @@ export class Session extends Base { if (filepath === undefined) { return; } - const sources = this.stream.observe().sources(); + const sources = this.stream.observe().operations(); if (sources.length === 0) { throw new Error(`Fail to find bound source`); } - const current = sources[0].observe.clone(); - const parentSearchStore = this.search.store(); - const observe = (() => { - const file = current.origin.as(Origins.File.Configuration); - const concat = current.origin.as( - Origins.Concat.Configuration, - ); - if (file !== undefined) { - file.set().filename(filepath); - return current; - } else if (concat !== undefined) { - if (concat.filetypes().length === 0) { - throw new Error(`Cannot find type of concated files`); - } - return new Factory.File().type(concat.filetypes()[0]).file(filepath).asDlt().get(); - } else { - const observe = new Factory.File() - .type( - (() => { - switch (current.parser.alias()) { - case Parsers.Protocol.Text: - case Parsers.Protocol.Plugin: - return Types.File.FileType.Text; - case Parsers.Protocol.Dlt: - case Parsers.Protocol.SomeIp: - throw new Error( - `Exporting from none-text streams to create new session aren't supported yet`, - ); - } - })(), - ) - .file(filepath) - .asText() - .get(); - observe.parser.overwrite(current.parser.configuration); - return observe; - } - })(); + // const current = sources[0].observe.clone(); + // const parentSearchStore = this.search.store(); + // const observe = (() => { + // const file = current.origin.as(Origins.File.Configuration); + // const concat = current.origin.as( + // Origins.Concat.Configuration, + // ); + // if (file !== undefined) { + // file.set().filename(filepath); + // return current; + // } else if (concat !== undefined) { + // if (concat.filetypes().length === 0) { + // throw new Error(`Cannot find type of concated files`); + // } + // return new Factory.File().type(concat.filetypes()[0]).file(filepath).asDlt().get(); + // } else { + // const observe = new Factory.File() + // .type( + // (() => { + // switch (current.parser.alias()) { + // case Parsers.Protocol.Text: + // case Parsers.Protocol.Plugin: + // return Types.File.FileType.Text; + // case Parsers.Protocol.Dlt: + // case Parsers.Protocol.SomeIp: + // throw new Error( + // `Exporting from none-text streams to create new session aren't supported yet`, + // ); + // } + // })(), + // ) + // .file(filepath) + // .asText() + // .get(); + // observe.parser.overwrite(current.parser.configuration); + // return observe; + // } + // })(); console.error(`Not implemented`); return Promise.reject(new Error(`Not implemented`)); // return session diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index 247f3f90f6..c5f6118ce4 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -83,11 +83,15 @@ export class AttachSourceMenu extends ChangesDetector { } { return { disabled: (): boolean => { - return this.session.observed.getNewSourceError() instanceof Error; + // return this.session.observed.getNewSourceError() instanceof Error; + console.error(`Not implemented`); + return true; }, error: (): string | undefined => { - const error = this.session.observed.getNewSourceError(); - return error instanceof Error ? error.message : undefined; + // const error = this.session.observed.getNewSourceError(); + // return error instanceof Error ? error.message : undefined; + console.error(`Not implemented`); + return undefined; }, }; } diff --git a/application/client/src/app/ui/elements/navigator/component.ts b/application/client/src/app/ui/elements/navigator/component.ts index 8cc667853c..9a247d0d1e 100644 --- a/application/client/src/app/ui/elements/navigator/component.ts +++ b/application/client/src/app/ui/elements/navigator/component.ts @@ -16,7 +16,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, CloseHandler } from './state'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { InputFilter } from '@elements/filter/component'; -import { Observe } from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; @Component({ selector: 'app-navigator', @@ -35,7 +35,7 @@ export class Navigator @ViewChild('filter') public filterInputRef!: InputFilter; @Input() close: CloseHandler | undefined; - @Input() public observe?: Observe; + @Input() public operation?: ObserveOperation; public state!: State; @@ -48,16 +48,17 @@ export class Navigator } public ngAfterContentInit(): void { - this.state = new State( - this, - (): HTMLInputElement | undefined => { - this.detectChanges(); - return this.filterInputRef !== undefined - ? this.filterInputRef.getInputElementRef() - : undefined; - }, - this.observe, - ); + // this.state = new State( + // this, + // (): HTMLInputElement | undefined => { + // this.detectChanges(); + // return this.filterInputRef !== undefined + // ? this.filterInputRef.getInputElementRef() + // : undefined; + // }, + // this.observe, + // ); + console.error(`Not implemented`); this.close !== undefined && this.state.bind(this.close); } diff --git a/application/client/src/app/ui/views/sidebar/observe/component.ts b/application/client/src/app/ui/views/sidebar/observe/component.ts index b4b874d874..8adaf27838 100644 --- a/application/client/src/app/ui/views/sidebar/observe/component.ts +++ b/application/client/src/app/ui/views/sidebar/observe/component.ts @@ -1,5 +1,13 @@ -import { Component, Input, ChangeDetectorRef, ViewEncapsulation } from '@angular/core'; +import { + Component, + Input, + ChangeDetectorRef, + ViewEncapsulation, + AfterContentInit, +} from '@angular/core'; import { Session } from '@service/session'; +import { Stream } from '@service/session/dependencies/stream'; +import { ObserveOperation } from '@service/session/dependencies/observing/operation'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; @@ -13,11 +21,35 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; }) @Initial() @Ilc() -export class Observed extends ChangesDetector { +export class Observed extends ChangesDetector implements AfterContentInit { @Input() session!: Session; + public stream!: Stream; + public operations: ObserveOperation[] = []; + constructor(cdRef: ChangeDetectorRef) { super(cdRef); } + + public ngAfterContentInit(): void { + this.stream = this.session.stream; + this.operations = this.stream.observe().operations(); + this.env().subscriber.register( + this.stream.subjects.get().sources.subscribe(() => { + this.update(); + }), + this.stream.subjects.get().finished.subscribe(() => { + this.update(); + }), + this.stream.sde.subjects.get().selected.subscribe(() => { + this.update(); + }), + ); + } + + protected update() { + this.operations = this.stream.observe().operations(); + this.detectChanges(); + } } export interface Observed extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/module.ts b/application/client/src/app/ui/views/sidebar/observe/module.ts index 7631400b7e..88eaa297ec 100644 --- a/application/client/src/app/ui/views/sidebar/observe/module.ts +++ b/application/client/src/app/ui/views/sidebar/observe/module.ts @@ -9,11 +9,11 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; import { AttachSourceMenuModule } from '@elements/menu.attachsource/module'; -import { ListModule as FileListModule } from './lists/file/module'; -import { ListModule as ProcessListModule } from './lists/process/module'; -import { ListModule as SerialListModule } from './lists/serial/module'; -import { ListModule as TcpListModule } from './lists/tcp/module'; -import { ListModule as UdpListModule } from './lists/udp/module'; +// import { ListModule as FileListModule } from './lists/file/module'; +// import { ListModule as ProcessListModule } from './lists/process/module'; +// import { ListModule as SerialListModule } from './lists/serial/module'; +// import { ListModule as TcpListModule } from './lists/tcp/module'; +// import { ListModule as UdpListModule } from './lists/udp/module'; @NgModule({ imports: [ @@ -22,11 +22,11 @@ import { ListModule as UdpListModule } from './lists/udp/module'; MatButtonModule, MatCardModule, MatExpansionModule, - FileListModule, - ProcessListModule, - SerialListModule, - TcpListModule, - UdpListModule, + // FileListModule, + // ProcessListModule, + // SerialListModule, + // TcpListModule, + // UdpListModule, MatMenuModule, MatIconModule, MatDividerModule, diff --git a/application/client/src/app/ui/views/sidebar/observe/template.html b/application/client/src/app/ui/views/sidebar/observe/template.html index 7710be9170..7898535fa1 100644 --- a/application/client/src/app/ui/views/sidebar/observe/template.html +++ b/application/client/src/app/ui/views/sidebar/observe/template.html @@ -1,29 +1,14 @@

Observing Sources - ({{session.observed.count()}}) + ({{operations.length}})
- - - - {{provider.value.panels.list.name}} - {{provider.value.panels.list.desc}} - -
- -
- -
-
- + +
{{operation.getOrigin().getTitle()}}
+
diff --git a/application/client/src/app/ui/views/workspace/sde/state.ts b/application/client/src/app/ui/views/workspace/sde/state.ts index 50967b31cf..3e7070e590 100644 --- a/application/client/src/app/ui/views/workspace/sde/state.ts +++ b/application/client/src/app/ui/views/workspace/sde/state.ts @@ -54,10 +54,6 @@ export class State implements Destroyable { return id === undefined ? '' : getSourceColor(id); } - public desc(source: ObserveOperation): IOriginDetails { - return source.asOrigin().desc(); - } - public send(data: string): Promise { const selected = this.session.stream.sde.selecting().get(); if (selected === undefined) { diff --git a/application/client/src/app/ui/views/workspace/sde/template.html b/application/client/src/app/ui/views/workspace/sde/template.html index 552bf24662..b8eaf663ef 100644 --- a/application/client/src/app/ui/views/workspace/sde/template.html +++ b/application/client/src/app/ui/views/workspace/sde/template.html @@ -1,8 +1,11 @@
>> - + (panel)="panel()" + >

Send command/data to:

- -
\ No newline at end of file + diff --git a/application/client/src/app/ui/views/workspace/title/state.ts b/application/client/src/app/ui/views/workspace/title/state.ts index 73df2cc93a..151ee14393 100644 --- a/application/client/src/app/ui/views/workspace/title/state.ts +++ b/application/client/src/app/ui/views/workspace/title/state.ts @@ -60,21 +60,25 @@ export class State implements Destroyable { return; } this.flags.sde = this.session.stream.sde.isAvailable(); - const sources = this.session.stream.observe().sources(); - if (sources.length === 0) { + const operations = this.session.stream.observe().operations(); + if (operations.length === 0) { this.states.sde = false; this.title = 'no sources are bound with session'; - } else if (sources.length === 1) { + } else if (operations.length === 1) { this.states.sde = !this.session.stream.sde.visibility().hidden(); this.title = ((): string => { - const observe = sources[0].observe; - const desc = observe.origin.desc(); - return `${desc.major}: ${desc.minor}`; + const descriptor = operations[0].getDescriptor(); + if (descriptor) { + return `${descriptor.s_desc} (${descriptor.p_desc})`; + } else { + const origin = operations[0].getOrigin(); + return origin.getTitle(); + } })(); } else { - const running = sources.filter((s) => s.observer !== undefined).length; - this.title = `multiple ${sources.length} sources; ${running} running; ${ - sources.length - running + const running = operations.filter((opr) => opr.isRunning()).length; + this.title = `multiple ${operations.length} sources; ${running} running; ${ + operations.length - running } stopped`; } this.safe().updateRefComp(); diff --git a/application/holder/src/service/sessions/holder.ts b/application/holder/src/service/sessions/holder.ts index 8a23791ce6..ad0471acdf 100644 --- a/application/holder/src/service/sessions/holder.ts +++ b/application/holder/src/service/sessions/holder.ts @@ -8,7 +8,6 @@ import { ICancelablePromise } from 'platform/env/promise'; import { Tys } from 'rustcore'; import * as Events from 'platform/ipc/event'; -import * as path from 'path'; export enum Jobs { search = 'search', @@ -20,11 +19,11 @@ export class Holder { public readonly subscriber: Subscriber; protected readonly jobs: Map = new Map(); protected readonly observing: { - active: Map; - finished: Map; + active: Map; + finished: Set; } = { active: new Map(), - finished: new Map(), + finished: new Set(), }; protected readonly logger: Logger; protected shutdown = false; @@ -71,7 +70,7 @@ export class Holder { public observe(): { start(options: Tys.bindings.SessionSetup): Promise; cancel(uuid: string): Promise; - list(): { [key: string]: string }; + list(): string[]; } { return { start: (options: Tys.bindings.SessionSetup): Promise => { @@ -119,7 +118,7 @@ export class Holder { .finally(() => { job.done(); this.observing.active.delete(observer.uuid()); - this.observing.finished.set(observer.uuid(), options); + this.observing.finished.add(observer.uuid()); Events.IpcEvent.emit( new Events.Observe.Finished.Event({ session: this.session.getUUID(), @@ -128,29 +127,24 @@ export class Holder { }), ); }); - this.observing.active.set(observer.uuid(), { options, observer }); + this.observing.active.set(observer.uuid(), observer); }); }, cancel: (uuid: string): Promise => { - const operation = this.observing.active.get(uuid); - if (operation === undefined) { + const observer = this.observing.active.get(uuid); + if (observer === undefined) { return Promise.reject(new Error(`Operation isn't found`)); } return new Promise((resolve) => { - operation.observer + observer .finally(() => { resolve(); }) .abort(); }); }, - list: (): { [key: string]: string } => { - const list: { [key: string]: string } = {}; - console.error(`Not implemented`); - // this.observing.active.forEach((operation, uuid) => { - // list[uuid] = operation.source.json().to(); - // }); - return list; + list: (): string[] => { + return Array.from(this.observing.active.keys()); }, }; } diff --git a/application/holder/src/service/sessions/requests/observe/list.ts b/application/holder/src/service/sessions/requests/observe/list.ts index d40a64f481..541eb37a5f 100644 --- a/application/holder/src/service/sessions/requests/observe/list.ts +++ b/application/holder/src/service/sessions/requests/observe/list.ts @@ -20,7 +20,7 @@ export const handler = Requests.InjectLogger< resolve( new Requests.Observe.List.Response({ session: stored.session.getUUID(), - sources: stored.observe().list(), + operations: stored.observe().list(), }), ); }); diff --git a/application/holder/src/service/sessions/requests/observe/start.ts b/application/holder/src/service/sessions/requests/observe/start.ts index b97e7a2168..31449f4f2a 100644 --- a/application/holder/src/service/sessions/requests/observe/start.ts +++ b/application/holder/src/service/sessions/requests/observe/start.ts @@ -20,10 +20,11 @@ export const handler = Requests.InjectLogger< stored .observe() .start(request.options) - .then(() => { + .then((uuid: string) => { resolve( new Requests.Observe.Start.Response({ session: stored.session.getUUID(), + uuid, }), ); }) @@ -31,6 +32,7 @@ export const handler = Requests.InjectLogger< resolve( new Requests.Observe.Start.Response({ session: stored.session.getUUID(), + uuid: undefined, error: err.message, }), ); diff --git a/application/holder/src/service/sessions/requests/session/create.ts b/application/holder/src/service/sessions/requests/session/create.ts index ce4cc78a3d..0040765a5f 100644 --- a/application/holder/src/service/sessions/requests/session/create.ts +++ b/application/holder/src/service/sessions/requests/session/create.ts @@ -106,13 +106,14 @@ export const handler = Requests.InjectLogger< }), ); subscriber.register( - session.getEvents().SessionDescriptor.subscribe((descriptor) => { + session.getEvents().SessionDescriptor.subscribe((event) => { if (!sessions.exists(uuid)) { return; } Events.IpcEvent.emit( new Events.Stream.SessionDescriptor.Event({ - descriptor, + descriptor: event.desc, + operation: event.uuid, session: uuid, }), ); diff --git a/application/platform/ipc/event/stream/session_descriptor.ts b/application/platform/ipc/event/stream/session_descriptor.ts index 9195764729..4b85a8b03b 100644 --- a/application/platform/ipc/event/stream/session_descriptor.ts +++ b/application/platform/ipc/event/stream/session_descriptor.ts @@ -6,13 +6,15 @@ import * as validator from '../../../env/obj'; @Define({ name: 'SessionDescriptorUpdated' }) export class Event extends SignatureRequirement { public descriptor: SessionDescriptor; + public operation: string; public session: string; - constructor(input: { descriptor: SessionDescriptor; session: string }) { + constructor(input: { descriptor: SessionDescriptor; session: string; operation: string }) { super(); validator.isObject(input); this.descriptor = validator.getAsObj(input, 'descriptor'); this.session = validator.getAsNotEmptyString(input, 'session'); + this.operation = validator.getAsNotEmptyString(input, 'operation'); } } diff --git a/application/platform/ipc/request/observe/list.ts b/application/platform/ipc/request/observe/list.ts index f1685d68b9..c5bf27d739 100644 --- a/application/platform/ipc/request/observe/list.ts +++ b/application/platform/ipc/request/observe/list.ts @@ -18,13 +18,13 @@ export interface Request extends Interface {} @Define({ name: 'ObserveListResponse' }) export class Response extends SignatureRequirement { public session: string; - public sources: { [key: string]: string }; + public operations: string[]; - constructor(input: { session: string; sources: { [key: string]: string } }) { + constructor(input: { session: string; operations: string[] }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); - this.sources = validator.getAsObj(input, 'sources'); + this.operations = validator.getAsArray(input, 'operations'); } } diff --git a/application/platform/ipc/request/observe/start.ts b/application/platform/ipc/request/observe/start.ts index 911203cc15..b7420aa21d 100644 --- a/application/platform/ipc/request/observe/start.ts +++ b/application/platform/ipc/request/observe/start.ts @@ -20,13 +20,17 @@ export interface Request extends Interface {} @Define({ name: 'StartObserveResponse' }) export class Response extends SignatureRequirement { + /// Session Uuid public session: string; + /// Operation Uuid + public uuid?: string; public error?: string; - constructor(input: { session: string; error?: string }) { + constructor(input: { session: string; uuid: string | undefined; error?: string }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); + this.uuid = validator.getAsNotEmptyStringOrAsUndefined(input, 'uuid'); this.error = validator.getAsNotEmptyStringOrAsUndefined(input, 'error'); } } diff --git a/application/platform/types/bindings/callback.ts b/application/platform/types/bindings/callback.ts index 96d5e7d479..92558966f0 100644 --- a/application/platform/types/bindings/callback.ts +++ b/application/platform/types/bindings/callback.ts @@ -1,61 +1,104 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { AttachmentInfo } from "./attachment"; -import type { FilterMatchList } from "./miscellaneous"; -import type { NativeError } from "./error"; -import type { Progress } from "./progress"; +import type { AttachmentInfo } from './attachment'; +import type { FilterMatchList } from './miscellaneous'; +import type { NativeError } from './error'; +import type { Progress } from './progress'; +import type { SessionDescriptor } from './observe'; /** * Represents events sent to the client. */ -export type CallbackEvent = { "StreamUpdated": number } | "FileRead" | { "SearchUpdated": { -/** - * The number of logs with matches. Can be `0` if the search is reset on the client side. - */ -found: number, -/** - * A map of search conditions and their global match counts within the session. - * - `String`: The search condition. - * - `u64`: The count of matches. - */ -stat: Map, } } | { "IndexedMapUpdated": { -/** - * The number of log entries from search results available for reading. - */ -len: number, } } | { "SearchMapUpdated": FilterMatchList | null } | { "SearchValuesUpdated": Map } | { "AttachmentsUpdated": { -/** - * The size of the attachment in bytes. - */ -len: number, -/** - * The description of the attachment. - */ -attachment: AttachmentInfo, } } | { "Progress": { -/** - * The unique identifier of the operation. - */ -uuid: string, -/** - * Information about the progress. - */ -progress: Progress, } } | { "SessionError": NativeError } | { "OperationError": { -/** - * The unique identifier of the operation that caused the error. - */ -uuid: string, -/** - * The error details. - */ -error: NativeError, } } | { "OperationStarted": string } | { "OperationProcessing": string } | { "OperationDone": OperationDone } | "SessionDestroyed"; +export type CallbackEvent = + | { StreamUpdated: number } + | 'FileRead' + | { + SearchUpdated: { + /** + * The number of logs with matches. Can be `0` if the search is reset on the client side. + */ + found: number; + /** + * A map of search conditions and their global match counts within the session. + * - `String`: The search condition. + * - `u64`: The count of matches. + */ + stat: Map; + }; + } + | { + IndexedMapUpdated: { + /** + * The number of log entries from search results available for reading. + */ + len: number; + }; + } + | { SearchMapUpdated: FilterMatchList | null } + | { + SessionDescriptor: { + /** + * Uuid of the observe operation, which adds new `SessionDescriptor` + */ + uuid: string; + /** + * Added `SessionDescriptor` + */ + desc: SessionDescriptor; + }; + } + | { SearchValuesUpdated: Map } + | { + AttachmentsUpdated: { + /** + * The size of the attachment in bytes. + */ + len: number; + /** + * The description of the attachment. + */ + attachment: AttachmentInfo; + }; + } + | { + Progress: { + /** + * The unique identifier of the operation. + */ + uuid: string; + /** + * Information about the progress. + */ + progress: Progress; + }; + } + | { SessionError: NativeError } + | { + OperationError: { + /** + * The unique identifier of the operation that caused the error. + */ + uuid: string; + /** + * The error details. + */ + error: NativeError; + }; + } + | { OperationStarted: string } + | { OperationProcessing: string } + | { OperationDone: OperationDone } + | 'SessionDestroyed'; /** * Contains the results of an operation. */ -export type OperationDone = { -/** - * The unique identifier of the operation. - */ -uuid: string, -/** - * The results of the operation, if available. - */ -result: Array | null, }; +export type OperationDone = { + /** + * The unique identifier of the operation. + */ + uuid: string; + /** + * The results of the operation, if available. + */ + result: Array | null; +}; diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 1842c6010f..9acb566325 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -42,7 +42,28 @@ export type DltParserSettings = { */ export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; -export type Ident = { name: string; desc: string; io: IODataType; uuid: string }; +export type Ident = { + /** + * A short, human-readable name of the component (parser/source), independent of context. + * Used on the client side to display available components. + */ + name: string; + /** + * A more detailed description of the component. This information is displayed in a separate + * area and can contain an extended explanation. + */ + desc: string; + /** + * The type of data the component produces or expects. + * For sources, this is the type of data they emit. + * For parsers, this is the type of data they expect as input. + */ + io: IODataType; + /** + * A unique identifier of the component. + */ + uuid: string; +}; export type IdentList = Array; @@ -151,9 +172,21 @@ export type SessionAction = | 'Source'; export type SessionDescriptor = { + /** + * Identifier of the parser being used. Provides a general description of the parser. + */ parser: Ident; + /** + * Identifier of the source being used. Provides a general description of the source. + */ source: Ident; + /** + * Parser description in the context of the current session. + */ p_desc: string | null; + /** + * Source description in the context of the current session. For example, instead of "UDP", it might be "UDP on 0.0.0.0::8888". + */ s_desc: string | null; }; diff --git a/cli/development-cli/src/release/mod.rs b/cli/development-cli/src/release/mod.rs index 2b4d8cd3ab..fc5fd8047f 100644 --- a/cli/development-cli/src/release/mod.rs +++ b/cli/development-cli/src/release/mod.rs @@ -87,7 +87,8 @@ pub async fn do_release(development: bool, code_sign_path: Option) -> a ); let results = jobs_runner::run( - &[Target::App, Target::Updater, Target::CliChipmunk], + // &[Target::App, Target::Updater, Target::CliChipmunk], + &[Target::App, Target::Updater], JobType::Build { production: !development, }, @@ -159,26 +160,26 @@ pub async fn do_release(development: bool, code_sign_path: Option) -> a // *** Compressing *** - println!( - "{}", - style("Start Compressing release files...") - .blue() - .bright() - .bold() - ); - - let compress_start = Instant::now(); - - compress().await?; - compress_cli() - .await - .context("Error while compressing Chipmunk CLI binary")?; - - let finish_msg = format!( - "Compressing succeeded in {} seconds.", - compress_start.elapsed().as_secs().max(1) - ); - println!("{}", style(finish_msg).green().bold()); + // println!( + // "{}", + // style("Start Compressing release files...") + // .blue() + // .bright() + // .bold() + // ); + + // let compress_start = Instant::now(); + + // compress().await?; + // compress_cli() + // .await + // .context("Error while compressing Chipmunk CLI binary")?; + + // let finish_msg = format!( + // "Compressing succeeded in {} seconds.", + // compress_start.elapsed().as_secs().max(1) + // ); + // println!("{}", style(finish_msg).green().bold()); print_log_separator(); From 5ad915d6d4f3bafa52b69d3ee43b2e243fb2fd0b Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 4 Jul 2025 15:44:18 +0200 Subject: [PATCH 082/121] Remove Observe interface and class --- .../apps/rustcore/ts-bindings/src/api/jobs.ts | 22 - .../ts-bindings/src/api/session.stream.ts | 2 +- .../rustcore/ts-bindings/src/api/session.ts | 4 +- .../apps/rustcore/ts-bindings/src/index.ts | 1 - .../ts-bindings/src/native/native.session.ts | 4 +- .../client/src/app/schema/render/columns.ts | 5 - .../client/src/app/schema/render/dlt.ts | 81 ---- .../client/src/app/schema/render/index.ts | 5 - .../client/src/app/schema/render/plugin.ts | 5 - .../client/src/app/schema/render/someip.ts | 76 ---- .../client/src/app/schema/render/text.ts | 7 +- .../client/src/app/schema/render/tools.ts | 60 --- application/client/src/app/service/actions.ts | 111 +++-- .../src/app/service/actions/file.any.ts | 3 +- .../src/app/service/actions/file.dlt.ts | 1 - .../app/service/actions/file.parserPlugin.ts | 1 - .../src/app/service/actions/file.pcap.ts | 1 - .../src/app/service/actions/file.pcapng.ts | 1 - .../src/app/service/actions/file.plugins.ts | 13 +- .../src/app/service/actions/file.text.ts | 1 - .../src/app/service/actions/folder.any.ts | 3 +- .../src/app/service/actions/folder.dlt.ts | 1 - .../service/actions/folder.parserPlugin.ts | 1 - .../src/app/service/actions/folder.pcap.ts | 1 - .../src/app/service/actions/folder.pcapng.ts | 1 - .../src/app/service/actions/folder.plugins.ts | 1 - .../src/app/service/actions/folder.text.ts | 1 - .../service/actions/serial.parserPlugin.ts | 1 - .../src/app/service/actions/serial.text.ts | 1 - .../src/app/service/actions/stdout.plugin.ts | 1 - .../src/app/service/actions/stdout.text.ts | 1 - .../client/src/app/service/actions/tcp.dlt.ts | 1 - .../app/service/actions/tcp.parserPlugin.ts | 1 - .../client/src/app/service/actions/udp.dlt.ts | 1 - .../app/service/actions/udp.parserPlugin.ts | 1 - application/client/src/app/service/bridge.ts | 25 +- .../client/src/app/service/cli/observe.ts | 1 - .../app/service/history/definition.concat.ts | 33 +- .../app/service/history/definition.file.ts | 15 +- .../app/service/history/definition.stream.ts | 60 +-- .../src/app/service/history/definition.ts | 38 +- application/client/src/app/service/recent.ts | 27 +- .../client/src/app/service/recent/action.ts | 192 +++++---- .../src/app/service/recent/compatibility.ts | 181 ++++---- application/client/src/app/service/session.ts | 3 - .../app/service/session/dependencies/cli.ts | 326 ++++++++------- .../app/service/session/dependencies/info.ts | 116 +++--- .../service/session/dependencies/stream.ts | 16 +- .../service/session/dependencies/teamwork.ts | 1 - .../client/src/app/service/session/session.ts | 5 - .../elements/menu.attachsource/component.ts | 1 - .../ui/elements/navigator/providers/entity.ts | 21 +- .../navigator/providers/provider.files.ts | 47 ++- .../navigator/providers/provider.recent.ts | 18 +- .../elements/navigator/providers/provider.ts | 4 +- .../elements/navigator/providers/providers.ts | 6 +- .../src/app/ui/elements/navigator/state.ts | 6 +- .../src/app/ui/elements/recent/component.ts | 22 +- .../app/ui/elements/recent/icon/component.ts | 68 +-- .../app/ui/elements/recent/icon/template.html | 5 +- .../ui/elements/recent/nature/component.ts | 44 +- .../recent/nature/concat/component.ts | 31 -- .../elements/recent/nature/concat/styles.less | 15 - .../recent/nature/concat/template.html | 1 - .../elements/recent/nature/file/component.ts | 27 -- .../elements/recent/nature/file/styles.less | 19 - .../elements/recent/nature/file/template.html | 2 - .../app/ui/elements/recent/nature/module.ts | 16 +- .../recent/nature/process/component.ts | 22 - .../recent/nature/process/styles.less | 19 - .../recent/nature/process/template.html | 2 - .../recent/nature/serial/component.ts | 22 - .../elements/recent/nature/serial/styles.less | 15 - .../recent/nature/serial/template.html | 1 - .../elements/recent/nature/tcp/component.ts | 22 - .../ui/elements/recent/nature/tcp/styles.less | 15 - .../elements/recent/nature/tcp/template.html | 1 - .../ui/elements/recent/nature/template.html | 6 +- .../elements/recent/nature/udp/component.ts | 22 - .../ui/elements/recent/nature/udp/styles.less | 15 - .../elements/recent/nature/udp/template.html | 4 - .../ui/elements/recent/parser/component.ts | 29 +- .../elements/recent/parser/dlt/component.ts | 42 -- .../ui/elements/recent/parser/dlt/styles.less | 31 -- .../elements/recent/parser/dlt/template.html | 14 - .../app/ui/elements/recent/parser/module.ts | 4 +- .../recent/parser/someip/component.ts | 28 -- .../elements/recent/parser/someip/styles.less | 31 -- .../recent/parser/someip/template.html | 13 - .../ui/elements/recent/parser/template.html | 5 +- .../src/app/ui/elements/recent/state.ts | 42 +- .../src/app/ui/elements/recent/template.html | 6 +- .../src/app/ui/elements/tree/component.ts | 3 +- application/client/src/app/ui/tabs/module.ts | 4 +- .../app/ui/tabs/multiplefiles/file.holder.ts | 2 +- .../src/app/ui/tabs/multiplefiles/state.ts | 3 +- .../tabs/multiplefiles/structure/component.ts | 2 +- .../client/src/app/ui/tabs/observe/action.ts | 50 --- .../src/app/ui/tabs/observe/component.ts | 47 --- .../tabs/observe/config-schema/component.ts | 34 -- .../observe/config-schema/entry/component.ts | 19 - .../observe/config-schema/entry/styles.less | 6 - .../observe/config-schema/entry/template.html | 41 -- .../ui/tabs/observe/config-schema/module.ts | 63 --- .../config-schema/renders/bool/component.ts | 32 -- .../config-schema/renders/bool/styles.less | 1 - .../config-schema/renders/bool/template.html | 9 - .../config-schema/renders/dirs/component.ts | 55 --- .../config-schema/renders/dirs/styles.less | 27 -- .../config-schema/renders/dirs/template.html | 23 -- .../renders/dropdown/component.ts | 47 --- .../renders/dropdown/styles.less | 7 - .../renders/dropdown/template.html | 7 - .../config-schema/renders/files/component.ts | 65 --- .../config-schema/renders/files/styles.less | 14 - .../config-schema/renders/files/template.html | 19 - .../config-schema/renders/float/component.ts | 53 --- .../config-schema/renders/float/styles.less | 12 - .../config-schema/renders/float/template.html | 11 - .../renders/integer/component.ts | 49 --- .../config-schema/renders/integer/styles.less | 12 - .../renders/integer/template.html | 11 - .../config-schema/renders/string/component.ts | 47 --- .../config-schema/renders/string/styles.less | 7 - .../renders/string/template.html | 10 - .../ui/tabs/observe/config-schema/state.ts | 52 --- .../ui/tabs/observe/config-schema/styles.less | 10 - .../tabs/observe/config-schema/template.html | 6 - .../app/ui/tabs/observe/error/component.ts | 34 -- .../src/app/ui/tabs/observe/error/module.ts | 13 - .../src/app/ui/tabs/observe/error/styles.less | 14 - .../app/ui/tabs/observe/error/template.html | 6 - .../client/src/app/ui/tabs/observe/module.ts | 30 -- .../tabs/observe/origin/concat/component.ts | 58 --- .../ui/tabs/observe/origin/concat/module.ts | 20 - .../ui/tabs/observe/origin/concat/styles.less | 37 -- .../tabs/observe/origin/concat/template.html | 6 - .../ui/tabs/observe/origin/file/component.ts | 58 --- .../app/ui/tabs/observe/origin/file/module.ts | 20 - .../ui/tabs/observe/origin/file/styles.less | 12 - .../ui/tabs/observe/origin/file/template.html | 6 - .../tabs/observe/origin/stream/component.ts | 60 --- .../ui/tabs/observe/origin/stream/module.ts | 22 - .../ui/tabs/observe/origin/stream/styles.less | 12 - .../tabs/observe/origin/stream/template.html | 17 - .../stream/transport/listed/component.ts | 132 ------ .../origin/stream/transport/listed/module.ts | 12 - .../stream/transport/listed/styles.less | 57 --- .../stream/transport/listed/template.html | 10 - .../setup/bases/process/component.ts | 207 ---------- .../transport/setup/bases/process/error.ts | 30 -- .../transport/setup/bases/serial/component.ts | 97 ----- .../transport/setup/bases/serial/error.ts | 27 -- .../transport/setup/bases/serial/module.ts | 34 -- .../transport/setup/bases/tcp/component.ts | 46 --- .../stream/transport/setup/bases/tcp/error.ts | 89 ---- .../transport/setup/bases/udp/component.ts | 54 --- .../stream/transport/setup/bases/udp/error.ts | 88 ---- .../transport/setup/bases/udp/module.ts | 24 -- .../setup/bases/udp/multicast/component.ts | 44 -- .../setup/bases/udp/multicast/styles.less | 26 -- .../setup/bases/udp/multicast/template.html | 22 - .../setup/complete/process/component.ts | 70 ---- .../setup/complete/process/module.ts | 24 -- .../setup/complete/process/styles.less | 61 --- .../setup/complete/process/template.html | 53 --- .../setup/complete/serial/component.ts | 30 -- .../transport/setup/complete/serial/module.ts | 41 -- .../setup/complete/serial/styles.less | 24 -- .../setup/complete/serial/template.html | 96 ----- .../transport/setup/complete/tcp/component.ts | 30 -- .../transport/setup/complete/tcp/module.ts | 31 -- .../transport/setup/complete/tcp/styles.less | 14 - .../setup/complete/tcp/template.html | 20 - .../transport/setup/complete/udp/component.ts | 30 -- .../transport/setup/complete/udp/module.ts | 34 -- .../transport/setup/complete/udp/styles.less | 28 -- .../setup/complete/udp/template.html | 20 - .../stream/transport/setup/component.ts | 52 --- .../origin/stream/transport/setup/module.ts | 32 -- .../setup/quick/process/component.ts | 59 --- .../transport/setup/quick/process/module.ts | 24 -- .../transport/setup/quick/process/styles.less | 67 --- .../setup/quick/process/template.html | 53 --- .../transport/setup/quick/serial/component.ts | 21 - .../transport/setup/quick/serial/module.ts | 39 -- .../transport/setup/quick/serial/styles.less | 12 - .../setup/quick/serial/template.html | 4 - .../transport/setup/quick/tcp/component.ts | 21 - .../transport/setup/quick/tcp/module.ts | 32 -- .../transport/setup/quick/tcp/styles.less | 25 -- .../transport/setup/quick/tcp/template.html | 13 - .../transport/setup/quick/udp/component.ts | 21 - .../transport/setup/quick/udp/module.ts | 34 -- .../transport/setup/quick/udp/styles.less | 31 -- .../transport/setup/quick/udp/template.html | 21 - .../origin/stream/transport/setup/state.ts | 45 -- .../stream/transport/setup/states/process.ts | 109 ----- .../stream/transport/setup/states/serial.ts | 269 ------------ .../stream/transport/setup/states/tcp.ts | 38 -- .../stream/transport/setup/states/udp.ts | 45 -- .../origin/stream/transport/setup/styles.less | 19 - .../stream/transport/setup/template.html | 4 - .../tabs/observe/parsers/extra/component.ts | 26 -- .../observe/parsers/extra/dlt/component.ts | 168 -------- .../tabs/observe/parsers/extra/dlt/module.ts | 40 -- .../tabs/observe/parsers/extra/dlt/state.ts | 192 --------- .../parsers/extra/dlt/structure/component.ts | 98 ----- .../parsers/extra/dlt/structure/section.ts | 23 -- .../parsers/extra/dlt/structure/statentity.ts | 61 --- .../parsers/extra/dlt/structure/styles.less | 40 -- .../parsers/extra/dlt/structure/template.html | 68 --- .../observe/parsers/extra/dlt/styles.less | 116 ------ .../tabs/observe/parsers/extra/dlt/summary.ts | 51 --- .../observe/parsers/extra/dlt/template.html | 67 --- .../ui/tabs/observe/parsers/extra/module.ts | 17 - .../observe/parsers/extra/someip/component.ts | 30 -- .../observe/parsers/extra/someip/module.ts | 35 -- .../observe/parsers/extra/someip/state.ts | 3 - .../observe/parsers/extra/someip/styles.less | 111 ----- .../parsers/extra/someip/template.html | 2 - .../ui/tabs/observe/parsers/extra/styles.less | 111 ----- .../tabs/observe/parsers/extra/template.html | 2 - .../tabs/observe/parsers/general/component.ts | 28 -- .../observe/parsers/general/dlt/component.ts | 54 --- .../observe/parsers/general/dlt/module.ts | 35 -- .../tabs/observe/parsers/general/dlt/state.ts | 118 ------ .../observe/parsers/general/dlt/styles.less | 17 - .../observe/parsers/general/dlt/template.html | 48 --- .../ui/tabs/observe/parsers/general/module.ts | 31 -- .../parsers/general/plugin/component.ts | 58 --- .../observe/parsers/general/plugin/module.ts | 37 -- .../observe/parsers/general/plugin/state.ts | 100 ----- .../parsers/general/plugin/styles.less | 8 - .../parsers/general/plugin/template.html | 6 - .../parsers/general/someip/component.ts | 33 -- .../observe/parsers/general/someip/module.ts | 35 -- .../observe/parsers/general/someip/state.ts | 47 --- .../parsers/general/someip/styles.less | 18 - .../parsers/general/someip/template.html | 24 -- .../tabs/observe/parsers/general/styles.less | 111 ----- .../observe/parsers/general/template.html | 18 - .../observe/parsers/general/text/component.ts | 17 - .../observe/parsers/general/text/module.ts | 14 - .../observe/parsers/general/text/styles.less | 9 - .../parsers/general/text/template.html | 7 - .../src/app/ui/tabs/observe/parsers/module.ts | 12 - .../src/app/ui/tabs/observe/parsers/state.ts | 16 - .../client/src/app/ui/tabs/observe/state.ts | 347 ---------------- .../src/app/ui/tabs/observe/styles.less | 74 ---- .../src/app/ui/tabs/observe/template.html | 35 -- .../sidebar/observe/element/component.ts | 44 -- .../views/sidebar/observe/element/element.ts | 42 -- .../sidebar/observe/element/file/component.ts | 55 --- .../sidebar/observe/element/file/styles.less | 51 --- .../observe/element/file/template.html | 14 - .../views/sidebar/observe/element/module.ts | 16 - .../observe/element/process/component.ts | 61 --- .../observe/element/process/styles.less | 51 --- .../observe/element/process/template.html | 16 - .../observe/element/serial/component.ts | 60 --- .../observe/element/serial/styles.less | 51 --- .../observe/element/serial/template.html | 16 - .../observe/element/signature/component.ts | 36 -- .../observe/element/signature/styles.less | 26 -- .../observe/element/signature/template.html | 2 - .../views/sidebar/observe/element/styles.less | 18 - .../sidebar/observe/element/tcp/component.ts | 60 --- .../sidebar/observe/element/tcp/styles.less | 51 --- .../sidebar/observe/element/tcp/template.html | 13 - .../sidebar/observe/element/template.html | 6 - .../sidebar/observe/element/udp/component.ts | 60 --- .../sidebar/observe/element/udp/styles.less | 51 --- .../sidebar/observe/element/udp/template.html | 14 - .../views/sidebar/observe/lists/component.ts | 40 -- .../sidebar/observe/lists/file/component.ts | 164 -------- .../sidebar/observe/lists/file/module.ts | 13 - .../sidebar/observe/lists/file/styles.less | 23 -- .../sidebar/observe/lists/file/template.html | 15 - .../observe/lists/process/component.ts | 100 ----- .../sidebar/observe/lists/process/module.ts | 13 - .../sidebar/observe/lists/process/styles.less | 15 - .../observe/lists/process/template.html | 12 - .../sidebar/observe/lists/serial/component.ts | 95 ----- .../sidebar/observe/lists/serial/module.ts | 13 - .../sidebar/observe/lists/serial/styles.less | 12 - .../observe/lists/serial/template.html | 12 - .../sidebar/observe/lists/tcp/component.ts | 95 ----- .../views/sidebar/observe/lists/tcp/module.ts | 13 - .../sidebar/observe/lists/tcp/styles.less | 15 - .../sidebar/observe/lists/tcp/template.html | 12 - .../sidebar/observe/lists/udp/component.ts | 95 ----- .../views/sidebar/observe/lists/udp/module.ts | 13 - .../sidebar/observe/lists/udp/styles.less | 15 - .../sidebar/observe/lists/udp/template.html | 12 - .../app/ui/views/sidebar/observe/module.ts | 5 - .../ui/views/sidebar/observe/states/files.ts | 11 - .../views/sidebar/observe/states/process.ts | 12 - .../ui/views/sidebar/observe/states/serial.ts | 10 - .../ui/views/sidebar/observe/states/state.ts | 19 - .../ui/views/sidebar/observe/states/tcp.ts | 10 - .../ui/views/sidebar/observe/states/udp.ts | 10 - .../src/app/ui/views/workspace/sde/state.ts | 1 - application/holder/src/env/fs/index.ts | 3 +- .../holder/src/service/actions/open.file.ts | 2 +- .../holder/src/service/actions/open.folder.ts | 2 +- .../holder/src/service/actions/stream.ts | 10 +- .../holder/src/service/bridge/file/select.ts | 2 +- .../src/service/bridge/folder/select.ts | 2 +- application/holder/src/service/cli.ts | 53 ++- application/holder/src/service/cli/open.ts | 77 ++-- application/holder/src/service/cli/parser.ts | 75 ++-- application/holder/src/service/cli/search.ts | 43 +- application/holder/src/service/cli/stream.ts | 386 +++++++++--------- application/holder/src/service/menu.ts | 169 ++++---- .../holder/src/service/sessions/holder.ts | 53 ++- application/holder/src/service/unbound.ts | 9 - .../holder/src/service/unbound/dlt/index.ts | 1 - .../holder/src/service/unbound/dlt/stat.ts | 42 -- .../holder/src/service/unbound/index.ts | 2 - .../src/service/unbound/someip/index.ts | 1 - .../src/service/unbound/someip/statistic.ts | 42 -- .../platform/ipc/request/actions/open.file.ts | 2 +- .../ipc/request/actions/open.folder.ts | 2 +- .../platform/ipc/request/actions/stream.ts | 13 +- .../platform/ipc/request/cli/observe.ts | 12 +- application/platform/ipc/request/dlt/index.ts | 1 - application/platform/ipc/request/dlt/stat.ts | 28 -- .../platform/ipc/request/file/select.ts | 2 +- .../platform/ipc/request/folder/select.ts | 2 +- application/platform/ipc/request/index.ts | 2 - .../platform/ipc/request/observe/sources.ts | 2 +- .../platform/ipc/request/someip/index.ts | 1 - .../platform/ipc/request/someip/statistic.ts | 29 -- application/platform/package.json | 20 +- application/platform/types/files.ts | 42 +- application/platform/types/index.ts | 2 - .../platform/types/observe/compatibility.ts | 91 ----- .../platform/types/observe/configuration.ts | 254 ------------ .../platform/types/observe/description.ts | 39 -- application/platform/types/observe/factory.ts | 370 ----------------- application/platform/types/observe/index.ts | 207 ---------- .../platform/types/observe/origin/concat.ts | 173 -------- .../platform/types/observe/origin/file.ts | 170 -------- .../platform/types/observe/origin/index.ts | 258 ------------ .../platform/types/observe/origin/plugin.ts | 123 ------ .../platform/types/observe/origin/stream.ts | 150 ------- .../types/observe/origin/stream/index.ts | 255 ------------ .../observe/origin/stream/process/index.ts | 109 ----- .../observe/origin/stream/serial/index.ts | 105 ----- .../types/observe/origin/stream/tcp/index.ts | 80 ---- .../types/observe/origin/stream/udp/index.ts | 113 ----- .../types/observe/parser/dlt/index.ts | 185 --------- .../platform/types/observe/parser/index.ts | 235 ----------- .../types/observe/parser/plugin/index.ts | 129 ------ .../types/observe/parser/someip/index.ts | 87 ---- .../types/observe/parser/text/index.ts | 62 --- application/platform/types/observe/sde.ts | 3 - .../types/observe/types/file/index.ts | 38 -- .../platform/types/observe/types/index.ts | 3 - .../types/observe/types/plugin/index.ts | 1 - .../platform/types/sessionsetup/index.ts | 14 - .../types/sourcelink.ts => source.ts} | 0 363 files changed, 1182 insertions(+), 13717 deletions(-) delete mode 100644 application/client/src/app/schema/render/dlt.ts delete mode 100644 application/client/src/app/schema/render/someip.ts delete mode 100644 application/client/src/app/schema/render/tools.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/concat/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/concat/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/concat/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/file/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/file/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/file/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/process/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/process/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/process/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/serial/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/serial/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/serial/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/tcp/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/tcp/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/tcp/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/udp/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/udp/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/udp/template.html delete mode 100644 application/client/src/app/ui/elements/recent/parser/dlt/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/parser/dlt/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/parser/dlt/template.html delete mode 100644 application/client/src/app/ui/elements/recent/parser/someip/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/parser/someip/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/parser/someip/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/action.ts delete mode 100644 application/client/src/app/ui/tabs/observe/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/entry/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/entry/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/entry/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/bool/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/bool/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/bool/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/files/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/files/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/files/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/float/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/float/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/float/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/integer/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/integer/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/integer/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/string/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/string/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/renders/string/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/config-schema/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/error/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/error/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/error/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/error/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/concat/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/concat/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/concat/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/concat/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/file/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/file/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/file/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/file/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/error.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/error.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/error.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/error.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/process.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/serial.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/tcp.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/udp.ts delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/section.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/statentity.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/summary.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/dlt/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/someip/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/someip/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/someip/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/someip/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/someip/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/extra/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/dlt/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/dlt/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/dlt/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/dlt/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/dlt/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/plugin/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/plugin/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/plugin/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/plugin/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/plugin/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/someip/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/someip/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/someip/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/someip/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/someip/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/text/component.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/text/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/text/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/general/text/template.html delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/module.ts delete mode 100644 application/client/src/app/ui/tabs/observe/parsers/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/state.ts delete mode 100644 application/client/src/app/ui/tabs/observe/styles.less delete mode 100644 application/client/src/app/ui/tabs/observe/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/element.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/file/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/file/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/file/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/process/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/process/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/process/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/serial/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/serial/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/serial/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/signature/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/signature/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/signature/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/tcp/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/tcp/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/tcp/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/udp/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/udp/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/element/udp/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/file/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/file/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/file/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/process/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/process/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/process/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/process/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/serial/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/serial/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/serial/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/serial/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/tcp/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/tcp/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/tcp/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/tcp/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/udp/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/udp/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/udp/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/lists/udp/template.html delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/files.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/process.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/serial.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/state.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/tcp.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/states/udp.ts delete mode 100644 application/holder/src/service/unbound/dlt/index.ts delete mode 100644 application/holder/src/service/unbound/dlt/stat.ts delete mode 100644 application/holder/src/service/unbound/someip/index.ts delete mode 100644 application/holder/src/service/unbound/someip/statistic.ts delete mode 100644 application/platform/ipc/request/dlt/index.ts delete mode 100644 application/platform/ipc/request/dlt/stat.ts delete mode 100644 application/platform/ipc/request/someip/index.ts delete mode 100644 application/platform/ipc/request/someip/statistic.ts delete mode 100644 application/platform/types/observe/compatibility.ts delete mode 100644 application/platform/types/observe/configuration.ts delete mode 100644 application/platform/types/observe/description.ts delete mode 100644 application/platform/types/observe/factory.ts delete mode 100644 application/platform/types/observe/index.ts delete mode 100644 application/platform/types/observe/origin/concat.ts delete mode 100644 application/platform/types/observe/origin/file.ts delete mode 100644 application/platform/types/observe/origin/index.ts delete mode 100644 application/platform/types/observe/origin/plugin.ts delete mode 100644 application/platform/types/observe/origin/stream.ts delete mode 100644 application/platform/types/observe/origin/stream/index.ts delete mode 100644 application/platform/types/observe/origin/stream/process/index.ts delete mode 100644 application/platform/types/observe/origin/stream/serial/index.ts delete mode 100644 application/platform/types/observe/origin/stream/tcp/index.ts delete mode 100644 application/platform/types/observe/origin/stream/udp/index.ts delete mode 100644 application/platform/types/observe/parser/dlt/index.ts delete mode 100644 application/platform/types/observe/parser/index.ts delete mode 100644 application/platform/types/observe/parser/plugin/index.ts delete mode 100644 application/platform/types/observe/parser/someip/index.ts delete mode 100644 application/platform/types/observe/parser/text/index.ts delete mode 100644 application/platform/types/observe/sde.ts delete mode 100644 application/platform/types/observe/types/file/index.ts delete mode 100644 application/platform/types/observe/types/index.ts delete mode 100644 application/platform/types/observe/types/plugin/index.ts delete mode 100644 application/platform/types/sessionsetup/index.ts rename application/platform/types/{observe/types/sourcelink.ts => source.ts} (100%) diff --git a/application/apps/rustcore/ts-bindings/src/api/jobs.ts b/application/apps/rustcore/ts-bindings/src/api/jobs.ts index 4095fee5cd..93d36de17e 100644 --- a/application/apps/rustcore/ts-bindings/src/api/jobs.ts +++ b/application/apps/rustcore/ts-bindings/src/api/jobs.ts @@ -2,7 +2,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Base, Cancelled, decode } from '../native/native.jobs'; import { error } from 'platform/log/utils'; import { IFilter } from 'platform/types/filter'; -import { SomeipStatistic } from 'platform/types/observe/parser/someip'; import { FoldersScanningResult, DltStatisticInfo, @@ -132,27 +131,6 @@ export class Jobs extends Base { return job; } - public getSomeipStatistic(paths: string[]): CancelablePromise { - const sequence = this.sequence(); - const job: CancelablePromise = this.execute( - (buf: Uint8Array): any | Error => { - const decoded = decode(buf, protocol.decodeCommandOutcomeWithString); - if (decoded instanceof Error) { - return decoded; - } - try { - return JSON.parse(decoded) as SomeipStatistic; - } catch (e) { - return new Error(error(e)); - } - }, - this.native.getSomeipStatistic(sequence, paths), - sequence, - 'getSomeipStatistic', - ); - return job; - } - public getShellProfiles(): CancelablePromise { const sequence = this.sequence(); const job: CancelablePromise = this.execute( diff --git a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts index 4e314503f2..6cfdf5d299 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts @@ -8,7 +8,7 @@ import { Executors } from './executors/session.stream.executors'; import { EFileOptionsRequirements } from './executors/session.stream.observe.executor'; import { GrabbedElement } from 'platform/types/bindings/miscellaneous'; import { IRange } from 'platform/types/range'; -import { ISourceLink } from 'platform/types/observe/types'; +import { ISourceLink } from 'platform/types/source'; import { Attachment, IndexingMode } from 'platform/types/content'; import { SessionSetup } from 'platform/types/bindings'; import { TextExportOptions } from 'platform/types/exporting'; diff --git a/application/apps/rustcore/ts-bindings/src/api/session.ts b/application/apps/rustcore/ts-bindings/src/api/session.ts index 0a7dededa2..8820c18201 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.ts @@ -20,10 +20,8 @@ export { IEventMatchesUpdated, IEventIndexedMapUpdated, } from '../api/session.provider'; -export { EventProvider, SessionStream, SessionSearch }; -export * as $ from 'platform/types/observe'; -export * as Factory from 'platform/types/observe/factory'; +export { EventProvider, SessionStream, SessionSearch }; enum ESessionState { destroyed, diff --git a/application/apps/rustcore/ts-bindings/src/index.ts b/application/apps/rustcore/ts-bindings/src/index.ts index 9265072db6..0be61cd1ba 100644 --- a/application/apps/rustcore/ts-bindings/src/index.ts +++ b/application/apps/rustcore/ts-bindings/src/index.ts @@ -27,7 +27,6 @@ export { Tracker } from './api/tracker'; export { Components } from './api/components'; export { Units, Events, Interfaces }; -export * as $ from 'platform/types/observe'; export * as Bindings from 'platform/types/bindings'; export * as Tys from 'platform/types'; diff --git a/application/apps/rustcore/ts-bindings/src/native/native.session.ts b/application/apps/rustcore/ts-bindings/src/native/native.session.ts index a2e65986e6..ab1ce962fa 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.session.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.session.ts @@ -9,8 +9,8 @@ import { EFileOptionsRequirements } from '../api/executors/session.stream.observ import { Type, Source, NativeError } from '../interfaces/errors'; import { v4 as uuidv4 } from 'uuid'; import { getValidNum } from '../util/numbers'; -import { IRange, fromTuple } from 'platform/types/range'; -import { ISourceLink } from 'platform/types/observe/types'; +import { IRange } from 'platform/types/range'; +import { ISourceLink } from 'platform/types/source'; import { IndexingMode, Attachment } from 'platform/types/content'; import { Logger, utils } from 'platform/log'; import { scope } from 'platform/env/scope'; diff --git a/application/client/src/app/schema/render/columns.ts b/application/client/src/app/schema/render/columns.ts index 6e67c89f1f..2ab96532db 100644 --- a/application/client/src/app/schema/render/columns.ts +++ b/application/client/src/app/schema/render/columns.ts @@ -7,7 +7,6 @@ import { LimittedValue } from '@ui/env/entities/value.limited'; import { Render } from './index'; import * as num from '@platform/env/num'; -import { Protocol } from '@platform/types/observe/parser'; const MIN_COLUMN_WIDTH = 30; const MAX_COLUMN_WIDTH = 600; @@ -26,10 +25,6 @@ export class ColumnsRender extends Render { ); } - public override protocol(): Protocol { - return Protocol.Dlt; - } - public override columns(): number { return this.headers.length; } diff --git a/application/client/src/app/schema/render/dlt.ts b/application/client/src/app/schema/render/dlt.ts deleted file mode 100644 index 7970be44af..0000000000 --- a/application/client/src/app/schema/render/dlt.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Render } from './index'; -import { Columns } from './columns'; -import { Protocol } from '@platform/types/observe/parser/index'; - -const MIN_COLUMN_WIDTH = 30; -const MAX_COLUMN_WIDTH = 600; -// public readonly widths: number[] = [150, 80, 80, 80, 80, 80, 80, 80, 80, 80, -1]; - -export class Implementation extends Render { - public static HEADERS = [ - { - caption: 'Datetime', - desc: 'Datetime', - }, - { - caption: 'ECUID', - desc: 'ECU', - }, - { - caption: 'VERS', - desc: 'Dlt Protocol Version (VERS)', - }, - { - caption: 'SID', - desc: 'Session ID (SEID)', - }, - { - caption: 'MCNT', - desc: 'Message counter (MCNT)', - }, - { - caption: 'TMS', - desc: 'Timestamp (TMSP)', - }, - { - caption: 'EID', - desc: 'ECU', - }, - { - caption: 'APID', - desc: 'Application ID (APID)', - }, - { - caption: 'CTID', - desc: 'Context ID (CTID)', - }, - { - caption: 'MSTP', - desc: 'Message Type (MSTP)', - }, - { - caption: 'PAYLOAD', - desc: 'Payload', - }, - ]; - - constructor() { - super(); - this.setBoundEntity( - new Columns( - // Implementation.HEADERS, - [], - true, - [150, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1], - MIN_COLUMN_WIDTH, - MAX_COLUMN_WIDTH, - ), - ); - } - - public override protocol(): Protocol { - return Protocol.Dlt; - } - - public override columns(): number { - return Implementation.HEADERS.length; - } - public override delimiter(): string | undefined { - return `\u0004`; - } -} diff --git a/application/client/src/app/schema/render/index.ts b/application/client/src/app/schema/render/index.ts index d57da7cc9c..43f28ee622 100644 --- a/application/client/src/app/schema/render/index.ts +++ b/application/client/src/app/schema/render/index.ts @@ -1,5 +1,3 @@ -import { Protocol } from '@platform/types/observe/parser/index'; - export interface RenderReference { new (): Render; } @@ -7,9 +5,6 @@ export interface RenderReference { export abstract class Render { private _bound: T | undefined; - // TODO: not need any more this method - abstract protocol(): Protocol; - public delimiter(): string | undefined { return undefined; } diff --git a/application/client/src/app/schema/render/plugin.ts b/application/client/src/app/schema/render/plugin.ts index 63fd573874..a045ff6068 100644 --- a/application/client/src/app/schema/render/plugin.ts +++ b/application/client/src/app/schema/render/plugin.ts @@ -1,7 +1,6 @@ import { PluginEntity } from '@platform/types/bindings'; import { Render } from './index'; import { Columns } from './columns'; -import { Protocol } from '@platform/types/observe/parser'; export class Implementation extends Render { private _columnsCount: number = 0; @@ -46,10 +45,6 @@ export class Implementation extends Render { } } - override protocol(): Protocol { - return Protocol.Plugin; - } - public override columns(): number { return this._columnsCount; } diff --git a/application/client/src/app/schema/render/someip.ts b/application/client/src/app/schema/render/someip.ts deleted file mode 100644 index 42aef54474..0000000000 --- a/application/client/src/app/schema/render/someip.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Render } from './index'; -import { Columns } from './columns'; -import { Protocol } from '@platform/types/observe/parser/index'; - -const MIN_COLUMN_WIDTH = 30; -const MAX_COLUMN_WIDTH = 600; - -export class Implementation extends Render { - public static HEADERS = [ - { - caption: 'SOME/IP', - desc: 'The Message-Kind.', - }, - { - caption: 'SERV', - desc: 'The Service-ID', - }, - { - caption: 'METH', - desc: 'The Method-ID', - }, - { - caption: 'LENG', - desc: 'The Length-Field', - }, - { - caption: 'CLID', - desc: 'The Client-ID', - }, - { - caption: 'SEID', - desc: 'The Session-ID', - }, - { - caption: 'IVER', - desc: 'The Interface-Version', - }, - { - caption: 'MSTP', - desc: 'The Message-Type', - }, - { - caption: 'RETC', - desc: 'The Return-Code', - }, - { - caption: 'PAYLOAD', - desc: 'Payload', - }, - ]; - - constructor() { - super(); - this.setBoundEntity( - new Columns( - // Implementation.HEADERS, - [], - true, - [50, 50, 50, 30, 30, 30, 30, 30, 30, -1], - MIN_COLUMN_WIDTH, - MAX_COLUMN_WIDTH, - ), - ); - } - - public override protocol(): Protocol { - return Protocol.SomeIp; - } - - public override columns(): number { - return Implementation.HEADERS.length; - } - public override delimiter(): string | undefined { - return `\u0004`; - } -} diff --git a/application/client/src/app/schema/render/text.ts b/application/client/src/app/schema/render/text.ts index 0c822a8db9..b24cc56e43 100644 --- a/application/client/src/app/schema/render/text.ts +++ b/application/client/src/app/schema/render/text.ts @@ -1,8 +1,3 @@ import { Render } from './index'; -import { Protocol } from '@platform/types/observe/parser/index'; -export class TextRender extends Render { - public override protocol(): Protocol { - return Protocol.Text; - } -} +export class TextRender extends Render {} diff --git a/application/client/src/app/schema/render/tools.ts b/application/client/src/app/schema/render/tools.ts deleted file mode 100644 index e3463f9fa0..0000000000 --- a/application/client/src/app/schema/render/tools.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Implementation as Dlt } from './dlt'; -import { Implementation as SomeIp } from './someip'; -import { TextRender } from './text'; -import { Implementation as Plugin } from './plugin'; -import { Render, RenderReference } from './index'; -import { Session } from '@service/session/session'; -import { Observe } from '@platform/types/observe'; -import { plugins } from '@service/plugins'; - -import * as Parsers from '@platform/types/observe/parser/index'; -import { Columns } from './columns'; - -const RENDERS: { - [key: string]: RenderReference; -} = { - [Parsers.Protocol.Dlt]: Dlt, - [Parsers.Protocol.SomeIp]: SomeIp, - [Parsers.Protocol.Text]: TextRender, -}; -export function getDltRender(): Render { - return new Dlt(); -} -export async function getRender(observe: Observe): Promise | Error> { - const protocol = observe.parser.instance.alias(); - // Render options on plugins can't be static because they must be retrieved - // from the plugin itself. - if (protocol === Parsers.Protocol.Plugin) { - const config = observe.parser.as( - Parsers.Plugin.Configuration, - ); - - if (config === undefined) { - return new Error('No parser configurations for plugin.'); - } - - const pluginPath = config.configuration.plugin_path; - const parser = plugins - .list() - .preload() - .find((p) => p.info.wasm_file_path === pluginPath); - - if (parser === undefined) { - return new Error("Selected parser plugin does'n exit"); - } - - return new Plugin(parser); - } - - const Ref = RENDERS[protocol]; - return Ref === undefined ? new Error(`No render has been found for "${protocol}"`) : new Ref(); -} - -export function getLinkedProtocol(smth: Session | Render): Parsers.Protocol | Error { - return smth instanceof Session ? smth.render.protocol() : smth.protocol(); -} - -export function isRenderMatch(session: Session, render: Render): boolean | Error { - const assigned = getLinkedProtocol(session); - return assigned === render.protocol(); -} diff --git a/application/client/src/app/service/actions.ts b/application/client/src/app/service/actions.ts index 3fc33786c5..24127cd726 100644 --- a/application/client/src/app/service/actions.ts +++ b/application/client/src/app/service/actions.ts @@ -2,9 +2,7 @@ import { SetupService, Interface, Implementation, register } from '@platform/ent import { services } from '@register/services'; import { api } from '@service/api'; import { CancelablePromise } from '@platform/env/promise'; -import { FileType } from '@platform/types/observe/types/file'; -import { Protocol } from '@platform/types/observe/parser'; -import { Source } from '@platform/types/observe/origin/stream/index'; +import { FileType } from '@platform/types/files'; import * as Requests from '@platform/ipc/request'; import * as handlers from '@service/actions/index'; @@ -105,59 +103,60 @@ export class Service extends Implementation { ): CancelablePromise => { return new CancelablePromise((resolve, _reject) => { (() => { - switch (request.protocol) { - case Protocol.Text: - switch (request.source) { - case undefined: - case Source.Process: - return new handlers.StdoutText.Action().apply(); - case Source.Serial: - return new handlers.SerialText.Action().apply(); - default: - return Promise.reject( - new Error( - `Unsupported transport for Text: ${request.source}`, - ), - ); - } - case Protocol.Dlt: - switch (request.source) { - case undefined: - case Source.UDP: - return new handlers.UdpDlt.Action().apply(); - case Source.TCP: - return new handlers.TcpDlt.Action().apply(); - default: - return Promise.reject( - new Error( - `Unsupported transport for DLT: ${request.source}`, - ), - ); - } - case Protocol.Plugin: { - switch (request.source) { - case undefined: - case Source.Process: - return new handlers.StdoutPlugin.Action().apply(); - case Source.Serial: - return new handlers.SerialParserPlugin.Action().apply(); - case Source.UDP: - return new handlers.UdpParserPlugin.Action().apply(); - case Source.TCP: - return new handlers.TcpParserPlugin.Action().apply(); - default: - return Promise.reject( - new Error( - `Unsupported transport for Plugins: ${request.source}`, - ), - ); - } - } - default: - return Promise.reject( - new Error(`Unsupported format: ${request.protocol}`), - ); - } + return Promise.reject(new Error(`Not implemented`)); + // switch (request.protocol) { + // case Protocol.Text: + // switch (request.source) { + // case undefined: + // case Source.Process: + // return new handlers.StdoutText.Action().apply(); + // case Source.Serial: + // return new handlers.SerialText.Action().apply(); + // default: + // return Promise.reject( + // new Error( + // `Unsupported transport for Text: ${request.source}`, + // ), + // ); + // } + // case Protocol.Dlt: + // switch (request.source) { + // case undefined: + // case Source.UDP: + // return new handlers.UdpDlt.Action().apply(); + // case Source.TCP: + // return new handlers.TcpDlt.Action().apply(); + // default: + // return Promise.reject( + // new Error( + // `Unsupported transport for DLT: ${request.source}`, + // ), + // ); + // } + // case Protocol.Plugin: { + // switch (request.source) { + // case undefined: + // case Source.Process: + // return new handlers.StdoutPlugin.Action().apply(); + // case Source.Serial: + // return new handlers.SerialParserPlugin.Action().apply(); + // case Source.UDP: + // return new handlers.UdpParserPlugin.Action().apply(); + // case Source.TCP: + // return new handlers.TcpParserPlugin.Action().apply(); + // default: + // return Promise.reject( + // new Error( + // `Unsupported transport for Plugins: ${request.source}`, + // ), + // ); + // } + // } + // default: + // return Promise.reject( + // new Error(`Unsupported format: ${request.protocol}`), + // ); + // } })() .then(() => resolve( diff --git a/application/client/src/app/service/actions/file.any.ts b/application/client/src/app/service/actions/file.any.ts index f42ad8d00e..dc49743fda 100644 --- a/application/client/src/app/service/actions/file.any.ts +++ b/application/client/src/app/service/actions/file.any.ts @@ -3,9 +3,8 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { File } from '@platform/types/files'; -import { FileType } from '@platform/types/observe/types/file'; +import { FileType } from '@platform/types/files'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_file'; diff --git a/application/client/src/app/service/actions/file.dlt.ts b/application/client/src/app/service/actions/file.dlt.ts index ed2fb6fe79..9e6d2c58c0 100644 --- a/application/client/src/app/service/actions/file.dlt.ts +++ b/application/client/src/app/service/actions/file.dlt.ts @@ -3,7 +3,6 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_file'; diff --git a/application/client/src/app/service/actions/file.parserPlugin.ts b/application/client/src/app/service/actions/file.parserPlugin.ts index 656e613202..a3be8f018a 100644 --- a/application/client/src/app/service/actions/file.parserPlugin.ts +++ b/application/client/src/app/service/actions/file.parserPlugin.ts @@ -3,7 +3,6 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_file_parser_plugins'; diff --git a/application/client/src/app/service/actions/file.pcap.ts b/application/client/src/app/service/actions/file.pcap.ts index 12a17a1fce..46daeeac1e 100644 --- a/application/client/src/app/service/actions/file.pcap.ts +++ b/application/client/src/app/service/actions/file.pcap.ts @@ -3,7 +3,6 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_file'; diff --git a/application/client/src/app/service/actions/file.pcapng.ts b/application/client/src/app/service/actions/file.pcapng.ts index d8b67cb164..af0ab3505e 100644 --- a/application/client/src/app/service/actions/file.pcapng.ts +++ b/application/client/src/app/service/actions/file.pcapng.ts @@ -3,7 +3,6 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_file'; diff --git a/application/client/src/app/service/actions/file.plugins.ts b/application/client/src/app/service/actions/file.plugins.ts index 527ce7af84..c6a1e524a4 100644 --- a/application/client/src/app/service/actions/file.plugins.ts +++ b/application/client/src/app/service/actions/file.plugins.ts @@ -1,10 +1,9 @@ import { Base } from './action'; import { bridge } from '@service/bridge'; import { session } from '@service/session'; +import { SessionOrigin } from '@service/session/origin'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; - export const ACTION_UUID = 'open_file_parser_plugins'; export class Action extends Base { @@ -36,15 +35,7 @@ export class Action extends Base { }, }); } else { - session - .initialize() - .configure( - new Factory.File() - .type(files[0].type) - .file(files[0].filename) - .asParserPlugin() - .get(), - ); + session.initialize().configure(SessionOrigin.file(files[0].filename)); } return Promise.resolve(); } diff --git a/application/client/src/app/service/actions/file.text.ts b/application/client/src/app/service/actions/file.text.ts index 45dd6f98d1..abc84dcd87 100644 --- a/application/client/src/app/service/actions/file.text.ts +++ b/application/client/src/app/service/actions/file.text.ts @@ -3,7 +3,6 @@ import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_file'; diff --git a/application/client/src/app/service/actions/folder.any.ts b/application/client/src/app/service/actions/folder.any.ts index 533f526e33..67d83a009d 100644 --- a/application/client/src/app/service/actions/folder.any.ts +++ b/application/client/src/app/service/actions/folder.any.ts @@ -2,10 +2,9 @@ import { Base } from './action'; import { bridge } from '@service/bridge'; import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import { FileType } from '@platform/types/observe/types/file'; +import { FileType } from '@platform/types/files'; import { notifications, Notification } from '@ui/service/notifications'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_any_folder'; diff --git a/application/client/src/app/service/actions/folder.dlt.ts b/application/client/src/app/service/actions/folder.dlt.ts index feacfb1f6c..04235bf8d5 100644 --- a/application/client/src/app/service/actions/folder.dlt.ts +++ b/application/client/src/app/service/actions/folder.dlt.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_dlt_folder'; diff --git a/application/client/src/app/service/actions/folder.parserPlugin.ts b/application/client/src/app/service/actions/folder.parserPlugin.ts index 738e6aa7fd..fa6b58f1e6 100644 --- a/application/client/src/app/service/actions/folder.parserPlugin.ts +++ b/application/client/src/app/service/actions/folder.parserPlugin.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; diff --git a/application/client/src/app/service/actions/folder.pcap.ts b/application/client/src/app/service/actions/folder.pcap.ts index a346019ce4..744c6d5090 100644 --- a/application/client/src/app/service/actions/folder.pcap.ts +++ b/application/client/src/app/service/actions/folder.pcap.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_legacy_folder'; diff --git a/application/client/src/app/service/actions/folder.pcapng.ts b/application/client/src/app/service/actions/folder.pcapng.ts index a727470d84..a28d16e4b4 100644 --- a/application/client/src/app/service/actions/folder.pcapng.ts +++ b/application/client/src/app/service/actions/folder.pcapng.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_pcap_folder'; diff --git a/application/client/src/app/service/actions/folder.plugins.ts b/application/client/src/app/service/actions/folder.plugins.ts index 738e6aa7fd..fa6b58f1e6 100644 --- a/application/client/src/app/service/actions/folder.plugins.ts +++ b/application/client/src/app/service/actions/folder.plugins.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { notifications, Notification } from '@ui/service/notifications'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_folder_parser_plugins'; diff --git a/application/client/src/app/service/actions/folder.text.ts b/application/client/src/app/service/actions/folder.text.ts index f52952cef2..958f298eb8 100644 --- a/application/client/src/app/service/actions/folder.text.ts +++ b/application/client/src/app/service/actions/folder.text.ts @@ -4,7 +4,6 @@ import { session } from '@service/session'; import { TabSourceMultipleFiles } from '@ui/tabs/multiplefiles/component'; import { notifications, Notification } from '@ui/service/notifications'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'open_text_folder'; diff --git a/application/client/src/app/service/actions/serial.parserPlugin.ts b/application/client/src/app/service/actions/serial.parserPlugin.ts index 14f0a78526..30cab32c7f 100644 --- a/application/client/src/app/service/actions/serial.parserPlugin.ts +++ b/application/client/src/app/service/actions/serial.parserPlugin.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_serial'; diff --git a/application/client/src/app/service/actions/serial.text.ts b/application/client/src/app/service/actions/serial.text.ts index c22903a5f0..2e4c9cab42 100644 --- a/application/client/src/app/service/actions/serial.text.ts +++ b/application/client/src/app/service/actions/serial.text.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_serial'; diff --git a/application/client/src/app/service/actions/stdout.plugin.ts b/application/client/src/app/service/actions/stdout.plugin.ts index 4577877f96..5b0ae5bf51 100644 --- a/application/client/src/app/service/actions/stdout.plugin.ts +++ b/application/client/src/app/service/actions/stdout.plugin.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_plugin'; diff --git a/application/client/src/app/service/actions/stdout.text.ts b/application/client/src/app/service/actions/stdout.text.ts index ae968ea9b8..1dc146998b 100644 --- a/application/client/src/app/service/actions/stdout.text.ts +++ b/application/client/src/app/service/actions/stdout.text.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_text_on_stdout'; diff --git a/application/client/src/app/service/actions/tcp.dlt.ts b/application/client/src/app/service/actions/tcp.dlt.ts index df15f878de..ae40536eca 100644 --- a/application/client/src/app/service/actions/tcp.dlt.ts +++ b/application/client/src/app/service/actions/tcp.dlt.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_tcp'; diff --git a/application/client/src/app/service/actions/tcp.parserPlugin.ts b/application/client/src/app/service/actions/tcp.parserPlugin.ts index 5bb6aaa392..57264b17cf 100644 --- a/application/client/src/app/service/actions/tcp.parserPlugin.ts +++ b/application/client/src/app/service/actions/tcp.parserPlugin.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_tcp'; diff --git a/application/client/src/app/service/actions/udp.dlt.ts b/application/client/src/app/service/actions/udp.dlt.ts index d5eef7660b..fbd7feacf8 100644 --- a/application/client/src/app/service/actions/udp.dlt.ts +++ b/application/client/src/app/service/actions/udp.dlt.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_dlt_on_udp'; diff --git a/application/client/src/app/service/actions/udp.parserPlugin.ts b/application/client/src/app/service/actions/udp.parserPlugin.ts index ebc38d326d..e57d3e306e 100644 --- a/application/client/src/app/service/actions/udp.parserPlugin.ts +++ b/application/client/src/app/service/actions/udp.parserPlugin.ts @@ -1,7 +1,6 @@ import { Base } from './action'; import { session } from '@service/session'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export const ACTION_UUID = 'stream_parser_plugin_on_udp'; diff --git a/application/client/src/app/service/bridge.ts b/application/client/src/app/service/bridge.ts index 30e47c449c..37fece16b8 100644 --- a/application/client/src/app/service/bridge.ts +++ b/application/client/src/app/service/bridge.ts @@ -2,8 +2,8 @@ import { SetupService, Interface, Implementation, register } from '@platform/ent import { services } from '@register/services'; import { File, Entity, ParsedPath } from '@platform/types/files'; import { FolderEntity } from '@platform/types/bindings'; -import { FileType } from '@platform/types/observe/types/file'; -import { DltStatisticInfo, Profile } from '@platform/types/bindings'; +import { FileType } from '@platform/types/files'; +import { Profile } from '@platform/types/bindings'; import { Entry } from '@platform/types/storage/entry'; import { error } from '@platform/log/utils'; @@ -423,27 +423,6 @@ export class Service extends Implementation { }; } - public dlt(): { - stat(files: string[]): Promise; - } { - return { - stat: (files: string[]): Promise => { - return new Promise((resolve, reject) => { - Requests.IpcRequest.send( - Requests.Dlt.Stat.Response, - new Requests.Dlt.Stat.Request({ - files, - }), - ) - .then((response) => { - resolve(response.stat); - }) - .catch(reject); - }); - }, - }; - } - public storage(key: string): { write(content: string): Promise; read(): Promise; diff --git a/application/client/src/app/service/cli/observe.ts b/application/client/src/app/service/cli/observe.ts index 4684962f6b..6a16730c35 100644 --- a/application/client/src/app/service/cli/observe.ts +++ b/application/client/src/app/service/cli/observe.ts @@ -1,7 +1,6 @@ import { CancelablePromise } from '@platform/env/promise'; import { Service } from '@service/cli'; import { session } from '@service/session'; -import { Observe } from '@platform/types/observe'; import * as Requests from '@platform/ipc/request'; diff --git a/application/client/src/app/service/history/definition.concat.ts b/application/client/src/app/service/history/definition.concat.ts index bd6443de15..ddecc12f5f 100644 --- a/application/client/src/app/service/history/definition.concat.ts +++ b/application/client/src/app/service/history/definition.concat.ts @@ -1,22 +1,23 @@ import { IFileDesc, FileDesc } from './definition.file'; -import * as $ from '@platform/types/observe'; +import { SessionOrigin } from '@service/session/origin'; export class ConcatDesc { - static async fromDataSource(source: $.Observe): Promise { - const concat = source.origin.as<$.Origin.Concat.Configuration>( - $.Origin.Concat.Configuration, - ); - if (concat === undefined) { - return undefined; - } - const list: IFileDesc[] = []; - for (const filename of concat.files()) { - const desc = await FileDesc.fromFilename(filename); - if (desc !== undefined) { - list.push(desc); - } - } - return list; + static async fromDataSource(origin: SessionOrigin): Promise { + return Promise.reject(new Error(`Not implemented`)); + // const concat = source.origin.as<$.Origin.Concat.Configuration>( + // $.Origin.Concat.Configuration, + // ); + // if (concat === undefined) { + // return undefined; + // } + // const list: IFileDesc[] = []; + // for (const filename of concat.files()) { + // const desc = await FileDesc.fromFilename(filename); + // if (desc !== undefined) { + // list.push(desc); + // } + // } + // return list; } static fromMinifiedStr( src: Array<{ [key: string]: number | string }> | undefined, diff --git a/application/client/src/app/service/history/definition.file.ts b/application/client/src/app/service/history/definition.file.ts index 911819f9bb..d61a01759f 100644 --- a/application/client/src/app/service/history/definition.file.ts +++ b/application/client/src/app/service/history/definition.file.ts @@ -1,8 +1,8 @@ import { getParentFolder } from '@platform/types/files'; import { bridge } from '@service/bridge'; +import { SessionOrigin } from '@service/session/origin'; import * as obj from '@platform/env/obj'; -import * as $ from '@platform/types/observe'; export interface IFileDesc { extention: string; @@ -14,12 +14,13 @@ export interface IFileDesc { } export class FileDesc implements IFileDesc { - static async fromDataSource(source: $.Observe): Promise { - const file = source.origin.as<$.Origin.File.Configuration>($.Origin.File.Configuration); - if (file === undefined) { - return undefined; - } - return FileDesc.fromFilename(file.filename()); + static async fromDataSource(origin: SessionOrigin): Promise { + return Promise.reject(new Error(`Not implemented`)); + // const file = source.origin.as<$.Origin.File.Configuration>($.Origin.File.Configuration); + // if (file === undefined) { + // return undefined; + // } + // return FileDesc.fromFilename(file.filename()); } static async fromFilename(filename: string): Promise { const file = (await bridge.files().getByPathWithCache([filename]))[0]; diff --git a/application/client/src/app/service/history/definition.stream.ts b/application/client/src/app/service/history/definition.stream.ts index 37d0e4b940..0a2ae91422 100644 --- a/application/client/src/app/service/history/definition.stream.ts +++ b/application/client/src/app/service/history/definition.stream.ts @@ -1,57 +1,65 @@ -import * as $ from '@platform/types/observe'; +import { SessionOrigin } from '@service/session/origin'; + import * as obj from '@platform/env/obj'; export interface IStreamDesc { - source: $.Origin.Stream.Stream.Source; + origin: SessionOrigin; major: string; minor: string; } export class StreamDesc implements IStreamDesc { - static async fromDataSource(source: $.Observe): Promise { - const stream = source.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (stream === undefined) { - return undefined; - } - const def = stream.desc(); - return { - major: def.major, - minor: def.minor, - source: stream.instance.instance.alias(), - }; + static async fromDataSource(origin: SessionOrigin): Promise { + return Promise.reject(new Error(`Not implemented`)); + + // const stream = source.origin.as<$.Origin.Stream.Configuration>( + // $.Origin.Stream.Configuration, + // ); + // if (stream === undefined) { + // return undefined; + // } + // const def = stream.desc(); + // return { + // major: def.major, + // minor: def.minor, + // source: stream.instance.instance.alias(), + // }; } static fromMinifiedStr( src: { [key: string]: number | string } | undefined, ): StreamDesc | undefined { - return src === undefined - ? undefined - : new StreamDesc({ - source: obj.getAsNotEmptyString(src, 's') as $.Origin.Stream.Stream.Source, - major: obj.getAsString(src, 'ma'), - minor: obj.getAsString(src, 'mi'), - }); + console.error(`Not implemented`); + return undefined; + // return src === undefined + // ? undefined + // : new StreamDesc({ + // origin: obj.getAsNotEmptyString(src, 's') as $.Origin.Stream.Stream.Source, + // major: obj.getAsString(src, 'ma'), + // minor: obj.getAsString(src, 'mi'), + // }); } - public source: $.Origin.Stream.Stream.Source; + public origin: SessionOrigin; public major: string; public minor: string; constructor(definition: IStreamDesc) { - this.source = definition.source; + this.origin = definition.origin; this.major = definition.major; this.minor = definition.minor; } public isSame(stream: StreamDesc): boolean { - return stream.source === this.source && this.major === stream.major; + console.error(`Not implemented`); + return false; + // return stream.source === this.source && this.major === stream.major; } public minify(): { [key: string]: number | string } { + console.error(`Not implemented`); return { - s: this.source, + s: '', mi: this.minor, ma: this.major, }; diff --git a/application/client/src/app/service/history/definition.ts b/application/client/src/app/service/history/definition.ts index 5044000e4d..0c7c95c9bb 100644 --- a/application/client/src/app/service/history/definition.ts +++ b/application/client/src/app/service/history/definition.ts @@ -9,13 +9,12 @@ import { Equal } from '@platform/types/env/types'; import { Collections } from './collections'; import * as obj from '@platform/env/obj'; -import * as $ from '@platform/types/observe'; +import { SessionOrigin } from '@service/session/origin'; export interface IDefinition { stream?: IStreamDesc; file?: IFileDesc; concat?: IFileDesc[]; - parser: $.Parser.Protocol; uuid: string; } @@ -27,17 +26,16 @@ export class Definition implements EntryConvertable, Equal { static from(entry: Entry): Definition { return Definition.fromMinifiedStr(JSON.parse(entry.content)); } - static async fromDataSource(source: $.Observe): Promise { - const parser = source.parser.instance.alias(); - const file = await FileDesc.fromDataSource(source); - const concat = await ConcatDesc.fromDataSource(source); - const stream = await StreamDesc.fromDataSource(source); + static async fromDataSource(origin: SessionOrigin): Promise { + console.error(`Not Implemented (parser isn't considered)`); + const file = await FileDesc.fromDataSource(origin); + const concat = await ConcatDesc.fromDataSource(origin); + const stream = await StreamDesc.fromDataSource(origin); if (file !== undefined) { return new Definition({ file, stream: undefined, concat: undefined, - parser, uuid: unique(), }); } else if (concat !== undefined) { @@ -45,7 +43,6 @@ export class Definition implements EntryConvertable, Equal { file: undefined, stream: undefined, concat, - parser, uuid: unique(), }); } else if (stream !== undefined) { @@ -53,7 +50,6 @@ export class Definition implements EntryConvertable, Equal { file: undefined, concat: undefined, stream, - parser, uuid: unique(), }); } else { @@ -65,7 +61,6 @@ export class Definition implements EntryConvertable, Equal { file: FileDesc.fromMinifiedStr(obj.getAsObjOrUndefined(src, 'f')), stream: StreamDesc.fromMinifiedStr(obj.getAsObjOrUndefined(src, 's')), concat: ConcatDesc.fromMinifiedStr(obj.getAsObjOrUndefined(src, 'c'))?.files, - parser: obj.getAsNotEmptyString(src, 'p') as $.Parser.Protocol, uuid: obj.getAsNotEmptyString(src, 'u'), }); if (def.file === undefined && def.stream === undefined && def.concat === undefined) { @@ -77,7 +72,6 @@ export class Definition implements EntryConvertable, Equal { public stream?: StreamDesc; public file?: FileDesc; public concat?: ConcatDesc; - public parser: $.Parser.Protocol; public uuid: string; constructor(definition: IDefinition) { @@ -86,7 +80,7 @@ export class Definition implements EntryConvertable, Equal { this.file = definition.file === undefined ? undefined : new FileDesc(definition.file); this.concat = definition.concat === undefined ? undefined : new ConcatDesc(definition.concat); - this.parser = definition.parser; + // this.parser = definition.parser; this.uuid = definition.uuid; } @@ -94,14 +88,14 @@ export class Definition implements EntryConvertable, Equal { this.stream = definition.stream; this.file = definition.file; this.concat = definition.concat; - this.parser = definition.parser; + // this.parser = definition.parser; this.uuid = definition.uuid; } public isSame(definition: Definition): boolean { - if (this.parser !== definition.parser) { - return false; - } + // if (this.parser !== definition.parser) { + // return false; + // } if (definition.stream !== undefined && this.stream !== undefined) { return this.stream.isSame(definition.stream); } @@ -123,9 +117,9 @@ export class Definition implements EntryConvertable, Equal { return collections.relations.indexOf(this.uuid) !== -1; }, suitable: (definition: Definition): GroupRelations | undefined => { - if (this.parser !== definition.parser) { - return undefined; - } + // if (this.parser !== definition.parser) { + // return undefined; + // } if (this.file !== undefined && definition.file !== undefined) { if ( (this.file.extention === definition.file.extention && @@ -167,7 +161,7 @@ export class Definition implements EntryConvertable, Equal { stream: this.stream, file: this.file, concat: this.concat === undefined ? undefined : this.concat.files, - parser: this.parser, + // parser: this.parser, uuid: this.uuid, }; if (def.file !== undefined) { @@ -199,7 +193,7 @@ export class Definition implements EntryConvertable, Equal { s: this.stream?.minify(), f: this.file?.minify(), c: this.concat?.minify(), - p: this.parser, + // p: this.parser, u: this.uuid, }; } diff --git a/application/client/src/app/service/recent.ts b/application/client/src/app/service/recent.ts index 07116f3e53..27236f3ff8 100644 --- a/application/client/src/app/service/recent.ts +++ b/application/client/src/app/service/recent.ts @@ -10,8 +10,7 @@ import { bridge } from '@service/bridge'; import { Action } from './recent/action'; import { error } from '@platform/log/utils'; import { Subject } from '@platform/env/subscription'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from './session/dependencies/stream'; const STORAGE_KEY = 'user_recent_actions'; @@ -77,15 +76,17 @@ export class Service extends Implementation { } action.merge(found); }); - return bridge - .entries({ key: STORAGE_KEY }) - .update(actions.map((a) => a.entry().to())) - .then(() => { - this.updated.emit(); - }) - .catch((err: Error) => { - this.log().error(`Fail to update recent storage: ${err.message}`); - }); + console.error(`Not implemented`); + return Promise.resolve(); + // return bridge + // .entries({ key: STORAGE_KEY }) + // .update(actions.map((a) => a.entry().to())) + // .then(() => { + // this.updated.emit(); + // }) + // .catch((err: Error) => { + // this.log().error(`Fail to update recent storage: ${err.message}`); + // }); } public delete(uuids: string[]): Promise { @@ -103,8 +104,8 @@ export class Service extends Implementation { }); } - public add(observe: $.Observe): Promise { - const action = new Action(observe); + public add(operation: ObserveOperation): Promise { + const action = new Action(operation.getOrigin()); return this.update([action]); } } diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index 48cdc64847..4168022871 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -6,20 +6,21 @@ import { Stat, IStat } from './stat'; import { recent } from '@service/recent'; import { scope } from '@platform/env/scope'; import { Logger } from '@platform/log'; +import { SessionOrigin } from '@service/session/origin'; -import * as $ from '@platform/types/observe'; import * as compatibility from './compatibility'; interface IActionContent { stat: IStat; - observe: $.IObserve; + origin: SessionOrigin; } export class Action { static from(entry: Entry): Action | Error { - const action = new Action($.Observe.new()); - const error = action.entry().from(entry); - return error instanceof Error ? error : action; + return new Error(`Not Implemented`); + // const action = new Action($.Observe.new()); + // const error = action.entry().from(entry); + // return error instanceof Error ? error : action; } protected logger: Logger; @@ -34,108 +35,119 @@ export class Action { invalidUuid: undefined, }; - constructor(public observe: $.Observe) { - this.uuid = observe.signature(); - this.logger = scope.getLogger(`Action: ${this.uuid}`); + constructor(public origin: SessionOrigin) { + this.uuid = `Not implemented`; + // this.logger = scope.getLogger(`Action: ${this.uuid}`); + this.logger = scope.getLogger(`Action`); } - public isSuitable(observe?: $.Observe): boolean { - if (observe === undefined) { - return true; - } - if (observe.origin.nature().alias() !== this.observe.origin.nature().alias()) { - return false; - } - return this.observe.parser.alias() === observe.parser.alias(); + public isSuitable(origin: SessionOrigin): boolean { + console.error(`Not implemented`); + return false; + // if (observe === undefined) { + // return true; + // } + // if (observe.origin.nature().alias() !== this.observe.origin.nature().alias()) { + // return false; + // } + // return this.observe.parser.alias() === observe.parser.alias(); } - public description(): $.Description.IOriginDetails { - return this.observe.origin.desc(); + public description(): { major: string; minor: string } { + console.error(`Not implemented`); + return { major: '', minor: '' }; } public entry(): { from(entry: Entry): Error | undefined; - to(): Entry; + // to(): Entry; } { return { from: (entry: Entry): Error | undefined => { - try { - const body: IActionContent = JSON.parse(entry.content); - if (body.observe === undefined) { - // Check previous version (chipmunk <= 3.8.1) - this.observe = compatibility.from_3_8_1(entry); - this.compatibility.converted = true; - } else { - const observe = new $.Observe(body.observe); - this.observe = observe; - } - this.stat = Stat.from(body.stat); - this.uuid = this.observe.signature(); - this.compatibility.invalidUuid = - entry.uuid !== this.uuid ? entry.uuid : undefined; - return undefined; - } catch (err) { - return new Error(`Fail to parse action: ${error(err)}`); - } - }, - to: (): Entry => { - return { - uuid: this.uuid, - content: JSON.stringify({ - stat: this.stat.asObj(), - observe: this.observe.storable(), - } as IActionContent), - }; + console.error(`Not implemented`); + return undefined; + // try { + // const body: IActionContent = JSON.parse(entry.content); + // if (body.observe === undefined) { + // // Check previous version (chipmunk <= 3.8.1) + // this.observe = compatibility.from_3_8_1(entry); + // this.compatibility.converted = true; + // } else { + // const observe = new $.Observe(body.observe); + // this.observe = observe; + // } + // this.stat = Stat.from(body.stat); + // this.uuid = this.observe.signature(); + // this.compatibility.invalidUuid = + // entry.uuid !== this.uuid ? entry.uuid : undefined; + // return undefined; + // } catch (err) { + // return new Error(`Fail to parse action: ${error(err)}`); + // } }, + // to: (): Entry => { + // console.error(`Not implemented`); + // return undefined; + // return { + // uuid: this.uuid, + // content: JSON.stringify({ + // stat: this.stat.asObj(), + // observe: this.observe.storable(), + // } as IActionContent), + // }; + // }, }; } public getActions(): { caption?: string; handler?: () => void }[] { - const observe = this.observe; - const configurable = observe.isConfigurable(); - const nature = observe.origin.nature().desc(); - return [ - // **Note**: Recent actions isn't supported yet for plugins. Support of this - // feature will be included with general refactoring of Observe Configuration - // and a way to delivery available parser/source to client - ...(nature.type === $.Description.OriginType.plugin - ? [] - : [ - { - caption: ((): string => { - switch (nature.type) { - case $.Description.OriginType.file: - return 'Open with recent configuration'; - case $.Description.OriginType.net: - case $.Description.OriginType.serial: - return 'Connect with recent configuration'; - case $.Description.OriginType.command: - return 'Execute with recent configuration'; - } - })(), - handler: this.apply.bind(this), - }, - ]), - ...(configurable - ? [ - { - caption: 'Open with new configuration', - handler: () => { - console.error(`Not implemented`); + console.error(`Not Implemented`); + return []; + // const observe = this.observe; + // const configurable = observe.isConfigurable(); + // const nature = observe.origin.nature().desc(); + + // return [ + // // **Note**: Recent actions isn't supported yet for plugins. Support of this + // // feature will be included with general refactoring of Observe Configuration + // // and a way to delivery available parser/source to client + // ...(nature.type === $.Description.OriginType.plugin + // ? [] + // : [ + // { + // caption: ((): string => { + // switch (nature.type) { + // case $.Description.OriginType.file: + // return 'Open with recent configuration'; + // case $.Description.OriginType.net: + // case $.Description.OriginType.serial: + // return 'Connect with recent configuration'; + // case $.Description.OriginType.command: + // return 'Execute with recent configuration'; + // } + // })(), + // handler: this.apply.bind(this), + // }, + // ]), + // ...(configurable + // ? [ + // { + // caption: 'Open with new configuration', + // handler: () => { + // console.error(`Not implemented`); - // session - // .initialize() - // .configure(observe) - // .catch((err: Error) => { - // this.logger.error( - // `Fail to configure observe object: ${err.message}`, - // ); - // }); - }, - }, - ] - : []), - ]; + // // session + // // .initialize() + // // .configure(observe) + // // .catch((err: Error) => { + // // this.logger.error( + // // `Fail to configure observe object: ${err.message}`, + // // ); + // // }); + // }, + // }, + // ] + // : []), + // ]; } public remove(): Promise { diff --git a/application/client/src/app/service/recent/compatibility.ts b/application/client/src/app/service/recent/compatibility.ts index 2fe1e9c08c..7cff32b4a7 100644 --- a/application/client/src/app/service/recent/compatibility.ts +++ b/application/client/src/app/service/recent/compatibility.ts @@ -1,8 +1,5 @@ import { Entry } from '@platform/types/storage/entry'; -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; - // Interface belong to >= 3.8.1 interface IDLTFilters { app_ids?: string[]; @@ -18,93 +15,93 @@ interface IDLTOptions { tz?: string; } -export function optionsToParserSettings( - options: IDLTOptions, - with_storage_header: boolean, - app_id_count: number, - context_id_count: number, -): $.Parser.Dlt.IConfiguration { - const filter_config: $.Parser.Dlt.IFilters = { - min_log_level: options.logLevel, - app_ids: options.filters.app_ids, - context_ids: options.filters.context_ids, - ecu_ids: options.filters.ecu_ids, - app_id_count, - context_id_count, - }; - return { - filter_config, - fibex_file_paths: options.fibex.length > 0 ? options.fibex : undefined, - with_storage_header, - tz: undefined, - }; -} +// export function optionsToParserSettings( +// options: IDLTOptions, +// with_storage_header: boolean, +// app_id_count: number, +// context_id_count: number, +// ): $.Parser.Dlt.IConfiguration { +// const filter_config: $.Parser.Dlt.IFilters = { +// min_log_level: options.logLevel, +// app_ids: options.filters.app_ids, +// context_ids: options.filters.context_ids, +// ecu_ids: options.filters.ecu_ids, +// app_id_count, +// context_id_count, +// }; +// return { +// filter_config, +// fibex_file_paths: options.fibex.length > 0 ? options.fibex : undefined, +// with_storage_header, +// tz: undefined, +// }; +// } -// This function has to be removed since v 3.9.x or 3.10.x (after a couple of -// update iterations) -export function from_3_8_1(entry: Entry): $.Observe { - const action = JSON.parse(entry.content); - let observe; - if (action['file'] !== undefined) { - if (action['file']['dlt'] !== undefined) { - observe = new Factory.File() - .asDlt(optionsToParserSettings(action['file']['dlt'], true, 0, 0)) - .type($.Types.File.FileType.Binary) - .file(action['file']['filename']) - .get(); - } else if (action['file']['pcap'] !== undefined) { - observe = new Factory.File() - .asDlt(optionsToParserSettings(action['file']['pcap']['dlt'], true, 0, 0)) - .type($.Types.File.FileType.PcapNG) - .file(action['file']['filename']) - .get(); - } else { - observe = new Factory.File() - .asText() - .file(action['file']['filename']) - .type($.Types.File.FileType.Text) - .get(); - } - } else if (action['dlt_stream'] !== undefined) { - const defs = action['dlt_stream']; - const source = defs['source']; - const preconstructed = new Factory.Stream().asDlt( - optionsToParserSettings(defs['options'], false, 0, 0), - ); - if (source['process'] !== undefined) { - preconstructed.process(source['process']); - } else if (source['serial'] !== undefined) { - preconstructed.serial(source['serial']); - } else if (source['tcp'] !== undefined) { - preconstructed.tcp(source['tcp']); - } else if (source['udp'] !== undefined) { - preconstructed.udp(source['udp']); - } else { - throw new Error(`Unknonw type of source for stream.`); - } - observe = preconstructed.get(); - } else if (action['text_stream'] !== undefined) { - const defs = action['text_stream']; - const source = defs['source']; - const preconstructed = new Factory.Stream().asText(); - if (source['process'] !== undefined) { - preconstructed.process(source['process']); - } else if (source['serial'] !== undefined) { - preconstructed.serial(source['serial']); - } else if (source['tcp'] !== undefined) { - preconstructed.tcp(source['tcp']); - } else if (source['udp'] !== undefined) { - preconstructed.udp(source['udp']); - } else { - throw new Error(`Unknonw type of source for stream.`); - } - observe = preconstructed.get(); - } else { - throw new Error(`Unknonw type of action.`); - } - const error = observe.validate(); - if (error instanceof Error) { - throw error; - } - return observe; -} +// // This function has to be removed since v 3.9.x or 3.10.x (after a couple of +// // update iterations) +// export function from_3_8_1(entry: Entry): $.Observe { +// const action = JSON.parse(entry.content); +// let observe; +// if (action['file'] !== undefined) { +// if (action['file']['dlt'] !== undefined) { +// observe = new Factory.File() +// .asDlt(optionsToParserSettings(action['file']['dlt'], true, 0, 0)) +// .type($.Types.File.FileType.Binary) +// .file(action['file']['filename']) +// .get(); +// } else if (action['file']['pcap'] !== undefined) { +// observe = new Factory.File() +// .asDlt(optionsToParserSettings(action['file']['pcap']['dlt'], true, 0, 0)) +// .type($.Types.File.FileType.PcapNG) +// .file(action['file']['filename']) +// .get(); +// } else { +// observe = new Factory.File() +// .asText() +// .file(action['file']['filename']) +// .type($.Types.File.FileType.Text) +// .get(); +// } +// } else if (action['dlt_stream'] !== undefined) { +// const defs = action['dlt_stream']; +// const source = defs['source']; +// const preconstructed = new Factory.Stream().asDlt( +// optionsToParserSettings(defs['options'], false, 0, 0), +// ); +// if (source['process'] !== undefined) { +// preconstructed.process(source['process']); +// } else if (source['serial'] !== undefined) { +// preconstructed.serial(source['serial']); +// } else if (source['tcp'] !== undefined) { +// preconstructed.tcp(source['tcp']); +// } else if (source['udp'] !== undefined) { +// preconstructed.udp(source['udp']); +// } else { +// throw new Error(`Unknonw type of source for stream.`); +// } +// observe = preconstructed.get(); +// } else if (action['text_stream'] !== undefined) { +// const defs = action['text_stream']; +// const source = defs['source']; +// const preconstructed = new Factory.Stream().asText(); +// if (source['process'] !== undefined) { +// preconstructed.process(source['process']); +// } else if (source['serial'] !== undefined) { +// preconstructed.serial(source['serial']); +// } else if (source['tcp'] !== undefined) { +// preconstructed.tcp(source['tcp']); +// } else if (source['udp'] !== undefined) { +// preconstructed.udp(source['udp']); +// } else { +// throw new Error(`Unknonw type of source for stream.`); +// } +// observe = preconstructed.get(); +// } else { +// throw new Error(`Unknonw type of action.`); +// } +// const error = observe.validate(); +// if (error instanceof Error) { +// throw error; +// } +// return observe; +// } diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 1618474f38..9b56e53cc5 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -18,14 +18,11 @@ import { unique } from '@platform/env/sequence'; import { history } from '@service/history'; import { Render } from '@schema/render'; import { File } from '@platform/types/files'; -import { getRender, getDltRender } from '@schema/render/tools'; import { SetupObserve } from '@tabs/setup/component'; import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; import { SessionOrigin } from './session/origin'; -import * as Factory from '@platform/types/observe/factory'; - export { Session, TabControls, UnboundTab, Base }; @DependOn(history) diff --git a/application/client/src/app/service/session/dependencies/cli.ts b/application/client/src/app/service/session/dependencies/cli.ts index 668afb6bbf..ea0b119d3e 100644 --- a/application/client/src/app/service/session/dependencies/cli.ts +++ b/application/client/src/app/service/session/dependencies/cli.ts @@ -6,176 +6,174 @@ import { getFileName, getParentFolder } from '@platform/types/files'; import { env } from '@service/env'; import { cli } from '@service/cli'; -import * as $ from '@platform/types/observe'; - @SetupLogger() export class Cli extends Subscriber { protected session!: Session; protected _cmd: string = './chipmunk'; - protected source(): { - stream(stream: $.Origin.Stream.Configuration): string | undefined; - file(file: $.Origin.File.Configuration): string | undefined; - concat(concat: $.Origin.Concat.Configuration): string | undefined; - from(observed: $.Observe[]): string | undefined; - cx(observed: $.Observe[]): $.Origin.Context | undefined; - } { - return { - stream: (stream: $.Origin.Stream.Configuration): string | undefined => { - const serial = stream.as<$.Origin.Stream.Stream.Serial.Configuration>( - $.Origin.Stream.Stream.Serial.Configuration, - ); - const tcp = stream.as<$.Origin.Stream.Stream.TCP.Configuration>( - $.Origin.Stream.Stream.TCP.Configuration, - ); - const udp = stream.as<$.Origin.Stream.Stream.UDP.Configuration>( - $.Origin.Stream.Stream.UDP.Configuration, - ); - const process = stream.as<$.Origin.Stream.Stream.Process.Configuration>( - $.Origin.Stream.Stream.Process.Configuration, - ); - if (serial !== undefined) { - return `streams --serial "\ -${serial.configuration.path};\ -${serial.configuration.baud_rate};\ -${serial.configuration.data_bits};\ -${serial.configuration.flow_control};\ -${serial.configuration.parity};\ -${serial.configuration.stop_bits}"`; - } else if (tcp !== undefined) { - return `streams --tcp "${tcp.configuration.bind_addr}"`; - } else if (udp !== undefined) { - return `streams --udp "\ -${udp.configuration.bind_addr}|\ -${udp.configuration.multicast[0].multiaddr},\ -${udp.configuration.multicast[0].interface};"`; - } else if (process !== undefined) { - return `streams --stdout "${process.configuration.command}"`; - } else { - return undefined; - } - }, - file: (file: $.Origin.File.Configuration): string | undefined => { - return `-o ${this.filename(file.get().filename())}`; - }, - concat: (concat: $.Origin.Concat.Configuration): string | undefined => { - const relative = - Array.from(new Set(concat.files().map((f) => getParentFolder(f)))).length === 1; - return concat - .files() - .map((f) => `-o ${relative ? this.filename(f) : f}`) - .join(' '); - }, - from: (observed: $.Observe[]): string | undefined => { - const get = (observe: $.Observe): string | undefined => { - const stream = observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - const file = observe.origin.as<$.Origin.File.Configuration>( - $.Origin.File.Configuration, - ); - const concat = observe.origin.as<$.Origin.Concat.Configuration>( - $.Origin.Concat.Configuration, - ); - if (stream !== undefined) { - return this.source().stream(stream); - } else if (file !== undefined) { - return this.source().file(file); - } else if (concat !== undefined) { - return this.source().concat(concat); - } else { - return undefined; - } - }; - if (observed.length === 0) { - return undefined; - } - let args = ''; - for (const observe of observed) { - const arg = get(observe); - if (arg === undefined) { - return undefined; - } - args = `${args}${args.length === 0 ? '' : ' '}${arg}`; - } - return args; - }, - cx(observed: $.Observe[]): $.Origin.Context | undefined { - function get(observe: $.Observe): $.Origin.Context | undefined { - if ( - observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ) !== undefined - ) { - return $.Origin.Context.Stream; - } else if ( - observe.origin.as<$.Origin.File.Configuration>( - $.Origin.File.Configuration, - ) !== undefined - ) { - return $.Origin.Context.File; - } else if ( - observe.origin.as<$.Origin.Concat.Configuration>( - $.Origin.Concat.Configuration, - ) !== undefined - ) { - return $.Origin.Context.Concat; - } else { - return undefined; - } - } - if (observed.length === 0 || get(observed[0]) === undefined) { - return undefined; - } - const first: $.Origin.Context = get(observed[0]) as unknown as $.Origin.Context; - for (const observe of observed) { - if (first !== get(observe)) { - return undefined; - } - } - return first; - }, - }; - } + // protected source(): { + // stream(stream: $.Origin.Stream.Configuration): string | undefined; + // file(file: $.Origin.File.Configuration): string | undefined; + // concat(concat: $.Origin.Concat.Configuration): string | undefined; + // from(observed: $.Observe[]): string | undefined; + // cx(observed: $.Observe[]): $.Origin.Context | undefined; + // } { + // return { + // stream: (stream: $.Origin.Stream.Configuration): string | undefined => { + // const serial = stream.as<$.Origin.Stream.Stream.Serial.Configuration>( + // $.Origin.Stream.Stream.Serial.Configuration, + // ); + // const tcp = stream.as<$.Origin.Stream.Stream.TCP.Configuration>( + // $.Origin.Stream.Stream.TCP.Configuration, + // ); + // const udp = stream.as<$.Origin.Stream.Stream.UDP.Configuration>( + // $.Origin.Stream.Stream.UDP.Configuration, + // ); + // const process = stream.as<$.Origin.Stream.Stream.Process.Configuration>( + // $.Origin.Stream.Stream.Process.Configuration, + // ); + // if (serial !== undefined) { + // return `streams --serial "\ + // ${serial.configuration.path};\ + // ${serial.configuration.baud_rate};\ + // ${serial.configuration.data_bits};\ + // ${serial.configuration.flow_control};\ + // ${serial.configuration.parity};\ + // ${serial.configuration.stop_bits}"`; + // } else if (tcp !== undefined) { + // return `streams --tcp "${tcp.configuration.bind_addr}"`; + // } else if (udp !== undefined) { + // return `streams --udp "\ + // ${udp.configuration.bind_addr}|\ + // ${udp.configuration.multicast[0].multiaddr},\ + // ${udp.configuration.multicast[0].interface};"`; + // } else if (process !== undefined) { + // return `streams --stdout "${process.configuration.command}"`; + // } else { + // return undefined; + // } + // }, + // file: (file: $.Origin.File.Configuration): string | undefined => { + // return `-o ${this.filename(file.get().filename())}`; + // }, + // concat: (concat: $.Origin.Concat.Configuration): string | undefined => { + // const relative = + // Array.from(new Set(concat.files().map((f) => getParentFolder(f)))).length === 1; + // return concat + // .files() + // .map((f) => `-o ${relative ? this.filename(f) : f}`) + // .join(' '); + // }, + // from: (observed: $.Observe[]): string | undefined => { + // const get = (observe: $.Observe): string | undefined => { + // const stream = observe.origin.as<$.Origin.Stream.Configuration>( + // $.Origin.Stream.Configuration, + // ); + // const file = observe.origin.as<$.Origin.File.Configuration>( + // $.Origin.File.Configuration, + // ); + // const concat = observe.origin.as<$.Origin.Concat.Configuration>( + // $.Origin.Concat.Configuration, + // ); + // if (stream !== undefined) { + // return this.source().stream(stream); + // } else if (file !== undefined) { + // return this.source().file(file); + // } else if (concat !== undefined) { + // return this.source().concat(concat); + // } else { + // return undefined; + // } + // }; + // if (observed.length === 0) { + // return undefined; + // } + // let args = ''; + // for (const observe of observed) { + // const arg = get(observe); + // if (arg === undefined) { + // return undefined; + // } + // args = `${args}${args.length === 0 ? '' : ' '}${arg}`; + // } + // return args; + // }, + // cx(observed: $.Observe[]): $.Origin.Context | undefined { + // function get(observe: $.Observe): $.Origin.Context | undefined { + // if ( + // observe.origin.as<$.Origin.Stream.Configuration>( + // $.Origin.Stream.Configuration, + // ) !== undefined + // ) { + // return $.Origin.Context.Stream; + // } else if ( + // observe.origin.as<$.Origin.File.Configuration>( + // $.Origin.File.Configuration, + // ) !== undefined + // ) { + // return $.Origin.Context.File; + // } else if ( + // observe.origin.as<$.Origin.Concat.Configuration>( + // $.Origin.Concat.Configuration, + // ) !== undefined + // ) { + // return $.Origin.Context.Concat; + // } else { + // return undefined; + // } + // } + // if (observed.length === 0 || get(observed[0]) === undefined) { + // return undefined; + // } + // const first: $.Origin.Context = get(observed[0]) as unknown as $.Origin.Context; + // for (const observe of observed) { + // if (first !== get(observe)) { + // return undefined; + // } + // } + // return first; + // }, + // }; + // } - protected parser(): { - text(parser: $.Parser.Text.Configuration): string | undefined; - dlt(parser: $.Parser.Dlt.Configuration): string | undefined; - someip(parser: $.Parser.SomeIp.Configuration): string | undefined; - from(observe: $.Observe): string | undefined; - } { - return { - text: (_parser: $.Parser.Text.Configuration): string | undefined => { - return `--parser text`; - }, - dlt: (_parser: $.Parser.Dlt.Configuration): string | undefined => { - return `--parser dlt`; - }, - someip: (_parser: $.Parser.SomeIp.Configuration): string | undefined => { - return `--parser someip`; - }, - from: (observe: $.Observe): string | undefined => { - const text = observe.parser.as<$.Parser.Text.Configuration>( - $.Parser.Text.Configuration, - ); - const dlt = observe.parser.as<$.Parser.Dlt.Configuration>( - $.Parser.Dlt.Configuration, - ); - const someip = observe.parser.as<$.Parser.SomeIp.Configuration>( - $.Parser.SomeIp.Configuration, - ); - if (text !== undefined) { - return this.parser().text(text); - } else if (dlt !== undefined) { - return this.parser().dlt(dlt); - } else if (someip !== undefined) { - return this.parser().someip(someip); - } else { - return undefined; - } - }, - }; - } + // protected parser(): { + // text(parser: $.Parser.Text.Configuration): string | undefined; + // dlt(parser: $.Parser.Dlt.Configuration): string | undefined; + // someip(parser: $.Parser.SomeIp.Configuration): string | undefined; + // from(observe: $.Observe): string | undefined; + // } { + // return { + // text: (_parser: $.Parser.Text.Configuration): string | undefined => { + // return `--parser text`; + // }, + // dlt: (_parser: $.Parser.Dlt.Configuration): string | undefined => { + // return `--parser dlt`; + // }, + // someip: (_parser: $.Parser.SomeIp.Configuration): string | undefined => { + // return `--parser someip`; + // }, + // from: (observe: $.Observe): string | undefined => { + // const text = observe.parser.as<$.Parser.Text.Configuration>( + // $.Parser.Text.Configuration, + // ); + // const dlt = observe.parser.as<$.Parser.Dlt.Configuration>( + // $.Parser.Dlt.Configuration, + // ); + // const someip = observe.parser.as<$.Parser.SomeIp.Configuration>( + // $.Parser.SomeIp.Configuration, + // ); + // if (text !== undefined) { + // return this.parser().text(text); + // } else if (dlt !== undefined) { + // return this.parser().dlt(dlt); + // } else if (someip !== undefined) { + // return this.parser().someip(someip); + // } else { + // return undefined; + // } + // }, + // }; + // } protected filters(): string { const filters = this.session.search.store().filters().get(); diff --git a/application/client/src/app/service/session/dependencies/info.ts b/application/client/src/app/service/session/dependencies/info.ts index db00e42c1a..c09c822477 100644 --- a/application/client/src/app/service/session/dependencies/info.ts +++ b/application/client/src/app/service/session/dependencies/info.ts @@ -1,8 +1,7 @@ import { Subject } from '@platform/env/subscription'; import { unique } from '@platform/env/sequence'; -import { Observe } from '@platform/types/observe'; -import { Protocol } from '@platform/types/observe/parser'; import { getFileName } from '@platform/types/files'; +import { ObserveOperation } from './stream'; export interface IInfoBlock { caption: string; @@ -19,62 +18,63 @@ export class Info { this.updated.destroy(); } - public fromObserveInfo(observe: Observe) { - const uuid = observe.hash().toString(); - if (observe.configuration.parser[Protocol.Dlt] !== undefined) { - const cfg = observe.configuration.parser[Protocol.Dlt]; - if (cfg.fibex_file_paths !== undefined) { - if (cfg.fibex_file_paths.length === 1) { - this.add( - { - caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, - tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, - }, - `fibex_${uuid}`, - ); - } else if (cfg.fibex_file_paths.length > 1) { - this.add( - { - caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, - tooltip: cfg.fibex_file_paths.join('\n'), - }, - `fibex_${uuid}`, - ); - } - } - if (cfg.tz !== undefined) { - this.add( - { - caption: `TZ: ${cfg.tz}`, - tooltip: `Used TimeZone: ${cfg.tz}`, - }, - `tz_${uuid}`, - ); - } - } else if (observe.configuration.parser[Protocol.SomeIp] !== undefined) { - const cfg = observe.configuration.parser[Protocol.SomeIp]; - if (cfg.fibex_file_paths !== undefined) { - if (cfg.fibex_file_paths.length === 1) { - this.add( - { - caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, - tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, - }, - `fibex_${uuid}`, - ); - } else if (cfg.fibex_file_paths.length > 1) { - this.add( - { - caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, - tooltip: cfg.fibex_file_paths.join('\n'), - }, - `fibex_${uuid}`, - ); - } - } - } else if (observe.configuration.parser[Protocol.Text] !== undefined) { - // ignore - } + public fromOperation(operation: ObserveOperation) { + const uuid = operation.uuid; + console.error(`Not Implemented`); + // if (observe.configuration.parser[Protocol.Dlt] !== undefined) { + // const cfg = observe.configuration.parser[Protocol.Dlt]; + // if (cfg.fibex_file_paths !== undefined) { + // if (cfg.fibex_file_paths.length === 1) { + // this.add( + // { + // caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, + // tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, + // }, + // `fibex_${uuid}`, + // ); + // } else if (cfg.fibex_file_paths.length > 1) { + // this.add( + // { + // caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, + // tooltip: cfg.fibex_file_paths.join('\n'), + // }, + // `fibex_${uuid}`, + // ); + // } + // } + // if (cfg.tz !== undefined) { + // this.add( + // { + // caption: `TZ: ${cfg.tz}`, + // tooltip: `Used TimeZone: ${cfg.tz}`, + // }, + // `tz_${uuid}`, + // ); + // } + // } else if (observe.configuration.parser[Protocol.SomeIp] !== undefined) { + // const cfg = observe.configuration.parser[Protocol.SomeIp]; + // if (cfg.fibex_file_paths !== undefined) { + // if (cfg.fibex_file_paths.length === 1) { + // this.add( + // { + // caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, + // tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, + // }, + // `fibex_${uuid}`, + // ); + // } else if (cfg.fibex_file_paths.length > 1) { + // this.add( + // { + // caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, + // tooltip: cfg.fibex_file_paths.join('\n'), + // }, + // `fibex_${uuid}`, + // ); + // } + // } + // } else if (observe.configuration.parser[Protocol.Text] !== undefined) { + // // ignore + // } } public add(block: IInfoBlock, uuid?: string): string { diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index 479a832b9a..464d213b81 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -9,12 +9,12 @@ import { Info } from './info'; import { lockers } from '@ui/service/lockers'; import { Sde } from './observing/sde'; import { TextExportOptions } from '@platform/types/exporting'; -import { SessionSetup, SessionDescriptor } from '@platform/types/bindings'; +import { SessionDescriptor } from '@platform/types/bindings'; +import { SessionOrigin } from '../origin'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; -import * as $ from '@platform/types/observe'; -import { SessionOrigin } from '../origin'; +import { ISourceLink } from '@platform/types/source'; export { ObserveOperation }; @@ -50,7 +50,7 @@ export class Stream extends Subscriber { public readonly observed: { operations: Map; - map: Map; + map: Map; } = { operations: new Map(), map: new Map(), @@ -139,9 +139,9 @@ export class Stream extends Subscriber { isFileSource(): boolean; getSourceFileName(): string | undefined; descriptions: { - get(id: number): $.Types.ISourceLink | undefined; + get(id: number): ISourceLink | undefined; id(alias: string): number | undefined; - request(): Promise<$.Types.ISourceLink[]>; + request(): Promise; count(): number; }; } { @@ -286,7 +286,7 @@ export class Stream extends Subscriber { return sources[0].getOrigin().getFirstFilename(); }, descriptions: { - get: (id: number): $.Types.ISourceLink | undefined => { + get: (id: number): ISourceLink | undefined => { return this.observed.map.get(id); }, id: (alias: string): number | undefined => { @@ -295,7 +295,7 @@ export class Stream extends Subscriber { ); return link !== undefined ? link.id : undefined; }, - request: (): Promise<$.Types.ISourceLink[]> => { + request: (): Promise => { return new Promise((resolve, reject) => { Requests.IpcRequest.send( Requests.Observe.SourcesDefinitionsList.Response, diff --git a/application/client/src/app/service/session/dependencies/teamwork.ts b/application/client/src/app/service/session/dependencies/teamwork.ts index 8cb413b2e0..c830b0b37a 100644 --- a/application/client/src/app/service/session/dependencies/teamwork.ts +++ b/application/client/src/app/service/session/dependencies/teamwork.ts @@ -1,7 +1,6 @@ import { SetupLogger, LoggerInterface } from '@platform/entity/logger'; import { Subscriber, Subjects, Subject } from '@platform/env/subscription'; import { cutUuid } from '@log/index'; -import { Observe } from '@platform/types/observe'; import { GitHubRepo } from '@platform/types/github'; import { FileMetaDataDefinition, FileMetaData } from '@platform/types/github/filemetadata'; import { Session } from '@service/session'; diff --git a/application/client/src/app/service/session/session.ts b/application/client/src/app/service/session/session.ts index bd4c10c13e..d9a66828f0 100644 --- a/application/client/src/app/service/session/session.ts +++ b/application/client/src/app/service/session/session.ts @@ -16,7 +16,6 @@ import { Exporter } from './dependencies/exporter'; import { IRange, fromIndexes } from '@platform/types/range'; import { Attachments } from './dependencies/attachments'; import { Info } from './dependencies/info'; -import { session } from '@service/session'; import { Highlights } from './dependencies/search/highlights'; import { TeamWork } from './dependencies/teamwork'; import { Cli } from './dependencies/cli'; @@ -30,10 +29,6 @@ import { SessionDescriptor } from '@platform/types/bindings'; import * as ids from '@schema/ids'; import * as Requests from '@platform/ipc/request'; -import * as Origins from '@platform/types/observe/origin/index'; -import * as Factory from '@platform/types/observe/factory'; -import * as Parsers from '@platform/types/observe/parser/index'; -import * as Types from '@platform/types/observe/types/index'; export { Stream }; diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index c5f6118ce4..b8877e4b91 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -10,7 +10,6 @@ import { Session } from '@service/session'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; @Component({ diff --git a/application/client/src/app/ui/elements/navigator/providers/entity.ts b/application/client/src/app/ui/elements/navigator/providers/entity.ts index 295be5ac32..6b8649e3cb 100644 --- a/application/client/src/app/ui/elements/navigator/providers/entity.ts +++ b/application/client/src/app/ui/elements/navigator/providers/entity.ts @@ -26,8 +26,10 @@ export class Entity extends PassiveMatchee { public override asObj(): object { if (this.origin instanceof Action) { + console.error(`Not Implemented`); return { - criteria: this.origin.description().major, + // criteria: this.origin.description().major, + criteria: '', }; } else { return { @@ -42,12 +44,17 @@ export class Entity extends PassiveMatchee { } { const major: string | undefined = this.getHtmlOf('html_criteria'); if (this.origin instanceof Action) { - return major === undefined - ? this.origin.description() - : { - major, - minor: this.origin.description().minor, - }; + console.error(`Not Implemented`); + return { + major: 'Not Implemented', + minor: 'Not Implemented', + }; + // return major === undefined + // ? this.origin.description() + // : { + // major, + // minor: this.origin.description().minor, + // }; } else { return { major: major === undefined ? this.origin.name : major, diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts index 22ff3e2eab..5fe48958a2 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.files.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.files.ts @@ -5,8 +5,6 @@ import { bridge } from '@service/bridge'; import { getFileName } from '@platform/types/files'; import { notifications, Notification } from '@ui/service/notifications'; import { FolderEntityType } from '@platform/types/bindings'; - -import * as Factory from '@platform/types/observe/factory'; import { IMenuItem } from '@ui/service/contextmenu'; import { SessionOrigin } from '@service/session/origin'; @@ -136,28 +134,29 @@ export class Provider extends Base { this.ilc.log().info('More than one file detected'); return; } - const filetype = files[0].type; - if (filetype === Factory.FileType.Text) { - this.ilc - .ilc() - .services.system.session.initialize() - .observe(SessionOrigin.file(item.filename)) - .catch((err: Error) => { - this.ilc - .log() - .error(`Fail to open text file; error: ${err.message}`); - }); - } else { - this.ilc - .ilc() - .services.system.session.initialize() - .configure(SessionOrigin.file(item.filename)) - .catch((err: Error) => { - this.ilc - .log() - .error(`Fail to open text file; error: ${err.message}`); - }); - } + console.error(`Not implemented`); + // const filetype = files[0].type; + // if (filetype === Factory.FileType.Text) { + // this.ilc + // .ilc() + // .services.system.session.initialize() + // .observe(SessionOrigin.file(item.filename)) + // .catch((err: Error) => { + // this.ilc + // .log() + // .error(`Fail to open text file; error: ${err.message}`); + // }); + // } else { + // this.ilc + // .ilc() + // .services.system.session.initialize() + // .configure(SessionOrigin.file(item.filename)) + // .catch((err: Error) => { + // this.ilc + // .log() + // .error(`Fail to open text file; error: ${err.message}`); + // }); + // } }) .catch((error) => { this.ilc.log().error(error); diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts index e9975e7fee..b3f371fe3c 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts @@ -35,14 +35,16 @@ export class Provider extends Base { }; } public load(): Promise { - return recent.get().then((actions: Action[]) => { - actions = actions.filter((action) => action.isSuitable(this.observe)); - actions.sort((a: Action, b: Action) => { - return b.stat.score().recent() >= a.stat.score().recent() ? 1 : -1; - }); - this.count = actions.length; - return actions; - }); + console.error(`Not Implemented`); + return Promise.resolve([]); + // return recent.get().then((actions: Action[]) => { + // actions = actions.filter((action) => action.isSuitable(this.operation)); + // actions.sort((a: Action, b: Action) => { + // return b.stat.score().recent() >= a.stat.score().recent() ? 1 : -1; + // }); + // this.count = actions.length; + // return actions; + // }); } public action(action: unknown): void { diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.ts b/application/client/src/app/ui/elements/navigator/providers/provider.ts index 04d6d8df48..a09a117456 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.ts @@ -2,7 +2,7 @@ import { IlcInterface } from '@service/ilc'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { IMenuItem } from '@ui/service/contextmenu'; import { Subject } from '@platform/env/subscription'; -import { Observe } from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; export interface IStatistics { title: string; @@ -23,7 +23,7 @@ export abstract class Provider { constructor( public readonly ilc: IlcInterface & ChangesDetector, public readonly index: number, - protected readonly observe: Observe | undefined, + protected readonly operation: ObserveOperation | undefined, ) {} public abstract load(): Promise; diff --git a/application/client/src/app/ui/elements/navigator/providers/providers.ts b/application/client/src/app/ui/elements/navigator/providers/providers.ts index aff7330f75..6073607566 100644 --- a/application/client/src/app/ui/elements/navigator/providers/providers.ts +++ b/application/client/src/app/ui/elements/navigator/providers/providers.ts @@ -5,11 +5,11 @@ import { Provider as ProviderFiles } from './provider.files'; import { Provider as ProviderRecent } from './provider.recent'; import { IlcInterface } from '@service/ilc'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Observe } from '@platform/types/observe'; import * as wasm from '@loader/wasm'; import { IMenuItem } from '@ui/service/contextmenu'; +import { ObserveOperation } from '@service/session/dependencies/stream'; const PROVIDERS = [ProviderRecent, ProviderFiles]; @@ -20,9 +20,9 @@ export class Providers { protected readonly ilc: IlcInterface & ChangesDetector, protected readonly matcher: wasm.Matcher, protected readonly entries: Entries, - observe: Observe | undefined, + operation: ObserveOperation | undefined, ) { - this.providers = PROVIDERS.map((Ref, i) => new Ref(ilc, i, observe)); + this.providers = PROVIDERS.map((Ref, i) => new Ref(ilc, i, operation)); this.providers.forEach((provider: Provider, i: number) => { ilc.env().subscriber.register( provider.reload.subscribe(() => { diff --git a/application/client/src/app/ui/elements/navigator/state.ts b/application/client/src/app/ui/elements/navigator/state.ts index 722ecb2cd3..b59374b168 100644 --- a/application/client/src/app/ui/elements/navigator/state.ts +++ b/application/client/src/app/ui/elements/navigator/state.ts @@ -7,7 +7,7 @@ import { Holder } from '@module/matcher/holder'; import { unique } from '@platform/env/sequence'; import { INoContentActions, IStatistics } from './providers/provider'; import { IMenuItem } from '@ui/service/contextmenu'; -import { Observe } from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; const MOVE_SELECTION_DELAY = 150; @@ -48,12 +48,12 @@ export class State extends Holder { constructor( protected readonly ilc: IlcInterface & ChangesDetector, protected readonly filterRefGetter: () => HTMLInputElement | undefined, - protected readonly observe: Observe | undefined, + protected readonly operation: ObserveOperation | undefined, ) { super(); this.ilc = ilc; this.entries = new Entries(this.uuid, ilc, this.matcher); - this.providers = new Providers(ilc, this.matcher, this.entries, this.observe); + this.providers = new Providers(ilc, this.matcher, this.entries, this.operation); ilc.env().subscriber.register( ilc .ilc() diff --git a/application/client/src/app/ui/elements/recent/component.ts b/application/client/src/app/ui/elements/recent/component.ts index f795b9c8d8..5aab13fbcd 100644 --- a/application/client/src/app/ui/elements/recent/component.ts +++ b/application/client/src/app/ui/elements/recent/component.ts @@ -17,8 +17,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { State } from './state'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { HiddenFilter } from '@elements/filter.hidden/component'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; @Component({ selector: 'app-recent-actions', @@ -31,7 +30,7 @@ import * as $ from '@platform/types/observe'; @Initial() @Ilc() export class RecentActions extends ChangesDetector implements AfterContentInit, AfterViewInit { - @Input() public observe?: $.Observe; + @Input() public operation!: ObserveOperation; @Output() public applied: EventEmitter = new EventEmitter(); @@ -45,19 +44,20 @@ export class RecentActions extends ChangesDetector implements AfterContentInit, public ngAfterContentInit(): void { this.markChangesForCheck(); - this.state = new State(this, this.observe); + this.state = new State(this, this.operation); this.env().subscriber.register( this.state.update.subscribe(() => { this.detectChanges(); }), ); - if (this.observe !== undefined) { - this.env().subscriber.register( - this.observe.subscribe(() => { - this.state.reload(); - this.detectChanges(); - }), - ); + if (this.operation !== undefined) { + console.error(`Not Implemented`); + // this.env().subscriber.register( + // this.operation.subscribe(() => { + // this.state.reload(); + // this.detectChanges(); + // }), + // ); } } diff --git a/application/client/src/app/ui/elements/recent/icon/component.ts b/application/client/src/app/ui/elements/recent/icon/component.ts index 39d0020c38..49304de5e4 100644 --- a/application/client/src/app/ui/elements/recent/icon/component.ts +++ b/application/client/src/app/ui/elements/recent/icon/component.ts @@ -1,8 +1,7 @@ import { Component, Input } from '@angular/core'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; @Component({ selector: 'app-recent-nature-icon', @@ -13,69 +12,6 @@ import * as $ from '@platform/types/observe'; @Initial() @Ilc() export class RecentIcon { - @Input() public observe!: $.Observe; - - public nature(): { - File(): $.Origin.File.Configuration | undefined; - Concat(): $.Origin.Concat.Configuration | undefined; - Process(): $.Origin.Stream.Stream.Process.Configuration | undefined; - Serial(): $.Origin.Stream.Stream.Serial.Configuration | undefined; - TCP(): $.Origin.Stream.Stream.TCP.Configuration | undefined; - UDP(): $.Origin.Stream.Stream.UDP.Configuration | undefined; - } { - const nature = this.observe.origin.nature(); - return { - File: (): $.Origin.File.Configuration | undefined => { - return nature instanceof $.Origin.File.Configuration ? nature : undefined; - }, - Concat: (): $.Origin.Concat.Configuration | undefined => { - return nature instanceof $.Origin.Concat.Configuration ? nature : undefined; - }, - Process: (): $.Origin.Stream.Stream.Process.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.Process.Configuration - ? nature - : undefined; - }, - Serial: (): $.Origin.Stream.Stream.Serial.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.Serial.Configuration - ? nature - : undefined; - }, - TCP: (): $.Origin.Stream.Stream.TCP.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.TCP.Configuration - ? nature - : undefined; - }, - UDP: (): $.Origin.Stream.Stream.UDP.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.UDP.Configuration - ? nature - : undefined; - }, - }; - } - - public parser(): { - Dlt(): $.Parser.Dlt.Configuration | undefined; - SomeIp(): $.Parser.SomeIp.Configuration | undefined; - Text(): $.Parser.Text.Configuration | undefined; - } { - return { - Dlt: (): $.Parser.Dlt.Configuration | undefined => { - return this.observe.parser.as<$.Parser.Dlt.Configuration>( - $.Parser.Dlt.Configuration, - ); - }, - SomeIp: (): $.Parser.SomeIp.Configuration | undefined => { - return this.observe.parser.as<$.Parser.SomeIp.Configuration>( - $.Parser.SomeIp.Configuration, - ); - }, - Text: (): $.Parser.Text.Configuration | undefined => { - return this.observe.parser.as<$.Parser.Text.Configuration>( - $.Parser.Text.Configuration, - ); - }, - }; - } + @Input() public operation!: ObserveOperation; } export interface RecentIcon extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/icon/template.html b/application/client/src/app/ui/elements/recent/icon/template.html index 9bccc55960..8b378955e5 100644 --- a/application/client/src/app/ui/elements/recent/icon/template.html +++ b/application/client/src/app/ui/elements/recent/icon/template.html @@ -1,8 +1,9 @@ - insert_drive_file +Not Implemented + diff --git a/application/client/src/app/ui/elements/recent/nature/component.ts b/application/client/src/app/ui/elements/recent/nature/component.ts index fd15eb92d9..92dc11a962 100644 --- a/application/client/src/app/ui/elements/recent/nature/component.ts +++ b/application/client/src/app/ui/elements/recent/nature/component.ts @@ -1,8 +1,7 @@ import { Component, AfterContentInit, Input } from '@angular/core'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; @Component({ selector: 'app-recent-nature', @@ -13,49 +12,10 @@ import * as $ from '@platform/types/observe'; @Initial() @Ilc() export class RecentNature implements AfterContentInit { - @Input() public observe!: $.Observe; + @Input() public operation!: ObserveOperation; public ngAfterContentInit(): void { // } - - public as(): { - File(): $.Origin.File.Configuration | undefined; - Concat(): $.Origin.Concat.Configuration | undefined; - Process(): $.Origin.Stream.Stream.Process.Configuration | undefined; - Serial(): $.Origin.Stream.Stream.Serial.Configuration | undefined; - TCP(): $.Origin.Stream.Stream.TCP.Configuration | undefined; - UDP(): $.Origin.Stream.Stream.UDP.Configuration | undefined; - } { - const nature = this.observe.origin.nature(); - return { - File: (): $.Origin.File.Configuration | undefined => { - return nature instanceof $.Origin.File.Configuration ? nature : undefined; - }, - Concat: (): $.Origin.Concat.Configuration | undefined => { - return nature instanceof $.Origin.Concat.Configuration ? nature : undefined; - }, - Process: (): $.Origin.Stream.Stream.Process.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.Process.Configuration - ? nature - : undefined; - }, - Serial: (): $.Origin.Stream.Stream.Serial.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.Serial.Configuration - ? nature - : undefined; - }, - TCP: (): $.Origin.Stream.Stream.TCP.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.TCP.Configuration - ? nature - : undefined; - }, - UDP: (): $.Origin.Stream.Stream.UDP.Configuration | undefined => { - return nature instanceof $.Origin.Stream.Stream.UDP.Configuration - ? nature - : undefined; - }, - }; - } } export interface RecentNature extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/concat/component.ts b/application/client/src/app/ui/elements/recent/nature/concat/component.ts deleted file mode 100644 index 67b777a08a..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/concat/component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -// import { bytesToStr, timestampToUTC } from '@env/str'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-concat', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureConcat implements AfterContentInit { - @Input() public origin!: $.Origin.Concat.Configuration; - - public name!: string; - public path!: string; - public size!: string; - public created!: string; - - public ngAfterContentInit(): void { - // this.name = base.name; - // this.path = base.path; - // this.size = bytesToStr(base.size); - // this.created = timestampToUTC(base.created); - } -} -export interface RecentNatureConcat extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/concat/styles.less b/application/client/src/app/ui/elements/recent/nature/concat/styles.less deleted file mode 100644 index 190cad77be..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/concat/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/concat/template.html b/application/client/src/app/ui/elements/recent/nature/concat/template.html deleted file mode 100644 index e1c1dd52a0..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/concat/template.html +++ /dev/null @@ -1 +0,0 @@ -Concating {{origin.files().length}} files \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/file/component.ts b/application/client/src/app/ui/elements/recent/nature/file/component.ts deleted file mode 100644 index ded62a63b0..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/file/component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { getFileName, getParentFolder } from '@platform/types/files'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-file', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureFile implements AfterContentInit { - @Input() public origin!: $.Origin.File.Configuration; - - public name!: string; - public path!: string; - - public ngAfterContentInit(): void { - this.name = getFileName(this.origin.filename()); - this.path = getParentFolder(this.origin.filename()); - } -} -export interface RecentNatureFile extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/file/styles.less b/application/client/src/app/ui/elements/recent/nature/file/styles.less deleted file mode 100644 index 312349ebd9..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/file/styles.less +++ /dev/null @@ -1,19 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - & > p { - padding: 0; - margin: 0; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/file/template.html b/application/client/src/app/ui/elements/recent/nature/file/template.html deleted file mode 100644 index da8cef9bc8..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/file/template.html +++ /dev/null @@ -1,2 +0,0 @@ -

{{name}}

-

{{path}}

\ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/module.ts b/application/client/src/app/ui/elements/recent/nature/module.ts index 2b5c9d15db..3245e93882 100644 --- a/application/client/src/app/ui/elements/recent/nature/module.ts +++ b/application/client/src/app/ui/elements/recent/nature/module.ts @@ -2,24 +2,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RecentNature } from './component'; -import { RecentNatureConcat } from './concat/component'; -import { RecentNatureFile } from './file/component'; -import { RecentNatureUdp } from './udp/component'; -import { RecentNatureTcp } from './tcp/component'; -import { RecentNatureSerial } from './serial/component'; -import { RecentNatureProcess } from './process/component'; @NgModule({ imports: [CommonModule], - declarations: [ - RecentNature, - RecentNatureConcat, - RecentNatureFile, - RecentNatureUdp, - RecentNatureTcp, - RecentNatureSerial, - RecentNatureProcess, - ], + declarations: [RecentNature], exports: [RecentNature], }) export class RecentNatureModule {} diff --git a/application/client/src/app/ui/elements/recent/nature/process/component.ts b/application/client/src/app/ui/elements/recent/nature/process/component.ts deleted file mode 100644 index 8fdb8e12bd..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/process/component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-process', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureProcess implements AfterContentInit { - @Input() public origin!: $.Origin.Stream.Stream.Process.Configuration; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentNatureProcess extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/process/styles.less b/application/client/src/app/ui/elements/recent/nature/process/styles.less deleted file mode 100644 index 312349ebd9..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/process/styles.less +++ /dev/null @@ -1,19 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - & > p { - padding: 0; - margin: 0; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/process/template.html b/application/client/src/app/ui/elements/recent/nature/process/template.html deleted file mode 100644 index 8c5c072f09..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/process/template.html +++ /dev/null @@ -1,2 +0,0 @@ -

run: {{origin.configuration.command}}

-

from: {{origin.configuration.cwd}}

diff --git a/application/client/src/app/ui/elements/recent/nature/serial/component.ts b/application/client/src/app/ui/elements/recent/nature/serial/component.ts deleted file mode 100644 index f5f3c579e8..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/serial/component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-serial', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureSerial implements AfterContentInit { - @Input() public origin!: $.Origin.Stream.Stream.Serial.Configuration; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentNatureSerial extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/serial/styles.less b/application/client/src/app/ui/elements/recent/nature/serial/styles.less deleted file mode 100644 index 190cad77be..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/serial/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/serial/template.html b/application/client/src/app/ui/elements/recent/nature/serial/template.html deleted file mode 100644 index 2b2323785c..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/serial/template.html +++ /dev/null @@ -1 +0,0 @@ -Connection to: {{origin.configuration.path}} ({{origin.configuration.baud_rate}}/{{origin.configuration.data_bits}}) \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/tcp/component.ts b/application/client/src/app/ui/elements/recent/nature/tcp/component.ts deleted file mode 100644 index d0768f10e4..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/tcp/component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-tcp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureTcp implements AfterContentInit { - @Input() public origin!: $.Origin.Stream.Stream.TCP.Configuration; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentNatureTcp extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/tcp/styles.less b/application/client/src/app/ui/elements/recent/nature/tcp/styles.less deleted file mode 100644 index 190cad77be..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/tcp/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/tcp/template.html b/application/client/src/app/ui/elements/recent/nature/tcp/template.html deleted file mode 100644 index 42e79f9bc2..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/tcp/template.html +++ /dev/null @@ -1 +0,0 @@ -TCP on: {{origin.configuration.bind_addr}} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/template.html b/application/client/src/app/ui/elements/recent/nature/template.html index 43501548ba..432113f8c9 100644 --- a/application/client/src/app/ui/elements/recent/nature/template.html +++ b/application/client/src/app/ui/elements/recent/nature/template.html @@ -1,7 +1,7 @@ - - +Not Implemented + diff --git a/application/client/src/app/ui/elements/recent/nature/udp/component.ts b/application/client/src/app/ui/elements/recent/nature/udp/component.ts deleted file mode 100644 index 7fdcc5d17a..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/udp/component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-nature-udp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNatureUdp implements AfterContentInit { - @Input() public origin!: $.Origin.Stream.Stream.UDP.Configuration; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentNatureUdp extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/udp/styles.less b/application/client/src/app/ui/elements/recent/nature/udp/styles.less deleted file mode 100644 index 190cad77be..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/udp/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - white-space: nowrap; - font-size: 13px; - color: var(--scheme-color-3); - & > span { - color: var(--scheme-color-2); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/udp/template.html b/application/client/src/app/ui/elements/recent/nature/udp/template.html deleted file mode 100644 index 8a091f0643..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/udp/template.html +++ /dev/null @@ -1,4 +0,0 @@ -UDP on: {{origin.configuration.bind_addr}} - - Multicast: {{origin.configuration.multicast[0].multiaddr}} ({{origin.configuration.multicast[0].interface}}) - diff --git a/application/client/src/app/ui/elements/recent/parser/component.ts b/application/client/src/app/ui/elements/recent/parser/component.ts index 27bad6ed8e..a96349850b 100644 --- a/application/client/src/app/ui/elements/recent/parser/component.ts +++ b/application/client/src/app/ui/elements/recent/parser/component.ts @@ -1,8 +1,7 @@ import { Component, AfterContentInit, Input } from '@angular/core'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; @Component({ selector: 'app-recent-parser', @@ -13,34 +12,10 @@ import * as $ from '@platform/types/observe'; @Initial() @Ilc() export class RecentParser implements AfterContentInit { - @Input() public observe!: $.Observe; + @Input() public operation!: ObserveOperation; public ngAfterContentInit(): void { // } - - public as(): { - Dlt(): $.Parser.Dlt.Configuration | undefined; - SomeIp(): $.Parser.SomeIp.Configuration | undefined; - Text(): $.Parser.Text.Configuration | undefined; - } { - return { - Dlt: (): $.Parser.Dlt.Configuration | undefined => { - return this.observe.parser.as<$.Parser.Dlt.Configuration>( - $.Parser.Dlt.Configuration, - ); - }, - SomeIp: (): $.Parser.SomeIp.Configuration | undefined => { - return this.observe.parser.as<$.Parser.SomeIp.Configuration>( - $.Parser.SomeIp.Configuration, - ); - }, - Text: (): $.Parser.Text.Configuration | undefined => { - return this.observe.parser.as<$.Parser.Text.Configuration>( - $.Parser.Text.Configuration, - ); - }, - }; - } } export interface RecentParser extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/parser/dlt/component.ts b/application/client/src/app/ui/elements/recent/parser/dlt/component.ts deleted file mode 100644 index 36b5cd65a8..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/dlt/component.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -// import { bytesToStr, timestampToUTC } from '@env/str'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-parser-dlt', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentParserDlt implements AfterContentInit { - @Input() public parser!: $.Parser.Dlt.Configuration; - - public logLevel!: string; - public fibex: string[] = []; - public timezone: string | undefined; - - public ngAfterContentInit(): void { - if ( - this.parser.configuration.filter_config === undefined || - this.parser.configuration.filter_config.min_log_level === undefined - ) { - this.logLevel = $.Parser.Dlt.DltLogLevelNames[6]; - } else { - this.logLevel = $.Parser.Dlt.getLogLevelName( - this.parser.configuration.filter_config.min_log_level, - ); - } - if (this.parser.configuration.fibex_file_paths === undefined) { - this.fibex = []; - } else { - this.fibex = this.parser.configuration.fibex_file_paths; - } - this.timezone = this.parser.configuration.tz; - } -} -export interface RecentParserDlt extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/parser/dlt/styles.less b/application/client/src/app/ui/elements/recent/parser/dlt/styles.less deleted file mode 100644 index 3b485ebc82..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/dlt/styles.less +++ /dev/null @@ -1,31 +0,0 @@ -@import '../../../../styles/variables.less'; - - - -:host { - position: relative; - display: block; - & span.path { - margin-left: 12px; - } - & .nowrap { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } - & table { - margin-left: -3px; - } - & table > tr { - vertical-align: top; - } - & table * { - border-spacing: 0px; - } - & table { - & td.left-padding { - padding-left: 12px; - } - } - -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/parser/dlt/template.html b/application/client/src/app/ui/elements/recent/parser/dlt/template.html deleted file mode 100644 index 0e8ba23f8c..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/dlt/template.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - -
Log level:{{logLevel}} -

Bound fibex files:

-

{{fibex}}

-
-

Timezone:

-

{{timezone}}

-
diff --git a/application/client/src/app/ui/elements/recent/parser/module.ts b/application/client/src/app/ui/elements/recent/parser/module.ts index 1ae35bc15a..20ccdcc645 100644 --- a/application/client/src/app/ui/elements/recent/parser/module.ts +++ b/application/client/src/app/ui/elements/recent/parser/module.ts @@ -2,12 +2,10 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RecentParser } from './component'; -import { RecentParserDlt } from './dlt/component'; -import { RecentParserSomeIp } from './someip/component'; @NgModule({ imports: [CommonModule], - declarations: [RecentParser, RecentParserDlt, RecentParserSomeIp], + declarations: [RecentParser], exports: [RecentParser], }) export class RecentParserModule {} diff --git a/application/client/src/app/ui/elements/recent/parser/someip/component.ts b/application/client/src/app/ui/elements/recent/parser/someip/component.ts deleted file mode 100644 index d4958480d4..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/someip/component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -// import { bytesToStr, timestampToUTC } from '@env/str'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-recent-parser-someip', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentParserSomeIp implements AfterContentInit { - @Input() public parser!: $.Parser.SomeIp.Configuration; - public fibex: string[] = []; - - public ngAfterContentInit(): void { - if (this.parser.configuration.fibex_file_paths === undefined) { - this.fibex = []; - } else { - this.fibex = this.parser.configuration.fibex_file_paths; - } - } -} -export interface RecentParserSomeIp extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/parser/someip/styles.less b/application/client/src/app/ui/elements/recent/parser/someip/styles.less deleted file mode 100644 index 8ba5c2caa1..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/someip/styles.less +++ /dev/null @@ -1,31 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - padding: 6px 0; - & div.section { - position: relative; - } - & span.path { - margin-left: 12px; - } - & .nowrap { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } - & table > tr { - vertical-align: top; - } - & table * { - border-spacing: 0px; - } - & table { - & td.left-padding { - padding-left: 12px; - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/parser/someip/template.html b/application/client/src/app/ui/elements/recent/parser/someip/template.html deleted file mode 100644 index 2c661cb866..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/someip/template.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - -
-

Bound fibex files:

-

No any fibex files

-

{{fibex}}

-
\ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/parser/template.html b/application/client/src/app/ui/elements/recent/parser/template.html index dd91e7f740..fc92d0c332 100644 --- a/application/client/src/app/ui/elements/recent/parser/template.html +++ b/application/client/src/app/ui/elements/recent/parser/template.html @@ -1,4 +1 @@ - - - - \ No newline at end of file + Not Implemented diff --git a/application/client/src/app/ui/elements/recent/state.ts b/application/client/src/app/ui/elements/recent/state.ts index 8b4456dee0..c237a69b87 100644 --- a/application/client/src/app/ui/elements/recent/state.ts +++ b/application/client/src/app/ui/elements/recent/state.ts @@ -6,20 +6,19 @@ import { IlcInterface } from '@service/ilc'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { Holder } from '@module/matcher'; import { Logger } from '@platform/log'; - -import * as $ from '@platform/types/observe'; +import { ObserveOperation } from '@service/session/dependencies/stream'; export class State extends Holder { public actions: WrappedAction[] = []; public readonly update: Subject = new Subject(); - public readonly observe: $.Observe | undefined; + public readonly operation: ObserveOperation | undefined; private _logger: Logger; - constructor(ilc: IlcInterface & ChangesDetector, observe?: $.Observe) { + constructor(ilc: IlcInterface & ChangesDetector, operation: ObserveOperation) { super(); - this.observe = observe; + this.operation = operation; this._logger = ilc.log(); ilc.env().subscriber.register(recent.updated.subscribe(this.reload.bind(this))); this.reload(); @@ -64,21 +63,22 @@ export class State extends Holder { } public reload(): void { - recent - .get() - .then((actions: Action[]) => { - this.actions = actions - .filter((action) => action.isSuitable(this.observe)) - .map((action) => new WrappedAction(action, this.matcher)); - this.actions.sort((a: WrappedAction, b: WrappedAction) => { - return b.action.stat.score().recent() >= a.action.stat.score().recent() - ? 1 - : -1; - }); - this.update.emit(); - }) - .catch((error: Error) => { - this._logger.error(`Fail to get recent due error: ${error.message}`); - }); + console.error(`Not implemented`); + // recent + // .get() + // .then((actions: Action[]) => { + // this.actions = actions + // .filter((action) => action.isSuitable(this.operation)) + // .map((action) => new WrappedAction(action, this.matcher)); + // this.actions.sort((a: WrappedAction, b: WrappedAction) => { + // return b.action.stat.score().recent() >= a.action.stat.score().recent() + // ? 1 + // : -1; + // }); + // this.update.emit(); + // }) + // .catch((error: Error) => { + // this._logger.error(`Fail to get recent due error: ${error.message}`); + // }); } } diff --git a/application/client/src/app/ui/elements/recent/template.html b/application/client/src/app/ui/elements/recent/template.html index 53eb58ad7e..8f61b86949 100644 --- a/application/client/src/app/ui/elements/recent/template.html +++ b/application/client/src/app/ui/elements/recent/template.html @@ -6,11 +6,11 @@ (contextmenu)="onAllActions($event, action.action)" >
- +
- - + +

diff --git a/application/client/src/app/ui/elements/tree/component.ts b/application/client/src/app/ui/elements/tree/component.ts index 0478456fb1..021415d72e 100644 --- a/application/client/src/app/ui/elements/tree/component.ts +++ b/application/client/src/app/ui/elements/tree/component.ts @@ -13,10 +13,9 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { State } from './state'; import { Initial } from '@env/decorators/initial'; import { stop } from '@ui/env/dom'; +import { SessionOrigin } from '@service/session/origin'; import * as Scheme from './scheme'; -import * as Factory from '@platform/types/observe/factory'; -import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-elements-tree', diff --git a/application/client/src/app/ui/tabs/module.ts b/application/client/src/app/ui/tabs/module.ts index 8e36775805..a3c6f4adf5 100644 --- a/application/client/src/app/ui/tabs/module.ts +++ b/application/client/src/app/ui/tabs/module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ObserveModule } from '@ui/tabs/observe/module'; import { MultipleFilesModule } from '@ui/tabs/multiplefiles/module'; import { SettingsModule } from '@ui/tabs/settings/module'; import { ChangelogModule } from '@ui/tabs/changelogs/module'; @@ -11,7 +10,6 @@ import { SetupModule } from '@ui/tabs/setup/module'; @NgModule({ imports: [ CommonModule, - ObserveModule, MultipleFilesModule, SettingsModule, ChangelogModule, @@ -20,7 +18,7 @@ import { SetupModule } from '@ui/tabs/setup/module'; SetupModule, ], declarations: [], - exports: [ObserveModule, MultipleFilesModule, SettingsModule, ChangelogModule, HelpModule], + exports: [MultipleFilesModule, SettingsModule, ChangelogModule, HelpModule], bootstrap: [], }) export class TabsModule {} diff --git a/application/client/src/app/ui/tabs/multiplefiles/file.holder.ts b/application/client/src/app/ui/tabs/multiplefiles/file.holder.ts index a13a9d716b..c092edad9a 100644 --- a/application/client/src/app/ui/tabs/multiplefiles/file.holder.ts +++ b/application/client/src/app/ui/tabs/multiplefiles/file.holder.ts @@ -1,7 +1,7 @@ import { Matchee } from '@module/matcher'; import { bytesToStr } from '@env/str'; import { File } from '@platform/types/files'; -import { FileType } from '@platform/types/observe/types/file'; +import { FileType } from '@platform/types/files'; import * as wasm from '@loader/wasm'; diff --git a/application/client/src/app/ui/tabs/multiplefiles/state.ts b/application/client/src/app/ui/tabs/multiplefiles/state.ts index c8f6413dd8..dfdca61d0f 100644 --- a/application/client/src/app/ui/tabs/multiplefiles/state.ts +++ b/application/client/src/app/ui/tabs/multiplefiles/state.ts @@ -1,7 +1,7 @@ import { FileHolder } from './file.holder'; import { bytesToStr } from '@env/str'; import { File } from '@platform/types/files'; -import { FileType } from '@platform/types/observe/types/file'; +import { FileType } from '@platform/types/files'; import { Subject } from '@platform/env/subscription'; import { EEventType, IEvent } from './structure/component'; import { Holder } from '@module/matcher'; @@ -12,7 +12,6 @@ import { getUniqueColorTo } from '@ui/styles/colors'; import { Sort } from '@angular/material/sort'; import { ChangesDetector } from '@ui/env/extentions/changes'; -import * as Factory from '@platform/types/observe/factory'; import { SessionOrigin } from '@service/session/origin'; export interface IMultifile { diff --git a/application/client/src/app/ui/tabs/multiplefiles/structure/component.ts b/application/client/src/app/ui/tabs/multiplefiles/structure/component.ts index 14e6f10b0a..7493ad8b11 100644 --- a/application/client/src/app/ui/tabs/multiplefiles/structure/component.ts +++ b/application/client/src/app/ui/tabs/multiplefiles/structure/component.ts @@ -11,7 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { MatSort, Sort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; import { FileHolder } from '../file.holder'; -import { FileType } from '@platform/types/observe/types/file'; +import { FileType } from '@platform/types/files'; import { Subscription } from 'rxjs'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { State } from '../state'; diff --git a/application/client/src/app/ui/tabs/observe/action.ts b/application/client/src/app/ui/tabs/observe/action.ts deleted file mode 100644 index a4beea8239..0000000000 --- a/application/client/src/app/ui/tabs/observe/action.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Subject, Subjects } from '@platform/env/subscription'; - -const DEFAULT_CAPTION = 'Run'; -const DEFAULT_STATE = false; - -export class Action { - public subjects: Subjects<{ - updated: Subject; - apply: Subject; - applied: Subject; - }> = new Subjects({ - updated: new Subject(), - apply: new Subject(), - applied: new Subject(), - }); - - public caption: string = DEFAULT_CAPTION; - public disabled: boolean = DEFAULT_STATE; - - public destroy(): void { - this.subjects.destroy(); - } - - public setCaption(caption: string): void { - this.caption = caption; - this.subjects.get().updated.emit(); - } - - public setDisabled(disabled: boolean): void { - if (this.disabled === disabled) { - return; - } - this.disabled = disabled; - this.subjects.get().updated.emit(); - } - - public defaults(): void { - this.caption = DEFAULT_CAPTION; - this.disabled = DEFAULT_STATE; - this.subjects.get().updated.emit(); - } - - public apply(): void { - this.subjects.get().apply.emit(); - } - - public applied(): void { - this.subjects.get().applied.emit(); - } -} diff --git a/application/client/src/app/ui/tabs/observe/component.ts b/application/client/src/app/ui/tabs/observe/component.ts deleted file mode 100644 index 813e864348..0000000000 --- a/application/client/src/app/ui/tabs/observe/component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Component, - ChangeDetectorRef, - ChangeDetectionStrategy, - Input, - AfterContentInit, - OnDestroy, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State, IApi, IInputs } from './state'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-tabs-observe', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -@Initial() -@Ilc() -export class TabObserve extends ChangesDetector implements AfterContentInit, OnDestroy { - // This method is used only to highlight inputs of component - static inputs(inputs: IInputs): IInputs { - return inputs; - } - - @Input() observe!: Observe; - @Input() api!: IApi; - - public state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngOnDestroy(): void { - this.state.destroy(); - } - - public ngAfterContentInit(): void { - this.state = new State(this, this.observe); - } -} -export interface TabObserve extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/component.ts deleted file mode 100644 index e7340e3291..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from './state'; -import { State as ParserState } from '../parsers/general/plugin/state'; - -@Component({ - selector: 'app-tab-config-schemas', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class ConfigSchemas extends ChangesDetector implements AfterContentInit { - @Input() parserState!: ParserState; - - public readonly state: State = new State(); - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.env().subscriber.register( - this.parserState.selected.subscribe(() => { - this.state.reload(this.parserState); - }), - ); - } -} - -export interface ConfigSchemas extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/entry/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/entry/component.ts deleted file mode 100644 index 4719e84c4e..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/entry/component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component, ChangeDetectorRef, Input } from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings/plugins'; -import { State } from '../state'; - -@Component({ - selector: 'app-tabs-config-schema-entry', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfgiSchemaEntry extends ChangesDetector { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/entry/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/entry/styles.less deleted file mode 100644 index 3919c9833a..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/entry/styles.less +++ /dev/null @@ -1,6 +0,0 @@ -@import '../../../../styles/variables.less'; - -:host { - position: relative; - display: block; -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/entry/template.html b/application/client/src/app/ui/tabs/observe/config-schema/entry/template.html deleted file mode 100644 index e18cc543e9..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/entry/template.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - diff --git a/application/client/src/app/ui/tabs/observe/config-schema/module.ts b/application/client/src/app/ui/tabs/observe/config-schema/module.ts deleted file mode 100644 index d1e9030a11..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/module.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatCardModule } from '@angular/material/card'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatButtonModule } from '@angular/material/button'; -import { MatSelectModule } from '@angular/material/select'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatIconModule } from '@angular/material/icon'; -import { MatDividerModule } from '@angular/material/divider'; - -import { ConfigSchemas } from './component'; -import { ConfgiSchemaEntry } from './entry/component'; -import { ConfigSchemaBool } from './renders/bool/component'; -import { ConfigSchemaInteger } from './renders/integer/component'; -import { ConfigSchemaFloat } from './renders/float/component'; -import { ConfigSchemaString } from './renders/string/component'; -import { ConfigSchemaDropdown } from './renders/dropdown/component'; -import { ConfigSchemaFiles } from './renders/files/component'; -import { ConfigSchemaDirs } from './renders/dirs/component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatCheckboxModule, - MatCardModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatSelectModule, - MatChipsModule, - MatIconModule, - MatDividerModule, - ], - declarations: [ - ConfigSchemas, - ConfgiSchemaEntry, - ConfigSchemaBool, - ConfigSchemaInteger, - ConfigSchemaFloat, - ConfigSchemaString, - ConfigSchemaDropdown, - ConfigSchemaFiles, - ConfigSchemaDirs, - ], - exports: [ConfigSchemas], - bootstrap: [ - ConfigSchemas, - ConfgiSchemaEntry, - ConfigSchemaBool, - ConfigSchemaInteger, - ConfigSchemaFloat, - ConfigSchemaString, - ConfigSchemaDropdown, - ConfigSchemaFiles, - ConfigSchemaDirs, - ], -}) -export class ConfigSchmasModule {} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/component.ts deleted file mode 100644 index 029545a83f..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, ChangeDetectorRef, Input, SimpleChange, AfterContentInit } from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings/plugins'; -import { State } from '../../state'; - -@Component({ - selector: 'app-tabs-config-schema-bool', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaBool extends ChangesDetector implements AfterContentInit { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public value?: boolean; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngAfterContentInit(): void { - const input_type = this.config.input_type; - this.value = this.state.isBooleanItem(input_type) ? input_type.Boolean : false; - this.state.saveConfig(this.config.id, { Boolean: this.value }); - } - - public ngOnCheckboxChange(event: SimpleChange): void { - const val = event as unknown as boolean; - this.state.saveConfig(this.config.id, { Boolean: val }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/styles.less deleted file mode 100644 index 3abbabe199..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/styles.less +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../../styles/variables.less'; diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/template.html deleted file mode 100644 index 432b9b22a3..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/bool/template.html +++ /dev/null @@ -1,9 +0,0 @@ - - {{config.title}} - -

{{config.description}}

diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/component.ts deleted file mode 100644 index c50d7e7ae6..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/component.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings'; -import { State } from '../../state'; -import { bridge } from '@service/bridge'; - -@Component({ - selector: 'app-tabs-config-schema-dirs', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaDirs extends ChangesDetector implements AfterContentInit { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public paths: string[] = []; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngOnAddButtonkclick(): void { - bridge - .folders() - .select() - .then((dirs: string[]) => { - dirs = dirs.filter((added) => { - return this.paths.find((exist) => exist === added) === undefined; - }); - this.paths = this.paths.concat(dirs); - }) - .catch((err: Error) => { - console.error(`Error while opening folders: ${err.message}`); - }) - .finally(() => { - this.update(); - this.detectChanges(); - }); - } - - ngAfterContentInit(): void { - this.update(); - } - - public ngOnRemovePath(dir: string): void { - this.paths = this.paths.filter((f) => f !== dir); - this.update(); - this.detectChanges(); - } - - update(): void { - this.state.saveConfig(this.config.id, { Directories: this.paths }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/styles.less deleted file mode 100644 index 2d749f42f6..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/styles.less +++ /dev/null @@ -1,27 +0,0 @@ -@import '../../../../../styles/variables.less'; -:host { - & mat-form-field { - display: block; - } - & div.controlls { - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - & .chip-listbox { - display: flex; - flex-wrap: wrap; - gap: 8px; - } - & .chip-path { - max-width: 220px; /* Set a fixed width */ - overflow: hidden; /* Hide overflowing content */ - white-space: nowrap; /* Prevent text wrapping */ - direction: rtl; /* Align text to the right */ - text-overflow: ellipsis; /* Show ellipsis at the start */ - text-align: left; /* Ensure the visible part of the text is left-aligned */ - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/template.html deleted file mode 100644 index 813e81934c..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dirs/template.html +++ /dev/null @@ -1,23 +0,0 @@ -

{{config.title}}

-
- - -

{{ path }}

- -
-
-
- -
- -
-

{{config.description}}

diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/component.ts deleted file mode 100644 index 82f444c341..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - SimpleChanges, -} from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings'; -import { State } from '../../state'; - -@Component({ - selector: 'app-tabs-config-schema-dropdown', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaDropdown extends ChangesDetector implements AfterContentInit { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public selectedOption?: string; - public allOptions?: string[]; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngAfterContentInit(): void { - const input_type = this.config.input_type; - if (this.state.isDropdownItem(input_type)) { - this.allOptions = input_type.Dropdown[0]; - this.selectedOption = input_type.Dropdown[1]; - - this.state.saveConfig(this.config.id, { - Dropdown: this.selectedOption, - }); - } - } - - public ngOnSelectionChange(event: SimpleChanges): void { - const opt = event as unknown as string; - this.state.saveConfig(this.config.id, { - Dropdown: opt, - }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/styles.less deleted file mode 100644 index a77a7220b1..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/styles.less +++ /dev/null @@ -1,7 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - & mat-form-field { - display: block; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/template.html deleted file mode 100644 index 2f43bba2b7..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/dropdown/template.html +++ /dev/null @@ -1,7 +0,0 @@ - - {{config.title}} - - {{option}} - - {{config.description}} - diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/files/component.ts deleted file mode 100644 index f1a5abb640..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings'; -import { State } from '../../state'; -import { File } from '@platform/types/files'; -import { bridge } from '@service/bridge'; - -@Component({ - selector: 'app-tabs-config-schema-files', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaFiles extends ChangesDetector implements AfterContentInit { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public paths: File[] = []; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngOnAddButtonkclick(): void { - const inputType = this.config.input_type; - const exts = - typeof inputType === 'object' && 'Files' in inputType && inputType.Files.length > 0 - ? inputType.Files.join(',') - : '*'; - bridge - .files() - .select.custom(exts) - .then((files: File[]) => { - files = files.filter((added) => { - return ( - this.paths.find((exist) => exist.filename === added.filename) === undefined - ); - }); - this.paths = this.paths.concat(files); - }) - - .catch((err: Error) => { - console.error(`Error while opening folders: ${err.message}`); - }) - .finally(() => { - this.update(); - this.detectChanges(); - }); - } - - ngAfterContentInit(): void { - this.update(); - } - - public ngOnRemovePath(file: File): void { - this.paths = this.paths.filter((f) => f.filename !== file.filename); - this.update(); - this.detectChanges(); - } - - update(): void { - const files = this.paths.map((p) => p.filename); - this.state.saveConfig(this.config.id, { Files: files }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/files/styles.less deleted file mode 100644 index 39952dc702..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/styles.less +++ /dev/null @@ -1,14 +0,0 @@ -@import '../../../../../styles/variables.less'; -:host { - & mat-form-field { - display: block; - } - & div.controlls { - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/files/template.html deleted file mode 100644 index 92dc2a7fb7..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/files/template.html +++ /dev/null @@ -1,19 +0,0 @@ -

{{config.title}}

-
- - - {{path.name}} - - - -
- -
- -
-

{{config.description}}

diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/float/component.ts deleted file mode 100644 index 262c53b3a1..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/component.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - OnChanges, - SimpleChanges, -} from '@angular/core'; - -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings'; -import { State } from '../../state'; - -@Component({ - selector: 'app-tabs-config-schema-float', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaFloat extends ChangesDetector implements AfterContentInit, OnChanges { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public value?: number; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngOnChanges(changes: SimpleChanges): void { - if (changes['value']) { - this.state.saveConfig(this.config.id, { - Float: parseFloat(changes['value'].currentValue), - }); - } - } - - ngAfterContentInit(): void { - const input_type = this.config.input_type; - - this.value = this.state.isFloatItem(input_type) ? input_type.Float : 0.0; - this.state.saveConfig(this.config.id, { - Float: this.value, - }); - } - - ngOnInputChange(event: Event): void { - const target = event.target as HTMLInputElement; - if (target) { - this.state.saveConfig(this.config.id, { Float: parseFloat(target.value) }); - } - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/float/styles.less deleted file mode 100644 index 873d484f6b..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - & mat-form-field { - display: block; - } - & input::-webkit-outer-spin-button, - & input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/float/template.html deleted file mode 100644 index 6b510c4ac6..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/float/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - {{config.title}} - - {{config.description}} - diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/component.ts deleted file mode 100644 index 6b1180a1f5..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/component.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - OnChanges, - SimpleChanges, -} from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings'; -import { State } from '../../state'; - -@Component({ - selector: 'app-tabs-config-schema-integer', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaInteger extends ChangesDetector implements AfterContentInit, OnChanges { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public value?: number; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngOnChanges(changes: SimpleChanges): void { - if (changes['value']) { - this.state.saveConfig(this.config.id, { - Integer: parseInt(changes['value'].currentValue, 10), - }); - } - } - - ngAfterContentInit(): void { - const input_type = this.config.input_type; - this.value = this.state.isIntegerItem(input_type) ? input_type.Integer : 0; - this.state.saveConfig(this.config.id, { Integer: this.value }); - } - - public ngOnInputChange(event: Event): void { - const target = event.target as HTMLInputElement; - if (target) { - this.state.saveConfig(this.config.id, { Integer: parseInt(target.value, 10) }); - } - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/styles.less deleted file mode 100644 index 873d484f6b..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - & mat-form-field { - display: block; - } - & input::-webkit-outer-spin-button, - & input::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/template.html deleted file mode 100644 index 6b510c4ac6..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/integer/template.html +++ /dev/null @@ -1,11 +0,0 @@ - - {{config.title}} - - {{config.description}} - diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/component.ts b/application/client/src/app/ui/tabs/observe/config-schema/renders/string/component.ts deleted file mode 100644 index 321e1a88dc..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - OnChanges, - SimpleChanges, -} from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { PluginConfigSchemaItem } from '@platform/types/bindings/plugins'; -import { State } from '../../state'; - -@Component({ - selector: 'app-tabs-config-schema-string', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -export class ConfigSchemaString extends ChangesDetector implements AfterContentInit, OnChanges { - @Input() public config!: PluginConfigSchemaItem; - @Input() public state!: State; - - public value?: string; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngOnChanges(changes: SimpleChanges): void { - if (changes['value']) { - this.state.saveConfig(this.config.id, { - Text: changes['value'].currentValue, - }); - } - } - - ngAfterContentInit(): void { - const input_type = this.config.input_type; - this.value = this.state.isTextItem(input_type) ? input_type.Text : ''; - this.state.saveConfig(this.config.id, { Text: this.value }); - } - - public ngOnInputChange(event: Event): void { - const target = event.target as HTMLInputElement; - this.state.saveConfig(this.config.id, { Text: target?.value ?? '' }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/renders/string/styles.less deleted file mode 100644 index a77a7220b1..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/styles.less +++ /dev/null @@ -1,7 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - & mat-form-field { - display: block; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/template.html b/application/client/src/app/ui/tabs/observe/config-schema/renders/string/template.html deleted file mode 100644 index 01ce426b67..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/renders/string/template.html +++ /dev/null @@ -1,10 +0,0 @@ - - {{config.title}} - - {{config.description}} - diff --git a/application/client/src/app/ui/tabs/observe/config-schema/state.ts b/application/client/src/app/ui/tabs/observe/config-schema/state.ts deleted file mode 100644 index 7a1af2d44a..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/state.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - PluginConfigSchemaItem, - PluginConfigSchemaType, - PluginConfigValue, -} from '@platform/types/bindings/plugins'; - -import { State as ParserState } from '../parsers/general/plugin/state'; - -export class State { - private _parserState?: ParserState; - - public schemas: PluginConfigSchemaItem[] = []; - - public reload(parent: ParserState) { - this.schemas = parent.selectedParser?.info.config_schemas ?? []; - this._parserState = parent; - } - - public saveConfig(id: string, value: PluginConfigValue) { - this._parserState?.saveConfig(id, value); - } - - public isBooleanItem(schema: PluginConfigSchemaType): schema is { Boolean: boolean } { - return typeof schema === 'object' && 'Boolean' in schema; - } - - public isIntegerItem(schema: PluginConfigSchemaType): schema is { Integer: number } { - return typeof schema === 'object' && 'Integer' in schema; - } - - public isFloatItem(schema: PluginConfigSchemaType): schema is { Float: number } { - return typeof schema === 'object' && 'Float' in schema; - } - - public isTextItem(schema: PluginConfigSchemaType): schema is { Text: string } { - return typeof schema === 'object' && 'Text' in schema; - } - - public isDirectoriesItem(schema: PluginConfigSchemaType): schema is 'Directories' { - return schema === 'Directories'; - } - - public isDropdownItem( - schema: PluginConfigSchemaType, - ): schema is { Dropdown: [Array, string] } { - return typeof schema === 'object' && 'Dropdown' in schema; - } - - public isFilesPicker(schema: PluginConfigSchemaType): schema is { Files: Array } { - return typeof schema === 'object' && 'Files' in schema; - } -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/styles.less b/application/client/src/app/ui/tabs/observe/config-schema/styles.less deleted file mode 100644 index b372fafb5e..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/styles.less +++ /dev/null @@ -1,10 +0,0 @@ -@import '../../../styles/variables.less'; - -:host { - position: relative; - display: block; -} - -.itemspadding { - padding-bottom: 20px; -} diff --git a/application/client/src/app/ui/tabs/observe/config-schema/template.html b/application/client/src/app/ui/tabs/observe/config-schema/template.html deleted file mode 100644 index 8438e44a78..0000000000 --- a/application/client/src/app/ui/tabs/observe/config-schema/template.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/application/client/src/app/ui/tabs/observe/error/component.ts b/application/client/src/app/ui/tabs/observe/error/component.ts deleted file mode 100644 index 529738611c..0000000000 --- a/application/client/src/app/ui/tabs/observe/error/component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-tabs-observe-error-state', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class TabObserveErrorState extends ChangesDetector implements AfterContentInit { - @Input() observe!: Observe; - - public error: string | undefined; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.env().subscriber.register( - this.observe.subscribe(() => { - const error = this.observe.validate(); - this.error = error instanceof Error ? error.message : undefined; - this.detectChanges(); - }), - ); - } -} -export interface TabObserveErrorState extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/error/module.ts b/application/client/src/app/ui/tabs/observe/error/module.ts deleted file mode 100644 index 55fc1bf05e..0000000000 --- a/application/client/src/app/ui/tabs/observe/error/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; - -import { TabObserveErrorState } from './component'; - -@NgModule({ - imports: [CommonModule, MatCardModule], - declarations: [TabObserveErrorState], - exports: [TabObserveErrorState], - bootstrap: [TabObserveErrorState], -}) -export class ErrorStateModule {} diff --git a/application/client/src/app/ui/tabs/observe/error/styles.less b/application/client/src/app/ui/tabs/observe/error/styles.less deleted file mode 100644 index f3d8b743a2..0000000000 --- a/application/client/src/app/ui/tabs/observe/error/styles.less +++ /dev/null @@ -1,14 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - top: 0; - left: 0; - width: 100%; - overflow: hidden; - & p.error { - color: var(--scheme-color-error-light); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/error/template.html b/application/client/src/app/ui/tabs/observe/error/template.html deleted file mode 100644 index defc7f64f1..0000000000 --- a/application/client/src/app/ui/tabs/observe/error/template.html +++ /dev/null @@ -1,6 +0,0 @@ - - Error(s) - -

{{error}}

-
-
\ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/module.ts b/application/client/src/app/ui/tabs/observe/module.ts deleted file mode 100644 index cf01954940..0000000000 --- a/application/client/src/app/ui/tabs/observe/module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; - -import { FileModule } from './origin/file/module'; -import { ConcatModule } from './origin/concat/module'; -import { StreamModule } from './origin/stream/module'; - -import { TabObserve } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatFormFieldModule, - MatSelectModule, - FileModule, - ConcatModule, - StreamModule, - ], - declarations: [TabObserve], - exports: [TabObserve], - bootstrap: [TabObserve], -}) -export class ObserveModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/concat/component.ts b/application/client/src/app/ui/tabs/observe/origin/concat/component.ts deleted file mode 100644 index 0b9946a465..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/concat/component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - Component, - ChangeDetectorRef, - AfterViewInit, - Input, - AfterContentInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../state'; -import { State as ParserState } from '@ui/tabs/observe/parsers/state'; - -import * as Origins from '@platform/types/observe/origin/index'; -import * as Parsers from '@platform/types/observe/parser/index'; -@Component({ - selector: 'app-tabs-observe-concat', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class TabObserveConcat extends ChangesDetector implements AfterViewInit, AfterContentInit { - @Input() state!: State; - - public parser!: ParserState; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const origin = this.state.observe.origin.as( - Origins.Concat.Configuration, - ); - if (origin === undefined) { - throw new Error(`Current origin isn't a stream`); - } - this.parser = new ParserState(this.state.observe); - this.env().subscriber.register( - this.state.updates.get().parser.subscribe(() => { - const parser = this.state.getParser().embedded(); - if (parser === undefined) { - return; - } - this.state.observe.parser.overwrite({ - [parser]: Parsers.getByAlias(parser).configuration, - }); - }), - ); - } - - public ngAfterViewInit(): void { - // - } -} -export interface TabObserveConcat extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/concat/module.ts b/application/client/src/app/ui/tabs/observe/origin/concat/module.ts deleted file mode 100644 index c8521eed1d..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/concat/module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; -import { ParserGeneralConfigurationModule } from '@ui/tabs/observe/parsers/general/module'; -import { ParserExtraConfigurationModule } from '@ui/tabs/observe/parsers/extra/module'; - -import { TabObserveConcat } from './component'; - -@NgModule({ - imports: [ - CommonModule, - MatCardModule, - ParserGeneralConfigurationModule, - ParserExtraConfigurationModule, - ], - declarations: [TabObserveConcat], - exports: [TabObserveConcat], - bootstrap: [TabObserveConcat], -}) -export class ConcatModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/concat/styles.less b/application/client/src/app/ui/tabs/observe/origin/concat/styles.less deleted file mode 100644 index 7066106ca8..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/concat/styles.less +++ /dev/null @@ -1,37 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & div.title { - position: absolute; - display: flex; - flex-direction: row; - align-items: center; - left:0; - right: 0; - padding: 0 8px; - background: var(--scheme-color-5); - box-shadow: 0 3px 3px rgba(0,0,0,0.4); - height: 32px; - overflow: hidden; - & > * { - margin: 0 8px; - } - span.filler { - flex:auto; - width: 100%; - margin: 0; - } - p.info { - color: var(--scheme-color-1); - white-space: nowrap; - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/concat/template.html b/application/client/src/app/ui/tabs/observe/origin/concat/template.html deleted file mode 100644 index 19eafb4b94..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/concat/template.html +++ /dev/null @@ -1,6 +0,0 @@ -
- -
-
- -
diff --git a/application/client/src/app/ui/tabs/observe/origin/file/component.ts b/application/client/src/app/ui/tabs/observe/origin/file/component.ts deleted file mode 100644 index 970234317c..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/file/component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - Component, - ChangeDetectorRef, - AfterViewInit, - Input, - AfterContentInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../state'; -import { State as ParserState } from '@ui/tabs/observe/parsers/state'; - -import * as Origins from '@platform/types/observe/origin/index'; -import * as Parsers from '@platform/types/observe/parser/index'; -@Component({ - selector: 'app-tabs-observe-file', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class TabObserveFile extends ChangesDetector implements AfterViewInit, AfterContentInit { - @Input() state!: State; - - public parser!: ParserState; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const origin = this.state.observe.origin.as( - Origins.File.Configuration, - ); - if (origin === undefined) { - throw new Error(`Current origin isn't a stream`); - } - this.parser = new ParserState(this.state.observe); - this.env().subscriber.register( - this.state.updates.get().parser.subscribe(() => { - const parser = this.state.getParser().embedded(); - if (parser === undefined) { - return; - } - this.state.observe.parser.overwrite({ - [parser]: Parsers.getByAlias(parser).configuration, - }); - }), - ); - } - - public ngAfterViewInit(): void { - // - } -} -export interface TabObserveFile extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/file/module.ts b/application/client/src/app/ui/tabs/observe/origin/file/module.ts deleted file mode 100644 index 12d44af104..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/file/module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; -import { ParserGeneralConfigurationModule } from '@ui/tabs/observe/parsers/general/module'; -import { ParserExtraConfigurationModule } from '@ui/tabs/observe/parsers/extra/module'; - -import { TabObserveFile } from './component'; - -@NgModule({ - imports: [ - CommonModule, - MatCardModule, - ParserGeneralConfigurationModule, - ParserExtraConfigurationModule, - ], - declarations: [TabObserveFile], - exports: [TabObserveFile], - bootstrap: [TabObserveFile], -}) -export class FileModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/file/styles.less b/application/client/src/app/ui/tabs/observe/origin/file/styles.less deleted file mode 100644 index a4b66d866b..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/file/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/file/template.html b/application/client/src/app/ui/tabs/observe/origin/file/template.html deleted file mode 100644 index 19eafb4b94..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/file/template.html +++ /dev/null @@ -1,6 +0,0 @@ -
- -
-
- -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/component.ts deleted file mode 100644 index 40a8d6c5b3..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../state'; -import { State as StreamState } from './transport/setup/state'; -import { State as ParserState } from '@ui/tabs/observe/parsers/state'; - -import * as Streams from '@platform/types/observe/origin/stream/index'; -import * as Origins from '@platform/types/observe/origin/index'; -import * as Parsers from '@platform/types/observe/parser/index'; - -@Component({ - selector: 'app-tabs-observe-stream', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class TabObserveStream extends ChangesDetector implements AfterContentInit { - @Input() state!: State; - - public stream!: StreamState; - public parser!: ParserState; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const origin = this.state.observe.origin.as( - Origins.Stream.Configuration, - ); - if (origin === undefined) { - throw new Error(`Current origin isn't a stream`); - } - this.stream = new StreamState(this.state.action, origin.instance); - this.parser = new ParserState(this.state.observe); - this.env().subscriber.register( - this.state.updates.get().stream.subscribe(() => { - const stream = this.state.stream; - if (stream === undefined) { - return; - } - this.stream.from({ [stream]: Streams.getByAlias(stream).configuration }); - }), - this.state.updates.get().parser.subscribe(() => { - const parser = this.state.getParser().embedded(); - if (parser === undefined) { - return; - } - this.state.observe.parser.overwrite({ - [parser]: Parsers.getByAlias(parser).configuration, - }); - }), - ); - } -} -export interface TabObserveStream extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/module.ts deleted file mode 100644 index cf58383024..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; - -import { TabObserveStream } from './component'; -import { StreamsModule } from './transport/setup/module'; -import { ParserGeneralConfigurationModule } from '@ui/tabs/observe/parsers/general/module'; -import { RecentActionsModule } from '@elements/recent/module'; - -@NgModule({ - imports: [ - CommonModule, - MatCardModule, - StreamsModule, - ParserGeneralConfigurationModule, - RecentActionsModule, - ], - declarations: [TabObserveStream], - exports: [TabObserveStream], - bootstrap: [TabObserveStream], -}) -export class StreamModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/styles.less deleted file mode 100644 index a4b66d866b..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/template.html deleted file mode 100644 index 12f9c26228..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/template.html +++ /dev/null @@ -1,17 +0,0 @@ -
- - Connection - - - - - - Recent - - - - -
-
- -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts deleted file mode 100644 index ced07f494f..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/component.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit, HostListener } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Session } from '@service/session/session'; -import { IMenuItem, contextmenu } from '@ui/service/contextmenu'; -import { ObserveOperation } from '@service/session/dependencies/observing/operation'; -import { stop } from '@ui/env/dom'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-transport-review', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Transport extends ChangesDetector implements AfterContentInit { - @Input() public observe!: $.Observe; - @Input() public observer!: ObserveOperation | undefined; - @Input() public session!: Session; - @Input() public finished!: boolean; - - @HostListener('contextmenu', ['$event']) onContextMenu(event: MouseEvent) { - const items: IMenuItem[] = []; - const observer = this.observer; - if (this.observe.origin.files() !== undefined) { - if (this.observe.parser.instance instanceof $.Parser.Text.Configuration) { - // Text file can be opened just once per session - return; - } - } - const stream = this.observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (observer !== undefined) { - items.push( - ...[ - { - caption: 'Stop', - handler: () => { - observer - .abort() - .catch((err: Error) => { - this.log().error( - `Fail to stop observe operation: ${err.message}`, - ); - }) - .finally(() => { - this.detectChanges(); - }); - }, - }, - { - caption: 'Restart', - handler: () => { - observer - .restart() - .catch((err: Error) => { - this.log().error( - `Fail to restart observe operation: ${err.message}`, - ); - }) - .finally(() => { - this.detectChanges(); - }); - }, - }, - ], - ); - } else if (observer === undefined) { - stream !== undefined && - items.push( - ...[ - { - caption: 'Restart', - handler: () => { - console.log(`Not implemented`); - // this.ilc() - // .services.system.session.initialize() - // .observe(this.observe, this.session) - // .catch((err: Error) => { - // this.log().error( - // `Fail to restart observe operation: ${err.message}`, - // ); - // }) - // .finally(() => { - // this.detectChanges(); - // }); - }, - }, - ], - ); - } - stream !== undefined && - items.push( - ...[ - {}, - { - caption: 'Parameters', - handler: () => { - console.log(`Not implemented`); - // this.ilc() - // .services.system.session.initialize() - // .configure(this.observe, this.session); - }, - }, - ], - ); - contextmenu.show({ - items: items, - x: event.pageX, - y: event.pageY, - }); - stop(event); - } - public description!: $.IOriginDetails | undefined; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const description = this.observe.origin.desc(); - if (description instanceof Error) { - this.log().error(`Invalid description: ${description.message}`); - return; - } - this.description = description; - } -} -export interface Transport extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/module.ts deleted file mode 100644 index f5de015b8b..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatIconModule } from '@angular/material/icon'; - -import { Transport } from './component'; - -@NgModule({ - imports: [CommonModule, MatIconModule], - declarations: [Transport], - exports: [Transport] -}) -export class TransportReviewModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/styles.less deleted file mode 100644 index 29299fddc9..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/styles.less +++ /dev/null @@ -1,57 +0,0 @@ -@import '../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 12px 12px; - flex-direction: row; - & div.icon { - color: var(--scheme-color-2); - padding-right: 12px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - justify-items: center; - & mat-icon { - display: block; - font-size: 18px; - width: 18px; - height: 18px; - } - & span { - display: block; - } - &.running { - & mat-icon { - color: var(--scheme-color-0); - } - } - & span.type { - font-size: 11px; - } - & span.state { - font-size: 12px; - &.running { - color: var(--scheme-color-accent); - } - &.stopped { - color: var(--scheme-color-2); - } - } - } - & div.info { - position: relative; - overflow: hidden; - & p.major { - margin-bottom: 2px; - } - & p { - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - white-space: nowrap; - text-align: left; - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/template.html deleted file mode 100644 index 54d84787ff..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/listed/template.html +++ /dev/null @@ -1,10 +0,0 @@ -
- {{description.icon}} - {{description.type}} - {{description.state.running}} - {{description.state.stopped}} -
-
-

{{description.major}}

-

{{description.minor}}

-
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/component.ts deleted file mode 100644 index ef084659d0..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/component.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { - Component, - ChangeDetectorRef, - AfterContentInit, - AfterViewInit, - Input, - ViewChild, - OnDestroy, -} from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { FolderInput, Options as FoldersOptions } from '@elements/folderinput/component'; -import { - AutocompleteInput, - Options as AutocompleteOptions, -} from '@elements/autocomplete/component'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { State } from '../../states/process'; -import { components } from '@env/decorators/initial'; -import { Profile } from '@platform/types/bindings'; -import { Action } from '@ui/tabs/observe/action'; -import { Session } from '@service/session'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -@Component({ - selector: 'app-process-setup-base', - template: '', - standalone: false, -}) -@Ilc() -export class SetupBase - extends ChangesDetector - implements AfterContentInit, AfterViewInit, OnDestroy -{ - @Input() public configuration!: Stream.Process.Configuration; - @Input() public action!: Action; - @Input() public session: Session | undefined; - - public state!: State; - - @ViewChild('cwd') public cwdInputRef!: FolderInput; - @ViewChild('cmd') public cmdInputRef!: AutocompleteInput; - - private _inputs!: { - cmd: AutocompleteOptions; - cwd: FoldersOptions; - }; - - protected setup(): void { - this.cmdInputRef !== undefined && - this.cmdInputRef.set(this.state.configuration.configuration.command); - this.cwdInputRef !== undefined && - this.cwdInputRef.set(this.state.configuration.configuration.cwd); - this.action.setDisabled(this.configuration.validate() instanceof Error); - if (this.state.configuration.configuration.cwd.trim() !== '') { - return; - } - this.ilc() - .services.system.bridge.cwd() - .get(undefined) - .then((cwd) => { - this.cwdInputRef.set(cwd); - this.configuration.configuration.cwd = cwd; - }) - .catch((err: Error) => { - this.log().error(`Fail to get cwd path: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - } - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public setInputs(inputs: { cmd: AutocompleteOptions; cwd: FoldersOptions }): void { - this._inputs = inputs; - } - - public ngOnDestroy(): void { - this.state.destroy(); - } - - public ngAfterContentInit(): void { - this.state = new State(this.action, this.configuration); - this._inputs.cmd.defaults = this.state.configuration.configuration.command; - this._inputs.cwd.defaults = this.state.configuration.configuration.cwd; - this.ilc() - .services.system.bridge.env() - .get() - .then((envs) => { - this.state.configuration.configuration.envs = - Stream.Process.Configuration.sterilizeEnvVars(envs); - }) - .catch((err: Error) => { - this.log().error(`Fail to get envvars path: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - this.ilc() - .services.system.bridge.os() - .envvars() - .then((envvars) => { - this.state.envvars = envvars; - }) - .catch((err: Error) => { - this.log().warn(`Fail to get no context envvars: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - this.ilc() - .services.system.bridge.os() - .shells() - .then((profiles) => { - this.state - .setProfiles(profiles) - .catch((err: Error) => { - this.log().error(`Fail to set profiles: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - }) - .catch((err: Error) => { - this.log().warn(`Fail to get a list of shell's profiles: ${err.message}`); - this.state - .setProfiles([]) - .catch((err: Error) => { - this.log().error(`Fail to set profiles: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - }) - .finally(() => { - this.detectChanges(); - }); - this.env().subscriber.register( - this.configuration.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - this.action.subjects.get().applied.subscribe(() => { - this._inputs.cmd.recent.emit(this.state.configuration.configuration.command); - this.state.configuration.configuration.cwd.trim() !== '' && - this.ilc() - .services.system.bridge.cwd() - .set(undefined, this.state.configuration.configuration.cwd) - .catch((err: Error) => { - this.log().error(`Fail to set cwd path: ${err.message}`); - }); - }), - ); - this.action.setDisabled(this.configuration.validate() instanceof Error); - } - - public ngAfterViewInit(): void { - this.setup(); - } - - public edit(target: 'cmd' | 'cwd', value: string): void { - if (target === 'cmd') { - this.state.configuration.configuration.command = value; - } else { - this.state.configuration.configuration.cwd = value; - } - } - - public enter(target: 'cmd' | 'cwd'): void { - if (this.cwdInputRef.error.is() || this.cmdInputRef.error.is()) { - return; - } - if (target === 'cmd' && this.configuration.validate() === undefined) { - this.action.apply(); - this.cmdInputRef.control.drop(); - } - this.markChangesForCheck(); - } - - public panel(): void { - this.markChangesForCheck(); - } - - public showEnvVars() { - this.ilc().services.ui.popup.open({ - component: { - factory: components.get('app-elements-pairs'), - inputs: { - map: this.state.getSelectedEnvs(), - }, - }, - closeOnKey: 'Escape', - uuid: 'app-elements-pairs', - }); - } - - public importEnvVars(profile: Profile | undefined) { - this.state.importEnvvarsFromShell(profile).catch((err: Error) => { - this.log().error(`Fail to save selected profile: ${err.message}`); - }); - this.detectChanges(); - } -} -export interface SetupBase extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/error.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/error.ts deleted file mode 100644 index b3c490f9c3..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/process/error.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ErrorState } from '@elements/autocomplete/error'; -import { Subject } from '@platform/env/subscription'; - -export class CmdErrorState extends ErrorState { - protected updated: Subject = new Subject(); - protected error: string | undefined; - - public validate(): void { - const matches = this.value.match(/"/gi); - if (matches === null || matches.length === 0) { - this.error = undefined; - } else if (matches.length % 2 !== 0) { - this.error = `Not closed string: no closing "`; - } else { - this.error = undefined; - } - } - - public is(): boolean { - return this.error !== undefined; - } - - public msg(): string { - return this.error === undefined ? '' : this.error; - } - - public observer(): Subject { - return this.updated; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/component.ts deleted file mode 100644 index 237401751d..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/component.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - OnDestroy, - AfterContentInit, - ViewChild, -} from '@angular/core'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../states/serial'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Action } from '@ui/tabs/observe/action'; -import { Session } from '@service/session'; -import { - AutocompleteInput, - Options as AutocompleteOptions, -} from '@elements/autocomplete/component'; -import { Subject } from '@platform/env/subscription'; -import { PathErrorState } from './error'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -@Component({ - selector: 'app-serial-setup-base', - template: '', - standalone: false, -}) -@Ilc() -export class SetupBase extends ChangesDetector implements AfterContentInit, OnDestroy { - @Input() public configuration!: Stream.Serial.Configuration; - @Input() public action!: Action; - @Input() public session: Session | undefined; - @ViewChild('path') public pathInputRef!: AutocompleteInput; - - public state!: State; - public pathInputOptions: AutocompleteOptions = { - name: 'SerialPortPathRecentList', - storage: 'serialport_paths_recent', - defaults: '', - placeholder: 'Enter path to serial port', - label: 'Serial port path', - recent: new Subject(), - error: new PathErrorState(), - }; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state = new State(this.action, this.configuration); - this.env().subscriber.register( - this.state.changed.subscribe(() => { - this.detectChanges(); - }), - ); - this.env().subscriber.register( - this.configuration.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - this.action.subjects.get().applied.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - ); - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.state.scan().start(); - } - - public ngOnDestroy() { - this.state.scan().stop(); - this.state.destroy(); - } - - public selectDetectedPort(port: string): void { - this.state.configuration.configuration.path = port; - this.pathInputRef.set(port).focus(); - } - - public onPathChange(value: string): void { - this.state.configuration.configuration.path = value; - } - - public onPathEnter(): void { - if (this.pathInputRef.error.is() || this.state.configuration.validate() !== undefined) { - return; - } - this.action.apply(); - this.markChangesForCheck(); - } - - public panel(): void { - this.markChangesForCheck(); - } -} -export interface SetupBase extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/error.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/error.ts deleted file mode 100644 index 1921cae447..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/error.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Subject } from '@platform/env/subscription'; -import { ErrorState } from '@elements/autocomplete/error'; - -export class PathErrorState extends ErrorState { - protected updated: Subject = new Subject(); - protected error: string | undefined; - - public validate(): void { - if (this.value.trim().length === 0) { - this.error = 'No path' - } else { - this.error = undefined; - } - } - - public is(): boolean { - return this.error !== undefined; - } - - public msg(): string { - return this.error === undefined ? '' : this.error; - } - - public observer(): Subject { - return this.updated; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/module.ts deleted file mode 100644 index 3a2c02aa66..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/serial/module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - MatAutocompleteModule, - MatRadioModule, - ], - declarations: [], - exports: [], -}) -export class BaseModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/component.ts deleted file mode 100644 index bb70e7bf03..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, ChangeDetectorRef, Input, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../states/tcp'; -import { Action } from '@ui/tabs/observe/action'; -import { Session } from '@service/session'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -@Component({ - selector: 'app-tcp-setup-base', - template: '', - standalone: false, -}) -@Ilc() -export class SetupBase extends ChangesDetector implements OnDestroy, AfterContentInit { - @Input() public configuration!: Stream.TCP.Configuration; - @Input() public action!: Action; - @Input() public session: Session | undefined; - - public state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngOnDestroy(): void { - this.state.destroy(); - } - - public ngAfterContentInit(): void { - this.state = new State(this.action, this.configuration); - this.env().subscriber.register( - this.configuration.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - this.action.subjects.get().applied.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - ); - this.action.setDisabled(this.configuration.validate() instanceof Error); - } -} -export interface SetupBase extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/error.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/error.ts deleted file mode 100644 index cdea46d467..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/tcp/error.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ErrorStateMatcher } from '@angular/material/core'; -import { UntypedFormControl, FormGroupDirective, NgForm } from '@angular/forms'; - -import * as ip from '@platform/env/ipaddr'; - -export enum Field { - address = 'address', - multicastInterface = 'multicastInterface', -} - -export enum Codes { - NO_ERRORS = 'NO_ERRORS', - REQUIRED = 'REQUIRED', - INVALID = 'INVALID', -} - -export type UpdateHandler = () => void; - -export class ErrorState implements ErrorStateMatcher { - readonly _alias: Field; - readonly _updated: UpdateHandler; - private _code: Codes = Codes.NO_ERRORS; - - constructor(alias: Field, updated: UpdateHandler) { - this._alias = alias; - this._updated = updated; - } - - public isErrorState( - control: UntypedFormControl | null, - _form: FormGroupDirective | NgForm | null, - ): boolean { - if (control === null) { - return false; - } - if (this.isFieldRequired(control.value)) { - this._code = Codes.REQUIRED; - } else if (!this.isFieldValid(control.value)) { - this._code = Codes.INVALID; - } else { - this._code = Codes.NO_ERRORS; - } - this._updated(); - return this._code !== Codes.NO_ERRORS; - } - - public isFieldValid(value: string): boolean { - if (typeof value !== 'string') { - return false; - } - switch (this._alias) { - case Field.address: - return ( - ip.isValidIPv4(value) || - ip.isValidIPv6(value) || - ip.isValidIPv4WithPort(value) || - ip.isValidIPv6WithPort(value) - ); - case Field.multicastInterface: - return ( - ip.isValidIPv4(value) || - ip.isValidIPv6(value) || - ip.isValidIPv4WithPort(value) || - ip.isValidIPv6WithPort(value) - ); - default: - throw new Error(`Unexpected Field value: ${this._alias}`); - } - } - - public isFieldRequired(value: string): boolean { - if (typeof value !== 'string') { - return true; - } - switch (this._alias) { - case Field.address: - case Field.multicastInterface: - return value.trim() === ''; - } - } - - public getErrorCode(): Codes { - return this._code; - } - - public isValid(): boolean { - return this._code === Codes.NO_ERRORS; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/component.ts deleted file mode 100644 index 409586df14..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Component, ChangeDetectorRef, Input, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../../states/udp'; -import { Action } from '@ui/tabs/observe/action'; -import { Session } from '@service/session'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -@Component({ - selector: 'app-udp-setup-base', - template: '', - standalone: false, -}) -@Ilc() -export class SetupBase extends ChangesDetector implements OnDestroy, AfterContentInit { - @Input() public configuration!: Stream.UDP.Configuration; - @Input() public action!: Action; - @Input() public session: Session | undefined; - - public state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngOnDestroy(): void { - this.state.destroy(); - } - - public ngAfterContentInit(): void { - this.state = new State(this.action, this.configuration); - this.env().subscriber.register( - this.configuration.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - this.action.subjects.get().applied.subscribe(() => { - this.action.setDisabled(this.configuration.validate() instanceof Error); - this.detectChanges(); - }), - ); - this.action.setDisabled(this.configuration.validate() instanceof Error); - } - - public addMulticast() { - this.state.addMulticast(); - } - - public removeMulticast(index: number) { - this.state.removeMulticast(index); - } -} -export interface SetupBase extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/error.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/error.ts deleted file mode 100644 index fa36b206a6..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/error.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { ErrorStateMatcher } from '@angular/material/core'; -import { UntypedFormControl, FormGroupDirective, NgForm } from '@angular/forms'; - -import * as ip from '@platform/env/ipaddr'; - -export enum Field { - bindingAddress = 'bindingAddress', - multicastAddress = 'multicastAddress', - multicastInterface = 'multicastInterface', -} - -export enum Codes { - NO_ERRORS = 'NO_ERRORS', - REQUIRED = 'REQUIRED', - INVALID = 'INVALID', -} - -export type UpdateHandler = () => void; - -export class ErrorState implements ErrorStateMatcher { - readonly _alias: Field; - readonly _updated: UpdateHandler; - private _code: Codes = Codes.NO_ERRORS; - - constructor(alias: Field, updated: UpdateHandler) { - this._alias = alias; - this._updated = updated; - } - - public isErrorState( - control: UntypedFormControl | null, - _form: FormGroupDirective | NgForm | null, - ): boolean { - if (control === null) { - return false; - } - const prev = this._code; - if (this.isFieldRequired(control.value)) { - this._code = Codes.REQUIRED; - } else if (!this.isFieldValid(control.value)) { - this._code = Codes.INVALID; - } else { - this._code = Codes.NO_ERRORS; - } - prev !== this._code && this._updated(); - return this._code !== Codes.NO_ERRORS; - } - - public isFieldValid(value: string): boolean { - if (typeof value !== 'string') { - return false; - } - switch (this._alias) { - case Field.bindingAddress: - case Field.multicastAddress: - return ( - ip.isValidIPv4(value) || - ip.isValidIPv6(value) || - ip.isValidIPv4WithPort(value) || - ip.isValidIPv6WithPort(value) - ); - case Field.multicastInterface: - return ip.isValidIPv4(value) || ip.isValidIPv6(value); - default: - throw new Error(`Unexpected Field value: ${this._alias}`); - } - } - - public isFieldRequired(value: string): boolean { - if (typeof value !== 'string') { - return true; - } - switch (this._alias) { - case Field.bindingAddress: - case Field.multicastAddress: - case Field.multicastInterface: - return value.trim() === ''; - } - } - - public getErrorCode(): Codes { - return this._code; - } - - public isValid(): boolean { - return this._code === Codes.NO_ERRORS; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/module.ts deleted file mode 100644 index d5d9f86eac..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; - -import { Multicast } from './multicast/component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatIconModule, - MatFormFieldModule, - MatInputModule, - ], - declarations: [Multicast], - exports: [Multicast] -}) -export class BaseModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/component.ts deleted file mode 100644 index 1cbf4793d1..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import * as Errors from '../error'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -@Component({ - selector: 'app-transport-udp-multicast', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Multicast { - @Input() public multicast!: Stream.UDP.Multicast; - - public errors: { - multiaddr: Errors.ErrorState; - interface: Errors.ErrorState; - } = { - multiaddr: new Errors.ErrorState(Errors.Field.multicastAddress, () => { - // this.update(); - }), - interface: new Errors.ErrorState(Errors.Field.multicastAddress, () => { - // this.update(); - }), - }; - @Output() public clean: EventEmitter = new EventEmitter(); - - public onChanges() { - if ( - this.multicast.multiaddr.trim() !== '' || - (this.multicast.interface !== undefined && this.multicast.interface.trim() !== '') - ) { - return; - } - this.clean.next(); - } - - public onRemove() { - this.clean.next(); - } -} -export interface Multicast extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/styles.less deleted file mode 100644 index 9b5a4a41cf..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/styles.less +++ /dev/null @@ -1,26 +0,0 @@ -@import '../../../../../../../../../styles/variables.less'; - - -:host { - align-items: baseline; - position: relative; - display: flex; - margin: 0; - cursor: default; - padding-right: 6px; - & mat-form-field.inline { - margin-right: 12px; - } - & .mat-icon { - width: 16px; - height: 16px; - font-size: 16px; - color: var(--scheme-color-1); - &:hover { - color: var(--scheme-color-3); - } - } - & span.filler { - flex: auto; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/template.html deleted file mode 100644 index e1167c5145..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/bases/udp/multicast/template.html +++ /dev/null @@ -1,22 +0,0 @@ - - Address - - - Please enter a valid IPv4/IPv6 address - - - required - - - - Interface Address - - - Please define valid interface - - - required - - - -close diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/component.ts deleted file mode 100644 index b4f3201152..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/component.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - AfterViewInit, - OnDestroy, - ViewEncapsulation, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Options as AutocompleteOptions } from '@elements/autocomplete/component'; -import { Options as FoldersOptions } from '@elements/folderinput/component'; -import { Subject } from '@platform/env/subscription'; -import { CmdErrorState } from '../../bases/process/error'; -import { SetupBase } from '../../bases/process/component'; -import { Profile } from '@platform/types/bindings'; - -@Component({ - selector: 'app-transport-process', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - encapsulation: ViewEncapsulation.None, - standalone: false, -}) -@Ilc() -export class Setup extends SetupBase implements AfterContentInit, AfterViewInit, OnDestroy { - @Input() public update?: Subject; - - public readonly inputs: { - cmd: AutocompleteOptions; - cwd: FoldersOptions; - } = { - cmd: { - name: 'CommandsRecentList', - storage: 'processes_cmd_recent', - defaults: '', - placeholder: 'Enter terminal command', - label: 'Terminal command', - recent: new Subject(), - error: new CmdErrorState(), - }, - cwd: { - placeholder: 'Enter working folder', - label: 'Working folder', - defaults: '', - passive: true, - }, - }; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - this.setInputs(this.inputs); - } - - public override ngAfterContentInit(): void { - this.update !== undefined && - this.env().subscriber.register( - this.update.subscribe(() => { - this.cmdInputRef.set(this.state.configuration.configuration.command); - this.cwdInputRef.set(this.state.configuration.configuration.cwd); - }), - ); - super.ngAfterContentInit(); - } - - public getEnvvarsCount(profile: Profile) { - return profile.envvars ? profile.envvars.size : 0; - } -} -export interface Setup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/module.ts deleted file mode 100644 index 351a776791..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { AutocompleteModule } from '@elements/autocomplete/module'; -import { FolderInputModule } from '@elements/folderinput/module'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatDividerModule } from '@angular/material/divider'; -import { Setup } from './component'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - -@NgModule({ - imports: [ - CommonModule, - AutocompleteModule, - FolderInputModule, - MatMenuModule, - MatDividerModule, - MatProgressBarModule, - MatProgressSpinnerModule, - ], - declarations: [Setup], - exports: [Setup] -}) -export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/styles.less deleted file mode 100644 index 992a05aa79..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/styles.less +++ /dev/null @@ -1,61 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -app-transport-process { - position: relative; - display: block; - width: 100%; - & div.command { - position: relative; - display: flex; - width: 100%; - align-items: center; - margin-bottom: 12px; - & button { - margin-left: 6px; - } - } -} -.app-transport-process-menu { - & button[mat-menu-item] { - & div.shell-profile { - position: relative; - margin-top: -7px; - & > span { - height: 16px; - line-height: 16px; - } - & > span.envvars-count { - color: var(--scheme-color-3); - } - & > span.shell-path { - position: relative; - display: block; - color: var(--scheme-color-3); - font-size: 10px; - max-width: 250px; - overflow: hidden; - text-overflow: ellipsis; - margin-top: -8px; - white-space: nowrap; - direction: rtl; - max-width: 200px; - } - } - &[data-selected="true"] { - & div.shell-profile { - &::before { - display: block; - position: absolute; - width: 2px; - height: 24px; - background: var(--scheme-color-accent); - left: -9px; - top: 7px; - content: ''; - } - } - } - } - -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/template.html deleted file mode 100644 index 1c2757cde8..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/process/template.html +++ /dev/null @@ -1,53 +0,0 @@ -
- - -
- - - - - -

Import variables from:

- -
- -

Loading profiles...

- -
- - -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/component.ts deleted file mode 100644 index de3561dee3..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, ChangeDetectorRef, Input, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Subject } from '@platform/env/subscription'; -import { SetupBase } from '../../bases/serial/component'; - -@Component({ - selector: 'app-transport-serial', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Setup extends SetupBase implements AfterContentInit, OnDestroy { - @Input() public update?: Subject; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public override ngAfterContentInit() { - this.update !== undefined && - this.env().subscriber.register( - this.update.subscribe(() => { - this.detectChanges(); - }), - ); - super.ngAfterContentInit(); - } -} -export interface TransportSerial extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/module.ts deleted file mode 100644 index 3856d61cf8..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/module.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { BaseModule } from '../../bases/serial/module'; -import { Setup } from './component'; -import { AutocompleteModule } from '@elements/autocomplete/module'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - AutocompleteModule, - MatRadioModule, - BaseModule, - MatProgressSpinnerModule, - MatMenuModule, - ], - declarations: [Setup], - exports: [Setup], -}) -export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/styles.less deleted file mode 100644 index bb4e6031d9..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/styles.less +++ /dev/null @@ -1,24 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - p.settings { - margin: 8px 0; - } - & div.path { - position: relative; - display: flex; - width: 100%; - align-items: center; - margin-bottom: 12px; - & button { - margin-left: 6px; - } - } - div.controlls{ - text-align: right; - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/template.html deleted file mode 100644 index f97479eb58..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/serial/template.html +++ /dev/null @@ -1,96 +0,0 @@ -
- - -
- -

Settings

- - Baud Rate - - - {{value}} - - - - - Custom Baud Rate - - - - Data Bits - - - {{value}} - - - - - Flow Control - - - {{keyvalue.name}} - - - - - Parity - - - {{keyvalue.name}} - - - - - Stop Bits - - - {{value}} - - - - - Exclusive opening - - - {{keyvalue.name}} - - - - - Delay on writing - - - {{keyvalue.name}} - - - -
- -
- - - -

No ports found

-
- - - -
- -

Scanning for available ports...

- -
-
\ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/component.ts deleted file mode 100644 index ca5a8abf43..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, ChangeDetectorRef, Input, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Subject } from '@platform/env/subscription'; -import { SetupBase } from '../../bases/tcp/component'; - -@Component({ - selector: 'app-transport-tcp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Setup extends SetupBase implements OnDestroy, AfterContentInit { - @Input() public update?: Subject; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public override ngAfterContentInit(): void { - this.update !== undefined && - this.env().subscriber.register( - this.update.subscribe(() => { - this.detectChanges(); - }), - ); - super.ngAfterContentInit(); - } -} -export interface Setup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/module.ts deleted file mode 100644 index 408a3f3895..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { Setup } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - ], - declarations: [Setup], - exports: [Setup] -}) -export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/styles.less deleted file mode 100644 index 1890004256..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/styles.less +++ /dev/null @@ -1,14 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - & mat-form-field.full-width { - display: block; - } - & mat-form-field.inline { - margin-right: 12px; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/template.html deleted file mode 100644 index 8d0df136b3..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/tcp/template.html +++ /dev/null @@ -1,20 +0,0 @@ - - IP Address - - - Please enter a valid IPv4/IPv6 address - - - required - - - diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/component.ts deleted file mode 100644 index 772ff593db..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, ChangeDetectorRef, Input, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Subject } from '@platform/env/subscription'; -import { SetupBase } from '../../bases/udp/component'; - -@Component({ - selector: 'app-transport-udp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Setup extends SetupBase implements OnDestroy, AfterContentInit { - @Input() public update?: Subject; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public override ngAfterContentInit(): void { - this.update !== undefined && - this.env().subscriber.register( - this.update.subscribe(() => { - this.detectChanges(); - }), - ); - super.ngAfterContentInit(); - } -} -export interface Setup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/module.ts deleted file mode 100644 index d3cee77cb9..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; - -import { Setup } from './component'; -import { BaseModule } from '../../bases/udp/module'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - BaseModule, - ], - declarations: [Setup], - exports: [Setup] -}) -export class SetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/styles.less deleted file mode 100644 index 201ebcbb99..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/styles.less +++ /dev/null @@ -1,28 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - & mat-form-field.full-width { - display: block; - } - & mat-form-field.inline { - margin-right: 12px; - } - div.mutlicasts{ - margin: 12px 0; - & p { - padding-top: 6px; - } - } - div.controlls{ - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/template.html deleted file mode 100644 index 79aee38d2a..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/complete/udp/template.html +++ /dev/null @@ -1,20 +0,0 @@ - - IP Address - - - Please enter a valid IPv4/IPv6 address - - - required - - -
- -

Multicasts

-
- -
- -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/component.ts deleted file mode 100644 index 5f24f76559..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from './state'; - -import * as Stream from '@platform/types/observe/origin/stream'; - -@Component({ - selector: 'app-transport', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Transport extends ChangesDetector implements AfterContentInit { - public Source = Stream.Stream.Source; - - @Input() public state!: State; - - public instance: { - [Stream.Stream.Source.Process]: Stream.Stream.Process.Configuration | undefined; - [Stream.Stream.Source.Serial]: Stream.Stream.Serial.Configuration | undefined; - [Stream.Stream.Source.UDP]: Stream.Stream.UDP.Configuration | undefined; - [Stream.Stream.Source.TCP]: Stream.Stream.TCP.Configuration | undefined; - } = { - [Stream.Stream.Source.Process]: undefined, - [Stream.Stream.Source.Serial]: undefined, - [Stream.Stream.Source.UDP]: undefined, - [Stream.Stream.Source.TCP]: undefined, - }; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.env().subscriber.register( - this.state.updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - this.update(); - } - - protected update(): Transport { - (this.instance as any) = { - [this.state.configuration.instance.alias()]: this.state.configuration.instance, - }; - return this; - } -} -export interface Transport extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/module.ts deleted file mode 100644 index 88c5de6b96..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { SetupModule as UdpSetupModule } from './complete/udp/module'; -import { SetupModule as TcpSetupModule } from './complete/tcp/module'; -import { SetupModule as SerialSetupModule } from './complete/serial/module'; -import { SetupModule as ProcessSetupModule } from './complete/process/module'; - -import { Transport } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatDividerModule, - MatFormFieldModule, - MatSelectModule, - UdpSetupModule, - TcpSetupModule, - SerialSetupModule, - ProcessSetupModule, - ], - declarations: [Transport], - exports: [Transport, UdpSetupModule, TcpSetupModule, SerialSetupModule, ProcessSetupModule], -}) -export class StreamsModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/component.ts deleted file mode 100644 index e0ed2fbcc4..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/component.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - Component, - ChangeDetectorRef, - AfterContentInit, - OnDestroy, - ViewEncapsulation, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Options as AutocompleteOptions } from '@elements/autocomplete/component'; -import { Options as FoldersOptions } from '@elements/folderinput/component'; -import { Subject } from '@platform/env/subscription'; -import { CmdErrorState } from '../../bases/process/error'; -import { SetupBase } from '../../bases/process/component'; -import { Profile } from '@platform/types/bindings'; - -@Component({ - selector: 'app-process-quicksetup', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - encapsulation: ViewEncapsulation.None, - standalone: false, -}) -@Ilc() -export class QuickSetup extends SetupBase implements AfterContentInit, OnDestroy { - public readonly inputs: { - cmd: AutocompleteOptions; - cwd: FoldersOptions; - } = { - cmd: { - name: 'CommandsRecentList', - storage: 'processes_cmd_recent', - defaults: '', - placeholder: 'Enter terminal command', - label: 'Terminal command', - recent: new Subject(), - error: new CmdErrorState(), - }, - cwd: { - placeholder: 'Enter working folder', - label: undefined, - defaults: '', - passive: true, - }, - }; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - this.setInputs(this.inputs); - } - - public getEnvvarsCount(profile: Profile) { - return profile.envvars ? profile.envvars.size : 0; - } - - public destroy(): Promise { - return Promise.resolve(); - } -} -export interface QuickSetup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/module.ts deleted file mode 100644 index 684c7c7713..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { AutocompleteModule } from '@elements/autocomplete/module'; -import { FolderInputModule } from '@elements/folderinput/module'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatDividerModule } from '@angular/material/divider'; -import { QuickSetup } from './component'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; - -@NgModule({ - imports: [ - CommonModule, - AutocompleteModule, - FolderInputModule, - MatMenuModule, - MatDividerModule, - MatProgressBarModule, - MatProgressSpinnerModule, - ], - declarations: [QuickSetup], - exports: [QuickSetup] -}) -export class QuickSetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/styles.less deleted file mode 100644 index eb5b473183..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/styles.less +++ /dev/null @@ -1,67 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -app-process-quicksetup { - position: relative; - display: block; - width: 100%; - & div.command { - position: relative; - display: flex; - width: 100%; - align-items: center; - margin-bottom: 6px; - & button { - margin-left: 6px; - } - } - & mat-form-field.full-width { - display: block; - } - & mat-form-field.outline { - margin: 0!important; - } - -} -.app-transport-process-menu { - & .mat-menu-item { - & > div.shell-profile { - position: relative; - margin-top: -7px; - & > span { - height: 16px; - line-height: 16px; - } - & > span.envvars-count { - color: var(--scheme-color-3); - } - & > span.shell-path { - position: relative; - display: block; - color: var(--scheme-color-3); - font-size: 10px; - max-width: 250px; - overflow: hidden; - text-overflow: ellipsis; - margin-top: -8px; - white-space: nowrap; - direction: rtl; - max-width: 200px; - } - } - } - & .mat-menu-item[data-selected="true"] { - & > div.shell-profile { - &::before { - display: block; - position: absolute; - width: 2px; - height: 24px; - background: var(--scheme-color-accent); - left: -9px; - top: 11px; - content: ''; - } - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/template.html deleted file mode 100644 index 1c2757cde8..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/process/template.html +++ /dev/null @@ -1,53 +0,0 @@ -
- - -
- - - - - -

Import variables from:

- -
- -

Loading profiles...

- -
- - -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/component.ts deleted file mode 100644 index 3f0bda0436..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, ChangeDetectorRef, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { SetupBase } from '../../bases/serial/component'; - -@Component({ - selector: 'app-serial-quicksetup', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class QuickSetup extends SetupBase implements AfterContentInit, OnDestroy { - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public connect() { - this.action.apply(); - } -} -export interface QuickSetup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/module.ts deleted file mode 100644 index 95da2b450a..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/module.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatRadioModule } from '@angular/material/radio'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; -import { MatAutocompleteModule } from '@angular/material/autocomplete'; -import { BaseModule } from '../../bases/serial/module'; -import { QuickSetup } from './component'; -import { SetupModule } from '../../complete/serial/module'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - MatAutocompleteModule, - MatRadioModule, - BaseModule, - SetupModule, - ], - declarations: [QuickSetup], - exports: [QuickSetup], -}) -export class QuickSetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/styles.less deleted file mode 100644 index 32de7112f4..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - width: auto; - padding: 0 24px; - div.controlls{ - text-align: right; - padding: 8px 0 0 0; - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/template.html deleted file mode 100644 index 5ed1034fc1..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/serial/template.html +++ /dev/null @@ -1,4 +0,0 @@ - -
- -
\ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/component.ts deleted file mode 100644 index 76101e9545..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { SetupBase } from '../../bases/tcp/component'; - -@Component({ - selector: 'app-tcp-quicksetup', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class QuickSetup extends SetupBase implements AfterContentInit { - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public connect() { - this.action.apply(); - } -} -export interface QuickSetup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/module.ts deleted file mode 100644 index d25b0c9b98..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/module.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; - -import { QuickSetup } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - ], - declarations: [QuickSetup], - exports: [QuickSetup] -}) -export class QuickSetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/styles.less deleted file mode 100644 index 537eda256d..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/styles.less +++ /dev/null @@ -1,25 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - & mat-form-field { - width: 100%; - } - & mat-form-field.full-width { - display: block; - } - & mat-form-field.inline { - margin-right: 12px; - } - div.controlls{ - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/template.html deleted file mode 100644 index 1ec1653777..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/tcp/template.html +++ /dev/null @@ -1,13 +0,0 @@ - - IP Address - - - Please enter a valid IPv4/IPv6 address - - - required - - -
- -
\ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/component.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/component.ts deleted file mode 100644 index 8856acba55..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, ChangeDetectorRef, OnDestroy, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { SetupBase } from '../../bases/udp/component'; - -@Component({ - selector: 'app-udp-quicksetup', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class QuickSetup extends SetupBase implements OnDestroy, AfterContentInit { - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public connect() { - this.action.apply(); - } -} -export interface QuickSetup extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/module.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/module.ts deleted file mode 100644 index 5bf02f945f..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/module.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatInputModule } from '@angular/material/input'; - -import { QuickSetup } from './component'; -import { BaseModule } from '../../bases/udp/module'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatInputModule, - BaseModule, - ], - declarations: [QuickSetup], - exports: [QuickSetup] -}) -export class QuickSetupModule {} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/styles.less deleted file mode 100644 index c7c410479d..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/styles.less +++ /dev/null @@ -1,31 +0,0 @@ -@import '../../../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - & mat-form-field { - width: 100%; - } - & mat-form-field.full-width { - display: block; - } - & mat-form-field.inline { - margin-right: 12px; - } - div.mutlicasts{ - margin: 12px 0; - & p { - padding-top: 6px; - } - } - div.controlls{ - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/template.html deleted file mode 100644 index 8fdbe94855..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/quick/udp/template.html +++ /dev/null @@ -1,21 +0,0 @@ - - IP Address - - - Please enter a valid IPv4/IPv6 address - - - required - - -
- -

Multicasts

-
- -
- - -
diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/state.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/state.ts deleted file mode 100644 index dfd8dd4c9a..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/state.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Subject } from '@platform/env/subscription'; -import { Action } from '@ui/tabs/observe/action'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -export class State { - protected readonly history: Map = new Map(); - - public source: Stream.Source; - public updated: Subject = new Subject(); - - constructor( - public readonly action: Action, - public readonly configuration: Stream.Configuration, - ) { - this.source = Stream.getAliasByConfiguration(configuration.configuration); - this.action = action; - } - - public destroy() { - this.updated.destroy(); - } - - public from(configuration: Stream.IConfiguration) { - this.history.set( - this.configuration.instance.alias(), - this.configuration.instance.configuration, - ); - this.configuration.change().byConfiguration(configuration); - this.source = Stream.getAliasByConfiguration(configuration); - this.updated.emit(); - } - - public switch(source: Stream.Source) { - this.history.set( - this.configuration.instance.alias(), - this.configuration.instance.configuration, - ); - this.configuration - .change() - .byDeclaration(Stream.getByAlias(source, this.history.get(source))); - this.source = source; - this.updated.emit(); - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/process.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/process.ts deleted file mode 100644 index c7380b1d99..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/process.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Profile } from '@platform/types/bindings'; -import { bridge } from '@service/bridge'; -import { Destroy } from '@platform/types/env/types'; -import { Action } from '../../../../../action'; - -import * as obj from '@platform/env/obj'; -import * as Stream from '@platform/types/observe/origin/stream/index'; - -const ROOTS_STORAGE_KEY = 'user_selected_profile'; -const ENTRY_KEY = 'selected_profile_path'; - -export class State implements Destroy { - public profiles: { - all: Profile[] | undefined; - valid: Profile[] | undefined; - } = { - all: undefined, - valid: undefined, - }; - // No context envvars - public envvars: Map = new Map(); - public current: Profile | undefined; - - constructor( - public readonly action: Action, - public readonly configuration: Stream.Process.Configuration, - ) {} - - public destroy(): void { - // Having method "destroy()" is requirement of session's storage - } - - public setProfiles(profiles: Profile[]): Promise { - const valid: Profile[] = []; - profiles.forEach((profile) => { - valid.find((p) => p.path === profile.path) === undefined && - profile.envvars !== undefined && - valid.push(profile); - }); - this.profiles.all = profiles; - this.profiles.valid = valid; - return this.storage() - .get() - .then((path: string | undefined) => { - this.current = this.profiles.all?.find((p) => p.path === path); - if (this.current !== undefined && this.current.envvars !== undefined) { - this.configuration.configuration.envs = obj.mapToObj(this.current.envvars); - } - }); - } - - public isProfilesLoaded(): boolean { - return this.profiles.all !== undefined; - } - - public importEnvvarsFromShell(profile: Profile | undefined): Promise { - if (profile === undefined) { - this.current = undefined; - this.configuration.configuration.envs = obj.mapToObj(this.envvars); - return this.storage().set(undefined); - } else { - if (profile.envvars === undefined) { - return Promise.resolve(); - } - this.configuration.configuration.envs = obj.mapToObj(profile.envvars); - this.current = profile; - return this.storage().set(profile.path); - } - } - - public getSelectedEnvs(): Map { - return obj.objToStringMap(this.configuration.configuration.envs); - } - - public isShellSelected(profile: Profile): boolean { - return this.current ? profile.path === this.current.path : false; - } - - protected storage(): { - get(): Promise; - set(path: string | undefined): Promise; - } { - return { - get: (): Promise => { - return new Promise((resolve, reject) => { - bridge - .entries({ key: ROOTS_STORAGE_KEY }) - .get() - .then((entries) => { - resolve(entries.length === 0 ? undefined : entries[0].content); - }) - .catch(reject); - }); - }, - set: (path: string | undefined): Promise => { - if (path === undefined) { - return bridge.entries({ key: ROOTS_STORAGE_KEY }).delete([ENTRY_KEY]); - } else { - return bridge.entries({ key: ROOTS_STORAGE_KEY }).overwrite([ - { - uuid: ENTRY_KEY, - content: path, - }, - ]); - } - }, - }; - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/serial.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/serial.ts deleted file mode 100644 index d902a5254b..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/serial.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { bridge } from '@service/bridge'; -import { scope } from '@platform/env/scope'; -import { Subject } from '@platform/env/subscription'; -import { error } from '@platform/log/utils'; -import { Destroy } from '@platform/types/env/types'; -import { Action } from '../../../../../action'; - -import * as Stream from '@platform/types/observe/origin/stream/index'; - -const SERIAL_PORT_SETTINGS_STORAGE = 'serial_port_settings'; -const REGULAR_RESCAN_PORTS_DURATION_MS = 3000; -const NOPORTS_RESCAN_PORTS_DURATION_MS = 1000; - -const CUSTOM_BAUD_RATE_REF = 'Custom'; -const BAUD_RATE = [ - CUSTOM_BAUD_RATE_REF, - 50, - 75, - 110, - 134, - 150, - 200, - 300, - 600, - 1200, - 1800, - 2400, - 4800, - 9600, - 19200, - 38400, - 57600, - 115200, - 230400, - 460800, - 500000, - 576000, - 921600, - 1000000, - 1152000, - 1500000, - 2000000, - 2500000, - 3000000, - 3500000, - 4000000, -]; -const DATA_BITS: number[] = [8, 7, 6, 5]; -const FLOW_CONTROL = [ - { value: 0, name: 'None' }, - { value: 1, name: 'Hardware' }, - { value: 2, name: 'Software' }, -]; -const PARITY = [ - { value: 0, name: 'None' }, - { value: 1, name: 'Odd' }, - { value: 2, name: 'Even' }, -]; -const STOP_BITS = [1, 2]; -const EXCLUSIVE = [ - { value: true, name: 'Yes (default)' }, - { value: false, name: 'No' }, -]; -const DELAY = [ - { value: 0, name: 'No delay (default)' }, - { value: 10, name: '10 ms' }, - { value: 20, name: '20 ms' }, - { value: 30, name: '30 ms' }, - { value: 40, name: '40 ms' }, - { value: 50, name: '50 ms' }, -]; - -export class State implements Destroy { - public ports: string[] = []; - public changed: Subject = new Subject(); - public baudRateProxy: number | string = 115200; - public loaded: boolean = false; - - public BAUD_RATE = BAUD_RATE; - public DATA_BITS = DATA_BITS; - public FLOW_CONTROL = FLOW_CONTROL; - public PARITY = PARITY; - public STOP_BITS = STOP_BITS; - public EXCLUSIVE = EXCLUSIVE; - public DELAY = DELAY; - - protected timer: number = -1; - protected states: Map = new Map(); - protected prev: string = ''; - - constructor( - public readonly action: Action, - public readonly configuration: Stream.Serial.Configuration, - ) { - this.history().load(); - } - - public destroy(): void { - // Having method "destroy()" is requirement of session's storage - } - - public scan(): { - start(): void; - stop(): void; - } { - const logger = scope.getLogger('SerialPorts Scanner'); - return { - start: (): void => { - function isSame(prev: string[], current: string[]): boolean { - return prev.join(';') === current.join(';'); - } - this.scan().stop(); - bridge - .ports() - .list() - .then((ports: string[]) => { - this.loaded = true; - if (!isSame(this.ports.slice(1), ports)) { - this.ports = ports; - } - this.changed.emit(); - // if (this.ports.includes(this.configuration.configuration.path)) { - // return; - // } - // this.configuration.configuration.path = - // this.ports[0] === undefined ? '' : this.ports[0]; - // this.configuration.configuration.path !== '' && - // this.history().restore(this.configuration.configuration.path); - // this.prev = this.configuration.configuration.path; - // this.changed.emit(); - }) - .catch((err: Error) => { - logger.error(`Fail to update ports list due error: ${err.message}`); - }) - .finally(() => { - this.timer = setTimeout( - () => { - this.scan().start(); - }, - this.ports.length === 0 - ? NOPORTS_RESCAN_PORTS_DURATION_MS - : REGULAR_RESCAN_PORTS_DURATION_MS, - ) as unknown as number; - }); - }, - stop: (): void => { - clearTimeout(this.timer); - }, - }; - } - - public isEmpty(): boolean { - return this.ports.length === 0; - } - - public history(): { - update(path: string): void; - restore(path: string): void; - load(): void; - save(): void; - } { - const logger = scope.getLogger('SerialPorts Settings History'); - return { - update: (path: string): void => { - if (this.prev !== '') { - this.states.set(this.prev, this.configuration.configuration); - } - this.history().restore(path); - this.prev = path; - this.history().save(); - this.changed.emit(); - }, - restore: (path: string): void => { - const state = this.states.get(path); - this.configuration.overwrite( - state === undefined ? Stream.Serial.Configuration.initial() : state, - ); - this.baudRateProxtUpdate(); - this.configuration.configuration.path = path; - }, - load: (): void => { - bridge - .storage(SERIAL_PORT_SETTINGS_STORAGE) - .read() - .then((content: string) => { - if (content === '') { - return; - } - try { - const map = JSON.parse(content); - if (!(map instanceof Array)) { - logger.warn(`Invalid format of history`); - return; - } - this.states.clear(); - map.forEach((pair) => { - if (pair instanceof Array && pair.length === 2) { - this.states.set(pair[0], pair[1]); - } - }); - this.configuration.configuration.path !== '' && - this.history().restore(this.configuration.configuration.path); - } catch (e) { - logger.warn(`Fail to parse history: ${error(e)}`); - } - }) - .catch((err: Error) => { - logger.warn(`Fail to get history: ${err.message}`); - }); - }, - save: (): void => { - const map: [string, Stream.Serial.IConfiguration][] = []; - this.states.forEach((value, key) => { - map.push([key, value]); - }); - bridge - .storage(SERIAL_PORT_SETTINGS_STORAGE) - .write(JSON.stringify(map)) - .catch((err: Error) => { - logger.warn(`Fail to save history: ${err.message}`); - }); - }, - }; - } - - public isBoudRateCustom(): boolean { - return this.baudRateProxy === CUSTOM_BAUD_RATE_REF; - } - - public baudRateChange(): void { - this.configuration.configuration.baud_rate = - typeof this.baudRateProxy === 'string' - ? this.configuration.configuration.baud_rate - : this.baudRateProxy; - this.configuration.configuration.baud_rate = - typeof this.configuration.configuration.baud_rate === 'string' - ? parseInt(this.configuration.configuration.baud_rate, 10) - : this.configuration.configuration.baud_rate; - if ( - isNaN(this.configuration.configuration.baud_rate) || - !isFinite(this.configuration.configuration.baud_rate) - ) { - this.configuration.configuration.baud_rate = - Stream.Serial.Configuration.initial().baud_rate; - } - this.changed.emit(); - } - - public defaluts(): void { - const path = this.configuration.configuration.path; - this.configuration.overwrite(Stream.Serial.Configuration.initial()); - this.configuration.configuration.path = path; - this.states.set(this.configuration.configuration.path, this.configuration.configuration); - this.baudRateProxtUpdate(); - this.history().save(); - this.changed.emit(); - } - - protected baudRateProxtUpdate(): void { - if ( - this.BAUD_RATE.find((r) => r == this.configuration.configuration.baud_rate) === - undefined - ) { - this.baudRateProxy = CUSTOM_BAUD_RATE_REF; - } else { - this.baudRateProxy = this.configuration.configuration.baud_rate; - } - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/tcp.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/tcp.ts deleted file mode 100644 index 133c2bdd33..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/tcp.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Destroy } from '@platform/types/env/types'; -import { Action } from '@ui/tabs/observe/action'; - -import * as Errors from '../bases/tcp/error'; -import * as Stream from '@platform/types/observe/origin/stream/index'; - -export class State implements Destroy { - public errors: { - address: Errors.ErrorState; - }; - - constructor( - public readonly action: Action, - public readonly configuration: Stream.TCP.Configuration, - ) { - this.errors = { - address: new Errors.ErrorState(Errors.Field.address, () => { - // this.update(); - }), - }; - } - - public destroy(): void { - // Having method "destroy()" is requirement of session's storage - } - - public isValid(): boolean { - if (!this.errors.address.isValid()) { - return false; - } - return true; - } - - public drop() { - this.configuration.configuration.bind_addr = Stream.TCP.Configuration.initial().bind_addr; - } - -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/udp.ts b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/udp.ts deleted file mode 100644 index 6aa9fd202f..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/states/udp.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Destroy } from '@platform/types/env/types'; -import { Action } from '../../../../../action'; - -import * as Errors from '../bases/udp/error'; -import * as Stream from '@platform/types/observe/origin/stream/index'; - -const MULTICAST_ADDR = '255.255.255.255'; -const MULTUCAST_INTERFACE = '0.0.0.0'; - -export class State implements Destroy { - public errors: { - address: Errors.ErrorState; - }; - - constructor( - public readonly action: Action, - public readonly configuration: Stream.UDP.Configuration, - ) { - this.errors = { - address: new Errors.ErrorState(Errors.Field.bindingAddress, () => { - // this.update(); - }), - }; - } - - public destroy(): void { - // Having method "destroy()" is requirement of session's storage - } - - public drop() { - this.configuration.configuration.bind_addr = Stream.UDP.Configuration.initial().bind_addr; - this.configuration.configuration.multicast = Stream.UDP.Configuration.initial().multicast; - } - - public addMulticast() { - this.configuration.configuration.multicast.push({ - multiaddr: MULTICAST_ADDR, - interface: MULTUCAST_INTERFACE, - }); - } - - public removeMulticast(index: number) { - index > -1 && this.configuration.configuration.multicast.splice(index, 1); - } -} diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/styles.less b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/styles.less deleted file mode 100644 index 51a34a89c7..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/styles.less +++ /dev/null @@ -1,19 +0,0 @@ -@import '../../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - & mat-form-field.full-width { - display: block; - } - & mat-form-field.inline { - margin-right: 12px; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/template.html b/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/template.html deleted file mode 100644 index effbaf0d68..0000000000 --- a/application/client/src/app/ui/tabs/observe/origin/stream/transport/setup/template.html +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/component.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/component.ts deleted file mode 100644 index a70358b249..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/component.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../state'; - -@Component({ - selector: 'app-el-parser-extra', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class ParserExtraConfiguration extends ChangesDetector implements AfterContentInit { - @Input() state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state.bind(this); - } -} -export interface ParserExtraConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/component.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/component.ts deleted file mode 100644 index eb817b3b75..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/component.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - AfterViewInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { bytesToStr, timestampToUTC } from '@env/str'; -import { State } from './state'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-el-dlt-extra', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class DltExtraConfiguration - extends ChangesDetector - implements AfterContentInit, AfterViewInit -{ - @Input() observe!: Observe; - - protected state!: State; - - public bytesToStr = bytesToStr; - public timestampToUTC = timestampToUTC; - public error: string | undefined; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state = new State(this.observe); - this.state.bind(this); - } - - public ngAfterViewInit(): void { - this.state - .struct() - .load() - .catch((err: Error) => { - this.log().error(`Fail load DLT statistics: ${err.message}`); - this.error = err.message; - this.detectChanges(); - }); - } - - public ngOnEntitySelect() { - this.state.buildSummary().selected(); - this.detectChanges(); - } - - public ngContextMenu(event: MouseEvent) { - const after = () => { - this.state.buildSummary().selected(); - this.detectChanges(); - }; - this.ilc().emitter.ui.contextmenu.open({ - items: [ - { - caption: 'Select all', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => e.select()); - }); - after(); - }, - }, - { - caption: 'Unselect all', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => e.unselect()); - }); - after(); - }, - }, - { - caption: 'Reverse selection', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => e.toggle()); - }); - after(); - }, - }, - {}, - { - caption: 'Select with fotal', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_fatal > 0 && e.select(); - }); - }); - after(); - }, - }, - { - caption: 'Select with errors', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_error > 0 && e.select(); - }); - }); - after(); - }, - }, - { - caption: 'Select with warnings', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_warning > 0 && e.select(); - }); - }); - after(); - }, - }, - {}, - { - caption: 'Unselect without fotal', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_fatal === 0 && e.unselect(); - }); - }); - after(); - }, - }, - { - caption: 'Unselect without errors', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_error === 0 && e.unselect(); - }); - }); - after(); - }, - }, - { - caption: 'Unselect without warnings', - handler: () => { - this.state.structure.forEach((section) => { - section.entities.forEach((e) => { - e.log_warning === 0 && e.unselect(); - }); - }); - after(); - }, - }, - ], - x: event.x, - y: event.y, - }); - } -} -export interface DltExtraConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/module.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/module.ts deleted file mode 100644 index 760ef5b981..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/module.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; -import { MatTableModule } from '@angular/material/table'; -import { MatSortModule } from '@angular/material/sort'; - -import { DltExtraConfiguration } from './component'; -import { DltExtraConfigurationStructure } from './structure/component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, - MatTableModule, - MatSortModule, - ], - declarations: [DltExtraConfiguration, DltExtraConfigurationStructure], - exports: [DltExtraConfiguration], - bootstrap: [DltExtraConfiguration, DltExtraConfigurationStructure], -}) -export class DltExtraConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/state.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/state.ts deleted file mode 100644 index 9067f14986..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/state.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { State as Base } from '../../state'; -import { Section } from './structure/section'; -import { Summary } from './summary'; -import { StatEntity } from './structure/statentity'; -import { getTypedProp } from '@platform/env/obj'; -import { DltStatisticInfo, DltLevelDistribution } from '@platform/types/bindings'; - -import * as Dlt from '@platform/types/observe/parser/dlt'; - -export const ENTITIES = { - app_ids: 'app_ids', - context_ids: 'context_ids', - ecu_ids: 'ecu_ids', -}; - -export const NAMES: { [key: string]: string } = { - [ENTITIES.app_ids]: 'Applications', - [ENTITIES.context_ids]: 'Contexts', - [ENTITIES.ecu_ids]: 'ECUs', -}; -export class State extends Base { - public structure: Section[] = []; - public stat: DltStatisticInfo | undefined; - public summary: { - total: Summary; - selected: Summary; - } = { - total: new Summary(), - selected: new Summary(), - }; - - protected files(): string[] { - const files = this.observe.origin.files(); - if (files === undefined) { - throw new Error( - `Extra settings of DLT parser are available only for File and Concat origins`, - ); - } - return typeof files === 'string' ? [files] : files; - } - - protected filters(): Dlt.IFilters | undefined { - const parser = this.observe.parser.as(Dlt.Configuration); - if (parser === undefined) { - throw new Error(`Observe object uses not ${Dlt.Configuration.alias()} parser.`); - } - return parser.configuration.filter_config; - } - - public isStatLoaded(): boolean { - return this.stat !== undefined; - } - - public getSelectedEntities(): StatEntity[] { - let selected: StatEntity[] = []; - this.structure.forEach((section) => { - selected = selected.concat(section.getSelected()); - }); - return selected; - } - - public struct(): { - load(): Promise; - build(preselection?: Dlt.IFilters): void; - filter(value: string): void; - supported(): boolean; - } { - return { - supported: (): boolean => { - const parser = this.observe.parser.as(Dlt.Configuration); - return parser === undefined ? false : parser.configuration.with_storage_header; - }, - load: (): Promise => { - if (!this.struct().supported()) { - return Promise.resolve(); - } - return this.ref - .ilc() - .services.system.bridge.dlt() - .stat(this.files()) - .then((stat) => { - // this.tab.setTitle( - // this.files.length === 1 - // ? this.files[0].name - // : `${this.files.length} DLT files`, - // ); - this.stat = stat; - this.struct().build(this.filters()); - this.ref.detectChanges(); - }); - }, - build: (preselection?: Dlt.IFilters): void => { - if (!this.struct().supported()) { - return; - } - if (this.stat === undefined) { - return; - } - const stat = this.stat; - const structure: Section[] = []; - ['app_ids', 'context_ids', 'ecu_ids'].forEach((key: string) => { - const content: Array<[string, DltLevelDistribution]> = getTypedProp< - DltStatisticInfo, - Array<[string, DltLevelDistribution]> - >(stat, key); - const entities: StatEntity[] = content.map((record) => { - const entity = new StatEntity(record[0], key, record[1], this.matcher); - if ( - preselection !== undefined && - (preselection as any)[key] !== undefined - ) { - if ( - ((preselection as any)[key] as string[]).indexOf(record[0]) !== -1 - ) { - entity.select(); - } - } - return entity; - }); - structure.push(new Section(key, NAMES[key]).fill(entities)); - }); - this.structure = structure; - this.buildSummary().all(); - }, - filter: (value: string): void => { - if (!this.struct().supported()) { - return; - } - this.matcher.search(value); - this.structure.forEach((structure) => { - structure.entities.sort( - (a: StatEntity, b: StatEntity) => b.getScore() - a.getScore(), - ); - structure.update.emit(); - }); - }, - }; - } - - public buildSummary(): { - total(): void; - selected(): void; - all(): void; - } { - return { - total: (): void => { - this.summary.total.reset(); - this.structure.forEach((structure) => { - structure.entities.forEach((entity) => { - this.summary.total.inc(entity); - }); - }); - }, - selected: (): void => { - this.summary.selected.reset(); - this.structure.forEach((structure) => { - structure.entities.forEach((entity) => { - entity.selected && this.summary.selected.inc(entity); - }); - }); - const conf = this.observe.parser.as(Dlt.Configuration); - if (conf === undefined) { - return; - } - conf.setDefaultsFilterConfig(); - let app_id_count = 0; - let context_id_count = 0; - this.structure.forEach((structure) => { - if (structure.key === ENTITIES.app_ids) { - app_id_count = structure.entities.length; - } else if (structure.key === ENTITIES.context_ids) { - context_id_count = structure.entities.length; - } - const selected = structure.getSelected().map((f) => f.id); - if (selected.length === 0) { - (conf.configuration.filter_config as any)[structure.key] = undefined; - } else { - (conf.configuration.filter_config as any)[structure.key] = selected; - } - }); - if (conf.configuration.filter_config) { - conf.configuration.filter_config.app_id_count = app_id_count; - conf.configuration.filter_config.context_id_count = context_id_count; - } - }, - all: (): void => { - this.buildSummary().total(); - this.buildSummary().selected(); - }, - }; - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/component.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/component.ts deleted file mode 100644 index a958c96ce1..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/component.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { - Component, - ViewChild, - ChangeDetectorRef, - AfterViewInit, - AfterContentInit, - Input, - Output, - EventEmitter, - ViewEncapsulation, -} from '@angular/core'; -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { MatSort } from '@angular/material/sort'; -import { MatTableDataSource } from '@angular/material/table'; -import { StatEntity } from './statentity'; -import { Section } from './section'; -import { MatTable } from '@angular/material/table'; - -export const COLUMNS = { - id: 'id', - non_log: 'non_log', - log_fatal: 'log_fatal', - log_error: 'log_error', - log_warning: 'log_warning', - log_info: 'log_info', - log_debug: 'log_debug', - log_verbose: 'log_verbose', - log_invalid: 'log_invalid', -}; - -@Component({ - selector: 'app-el-dlt-extra-structure', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - encapsulation: ViewEncapsulation.None, - standalone: false, -}) -@Ilc() -export class DltExtraConfigurationStructure - extends ChangesDetector - implements AfterContentInit, AfterViewInit -{ - columns: string[] = [ - COLUMNS.id, - COLUMNS.log_fatal, - COLUMNS.log_error, - COLUMNS.log_warning, - COLUMNS.log_debug, - COLUMNS.log_info, - COLUMNS.log_verbose, - COLUMNS.non_log, - ]; - - @Input() section!: Section; - @Output() select: EventEmitter = new EventEmitter(); - - @ViewChild(MatSort) sort!: MatSort; - @ViewChild(MatTable) table!: MatTable; - - public data!: MatTableDataSource; - - constructor(cdRef: ChangeDetectorRef, private _sanitizer: DomSanitizer) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.data = new MatTableDataSource(this.section.entities); - this.env().subscriber.register( - this.section.update.subscribe(() => { - this.data.data = this.section.entities.filter((e: StatEntity) => !e.hidden()); - this.table.renderRows(); - this.detectChanges(); - }), - ); - } - - public ngAfterViewInit(): void { - this.data.sort = this.sort; - } - - public ngOnSortChange() { - this.detectChanges(); - this.table.renderRows(); - } - - public ngOnRowSelect(entity: StatEntity) { - entity.toggle(); - this.select.emit(entity); - this.detectChanges(); - } - - public safeHtml(html: string): SafeHtml { - return this._sanitizer.bypassSecurityTrustHtml(html); - } -} -export interface DltExtraConfigurationStructure extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/section.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/section.ts deleted file mode 100644 index 46180a6ae2..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/section.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Subject } from '@platform/env/subscription'; -import { StatEntity } from './statentity'; - -export class Section { - public key: string; - public name: string; - public update: Subject = new Subject(); - public entities: StatEntity[] = []; - - constructor(key: string, name: string) { - this.key = key; - this.name = name; - } - - public getSelected(): StatEntity[] { - return this.entities.filter((f) => f.selected); - } - - public fill(entities: StatEntity[]): Section { - this.entities = entities; - return this; - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/statentity.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/statentity.ts deleted file mode 100644 index fb2961ffc2..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/statentity.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { DltLevelDistribution } from '@platform/types/bindings'; -import { Matchee } from '@module/matcher'; - -import * as wasm from '@loader/wasm'; - -export class StatEntity extends Matchee { - public selected: boolean = false; - public id: string; - public parent: string; - public non_log: number; - public log_fatal: number; - public log_error: number; - public log_warning: number; - public log_info: number; - public log_debug: number; - public log_verbose: number; - public log_invalid: number; - - constructor(id: string, parent: string, from: DltLevelDistribution, matcher: wasm.Matcher) { - super(matcher, { id: id }); - this.id = id; - this.parent = parent; - this.non_log = from.non_log; - this.log_fatal = from.log_fatal; - this.log_error = from.log_error; - this.log_warning = from.log_warning; - this.log_info = from.log_info; - this.log_debug = from.log_debug; - this.log_verbose = from.log_verbose; - this.log_invalid = from.log_invalid; - } - - public html(): string { - const html: string | undefined = this.getHtmlOf('html_id'); - return html === undefined ? this.id : html; - } - - public hash(): string { - return `${this.parent}-${this.id}`; - } - - public equal(entity: StatEntity): boolean { - return entity.hash() === this.hash(); - } - - public toggle() { - this.selected = !this.selected; - } - - public select() { - this.selected = true; - } - - public unselect() { - this.selected = false; - } - - public hidden(): boolean { - return this.getScore() === 0; - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/styles.less b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/styles.less deleted file mode 100644 index 9c0dd96e72..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/styles.less +++ /dev/null @@ -1,40 +0,0 @@ -@import '../../../../../../styles/variables.less'; - - -app-el-dlt-extra-structure { - position: relative; - display: block; - width: 100%; - & p.title { - margin: 8px 0 0px 16px; - color: var(--scheme-color-2); - font-size: 14px; - font-weight: 500; - } - & tr{ - position: relative; - &:hover { - background-color: var(--scheme-color-4)!important; - } - } - & tr[dataselected="true"] { - background: var(--scheme-color-active); - &::after { - position: absolute; - display: block; - content: ''; - width: 4px; - height: 100%; - background: var(--scheme-color-accent); - left: 0; - } - &:hover { - background-color: var(--scheme-color-active-75)!important; - } - } - & span.id{ - & > span { - background-color: var(--scheme-color-3); - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html deleted file mode 100644 index 311f57ed30..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/structure/template.html +++ /dev/null @@ -1,68 +0,0 @@ -

{{section.name}}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ID - - - - FATAL - {{element.log_fatal}} - ERROR - {{element.log_error}} - WARN - {{element.log_warning}} - DEBUG - {{element.log_debug}} - INFO - {{element.log_info}} - VERB - {{element.log_verbose}} - NON - {{element.non_log}}
diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/styles.less b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/styles.less deleted file mode 100644 index e813d7e1d2..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/styles.less +++ /dev/null @@ -1,116 +0,0 @@ -@import '../../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & p.error { - color: var(--scheme-color-error-light); - text-align: center; - padding: 64px 0; - } - & app-transport { - margin-top: 12px; - } - & div.controlls { - text-align: right; - } - & h1 { - font-size: 14px; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - & div.left { - position: absolute; - display: flex; - flex-direction: column; - top: 0; - bottom: 0; - left: 0; - right: 330px; - overflow: hidden; - justify-content: stretch; - justify-items: stretch; - & div.filter { - position: absolute; - z-index: 1000; - padding: 6px 12px; - background: var(--scheme-color-warning-light); - top: 6px; - right: 6px; - box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); - border-radius: 3px; - } - & .fill { - overflow: auto; - } - & .section { - padding-top: 12px; - & mat-divider { - margin-top: -12px; - } - & .mat-standard-chip{ - min-height: 24px; - } - } - } - & div.right { - position: absolute; - width: 320px; - right: 6px; - bottom: 0; - top: 0; - overflow-y: auto; - overflow-x: hidden; - display: flex; - flex-direction: column; - & mat-divider.fibex-divider { - margin-top: 12px; - } - & div.fibex-controlls{ - position: relative; - width: 100%; - margin-top: 19px; - text-align: right; - } - & div.spacer { - flex: auto; - } - & app-tabs-source-actions { - position: sticky; - bottom: 0; - z-index: 1; - box-shadow: 0px 0px 16px rgba(0,0,0,0.4); - } - } - & .table-fill { - width: 100%; - } - & .info { - & .caption { - text-align: left; - padding-right: 12px; - } - & .value { - text-align: left; - } - } - - & div.progress { - margin-top: 24px; - & p { - text-align: center; - } - } - & .mat-progress-bar-fill::after { - background-color: var(--scheme-color-accent); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/summary.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/summary.ts deleted file mode 100644 index 194eabc187..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/summary.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { StatEntity } from './structure/statentity'; - -export class Summary { - public fatal: number = 0; - public error: number = 0; - public warning: number = 0; - public info: number = 0; - public debug: number = 0; - public verbose: number = 0; - public invalid: number = 0; - public total: number = 0; - public count: number = 0; - - private _loaded: boolean = false; - - public isLoaded(): boolean { - return this._loaded; - } - - public inc(entity: StatEntity) { - this.fatal += entity.log_fatal; - this.error += entity.log_error; - this.warning += entity.log_warning; - this.info += entity.log_info; - this.debug += entity.log_debug; - this.verbose += entity.log_verbose; - this.invalid += entity.log_invalid; - this.total += - entity.log_fatal + - entity.log_error + - entity.log_warning + - entity.log_info + - entity.log_debug + - entity.log_verbose + - entity.log_invalid; - this.count += 1; - } - - public reset() { - this._loaded = true; - this.fatal = 0; - this.error = 0; - this.warning = 0; - this.info = 0; - this.debug = 0; - this.verbose = 0; - this.invalid = 0; - this.total = 0; - this.count = 0; - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/template.html b/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/template.html deleted file mode 100644 index 122c91d51d..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/dlt/template.html +++ /dev/null @@ -1,67 +0,0 @@ - - -

- Cannot load statistics information because of an error: {{error}} -

- -
- -

Scanning DLT structure...

-
-
-

- DLT structure scanning isn't supported for this type of source -

-
-
- -

- Summary ({{state.summary.total.count}} / {{state.summary.selected.count}}) -

- - - - - - - - - - - - - - - - - - - - - - - -
Fatal - {{state.summary.total.fatal}}
{{state.summary.selected.fatal}} -
Error - {{state.summary.total.error}}
{{state.summary.selected.error}} -
Debug - {{state.summary.total.debug}}
{{state.summary.selected.debug}} -
Info - {{state.summary.total.info}}
{{state.summary.selected.info}} -
Verbose - {{state.summary.total.verbose}}
{{state.summary.selected.verbose}} -
Invalid - {{state.summary.total.invalid}}
{{state.summary.selected.invalid}} -
Total - {{state.summary.total.total}} / {{state.summary.selected.total}} -
-
- -
-
diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/module.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/module.ts deleted file mode 100644 index 311c764732..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { DltExtraConfigurationModule } from './dlt/module'; -import { SomeIpExtraConfigurationModule } from './someip/module'; -import { ParserExtraConfiguration } from './component'; -@NgModule({ - imports: [CommonModule, DltExtraConfigurationModule, SomeIpExtraConfigurationModule], - declarations: [ParserExtraConfiguration], - exports: [ - ParserExtraConfiguration, - DltExtraConfigurationModule, - SomeIpExtraConfigurationModule, - ], - bootstrap: [ParserExtraConfiguration], -}) -export class ParserExtraConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/component.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/someip/component.ts deleted file mode 100644 index b3419cc4e7..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from './state'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-el-someip-extra', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class SomeIpExtraConfiguration extends ChangesDetector implements AfterContentInit { - @Input() observe!: Observe; - - protected state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state = new State(this.observe); - this.state.bind(this); - } -} -export interface SomeIpExtraConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/module.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/someip/module.ts deleted file mode 100644 index c02e703ca4..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; - -import { SomeIpExtraConfiguration } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, - ], - declarations: [SomeIpExtraConfiguration], - exports: [SomeIpExtraConfiguration], - bootstrap: [SomeIpExtraConfiguration], -}) -export class SomeIpExtraConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/state.ts b/application/client/src/app/ui/tabs/observe/parsers/extra/someip/state.ts deleted file mode 100644 index 4579f844c7..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/state.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { State as Base } from '../../state'; - -export class State extends Base {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/styles.less b/application/client/src/app/ui/tabs/observe/parsers/extra/someip/styles.less deleted file mode 100644 index 4bd77884a2..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/styles.less +++ /dev/null @@ -1,111 +0,0 @@ -@import '../../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & app-transport { - margin-top: 12px; - } - & div.controlls { - text-align: right; - } - & h1 { - font-size: 14px; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - & div.left { - position: absolute; - display: flex; - flex-direction: column; - top: 0; - bottom: 0; - left: 0; - right: 330px; - overflow: hidden; - justify-content: stretch; - justify-items: stretch; - & div.filter { - position: absolute; - z-index: 1000; - padding: 6px 12px; - background: var(--scheme-color-warning-light); - top: 6px; - right: 6px; - box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); - border-radius: 3px; - } - & .fill { - overflow: auto; - } - & .section { - padding-top: 12px; - & mat-divider { - margin-top: -12px; - } - & .mat-standard-chip{ - min-height: 24px; - } - } - } - & div.right { - position: absolute; - width: 320px; - right: 6px; - bottom: 0; - top: 0; - overflow-y: auto; - overflow-x: hidden; - display: flex; - flex-direction: column; - & mat-divider.fibex-divider { - margin-top: 12px; - } - & div.fibex-controlls{ - position: relative; - width: 100%; - margin-top: 19px; - text-align: right; - } - & div.spacer { - flex: auto; - } - & app-tabs-source-actions { - position: sticky; - bottom: 0; - z-index: 1; - box-shadow: 0px 0px 16px rgba(0,0,0,0.4); - } - } - & .table-fill { - width: 100%; - } - & .info { - & .caption { - text-align: left; - padding-right: 12px; - } - & .value { - text-align: left; - } - } - - & div.progress { - margin-top: 24px; - & p { - text-align: center; - } - } - & .mat-progress-bar-fill::after { - background-color: var(--scheme-color-accent); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/template.html b/application/client/src/app/ui/tabs/observe/parsers/extra/someip/template.html deleted file mode 100644 index 139597f9cb..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/someip/template.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/styles.less b/application/client/src/app/ui/tabs/observe/parsers/extra/styles.less deleted file mode 100644 index be92722fb8..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/styles.less +++ /dev/null @@ -1,111 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & app-transport { - margin-top: 12px; - } - & div.controlls { - text-align: right; - } - & h1 { - font-size: 14px; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - & div.left { - position: absolute; - display: flex; - flex-direction: column; - top: 0; - bottom: 0; - left: 0; - right: 330px; - overflow: hidden; - justify-content: stretch; - justify-items: stretch; - & div.filter { - position: absolute; - z-index: 1000; - padding: 6px 12px; - background: var(--scheme-color-warning-light); - top: 6px; - right: 6px; - box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); - border-radius: 3px; - } - & .fill { - overflow: auto; - } - & .section { - padding-top: 12px; - & mat-divider { - margin-top: -12px; - } - & .mat-standard-chip{ - min-height: 24px; - } - } - } - & div.right { - position: absolute; - width: 320px; - right: 6px; - bottom: 0; - top: 0; - overflow-y: auto; - overflow-x: hidden; - display: flex; - flex-direction: column; - & mat-divider.fibex-divider { - margin-top: 12px; - } - & div.fibex-controlls{ - position: relative; - width: 100%; - margin-top: 19px; - text-align: right; - } - & div.spacer { - flex: auto; - } - & app-tabs-source-actions { - position: sticky; - bottom: 0; - z-index: 1; - box-shadow: 0px 0px 16px rgba(0,0,0,0.4); - } - } - & .table-fill { - width: 100%; - } - & .info { - & .caption { - text-align: left; - padding-right: 12px; - } - & .value { - text-align: left; - } - } - - & div.progress { - margin-top: 24px; - & p { - text-align: center; - } - } - & .mat-progress-bar-fill::after { - background-color: var(--scheme-color-accent); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/extra/template.html b/application/client/src/app/ui/tabs/observe/parsers/extra/template.html deleted file mode 100644 index ea14b75d8d..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/extra/template.html +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/component.ts b/application/client/src/app/ui/tabs/observe/parsers/general/component.ts deleted file mode 100644 index 71e961f332..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { State } from '../state'; -import { State as GlobalState } from '../../state'; - -@Component({ - selector: 'app-el-parser-general', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class ParserGeneralConfiguration extends ChangesDetector implements AfterContentInit { - @Input() state!: State; - @Input() global!: GlobalState; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state.bind(this); - } -} -export interface ParserGeneralConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/component.ts b/application/client/src/app/ui/tabs/observe/parsers/general/dlt/component.ts deleted file mode 100644 index 2c97a1320c..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/component.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - Component, - ChangeDetectorRef, - Input, - AfterContentInit, - AfterViewInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { bytesToStr, timestampToUTC } from '@env/str'; -import { State } from './state'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-el-dlt-general', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class DltGeneralConfiguration - extends ChangesDetector - implements AfterContentInit, AfterViewInit -{ - @Input() observe!: Observe; - - protected state!: State; - - public bytesToStr = bytesToStr; - public timestampToUTC = timestampToUTC; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state = new State(this.observe); - this.state.bind(this); - } - - public ngAfterViewInit(): void { - this.state - .load() - .then(() => { - this.detectChanges(); - }) - .catch((err: Error) => { - this.log().error(`Fail to restore configuration with: ${err.message}`); - }); - } -} -export interface DltGeneralConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/module.ts b/application/client/src/app/ui/tabs/observe/parsers/general/dlt/module.ts deleted file mode 100644 index c308db8a67..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; - -import { DltGeneralConfiguration } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, - ], - declarations: [DltGeneralConfiguration], - exports: [DltGeneralConfiguration], - bootstrap: [DltGeneralConfiguration], -}) -export class DltGeneralConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/state.ts b/application/client/src/app/ui/tabs/observe/parsers/general/dlt/state.ts deleted file mode 100644 index 56e340b9aa..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/state.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { File } from '@platform/types/files'; -import { Timezone } from '@elements/timezones/timezone'; -import { bridge } from '@service/bridge'; -import { components } from '@env/decorators/initial'; -import { State as Base } from '../../state'; -import { Observe } from '@platform/types/observe'; - -import * as Dlt from '@platform/types/observe/parser/dlt'; - -export class State extends Base { - public readonly LOG_LEVELS: { value: Dlt.LogLevel; caption: string }[] = [ - { value: Dlt.LogLevel.Fatal, caption: 'Fatal' }, - { value: Dlt.LogLevel.Error, caption: 'Error' }, - { value: Dlt.LogLevel.Warn, caption: 'Warn' }, - { value: Dlt.LogLevel.Info, caption: 'Info' }, - { value: Dlt.LogLevel.Debug, caption: 'Debug' }, - { value: Dlt.LogLevel.Verbose, caption: 'Verbose' }, - ]; - public fibex: File[] = []; - public timezone: Timezone | undefined; - public logLevel: Dlt.LogLevel = Dlt.LogLevel.Verbose; - - constructor(observe: Observe) { - super(observe); - } - - public async load(): Promise { - const conf = this.observe.parser.as(Dlt.Configuration); - if (conf === undefined) { - return; - } - const stored: string[] | string | undefined = conf.configuration.fibex_file_paths; - const paths: string[] = - stored === undefined - ? [] - : typeof stored === 'string' - ? [stored] - : stored.map((p) => p); - this.fibex = await bridge.files().getByPath(paths); - conf.configuration.filter_config !== undefined && - conf.configuration.filter_config.min_log_level !== undefined && - (this.logLevel = conf.configuration.filter_config.min_log_level); - const timezone = - conf.configuration.tz !== undefined ? Timezone.from(conf.configuration.tz) : undefined; - this.timezone = timezone instanceof Error ? undefined : timezone; - } - - public update(): State { - const conf = this.observe.parser.as(Dlt.Configuration); - if (conf === undefined) { - return this; - } - if (this.fibex.length !== 0) { - conf.configuration.fibex_file_paths = this.fibex.map((f) => f.filename); - } else { - conf.configuration.fibex_file_paths = undefined; - } - if (this.logLevel !== Dlt.LogLevel.Verbose) { - conf.setDefaultsFilterConfig(); - conf.configuration.filter_config!.min_log_level = this.logLevel; - } - conf.configuration.tz = this.timezone === undefined ? undefined : this.timezone.name; - return this; - } - - public addFibexFile() { - bridge - .files() - .select.custom('xml') - .then((files: File[]) => { - files = files.filter((added) => { - return ( - this.fibex.find((exist) => exist.filename === added.filename) === undefined - ); - }); - this.fibex = this.fibex.concat(files); - }) - .catch((err: Error) => { - this.ref.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); - }) - .finally(() => { - this.update().ref.detectChanges(); - }); - } - - public removeFibex(file: File) { - this.fibex = this.fibex.filter((f) => f.filename !== file.filename); - this.update().ref.detectChanges(); - } - - public timezoneSelect() { - const subscription = this.ref - .ilc() - .services.ui.popup.open({ - component: { - factory: components.get('app-elements-timezone-selector'), - inputs: { - selected: (timezone: Timezone): void => { - if (timezone.name.toLowerCase().startsWith('utc')) { - this.timezone = undefined; - } else { - this.timezone = timezone; - } - this.update(); - this.ref.detectChanges(); - }, - }, - }, - closeOnKey: 'Escape', - width: 350, - uuid: 'app-elements-timezone-selector', - }) - .subjects.get() - .closed.subscribe(() => { - subscription.unsubscribe(); - }); - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/styles.less b/application/client/src/app/ui/tabs/observe/parsers/general/dlt/styles.less deleted file mode 100644 index d28051537c..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/styles.less +++ /dev/null @@ -1,17 +0,0 @@ -@import '../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - & div.controlls { - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/template.html b/application/client/src/app/ui/tabs/observe/parsers/general/dlt/template.html deleted file mode 100644 index 89e1ea8ab8..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/dlt/template.html +++ /dev/null @@ -1,48 +0,0 @@ - - Log level - - - Select required level of logs - - - {{level.caption}} - - - - - - - Fibex - Attach fibex file (optional) - -
- - - {{file.name}} - - - -
- -
- -
-
-
- - Timezone - Select required timezone (optional). UTC is default - - - {{state.timezone.name}} ({{state.timezone.utc}}) - -
- -
-
-
diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/module.ts b/application/client/src/app/ui/tabs/observe/parsers/general/module.ts deleted file mode 100644 index 740831ef04..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/module.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { ErrorStateModule } from '../../error/module'; -import { DltGeneralConfigurationModule } from './dlt/module'; -import { SomeIpGeneralConfigurationModule } from './someip/module'; -import { TextGeneralConfigurationModule } from './text/module'; -import { ParserPluginGeneralConfigurationModule } from './plugin/module'; -import { ParserGeneralConfiguration } from './component'; - -@NgModule({ - imports: [ - CommonModule, - DltGeneralConfigurationModule, - SomeIpGeneralConfigurationModule, - TextGeneralConfigurationModule, - ParserPluginGeneralConfigurationModule, - ErrorStateModule, - ], - declarations: [ParserGeneralConfiguration], - exports: [ - ParserGeneralConfiguration, - DltGeneralConfigurationModule, - SomeIpGeneralConfigurationModule, - TextGeneralConfigurationModule, - ParserPluginGeneralConfigurationModule, - ErrorStateModule, - ], - bootstrap: [ParserGeneralConfiguration], -}) -export class ParserGeneralConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/component.ts b/application/client/src/app/ui/tabs/observe/parsers/general/plugin/component.ts deleted file mode 100644 index e7192c6a2b..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/component.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - Component, - Input, - ChangeDetectorRef, - AfterContentInit, - AfterViewInit, -} from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Observe } from '@platform/types/observe'; -import { State } from './state'; - -@Component({ - selector: 'app-el-parser-plugin-general', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class ParserPluginGeneralConfiguration - extends ChangesDetector - implements AfterContentInit, AfterViewInit -{ - @Input() observe!: Observe; - @Input() path!: string | undefined; - - protected state!: State; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - ngAfterContentInit(): void { - this.state = new State(this.observe, this.path); - this.state.bind(this); - this.env().subscriber.register( - this.observe.parser.subscribe(() => { - if (!this.path) { - return; - } - this.state.setPath(this.path); - }), - ); - } - - public update(): void { - this.state.update(); - } - - ngAfterViewInit(): void { - this.state.init(); - this.detectChanges(); - } -} - -export interface ParserPluginGeneralConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/module.ts b/application/client/src/app/ui/tabs/observe/parsers/general/plugin/module.ts deleted file mode 100644 index 12bf08c02f..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; - -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatInputModule } from '@angular/material/input'; -import { ConfigSchmasModule } from '@ui/tabs/observe/config-schema/module'; -import { ParserPluginGeneralConfiguration } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatChipsModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, - ConfigSchmasModule, - MatCheckboxModule, - MatInputModule, - ], - declarations: [ParserPluginGeneralConfiguration], - exports: [ParserPluginGeneralConfiguration], - bootstrap: [ParserPluginGeneralConfiguration], -}) -export class ParserPluginGeneralConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/state.ts b/application/client/src/app/ui/tabs/observe/parsers/general/plugin/state.ts deleted file mode 100644 index 7e7cf45f7b..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/state.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { State as Base } from '../../state'; -import { Observe } from '@platform/types/observe'; -import { plugins } from '@service/plugins'; -import { getSafeFileName } from '@platform/types/files'; -import { Subject } from '@platform/env/subscription'; - -import * as Plugin from '@platform/types/observe/parser/plugin'; -import { - PluginEntity, - PluginConfigSchemaItem, - PluginConfigValue, -} from '@platform/types/bindings/plugins'; - -export class State extends Base { - public parsers: PluginEntity[] = []; - public path: string | undefined; - public selectedParser?: PluginEntity; - public selected: Subject = new Subject(); - - constructor(observe: Observe, path: string | undefined) { - super(observe); - this.path = path; - } - - public init() { - const conf = this.observe.parser.as(Plugin.Configuration); - if (conf === undefined) { - this.ref.log().error(`Currnet parser configuration must match plugin parser`); - return; - } - this.parsers = plugins - .list() - .preload() - .filter((p) => p.plugin_type === 'Parser'); - - if (this.parsers.length > 0) { - this.selectedParser = this.path - ? this.parsers.find((p) => p.dir_path == this.path) - : this.parsers[0]; - this.update(); - } - } - - public setPath(path: string) { - this.path = path; - if (this.parsers.length > 0) { - this.selectedParser = this.path - ? this.parsers.find((p) => p.dir_path == this.path) - : this.parsers[0]; - this.update(); - } - } - - /** - * Saves the given value to the tab configurations - * @param id - ID of the configuration entry - * @param value - Value of the configuration - */ - public saveConfig(id: string, value: PluginConfigValue) { - const conf = this.observe.parser.as(Plugin.Configuration); - if (conf === undefined) { - this.ref.log().error(`Currnet parser configuration must match plugin parser`); - return; - } - - const item = conf.configuration.plugin_configs.find((item) => item.id === id); - if (item !== undefined) { - item.value = value; - } else { - conf.configuration.plugin_configs.push({ id, value }); - } - } - - /** - * Updates the configuration data for the parser in the current tab - */ - public update() { - const conf = this.observe.parser.as(Plugin.Configuration); - if (conf === undefined) { - return; - } - - const pluginPath = this.selectedParser?.info.wasm_file_path ?? ''; - - if (conf.configuration.plugin_path !== pluginPath) { - conf.configuration.plugin_path = pluginPath; - // Clear configurations on plugin change. - conf.configuration.plugin_configs = []; - } - this.selectedParser && this.selected.emit(this.selectedParser); - } - - public getPluginTitle(parser: PluginEntity): string { - return parser.metadata?.title ?? getSafeFileName(parser.dir_path); - } - - public getPluginConfigs(parser?: PluginEntity): PluginConfigSchemaItem[] { - return parser?.info.config_schemas ?? []; - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/styles.less b/application/client/src/app/ui/tabs/observe/parsers/general/plugin/styles.less deleted file mode 100644 index 04f4051d5b..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/styles.less +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/template.html b/application/client/src/app/ui/tabs/observe/parsers/general/plugin/template.html deleted file mode 100644 index e689845ed8..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/plugin/template.html +++ /dev/null @@ -1,6 +0,0 @@ - - Configurations - - - - diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/someip/component.ts b/application/client/src/app/ui/tabs/observe/parsers/general/someip/component.ts deleted file mode 100644 index c446a49095..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/someip/component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, ChangeDetectorRef, Input, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { bytesToStr } from '@env/str'; -import { State } from './state'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-el-someip-general', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class SomeIpGeneralConfiguration extends ChangesDetector implements AfterContentInit { - @Input() observe!: Observe; - - protected state!: State; - - public bytesToStr = bytesToStr; - - constructor(cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.state = new State(this.observe); - this.state.bind(this); - } -} -export interface SomeIpGeneralConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/someip/module.ts b/application/client/src/app/ui/tabs/observe/parsers/general/someip/module.ts deleted file mode 100644 index c7400a6791..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/someip/module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MatButtonModule } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatChipsModule } from '@angular/material/chips'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { MatIconModule } from '@angular/material/icon'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatSelectModule } from '@angular/material/select'; -import { MatListModule } from '@angular/material/list'; - -import { SomeIpGeneralConfiguration } from './component'; - -@NgModule({ - imports: [ - CommonModule, - FormsModule, - ReactiveFormsModule, - MatButtonModule, - MatCardModule, - MatDividerModule, - MatProgressBarModule, - MatChipsModule, - MatIconModule, - MatFormFieldModule, - MatSelectModule, - MatListModule, - ], - declarations: [SomeIpGeneralConfiguration], - exports: [SomeIpGeneralConfiguration], - bootstrap: [SomeIpGeneralConfiguration], -}) -export class SomeIpGeneralConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/someip/state.ts b/application/client/src/app/ui/tabs/observe/parsers/general/someip/state.ts deleted file mode 100644 index 28a593ef44..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/someip/state.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { File } from '@platform/types/files'; -import { bridge } from '@service/bridge'; -import { State as Base } from '../../state'; - -import * as SomeIp from '@platform/types/observe/parser/someip'; - -export class State extends Base { - public fibex: File[] = []; - - public update(): State { - const conf = this.observe.parser.as(SomeIp.Configuration); - if (conf === undefined) { - return this; - } - if (this.fibex.length !== 0) { - conf.configuration.fibex_file_paths = this.fibex.map((f) => f.filename); - } else { - conf.configuration.fibex_file_paths = undefined; - } - return this; - } - - public addFibexFile() { - bridge - .files() - .select.custom('xml') - .then((files: File[]) => { - files = files.filter((added) => { - return ( - this.fibex.find((exist) => exist.filename === added.filename) === undefined - ); - }); - this.fibex = this.fibex.concat(files); - }) - .catch((err: Error) => { - this.ref.log().error(`Fail to open xml (fibex) file(s): ${err.message}`); - }) - .finally(() => { - this.update().ref.detectChanges(); - }); - } - - public removeFibex(file: File) { - this.fibex = this.fibex.filter((f) => f.filename !== file.filename); - this.update().ref.detectChanges(); - } -} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/someip/styles.less b/application/client/src/app/ui/tabs/observe/parsers/general/someip/styles.less deleted file mode 100644 index 684961bca9..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/someip/styles.less +++ /dev/null @@ -1,18 +0,0 @@ -@import '../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; - & div.controlls { - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/someip/template.html b/application/client/src/app/ui/tabs/observe/parsers/general/someip/template.html deleted file mode 100644 index b25ae24971..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/someip/template.html +++ /dev/null @@ -1,24 +0,0 @@ - - Fibex - Attach fibex file (optional) - -
- - - {{file.name}} - - - -
- -
- -
-
-
- diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/styles.less b/application/client/src/app/ui/tabs/observe/parsers/general/styles.less deleted file mode 100644 index be92722fb8..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/styles.less +++ /dev/null @@ -1,111 +0,0 @@ -@import '../../../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & app-transport { - margin-top: 12px; - } - & div.controlls { - text-align: right; - } - & h1 { - font-size: 14px; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } - & div.left { - position: absolute; - display: flex; - flex-direction: column; - top: 0; - bottom: 0; - left: 0; - right: 330px; - overflow: hidden; - justify-content: stretch; - justify-items: stretch; - & div.filter { - position: absolute; - z-index: 1000; - padding: 6px 12px; - background: var(--scheme-color-warning-light); - top: 6px; - right: 6px; - box-shadow: 3px 3px 3px rgb(0 0 0 / 40%); - border-radius: 3px; - } - & .fill { - overflow: auto; - } - & .section { - padding-top: 12px; - & mat-divider { - margin-top: -12px; - } - & .mat-standard-chip{ - min-height: 24px; - } - } - } - & div.right { - position: absolute; - width: 320px; - right: 6px; - bottom: 0; - top: 0; - overflow-y: auto; - overflow-x: hidden; - display: flex; - flex-direction: column; - & mat-divider.fibex-divider { - margin-top: 12px; - } - & div.fibex-controlls{ - position: relative; - width: 100%; - margin-top: 19px; - text-align: right; - } - & div.spacer { - flex: auto; - } - & app-tabs-source-actions { - position: sticky; - bottom: 0; - z-index: 1; - box-shadow: 0px 0px 16px rgba(0,0,0,0.4); - } - } - & .table-fill { - width: 100%; - } - & .info { - & .caption { - text-align: left; - padding-right: 12px; - } - & .value { - text-align: left; - } - } - - & div.progress { - margin-top: 24px; - & p { - text-align: center; - } - } - & .mat-progress-bar-fill::after { - background-color: var(--scheme-color-accent); - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/template.html b/application/client/src/app/ui/tabs/observe/parsers/general/template.html deleted file mode 100644 index 41493effc4..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/template.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/text/component.ts b/application/client/src/app/ui/tabs/observe/parsers/general/text/component.ts deleted file mode 100644 index 5b989063ad..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/text/component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { Observe } from '@platform/types/observe'; - -@Component({ - selector: 'app-el-text-general', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class TextGeneralConfiguration { - @Input() observe!: Observe; -} -export interface TextGeneralConfiguration extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/text/module.ts b/application/client/src/app/ui/tabs/observe/parsers/general/text/module.ts deleted file mode 100644 index ed5b0df2df..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/text/module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; - -import { TextGeneralConfiguration } from './component'; - -@NgModule({ - imports: [CommonModule, MatCardModule, MatDividerModule], - declarations: [TextGeneralConfiguration], - exports: [TextGeneralConfiguration], - bootstrap: [TextGeneralConfiguration], -}) -export class TextGeneralConfigurationModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/text/styles.less b/application/client/src/app/ui/tabs/observe/parsers/general/text/styles.less deleted file mode 100644 index 2276da0bdd..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/text/styles.less +++ /dev/null @@ -1,9 +0,0 @@ -@import '../../../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; - overflow: hidden; -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/parsers/general/text/template.html b/application/client/src/app/ui/tabs/observe/parsers/general/text/template.html deleted file mode 100644 index de35a517dc..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/general/text/template.html +++ /dev/null @@ -1,7 +0,0 @@ - - Plain text parser - -

This parser doesn't require any kind of configuration

-
-
- diff --git a/application/client/src/app/ui/tabs/observe/parsers/module.ts b/application/client/src/app/ui/tabs/observe/parsers/module.ts deleted file mode 100644 index 79fc31786b..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ParserGeneralConfigurationModule } from './general/module'; -import { ParserExtraConfigurationModule } from './extra/module'; - -@NgModule({ - imports: [CommonModule, ParserGeneralConfigurationModule, ParserExtraConfigurationModule], - declarations: [], - exports: [], - bootstrap: [], -}) -export class ParsersModule {} diff --git a/application/client/src/app/ui/tabs/observe/parsers/state.ts b/application/client/src/app/ui/tabs/observe/parsers/state.ts deleted file mode 100644 index afd24b19cc..0000000000 --- a/application/client/src/app/ui/tabs/observe/parsers/state.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Observe } from '@platform/types/observe'; -import { Holder } from '@module/matcher'; - -export class State extends Holder { - protected ref!: IlcInterface & ChangesDetector; - - constructor(public readonly observe: Observe) { - super(); - } - - public bind(ref: IlcInterface & ChangesDetector) { - this.ref = ref; - } -} diff --git a/application/client/src/app/ui/tabs/observe/state.ts b/application/client/src/app/ui/tabs/observe/state.ts deleted file mode 100644 index a79f5e0841..0000000000 --- a/application/client/src/app/ui/tabs/observe/state.ts +++ /dev/null @@ -1,347 +0,0 @@ -import { IList, Observe, Parser } from '@platform/types/observe'; -import { IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Subjects, Subject, Subscriber } from '@platform/env/subscription'; -import { File } from '@platform/types/files'; -import { bytesToStr } from '@env/str'; -import { Action } from './action'; -import { TabControls } from '@service/session'; -import { Notification } from '@ui/service/notifications'; -import { Locker, Level } from '@ui/service/lockers'; -import { PluginEntity } from '@platform/types/bindings'; - -import * as StreamOrigin from '@platform/types/observe/origin/stream/index'; -import * as Origin from '@platform/types/observe/origin/index'; -import * as FileOrigin from '@platform/types/observe/origin/file'; -import * as ConcatOrigin from '@platform/types/observe/origin/concat'; -import * as Parsers from '@platform/types/observe/parser'; -import * as Streams from '@platform/types/observe/origin/stream/index'; -import { plugins } from '@service/plugins'; - -export interface IApi { - finish(observe: Observe): Promise; - cancel(): void; - tab(): TabControls; -} - -export interface IInputs { - observe: Observe; - api: IApi; -} - -export class WrappedParserRef { - constructor(protected readonly inner: Parser.Reference | PluginEntity) {} - - private as_embedded(): Parser.Reference | undefined { - if (typeof (this.inner as any)['alias'] === 'function') { - return this.inner as Parser.Reference; - } - return undefined; - } - - private as_plugin(): PluginEntity | undefined { - if (typeof (this.inner as any)['dir_path'] === 'string') { - return this.inner as PluginEntity; - } - return undefined; - } - - public desc(): IList { - const embedded = this.as_embedded(); - if (embedded !== undefined) { - return embedded.desc(); - } - const plugin = this.as_plugin(); - if (plugin !== undefined) { - return { - major: plugin.metadata.title, - minor: plugin.metadata.description - ? plugin.metadata.description - : plugin.metadata.title, - icon: undefined, - }; - } - throw new Error(`Fail to process ${JSON.stringify(this.inner)}`); - } - public alias(): Parser.Protocol | string { - const embedded = this.as_embedded(); - if (embedded !== undefined) { - return embedded.alias(); - } - const plugin = this.as_plugin(); - if (plugin !== undefined) { - return plugin.dir_path; - } - throw new Error(`Fail to process ${JSON.stringify(this.inner)}`); - } -} - -export class State extends Subscriber { - public parser: Parser.Protocol | string | undefined; - public parsers: { ref: WrappedParserRef; disabled: boolean }[] = []; - public streams: { ref: StreamOrigin.Reference; disabled: boolean }[] = []; - public file: File | undefined; - public concat: File[] | undefined; - public stream: StreamOrigin.Source | undefined; - public size: string | undefined; - public action: Action = new Action(); - public updates: Subjects<{ - parser: Subject; - stream: Subject; - }> = new Subjects({ - parser: new Subject(), - stream: new Subject(), - }); - - constructor( - protected readonly ref: IlcInterface & ChangesDetector & { api: IApi }, - public readonly observe: Observe, - ) { - super(); - this.update().stream(); - this.update().files(); - this.update().parser(); - this.update().validate(); - this.update().action(); - this.register( - this.action.subjects.get().updated.subscribe(() => { - this.ref.markChangesForCheck(); - }), - this.action.subjects.get().apply.subscribe(() => { - this.finish(); - }), - this.observe.subscribe(() => { - this.update().validate(); - this.update().action(); - }), - ); - } - - public destroy() { - this.updates.destroy(); - this.unsubscribe(); - } - - public finish() { - if (this.action.disabled) { - return; - } - this.action.applied(); - this.ref.api.finish(this.observe).catch((err: Error) => { - this.ref - .ilc() - .services.ui.lockers.lock( - new Locker(true, err.message).set().type(Level.error).spinner(false).end(), - { - closable: true, - closeOnKey: 'Escape', - closeOnBGClick: true, - }, - ); - this.ref.ilc().services.ui.notifications.store( - new Notification({ - message: err.message, - actions: [], - }), - ); - }); - } - - public cancel() { - this.ref.api.cancel(); - } - - public getParser(): { - embedded(): Parser.Protocol | undefined; - pluginPath(): string | undefined; - } { - return { - embedded: (): Parser.Protocol | undefined => { - if (!this.parser) { - return undefined; - } - const embedded = Parser.tryAsEmbedded(this.parser); - return embedded ? embedded : undefined; - }, - pluginPath: (): string | undefined => { - if (!this.parser) { - return undefined; - } - return Parser.tryAsEmbedded(this.parser) ? undefined : (this.parser as string); - }, - }; - } - - public update(): { - stream(): void; - files(): void; - parser(): void; - validate(): void; - action(): void; - } { - return { - stream: (): void => { - const prev = this.stream; - const nature = this.observe.origin.nature(); - if ( - nature instanceof Origin.File.Configuration || - nature instanceof Origin.Concat.Configuration || - nature instanceof Origin.Plugin.Configuration - ) { - this.streams = []; - this.stream = undefined; - } else { - this.streams = this.observe.parser.getSupportedStream().map((ref) => { - return { ref, disabled: false }; - }); - if (this.stream === undefined) { - this.stream = nature.alias(); - } else { - const current = this.stream; - this.stream = - current !== undefined && - this.streams.find((p) => p.ref.alias() === current) !== undefined - ? current - : this.streams[0].ref.alias(); - } - this.streams.push( - ...Streams.getAllRefs() - .filter( - (ref) => - this.streams.find((p) => p.ref.alias() === ref.alias()) === - undefined, - ) - .map((ref) => { - return { ref, disabled: true }; - }), - ); - } - this.ref.markChangesForCheck(); - prev !== this.stream && this.updates.get().stream.emit(); - }, - files: (): void => { - const instance = this.observe.origin.instance; - const files: string[] | undefined = - instance instanceof FileOrigin.Configuration - ? [instance.filename()] - : instance instanceof ConcatOrigin.Configuration - ? instance.files() - : undefined; - if (files === undefined) { - return; - } - this.ref - .ilc() - .services.system.bridge.files() - .getByPath(files) - .then((files: File[]) => { - this.size = bytesToStr( - files - .map((f) => f.stat.size) - .reduce((partialSum, a) => partialSum + a, 0), - ); - if (instance instanceof FileOrigin.Configuration) { - if (files.length !== 1) { - this.ref - .log() - .error( - `Expecting only 1 file stats. Has been gotten: ${files.length}`, - ); - return; - } - this.file = files[0]; - } else if (instance instanceof ConcatOrigin.Configuration) { - this.concat = files; - } - }) - .catch((err: Error) => { - this.ref - .log() - .error( - `Fail to get stats for files: ${files.join(', ')}: ${err.message}`, - ); - }) - .finally(() => { - this.ref.markChangesForCheck(); - }); - }, - parser: (): void => { - const current = this.parser; - this.parsers = [ - ...this.observe.origin - .getSupportedParsers() - .filter((ref) => ref.alias() !== Parser.Protocol.Plugin) - .map((ref) => { - return { ref: new WrappedParserRef(ref), disabled: false }; - }), - ...plugins - .list() - .preload() - .filter((ref) => ref.plugin_type === 'Parser') - .map((ref) => { - return { ref: new WrappedParserRef(ref), disabled: false }; - }), - ]; - this.parser = - current !== undefined && - this.parsers.find((p) => p.ref.alias() === current) !== undefined - ? current - : this.parsers[0].ref.alias(); - this.parsers.push( - ...Parsers.getAllRefs() - .filter( - (ref) => - this.parsers.find((p) => p.ref.alias() === ref.alias()) === - undefined && ref.inited(), - ) - .map((ref) => { - return { ref: new WrappedParserRef(ref), disabled: true }; - }), - ); - this.ref.markChangesForCheck(); - current !== this.parser && this.updates.get().parser.emit(); - }, - validate: (): void => { - const error = this.observe.validate(); - this.action.setDisabled(error instanceof Error); - }, - action: (): void => { - this.action.setCaption(this.observe.origin.desc().action); - }, - }; - } - - public change(): { - stream(): void; - parser(): void; - } { - return { - stream: (): void => { - if (this.stream === undefined) { - this.ref.log().error(`Stream cannot be changed, because it's undefined`); - return; - } - const instance = this.observe.origin.instance; - if (!(instance instanceof Origin.Stream.Configuration)) { - this.ref.log().error(`Stream cannot be changed, because origin isn't Stream`); - return; - } - instance.change(StreamOrigin.getByAlias(this.stream)); - this.updates.get().stream.emit(); - this.update().parser(); - }, - parser: (): void => { - if (this.parser === undefined) { - this.ref.log().error(`Parser cannot be changed, because it's undefined`); - return; - } - const alias = Parser.tryAsEmbedded(this.parser); - this.observe.parser.change( - Parser.getByAlias(alias ? alias : Parser.Protocol.Plugin), - ); - this.updates.get().parser.emit(); - this.update().stream(); - }, - }; - } -} diff --git a/application/client/src/app/ui/tabs/observe/styles.less b/application/client/src/app/ui/tabs/observe/styles.less deleted file mode 100644 index 7b24c33a16..0000000000 --- a/application/client/src/app/ui/tabs/observe/styles.less +++ /dev/null @@ -1,74 +0,0 @@ -@import '../../styles/variables.less'; - - -:host { - position: absolute; - display: block; - top: 0; - left: 0; - width: 100%; - height: 100%; - overflow: hidden; - & div.preset-container { - top: 48px; - bottom: 0px; - height: auto; - } - & div.title { - position: absolute; - display: flex; - flex-direction: row; - align-items: center; - left:0; - right: 0; - padding: 0 8px; - background: var(--scheme-color-5); - box-shadow: 0 3px 3px rgba(0,0,0,0.4); - height: 48px; - overflow: hidden; - & *.entity { - display: flex; - flex-direction: row; - align-items: center; - left:0; - right: 0; - padding: 0 8px; - background: var(--scheme-color-5); - height: 48px; - &.filler { - flex: auto; - width: 100%; - margin: 0; - justify-content: end; - } - &.info { - color: var(--scheme-color-2); - white-space: nowrap; - align-items: center; - flex: auto; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - & span { - margin-left: 8px; - } - } - & button { - position: relative; - display: inline-block; - margin: 0 16px 0 0; - margin-bottom: 0!important; - &:last-child { - margin-right: 0; - } - } - & p.info { - color: var(--scheme-color-2); - white-space: nowrap; - } - } - & > * { - margin: 0 8px; - } - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/tabs/observe/template.html b/application/client/src/app/ui/tabs/observe/template.html deleted file mode 100644 index 7400d0a98a..0000000000 --- a/application/client/src/app/ui/tabs/observe/template.html +++ /dev/null @@ -1,35 +0,0 @@ -
-

{{state.file.name}}({{state.size}})

-

Concating:{{state.concat.length}}files({{state.size}})

-
-

Stream From:

- - - - {{stream.ref.desc().major}} - - - -
-
-

Used Parser:

- - - - {{parser.ref.desc().major}} - - - -
-
- - -
-
-
-
- - - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/element/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/component.ts deleted file mode 100644 index 16207193d0..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Component, Input, HostListener } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Element } from './element'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-views-observed-list-item', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item { - public readonly Context = $.Origin.Context; - public readonly Source = $.Origin.Stream.Stream.Source; - - @Input() element!: Element; - - @HostListener('click') onClick() { - this.element.select(); - } - - @HostListener('contextmenu', ['$event']) async onContextMenu(event: MouseEvent) { - const items = this.element.provider.contextMenu(this.element.source); - if (items.length > 0) { - items.push({}); - } - items.push({ - caption: 'Reopen in New Tab', - handler: () => { - this.element.provider.openAsNew(this.element.source).catch((err: Error) => { - this.log().error(`Fail to open Source: ${err.message}`); - }); - }, - }); - this.ilc().emitter.ui.contextmenu.open({ - items, - x: event.x, - y: event.y, - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/element.ts b/application/client/src/app/ui/views/sidebar/observe/element/element.ts deleted file mode 100644 index bb840c3e24..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/element.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ObserveSource } from '@service/session/dependencies/observing/source'; -import { File } from '@platform/types/files'; -import { Mutable } from '@platform/types/unity/mutable'; -import { Provider } from '@service/session/dependencies/observing/provider'; - -import * as $ from '@platform/types/observe'; - -export class Element { - public readonly source: ObserveSource; - public readonly provider: Provider; - public readonly id: number | undefined; - public readonly file: File | undefined; - public selected: boolean = false; - - constructor(source: ObserveSource, provider: Provider) { - this.source = source; - this.provider = provider; - const session = this.provider.session; - const sourceId = this.source.observe.origin.source(); - this.id = - sourceId !== undefined ? session.stream.observe().descriptions.id(sourceId) : undefined; - this.selected = session.stream.sde.selecting().is(this.source.observe.uuid); - } - - public select(): void { - const sde = this.provider.session.stream.sde; - this.selected = sde.selecting().select(this.source.observe.uuid); - } - - public set(): { file(file: File): Element } { - return { - file: (file: File): Element => { - (this as Mutable).file = file; - return this; - }, - }; - } - - public nature(): $.Origin.Context | $.Origin.Stream.Stream.Source { - return this.source.observe.origin.getNatureAlias(); - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/file/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/file/component.ts deleted file mode 100644 index 9866b90ae6..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/file/component.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { File } from '@platform/types/files'; -import { Element } from '../element'; -import { Mutable } from '@platform/types/unity/mutable'; -import { stop } from '@ui/env/dom'; - -@Component({ - selector: 'app-views-observed-file', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - - public readonly file: File | undefined; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - if (this.element.file === undefined) { - this.log().error(`Field "file" in Element is undefined`); - return; - } - (this as Mutable).file = this.element.file; - } - - public isActive(): boolean { - return this.element.source.observer !== undefined; - } - - public openAsNew(event: MouseEvent): void { - stop(event); - this.element.provider.openAsNew(this.element.source).catch((err: Error) => { - this.log().error(`Fail to restart file: ${err.message}`); - }); - } - - public stop(event: MouseEvent): void { - stop(event); - const observer = this.element.source.observer; - if (observer === undefined) { - return; - } - observer.abort().catch((err: Error) => { - this.log().error(`Fail to abort file tailing: ${err.message}`); - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/file/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/file/styles.less deleted file mode 100644 index fa223d7b34..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/file/styles.less +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: 100%; - height: 32px; - overflow: hidden; - & span.ext { - display: block; - position: relative; - font-size: 14px; - height: 18px; - line-height: 14px; - font-weight: 700; - margin-right: 12px; - margin-left: 6px; - color: var(--scheme-color-3); - &.active { - color: var(--scheme-color-accent); - } - } - & div.info { - overflow: hidden; - flex: auto; - text-align: left; - & span { - display: block; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - line-height: 0.9rem; - white-space: nowrap; - } - & span.name { - color: var(--scheme-color-1); - margin-bottom: 3px; - } - & span.path { - color: var(--scheme-color-3); - } - } - & div.controlls { - margin: 0 24px 0 12px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/file/template.html b/application/client/src/app/ui/views/sidebar/observe/element/file/template.html deleted file mode 100644 index c45d03c0c1..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/file/template.html +++ /dev/null @@ -1,14 +0,0 @@ - - No data about file - - - {{file.ext}} -
- {{file.name}} - {{file.path}} -
-
- - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/element/module.ts b/application/client/src/app/ui/views/sidebar/observe/element/module.ts deleted file mode 100644 index 9741b26fcd..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Item } from './component'; -import { Item as FileItem } from './file/component'; -import { Item as ProcessItem } from './process/component'; -import { Item as SerialItem } from './serial/component'; -import { Item as TCPItem } from './tcp/component'; -import { Item as UDPItem } from './udp/component'; -import { Signature } from './signature/component'; - -@NgModule({ - imports: [CommonModule], - declarations: [Item, FileItem, ProcessItem, SerialItem, TCPItem, UDPItem, Signature], - exports: [Item], -}) -export class ElementModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/process/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/process/component.ts deleted file mode 100644 index 1402a070f2..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/process/component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Element } from '../element'; -import { Mutable } from '@platform/types/unity/mutable'; -import { stop } from '@ui/env/dom'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-views-observed-process', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - - public readonly connection: $.Origin.Stream.Stream.Process.IConfiguration | undefined; - public readonly selected!: boolean; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const conf = - this.element.source.observe.origin.as<$.Origin.Stream.Stream.Process.Configuration>( - $.Origin.Stream.Stream.Process.Configuration, - ); - if (conf === undefined) { - this.log().error(`Expected origin Source would be Process`); - return; - } - (this as Mutable).connection = conf.configuration; - } - - public isActive(): boolean { - return this.element.source.observer !== undefined; - } - - public restart(event: MouseEvent): void { - stop(event); - this.element.provider.clone(this.element.source.observe).catch((err: Error) => { - this.log().error(`Fail to restart process: ${err.message}`); - }); - } - - public stop(event: MouseEvent): void { - stop(event); - const observer = this.element.source.observer; - if (observer === undefined) { - return; - } - observer.abort().catch((err: Error) => { - this.log().error(`Fail to abort process: ${err.message}`); - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/process/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/process/styles.less deleted file mode 100644 index 404ed07c50..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/process/styles.less +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: 100%; - height: 32px; - overflow: hidden; - & span.ext { - display: block; - position: relative; - margin-right: 12px; - margin-left: 6px; - line-height: 10px; - & span.codicon { - color: var(--scheme-color-3); - font-weight: 700; - &.active { - color: var(--scheme-color-accent); - } - } - } - & div.info { - overflow: hidden; - flex: auto; - text-align: left; - & span { - display: block; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - line-height: 0.9rem; - white-space: nowrap; - } - & span.name { - color: var(--scheme-color-1); - margin-bottom: 3px; - } - & span.path { - color: var(--scheme-color-3); - } - } - & div.controlls { - margin: 0 24px 0 12px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/process/template.html b/application/client/src/app/ui/views/sidebar/observe/element/process/template.html deleted file mode 100644 index 5b9f49cce3..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/process/template.html +++ /dev/null @@ -1,16 +0,0 @@ - - No data about process - - - - - -
- {{connection.command}} - {{connection.cwd}} -
-
- - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/element/serial/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/serial/component.ts deleted file mode 100644 index 36caabd1d7..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/serial/component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Element } from '../element'; -import { Mutable } from '@platform/types/unity/mutable'; -import { stop } from '@ui/env/dom'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-views-observed-serial', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - - public readonly connection: $.Origin.Stream.Stream.Serial.IConfiguration | undefined; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const conf = - this.element.source.observe.origin.as<$.Origin.Stream.Stream.Serial.Configuration>( - $.Origin.Stream.Stream.Serial.Configuration, - ); - if (conf === undefined) { - this.log().error(`Expected origin Source would be TCP`); - return; - } - (this as Mutable).connection = conf.configuration; - } - - public isActive(): boolean { - return this.element.source.observer !== undefined; - } - - public restart(event: MouseEvent): void { - stop(event); - this.element.provider.clone(this.element.source.observe).catch((err: Error) => { - this.log().error(`Fail to restart Serial connection: ${err.message}`); - }); - } - - public stop(event: MouseEvent): void { - stop(event); - const observer = this.element.source.observer; - if (observer === undefined) { - return; - } - observer.abort().catch((err: Error) => { - this.log().error(`Fail to abort Serial connection: ${err.message}`); - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/serial/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/serial/styles.less deleted file mode 100644 index 404ed07c50..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/serial/styles.less +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: 100%; - height: 32px; - overflow: hidden; - & span.ext { - display: block; - position: relative; - margin-right: 12px; - margin-left: 6px; - line-height: 10px; - & span.codicon { - color: var(--scheme-color-3); - font-weight: 700; - &.active { - color: var(--scheme-color-accent); - } - } - } - & div.info { - overflow: hidden; - flex: auto; - text-align: left; - & span { - display: block; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - line-height: 0.9rem; - white-space: nowrap; - } - & span.name { - color: var(--scheme-color-1); - margin-bottom: 3px; - } - & span.path { - color: var(--scheme-color-3); - } - } - & div.controlls { - margin: 0 24px 0 12px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/serial/template.html b/application/client/src/app/ui/views/sidebar/observe/element/serial/template.html deleted file mode 100644 index 276c1b3d0d..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/serial/template.html +++ /dev/null @@ -1,16 +0,0 @@ - - No data about serial connection - - - - - -
- {{connection.path}} - boud rate: {{connection.baud_rate.toString()}} -
-
- - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/element/signature/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/signature/component.ts deleted file mode 100644 index 5ba82829e5..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/signature/component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { getSourceColor } from '@ui/styles/colors'; -import { Element } from '../element'; - -@Component({ - selector: 'app-views-observed-list-item-signature', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Signature extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - @Input() id!: number | undefined; - - public selected!: boolean; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - this.selected = this.element.selected; - } - - public getSourceMarkerStyles(): { [key: string]: string } { - return this.id === undefined - ? {} - : { - background: getSourceColor(this.id), - }; - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/signature/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/signature/styles.less deleted file mode 100644 index d377d303dc..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/signature/styles.less +++ /dev/null @@ -1,26 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0 6px 0 0 ; - height: 32px; - & span.color { - position: relative; - display: inline-block; - width: 4px; - height: 100%; - } - & span.selected { - position: absolute; - display: block; - width: 8px; - height: 8px; - border-radius: 4px; - top: 50%; - margin-top: -4px; - left: -17px; - background-color: var(--scheme-color-accent); - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/signature/template.html b/application/client/src/app/ui/views/sidebar/observe/element/signature/template.html deleted file mode 100644 index 99e32df210..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/signature/template.html +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/element/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/styles.less deleted file mode 100644 index 77d20f99e9..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/styles.less +++ /dev/null @@ -1,18 +0,0 @@ -@import '../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: ~"calc(100% - 24px)"; - height: 32px; - overflow: hidden; - padding: 3px 24px; - cursor: default; - &:hover { - background: var(--scheme-color-5); - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/tcp/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/tcp/component.ts deleted file mode 100644 index 5c9d951056..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/tcp/component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Element } from '../element'; -import { Mutable } from '@platform/types/unity/mutable'; -import { stop } from '@ui/env/dom'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-views-observed-tcp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - - public readonly connection: $.Origin.Stream.Stream.TCP.IConfiguration | undefined; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const conf = - this.element.source.observe.origin.as<$.Origin.Stream.Stream.TCP.Configuration>( - $.Origin.Stream.Stream.TCP.Configuration, - ); - if (conf === undefined) { - this.log().error(`Expected origin Source would be TCP`); - return; - } - (this as Mutable).connection = conf.configuration; - } - - public isActive(): boolean { - return this.element.source.observer !== undefined; - } - - public restart(event: MouseEvent): void { - stop(event); - this.element.provider.clone(this.element.source.observe).catch((err: Error) => { - this.log().error(`Fail to restart TCP connection: ${err.message}`); - }); - } - - public stop(event: MouseEvent): void { - stop(event); - const observer = this.element.source.observer; - if (observer === undefined) { - return; - } - observer.abort().catch((err: Error) => { - this.log().error(`Fail to abort TCP connection: ${err.message}`); - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/tcp/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/tcp/styles.less deleted file mode 100644 index fa223d7b34..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/tcp/styles.less +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: 100%; - height: 32px; - overflow: hidden; - & span.ext { - display: block; - position: relative; - font-size: 14px; - height: 18px; - line-height: 14px; - font-weight: 700; - margin-right: 12px; - margin-left: 6px; - color: var(--scheme-color-3); - &.active { - color: var(--scheme-color-accent); - } - } - & div.info { - overflow: hidden; - flex: auto; - text-align: left; - & span { - display: block; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - line-height: 0.9rem; - white-space: nowrap; - } - & span.name { - color: var(--scheme-color-1); - margin-bottom: 3px; - } - & span.path { - color: var(--scheme-color-3); - } - } - & div.controlls { - margin: 0 24px 0 12px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/tcp/template.html b/application/client/src/app/ui/views/sidebar/observe/element/tcp/template.html deleted file mode 100644 index 64f1441f91..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/tcp/template.html +++ /dev/null @@ -1,13 +0,0 @@ - - No data about TCP connection - - - TCP -
- {{connection.bind_addr}} -
-
- - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/element/template.html b/application/client/src/app/ui/views/sidebar/observe/element/template.html deleted file mode 100644 index d12c7bdce8..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/template.html +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/element/udp/component.ts b/application/client/src/app/ui/views/sidebar/observe/element/udp/component.ts deleted file mode 100644 index 15a56bc047..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/udp/component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Component, Input, ChangeDetectorRef, ElementRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { ChangesDetector } from '@ui/env/extentions/changes'; -import { Element } from '../element'; -import { Mutable } from '@platform/types/unity/mutable'; -import { stop } from '@ui/env/dom'; - -import * as $ from '@platform/types/observe'; - -@Component({ - selector: 'app-views-observed-udp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Item extends ChangesDetector implements AfterContentInit { - @Input() element!: Element; - - public readonly connection: $.Origin.Stream.Stream.UDP.IConfiguration | undefined; - - constructor(cdRef: ChangeDetectorRef, private _self: ElementRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const conf = - this.element.source.observe.origin.as<$.Origin.Stream.Stream.UDP.Configuration>( - $.Origin.Stream.Stream.UDP.Configuration, - ); - if (conf === undefined) { - this.log().error(`Expected origin Source would be UDP`); - return; - } - (this as Mutable).connection = conf.configuration; - } - - public isActive(): boolean { - return this.element.source.observer !== undefined; - } - - public restart(event: MouseEvent): void { - stop(event); - this.element.provider.clone(this.element.source.observe).catch((err: Error) => { - this.log().error(`Fail to restart UDP connection: ${err.message}`); - }); - } - - public stop(event: MouseEvent): void { - stop(event); - const observer = this.element.source.observer; - if (observer === undefined) { - return; - } - observer.abort().catch((err: Error) => { - this.log().error(`Fail to abort UDP connection: ${err.message}`); - }); - } -} -export interface Item extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/udp/styles.less b/application/client/src/app/ui/views/sidebar/observe/element/udp/styles.less deleted file mode 100644 index ab053647ac..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/udp/styles.less +++ /dev/null @@ -1,51 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0; - margin: 0; - flex-direction: row; - align-items: center; - width: 100%; - height: 32px; - overflow: hidden; - & span.ext { - display: block; - position: relative; - margin-left: 6px; - font-size: 14px; - height: 18px; - line-height: 14px; - font-weight: 700; - margin-right: 12px; - color: var(--scheme-color-3); - &.active { - color: var(--scheme-color-accent); - } - } - & div.info { - overflow: hidden; - flex: auto; - text-align: left; - & span { - display: block; - position: relative; - overflow: hidden; - text-overflow: ellipsis; - direction: rtl; - line-height: 0.9rem; - white-space: nowrap; - } - & span.name { - color: var(--scheme-color-1); - margin-bottom: 3px; - } - & span.path { - color: var(--scheme-color-3); - } - } - & div.controlls { - margin: 0 24px 0 12px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/element/udp/template.html b/application/client/src/app/ui/views/sidebar/observe/element/udp/template.html deleted file mode 100644 index ace0ad5458..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/element/udp/template.html +++ /dev/null @@ -1,14 +0,0 @@ - - No data about UDP connection - - - UDP -
- {{connection.bind_addr}} - {{connection.multicast[0].multiaddr}} -
-
- - -
-
diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/component.ts deleted file mode 100644 index c85f754be9..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - Component, - Input, - OnDestroy, - Inject, - AfterContentInit, - ChangeDetectorRef, -} from '@angular/core'; -import { Provider as ProviderBase } from '@service/session/dependencies/observing/provider'; -import { Mutable } from '@platform/types/unity/mutable'; -import { Base } from '../states/state'; -import { ChangesDetector } from '@ui/env/extentions/changes'; - -@Component({ - selector: 'app-observe-list-base', - template: '', - standalone: false, -}) -export class ListBase - extends ChangesDetector - implements OnDestroy, AfterContentInit -{ - public readonly state!: State; - - @Input() public provider!: Provider; - - constructor(@Inject('defaults') protected readonly defaults: State, cdRef: ChangeDetectorRef) { - super(cdRef); - } - - public ngAfterContentInit(): void { - const state = this.provider.session.storage.get(this.defaults.key()); - (this as Mutable>).state = - state === undefined ? this.defaults : state; - } - - public ngOnDestroy(): void { - this.provider.session.storage.set(this.defaults.key(), this.state); - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts deleted file mode 100644 index e73bfa3047..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/file/component.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Element } from '../../element/element'; -import { File } from '@platform/types/files'; -import { State } from '../../states/files'; -import { IButton } from '../../common/title/component'; -import { ListBase } from '../component'; -import { Provider } from '@service/session/dependencies/observing/implementations/files'; -import { ObserveSource } from '@service/session/dependencies/observing/source'; - -import * as $ from '@platform/types/observe'; -import * as Factory from '@platform/types/observe/factory'; - -@Component({ - selector: 'app-views-observed-list-file', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class List extends ListBase implements AfterContentInit { - public tailing: Element[] = []; - public offline: Element[] = []; - public buttons: IButton[] = [ - { - icon: 'codicon-tasklist', - handler: () => { - this.provider.recent(); - }, - }, - ]; - public warning: string | undefined = undefined; - - protected inited: boolean = false; - - constructor(cdRef: ChangeDetectorRef) { - super(new State(), cdRef); - } - - public override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.update(); - this.env().subscriber.register( - this.provider.subjects.get().updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - } - - protected update(): List { - const filterFileNature = (s: ObserveSource): boolean => { - return s.observe.origin.nature().alias() === $.Origin.Context.File; - }; - const filterAsFile = (s: ObserveSource): $.Origin.File.Configuration => { - return s.observe.origin.as<$.Origin.File.Configuration>( - $.Origin.File.Configuration, - ) as $.Origin.File.Configuration; - }; - const asFileInstance = (s: ObserveSource): $.Origin.File.Configuration => { - return s.observe.origin.as<$.Origin.File.Configuration>( - $.Origin.File.Configuration, - ) as $.Origin.File.Configuration; - }; - const tailing = this.provider - .sources() - .filter((s) => s.observer !== undefined) - .filter(filterFileNature); - const offline = this.provider - .sources() - .filter((s) => s.observer === undefined) - .filter(filterFileNature); - const attachNewSourceErr = this.provider.getNewSourceError(); - this.warning = attachNewSourceErr instanceof Error ? attachNewSourceErr.message : undefined; - this.ilc() - .services.system.bridge.files() - .getByPathWithCache([ - ...tailing.map(filterAsFile).map((i) => i.filename()), - ...offline.map(filterAsFile).map((i) => i.filename()), - ]) - .then((files: File[]) => { - this.tailing = tailing - .map((s) => { - const filename = asFileInstance(s).filename(); - const file = files.find((f) => f.filename === filename); - if (file === undefined) { - this.log().error( - `Fail to find a file ${filename} in cache; or get file metadata`, - ); - } - return file === undefined - ? null - : new Element(s, this.provider).set().file(file); - }) - .filter((i) => i !== null) as Element[]; - this.offline = offline - .map((s) => { - const filename = asFileInstance(s).filename(); - const file = files.find((f) => f.filename === filename); - if (file === undefined) { - this.log().error( - `Fail to find a file ${filename} in cache; or get file metadata`, - ); - } - return file === undefined - ? null - : new Element(s, this.provider).set().file(file); - }) - .filter((i) => i !== null) as Element[]; - }) - .catch((err: Error) => { - this.log().error(`Fail load stat of files: ${err.message}`); - }) - .finally(() => { - this.detectChanges(); - }); - return this; - } - - public toggled(opened: boolean) { - this.state.toggleQuickSetup(opened); - } - - public attach() { - const last = this.provider.last(); - if (last === undefined) { - return; - } - const lastFile = last.origin.as<$.Origin.File.Configuration>($.Origin.File.Configuration); - if (lastFile === undefined) { - return; - } - console.error(`Not implemented`); - // this.ilc() - // .services.system.bridge.files() - // .select.any() - // .then((files: File[]) => { - // if (files.length === 0) { - // return; - // } - // this.provider.session.stream - // .observe() - // .start( - // files.length === 1 - // ? new Factory.File() - // .type(lastFile.configuration[1]) - // .file(files[0].filename) - // .protocol(last.parser.instance.alias()) - // .get() - // : new Factory.Concat() - // .type(lastFile.configuration[1]) - // .files(files.map((f) => f.filename)) - // .protocol(last.parser.instance.alias()) - // .get(), - // ) - // .catch((err: Error) => { - // this.log().error(`Fail to observe: ${err.message}`); - // }); - // }) - // .catch((err: Error) => { - // this.log().error(`Fail to select file(s): ${err.message}`); - // }); - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/file/module.ts b/application/client/src/app/ui/views/sidebar/observe/lists/file/module.ts deleted file mode 100644 index 0965ac7086..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/file/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { List } from './component'; -import { ElementModule } from '../../element/module'; -import { CommonObserveModule } from '../../common/module'; -import { MatButtonModule } from '@angular/material/button'; - -@NgModule({ - imports: [CommonModule, ElementModule, CommonObserveModule, MatButtonModule], - declarations: [List], - exports: [List], -}) -export class ListModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/file/styles.less b/application/client/src/app/ui/views/sidebar/observe/lists/file/styles.less deleted file mode 100644 index b4d727cfca..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/file/styles.less +++ /dev/null @@ -1,23 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0; - p.subtitle{ - color: var(--scheme-color-3); - text-align: right; - } - div.quicksetup { - padding: 0 24px 24px 24px; - } - div.controlls{ - text-align: right; - } - & button { - position: relative; - display: inline-block; - margin: 6px 0 6px 6px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/file/template.html b/application/client/src/app/ui/views/sidebar/observe/lists/file/template.html deleted file mode 100644 index 898c3a222c..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/file/template.html +++ /dev/null @@ -1,15 +0,0 @@ - -
-

{{warning}}

-
- -
-
-
- - -
-
- - -
\ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/process/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/process/component.ts deleted file mode 100644 index 8bcdcc303c..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/process/component.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit, ViewChild } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Element } from '../../element/element'; -import { Action } from '@ui/tabs/observe/action'; -import { QuickSetup } from '@tabs/observe/origin/stream/transport/setup/quick/process/component'; -import { IButton } from '../../common/title/component'; -import { State } from '../../states/process'; -import { ListBase } from '../component'; -import { Provider } from '@service/session/dependencies/observing/implementations/processes'; -import { Configuration } from '@platform/types/observe/origin/stream/process'; -import { notifications, Notification } from '@ui/service/notifications'; - -import * as Factroy from '@platform/types/observe/factory'; - -@Component({ - selector: 'app-views-observed-list-process', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class List extends ListBase implements AfterContentInit { - @ViewChild('quicksetupref') public quickSetupRef!: QuickSetup; - - public tailing: Element[] = []; - public offline: Element[] = []; - public action: Action = new Action(); - public initial: Configuration = new Configuration(Configuration.initial(), undefined); - public buttons: IButton[] = [ - { - icon: 'codicon-tasklist', - handler: () => { - this.provider.recent(); - }, - }, - { - icon: 'codicon-empty-window', - handler: () => { - this.provider.openNewSessionOptions(); - }, - }, - ]; - - constructor(cdRef: ChangeDetectorRef) { - super(new State(), cdRef); - } - - public override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.update(); - this.env().subscriber.register( - this.provider.subjects.get().updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - this.env().subscriber.register( - this.action.subjects.get().apply.subscribe(() => { - this.provider - .openAsNewOrigin( - new Factroy.Stream().asText().process(this.initial.configuration).get(), - ) - .then(() => { - const cloned = this.initial.sterilized(); - this.action.applied(); - this.initial.overwrite({ - command: '', - cwd: cloned.cwd, - envs: cloned.envs, - }); - }) - .catch((err: Error) => { - notifications.notify( - new Notification({ - message: err.message, - actions: [], - }), - ); - this.log().error(`Fail to apply connection to Process: ${err.message}`); - }); - }), - ); - } - - public toggled(opened: boolean) { - this.state.toggleQuickSetup(opened); - } - - protected update(): List { - this.tailing = this.provider - .sources() - .filter((s) => s.observer !== undefined) - .map((s) => new Element(s, this.provider)); - this.offline = this.provider - .sources() - .filter((s) => s.observer === undefined) - .map((s) => new Element(s, this.provider)); - return this; - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/process/module.ts b/application/client/src/app/ui/views/sidebar/observe/lists/process/module.ts deleted file mode 100644 index f4020c2c40..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/process/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { List } from './component'; -import { ElementModule } from '../../element/module'; -import { CommonObserveModule } from '../../common/module'; -import { QuickSetupModule } from '@ui/tabs/observe/origin/stream/transport/setup/quick/process/module'; - -@NgModule({ - imports: [CommonModule, ElementModule, CommonObserveModule, QuickSetupModule], - declarations: [List], - exports: [List], -}) -export class ListModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/process/styles.less b/application/client/src/app/ui/views/sidebar/observe/lists/process/styles.less deleted file mode 100644 index c1c9e9f852..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/process/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0; - p.subtitle{ - color: var(--scheme-color-3); - text-align: right; - } - div.quicksetup { - padding: 0 24px 24px 24px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/process/template.html b/application/client/src/app/ui/views/sidebar/observe/lists/process/template.html deleted file mode 100644 index d5ba862cce..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/process/template.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- -
-
- - -
-
- - -
\ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/serial/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/serial/component.ts deleted file mode 100644 index 13b8af7d3b..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/serial/component.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit, ViewChild } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Provider } from '@service/session/dependencies/observing/implementations/serial'; -import { Element } from '../../element/element'; -import { Action } from '@ui/tabs/observe/action'; -import { QuickSetup } from '@tabs/observe/origin/stream/transport/setup/quick/serial/component'; -import { IButton } from '../../common/title/component'; -import { State } from '../../states/serial'; -import { ListBase } from '../component'; -import { Configuration } from '@platform/types/observe/origin/stream/serial'; -import { notifications, Notification } from '@ui/service/notifications'; - -import * as Factroy from '@platform/types/observe/factory'; - -@Component({ - selector: 'app-views-observed-list-serial', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class List extends ListBase implements AfterContentInit { - @ViewChild('quicksetupref') public quickSetupRef!: QuickSetup; - - public tailing: Element[] = []; - public offline: Element[] = []; - public action: Action = new Action(); - public initial: Configuration = new Configuration(Configuration.initial(), undefined); - public buttons: IButton[] = [ - { - icon: 'codicon-tasklist', - handler: () => { - this.provider.recent(); - }, - }, - { - icon: 'codicon-empty-window', - handler: () => { - // this.provider.openNewSessionOptions(); - }, - }, - ]; - - constructor(cdRef: ChangeDetectorRef) { - super(new State(), cdRef); - } - - public override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.update(); - this.env().subscriber.register( - this.provider.subjects.get().updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - this.env().subscriber.register( - this.action.subjects.get().apply.subscribe(() => { - this.provider - .openAsNewOrigin( - new Factroy.Stream().asText().serial(this.initial.configuration).get(), - ) - .then(() => { - this.action.applied(); - this.initial.overwrite(Configuration.initial()); - }) - .catch((err: Error) => { - notifications.notify( - new Notification({ - message: err.message, - actions: [], - }), - ); - this.log().error(`Fail to apply connection to Serial: ${err.message}`); - }); - }), - ); - } - - public toggled(opened: boolean) { - this.state.toggleQuickSetup(opened); - } - - protected update(): List { - this.tailing = this.provider - .sources() - .filter((s) => s.observer !== undefined) - .map((s) => new Element(s, this.provider)); - this.offline = this.provider - .sources() - .filter((s) => s.observer === undefined) - .map((s) => new Element(s, this.provider)); - return this; - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/serial/module.ts b/application/client/src/app/ui/views/sidebar/observe/lists/serial/module.ts deleted file mode 100644 index 8ff83a3eca..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/serial/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { List } from './component'; -import { ElementModule } from '../../element/module'; -import { CommonObserveModule } from '../../common/module'; -import { QuickSetupModule } from '@ui/tabs/observe/origin/stream/transport/setup/quick/serial/module'; - -@NgModule({ - imports: [CommonModule, ElementModule, CommonObserveModule, QuickSetupModule], - declarations: [List], - exports: [List], -}) -export class ListModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/serial/styles.less b/application/client/src/app/ui/views/sidebar/observe/lists/serial/styles.less deleted file mode 100644 index 2a8ccf7f22..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/serial/styles.less +++ /dev/null @@ -1,12 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0; - p.subtitle{ - color: var(--scheme-color-3); - text-align: right; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/serial/template.html b/application/client/src/app/ui/views/sidebar/observe/lists/serial/template.html deleted file mode 100644 index 1958a0ea6e..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/serial/template.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- -
-
- - -
-
- - -
\ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/tcp/component.ts deleted file mode 100644 index ae222ec039..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/component.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit, ViewChild } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Provider } from '@service/session/dependencies/observing/implementations/tcp'; -import { Element } from '../../element/element'; -import { QuickSetup } from '@tabs/observe/origin/stream/transport/setup/quick/tcp/component'; -import { Action } from '@ui/tabs/observe/action'; -import { IButton } from '../../common/title/component'; -import { State } from '../../states/tcp'; -import { ListBase } from '../component'; -import { Configuration } from '@platform/types/observe/origin/stream/tcp'; -import { notifications, Notification } from '@ui/service/notifications'; - -import * as Factroy from '@platform/types/observe/factory'; - -@Component({ - selector: 'app-views-observed-list-tcp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class List extends ListBase implements AfterContentInit { - @ViewChild('quicksetupref') public quickSetupRef!: QuickSetup; - - public tailing: Element[] = []; - public offline: Element[] = []; - public action: Action = new Action(); - public initial: Configuration = new Configuration(Configuration.initial(), undefined); - public buttons: IButton[] = [ - { - icon: 'codicon-tasklist', - handler: () => { - this.provider.recent(); - }, - }, - { - icon: 'codicon-empty-window', - handler: () => { - this.provider.openNewSessionOptions(); - }, - }, - ]; - - constructor(cdRef: ChangeDetectorRef) { - super(new State(), cdRef); - } - - public override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.update(); - this.env().subscriber.register( - this.provider.subjects.get().updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - this.env().subscriber.register( - this.action.subjects.get().apply.subscribe(() => { - this.provider - .openAsNewOrigin( - new Factroy.Stream().asDlt().tcp(this.initial.configuration).get(), - ) - .then(() => { - this.action.applied(); - this.initial.overwrite(Configuration.initial()); - }) - .catch((err: Error) => { - notifications.notify( - new Notification({ - message: err.message, - actions: [], - }), - ); - this.log().warn(`Fail to apply connection to TCP: ${err.message}`); - }); - }), - ); - } - - public toggled(opened: boolean) { - this.state.toggleQuickSetup(opened); - } - - protected update(): List { - this.tailing = this.provider - .sources() - .filter((s) => s.observer !== undefined) - .map((s) => new Element(s, this.provider)); - this.offline = this.provider - .sources() - .filter((s) => s.observer === undefined) - .map((s) => new Element(s, this.provider)); - return this; - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/module.ts b/application/client/src/app/ui/views/sidebar/observe/lists/tcp/module.ts deleted file mode 100644 index 0ed497def2..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { List } from './component'; -import { ElementModule } from '../../element/module'; -import { CommonObserveModule } from '../../common/module'; -import { QuickSetupModule } from '@ui/tabs/observe/origin/stream/transport/setup/quick/tcp/module'; - -@NgModule({ - imports: [CommonModule, ElementModule, CommonObserveModule, QuickSetupModule], - declarations: [List], - exports: [List], -}) -export class ListModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/styles.less b/application/client/src/app/ui/views/sidebar/observe/lists/tcp/styles.less deleted file mode 100644 index c1c9e9f852..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0; - p.subtitle{ - color: var(--scheme-color-3); - text-align: right; - } - div.quicksetup { - padding: 0 24px 24px 24px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/template.html b/application/client/src/app/ui/views/sidebar/observe/lists/tcp/template.html deleted file mode 100644 index 59fa652d62..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/tcp/template.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- -
-
- - -
-
- - -
\ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/udp/component.ts b/application/client/src/app/ui/views/sidebar/observe/lists/udp/component.ts deleted file mode 100644 index 6c3b3687db..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/udp/component.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { Component, ChangeDetectorRef, AfterContentInit, ViewChild } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Provider } from '@service/session/dependencies/observing/implementations/udp'; -import { Element } from '../../element/element'; -import { QuickSetup } from '@tabs/observe/origin/stream/transport/setup/quick/udp/component'; -import { Action } from '@ui/tabs/observe/action'; -import { IButton } from '../../common/title/component'; -import { State } from '../../states/udp'; -import { ListBase } from '../component'; -import { Configuration } from '@platform/types/observe/origin/stream/udp'; -import { notifications, Notification } from '@ui/service/notifications'; - -import * as Factroy from '@platform/types/observe/factory'; - -@Component({ - selector: 'app-views-observed-list-udp', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class List extends ListBase implements AfterContentInit { - @ViewChild('quicksetupref') public quickSetupRef!: QuickSetup; - - public tailing: Element[] = []; - public offline: Element[] = []; - public action: Action = new Action(); - public initial: Configuration = new Configuration(Configuration.initial(), undefined); - public buttons: IButton[] = [ - { - icon: 'codicon-tasklist', - handler: () => { - this.provider.recent(); - }, - }, - { - icon: 'codicon-empty-window', - handler: () => { - this.provider.openNewSessionOptions(); - }, - }, - ]; - - constructor(cdRef: ChangeDetectorRef) { - super(new State(), cdRef); - } - - public override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.update(); - this.env().subscriber.register( - this.provider.subjects.get().updated.subscribe(() => { - this.update().detectChanges(); - }), - ); - this.env().subscriber.register( - this.action.subjects.get().apply.subscribe(() => { - this.provider - .openAsNewOrigin( - new Factroy.Stream().asDlt().udp(this.initial.configuration).get(), - ) - .then(() => { - this.action.applied(); - this.initial.overwrite(Configuration.initial()); - }) - .catch((err: Error) => { - notifications.notify( - new Notification({ - message: err.message, - actions: [], - }), - ); - this.log().error(`Fail to apply connection to UDP: ${err.message}`); - }); - }), - ); - } - - public toggled(opened: boolean) { - this.state.toggleQuickSetup(opened); - } - - protected update(): List { - this.tailing = this.provider - .sources() - .filter((s) => s.observer !== undefined) - .map((s) => new Element(s, this.provider)); - this.offline = this.provider - .sources() - .filter((s) => s.observer === undefined) - .map((s) => new Element(s, this.provider)); - return this; - } -} -export interface List extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/udp/module.ts b/application/client/src/app/ui/views/sidebar/observe/lists/udp/module.ts deleted file mode 100644 index f1b92a3d71..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/udp/module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { List } from './component'; -import { ElementModule } from '../../element/module'; -import { CommonObserveModule } from '../../common/module'; -import { QuickSetupModule } from '@ui/tabs/observe/origin/stream/transport/setup/quick/udp/module'; - -@NgModule({ - imports: [CommonModule, ElementModule, CommonObserveModule, QuickSetupModule], - declarations: [List], - exports: [List], -}) -export class ListModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/udp/styles.less b/application/client/src/app/ui/views/sidebar/observe/lists/udp/styles.less deleted file mode 100644 index c1c9e9f852..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/udp/styles.less +++ /dev/null @@ -1,15 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: block; - padding: 0; - margin: 0; - p.subtitle{ - color: var(--scheme-color-3); - text-align: right; - } - div.quicksetup { - padding: 0 24px 24px 24px; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/lists/udp/template.html b/application/client/src/app/ui/views/sidebar/observe/lists/udp/template.html deleted file mode 100644 index 818a546edf..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/lists/udp/template.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- -
-
- - -
-
- - -
\ No newline at end of file diff --git a/application/client/src/app/ui/views/sidebar/observe/module.ts b/application/client/src/app/ui/views/sidebar/observe/module.ts index 88eaa297ec..022b18a280 100644 --- a/application/client/src/app/ui/views/sidebar/observe/module.ts +++ b/application/client/src/app/ui/views/sidebar/observe/module.ts @@ -9,11 +9,6 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; import { AttachSourceMenuModule } from '@elements/menu.attachsource/module'; -// import { ListModule as FileListModule } from './lists/file/module'; -// import { ListModule as ProcessListModule } from './lists/process/module'; -// import { ListModule as SerialListModule } from './lists/serial/module'; -// import { ListModule as TcpListModule } from './lists/tcp/module'; -// import { ListModule as UdpListModule } from './lists/udp/module'; @NgModule({ imports: [ diff --git a/application/client/src/app/ui/views/sidebar/observe/states/files.ts b/application/client/src/app/ui/views/sidebar/observe/states/files.ts deleted file mode 100644 index 217f0e4c60..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/files.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Base } from './state'; -import { unique } from '@platform/env/sequence'; - -export const KEY: string = unique(); - -export class State extends Base { - - public key(): string { - return KEY; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/states/process.ts b/application/client/src/app/ui/views/sidebar/observe/states/process.ts deleted file mode 100644 index 4210b3fdc7..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/process.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Base } from './state'; -import { unique } from '@platform/env/sequence'; - -export const KEY: string = unique(); - -export class State extends Base { - public override quicksetup: boolean = true; - - public key(): string { - return KEY; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/states/serial.ts b/application/client/src/app/ui/views/sidebar/observe/states/serial.ts deleted file mode 100644 index 485e5bf7ec..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/serial.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Base } from './state'; -import { unique } from '@platform/env/sequence'; - -export const KEY: string = unique(); - -export class State extends Base { - public key(): string { - return KEY; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/states/state.ts b/application/client/src/app/ui/views/sidebar/observe/states/state.ts deleted file mode 100644 index 482b33b764..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/state.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Destroy } from '@platform/types/env/types'; - -export abstract class Base implements Destroy { - public quicksetup: boolean = false; - - public toggleQuickSetup(quicksetup?: boolean) { - if (quicksetup !== undefined) { - this.quicksetup = quicksetup; - } else { - this.quicksetup = !this.quicksetup; - } - } - - public destroy(): void { - // - } - - public abstract key(): string; -} diff --git a/application/client/src/app/ui/views/sidebar/observe/states/tcp.ts b/application/client/src/app/ui/views/sidebar/observe/states/tcp.ts deleted file mode 100644 index 485e5bf7ec..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/tcp.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Base } from './state'; -import { unique } from '@platform/env/sequence'; - -export const KEY: string = unique(); - -export class State extends Base { - public key(): string { - return KEY; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/states/udp.ts b/application/client/src/app/ui/views/sidebar/observe/states/udp.ts deleted file mode 100644 index 485e5bf7ec..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/states/udp.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Base } from './state'; -import { unique } from '@platform/env/sequence'; - -export const KEY: string = unique(); - -export class State extends Base { - public key(): string { - return KEY; - } -} diff --git a/application/client/src/app/ui/views/workspace/sde/state.ts b/application/client/src/app/ui/views/workspace/sde/state.ts index 3e7070e590..20e4e6536f 100644 --- a/application/client/src/app/ui/views/workspace/sde/state.ts +++ b/application/client/src/app/ui/views/workspace/sde/state.ts @@ -2,7 +2,6 @@ import { Session } from '@service/session'; import { IlcInterface } from '@env/decorators/component'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { ObserveOperation } from '@service/session/dependencies/stream'; -import { IOriginDetails } from '@platform/types/observe'; import { Destroyable } from '@platform/types/life/destroyable'; import { Notification } from '@ui/service/notifications'; import { getSourceColor } from '@ui/styles/colors'; diff --git a/application/holder/src/env/fs/index.ts b/application/holder/src/env/fs/index.ts index fee261da88..49a4856537 100644 --- a/application/holder/src/env/fs/index.ts +++ b/application/holder/src/env/fs/index.ts @@ -1,5 +1,5 @@ import { File, Stat, getFileExtention } from 'platform/types/files'; -import { FileType, extname } from 'platform/types/observe/types/file'; +import { FileType, extname } from 'platform/types/files'; import { error } from 'platform/log/utils'; import { unbound } from '@service/unbound'; @@ -148,4 +148,3 @@ export async function getFileTypeByFilename(filename: string): Promise return FileType.Text; } } - diff --git a/application/holder/src/service/actions/open.file.ts b/application/holder/src/service/actions/open.file.ts index aa8b4896e8..7bb1189196 100644 --- a/application/holder/src/service/actions/open.file.ts +++ b/application/holder/src/service/actions/open.file.ts @@ -1,4 +1,4 @@ -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import * as Requests from 'platform/ipc/request'; diff --git a/application/holder/src/service/actions/open.folder.ts b/application/holder/src/service/actions/open.folder.ts index 0c041a0c80..193e9fb6a8 100644 --- a/application/holder/src/service/actions/open.folder.ts +++ b/application/holder/src/service/actions/open.folder.ts @@ -1,4 +1,4 @@ -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import * as Requests from 'platform/ipc/request'; diff --git a/application/holder/src/service/actions/stream.ts b/application/holder/src/service/actions/stream.ts index d33c19cde7..c15a749496 100644 --- a/application/holder/src/service/actions/stream.ts +++ b/application/holder/src/service/actions/stream.ts @@ -1,15 +1,11 @@ -import * as $ from 'platform/types/observe'; - import * as Requests from 'platform/ipc/request'; +import { Ident } from 'platform/types/bindings'; -export function handler( - protocol: $.Parser.Protocol, - source?: $.Origin.Stream.Stream.Source, -): Promise { +export function handler(parser: Ident | undefined, source: Ident | undefined): Promise { return new Promise((resolve, reject) => { Requests.IpcRequest.send( Requests.Actions.Stream.Response, - new Requests.Actions.Stream.Request({ protocol, source }), + new Requests.Actions.Stream.Request({ parser, source }), ).then((response) => { if (response.error === undefined) { return resolve(); diff --git a/application/holder/src/service/bridge/file/select.ts b/application/holder/src/service/bridge/file/select.ts index dd038a9b16..6cc316e36d 100644 --- a/application/holder/src/service/bridge/file/select.ts +++ b/application/holder/src/service/bridge/file/select.ts @@ -2,7 +2,7 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; import { electron } from '@service/electron'; import { File } from 'platform/types/files'; -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import { getFileEntities } from '@env/fs'; import * as Requests from 'platform/ipc/request'; diff --git a/application/holder/src/service/bridge/folder/select.ts b/application/holder/src/service/bridge/folder/select.ts index af954dacb6..78d78397b4 100644 --- a/application/holder/src/service/bridge/folder/select.ts +++ b/application/holder/src/service/bridge/folder/select.ts @@ -2,7 +2,7 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; import { electron } from '@service/electron'; import { File } from 'platform/types/files'; -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import { getFileEntities, getFilesFromFolder } from '@env/fs'; import * as Requests from 'platform/ipc/request'; diff --git a/application/holder/src/service/cli.ts b/application/holder/src/service/cli.ts index 5a4c2604b9..a9c00043c7 100644 --- a/application/holder/src/service/cli.ts +++ b/application/holder/src/service/cli.ts @@ -18,7 +18,6 @@ import * as Actions from './cli/index'; import * as Events from 'platform/ipc/event'; import * as Requests from 'platform/ipc/request'; import * as fs from 'fs'; -import * as $ from 'platform/types/observe'; const UNIX_LOCAL_BIN = '/usr/local/bin'; const UNIX_SYMLINK_PATH = `${UNIX_LOCAL_BIN}/cm`; @@ -32,13 +31,13 @@ export class Service extends Implementation { protected args: string[] = []; private _available: boolean | undefined; - private readonly _state: { - sessions: string[]; - parser: $.Parser.Protocol; - } = { - sessions: [], - parser: $.Parser.Protocol.Text, - }; + // private readonly _state: { + // sessions: string[]; + // parser: $.Parser.Protocol; + // } = { + // sessions: [], + // parser: $.Parser.Protocol.Text, + // }; public override ready(): Promise { this.log().debug(`Incoming arguments:\n\t${process.argv.join('\n\t')}`); @@ -106,25 +105,25 @@ export class Service extends Implementation { return Promise.resolve(); } - public state(): { - sessions(sessions?: string[]): string[]; - parser(parser?: $.Parser.Protocol): $.Parser.Protocol; - } { - return { - sessions: (sessions?: string[]): string[] => { - if (sessions !== undefined) { - this._state.sessions = sessions; - } - return this._state.sessions; - }, - parser: (parser?: $.Parser.Protocol): $.Parser.Protocol => { - if (parser !== undefined) { - this._state.parser = parser; - } - return this._state.parser; - }, - }; - } + // public state(): { + // sessions(sessions?: string[]): string[]; + // parser(parser?: $.Parser.Protocol): $.Parser.Protocol; + // } { + // return { + // sessions: (sessions?: string[]): string[] => { + // if (sessions !== undefined) { + // this._state.sessions = sessions; + // } + // return this._state.sessions; + // }, + // parser: (parser?: $.Parser.Protocol): $.Parser.Protocol => { + // if (parser !== undefined) { + // this._state.parser = parser; + // } + // return this._state.parser; + // }, + // }; + // } public support(): { install(): Promise; diff --git a/application/holder/src/service/cli/open.ts b/application/holder/src/service/cli/open.ts index f6843f623e..77af8b6021 100644 --- a/application/holder/src/service/cli/open.ts +++ b/application/holder/src/service/cli/open.ts @@ -1,15 +1,13 @@ import { CLIAction, Type } from './action'; import { Service } from '@service/cli'; import { getFileEntities } from '@env/fs'; -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import { globSync } from 'glob'; import { error } from 'platform/log/utils'; import * as fs from 'fs'; import * as path from 'path'; import * as Requests from 'platform/ipc/request'; -import * as Factory from 'platform/types/observe/factory'; -import * as Parser from 'platform/types/observe/parser'; export class Action extends CLIAction { protected files: string[] = []; @@ -62,42 +60,43 @@ export class Action extends CLIAction { if (files.length === 0) { return; } - const observe = - files.length === 1 - ? new Factory.File().file(files[0].filename).get() - : new Factory.Concat().files(files.map((f) => f.filename)).get(); - const types: FileType[] = []; - files.forEach((file) => { - if (types.includes(file.type)) { - return; - } - types.push(file.type); - }); - if (types.length === 1) { - if (types[0] === FileType.Text) { - observe.parser.change( - new Parser.Text.Configuration(Parser.Text.Configuration.initial(), undefined), - ); - } - } - return new Promise((resolve, _reject) => { - Requests.IpcRequest.send( - Requests.Cli.Observe.Response, - new Requests.Cli.Observe.Request({ - observe: [observe.sterilized()], - }), - ) - .then((response) => { - if (response.session === undefined) { - return; - } - cli.state().sessions([response.session]); - }) - .catch((err: Error) => { - cli.log().error(`Fail apply open-action: ${err.message}`); - }) - .finally(resolve); - }); + // const observe = + // files.length === 1 + // ? new Factory.File().file(files[0].filename).get() + // : new Factory.Concat().files(files.map((f) => f.filename)).get(); + // const types: FileType[] = []; + // files.forEach((file) => { + // if (types.includes(file.type)) { + // return; + // } + // types.push(file.type); + // }); + // if (types.length === 1) { + // if (types[0] === FileType.Text) { + // observe.parser.change( + // new Parser.Text.Configuration(Parser.Text.Configuration.initial(), undefined), + // ); + // } + // } + // return new Promise((resolve, _reject) => { + // Requests.IpcRequest.send( + // Requests.Cli.Observe.Response, + // new Requests.Cli.Observe.Request({ + // observe: [observe.sterilized()], + // }), + // ) + // .then((response) => { + // if (response.session === undefined) { + // return; + // } + // cli.state().sessions([response.session]); + // }) + // .catch((err: Error) => { + // cli.log().error(`Fail apply open-action: ${err.message}`); + // }) + // .finally(resolve); + // }); + return Promise.reject(new Error(`Not implemented!`)); } public type(): Type { diff --git a/application/holder/src/service/cli/parser.ts b/application/holder/src/service/cli/parser.ts index 41c84c12c0..ccbfcc5c70 100644 --- a/application/holder/src/service/cli/parser.ts +++ b/application/holder/src/service/cli/parser.ts @@ -1,32 +1,31 @@ import { CLIAction, Type } from './action'; import { Service } from '@service/cli'; -import { Protocol } from 'platform/types/observe/parser'; export class Action extends CLIAction { - protected parser: Protocol | undefined; + // protected parser: Protocol | undefined; protected error: Error[] = []; public argument(_target: string | undefined, _cwd: string, arg: string): string { - switch (arg.toLowerCase()) { - case Protocol.Dlt.toLowerCase(): - this.parser = Protocol.Dlt; - return arg; - case Protocol.SomeIp.toLowerCase(): - this.parser = Protocol.SomeIp; - return arg; - case Protocol.Text.toLowerCase(): - this.parser = Protocol.Text; - return arg; - } - this.error.push( - new Error( - `Invalid value of parser: ${arg}. Available: ${[ - Protocol.Dlt, - Protocol.SomeIp, - Protocol.Text, - ].join(', ')}.`, - ), - ); + // switch (arg.toLowerCase()) { + // case Protocol.Dlt.toLowerCase(): + // this.parser = Protocol.Dlt; + // return arg; + // case Protocol.SomeIp.toLowerCase(): + // this.parser = Protocol.SomeIp; + // return arg; + // case Protocol.Text.toLowerCase(): + // this.parser = Protocol.Text; + // return arg; + // } + // this.error.push( + // new Error( + // `Invalid value of parser: ${arg}. Available: ${[ + // Protocol.Dlt, + // Protocol.SomeIp, + // Protocol.Text, + // ].join(', ')}.`, + // ), + // ); return ''; } @@ -35,20 +34,21 @@ export class Action extends CLIAction { } public execute(cli: Service): Promise { - if (this.error.length > 0) { - return Promise.reject( - new Error( - `Handler cannot be executed, because errors: \n${this.error - .map((e) => e.message) - .join('\n')}`, - ), - ); - } - if (this.parser === undefined) { - return Promise.resolve(); - } - cli.state().parser(this.parser); - return Promise.resolve(); + // if (this.error.length > 0) { + // return Promise.reject( + // new Error( + // `Handler cannot be executed, because errors: \n${this.error + // .map((e) => e.message) + // .join('\n')}`, + // ), + // ); + // } + // if (this.parser === undefined) { + // return Promise.resolve(); + // } + // cli.state().parser(this.parser); + // return Promise.resolve(); + return Promise.reject(new Error(`Not implemented!`)); } public type(): Type { @@ -56,6 +56,7 @@ export class Action extends CLIAction { } public defined(): boolean { - return this.parser !== undefined; + return false; + // return this.parser !== undefined; } } diff --git a/application/holder/src/service/cli/search.ts b/application/holder/src/service/cli/search.ts index 6db3469e17..c8c1f41181 100644 --- a/application/holder/src/service/cli/search.ts +++ b/application/holder/src/service/cli/search.ts @@ -37,27 +37,28 @@ export class Action extends CLIAction { if (!this.defined()) { return Promise.resolve(); } - if (cli.state().sessions().length === 0) { - return Promise.resolve(); - } - return new Promise((resolve, _reject) => { - Requests.IpcRequest.send( - Requests.Cli.Search.Response, - new Requests.Cli.Search.Request({ - sessions: cli.state().sessions(), - filters: this.filters, - }), - ) - .then((response) => { - if (response.error !== undefined) { - cli.log().error(`Fail apply search via CLI: ${response.error}`); - } - }) - .catch((err: Error) => { - cli.log().error(`Fail apply CLI.Search: ${err.message}`); - }) - .finally(resolve); - }); + // if (cli.state().sessions().length === 0) { + // return Promise.resolve(); + // } + // return new Promise((resolve, _reject) => { + // Requests.IpcRequest.send( + // Requests.Cli.Search.Response, + // new Requests.Cli.Search.Request({ + // sessions: cli.state().sessions(), + // filters: this.filters, + // }), + // ) + // .then((response) => { + // if (response.error !== undefined) { + // cli.log().error(`Fail apply search via CLI: ${response.error}`); + // } + // }) + // .catch((err: Error) => { + // cli.log().error(`Fail apply CLI.Search: ${err.message}`); + // }) + // .finally(resolve); + // }); + return Promise.reject(new Error(`Not implemented!`)); } public type(): Type { diff --git a/application/holder/src/service/cli/stream.ts b/application/holder/src/service/cli/stream.ts index 50a51fccdc..992cc1b733 100644 --- a/application/holder/src/service/cli/stream.ts +++ b/application/holder/src/service/cli/stream.ts @@ -2,186 +2,184 @@ import { CLIAction, Type } from './action'; import { Service } from '@service/cli'; import * as Requests from 'platform/ipc/request'; -import * as Factory from 'platform/types/observe/factory'; -import * as $ from 'platform/types/observe'; -function serial(settings: string): $.Origin.Stream.Stream.Serial.IConfiguration | Error { - settings = settings.replace(/\s/gi, ''); - const parts = settings.split(';'); - if (parts.length !== 6) { - return new Error( - `Expecting definition for path; baud_rate; data_bits; flow_control; parity; stop_bits. Use -h (--help) for more details`, - ); - } - let error: Error | undefined; - const parameters: $.Origin.Stream.Stream.Serial.IConfiguration = { - path: '', - baud_rate: -1, - data_bits: -1, - flow_control: -1, - parity: -1, - stop_bits: -1, - send_data_delay: -1, - exclusive: true, - }; - const keys = [ - 'path', - 'baud_rate', - 'data_bits', - 'flow_control', - 'parity', - 'stop_bits', - 'send_data_delay', - 'exclusive', - ]; - parts.forEach((p, i) => { - if (error instanceof Error) { - return; - } - if (i === 0 && p.trim().length === 0) { - error = new Error(`Path has invalid value.`); - return; - } - if (i === 0) { - (parameters as unknown as { [key: string]: string | number | boolean })[keys[i]] = p; - } else if (i === keys.length - 1) { - (parameters as unknown as { [key: string]: string | number | boolean })[keys[i]] = - typeof p === 'boolean' - ? p - : typeof p === 'string' - ? p === 'true' - ? true - : false - : typeof p === 'number' - ? p === 1 - ? true - : false - : true; - } else { - const value = parseInt(p, 10); - if (isNaN(value) || !isFinite(value)) { - error = new Error(`Parameter "${keys[i]}" has invalid value.`); - return; - } - (parameters as unknown as { [key: string]: string | number })[keys[i]] = value; - } - }); - if (error instanceof Error) { - return error; - } - return parameters; -} +// function serial(settings: string): $.Origin.Stream.Stream.Serial.IConfiguration | Error { +// settings = settings.replace(/\s/gi, ''); +// const parts = settings.split(';'); +// if (parts.length !== 6) { +// return new Error( +// `Expecting definition for path; baud_rate; data_bits; flow_control; parity; stop_bits. Use -h (--help) for more details`, +// ); +// } +// let error: Error | undefined; +// const parameters: $.Origin.Stream.Stream.Serial.IConfiguration = { +// path: '', +// baud_rate: -1, +// data_bits: -1, +// flow_control: -1, +// parity: -1, +// stop_bits: -1, +// send_data_delay: -1, +// exclusive: true, +// }; +// const keys = [ +// 'path', +// 'baud_rate', +// 'data_bits', +// 'flow_control', +// 'parity', +// 'stop_bits', +// 'send_data_delay', +// 'exclusive', +// ]; +// parts.forEach((p, i) => { +// if (error instanceof Error) { +// return; +// } +// if (i === 0 && p.trim().length === 0) { +// error = new Error(`Path has invalid value.`); +// return; +// } +// if (i === 0) { +// (parameters as unknown as { [key: string]: string | number | boolean })[keys[i]] = p; +// } else if (i === keys.length - 1) { +// (parameters as unknown as { [key: string]: string | number | boolean })[keys[i]] = +// typeof p === 'boolean' +// ? p +// : typeof p === 'string' +// ? p === 'true' +// ? true +// : false +// : typeof p === 'number' +// ? p === 1 +// ? true +// : false +// : true; +// } else { +// const value = parseInt(p, 10); +// if (isNaN(value) || !isFinite(value)) { +// error = new Error(`Parameter "${keys[i]}" has invalid value.`); +// return; +// } +// (parameters as unknown as { [key: string]: string | number })[keys[i]] = value; +// } +// }); +// if (error instanceof Error) { +// return error; +// } +// return parameters; +// } -function udp(settings: string): $.Origin.Stream.Stream.UDP.IConfiguration | Error { - settings = settings.replace(/\s/gi, ''); - const parts = settings.split('|'); - if (parts.length !== 2) { - return new Error( - `Expecting definition for addr and multicast splitted with "|". Use -h (--help) for more details`, - ); - } - if (parts[0].length === 0) { - return new Error(`Fail to find addr to connect. Use -h (--help) for more details`); - } - if (parts[1].length === 0) { - return new Error(`Fail to find multicast defenitions. Use -h (--help) for more details`); - } - let error: Error | undefined; - const multicast = parts[1] - .split(';') - .map((pair) => { - if (pair.trim().length === 0) { - return undefined; - } - if (error !== undefined) { - return { - multiaddr: '', - interface: undefined, - }; - } - const pairs = pair.split(','); - if (pairs.length !== 1 && pairs.length !== 2) { - error = new Error( - `Each multicast defenition should include address and interface (or at least mutlicast address). Use -h (--help) for more details `, - ); - } - if (pairs.length === 1 && pairs[0].length === 0) { - error = new Error( - `Each multicast defenition should include at least address. Use -h (--help) for more details `, - ); - } - if (pairs.length === 2 && pairs[0].length === 0 && pairs[1].length === 0) { - error = new Error( - `Each multicast defenition should include address and interface. Use -h (--help) for more details `, - ); - } - return { - multiaddr: pairs[0], - interface: pairs.length === 1 ? undefined : pairs[1], - }; - }) - .filter( - (m: $.Origin.Stream.Stream.UDP.Multicast | undefined) => m !== undefined, - ) as $.Origin.Stream.Stream.UDP.Multicast[]; - if (error instanceof Error) { - return error; - } - return { - bind_addr: parts[0], - multicast, - }; -} +// function udp(settings: string): $.Origin.Stream.Stream.UDP.IConfiguration | Error { +// settings = settings.replace(/\s/gi, ''); +// const parts = settings.split('|'); +// if (parts.length !== 2) { +// return new Error( +// `Expecting definition for addr and multicast splitted with "|". Use -h (--help) for more details`, +// ); +// } +// if (parts[0].length === 0) { +// return new Error(`Fail to find addr to connect. Use -h (--help) for more details`); +// } +// if (parts[1].length === 0) { +// return new Error(`Fail to find multicast defenitions. Use -h (--help) for more details`); +// } +// let error: Error | undefined; +// const multicast = parts[1] +// .split(';') +// .map((pair) => { +// if (pair.trim().length === 0) { +// return undefined; +// } +// if (error !== undefined) { +// return { +// multiaddr: '', +// interface: undefined, +// }; +// } +// const pairs = pair.split(','); +// if (pairs.length !== 1 && pairs.length !== 2) { +// error = new Error( +// `Each multicast defenition should include address and interface (or at least mutlicast address). Use -h (--help) for more details `, +// ); +// } +// if (pairs.length === 1 && pairs[0].length === 0) { +// error = new Error( +// `Each multicast defenition should include at least address. Use -h (--help) for more details `, +// ); +// } +// if (pairs.length === 2 && pairs[0].length === 0 && pairs[1].length === 0) { +// error = new Error( +// `Each multicast defenition should include address and interface. Use -h (--help) for more details `, +// ); +// } +// return { +// multiaddr: pairs[0], +// interface: pairs.length === 1 ? undefined : pairs[1], +// }; +// }) +// .filter( +// (m: $.Origin.Stream.Stream.UDP.Multicast | undefined) => m !== undefined, +// ) as $.Origin.Stream.Stream.UDP.Multicast[]; +// if (error instanceof Error) { +// return error; +// } +// return { +// bind_addr: parts[0], +// multicast, +// }; +// } -function getObserveFactory( - target: string | undefined, - cwd: string, - arg: string, -): Factory.Stream | Error { - if (target === 'stdout') { - if (arg.trim() !== '') { - return new Factory.Stream().process({ - command: arg, - cwd, - envs: {}, - }); - } else { - return new Error(`Command to spawn cannot be empty`); - } - } else if (target === 'serial') { - const settings = serial(arg); - if (settings instanceof Error) { - return settings; - } else { - return new Factory.Stream().serial(settings); - } - } else if (target === 'tcp') { - if (arg.trim() !== '') { - return new Factory.Stream().tcp({ bind_addr: arg }); - } else { - return new Error(`No bidning address for TCP connection`); - } - } else if (target === 'udp') { - const settings = udp(arg); - if (settings instanceof Error) { - return settings; - } else { - return new Factory.Stream().udp(settings); - } - } - return new Error(`Unknown target for streaming: ${target}`); -} +// function getObserveFactory( +// target: string | undefined, +// cwd: string, +// arg: string, +// ): Factory.Stream | Error { +// if (target === 'stdout') { +// if (arg.trim() !== '') { +// return new Factory.Stream().process({ +// command: arg, +// cwd, +// envs: {}, +// }); +// } else { +// return new Error(`Command to spawn cannot be empty`); +// } +// } else if (target === 'serial') { +// const settings = serial(arg); +// if (settings instanceof Error) { +// return settings; +// } else { +// return new Factory.Stream().serial(settings); +// } +// } else if (target === 'tcp') { +// if (arg.trim() !== '') { +// return new Factory.Stream().tcp({ bind_addr: arg }); +// } else { +// return new Error(`No bidning address for TCP connection`); +// } +// } else if (target === 'udp') { +// const settings = udp(arg); +// if (settings instanceof Error) { +// return settings; +// } else { +// return new Factory.Stream().udp(settings); +// } +// } +// return new Error(`Unknown target for streaming: ${target}`); +// } export class Action extends CLIAction { - protected factories: Factory.Stream[] = []; + // protected factories: Factory.Stream[] = []; protected error: Error[] = []; public argument(target: string | undefined, cwd: string, arg: string): string { - const factory = getObserveFactory(target, cwd, arg); - if (factory instanceof Error) { - this.error.push(factory); - return ''; - } - this.factories.push(factory); + // const factory = getObserveFactory(target, cwd, arg); + // if (factory instanceof Error) { + // this.error.push(factory); + // return ''; + // } + // this.factories.push(factory); return arg; } @@ -202,26 +200,27 @@ export class Action extends CLIAction { if (!this.defined()) { return Promise.resolve(); } - return new Promise((resolve, _reject) => { - Requests.IpcRequest.send( - Requests.Cli.Observe.Response, - new Requests.Cli.Observe.Request({ - observe: this.factories.map((factory) => - factory.protocol(cli.state().parser()).get().sterilized(), - ), - }), - ) - .then((response) => { - if (response.session === undefined) { - return; - } - cli.state().sessions([response.session]); - }) - .catch((err: Error) => { - cli.log().error(`Fail apply stream action: ${err.message}`); - }) - .finally(resolve); - }); + return Promise.reject(new Error(`Not implemented!`)); + // return new Promise((resolve, _reject) => { + // Requests.IpcRequest.send( + // Requests.Cli.Observe.Response, + // new Requests.Cli.Observe.Request({ + // observe: this.factories.map((factory) => + // factory.protocol(cli.state().parser()).get().sterilized(), + // ), + // }), + // ) + // .then((response) => { + // if (response.session === undefined) { + // return; + // } + // cli.state().sessions([response.session]); + // }) + // .catch((err: Error) => { + // cli.log().error(`Fail apply stream action: ${err.message}`); + // }) + // .finally(resolve); + // }); } public type(): Type { @@ -229,6 +228,7 @@ export class Action extends CLIAction { } public defined(): boolean { - return this.factories.length > 0; + return false; + // return this.factories.length > 0; } } diff --git a/application/holder/src/service/menu.ts b/application/holder/src/service/menu.ts index 41f3bcf130..f744b5b59a 100644 --- a/application/holder/src/service/menu.ts +++ b/application/holder/src/service/menu.ts @@ -10,11 +10,10 @@ import { cli } from '@service/cli'; import { Menu, MenuItem } from 'electron'; import { notifications } from '@service/notifications'; import { unique } from 'platform/env/sequence'; -import { FileType } from 'platform/types/observe/types/file'; +import { FileType } from 'platform/types/files'; import { ChipmunkGlobal } from '@register/global'; import * as Actions from './actions'; -import * as $ from 'platform/types/observe'; declare const global: ChipmunkGlobal; @@ -455,89 +454,89 @@ export class Service extends Implementation { }, ], }, - { - label: 'Connections', - submenu: [ - { - label: 'DLT on UDP', - click: async () => { - Actions.stream( - $.Parser.Protocol.Dlt, - $.Origin.Stream.Stream.Source.UDP, - ).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - { - label: 'DLT on TCP', - click: async () => { - Actions.stream( - $.Parser.Protocol.Dlt, - $.Origin.Stream.Stream.Source.TCP, - ).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - { - label: 'DLT on Serial Port', - click: async () => { - Actions.stream( - $.Parser.Protocol.Dlt, - $.Origin.Stream.Stream.Source.Serial, - ).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - { type: 'separator' }, - { - label: 'Plain text Serial Port', - click: async () => { - Actions.stream( - $.Parser.Protocol.Text, - $.Origin.Stream.Stream.Source.Serial, - ).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - { type: 'separator' }, - { - label: 'Select Source for Plain text', - click: async () => { - Actions.stream($.Parser.Protocol.Text).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - { - label: 'Select Source for DLT', - click: async () => { - Actions.stream($.Parser.Protocol.Dlt).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - ], - }, - { - label: 'Terminal', - submenu: [ - { - label: 'Execute command', - click: async () => { - Actions.stream( - $.Parser.Protocol.Text, - $.Origin.Stream.Stream.Source.Process, - ).catch((err: Error) => { - this.log().error(`Fail call action Stream: ${err.message}`); - }); - }, - }, - ], - }, + // { + // label: 'Connections', + // submenu: [ + // { + // label: 'DLT on UDP', + // click: async () => { + // Actions.stream( + // $.Parser.Protocol.Dlt, + // $.Origin.Stream.Stream.Source.UDP, + // ).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // { + // label: 'DLT on TCP', + // click: async () => { + // Actions.stream( + // $.Parser.Protocol.Dlt, + // $.Origin.Stream.Stream.Source.TCP, + // ).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // { + // label: 'DLT on Serial Port', + // click: async () => { + // Actions.stream( + // $.Parser.Protocol.Dlt, + // $.Origin.Stream.Stream.Source.Serial, + // ).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // { type: 'separator' }, + // { + // label: 'Plain text Serial Port', + // click: async () => { + // Actions.stream( + // $.Parser.Protocol.Text, + // $.Origin.Stream.Stream.Source.Serial, + // ).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // { type: 'separator' }, + // { + // label: 'Select Source for Plain text', + // click: async () => { + // Actions.stream($.Parser.Protocol.Text).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // { + // label: 'Select Source for DLT', + // click: async () => { + // Actions.stream($.Parser.Protocol.Dlt).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // ], + // }, + // { + // label: 'Terminal', + // submenu: [ + // { + // label: 'Execute command', + // click: async () => { + // Actions.stream( + // $.Parser.Protocol.Text, + // $.Origin.Stream.Stream.Source.Process, + // ).catch((err: Error) => { + // this.log().error(`Fail call action Stream: ${err.message}`); + // }); + // }, + // }, + // ], + // }, { label: 'Edit', submenu: [ diff --git a/application/holder/src/service/sessions/holder.ts b/application/holder/src/service/sessions/holder.ts index ad0471acdf..1bd4211372 100644 --- a/application/holder/src/service/sessions/holder.ts +++ b/application/holder/src/service/sessions/holder.ts @@ -5,9 +5,10 @@ import { scope } from 'platform/env/scope'; import { Logger } from 'platform/log'; import { jobs } from '@service/jobs'; import { ICancelablePromise } from 'platform/env/promise'; -import { Tys } from 'rustcore'; +import { SessionSetup } from 'platform/types/bindings'; import * as Events from 'platform/ipc/event'; +import { IJob } from '@service/jobs/job'; export enum Jobs { search = 'search', @@ -68,43 +69,35 @@ export class Holder { } public observe(): { - start(options: Tys.bindings.SessionSetup): Promise; + start(setup: SessionSetup): Promise; cancel(uuid: string): Promise; list(): string[]; } { return { - start: (options: Tys.bindings.SessionSetup): Promise => { - const holder = new Tys.sessionsetup.SessionSetupHolder(options); + start: (setup: SessionSetup): Promise => { if (this.shutdown) { return Promise.reject(new Error(`Session is closing`)); } - let jobDesc = holder.asJob(); - if (jobDesc instanceof Error) { - this.logger.error(`Fail to get job description: ${jobDesc.message}`); - jobDesc = { - name: 'unknown', - desc: 'unknown', - icon: undefined, - }; - } + const description = this.getDescription(setup); const job = jobs .create({ session: this.session.getUUID(), - name: jobDesc.name, - desc: jobDesc.desc, - icon: jobDesc.icon, + name: description.title, + desc: description.desctiption, + // TODO: probably we should refuse from icons + icon: '', }) .start(); return new Promise((resolve, reject) => { const observer = this.session .getStream() - .observe(options) + .observe(setup) .on('confirmed', () => { Events.IpcEvent.emit( new Events.Observe.Started.Event({ session: this.session.getUUID(), operation: observer.uuid(), - options, + options: setup, }), ); }) @@ -123,7 +116,7 @@ export class Holder { new Events.Observe.Finished.Event({ session: this.session.getUUID(), operation: observer.uuid(), - options, + options: setup, }), ); }); @@ -196,4 +189,26 @@ export class Holder { public isShutdowning(): boolean { return this.shutdown; } + + protected getDescription(setup: SessionSetup): { + title: string; + desctiption: string | undefined; + } { + if (setup.origin === 'Source') { + // TODO: Check idents + return { + title: 'Custom Source', + desctiption: `Data comes from selected source provider`, + }; + } else if ((setup.origin as { File: string }).File) { + return { title: `Selected File`, desctiption: (setup.origin as { File: string }).File }; + } else if ((setup.origin as { Files: string[] }).Files) { + return { + title: `Collection of Files`, + desctiption: `${(setup.origin as { Files: string[] }).Files.length} files`, + }; + } else { + return { title: 'Unknown', desctiption: undefined }; + } + } } diff --git a/application/holder/src/service/unbound.ts b/application/holder/src/service/unbound.ts index a09f35c13b..789c52feb3 100644 --- a/application/holder/src/service/unbound.ts +++ b/application/holder/src/service/unbound.ts @@ -20,15 +20,6 @@ export class Service extends Implementation { public override async ready(): Promise { (this as Mutable).jobs = await Jobs.create(); - this.register( - electron - .ipc() - .respondent( - this.getName(), - Requests.Dlt.Stat.Request, - RequestHandlers.Dlt.Stat.handler, - ), - ); this.register( electron .ipc() diff --git a/application/holder/src/service/unbound/dlt/index.ts b/application/holder/src/service/unbound/dlt/index.ts deleted file mode 100644 index 8d9feda931..0000000000 --- a/application/holder/src/service/unbound/dlt/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Stat from './stat'; diff --git a/application/holder/src/service/unbound/dlt/stat.ts b/application/holder/src/service/unbound/dlt/stat.ts deleted file mode 100644 index a87d7b20fe..0000000000 --- a/application/holder/src/service/unbound/dlt/stat.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CancelablePromise } from 'platform/env/promise'; -import { Logger } from 'platform/log'; -import { jobs } from '@service/jobs'; -import { unbound } from '@service/unbound'; -import { DltStatisticInfo } from 'platform/types/bindings'; - -import * as Requests from 'platform/ipc/request'; - -export const handler = Requests.InjectLogger< - Requests.Dlt.Stat.Request, - CancelablePromise ->( - ( - _log: Logger, - request: Requests.Dlt.Stat.Request, - ): CancelablePromise => { - return new CancelablePromise((resolve, reject) => { - const scanning = jobs - .create({ - name: 'scanning dlt', - desc: - request.files.length === 1 - ? `file: ${request.files[0]}` - : `${request.files.length} for files`, - }) - .start(); - unbound.jobs - .getDltStats(request.files) - .then((stat: DltStatisticInfo) => { - resolve( - new Requests.Dlt.Stat.Response({ - stat, - }), - ); - }) - .catch(reject) - .finally(() => { - scanning.done(); - }); - }); - }, -); diff --git a/application/holder/src/service/unbound/index.ts b/application/holder/src/service/unbound/index.ts index b688947940..52027b11d8 100644 --- a/application/holder/src/service/unbound/index.ts +++ b/application/holder/src/service/unbound/index.ts @@ -1,5 +1,3 @@ -export * as Dlt from './dlt'; export * as Os from './os'; export * as File from './file'; export * as Serial from './serial'; -export * as Someip from './someip'; diff --git a/application/holder/src/service/unbound/someip/index.ts b/application/holder/src/service/unbound/someip/index.ts deleted file mode 100644 index 48f2d93fda..0000000000 --- a/application/holder/src/service/unbound/someip/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Statistic from './statistic'; diff --git a/application/holder/src/service/unbound/someip/statistic.ts b/application/holder/src/service/unbound/someip/statistic.ts deleted file mode 100644 index 1636b4aaa9..0000000000 --- a/application/holder/src/service/unbound/someip/statistic.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CancelablePromise } from 'platform/env/promise'; -import { Logger } from 'platform/log'; -import { jobs } from '@service/jobs'; -import { unbound } from '@service/unbound'; -import { SomeipStatistic } from 'platform/types/observe/parser/someip'; - -import * as Requests from 'platform/ipc/request'; - -export const handler = Requests.InjectLogger< - Requests.Dlt.Stat.Request, - CancelablePromise ->( - ( - _log: Logger, - request: Requests.Dlt.Stat.Request, - ): CancelablePromise => { - return new CancelablePromise((resolve, reject) => { - const scanning = jobs - .create({ - name: 'scanning dlt', - desc: - request.files.length === 1 - ? `file: ${request.files[0]}` - : `${request.files.length} for files`, - }) - .start(); - unbound.jobs - .getSomeipStatistic(request.files) - .then((statistic: SomeipStatistic) => { - resolve( - new Requests.Someip.Statistic.Response({ - statistic, - }), - ); - }) - .catch(reject) - .finally(() => { - scanning.done(); - }); - }); - }, -); diff --git a/application/platform/ipc/request/actions/open.file.ts b/application/platform/ipc/request/actions/open.file.ts index d2a3c0baf4..c8c6bc34a9 100644 --- a/application/platform/ipc/request/actions/open.file.ts +++ b/application/platform/ipc/request/actions/open.file.ts @@ -1,5 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { FileType } from '../../../types/observe/types/file'; +import { FileType } from '../../../types/files'; import * as validator from '../../../env/obj'; diff --git a/application/platform/ipc/request/actions/open.folder.ts b/application/platform/ipc/request/actions/open.folder.ts index 2d8d582c5b..c9a7063b50 100644 --- a/application/platform/ipc/request/actions/open.folder.ts +++ b/application/platform/ipc/request/actions/open.folder.ts @@ -1,5 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { FileType } from '../../../types/observe/types/file'; +import { FileType } from '../../../types/files'; import * as validator from '../../../env/obj'; diff --git a/application/platform/ipc/request/actions/stream.ts b/application/platform/ipc/request/actions/stream.ts index eadc44a430..0dd9e9c469 100644 --- a/application/platform/ipc/request/actions/stream.ts +++ b/application/platform/ipc/request/actions/stream.ts @@ -1,19 +1,18 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { Protocol } from '../../../types/observe/parser'; -import { Source } from '../../../types/observe/origin/stream/index'; +import { Ident } from '../../../types/bindings/observe'; import * as validator from '../../../env/obj'; @Define({ name: 'StreamActionRequest' }) export class Request extends SignatureRequirement { - public protocol: Protocol; - public source?: Source; + public parser?: Ident; + public source?: Ident; - constructor(input: { protocol: Protocol; source: Source | undefined }) { + constructor(input: { parser: Ident | undefined; source: Ident | undefined }) { super(); validator.isObject(input); - this.protocol = validator.getAsNotEmptyString(input, 'protocol') as Protocol; - this.source = validator.getAsNotEmptyStringOrAsUndefined(input, 'source') as Source; + this.parser = validator.getAsObjOrUndefined(input, 'parser'); + this.source = validator.getAsObjOrUndefined(input, 'source'); } } export interface Request extends Interface {} diff --git a/application/platform/ipc/request/cli/observe.ts b/application/platform/ipc/request/cli/observe.ts index 4d746469a9..39b86dec7d 100644 --- a/application/platform/ipc/request/cli/observe.ts +++ b/application/platform/ipc/request/cli/observe.ts @@ -1,22 +1,16 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { IObserve, Observe } from '../../../types/observe/index'; +import { SessionSetup } from '../../../types/bindings/observe'; import * as validator from '../../../env/obj'; @Define({ name: 'ObserveCLICommandRequest' }) export class Request extends SignatureRequirement { - public observe: IObserve[]; + public observe: SessionSetup[]; - constructor(input: { observe: IObserve[] }) { + constructor(input: { observe: SessionSetup[] }) { super(); validator.isObject(input); this.observe = validator.getAsObj(input, 'observe'); - for (const obsv of this.observe) { - const error = Observe.validate(obsv); - if (error instanceof Error) { - throw error; - } - } } } diff --git a/application/platform/ipc/request/dlt/index.ts b/application/platform/ipc/request/dlt/index.ts deleted file mode 100644 index 8d9feda931..0000000000 --- a/application/platform/ipc/request/dlt/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Stat from './stat'; diff --git a/application/platform/ipc/request/dlt/stat.ts b/application/platform/ipc/request/dlt/stat.ts deleted file mode 100644 index ebb1364bea..0000000000 --- a/application/platform/ipc/request/dlt/stat.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Define, Interface, SignatureRequirement } from '../declarations'; -import { DltStatisticInfo } from '../../../types/bindings'; -import * as validator from '../../../env/obj'; - -@Define({ name: 'DltStatRequest' }) -export class Request extends SignatureRequirement { - public files: string[]; - - constructor(input: { files: string[] }) { - super(); - validator.isObject(input); - this.files = validator.getAsArray(input, 'files'); - } -} -export interface Request extends Interface {} - -@Define({ name: 'DltStatResponse' }) -export class Response extends SignatureRequirement { - public stat: DltStatisticInfo; - - constructor(input: { stat: DltStatisticInfo }) { - super(); - validator.isObject(input); - this.stat = validator.getAsObj(input, 'stat'); - } -} - -export interface Response extends Interface {} diff --git a/application/platform/ipc/request/file/select.ts b/application/platform/ipc/request/file/select.ts index c01841b8ab..e82af6cff3 100644 --- a/application/platform/ipc/request/file/select.ts +++ b/application/platform/ipc/request/file/select.ts @@ -1,6 +1,6 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; import { File } from '../../../types/files'; -import { FileType } from '../../../types/observe/types/file'; +import { FileType } from '../../../types/files'; import * as validator from '../../../env/obj'; diff --git a/application/platform/ipc/request/folder/select.ts b/application/platform/ipc/request/folder/select.ts index 231724530f..02f0a16525 100644 --- a/application/platform/ipc/request/folder/select.ts +++ b/application/platform/ipc/request/folder/select.ts @@ -1,6 +1,6 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; import { File } from '../../../types/files'; -import { FileType } from '../../../types/observe/types/file'; +import { FileType } from '../../../types/files'; import * as validator from '../../../env/obj'; diff --git a/application/platform/ipc/request/index.ts b/application/platform/ipc/request/index.ts index 66aacb2522..f13a57711c 100644 --- a/application/platform/ipc/request/index.ts +++ b/application/platform/ipc/request/index.ts @@ -2,8 +2,6 @@ export { IpcRequest, InjectLogger } from './declarations'; export * as Session from './session'; export * as File from './file'; export * as Stream from './stream'; -export * as Dlt from './dlt'; -export * as Someip from './someip'; export * as Search from './search'; export * as Storage from './storage'; export * as Os from './os'; diff --git a/application/platform/ipc/request/observe/sources.ts b/application/platform/ipc/request/observe/sources.ts index 9724bc1bf6..0bbce8e984 100644 --- a/application/platform/ipc/request/observe/sources.ts +++ b/application/platform/ipc/request/observe/sources.ts @@ -1,5 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { ISourceLink } from '../../../types/observe/types/index'; +import { ISourceLink } from '../../../types/source'; import * as validator from '../../../env/obj'; diff --git a/application/platform/ipc/request/someip/index.ts b/application/platform/ipc/request/someip/index.ts deleted file mode 100644 index 48f2d93fda..0000000000 --- a/application/platform/ipc/request/someip/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as Statistic from './statistic'; diff --git a/application/platform/ipc/request/someip/statistic.ts b/application/platform/ipc/request/someip/statistic.ts deleted file mode 100644 index 0c3726a69a..0000000000 --- a/application/platform/ipc/request/someip/statistic.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Define, Interface, SignatureRequirement } from '../declarations'; -import { SomeipStatistic } from '../../../types/observe/parser/someip'; - -import * as validator from '../../../env/obj'; - -@Define({ name: 'SomeipStatisticRequest' }) -export class Request extends SignatureRequirement { - public files: string[]; - - constructor(input: { files: string[] }) { - super(); - validator.isObject(input); - this.files = validator.getAsArray(input, 'files'); - } -} -export interface Request extends Interface {} - -@Define({ name: 'SomeipStatisticResponse' }) -export class Response extends SignatureRequirement { - public statistic: SomeipStatistic; - - constructor(input: { statistic: SomeipStatistic }) { - super(); - validator.isObject(input); - this.statistic = validator.getAsObj(input, 'statistic'); - } -} - -export interface Response extends Interface {} diff --git a/application/platform/package.json b/application/platform/package.json index b57c5dcf52..61173b7daa 100644 --- a/application/platform/package.json +++ b/application/platform/package.json @@ -96,26 +96,8 @@ "./types/bookmark": "./dist/types/bookmark.js", "./types/exporting": "./dist/types/exporting.js", "./types/sessionsetup": "./dist/types/sessionsetup/index.js", - "./types/observe": "./dist/types/observe/index.js", - "./types/observe/factory": "./dist/types/observe/factory.js", - "./types/observe/description": "./dist/types/observe/description.js", - "./types/observe/parser": "./dist/types/observe/parser/index.js", - "./types/observe/parser/dlt": "./dist/types/observe/parser/dlt/index.js", - "./types/observe/parser/someip": "./dist/types/observe/parser/someip/index.js", - "./types/observe/parser/text": "./dist/types/observe/parser/text/index.js", - "./types/observe/types": "./dist/types/observe/types/index.js", - "./types/observe/types/file": "./dist/types/observe/types/file/index.js", - "./types/observe/types/sourcelink": "./dist/types/observe/types/sourcelink.js", - "./types/observe/origin": "./dist/types/observe/origin/index.js", - "./types/observe/origin/file": "./dist/types/observe/origin/file.js", - "./types/observe/origin/concat": "./dist/types/observe/origin/concat.js", - "./types/observe/origin/stream": "./dist/types/observe/origin/stream.js", - "./types/observe/origin/stream/index": "./dist/types/observe/origin/stream/index.js", - "./types/observe/origin/stream/process": "./dist/types/observe/origin/stream/process/index.js", - "./types/observe/origin/stream/udp": "./dist/types/observe/origin/stream/udp/index.js", - "./types/observe/origin/stream/tcp": "./dist/types/observe/origin/stream/tcp/index.js", - "./types/observe/origin/stream/serial": "./dist/types/observe/origin/stream/serial/index.js", "./types/range": "./dist/types/range.js", + "./types/source": "./dist/types/source.js", "./types/hotkeys/map": "./dist/types/hotkeys/map.js", "./types/parsers": "./dist/types/parsers/index.js", "./types/parsers/dlt": "./dist/types/parsers/dlt.js", diff --git a/application/platform/types/files.ts b/application/platform/types/files.ts index dc97a38621..35929da769 100644 --- a/application/platform/types/files.ts +++ b/application/platform/types/files.ts @@ -1,7 +1,45 @@ -import { FileType } from './observe/types/file'; - import * as obj from '../env/obj'; +export abstract class Support { + public abstract getSupportedFileType(): FileName[]; +} + +// TODO: remove it! +export enum FileType { + PcapNG = 'PcapNG', + PcapLegacy = 'PcapLegacy', + Text = 'Text', + Binary = 'Binary', + ParserPlugin = 'ParserPlugin', +} + +export function getFileTypeFrom(smth: unknown): FileType | Error { + switch (smth as FileType) { + case FileType.Binary: + return FileType.Binary; + case FileType.PcapNG: + return FileType.PcapNG; + case FileType.PcapLegacy: + return FileType.PcapLegacy; + case FileType.Text: + return FileType.Text; + case FileType.ParserPlugin: + return FileType.ParserPlugin; + default: + return new Error(`${smth} isn't FileType`); + } +} + +export function extname(filename: string): string { + const matches = filename.match(/\.[\w\d_]*$/gi); + if (matches !== null) { + return matches[0]; + } + return ''; +} + +export type FileName = string; + export enum EntityType { BlockDevice = 0, CharacterDevice = 1, diff --git a/application/platform/types/index.ts b/application/platform/types/index.ts index f648d239a3..9fc82fe979 100644 --- a/application/platform/types/index.ts +++ b/application/platform/types/index.ts @@ -1,7 +1,6 @@ export * as files from './files'; export * as filter from './filter'; export * as chart from './chart'; -export * as observe from './observe'; export * as content from './content'; export * as range from './range'; export * as storage from './storage'; @@ -11,4 +10,3 @@ export * as bookmark from './bookmark'; export * as sde from './sde'; export * as bindings from './bindings'; export * as components from './components'; -export * as sessionsetup from './sessionsetup'; diff --git a/application/platform/types/observe/compatibility.ts b/application/platform/types/observe/compatibility.ts deleted file mode 100644 index 876dbf0bf0..0000000000 --- a/application/platform/types/observe/compatibility.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as Parser from './parser'; -import * as Stream from './origin/stream/index'; -import * as Origin from './origin/index'; -import * as File from './types/file'; - -export const Streams: { - [key: string]: Stream.Reference[]; -} = { - [Parser.Protocol.Dlt]: [ - // Supported streams - Stream.TCP.Configuration, - Stream.UDP.Configuration, - ], - [Parser.Protocol.SomeIp]: [ - // Supported streams - Stream.UDP.Configuration, - ], - [Parser.Protocol.Text]: [ - // Supported streams - Stream.Serial.Configuration, - Stream.Process.Configuration, - ], - // Make sure we support all kinds of stream - [Parser.Protocol.Plugin]: [ - Stream.TCP.Configuration, - Stream.UDP.Configuration, - Stream.Serial.Configuration, - Stream.Process.Configuration, - ], -}; - -export const Files: { - [key: string]: File.FileType[]; -} = { - [Parser.Protocol.Dlt]: [ - // Supported file types - File.FileType.Binary, - File.FileType.PcapNG, - File.FileType.PcapLegacy, - ], - [Parser.Protocol.SomeIp]: [ - // Supported file types - File.FileType.PcapNG, - File.FileType.PcapLegacy, - ], - [Parser.Protocol.Text]: [ - // Supported file types - File.FileType.Text, - ], - // Plugins support all file types. - [Parser.Protocol.Plugin]: [ - File.FileType.Text, - File.FileType.Binary, - File.FileType.PcapNG, - File.FileType.PcapLegacy, - File.FileType.ParserPlugin, - ], -}; - -export const SDESupport: { - [key: string]: boolean; -} = { - [Origin.Context.File]: false, - [Origin.Context.Concat]: false, - [Origin.Context.Plugin]: false, - [Stream.Source.Process]: true, - [Stream.Source.Serial]: true, - [Stream.Source.TCP]: false, - [Stream.Source.UDP]: false, -}; - -export const Configurable: { - [key: string]: - | { - [key: string]: boolean; - } - | boolean; -} = { - [Origin.Context.File]: { - [Parser.Protocol.Text]: false, - [Parser.Protocol.Dlt]: true, - [Parser.Protocol.SomeIp]: true, - [Parser.Protocol.Plugin]: true, - }, - [Origin.Context.Concat]: true, - [Origin.Context.Plugin]: true, - [Stream.Source.Process]: true, - [Stream.Source.Serial]: true, - [Stream.Source.TCP]: true, - [Stream.Source.UDP]: true, -}; diff --git a/application/platform/types/observe/configuration.ts b/application/platform/types/observe/configuration.ts deleted file mode 100644 index 431238743f..0000000000 --- a/application/platform/types/observe/configuration.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { error } from '../../log/utils'; -import { JsonConvertor } from '../storage/json'; -import { Validate, SelfValidate, Alias, Destroy, Storable, Hash } from '../env/types'; -import { List } from './description'; -import { scope } from '../../env/scope'; -import { Subject, Subscriber, Subscription } from '../../env/subscription'; -import { unique } from '../../env/sequence'; -import { Observer } from '../../env/observer'; - -import * as Stream from './origin/stream/index'; -import * as File from './types/file'; - -export interface ConfigurationStatic extends Validate, Alias { - initial(): T; -} - -export interface ConfigurationStaticDesc extends Validate, Alias, List { - initial(): T; - inited(): boolean; -} - -export interface Reference extends ConfigurationStatic { - // new (configuration: T, linked: Linked | undefined): C & Configuration; - new (...args: any[]): C & Configuration; -} - -export interface ReferenceDesc extends ConfigurationStaticDesc { - // new (configuration: T, linked: Linked | undefined): C & Configuration; - new (...args: any[]): C & Configuration; -} - -export interface ICompatibilityMod { - Streams: { - [key: string]: Stream.Reference[]; - }; - Files: { - [key: string]: File.FileType[]; - }; - SDESupport: { - [key: string]: boolean; - }; - Configurable: { - [key: string]: - | { - [key: string]: boolean; - } - | boolean; - }; -} -// To prevent circle dependency we are loading compatibility table in async way -let compatibility: ICompatibilityMod | undefined; -import('./compatibility') - .then((mod) => { - compatibility = mod; - }) - .catch((err: Error) => { - scope.getLogger('CompatibilityModule').error(err.message); - }); - -export function getCompatibilityMod(): ICompatibilityMod { - if (compatibility === undefined) { - throw new Error(`Moudle "compatibility" isn't loaded yet`); - } - return compatibility; -} - -export type TOverwriteHandler = (configuration: T) => T; - -export interface Linked { - overwrite: TOverwriteHandler; - watcher: Subject; -} - -export abstract class Configuration - extends Subscriber - implements - JsonConvertor>, - SelfValidate, - Storable, - Hash, - Destroy -{ - protected ref: Reference; - protected readonly src: T; - protected readonly observer: Observer | undefined; - protected readonly linked: Linked | undefined; - protected overwriting: boolean = false; - - public readonly uuid: string = unique(); - public watcher: Subject = new Subject(); - - protected getOriginWatcher(): Subject { - if (this.linked !== undefined) { - return this.linked.watcher; - } - if (this.observer !== undefined) { - return this.observer.watcher; - } - throw new Error(`002: Configuration doesn't have observer or linked`); - } - - constructor(configuration: T, linked: Linked | undefined) { - super(); - if (typeof this.constructor !== 'function') { - throw new Error(`Fail to get reference to Constructor`); - } - this.ref = this.constructor as Reference; - this.src = configuration; - this.linked = linked; - if (linked === undefined) { - this.observer = new Observer(configuration); - } - this.register( - this.getOriginWatcher().subscribe(() => { - if (this.overwriting) { - return; - } - this.watcher.emit(); - }), - ); - } - - public get configuration(): T { - if (this.linked !== undefined) { - return this.src; - } - if (this.observer !== undefined) { - return this.observer.target; - } - throw new Error(`001: Configuration doesn't have observer or linked`); - } - - public destroy(): void { - if (this.observer !== undefined) { - this.observer.destroy(); - } - this.watcher.destroy(); - this.unsubscribe(); - } - - public validate(): Error | undefined { - const error: Error | T = this.ref.validate(this.sterilized()); - return error instanceof Error ? error : undefined; - } - - public alias(): A { - return this.ref.alias(); - } - - public overwrite(configuration: T): void { - this.overwriting = true; - if (this.linked !== undefined) { - (this as any).src = this.linked.overwrite(configuration); - } else if (this.observer !== undefined) { - (this as any).src = Observer.sterilize(configuration); - this.observer.overwrite(configuration); - } else { - this.overwriting = false; - throw new Error(`004: Configuration doesn't have observer or linked`); - } - this.overwriting = false; - this.watcher.emit(); - } - - public setRef(configuration: T | C): void { - if (this.linked !== undefined) { - (this as any).src = configuration; - } else if (this.observer !== undefined) { - throw new Error(`005: Only linked configuration can be rerefered to new target`); - } else { - throw new Error(`006: Configuration doesn't have observer or linked`); - } - } - - public subscribe(handler: () => void): Subscription { - return this.watcher.subscribe(() => { - setTimeout(handler); - }); - } - - public json(): { - to(): string; - from(str: string): Configuration | Error; - } { - return { - to: (): string => { - if (this.linked !== undefined) { - return JSON.stringify(Observer.sterilize(this.src)); - } - if (this.observer !== undefined) { - return JSON.stringify(this.observer.sterilize()); - } - throw new Error(`007: Configuration doesn't have observer or linked`); - }, - from: (str: string): Configuration | Error => { - try { - const configuration: T | Error = this.ref.validate(JSON.parse(str)); - if (configuration instanceof Error) { - return configuration; - } - this.overwrite(configuration); - return this; - } catch (e) { - return new Error(error(e)); - } - }, - }; - } - - public getSupportedStream(): Stream.Reference[] { - if (getCompatibilityMod().Streams[this.ref.alias() as string] === undefined) { - throw new Error( - `008: Entity "${this.ref.alias()}" isn't registred in compatibility.Streams list`, - ); - } - return getCompatibilityMod().Streams[this.ref.alias() as string]; - } - - public getSupportedFileType(): File.FileType[] { - if (getCompatibilityMod().Files[this.ref.alias() as string] === undefined) { - throw new Error( - `009: Entity "${this.ref.alias()}" isn't registred in compatibility.Files list`, - ); - } - return getCompatibilityMod().Files[this.ref.alias() as string]; - } - - public isSdeSupported(): boolean { - if (getCompatibilityMod().SDESupport[this.ref.alias() as string] === undefined) { - throw new Error( - `010: Entity "${this.ref.alias()}" isn't registred in compatibility.SDESupport list`, - ); - } - return getCompatibilityMod().SDESupport[this.ref.alias() as string]; - } - - public sterilized(): T { - if (this.linked !== undefined) { - return Observer.sterilize(this.src); - } - if (this.observer !== undefined) { - return this.observer.sterilize(); - } - throw new Error(`011: Configuration doesn't have observer or linked`); - } - - // This is default implementation, but in some cases (like "Stream.Process") - // it should be adjusted - public storable(): T { - return this.sterilized(); - } - - public abstract hash(): number; -} diff --git a/application/platform/types/observe/description.ts b/application/platform/types/observe/description.ts deleted file mode 100644 index e5bebc40f7..0000000000 --- a/application/platform/types/observe/description.ts +++ /dev/null @@ -1,39 +0,0 @@ -export interface IList { - major: string; - minor: string; - icon: string | undefined; -} - -export interface IJob { - name: string; - desc: string; - icon: string | undefined; -} - -export enum OriginType { - net = 'net', - serial = 'serial', - command = 'command', - file = 'file', - plugin = 'plugin', -} -export interface IOriginDetails extends IList { - type: OriginType; - action: string; - state: { - running: string; - stopped: string; - }; -} - -export abstract class List { - public abstract desc(): IList; -} - -export abstract class Job { - public abstract asJob(): IJob; -} - -export abstract class OriginDetails { - public abstract desc(): IOriginDetails; -} diff --git a/application/platform/types/observe/factory.ts b/application/platform/types/observe/factory.ts deleted file mode 100644 index 794e18721e..0000000000 --- a/application/platform/types/observe/factory.ts +++ /dev/null @@ -1,370 +0,0 @@ -import * as $ from './index'; - -export { FileType } from './types/file/index'; - -class Factory { - protected changes: { parser: boolean; origin: boolean } = { parser: false, origin: false }; - - protected updated(): { - parser(): void; - origin(): void; - } { - const update = () => { - if (this.changes.origin === this.changes.parser) { - // No needs to update if and parser and origin were changed or weren't changed both - return; - } - if (this.changes.origin) { - const parsers = this.observe.origin.getSupportedParsers(); - if (parsers.length === 0) { - throw new Error( - `No supported parser for ${this.observe.origin.instance.alias()}`, - ); - } - this.protocol(parsers[0].alias()); - } else if (this.changes.parser) { - // TODO: check source also - // switch (this.observe.origin.instance.alias()) { - // case $.Origin.Context.File: - // const _filetypes = this.observe.parser.getSupportedFileType(); - // break; - // case $.Origin.Context.Concat: - // const _filetypes = this.observe.parser.getSupportedFileType(); - // break; - // case $.Origin.Context.Stream: - // const _streams = this.observe.parser - // .getSupportedStream() - // .map((r) => r.alias()); - // break; - // } - } - }; - return { - parser: (): void => { - this.changes.parser = true; - update(); - }, - origin: (): void => { - this.changes.origin = true; - update(); - }, - }; - } - - protected observe: $.Observe = $.Observe.new(); - - public protocol(protocol: $.Parser.Protocol): T { - this.observe.parser.change($.Parser.getByAlias(protocol)); - this.updated().parser(); - return this as unknown as T; - } - - public asDlt(configuration?: $.Parser.Dlt.IConfiguration): T { - this.observe.parser.change( - new $.Parser.Dlt.Configuration( - configuration === undefined ? $.Parser.Dlt.Configuration.initial() : configuration, - undefined, - ), - ); - this.updated().parser(); - return this as unknown as T; - } - - public asSomeip(configuration?: $.Parser.SomeIp.IConfiguration): T { - this.observe.parser.change( - new $.Parser.SomeIp.Configuration( - configuration === undefined - ? $.Parser.SomeIp.Configuration.initial() - : configuration, - undefined, - ), - ); - this.updated().parser(); - return this as unknown as T; - } - - public parser(parser: $.Parser.Declaration): T { - this.observe.parser.change(parser); - return this as unknown as T; - } - - public asText(): T { - this.observe.parser.change(new $.Parser.Text.Configuration(null, undefined)); - this.updated().parser(); - return this as unknown as T; - } - - public asParserPlugin(configuration?: $.Parser.Plugin.IConfiguration): T { - this.observe.parser.change( - new $.Parser.Plugin.Configuration( - configuration === undefined - ? $.Parser.Plugin.Configuration.initial() - : configuration, - - undefined, - ), - ); - this.updated().parser(); - return this as unknown as T; - } - - public guessParser(): T { - const parsers = this.observe.origin.getSupportedParsers(); - if (parsers.length === 0) { - throw new Error( - `Origin "${this.observe.origin.getNatureAlias()}" doesn't have any suitable parseres.`, - ); - } - const Ref = parsers[0]; - this.observe.parser.change(new Ref(Ref.initial(), undefined)); - this.updated().parser(); - return this as unknown as T; - } - - public get(): $.Observe { - const parsers = this.observe.origin.getSupportedParsers().map((ref) => ref.alias()); - const selected = this.observe.parser.alias(); - if (!parsers.includes(selected)) { - throw new Error( - `Origin "${this.observe.origin.getNatureAlias()}" doesn't support parser: ${selected}; available parsers: ${parsers.join( - ', ', - )}.`, - ); - } - return this.observe; - } -} - -export class File extends Factory { - static FileType = $.Types.File.FileType; - - constructor() { - super(); - this.observe.origin.change( - new $.Origin.File.Configuration($.Origin.File.Configuration.initial(), undefined), - ); - } - - public alias(alias: string): File { - if (!(this.observe.origin.instance instanceof $.Origin.File.Configuration)) { - throw new Error(`Given observe object doesn't have File origin`); - } - this.observe.origin.instance.set().alias(alias); - this.updated().origin(); - return this; - } - - public file(filename: string): File { - if (!(this.observe.origin.instance instanceof $.Origin.File.Configuration)) { - throw new Error(`Given observe object doesn't have File origin`); - } - this.observe.origin.instance.set().filename(filename); - this.updated().origin(); - return this; - } - - public type(type: $.Types.File.FileType): File { - if (!(this.observe.origin.instance instanceof $.Origin.File.Configuration)) { - throw new Error(`Given observe object doesn't have File origin`); - } - this.observe.origin.instance.set().type(type); - this.updated().origin(); - return this; - } -} - -export class Concat extends Factory { - static FileType = $.Types.File.FileType; - - constructor() { - super(); - this.observe.origin.change( - new $.Origin.Concat.Configuration($.Origin.Concat.Configuration.initial(), undefined), - ); - } - - public files(files: string[]): Concat { - if (!(this.observe.origin.instance instanceof $.Origin.Concat.Configuration)) { - throw new Error(`Given observe object doesn't have Concat origin`); - } - this.observe.origin.instance.set().files(files); - this.updated().origin(); - return this; - } - - public type(type: $.Types.File.FileType): Concat { - if (!(this.observe.origin.instance instanceof $.Origin.Concat.Configuration)) { - throw new Error(`Given observe object doesn't have Concat origin`); - } - this.observe.origin.instance.set().defaults(type); - this.updated().origin(); - return this; - } - - public push(filename: string, type: $.Types.File.FileType): Concat { - if (!(this.observe.origin.instance instanceof $.Origin.Concat.Configuration)) { - throw new Error(`Given observe object doesn't have Concat origin`); - } - this.observe.origin.instance.set().push(filename, type); - this.updated().origin(); - return this; - } - - public remove(filename: string): Concat { - if (!(this.observe.origin.instance instanceof $.Origin.Concat.Configuration)) { - throw new Error(`Given observe object doesn't have Concat origin`); - } - this.observe.origin.instance.set().remove(filename); - this.updated().origin(); - return this; - } -} - -export class Stream extends Factory { - constructor() { - super(); - this.observe.origin.change( - new $.Origin.Stream.Configuration($.Origin.Stream.Configuration.initial(), undefined), - ); - } - - public process(configuration?: $.Origin.Stream.Stream.Process.IConfiguration): Stream { - if (!(this.observe.origin.instance instanceof $.Origin.Stream.Configuration)) { - throw new Error(`Given observe object doesn't have Stream origin`); - } - this.observe.origin.instance.change( - new $.Origin.Stream.Stream.Process.Configuration( - configuration !== undefined - ? configuration - : $.Origin.Stream.Stream.Process.Configuration.initial(), - undefined, - ), - ); - this.updated().origin(); - return this; - } - - public serial(configuration?: $.Origin.Stream.Stream.Serial.IConfiguration): Stream { - if (!(this.observe.origin.instance instanceof $.Origin.Stream.Configuration)) { - throw new Error(`Given observe object doesn't have Stream origin`); - } - this.observe.origin.instance.change( - new $.Origin.Stream.Stream.Serial.Configuration( - configuration !== undefined - ? configuration - : $.Origin.Stream.Stream.Serial.Configuration.initial(), - undefined, - ), - ); - this.updated().origin(); - return this; - } - - public tcp(configuration?: $.Origin.Stream.Stream.TCP.IConfiguration): Stream { - if (!(this.observe.origin.instance instanceof $.Origin.Stream.Configuration)) { - throw new Error(`Given observe object doesn't have Stream origin`); - } - this.observe.origin.instance.change( - new $.Origin.Stream.Stream.TCP.Configuration( - configuration !== undefined - ? configuration - : $.Origin.Stream.Stream.TCP.Configuration.initial(), - undefined, - ), - ); - this.updated().origin(); - return this; - } - - public udp(configuration?: $.Origin.Stream.Stream.UDP.IConfiguration): Stream { - if (!(this.observe.origin.instance instanceof $.Origin.Stream.Configuration)) { - throw new Error(`Given observe object doesn't have Stream origin`); - } - this.observe.origin.instance.change( - new $.Origin.Stream.Stream.UDP.Configuration( - configuration !== undefined - ? configuration - : $.Origin.Stream.Stream.UDP.Configuration.initial(), - undefined, - ), - ); - this.updated().origin(); - return this; - } -} - -export function map(observe: $.Observe): { - file(): boolean; - concat(): boolean; - process(): boolean; - serial(): boolean; - udp(): boolean; - tcp(): boolean; -} { - return { - file: (): boolean => { - return ( - observe.origin.as<$.Origin.File.Configuration>($.Origin.File.Configuration) !== - undefined - ); - }, - concat: (): boolean => { - return ( - observe.origin.as<$.Origin.Concat.Configuration>($.Origin.Concat.Configuration) !== - undefined - ); - }, - process: (): boolean => { - const stream = observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (stream === undefined) { - return false; - } - return ( - stream.as<$.Origin.Stream.Stream.Process.Configuration>( - $.Origin.Stream.Stream.Process.Configuration, - ) !== undefined - ); - }, - serial: (): boolean => { - const stream = observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (stream === undefined) { - return false; - } - return ( - stream.as<$.Origin.Stream.Stream.Serial.Configuration>( - $.Origin.Stream.Stream.Serial.Configuration, - ) !== undefined - ); - }, - udp: (): boolean => { - const stream = observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (stream === undefined) { - return false; - } - return ( - stream.as<$.Origin.Stream.Stream.UDP.Configuration>( - $.Origin.Stream.Stream.UDP.Configuration, - ) !== undefined - ); - }, - tcp: (): boolean => { - const stream = observe.origin.as<$.Origin.Stream.Configuration>( - $.Origin.Stream.Configuration, - ); - if (stream === undefined) { - return false; - } - return ( - stream.as<$.Origin.Stream.Stream.TCP.Configuration>( - $.Origin.Stream.Stream.TCP.Configuration, - ) !== undefined - ); - }, - }; -} diff --git a/application/platform/types/observe/index.ts b/application/platform/types/observe/index.ts deleted file mode 100644 index 1098b2d2c6..0000000000 --- a/application/platform/types/observe/index.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { Configuration as Base, getCompatibilityMod } from './configuration'; -import { Mutable } from '../unity/mutable'; -import { LockToken } from '../../env/lock.token'; -import { Signature } from '../env/types'; - -import * as Parser from './parser'; -import * as Origin from './origin'; -import * as Sde from './sde'; -import * as Bindings from '../bindings'; - -export * as Parser from './parser'; -export * as Origin from './origin'; -export * as Types from './types'; -export * as Description from './description'; -export * as Bindings from '../bindings'; - -export { IList, IOriginDetails, IJob } from './description'; - -export interface ISessionSetup { - origin: Bindings.SessionAction; - parser: { - uuid: string; - options: Bindings.Field[]; - }; - source: { - uuid: string; - options: Bindings.Field[]; - }; -} - -export interface IObserve { - origin: Origin.IConfiguration; - parser: Parser.IConfiguration; -} - -export class Observe - extends Base - implements Sde.Support, Parser.Support, Signature -{ - static new(): Observe { - return new Observe({ - parser: { - Dlt: Parser.Dlt.Configuration.initial(), - }, - origin: { - Stream: Origin.Stream.Configuration.initial(), - }, - }); - } - - static from(json: string): Observe | Error { - const observe = new Observe({ - parser: { - Dlt: Parser.Dlt.Configuration.initial(), - }, - origin: { - Stream: Origin.Stream.Configuration.initial(), - }, - }); - const error = observe.json().from(json); - return error instanceof Error ? error : observe; - } - - static alias(): undefined { - return undefined; - } - - static initial(): IObserve { - return { - origin: Origin.Configuration.initial(), - parser: Parser.Configuration.initial(), - }; - } - - static validate(configuration: IObserve): Error | IObserve { - let error: Error | unknown = Origin.Configuration.validate(configuration.origin); - if (error instanceof Error) { - return error; - } - error = Parser.Configuration.validate(configuration.parser); - return error instanceof Error ? error : configuration; - } - - public readonly origin!: Origin.Configuration; - public readonly parser!: Parser.Configuration; - - /// Lock-state - /// Allows define a way to process observe data. - /// true - observe data can be used to observe (create a session); - /// false - observe data probably requires some addition configuration - /// for example settings of DLT or SomeIP - protected readonly lock: LockToken = new LockToken(false); - - protected link(): void { - (this as Mutable).origin = new Origin.Configuration(this.configuration.origin, { - watcher: this.watcher, - overwrite: (config: Origin.IConfiguration) => { - this.configuration.origin = config; - return this.configuration.origin; - }, - }); - (this as Mutable).parser = new Parser.Configuration(this.configuration.parser, { - watcher: this.watcher, - overwrite: (config: Parser.IConfiguration) => { - this.configuration.parser = config; - return this.configuration.parser; - }, - }); - } - - protected onOriginChange() { - this.parser.onOriginChange(this.origin); - } - - constructor(observe: IObserve) { - super(observe, undefined); - this.link(); - this.parser.onOriginChange(this.origin); - this.origin.watcher.subscribe(this.onOriginChange.bind(this)); - this.parser.watcher.subscribe(this.onOriginChange.bind(this)); - } - - public override destroy(): void { - super.destroy(); - this.parser !== undefined && this.parser.destroy(); - this.origin !== undefined && this.origin.destroy(); - } - - public override isSdeSupported(): boolean { - return this.origin.isSdeSupported(); - } - - public override overwrite(configuration: IObserve): void { - this.origin !== undefined && this.origin.destroy(); - this.parser !== undefined && this.parser.destroy(); - super.overwrite(configuration); - this.link(); - } - - public getSupportedParsers(): Parser.Reference[] { - return this.origin.getSupportedParsers(); - } - - public clone(): Observe { - const cloned = new Observe(this.sterilized()); - // Drop alias to prevent multiple observing entries with same UUID - cloned.origin.set().alias(); - return cloned; - } - - public locker(): { - lock(): Observe; - unlock(): Observe; - // Lock configuration if it's possible to lock - guess(): Observe; - is(): boolean; - } { - return { - lock: (): Observe => { - this.lock.lock(); - return this; - }, - unlock: (): Observe => { - this.lock.unlock(); - return this; - }, - guess: (): Observe => { - if (this.parser.instance instanceof Parser.Text.Configuration) { - this.lock.lock(); - } - return this; - }, - is: (): boolean => { - return this.lock.isLocked(); - }, - }; - } - - public isConfigurable(): boolean { - const map: any = getCompatibilityMod().Configurable; - const nature = this.origin.nature().alias(); - const parser = this.parser.alias(); - if (typeof map[nature] === 'boolean') { - return map[nature]; - } else if (typeof map[nature][parser] === 'boolean') { - return map[nature][parser]; - } - throw new Error( - `Parser "${parser}" and origin "${nature}" don't have description in Compatibility.Configurable table`, - ); - } - - public override storable(): IObserve { - return { - origin: this.origin.storable(), - parser: this.parser.storable(), - }; - } - - public override hash(): number { - return this.origin.hash() + this.parser.hash(); - } - - public signature(): string { - return `${this.origin.hash()}${this.parser.hash()}`; - } -} diff --git a/application/platform/types/observe/origin/concat.ts b/application/platform/types/observe/origin/concat.ts deleted file mode 100644 index 8e94cf3861..0000000000 --- a/application/platform/types/observe/origin/concat.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { error } from '../../../log/utils'; -import { Configuration as Base, ConfigurationStaticDesc } from '../configuration'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../description'; -import { Configuration as ConfigurationFile } from './file'; -import { Context, SourceUuid, IConfiguration as IOriginConfiguration } from './index'; -import { getParentFolder } from '../../files'; -import { Statics } from '../../../env/decorators'; -import { unique } from '../../../env/sequence'; - -import * as Types from '../types'; -import * as Parser from '../parser'; -import * as Sde from '../sde'; -import * as str from '../../../env/str'; - -export type IConfiguration = [SourceUuid, Types.File.FileType, Types.File.FileName][]; - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Parser.Support, Job -{ - static desc(): IList { - return { - major: `Files`, - minor: 'Local Files', - icon: 'files', - }; - } - - static alias(): Context { - return Context.Concat; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (!(configuration instanceof Array) || configuration.length === 0) { - throw new Error( - `Source "${Context.Concat}" should be represented as a not empty array.`, - ); - } else { - configuration.forEach((file) => { - // If file settings are not correct it will throw an error - new ConfigurationFile(file, undefined); - }); - } - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return []; - } - - static inited(): boolean { - return true; - } - - protected defaultFileType: Types.File.FileType = Types.File.FileType.Text; - - public source(): string | undefined { - return undefined; - } - - public set(): { - files(files: string[]): Configuration; - defaults(type: Types.File.FileType): Configuration; - push(filename: string, type: Types.File.FileType): Configuration; - remove(filename: string): Configuration; - alias(alias?: string): Configuration; - } { - return { - files: (files: string[]): Configuration => { - this.configuration.push( - ...(files.map((filename: string) => { - return [unique(), this.defaultFileType, filename]; - }) as IConfiguration), - ); - return this; - }, - defaults: (type: Types.File.FileType): Configuration => { - this.defaultFileType = type; - return this; - }, - push: (filename: string, type: Types.File.FileType): Configuration => { - this.configuration.push([unique(), type, filename]); - return this; - }, - remove: (filename: string): Configuration => { - const index = this.configuration.findIndex((def) => def[2] === filename); - if (index !== -1) { - this.configuration.splice(index, 1); - } - return this; - }, - alias: (alias?: string): Configuration => { - this.configuration.forEach((file) => { - file[0] = alias === undefined ? unique() : alias; - }); - return this; - }, - }; - } - - public files(): string[] { - return this.configuration.map((c) => c[2]); - } - - public filetypes(): Types.File.FileType[] { - return this.configuration.map((c) => c[1]); - } - - public asFileOrigins(): IOriginConfiguration[] { - return this.configuration.map((c) => { - return { - [Context.File]: c, - }; - }); - } - - public desc(): IOriginDetails { - const first = this.configuration[0]; - return { - major: `Concating ${this.configuration.length} files`, - minor: first !== undefined ? getParentFolder(first[2]) : '', - icon: 'insert_drive_file', - type: OriginType.file, - action: 'Concat', - state: { - running: 'processing', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: 'concating', - desc: `concating ${this.configuration.length} files`, - icon: 'insert_drive_file', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - if (this.configuration.length === 0) { - // Returns default - return [ - Parser.Dlt.Configuration, - Parser.SomeIp.Configuration, - Parser.Text.Configuration, - Parser.Plugin.Configuration, - ]; - } - switch (this.configuration[0][1]) { - case Types.File.FileType.Binary: - return [Parser.Dlt.Configuration, Parser.SomeIp.Configuration]; - case Types.File.FileType.PcapNG: - return [Parser.Dlt.Configuration, Parser.SomeIp.Configuration]; - case Types.File.FileType.PcapLegacy: - return [Parser.Dlt.Configuration, Parser.SomeIp.Configuration]; - case Types.File.FileType.Text: - return [Parser.Text.Configuration]; - case Types.File.FileType.ParserPlugin: - return [Parser.Plugin.Configuration]; - } - } - - public override hash(): number { - return str.hash(`${this.files().join(';')};${this.filetypes().join(';')}`); - } -} diff --git a/application/platform/types/observe/origin/file.ts b/application/platform/types/observe/origin/file.ts deleted file mode 100644 index 50d31eeb35..0000000000 --- a/application/platform/types/observe/origin/file.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { error } from '../../../log/utils'; -import { Configuration as Base, ConfigurationStaticDesc } from '../configuration'; -import { Context, SourceUuid } from './index'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../description'; -import { getFileName, getParentFolder } from '../../files'; -import { Statics } from '../../../env/decorators'; -import { unique } from '../../../env/sequence'; - -import * as Types from '../types'; -import * as Parser from '../parser'; -import * as Sde from '../sde'; -import * as str from '../../../env/str'; - -export type IConfiguration = [SourceUuid, Types.File.FileType, Types.File.FileName]; - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Parser.Support, Job -{ - static desc(): IList { - return { - major: `File`, - minor: 'Local File', - icon: 'file', - }; - } - - static alias(): Context { - return Context.File; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (configuration instanceof Array && configuration.length === 3) { - str.asNotEmptyString( - configuration[0], - `SourceUuid isn't found: ${configuration[0]}`, - ); - str.asNotEmptyString( - configuration[2], - `Origin.FileName isn't found: ${configuration[2]}`, - ); - if (Types.File.getFileTypeFrom(configuration[1]) instanceof Error) { - throw new Error(`Invalid Origin.FileType: ${configuration[1]}`); - } - } else { - throw new Error( - `Source "${Context.File}" should be represented as an array, len = 3.`, - ); - } - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return [unique(), Types.File.FileType.Text, '']; - } - - public source(): string | undefined { - return this.configuration[0]; - } - - public set(): { - filename(filename: string): Configuration; - type(type: Types.File.FileType): Configuration; - alias(alias?: string): Configuration; - } { - return { - filename: (filename: string): Configuration => { - this.configuration[2] = filename; - return this; - }, - type: (type: Types.File.FileType): Configuration => { - this.configuration[1] = type; - return this; - }, - alias: (alias: string): Configuration => { - this.configuration[0] = alias === undefined ? unique() : alias; - return this; - }, - }; - } - - public get(): { - filename(): string; - type(): Types.File.FileType; - alias(): string; - } { - return { - filename: (): string => { - return this.configuration[2]; - }, - type: (): Types.File.FileType => { - return this.configuration[1]; - }, - alias: (): string => { - return this.configuration[0]; - }, - }; - } - - public filename(): string { - return this.configuration[2]; - } - - public filetype(): Types.File.FileType { - return this.configuration[1]; - } - - public desc(): IOriginDetails { - return { - major: getFileName(this.configuration[2]), - minor: getParentFolder(this.configuration[2]), - icon: 'insert_drive_file', - type: OriginType.file, - action: 'Open', - state: { - running: 'tail', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: 'tail', - desc: getFileName(this.filename()), - icon: 'insert_drive_file', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - switch (this.configuration[1]) { - case Types.File.FileType.Binary: - return [ - Parser.Dlt.Configuration, - Parser.SomeIp.Configuration, - Parser.Plugin.Configuration, - ]; - case Types.File.FileType.PcapNG: - return [ - Parser.Dlt.Configuration, - Parser.SomeIp.Configuration, - Parser.Plugin.Configuration, - ]; - case Types.File.FileType.PcapLegacy: - return [ - Parser.Dlt.Configuration, - Parser.SomeIp.Configuration, - Parser.Plugin.Configuration, - ]; - case Types.File.FileType.Text: - return [Parser.Text.Configuration]; - case Types.File.FileType.ParserPlugin: - return [Parser.Plugin.Configuration]; - } - } - - public override hash(): number { - return str.hash(`${this.filename()};${this.filetype()}`); - } -} diff --git a/application/platform/types/observe/origin/index.ts b/application/platform/types/observe/origin/index.ts deleted file mode 100644 index 44794c3650..0000000000 --- a/application/platform/types/observe/origin/index.ts +++ /dev/null @@ -1,258 +0,0 @@ -import { Configuration as Base, ConfigurationStatic, Linked } from '../configuration'; -import { Statics } from '../../../env/decorators'; -import { Mutable } from '../../unity/mutable'; -import { Alias } from '../../env/types'; -import { OriginDetails, IOriginDetails, Job, IJob } from '../description'; - -export * as File from './file'; -export * as Concat from './concat'; -export * as Stream from './stream'; -export * as Plugin from './plugin'; - -import * as File from './file'; -import * as Concat from './concat'; -import * as Stream from './stream'; -import * as Plugin from './plugin'; -import * as Parser from '../parser'; -import * as Sde from '../sde'; - -//TODO: Plugin byte source integration will be postponed. -export enum Context { - File = 'File', - Concat = 'Concat', - Stream = 'Stream', - Plugin = 'Plugin', -} - -export type SourceUuid = string; - -export interface OnChange { - onOriginChange(origin: Configuration): void; -} - -export interface IConfiguration { - [Context.File]?: File.IConfiguration; - [Context.Concat]?: Concat.IConfiguration; - [Context.Stream]?: Stream.IConfiguration; - [Context.Plugin]?: Plugin.IConfiguration; -} - -export const REGISTER = { - [Context.File]: File.Configuration, - [Context.Concat]: Concat.Configuration, - [Context.Stream]: Stream.Configuration, - [Context.Plugin]: Plugin.Configuration, -}; - -export const DEFAULT = File.Configuration; - -export type Declaration = - | File.Configuration - | Concat.Configuration - | Stream.Configuration - | Plugin.Configuration; - -export type OriginNature = - | File.Configuration - | Concat.Configuration - | Stream.Stream.Declaration - | Plugin.Configuration; - -@Statics>() -export class Configuration - extends Base - implements Parser.Support, Sde.Support, Job, OriginDetails -{ - static alias(): undefined { - return undefined; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - if ( - Object.keys(REGISTER) - .map((k) => configuration[k as Context]) - .filter((v) => v !== undefined).length === 0 - ) { - return new Error(`Origin isn't defined`); - } - let error: Error | undefined; - Object.keys(REGISTER).forEach((key) => { - if (error instanceof Error) { - return; - } - const config = configuration[key as Context]; - if (config === undefined) { - return; - } - const err = REGISTER[key as Context].validate(config as any); - if (err instanceof Error) { - error = err; - } else { - error = undefined; - } - }); - return error instanceof Error ? error : configuration; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - [DEFAULT.alias()]: DEFAULT.initial(), - }; - } - - protected getContextKey(): Context | undefined { - let found: Context | undefined; - Object.keys(REGISTER).forEach((key) => { - if (found !== undefined) { - return; - } - if (this.configuration[key as Context] === undefined) { - return; - } - found = key as Context; - }); - return found; - } - - protected setInstance(): Configuration { - const context = this.getContextKey(); - if (context === undefined) { - throw new Error(`Configuration of stream doesn't have definition of known context.`); - } - if (this.instance !== undefined && this.instance.alias() === context) { - this.instance.setRef(this.configuration[context]); - return this; - } - this.instance !== undefined && this.instance.destroy(); - (this as Mutable).instance = new REGISTER[context]( - this.configuration[context] as any, - { - watcher: this.watcher, - overwrite: (config: IConfiguration) => { - this.overwrite(config); - return this.configuration[context] as any; - }, - }, - ); - return this; - } - - public readonly instance!: Declaration; - - constructor(configuration: IConfiguration, linked: Linked | undefined) { - super(configuration, linked); - this.register( - this.watcher.subscribe(() => { - if (this.overwriting) { - return; - } - this.setInstance(); - }), - ); - this.setInstance(); - } - - public override destroy(): void { - super.destroy(); - this.instance !== undefined && this.instance.destroy(); - } - - public source(): string | undefined { - return this.instance.source(); - } - - public files(): string[] | string | undefined { - if (this.instance instanceof File.Configuration) { - return this.instance.filename(); - } else if (this.instance instanceof Concat.Configuration) { - return this.instance.files(); - } else { - return undefined; - } - } - - public change(origin: Declaration): void { - this.overwrite({ [origin.alias()]: origin.configuration }); - } - - public desc(): IOriginDetails { - return this.instance.desc(); - } - - public title(): string { - const desc = this.desc().major; - if (desc.trim() === '') { - if (this.instance instanceof File.Configuration) { - return File.Configuration.desc().major; - } else if (this.instance instanceof Concat.Configuration) { - return Concat.Configuration.desc().major; - } else if (this.instance instanceof Stream.Configuration) { - return Stream.Configuration.desc().major; - } else { - throw new Error(`Origin type isn't supported yet`); - } - } else { - return desc; - } - } - - public asJob(): IJob { - return this.instance.asJob(); - } - - public override isSdeSupported(): boolean { - return this.instance.isSdeSupported(); - } - - public getSupportedParsers(): Parser.Reference[] { - return this.instance.getSupportedParsers(); - } - - public as( - Ref: { new (...args: any[]): Declaration | Stream.Stream.Declaration } & Alias, - ): T | undefined { - if (this.instance.alias() === Ref.alias()) { - return this.instance as T; - } - if (typeof (this.instance as any).as === 'function') { - return (this.instance as any).as(Ref); - } - return undefined; - } - - public nature(): OriginNature { - if (this.instance instanceof Stream.Configuration) { - return this.instance.instance.instance; - } else { - return this.instance; - } - } - - public getNatureAlias(): Context | Stream.Stream.Source { - if (this.instance instanceof Stream.Configuration) { - return this.instance.instance.instance.alias(); - } else { - return this.instance.alias(); - } - } - - public set(): { - alias(alias?: string): void; - } { - return { - // Change signature of source - alias: (alias?: string): void => { - this.instance.set().alias(alias); - }, - }; - } - - public override storable(): IConfiguration { - return { [this.instance.alias()]: this.instance.storable() }; - } - - public override hash(): number { - return this.instance.hash(); - } -} diff --git a/application/platform/types/observe/origin/plugin.ts b/application/platform/types/observe/origin/plugin.ts deleted file mode 100644 index e9f2604596..0000000000 --- a/application/platform/types/observe/origin/plugin.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Configuration as Base, ConfigurationStaticDesc } from '../configuration'; -import { OriginDetails, Job, IOriginDetails, IJob, OriginType, IList } from '../description'; -import { Context, SourceUuid } from './index'; -import { Statics } from '../../../env/decorators'; -import { unique } from '../../../env/sequence'; -import { error } from '../../../log/utils'; - -import * as Types from '../types'; -import * as Sde from '../sde'; -import * as Parser from '../parser'; -import * as str from '../../../env/str'; - -export type IConfiguration = [SourceUuid, Types.Plugin.PluginDirPath]; -const CONFIG_LEN = 2 as const; - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Parser.Support, Job -{ - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return [unique(), '']; - } - - static alias(): Context { - return Context.Plugin; - } - - /// Origin (source) plugin will describe itself in run-time. fields `major` will be replaced - // with plugin name; `minor` with an addition data. - /// It will work simular to `WrappedParserRef` (application/client/src/app/ui/tabs/observe/state.ts) - static desc(): IList { - return { - major: '', - minor: '', - icon: undefined, - }; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (configuration instanceof Array && configuration.length === CONFIG_LEN) { - str.asNotEmptyString( - configuration[0], - `SourceUuid isn't found: ${configuration[0]}`, - ); - str.asNotEmptyString( - configuration[1], - `Pluging Directory Path isn't found: ${configuration[1]}`, - ); - } else { - throw new Error( - `Source "${Context.Plugin}" should be represented as an array, len = ${CONFIG_LEN}`, - ); - } - - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return Configuration.desc().major.trim() !== ''; - } - - public source(): string | undefined { - return this.configuration[0]; - } - - public set(): { - alias(alias?: string): void; - pluginDir(dir: string): void; - } { - return { - alias: (alias?: string): void => { - this.configuration[0] = alias === undefined ? unique() : alias; - }, - pluginDir: (dir: string): void => { - this.configuration[1] = dir; - }, - }; - } - - public override hash(): number { - return str.hash(this.dirPath()); - } - - public dirPath(): Types.Plugin.PluginDirPath { - return this.configuration[1]; - } - - public getSupportedParsers(): Parser.Reference[] { - // Returns all - return [ - Parser.Dlt.Configuration, - Parser.SomeIp.Configuration, - Parser.Text.Configuration, - Parser.Plugin.Configuration, - ]; - } - public asJob(): IJob { - return { - name: 'plugin source', - desc: 'plugin source TODO', - icon: undefined, - }; - } - public desc(): IOriginDetails { - return { - major: 'Plugin source instance major TODO', - minor: 'Plugin source instance minor TODO', - icon: undefined, - type: OriginType.plugin, - action: 'Load TODO', - state: { - running: 'load TODO', - stopped: 'TODO', - }, - }; - } -} diff --git a/application/platform/types/observe/origin/stream.ts b/application/platform/types/observe/origin/stream.ts deleted file mode 100644 index 6634c1573b..0000000000 --- a/application/platform/types/observe/origin/stream.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { error } from '../../../log/utils'; -import { Configuration as Base, ConfigurationStatic, Linked } from '../configuration'; -import { Context, SourceUuid } from './index'; -import { OriginDetails, IOriginDetails, IList, Job, IJob } from '../description'; -import { Statics } from '../../../env/decorators'; -import { unique } from '../../../env/sequence'; -import { Alias } from '../../env/types'; -import { Mutable } from '../../unity/mutable'; - -import * as str from '../../../env/str'; -import * as Stream from './stream/index'; -import * as Parser from '../parser/index'; -import * as Sde from '../sde'; - -export * as str from '../../../env/str'; -export * as Stream from './stream/index'; -export * as Parser from '../parser/index'; -export * as Sde from '../sde'; - -export type IConfiguration = [SourceUuid, Stream.IConfiguration]; - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Parser.Support, Job -{ - static desc(): IList { - return { - major: `Stream`, - minor: 'Streaming', - icon: 'files', - }; - } - - static alias(): Context { - return Context.Stream; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (configuration instanceof Array && configuration.length === 2) { - str.asNotEmptyString( - configuration[0], - `SourceUuid isn't found: ${configuration[0]}`, - ); - const error = Stream.Configuration.validate(configuration[1]); - return error instanceof Error ? error : configuration; - } else { - throw new Error( - `Source "${Context.Stream}" should be represented as an array, len = 2.`, - ); - } - } catch (e) { - return new Error(error(e)); - } - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return [unique(), Stream.Configuration.initial()]; - } - - protected setInstance(): Configuration { - if (this.instance !== undefined) { - this.instance.setRef(this.configuration[1]); - return this; - } else { - const instance = new Stream.Configuration(this.configuration[1], { - watcher: this.watcher, - overwrite: (config: Stream.IConfiguration) => { - this.configuration[1] = config; - return this.configuration[1]; - }, - }); - if (instance instanceof Error) { - throw instance; - } - (this as Mutable).instance = instance; - } - return this; - } - - public readonly instance!: Stream.Configuration; - - constructor(configuration: IConfiguration, linked: Linked | undefined) { - super(configuration, linked); - this.register( - this.watcher.subscribe(() => { - this.setInstance(); - }), - ); - this.setInstance(); - } - - public source(): string | undefined { - return this.configuration[0]; - } - - public override destroy(): void { - super.destroy(); - this.instance !== undefined && this.instance.destroy(); - } - - public change(stream: Stream.Declaration): void { - this.instance.change().byDeclaration(stream); - this.configuration[1] = this.instance.configuration; - } - - public desc(): IOriginDetails { - return this.instance.desc(); - } - - public asJob(): IJob { - return this.instance.asJob(); - } - - public override isSdeSupported(): boolean { - return this.instance.isSdeSupported(); - } - - public getSupportedParsers(): Parser.Reference[] { - return this.instance.getSupportedParsers(); - } - - public set(): { - alias(alias?: string): void; - } { - return { - alias: (alias?: string): void => { - this.configuration[0] = alias === undefined ? unique() : alias; - }, - }; - } - - public as( - Ref: { new (...args: any[]): Stream.Declaration } & Alias, - ): T | undefined { - return this.instance.instance.alias() === Ref.alias() - ? (this.instance.instance as T) - : undefined; - } - - public override storable(): IConfiguration { - return [this.configuration[0], this.instance.storable()]; - } - - public override hash(): number { - return this.instance.hash(); - } -} diff --git a/application/platform/types/observe/origin/stream/index.ts b/application/platform/types/observe/origin/stream/index.ts deleted file mode 100644 index 4541c45794..0000000000 --- a/application/platform/types/observe/origin/stream/index.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { - Configuration as Base, - ConfigurationStatic, - ReferenceDesc, - Linked, -} from '../../configuration'; -import { OriginDetails, IOriginDetails, Job, IJob } from '../../description'; -import { Statics } from '../../../../env/decorators'; -import { Mutable } from '../../../unity/mutable'; -import { Alias } from '../../../env/types'; - -import * as Process from './process'; -import * as Serial from './serial'; -import * as TCP from './tcp'; -import * as UDP from './udp'; -import * as Parser from '../../parser'; -import * as Sde from '../../sde'; - -export * as Process from './process'; -export * as Serial from './serial'; -export * as TCP from './tcp'; -export * as UDP from './udp'; - -export type Reference = - | ReferenceDesc - | ReferenceDesc - | ReferenceDesc - | ReferenceDesc; - -export enum Source { - TCP = 'TCP', - UDP = 'UDP', - Serial = 'Serial', - Process = 'Process', -} - -export type IDeclaration = - | Serial.IConfiguration - | Process.IConfiguration - | TCP.IConfiguration - | UDP.IConfiguration; - -export type Declaration = - | Serial.Configuration - | Process.Configuration - | TCP.Configuration - | UDP.Configuration; - -export interface IConfiguration { - [Source.Serial]?: Serial.IConfiguration; - [Source.Process]?: Process.IConfiguration; - [Source.TCP]?: TCP.IConfiguration; - [Source.UDP]?: UDP.IConfiguration; -} - -export const REGISTER: { - [key: string]: Reference; -} = { - [Source.Process]: Process.Configuration, - [Source.Serial]: Serial.Configuration, - [Source.TCP]: TCP.Configuration, - [Source.UDP]: UDP.Configuration, -}; - -export function getAllRefs(): Reference[] { - return Object.keys(REGISTER).map((key) => REGISTER[key]); -} - -export abstract class Support { - public abstract getSupportedStream(): Reference[]; -} - -export const DEFAULT = TCP.Configuration; - -export function getByAlias(alias: Source, configuration?: IDeclaration): Declaration { - const Ref: Reference = REGISTER[alias]; - if (Ref === undefined) { - throw new Error(`Unknown stream: ${alias}`); - } - return new Ref(configuration === undefined ? Ref.initial() : configuration, undefined); -} - -export function getAliasByConfiguration(configuration: IConfiguration): Source { - let detected: Source | undefined; - Object.keys(configuration).forEach((key) => { - if (REGISTER[key as Source] !== undefined) { - if (detected !== undefined) { - throw new Error( - `Configuration has a multiple source defined: ${JSON.stringify(configuration)}`, - ); - } - detected = key as Source; - } - }); - if (detected === undefined) { - throw new Error( - `Configuration doesn't have any known source: ${JSON.stringify(configuration)}`, - ); - } - return detected; -} - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Parser.Support, Job -{ - static alias(): Source { - //TODO: alias should be defined for holders. Same for Parser as holder of parsers - return Source.Process; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - if ( - Object.keys(REGISTER) - .map((k) => configuration[k as Source]) - .filter((v) => v !== undefined).length === 0 - ) { - return new Error(`Stream transport isn't defined`); - } - let error: Error | undefined; - Object.keys(REGISTER).forEach((key) => { - if (error instanceof Error) { - return; - } - const config: any = configuration[key as Source]; - if (config === undefined) { - return; - } - const err = REGISTER[key as Source].validate(config); - if (err instanceof Error) { - error = err; - } else { - error = undefined; - } - }); - return error instanceof Error ? error : configuration; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - [DEFAULT.alias()]: DEFAULT.initial(), - }; - } - - protected getSourceKey(): Source | undefined { - let found: Source | undefined; - Object.keys(REGISTER).forEach((key) => { - if (found !== undefined) { - return; - } - if (this.configuration[key as Source] === undefined) { - return; - } - found = key as Source; - }); - return found; - } - - protected setInstance(): Configuration { - const source = this.getSourceKey(); - if (source === undefined) { - throw new Error(`Configuration of stream doesn't have definition of known source.`); - } - if (this.configuration[source] === undefined) { - throw new Error(`No source is defined in stream configuration`); - } - if (this.instance !== undefined && this.instance.alias() === source) { - this.instance.setRef(this.configuration[source]); - return this; - } - this.instance !== undefined && this.instance.destroy(); - (this as Mutable).instance = new REGISTER[source]( - this.configuration[source], - { - watcher: this.watcher, - overwrite: (config: IConfiguration) => { - this.overwrite({ [source]: config }); - return this.configuration[source]; - }, - }, - ); - return this; - } - - public readonly instance!: Declaration; - - constructor(configuration: IConfiguration, linked: Linked | undefined) { - super(configuration, linked); - this.register( - this.watcher.subscribe(() => { - this.setInstance(); - }), - ); - this.setInstance(); - } - - public override destroy(): void { - super.destroy(); - this.instance !== undefined && this.instance.destroy(); - } - - public change(): { - byConfiguration(configuration: IConfiguration): void; - byDeclaration(declaration: Declaration): void; - byReference(reference: Reference): void; - } { - return { - byConfiguration: (configuration: IConfiguration): void => { - this.overwrite(configuration); - }, - byDeclaration: (stream: Declaration): void => { - this.overwrite({ [stream.alias()]: stream.configuration }); - }, - byReference: (Ref: Reference): void => { - this.overwrite({ [Ref.alias()]: new Ref(Ref.initial(), this.linked) }); - }, - }; - } - - public desc(): IOriginDetails { - return this.instance.desc(); - } - - public asJob(): IJob { - return this.instance.asJob(); - } - - public override isSdeSupported(): boolean { - return this.instance.isSdeSupported(); - } - - public getSupportedParsers(): Parser.Reference[] { - return this.instance.getSupportedParsers(); - } - - public as(Ref: { new (...args: any[]): Declaration } & Alias): T | undefined { - if (this.instance.alias() === Ref.alias()) { - return this.instance as T; - } - if (typeof (this.instance as any).as === 'function') { - return (this.instance as any).as(Ref); - } - return undefined; - } - - public override storable(): IConfiguration { - return { [this.instance.alias()]: this.instance.storable() }; - } - - public override hash(): number { - return this.instance.hash(); - } -} diff --git a/application/platform/types/observe/origin/stream/process/index.ts b/application/platform/types/observe/origin/stream/process/index.ts deleted file mode 100644 index 1d895b4536..0000000000 --- a/application/platform/types/observe/origin/stream/process/index.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { error } from '../../../../../log/utils'; -import { Source } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../../configuration'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../../../description'; -import { Statics } from '../../../../../env/decorators'; - -import * as obj from '../../../../../env/obj'; -import * as Parser from '../../../parser'; -import * as Sde from '../../../sde'; -import * as str from '../../../../../env/str'; - -export interface IConfiguration { - command: string; - cwd: string; - envs: { [key: string]: string }; -} - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Job -{ - public MARKER = 'application/platform/types/observe/origin/stream/process/index.ts'; - - static desc(): IList { - return { - major: `Terminal`, - minor: 'Executing Terminal Command', - icon: 'web_asset', - }; - } - - static alias(): Source { - return Source.Process; - } - - static sterilizeEnvVars(envs: { [key: string]: any }): { [key: string]: string } { - Object.keys(envs).forEach((key: string) => { - if (typeof envs[key] === 'string') { - return; - } - if (typeof envs[key].toString === 'function') { - envs[key] = envs[key].toString(); - } else { - envs[key] = JSON.stringify(envs[key]); - } - }); - return envs; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - obj.getAsNotEmptyString(configuration, 'command'); - obj.getAsString(configuration, 'cwd'); - obj.getAsObjWithPrimitives(configuration, 'envs'); - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - command: '', - cwd: '', - envs: {}, - }; - } - - public desc(): IOriginDetails { - return { - major: `${this.configuration.command}`, - minor: this.configuration.cwd === '' ? 'no defined cwd' : this.configuration.cwd, - icon: 'web_asset', - action: 'Execute', - type: OriginType.command, - state: { - running: 'spawning', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: `${this.configuration.command}`, - desc: `${this.configuration.command}`, - icon: 'web_asset', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - return [Parser.Text.Configuration, Parser.Plugin.Configuration]; - } - - public override storable(): IConfiguration { - const sterilized = this.sterilized(); - return sterilized; - } - - public override hash(): number { - return str.hash(`${this.configuration.command};${this.configuration.cwd}`); - } -} diff --git a/application/platform/types/observe/origin/stream/serial/index.ts b/application/platform/types/observe/origin/stream/serial/index.ts deleted file mode 100644 index 78bc4aa60a..0000000000 --- a/application/platform/types/observe/origin/stream/serial/index.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { error } from '../../../../../log/utils'; -import { Source } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../../configuration'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../../../description'; -import { Statics } from '../../../../../env/decorators'; - -import * as obj from '../../../../../env/obj'; -import * as Parser from '../../../parser'; -import * as Sde from '../../../sde'; -import * as str from '../../../../../env/str'; - -export interface IConfiguration { - path: string; - baud_rate: number; - data_bits: number; - flow_control: number; - parity: number; - stop_bits: number; - send_data_delay: number; - exclusive: boolean; -} - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Job -{ - public MARKER = 'application/platform/types/observe/origin/stream/serial/index.ts'; - - static desc(): IList { - return { - major: `Serial Port`, - minor: 'Connection to Serial Port', - icon: 'import_export', - }; - } - - static alias(): Source { - return Source.Serial; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - obj.getAsNotEmptyString(configuration, 'path'); - obj.getAsValidNumber(configuration, 'baud_rate'); - obj.getAsValidNumber(configuration, 'data_bits'); - obj.getAsValidNumber(configuration, 'flow_control'); - obj.getAsValidNumber(configuration, 'parity'); - obj.getAsValidNumber(configuration, 'stop_bits'); - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - baud_rate: 115200, - data_bits: 8, - flow_control: 0, - parity: 0, - path: '', - stop_bits: 1, - send_data_delay: 0, - exclusive: true, - }; - } - - public desc(): IOriginDetails { - return { - major: this.configuration.path, - minor: `Baud Rate: ${this.configuration.baud_rate}`, - icon: 'import_export', - action: 'Connect', - type: OriginType.serial, - state: { - running: 'listening', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: `Serial: ${this.configuration.path}`, - desc: `Baud Rate: ${this.configuration.baud_rate}`, - icon: 'import_export', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - return [Parser.Text.Configuration, Parser.Plugin.Configuration]; - } - - public override hash(): number { - return str.hash( - `${this.configuration.path};${this.configuration.baud_rate};${this.configuration.data_bits};${this.configuration.flow_control};${this.configuration.parity};${this.configuration.stop_bits}`, - ); - } -} diff --git a/application/platform/types/observe/origin/stream/tcp/index.ts b/application/platform/types/observe/origin/stream/tcp/index.ts deleted file mode 100644 index 58c5166a89..0000000000 --- a/application/platform/types/observe/origin/stream/tcp/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Source } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../../configuration'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../../../description'; -import { Statics } from '../../../../../env/decorators'; - -import * as Parser from '../../../parser'; -import * as Sde from '../../../sde'; -import * as Ip from '../../../../../env/ipaddr'; -import * as str from '../../../../../env/str'; - -export interface IConfiguration { - bind_addr: string; -} - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Job -{ - public MARKER = 'application/platform/types/observe/origin/stream/tcp/index.ts'; - - static desc(): IList { - return { - major: `TCP`, - minor: 'TCP Connection', - icon: 'network_wifi_3_bar', - }; - } - - static alias(): Source { - return Source.TCP; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - return Ip.anyIPAddr(configuration.bind_addr) - ? configuration - : new Error(`Invalid IP address`); - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - bind_addr: '', - }; - } - - public desc(): IOriginDetails { - return { - major: this.configuration.bind_addr, - minor: '', - icon: 'network_wifi_3_bar', - type: OriginType.net, - action: 'Connect', - state: { - running: 'listening', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: `TCP: ${this.configuration.bind_addr}`, - desc: `Connecting to ${this.configuration.bind_addr} via TCP`, - icon: 'network_wifi_3_bar', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - return [Parser.Dlt.Configuration, Parser.Plugin.Configuration]; - } - - public override hash(): number { - return str.hash(`${this.configuration.bind_addr}`); - } -} diff --git a/application/platform/types/observe/origin/stream/udp/index.ts b/application/platform/types/observe/origin/stream/udp/index.ts deleted file mode 100644 index 61a2f5124b..0000000000 --- a/application/platform/types/observe/origin/stream/udp/index.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { error } from '../../../../../log/utils'; -import { Source } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../../configuration'; -import { OriginDetails, IOriginDetails, IList, Job, IJob, OriginType } from '../../../description'; -import { Statics } from '../../../../../env/decorators'; - -import * as obj from '../../../../../env/obj'; -import * as Parser from '../../../parser'; -import * as Sde from '../../../sde'; -import * as Ip from '../../../../../env/ipaddr'; -import * as str from '../../../../../env/str'; - -export interface Multicast { - multiaddr: string; - interface: string | undefined; -} - -export interface IConfiguration { - bind_addr: string; - multicast: Multicast[]; -} - -@Statics>() -export class Configuration - extends Base - implements OriginDetails, Sde.Support, Job -{ - public MARKER = 'application/platform/types/observe/origin/stream/udp/index.ts'; - - static desc(): IList { - return { - major: `UDP`, - minor: 'UDP Connection', - icon: 'network_wifi_3_bar', - }; - } - - static alias(): Source { - return Source.UDP; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (!Ip.anyIPAddr(configuration.bind_addr)) { - return new Error(`Invalid binding address`); - } - obj.getAsArray(configuration, 'multicast'); - return configuration.multicast - .map((multicast: Multicast) => { - return ( - Ip.anyIPAddr(multicast.multiaddr) && - (Ip.isValidIPv4(multicast.interface) || Ip.isValidIPv6(multicast.interface)) - ); - }) - .filter((r) => !r).length === 0 - ? configuration - : new Error(`Invalid multicast definition`); - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - bind_addr: '', - multicast: [], - }; - } - - public desc(): IOriginDetails { - return { - major: this.configuration.bind_addr, - minor: - this.configuration.multicast.length === 0 - ? '' - : this.configuration.multicast.map((m) => m.multiaddr).join(', '), - action: 'Connect', - icon: 'network_wifi_3_bar', - type: OriginType.net, - state: { - running: 'listening', - stopped: '', - }, - }; - } - - public asJob(): IJob { - return { - name: `UDP: ${this.configuration.bind_addr}`, - desc: - this.configuration.multicast.length === 0 - ? `Connecting to ${this.configuration.bind_addr} via UDP (no multicasts)` - : this.configuration.multicast.map((m) => m.multiaddr).join(', '), - icon: 'network_wifi_3_bar', - }; - } - - public getSupportedParsers(): Parser.Reference[] { - return [Parser.Dlt.Configuration, Parser.SomeIp.Configuration, Parser.Plugin.Configuration]; - } - - public override hash(): number { - return str.hash( - `${this.configuration.bind_addr};${this.configuration.multicast - .map((m) => `${m.multiaddr};${m.interface}`) - .join(';')}`, - ); - } -} diff --git a/application/platform/types/observe/parser/dlt/index.ts b/application/platform/types/observe/parser/dlt/index.ts deleted file mode 100644 index 4eddc0d22b..0000000000 --- a/application/platform/types/observe/parser/dlt/index.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { error } from '../../../../log/utils'; -import { Protocol } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../configuration'; -import { Statics } from '../../../../env/decorators'; -import { List, IList } from '../../description'; - -import * as Stream from '../../origin/stream/index'; -import * as Files from '../../types/file'; -import * as obj from '../../../../env/obj'; -import * as Origin from '../../origin/index'; -import * as str from '../../../../env/str'; - -export function getLogLevelName(level: number): string { - const name = (DltLogLevelNames as Record)[level]; - return name === undefined ? 'unknown' : name; -} - -export const DltLogLevelNames = { - 1: 'Fatal', - 2: 'Error', - 3: 'Warn', - 4: 'Info', - 5: 'Debug', - 6: 'Verbose', -}; - -export enum LogLevel { - Fatal = 1, - Error = 2, - Warn = 3, - Info = 4, - Debug = 5, - Verbose = 6, -} - -export interface IFilters { - min_log_level: LogLevel | undefined; - app_ids: string[] | undefined; - ecu_ids: string[] | undefined; - context_ids: string[] | undefined; - app_id_count: number; - context_id_count: number; -} - -export interface IConfiguration { - filter_config: IFilters | undefined; - fibex_file_paths: string[] | undefined; - with_storage_header: boolean; - tz: string | undefined; -} - -@Statics>() -export class Configuration - extends Base - implements List, Stream.Support, Files.Support, Origin.OnChange -{ - static desc(): IList { - return { - major: 'DLT', - minor: 'Parsing DLT tracers', - icon: undefined, - }; - } - - static alias(): Protocol { - return Protocol.Dlt; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - obj.getAsBool(configuration, 'with_storage_header'); - obj.getAsNotEmptyStringsArrayOrUndefined(configuration, 'fibex_file_paths'); - obj.getAsObjOrUndefined(configuration, 'filter_config'); - obj.getAsNotEmptyStringOrAsUndefined(configuration, 'tz'); - const filter_config = configuration.filter_config; - if (filter_config !== undefined) { - obj.getAsValidNumber(filter_config, 'min_log_level'); - obj.getAsNotEmptyStringsArrayOrUndefined(filter_config, 'app_ids'); - obj.getAsNotEmptyStringsArrayOrUndefined(filter_config, 'ecu_ids'); - obj.getAsNotEmptyStringsArrayOrUndefined(filter_config, 'context_ids'); - obj.getAsValidNumber(filter_config, 'app_id_count'); - obj.getAsValidNumber(filter_config, 'context_id_count'); - } - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - filter_config: undefined, - fibex_file_paths: [], - with_storage_header: true, - tz: undefined, - }; - } - - protected getDefaultsFilters(): IFilters { - return { - min_log_level: LogLevel.Verbose, - app_ids: undefined, - ecu_ids: undefined, - context_ids: undefined, - app_id_count: 0, - context_id_count: 0, - }; - } - - public onOriginChange(origin: Origin.Configuration): void { - if (origin.instance instanceof Origin.Stream.Configuration) { - this.configuration.with_storage_header = false; - } else if (origin.instance instanceof Origin.File.Configuration) { - this.configuration.with_storage_header = - origin.instance.filetype() === Files.FileType.Binary; - } else if (origin.instance instanceof Origin.Concat.Configuration) { - // TODO: could be issue if concat configuration have different types - // of files - const types = origin.instance.filetypes(); - this.configuration.with_storage_header = - types.length === 0 ? true : types[0] === Files.FileType.Binary; - } else { - throw new Error(`Not implemented usecase for DLT parser onOriginChange`); - } - } - - public desc(): IList { - return Configuration.desc(); - } - - public setDefaultsFilterConfig(): void { - if (this.configuration.filter_config !== undefined) { - return; - } - this.configuration.filter_config = this.getDefaultsFilters(); - } - - public dropFilterConfigIfPossible(): void { - if (this.configuration.filter_config === undefined) { - return; - } - if (this.configuration.filter_config.min_log_level !== LogLevel.Verbose) { - return; - } - if ( - this.configuration.filter_config.app_ids !== undefined && - this.configuration.filter_config.app_ids.length > 0 - ) { - return; - } - if ( - this.configuration.filter_config.context_ids !== undefined && - this.configuration.filter_config.context_ids.length > 0 - ) { - return; - } - if ( - this.configuration.filter_config.ecu_ids !== undefined && - this.configuration.filter_config.ecu_ids.length > 0 - ) { - return; - } - this.configuration.filter_config = undefined; - } - - public override hash(): number { - const filters = - this.configuration.filter_config === undefined - ? this.getDefaultsFilters() - : this.configuration.filter_config; - return str.hash( - `dlt:${(this.configuration.fibex_file_paths === undefined - ? [] - : this.configuration.fibex_file_paths - ).join(';')};${this.configuration.with_storage_header};${this.configuration.tz};${ - filters.min_log_level - };${filters.ecu_ids?.length};${filters.app_ids?.length};${filters.context_ids?.length}`, - ); - } -} diff --git a/application/platform/types/observe/parser/index.ts b/application/platform/types/observe/parser/index.ts deleted file mode 100644 index 985e45be5d..0000000000 --- a/application/platform/types/observe/parser/index.ts +++ /dev/null @@ -1,235 +0,0 @@ -import { - Configuration as Base, - ConfigurationStatic, - ReferenceDesc, - Linked, -} from '../configuration'; -import { Statics } from '../../../env/decorators'; -import { List, IList } from '../description'; -import { Mutable } from '../../unity/mutable'; -import { Alias } from '../../env/types'; - -import * as Dlt from './dlt'; -import * as SomeIp from './someip'; -import * as Text from './text'; -import * as Plugin from './plugin'; - -export * as Dlt from './dlt'; -export * as SomeIp from './someip'; -export * as Text from './text'; -export * as Plugin from './plugin'; -import * as Stream from '../origin/stream/index'; -import * as Files from '../types/file'; -import * as Origin from '../origin/index'; - -export type Reference = - | ReferenceDesc - | ReferenceDesc - | ReferenceDesc - | ReferenceDesc; - -export enum Protocol { - Dlt = 'Dlt', - SomeIp = 'SomeIp', - Text = 'Text', - Plugin = 'Plugin', -} - -export type IDeclaration = - | Text.IConfiguration - | Dlt.IConfiguration - | SomeIp.IConfiguration - | Plugin.IConfiguration; - -export type Declaration = - | Text.Configuration - | Dlt.Configuration - | SomeIp.Configuration - | Plugin.Configuration; - -export interface IConfiguration { - [Protocol.Dlt]?: Dlt.IConfiguration; - [Protocol.SomeIp]?: SomeIp.IConfiguration; - [Protocol.Text]?: Text.IConfiguration; - [Protocol.Plugin]?: Plugin.IConfiguration; -} - -const REGISTER: { - [key: string]: Reference; -} = { - [Protocol.Dlt]: Dlt.Configuration, - [Protocol.SomeIp]: SomeIp.Configuration, - [Protocol.Text]: Text.Configuration, - [Protocol.Plugin]: Plugin.Configuration, -}; - -export function tryAsEmbedded(alias: Protocol | string): Protocol | undefined { - if (REGISTER[alias]) { - return alias as Protocol; - } - return undefined; -} - -export function getAllRefs(): Reference[] { - return Object.keys(REGISTER).map((key) => REGISTER[key]); -} - -export abstract class Support { - public abstract getSupportedParsers(): Reference[]; -} - -const DEFAULT = Text.Configuration; - -export function getByAlias(alias: Protocol): Declaration { - const Ref: Reference = REGISTER[alias]; - if (Ref === undefined) { - throw new Error(`Unknown parser: ${alias}`); - } - return new Ref(Ref.initial(), undefined); -} - -export function suggestParserByFileExt(filename: string): Reference | undefined { - const normalized = filename.toLowerCase().trim(); - if (normalized.endsWith('.dlt')) { - return Dlt.Configuration; - } else if (normalized.endsWith('.pcapng')) { - return Dlt.Configuration; - } else { - return undefined; - } -} - -@Statics>() -export class Configuration - extends Base - implements List, Stream.Support, Files.Support, Origin.OnChange -{ - static alias(): Protocol { - throw new Error(`Alias of parsers holder should be used`); - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - if ( - Object.keys(REGISTER) - .map((k) => configuration[k as Protocol]) - .filter((v) => v !== undefined).length === 0 - ) { - return new Error(`Stream transport isn't defined`); - } - let error: Error | undefined; - Object.keys(REGISTER).forEach((key) => { - if (error instanceof Error) { - return; - } - const config: any = configuration[key as Protocol]; - if (config === undefined) { - return; - } - // Error with "never" comes because text parser has settings NULL - const err = REGISTER[key as Protocol].validate(config as never); - if (err instanceof Error) { - error = err; - } else { - error = undefined; - } - }); - return error instanceof Error ? error : configuration; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - [DEFAULT.alias()]: DEFAULT.initial(), - }; - } - - protected getProtocolKey(): Protocol | undefined { - let found: Protocol | undefined; - Object.keys(REGISTER).forEach((key) => { - if (found !== undefined) { - return; - } - if (this.configuration[key as Protocol] === undefined) { - return; - } - found = key as Protocol; - }); - return found; - } - - protected setInstance(): Configuration { - const protocol = this.getProtocolKey(); - if (protocol === undefined) { - throw new Error(`Configuration of stream doesn't have definition of known protocol.`); - } - if (this.instance !== undefined && this.instance.alias() === protocol) { - this.instance.setRef(this.configuration[protocol]); - return this; - } - this.instance !== undefined && this.instance.destroy(); - (this as Mutable).instance = new REGISTER[protocol]( - this.configuration[protocol], - { - watcher: this.watcher, - overwrite: (config: IConfiguration) => { - this.overwrite(config); - return this.configuration[protocol]; - }, - }, - ); - return this; - } - - public readonly instance!: Declaration; - - constructor(configuration: IConfiguration, linked: Linked | undefined) { - super(configuration, linked); - this.register( - this.watcher.subscribe(() => { - this.setInstance(); - }), - ); - this.setInstance(); - } - - public override destroy(): void { - super.destroy(); - this.instance !== undefined && this.instance.destroy(); - } - - public onOriginChange(origin: Origin.Configuration): void { - this.instance.onOriginChange(origin); - } - - public change(parser: Declaration): void { - this.overwrite({ [parser.alias()]: parser.configuration }); - } - - public desc(): IList { - return this.instance.desc(); - } - - public override getSupportedStream(): Stream.Reference[] { - return this.instance.getSupportedStream(); - } - - public override alias(): Protocol { - return this.instance.alias(); - } - - public override getSupportedFileType(): Files.FileType[] { - return this.instance.getSupportedFileType(); - } - - public as(Ref: { new (...args: any[]): Declaration } & Alias): T | undefined { - return this.instance.alias() === Ref.alias() ? (this.instance as T) : undefined; - } - - public override storable(): IConfiguration { - return { [this.instance.alias()]: this.instance.storable() }; - } - - public override hash(): number { - return this.instance.hash(); - } -} diff --git a/application/platform/types/observe/parser/plugin/index.ts b/application/platform/types/observe/parser/plugin/index.ts deleted file mode 100644 index ca3f52a182..0000000000 --- a/application/platform/types/observe/parser/plugin/index.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { error } from '../../../../log/utils'; -import { Protocol } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../configuration'; -import { Statics } from '../../../../env/decorators'; -import { List, IList } from '../../description'; - -import * as str from '../../../../env/str'; -import * as Origin from '../../origin/index'; -import * as Stream from '../../origin/stream/index'; -import * as obj from '../../../../env/obj'; -import * as Files from '../../types/file'; - -import { PluginConfigItem, PluginConfigValue } from '../../../bindings/plugins'; - -export interface IConfiguration { - plugin_path: string; - plugin_configs: PluginConfigItem[]; -} - -@Statics>() -export class Configuration - extends Base - implements List, Stream.Support, Files.Support -{ - /// Plugin uses inner data to provide self description. In run-time fields - /// `major` will be replaced with plugin name; `minor` with an addition data - /// more details: `WrappedParserRef` (application/client/src/app/ui/tabs/observe/state.ts) - static desc(): IList { - return { - major: '', - minor: '', - icon: undefined, - }; - } - - static alias(): Protocol { - return Protocol.Plugin; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - const pluginPath = obj.getAsString(configuration, 'plugin_path'); - if (pluginPath.length == 0) { - return Error('No Valid Plugin Parser is selected.'); - } - - obj.getAsArray(configuration, 'plugin_configs'); - - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return Configuration.desc().major.trim() !== ''; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - plugin_path: '', - plugin_configs: [], - }; - } - - public desc(): IList { - return { - major: 'Plugin parser instance major TODO', - minor: 'Plugin parser instance minor TODO', - icon: undefined, - }; - } - - public onOriginChange(_origin: Origin.Configuration): void { - // Plugin parsers don't need to react on changing source at the current implementation. - } - - /** - * Extract the value from the configuration value item depending on its kind, - * returning a value that can represented as a formatted string. - */ - getConfigValue(value: PluginConfigValue): string | number | boolean { - if ('Boolean' in value) { - return value.Boolean; - } - - if ('Integer' in value) { - return value.Integer; - } - - if ('Float' in value) { - return value.Float; - } - - if ('Text' in value) { - return value.Text; - } - - if ('Files' in value) { - return value.Files.join(','); - } - - if ('Directories' in value) { - return value.Directories.join(','); - } - - if ('Dropdown' in value) { - return value.Dropdown; - } - - assertNever(value); - } - - public override hash(): number { - const configs = this.configuration.plugin_configs.map( - (item) => `${item.id}:${this.getConfigValue(item.value)}`, - ); - return str.hash(`${this.configuration.plugin_path}; ${configs.join(';')}`); - } -} - -/** - * Helper function to ensure the remaining item is never for exhausting matching at compile time, - * and throwing an unhandled error at runtime. - */ -function assertNever(nev: never): never { - throw new Error(`Unhandled case: ${JSON.stringify(nev)}`); -} diff --git a/application/platform/types/observe/parser/someip/index.ts b/application/platform/types/observe/parser/someip/index.ts deleted file mode 100644 index eca02fd97d..0000000000 --- a/application/platform/types/observe/parser/someip/index.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { error } from '../../../../log/utils'; -import { Protocol } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../configuration'; -import { Statics } from '../../../../env/decorators'; -import { List, IList } from '../../description'; - -import * as Stream from '../../origin/stream/index'; -import * as obj from '../../../../env/obj'; -import * as Files from '../../types/file'; -import * as Origin from '../../origin/index'; -import * as str from '../../../../env/str'; - -export interface SomeipStatistic { - /** Statistic on service-ids and related method-ids */ - services: SomeipStatisticItem[]; - /** Statistic on message-types and related return-codes */ - messages: SomeipStatisticItem[]; -} - -export interface SomeipStatisticItem { - item: SomeipStatisticDetail; - details: SomeipStatisticDetail[]; -} - -export interface SomeipStatisticDetail { - id: number; - num: number; -} - -export interface IConfiguration { - fibex_file_paths: string[] | undefined; -} - -@Statics>() -export class Configuration - extends Base - implements List, Stream.Support, Files.Support -{ - static desc(): IList { - return { - major: 'SomeIp', - minor: 'Parsing SomeIp tracers', - icon: undefined, - }; - } - - static alias(): Protocol { - return Protocol.SomeIp; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - obj.getAsNotEmptyStringsArrayOrUndefined(configuration, 'fibex_file_paths'); - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return { - fibex_file_paths: [], - }; - } - - public onOriginChange(_origin: Origin.Configuration): void { - //Do nothing - } - - public desc(): IList { - return Configuration.desc(); - } - - public override hash(): number { - return str.hash( - `someip:${(this.configuration.fibex_file_paths === undefined - ? [] - : this.configuration.fibex_file_paths - ).join(';')}`, - ); - } -} diff --git a/application/platform/types/observe/parser/text/index.ts b/application/platform/types/observe/parser/text/index.ts deleted file mode 100644 index 5a779c9fab..0000000000 --- a/application/platform/types/observe/parser/text/index.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { error } from '../../../../log/utils'; -import { Protocol } from '../index'; -import { Configuration as Base, ConfigurationStaticDesc } from '../../configuration'; -import { Statics } from '../../../../env/decorators'; -import { List, IList } from '../../description'; - -import * as str from '../../../../env/str'; -import * as Origin from '../../origin/index'; -import * as Stream from '../../origin/stream/index'; -import * as Files from '../../types/file'; - -export type IConfiguration = null; - -@Statics>() -export class Configuration - extends Base - implements List, Stream.Support, Files.Support -{ - static desc(): IList { - return { - major: 'Plain Text', - minor: 'Plain Text Parser ', - icon: undefined, - }; - } - - static alias(): Protocol { - return Protocol.Text; - } - - static validate(configuration: IConfiguration): Error | IConfiguration { - try { - if (configuration !== null) { - throw new Error(`Text parser doesn't have any configuration; it should null`); - } - return configuration; - } catch (e) { - return new Error(error(e)); - } - } - - static inited(): boolean { - return true; - } - - // Gives initial settings. Not necessarily valid. - static initial(): IConfiguration { - return null; - } - - public onOriginChange(_origin: Origin.Configuration): void { - // Do nothing - } - - public desc(): IList { - return Configuration.desc(); - } - - public override hash(): number { - return str.hash(`text`); - } -} diff --git a/application/platform/types/observe/sde.ts b/application/platform/types/observe/sde.ts deleted file mode 100644 index a045fb7192..0000000000 --- a/application/platform/types/observe/sde.ts +++ /dev/null @@ -1,3 +0,0 @@ -export abstract class Support { - public abstract isSdeSupported(): boolean; -} diff --git a/application/platform/types/observe/types/file/index.ts b/application/platform/types/observe/types/file/index.ts deleted file mode 100644 index b3ec14e012..0000000000 --- a/application/platform/types/observe/types/file/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -export abstract class Support { - public abstract getSupportedFileType(): FileName[]; -} - -export enum FileType { - PcapNG = 'PcapNG', - PcapLegacy = 'PcapLegacy', - Text = 'Text', - Binary = 'Binary', - ParserPlugin = 'ParserPlugin', -} - -export function getFileTypeFrom(smth: unknown): FileType | Error { - switch (smth as FileType) { - case FileType.Binary: - return FileType.Binary; - case FileType.PcapNG: - return FileType.PcapNG; - case FileType.PcapLegacy: - return FileType.PcapLegacy; - case FileType.Text: - return FileType.Text; - case FileType.ParserPlugin: - return FileType.ParserPlugin; - default: - return new Error(`${smth} isn't FileType`); - } -} - -export function extname(filename: string): string { - const matches = filename.match(/\.[\w\d_]*$/gi); - if (matches !== null) { - return matches[0]; - } - return ''; -} - -export type FileName = string; diff --git a/application/platform/types/observe/types/index.ts b/application/platform/types/observe/types/index.ts deleted file mode 100644 index 139f68171c..0000000000 --- a/application/platform/types/observe/types/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * as File from './file'; -export * as Plugin from './plugin'; -export { ISourceLink } from './sourcelink'; diff --git a/application/platform/types/observe/types/plugin/index.ts b/application/platform/types/observe/types/plugin/index.ts deleted file mode 100644 index eee207940d..0000000000 --- a/application/platform/types/observe/types/plugin/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type PluginDirPath = string; diff --git a/application/platform/types/sessionsetup/index.ts b/application/platform/types/sessionsetup/index.ts deleted file mode 100644 index 772deadcfc..0000000000 --- a/application/platform/types/sessionsetup/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IJob } from '../observe'; -import { SessionSetup } from '../bindings'; - -export class SessionSetupHolder { - constructor(protected readonly options: SessionSetup) {} - - public asJob(): IJob { - return { - name: this.options.source.uuid, - desc: this.options.source.uuid, - icon: undefined, - }; - } -} diff --git a/application/platform/types/observe/types/sourcelink.ts b/application/platform/types/source.ts similarity index 100% rename from application/platform/types/observe/types/sourcelink.ts rename to application/platform/types/source.ts From 0060c03a2ff9519ed3b92ad4f12d4faa1c36292e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Sun, 6 Jul 2025 22:19:29 +0200 Subject: [PATCH 083/121] Resolve conflict on sources tracker --- .../session/src/handlers/observe/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index 8ea6381404..3fc6fc42bf 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -20,15 +20,19 @@ pub async fn observing( match &options.origin { SessionAction::File(..) => { let (desciptor, source, parser) = components.setup(&options)?; - let mut logs_buffer = - session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let mut logs_buffer = session::LogsBuffer::new( + state.clone(), + state.add_source(operation_api.id(), desciptor).await?, + ); let producer = MessageProducer::new(parser, source, &mut logs_buffer); Ok(session::run_producer(operation_api, state, producer, None, None).await?) } SessionAction::Source => { let (desciptor, source, parser) = components.setup(&options)?; - let mut logs_buffer = - session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let mut logs_buffer = session::LogsBuffer::new( + state.clone(), + state.add_source(operation_api.id(), desciptor).await?, + ); let producer = MessageProducer::new(parser, source, &mut logs_buffer); Ok(session::run_producer(operation_api, state, producer, None, rx_sde).await?) } @@ -37,8 +41,10 @@ pub async fn observing( for file in files { let (desciptor, source, parser) = components.setup(&options.inherit(SessionAction::File(file.to_owned())))?; - let mut logs_buffer = - session::LogsBuffer::new(state.clone(), state.add_source(desciptor).await?); + let mut logs_buffer = session::LogsBuffer::new( + state.clone(), + state.add_source(operation_api.id(), desciptor).await?, + ); let producer = MessageProducer::new(parser, source, &mut logs_buffer); session::run_producer(operation_api.clone(), state.clone(), producer, None, None) .await?; From abc6970cd94b266b6a46a7edda65f018eb10f806 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 7 Jul 2025 14:55:39 +0200 Subject: [PATCH 084/121] Fix jobs definition --- application/client/src/app/service/jobs/job.ts | 2 +- application/holder/src/service/jobs/job.ts | 9 +++++++-- application/platform/env/obj.ts | 7 +++++++ application/platform/ipc/event/state/job.ts | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/application/client/src/app/service/jobs/job.ts b/application/client/src/app/service/jobs/job.ts index 3633e1c66f..1c284567be 100644 --- a/application/client/src/app/service/jobs/job.ts +++ b/application/client/src/app/service/jobs/job.ts @@ -32,7 +32,7 @@ export class Job { this.session = validator.getAsNotEmptyStringOrAsUndefined(job, 'session'); this.name = validator.getAsNotEmptyStringOrAsUndefined(job, 'name'); this.desc = validator.getAsNotEmptyStringOrAsUndefined(job, 'desc'); - this.icon = validator.getAsNotEmptyStringOrAsUndefined(job, 'icon'); + this.icon = validator.getAsStringOrAsUndefined(job, 'icon'); this.progress = validator.getAsValidNumber(job, 'progress', { defaults: 0, max: 100, diff --git a/application/holder/src/service/jobs/job.ts b/application/holder/src/service/jobs/job.ts index c071b048bd..4a22ccbbb5 100644 --- a/application/holder/src/service/jobs/job.ts +++ b/application/holder/src/service/jobs/job.ts @@ -38,7 +38,7 @@ export class Job { this.session = validator.getAsNotEmptyStringOrAsUndefined(job, 'session'); this.name = validator.getAsNotEmptyStringOrAsUndefined(job, 'name'); this.desc = validator.getAsNotEmptyStringOrAsUndefined(job, 'desc'); - this.icon = validator.getAsNotEmptyStringOrAsUndefined(job, 'icon'); + this.icon = validator.getAsStringOrAsUndefined(job, 'icon'); this.progress = validator.getAsValidNumber(job, 'progress', { defaults: 0, max: 100, @@ -122,7 +122,12 @@ export class Job { name: this.name, desc: this.desc, progress: this.progress, - icon: this.icon, + icon: + this.icon !== undefined + ? this.icon.trim() === '' + ? undefined + : this.icon + : undefined, }), ); this._done(this); diff --git a/application/platform/env/obj.ts b/application/platform/env/obj.ts index e4cb4bb4cd..7d008c24e1 100644 --- a/application/platform/env/obj.ts +++ b/application/platform/env/obj.ts @@ -203,6 +203,13 @@ export function getAsNotEmptyStringOrAsUndefined(src: any, key: string): string } return src[key]; } +export function getAsStringOrAsUndefined(src: any, key: string): string | undefined { + if (typeof src[key] === 'string' && src[key].trim() !== '') { + return src[key]; + } else { + return undefined; + } +} export function getAsBool(src: any, key: string, defaults?: boolean): boolean { if (typeof src[key] !== 'boolean') { diff --git a/application/platform/ipc/event/state/job.ts b/application/platform/ipc/event/state/job.ts index 1926eed7d3..5b146005b6 100644 --- a/application/platform/ipc/event/state/job.ts +++ b/application/platform/ipc/event/state/job.ts @@ -28,7 +28,7 @@ export class Event extends SignatureRequirement { this.session = validator.getAsNotEmptyStringOrAsUndefined(input, 'session'); this.name = validator.getAsNotEmptyStringOrAsUndefined(input, 'name'); this.desc = validator.getAsNotEmptyStringOrAsUndefined(input, 'desc'); - this.icon = validator.getAsNotEmptyStringOrAsUndefined(input, 'icon'); + this.icon = validator.getAsStringOrAsUndefined(input, 'icon'); this.spinner = typeof input.spinner === 'boolean' ? input.spinner : undefined; } } From fca8ddff95ef77266e42b7be563a61a46935e3fb Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 7 Jul 2025 17:19:12 +0200 Subject: [PATCH 085/121] Update recent actions format --- application/client/src/app/service/recent.ts | 51 +--- .../client/src/app/service/recent/action.ts | 289 +++++++++--------- .../src/app/service/recent/compatibility.ts | 107 ------- application/client/src/app/service/session.ts | 1 - .../service/session/dependencies/stream.ts | 7 +- .../client/src/app/service/session/origin.ts | 35 ++- .../navigator/providers/provider.recent.ts | 4 +- .../src/app/ui/elements/recent/component.ts | 2 +- .../src/app/ui/elements/recent/state.ts | 2 +- 9 files changed, 201 insertions(+), 297 deletions(-) delete mode 100644 application/client/src/app/service/recent/compatibility.ts diff --git a/application/client/src/app/service/recent.ts b/application/client/src/app/service/recent.ts index 27236f3ff8..00dc6b755a 100644 --- a/application/client/src/app/service/recent.ts +++ b/application/client/src/app/service/recent.ts @@ -37,30 +37,6 @@ export class Service extends Implementation { } }) .filter((a) => a !== undefined) as Action[]; - const invalid = actions.filter((a) => a.compatibility.invalidUuid !== undefined); - if (invalid.length > 0) { - await this.delete(invalid.map((a) => a.compatibility.invalidUuid as string)) - .then(() => { - this.log().debug( - `${invalid.length} actions with invalid UUIDs has been removed`, - ); - }) - .catch((err: Error) => { - this.log().error( - `Fail to remove recent actions with invalid uuid: ${err.message}`, - ); - }); - } - const converted = actions.filter((a) => a.compatibility.converted); - if (converted.length > 0) { - await this.update(converted) - .then(() => { - this.log().debug(`${converted.length} converted actions has been updated`); - }) - .catch((err: Error) => { - this.log().error(`Fail to update converted recent actions: ${err.message}`); - }); - } return actions; } @@ -70,23 +46,21 @@ export class Service extends Implementation { } const stored = await this.get(); actions.forEach((action) => { - const found = stored.find((a) => a.uuid === action.uuid); + const found = stored.find((a) => a.hash === action.hash); if (found === undefined) { return; } action.merge(found); }); - console.error(`Not implemented`); - return Promise.resolve(); - // return bridge - // .entries({ key: STORAGE_KEY }) - // .update(actions.map((a) => a.entry().to())) - // .then(() => { - // this.updated.emit(); - // }) - // .catch((err: Error) => { - // this.log().error(`Fail to update recent storage: ${err.message}`); - // }); + return bridge + .entries({ key: STORAGE_KEY }) + .update(actions.map((a) => a.entry().to())) + .then(() => { + this.updated.emit(); + }) + .catch((err: Error) => { + this.log().error(`Fail to update recent storage: ${err.message}`); + }); } public delete(uuids: string[]): Promise { @@ -105,7 +79,10 @@ export class Service extends Implementation { } public add(operation: ObserveOperation): Promise { - const action = new Action(operation.getOrigin()); + const action = Action.fromOperation(operation); + if (action instanceof Error) { + return Promise.reject(action); + } return this.update([action]); } } diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index 4168022871..7201a5afc0 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -6,191 +6,188 @@ import { Stat, IStat } from './stat'; import { recent } from '@service/recent'; import { scope } from '@platform/env/scope'; import { Logger } from '@platform/log'; -import { SessionOrigin } from '@service/session/origin'; +import { SessionComponents, SessionOrigin } from '@service/session/origin'; +import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionDescriptor, SessionSetup } from '@platform/types/bindings'; +import { hash } from '@platform/env/hash'; -import * as compatibility from './compatibility'; +import * as obj from '@platform/env/obj'; interface IActionContent { + hash: string; stat: IStat; - origin: SessionOrigin; + descriptor: SessionDescriptor; + setup: SessionSetup; } export class Action { static from(entry: Entry): Action | Error { - return new Error(`Not Implemented`); - // const action = new Action($.Observe.new()); - // const error = action.entry().from(entry); - // return error instanceof Error ? error : action; + try { + const body: IActionContent = JSON.parse(entry.content); + if (body.setup === undefined || typeof body.setup !== 'object') { + throw new Error(`No origin object. It might be old format. No converting support`); + } + obj.getAsObj(body.setup, 'origin'); + obj.getAsObj(body.setup, 'parser'); + obj.getAsObj(body.setup, 'source'); + if (body.descriptor === undefined || typeof body.descriptor !== 'object') { + throw new Error( + `No descriptor object. It might be old format. No converting support`, + ); + } + obj.getAsObj(body.descriptor, 'p_desc'); + obj.getAsObj(body.descriptor, 's_desc'); + obj.getAsObj(body.descriptor, 'parser'); + obj.getAsObj(body.descriptor, 'source'); + return new Action(body.setup, body.descriptor, body.stat); + } catch (err) { + return new Error(`Fail to parse action: ${error(err)}`); + } + } + + static getSetupHash(setup: SessionSetup): string { + return hash(JSON.stringify(setup)).toString(); + } + + static fromOperation(operation: ObserveOperation): Error | Action { + const setup = operation.getOrigin().getSessionSetup(); + const descriptor = operation.getDescriptor(); + if (!descriptor) { + return new Error( + `Action ${JSON.stringify(setup.origin)} doesn't have session descriptor`, + ); + } + return new Action(setup, descriptor); } protected logger: Logger; public stat: Stat = Stat.defaults(); - public uuid: string; - public compatibility: { - converted: boolean; - invalidUuid: string | undefined; - } = { - converted: false, - invalidUuid: undefined, - }; + public hash: string; - constructor(public origin: SessionOrigin) { - this.uuid = `Not implemented`; - // this.logger = scope.getLogger(`Action: ${this.uuid}`); - this.logger = scope.getLogger(`Action`); + constructor( + public readonly setup: SessionSetup, + public readonly descriptor: SessionDescriptor, + stat?: IStat, + ) { + this.hash = Action.getSetupHash(this.setup); + this.logger = scope.getLogger(`Action: ${this.hash}`); + if (stat) { + this.stat = new Stat(stat); + } } public isSuitable(origin: SessionOrigin): boolean { - console.error(`Not implemented`); - return false; - // if (observe === undefined) { - // return true; - // } - // if (observe.origin.nature().alias() !== this.observe.origin.nature().alias()) { - // return false; - // } - // return this.observe.parser.alias() === observe.parser.alias(); + if (!origin.isSameAction(this.setup.origin)) { + return false; + } + if (!origin.components) { + return false; + } + if (!origin.components.parser || !origin.components.source) { + return false; + } + return ( + origin.components.parser.uuid === this.descriptor.parser.uuid && + origin.components.source.uuid === this.descriptor.source.uuid + ); } public description(): { major: string; minor: string } { - console.error(`Not implemented`); - return { major: '', minor: '' }; + return { + major: this.descriptor.s_desc ? this.descriptor.s_desc : this.descriptor.source.name, + minor: this.descriptor.p_desc ? this.descriptor.p_desc : this.descriptor.parser.name, + }; } public entry(): { from(entry: Entry): Error | undefined; - // to(): Entry; + to(): Entry; } { return { - from: (entry: Entry): Error | undefined => { - console.error(`Not implemented`); - return undefined; - // try { - // const body: IActionContent = JSON.parse(entry.content); - // if (body.observe === undefined) { - // // Check previous version (chipmunk <= 3.8.1) - // this.observe = compatibility.from_3_8_1(entry); - // this.compatibility.converted = true; - // } else { - // const observe = new $.Observe(body.observe); - // this.observe = observe; - // } - // this.stat = Stat.from(body.stat); - // this.uuid = this.observe.signature(); - // this.compatibility.invalidUuid = - // entry.uuid !== this.uuid ? entry.uuid : undefined; - // return undefined; - // } catch (err) { - // return new Error(`Fail to parse action: ${error(err)}`); - // } + from: (_entry: Entry): Error | undefined => { + return new Error( + `Instance method cannot be used on Action. Please use static methed instead.`, + ); + }, + to: (): Entry => { + return { + uuid: this.hash, + content: JSON.stringify({ + stat: this.stat.asObj(), + descriptor: this.descriptor, + hash: this.hash, + setup: this.setup, + } as IActionContent), + }; }, - // to: (): Entry => { - // console.error(`Not implemented`); - // return undefined; - // return { - // uuid: this.uuid, - // content: JSON.stringify({ - // stat: this.stat.asObj(), - // observe: this.observe.storable(), - // } as IActionContent), - // }; - // }, }; } public getActions(): { caption?: string; handler?: () => void }[] { - console.error(`Not Implemented`); - return []; - // const observe = this.observe; - // const configurable = observe.isConfigurable(); - // const nature = observe.origin.nature().desc(); - - // return [ - // // **Note**: Recent actions isn't supported yet for plugins. Support of this - // // feature will be included with general refactoring of Observe Configuration - // // and a way to delivery available parser/source to client - // ...(nature.type === $.Description.OriginType.plugin - // ? [] - // : [ - // { - // caption: ((): string => { - // switch (nature.type) { - // case $.Description.OriginType.file: - // return 'Open with recent configuration'; - // case $.Description.OriginType.net: - // case $.Description.OriginType.serial: - // return 'Connect with recent configuration'; - // case $.Description.OriginType.command: - // return 'Execute with recent configuration'; - // } - // })(), - // handler: this.apply.bind(this), - // }, - // ]), - // ...(configurable - // ? [ - // { - // caption: 'Open with new configuration', - // handler: () => { - // console.error(`Not implemented`); - - // // session - // // .initialize() - // // .configure(observe) - // // .catch((err: Error) => { - // // this.logger.error( - // // `Fail to configure observe object: ${err.message}`, - // // ); - // // }); - // }, - // }, - // ] - // : []), - // ]; + const configurable = this.setup.parser.fields.length + this.setup.source.fields.length > 0; + return [ + ...(configurable + ? [ + { + caption: 'Open with new configuration', + handler: () => { + session + .initialize() + .configure( + SessionOrigin.fromSessionSetup(this.setup, this.descriptor), + ) + .catch((err: Error) => { + this.logger.error( + `Fail to configure session setup: ${err.message}`, + ); + }); + }, + }, + ] + : []), + ]; } public remove(): Promise { - return recent.delete([this.uuid]).catch((err: Error) => { + return recent.delete([this.hash]).catch((err: Error) => { this.logger.error(`Fail to remove recent action: ${err.message}`); }); } public apply(): Promise { - console.error(`Not implemented`); - return Promise.reject(new Error(`Not implemented`)); - // return session - // .initialize() - // .auto(this.observe.locker().lock()) - // .then(() => { - // return undefined; - // }) - // .catch((err: Error) => { - // const message = lockers.lock( - // new Locker(false, `Fail to apply action via error: ${err.message}`) - // .set() - // .buttons([ - // { - // caption: `Remove`, - // handler: () => { - // this.remove().finally(() => { - // message.popup.close(); - // }); - // }, - // }, - // { - // caption: `Cancel`, - // handler: () => { - // message.popup.close(); - // }, - // }, - // ]) - // .end(), - // { - // closable: false, - // }, - // ); - // }); + return session + .initialize() + .observe(SessionOrigin.fromSessionSetup(this.setup, this.descriptor)) + .then(() => { + return undefined; + }) + .catch((err: Error) => { + const message = lockers.lock( + new Locker(false, `Fail to apply action via error: ${err.message}`) + .set() + .buttons([ + { + caption: `Remove`, + handler: () => { + this.remove().finally(() => { + message.popup.close(); + }); + }, + }, + { + caption: `Cancel`, + handler: () => { + message.popup.close(); + }, + }, + ]) + .end(), + { + closable: false, + }, + ); + }); } public merge(action: Action): void { diff --git a/application/client/src/app/service/recent/compatibility.ts b/application/client/src/app/service/recent/compatibility.ts deleted file mode 100644 index 7cff32b4a7..0000000000 --- a/application/client/src/app/service/recent/compatibility.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Entry } from '@platform/types/storage/entry'; - -// Interface belong to >= 3.8.1 -interface IDLTFilters { - app_ids?: string[]; - context_ids?: string[]; - ecu_ids?: string[]; -} - -// Interface belong to >= 3.8.1 -interface IDLTOptions { - logLevel: number; - filters: IDLTFilters; - fibex: string[]; - tz?: string; -} - -// export function optionsToParserSettings( -// options: IDLTOptions, -// with_storage_header: boolean, -// app_id_count: number, -// context_id_count: number, -// ): $.Parser.Dlt.IConfiguration { -// const filter_config: $.Parser.Dlt.IFilters = { -// min_log_level: options.logLevel, -// app_ids: options.filters.app_ids, -// context_ids: options.filters.context_ids, -// ecu_ids: options.filters.ecu_ids, -// app_id_count, -// context_id_count, -// }; -// return { -// filter_config, -// fibex_file_paths: options.fibex.length > 0 ? options.fibex : undefined, -// with_storage_header, -// tz: undefined, -// }; -// } - -// // This function has to be removed since v 3.9.x or 3.10.x (after a couple of -// // update iterations) -// export function from_3_8_1(entry: Entry): $.Observe { -// const action = JSON.parse(entry.content); -// let observe; -// if (action['file'] !== undefined) { -// if (action['file']['dlt'] !== undefined) { -// observe = new Factory.File() -// .asDlt(optionsToParserSettings(action['file']['dlt'], true, 0, 0)) -// .type($.Types.File.FileType.Binary) -// .file(action['file']['filename']) -// .get(); -// } else if (action['file']['pcap'] !== undefined) { -// observe = new Factory.File() -// .asDlt(optionsToParserSettings(action['file']['pcap']['dlt'], true, 0, 0)) -// .type($.Types.File.FileType.PcapNG) -// .file(action['file']['filename']) -// .get(); -// } else { -// observe = new Factory.File() -// .asText() -// .file(action['file']['filename']) -// .type($.Types.File.FileType.Text) -// .get(); -// } -// } else if (action['dlt_stream'] !== undefined) { -// const defs = action['dlt_stream']; -// const source = defs['source']; -// const preconstructed = new Factory.Stream().asDlt( -// optionsToParserSettings(defs['options'], false, 0, 0), -// ); -// if (source['process'] !== undefined) { -// preconstructed.process(source['process']); -// } else if (source['serial'] !== undefined) { -// preconstructed.serial(source['serial']); -// } else if (source['tcp'] !== undefined) { -// preconstructed.tcp(source['tcp']); -// } else if (source['udp'] !== undefined) { -// preconstructed.udp(source['udp']); -// } else { -// throw new Error(`Unknonw type of source for stream.`); -// } -// observe = preconstructed.get(); -// } else if (action['text_stream'] !== undefined) { -// const defs = action['text_stream']; -// const source = defs['source']; -// const preconstructed = new Factory.Stream().asText(); -// if (source['process'] !== undefined) { -// preconstructed.process(source['process']); -// } else if (source['serial'] !== undefined) { -// preconstructed.serial(source['serial']); -// } else if (source['tcp'] !== undefined) { -// preconstructed.tcp(source['tcp']); -// } else if (source['udp'] !== undefined) { -// preconstructed.udp(source['udp']); -// } else { -// throw new Error(`Unknonw type of source for stream.`); -// } -// observe = preconstructed.get(); -// } else { -// throw new Error(`Unknonw type of action.`); -// } -// const error = observe.validate(); -// if (error instanceof Error) { -// throw error; -// } -// return observe; -// } diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 9b56e53cc5..e2bfe44a14 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -19,7 +19,6 @@ import { history } from '@service/history'; import { Render } from '@schema/render'; import { File } from '@platform/types/files'; import { SetupObserve } from '@tabs/setup/component'; -import { recent } from '@service/recent'; import { bridge } from '@service/bridge'; import { SessionOrigin } from './session/origin'; diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index 464d213b81..bed4606997 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -11,10 +11,11 @@ import { Sde } from './observing/sde'; import { TextExportOptions } from '@platform/types/exporting'; import { SessionDescriptor } from '@platform/types/bindings'; import { SessionOrigin } from '../origin'; +import { ISourceLink } from '@platform/types/source'; +import { recent } from '@service/recent'; import * as Requests from '@platform/ipc/request'; import * as Events from '@platform/ipc/event'; -import { ISourceLink } from '@platform/types/source'; export { ObserveOperation }; @@ -206,6 +207,10 @@ export class Stream extends Subscriber { }); this.sde.overwrite(this.observed.operations); this.subjects.get().started.emit(operation); + if (this.observed.operations.size === 1) { + // Only if it's the first operation, save as recent + recent.add(operation); + } return response.session; }) .finally(lockers.progress(`Creating session...`)); diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 41101359d3..6183909453 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -4,6 +4,7 @@ import { SessionSetup, ComponentOptions, OutputRender, + SessionDescriptor, } from '@platform/types/bindings'; import { Render } from '@schema/render'; import { ColumnsRender } from '@schema/render/columns'; @@ -13,6 +14,14 @@ import { components } from '@service/components'; export class SessionComponents { public parser: Ident | undefined; public source: Ident | undefined; + public setParser(ident: Ident): SessionComponents { + this.parser = ident; + return this; + } + public setSource(ident: Ident): SessionComponents { + this.source = ident; + return this; + } } export class ComponentsOptions { @@ -29,6 +38,14 @@ export class ComponentsOptions { } export class SessionOrigin { + static fromSessionSetup(setup: SessionSetup, descriptor: SessionDescriptor): SessionOrigin { + return new SessionOrigin( + setup.origin, + new SessionComponents().setSource(descriptor.source).setParser(descriptor.source), + new ComponentsOptions().setParser(setup.parser).setSource(setup.source), + ); + } + static file(path: string): SessionOrigin { return new SessionOrigin({ File: path }, undefined); } @@ -44,8 +61,9 @@ export class SessionOrigin { constructor( public readonly origin: SessionAction, public components: SessionComponents | undefined, + options?: ComponentsOptions, ) { - this.options = new ComponentsOptions(); + this.options = options ? options : new ComponentsOptions(); } public setComponents(components: SessionComponents) { @@ -143,6 +161,21 @@ export class SessionOrigin { return typeof (this.origin as { File: string }).File === 'string'; } + public isSameAction(other: SessionAction): boolean { + if ((this.origin as { File: string }).File && (other as { File: string }).File) { + return true; + } else if ( + (this.origin as { Files: string[] }).Files && + (other as { Files: string[] }).Files + ) { + return true; + } else if (this.origin === 'Source' && other === 'Source') { + return true; + } else { + return false; + } + } + public getFirstFilename(): string | undefined { if ((this.origin as { File: string }).File) { return (this.origin as { File: string }).File; diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts index b3f371fe3c..bc132dfbc1 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts @@ -25,7 +25,7 @@ export class Provider extends Base { recent .get() .then((actions: Action[]) => { - this.storage().remove(actions.map((action: Action) => action.uuid)); + this.storage().remove(actions.map((action: Action) => action.hash)); this.reload.emit(); }) .catch((err: Error) => { @@ -74,7 +74,7 @@ export class Provider extends Base { { caption: 'Remove recent', handler: () => { - this.storage().remove([entity.uuid]); + this.storage().remove([entity.hash]); }, }, { diff --git a/application/client/src/app/ui/elements/recent/component.ts b/application/client/src/app/ui/elements/recent/component.ts index 5aab13fbcd..1728a6e7ca 100644 --- a/application/client/src/app/ui/elements/recent/component.ts +++ b/application/client/src/app/ui/elements/recent/component.ts @@ -87,7 +87,7 @@ export class RecentActions extends ChangesDetector implements AfterContentInit, { caption: 'Remove recent', handler: () => { - this.state.remove([action.uuid]); + this.state.remove([action.hash]); }, }, { diff --git a/application/client/src/app/ui/elements/recent/state.ts b/application/client/src/app/ui/elements/recent/state.ts index c237a69b87..83dfc735bb 100644 --- a/application/client/src/app/ui/elements/recent/state.ts +++ b/application/client/src/app/ui/elements/recent/state.ts @@ -55,7 +55,7 @@ export class State extends Holder { recent .get() .then((actions: Action[]) => { - this.remove(actions.map((action: Action) => action.uuid)); + this.remove(actions.map((action: Action) => action.hash)); }) .catch((err: Error) => { this._logger.error(`Fail to remove all recent actions: ${err.message}`); From 814f43d139e62fe37483c9befe98d33478fd08f4 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 7 Jul 2025 18:56:05 +0200 Subject: [PATCH 086/121] Update recent action provider --- .../navigator/providers/provider.recent.ts | 20 ++++----- .../elements/navigator/providers/provider.ts | 4 +- .../elements/navigator/providers/providers.ts | 6 +-- .../src/app/ui/elements/navigator/state.ts | 5 ++- .../src/app/ui/elements/recent/component.ts | 8 ++-- .../app/ui/elements/recent/icon/component.ts | 17 -------- .../app/ui/elements/recent/icon/styles.less | 26 ----------- .../app/ui/elements/recent/icon/template.html | 9 ---- .../src/app/ui/elements/recent/module.ts | 7 +-- .../ui/elements/recent/nature/component.ts | 21 --------- .../app/ui/elements/recent/nature/module.ts | 11 ----- .../app/ui/elements/recent/nature/styles.less | 8 ---- .../ui/elements/recent/nature/template.html | 7 --- .../ui/elements/recent/parser/component.ts | 21 --------- .../app/ui/elements/recent/parser/module.ts | 11 ----- .../app/ui/elements/recent/parser/styles.less | 8 ---- .../ui/elements/recent/parser/template.html | 1 - .../src/app/ui/elements/recent/state.ts | 43 ++++++++++--------- .../src/app/ui/elements/recent/template.html | 8 ++-- .../client/src/app/ui/tabs/setup/state.ts | 2 +- 20 files changed, 49 insertions(+), 194 deletions(-) delete mode 100644 application/client/src/app/ui/elements/recent/icon/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/icon/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/icon/template.html delete mode 100644 application/client/src/app/ui/elements/recent/nature/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/module.ts delete mode 100644 application/client/src/app/ui/elements/recent/nature/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/nature/template.html delete mode 100644 application/client/src/app/ui/elements/recent/parser/component.ts delete mode 100644 application/client/src/app/ui/elements/recent/parser/module.ts delete mode 100644 application/client/src/app/ui/elements/recent/parser/styles.less delete mode 100644 application/client/src/app/ui/elements/recent/parser/template.html diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts index bc132dfbc1..4b67879f46 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.recent.ts @@ -35,16 +35,16 @@ export class Provider extends Base { }; } public load(): Promise { - console.error(`Not Implemented`); - return Promise.resolve([]); - // return recent.get().then((actions: Action[]) => { - // actions = actions.filter((action) => action.isSuitable(this.operation)); - // actions.sort((a: Action, b: Action) => { - // return b.stat.score().recent() >= a.stat.score().recent() ? 1 : -1; - // }); - // this.count = actions.length; - // return actions; - // }); + return recent.get().then((actions: Action[]) => { + actions = actions.filter((action) => + this.origin ? action.isSuitable(this.origin) : true, + ); + actions.sort((a: Action, b: Action) => { + return b.stat.score().recent() >= a.stat.score().recent() ? 1 : -1; + }); + this.count = actions.length; + return actions; + }); } public action(action: unknown): void { diff --git a/application/client/src/app/ui/elements/navigator/providers/provider.ts b/application/client/src/app/ui/elements/navigator/providers/provider.ts index a09a117456..4d22c34d93 100644 --- a/application/client/src/app/ui/elements/navigator/providers/provider.ts +++ b/application/client/src/app/ui/elements/navigator/providers/provider.ts @@ -2,7 +2,7 @@ import { IlcInterface } from '@service/ilc'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { IMenuItem } from '@ui/service/contextmenu'; import { Subject } from '@platform/env/subscription'; -import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; export interface IStatistics { title: string; @@ -23,7 +23,7 @@ export abstract class Provider { constructor( public readonly ilc: IlcInterface & ChangesDetector, public readonly index: number, - protected readonly operation: ObserveOperation | undefined, + protected readonly origin: SessionOrigin | undefined, ) {} public abstract load(): Promise; diff --git a/application/client/src/app/ui/elements/navigator/providers/providers.ts b/application/client/src/app/ui/elements/navigator/providers/providers.ts index 6073607566..874e77132a 100644 --- a/application/client/src/app/ui/elements/navigator/providers/providers.ts +++ b/application/client/src/app/ui/elements/navigator/providers/providers.ts @@ -9,7 +9,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import * as wasm from '@loader/wasm'; import { IMenuItem } from '@ui/service/contextmenu'; -import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; const PROVIDERS = [ProviderRecent, ProviderFiles]; @@ -20,9 +20,9 @@ export class Providers { protected readonly ilc: IlcInterface & ChangesDetector, protected readonly matcher: wasm.Matcher, protected readonly entries: Entries, - operation: ObserveOperation | undefined, + origin: SessionOrigin | undefined, ) { - this.providers = PROVIDERS.map((Ref, i) => new Ref(ilc, i, operation)); + this.providers = PROVIDERS.map((Ref, i) => new Ref(ilc, i, origin)); this.providers.forEach((provider: Provider, i: number) => { ilc.env().subscriber.register( provider.reload.subscribe(() => { diff --git a/application/client/src/app/ui/elements/navigator/state.ts b/application/client/src/app/ui/elements/navigator/state.ts index b59374b168..6cf6253495 100644 --- a/application/client/src/app/ui/elements/navigator/state.ts +++ b/application/client/src/app/ui/elements/navigator/state.ts @@ -8,6 +8,7 @@ import { unique } from '@platform/env/sequence'; import { INoContentActions, IStatistics } from './providers/provider'; import { IMenuItem } from '@ui/service/contextmenu'; import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; const MOVE_SELECTION_DELAY = 150; @@ -48,12 +49,12 @@ export class State extends Holder { constructor( protected readonly ilc: IlcInterface & ChangesDetector, protected readonly filterRefGetter: () => HTMLInputElement | undefined, - protected readonly operation: ObserveOperation | undefined, + protected readonly origin: SessionOrigin | undefined, ) { super(); this.ilc = ilc; this.entries = new Entries(this.uuid, ilc, this.matcher); - this.providers = new Providers(ilc, this.matcher, this.entries, this.operation); + this.providers = new Providers(ilc, this.matcher, this.entries, this.origin); ilc.env().subscriber.register( ilc .ilc() diff --git a/application/client/src/app/ui/elements/recent/component.ts b/application/client/src/app/ui/elements/recent/component.ts index 1728a6e7ca..0693758263 100644 --- a/application/client/src/app/ui/elements/recent/component.ts +++ b/application/client/src/app/ui/elements/recent/component.ts @@ -17,7 +17,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { State } from './state'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { HiddenFilter } from '@elements/filter.hidden/component'; -import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-recent-actions', @@ -30,7 +30,7 @@ import { ObserveOperation } from '@service/session/dependencies/stream'; @Initial() @Ilc() export class RecentActions extends ChangesDetector implements AfterContentInit, AfterViewInit { - @Input() public operation!: ObserveOperation; + @Input() public origin: SessionOrigin | undefined; @Output() public applied: EventEmitter = new EventEmitter(); @@ -44,13 +44,13 @@ export class RecentActions extends ChangesDetector implements AfterContentInit, public ngAfterContentInit(): void { this.markChangesForCheck(); - this.state = new State(this, this.operation); + this.state = new State(this, this.origin); this.env().subscriber.register( this.state.update.subscribe(() => { this.detectChanges(); }), ); - if (this.operation !== undefined) { + if (this.origin !== undefined) { console.error(`Not Implemented`); // this.env().subscriber.register( // this.operation.subscribe(() => { diff --git a/application/client/src/app/ui/elements/recent/icon/component.ts b/application/client/src/app/ui/elements/recent/icon/component.ts deleted file mode 100644 index 49304de5e4..0000000000 --- a/application/client/src/app/ui/elements/recent/icon/component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ObserveOperation } from '@service/session/dependencies/stream'; - -@Component({ - selector: 'app-recent-nature-icon', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentIcon { - @Input() public operation!: ObserveOperation; -} -export interface RecentIcon extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/icon/styles.less b/application/client/src/app/ui/elements/recent/icon/styles.less deleted file mode 100644 index 8c753a0fde..0000000000 --- a/application/client/src/app/ui/elements/recent/icon/styles.less +++ /dev/null @@ -1,26 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: flex; - flex-direction: column; - width: 42px; - overflow: hidden; - align-items: center; - justify-content: center; - & span { - color: var(--scheme-color-2); - font-weight: 500; - padding-right: 12px; - font-size: 12px; - } - & mat-icon { - font-size: 16px; - height: 16px; - width: 16px; - margin: 0; - color: var(--scheme-color-2); - padding: 0 12px 0 0; - } -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/icon/template.html b/application/client/src/app/ui/elements/recent/icon/template.html deleted file mode 100644 index 8b378955e5..0000000000 --- a/application/client/src/app/ui/elements/recent/icon/template.html +++ /dev/null @@ -1,9 +0,0 @@ -Not Implemented - diff --git a/application/client/src/app/ui/elements/recent/module.ts b/application/client/src/app/ui/elements/recent/module.ts index 755288703a..171fb71d4e 100644 --- a/application/client/src/app/ui/elements/recent/module.ts +++ b/application/client/src/app/ui/elements/recent/module.ts @@ -7,9 +7,6 @@ import { MatMenuModule } from '@angular/material/menu'; import { HiddenFilterModule } from '@elements/filter.hidden/module'; import { RecentActions } from './component'; -import { RecentIcon } from './icon/component'; -import { RecentNatureModule } from './nature/module'; -import { RecentParserModule } from './parser/module'; @NgModule({ imports: [ @@ -19,10 +16,8 @@ import { RecentParserModule } from './parser/module'; MatIconModule, MatMenuModule, HiddenFilterModule, - RecentNatureModule, - RecentParserModule, ], - declarations: [RecentActions, RecentIcon], + declarations: [RecentActions], exports: [RecentActions], }) export class RecentActionsModule {} diff --git a/application/client/src/app/ui/elements/recent/nature/component.ts b/application/client/src/app/ui/elements/recent/nature/component.ts deleted file mode 100644 index 92dc11a962..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ObserveOperation } from '@service/session/dependencies/stream'; - -@Component({ - selector: 'app-recent-nature', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentNature implements AfterContentInit { - @Input() public operation!: ObserveOperation; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentNature extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/nature/module.ts b/application/client/src/app/ui/elements/recent/nature/module.ts deleted file mode 100644 index 3245e93882..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { RecentNature } from './component'; - -@NgModule({ - imports: [CommonModule], - declarations: [RecentNature], - exports: [RecentNature], -}) -export class RecentNatureModule {} diff --git a/application/client/src/app/ui/elements/recent/nature/styles.less b/application/client/src/app/ui/elements/recent/nature/styles.less deleted file mode 100644 index 856ff2de94..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/styles.less +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/nature/template.html b/application/client/src/app/ui/elements/recent/nature/template.html deleted file mode 100644 index 432113f8c9..0000000000 --- a/application/client/src/app/ui/elements/recent/nature/template.html +++ /dev/null @@ -1,7 +0,0 @@ -Not Implemented - diff --git a/application/client/src/app/ui/elements/recent/parser/component.ts b/application/client/src/app/ui/elements/recent/parser/component.ts deleted file mode 100644 index a96349850b..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, AfterContentInit, Input } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; -import { Initial } from '@env/decorators/initial'; -import { ObserveOperation } from '@service/session/dependencies/stream'; - -@Component({ - selector: 'app-recent-parser', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Initial() -@Ilc() -export class RecentParser implements AfterContentInit { - @Input() public operation!: ObserveOperation; - - public ngAfterContentInit(): void { - // - } -} -export interface RecentParser extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/recent/parser/module.ts b/application/client/src/app/ui/elements/recent/parser/module.ts deleted file mode 100644 index 20ccdcc645..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import { RecentParser } from './component'; - -@NgModule({ - imports: [CommonModule], - declarations: [RecentParser], - exports: [RecentParser], -}) -export class RecentParserModule {} diff --git a/application/client/src/app/ui/elements/recent/parser/styles.less b/application/client/src/app/ui/elements/recent/parser/styles.less deleted file mode 100644 index 856ff2de94..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/styles.less +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../styles/variables.less'; - - -:host { - position: relative; - display: block; - width: 100%; -} \ No newline at end of file diff --git a/application/client/src/app/ui/elements/recent/parser/template.html b/application/client/src/app/ui/elements/recent/parser/template.html deleted file mode 100644 index fc92d0c332..0000000000 --- a/application/client/src/app/ui/elements/recent/parser/template.html +++ /dev/null @@ -1 +0,0 @@ - Not Implemented diff --git a/application/client/src/app/ui/elements/recent/state.ts b/application/client/src/app/ui/elements/recent/state.ts index 83dfc735bb..0caed351bb 100644 --- a/application/client/src/app/ui/elements/recent/state.ts +++ b/application/client/src/app/ui/elements/recent/state.ts @@ -6,19 +6,19 @@ import { IlcInterface } from '@service/ilc'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { Holder } from '@module/matcher'; import { Logger } from '@platform/log'; -import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; export class State extends Holder { public actions: WrappedAction[] = []; public readonly update: Subject = new Subject(); - public readonly operation: ObserveOperation | undefined; + public origin: SessionOrigin | undefined; private _logger: Logger; - constructor(ilc: IlcInterface & ChangesDetector, operation: ObserveOperation) { + constructor(ilc: IlcInterface & ChangesDetector, origin: SessionOrigin | undefined) { super(); - this.operation = operation; + this.origin = origin; this._logger = ilc.log(); ilc.env().subscriber.register(recent.updated.subscribe(this.reload.bind(this))); this.reload(); @@ -63,22 +63,23 @@ export class State extends Holder { } public reload(): void { - console.error(`Not implemented`); - // recent - // .get() - // .then((actions: Action[]) => { - // this.actions = actions - // .filter((action) => action.isSuitable(this.operation)) - // .map((action) => new WrappedAction(action, this.matcher)); - // this.actions.sort((a: WrappedAction, b: WrappedAction) => { - // return b.action.stat.score().recent() >= a.action.stat.score().recent() - // ? 1 - // : -1; - // }); - // this.update.emit(); - // }) - // .catch((error: Error) => { - // this._logger.error(`Fail to get recent due error: ${error.message}`); - // }); + recent + .get() + .then((actions: Action[]) => { + this.actions = actions + .filter((action) => { + this.origin ? action.isSuitable(this.origin) : true; + }) + .map((action) => new WrappedAction(action, this.matcher)); + this.actions.sort((a: WrappedAction, b: WrappedAction) => { + return b.action.stat.score().recent() >= a.action.stat.score().recent() + ? 1 + : -1; + }); + this.update.emit(); + }) + .catch((error: Error) => { + this._logger.error(`Fail to get recent due error: ${error.message}`); + }); } } diff --git a/application/client/src/app/ui/elements/recent/template.html b/application/client/src/app/ui/elements/recent/template.html index 8f61b86949..ae60acaaee 100644 --- a/application/client/src/app/ui/elements/recent/template.html +++ b/application/client/src/app/ui/elements/recent/template.html @@ -5,12 +5,10 @@ (click)="onDefaultAction(action.action)" (contextmenu)="onAllActions($event, action.action)" > -
- -
+
IC
- - +

{{action.description().major}}

+

{{action.description().minor}}

diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 2a28097080..3a5d1e6b62 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -1,4 +1,4 @@ -import { SessionAction, Ident, IODataType } from '@platform/types/bindings'; +import { Ident, IODataType } from '@platform/types/bindings'; import { components } from '@service/components'; import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; From 0ab2c1c5fb77a49cc2e57c4d8f3fdf3f7c3bc532 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 8 Jul 2025 15:54:18 +0200 Subject: [PATCH 087/121] Fix events validation on ts-bindings layer --- .../ts-bindings/src/api/session.provider.ts | 8 ++--- .../ts-bindings/src/provider/provider.ts | 1 + .../client/src/app/service/recent/action.ts | 4 +-- .../app/ui/elements/navigator/component.ts | 25 +++++++------- .../ui/elements/navigator/providers/entity.ts | 34 ++++++++----------- .../src/app/ui/elements/recent/state.ts | 4 +-- application/platform/env/subscription.ts | 15 ++++++-- 7 files changed, 47 insertions(+), 44 deletions(-) diff --git a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts index 66ba376b50..d6d88da579 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts @@ -150,8 +150,8 @@ interface ISessionEventsInterfaces { self: 'object'; parser: 'object'; source: 'object'; - s_desc: ['string', null, undefined]; - p_desc: ['string', null, undefined]; + s_desc: ['string', null]; + p_desc: ['string', null]; }; }; OperationProcessing: { self: 'string' }; @@ -191,8 +191,8 @@ const SessionEventsInterfaces: ISessionEventsInterfaces = { self: 'object', parser: 'object', source: 'object', - s_desc: ['string', null, undefined], - p_desc: ['string', null, undefined], + s_desc: ['string', null], + p_desc: ['string', null], }, }, OperationProcessing: { self: 'string' }, diff --git a/application/apps/rustcore/ts-bindings/src/provider/provider.ts b/application/apps/rustcore/ts-bindings/src/provider/provider.ts index 7afd6d5ac9..0fa059ef36 100644 --- a/application/apps/rustcore/ts-bindings/src/provider/provider.ts +++ b/application/apps/rustcore/ts-bindings/src/provider/provider.ts @@ -289,6 +289,7 @@ export abstract class Computation (this.getEventsInterfaces() as any)[event], data, ); + if (err instanceof Error) { this.debug().emit.error(`Error: ${error(err)}. Input: ${JSON.stringify(data)}`); this.logger.error(`Failed to parse event "${event}" due error: ${error(err)}`); diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index 7201a5afc0..439f5d7446 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -35,8 +35,8 @@ export class Action { `No descriptor object. It might be old format. No converting support`, ); } - obj.getAsObj(body.descriptor, 'p_desc'); - obj.getAsObj(body.descriptor, 's_desc'); + obj.getAsStringOrAsUndefined(body.descriptor, 'p_desc'); + obj.getAsStringOrAsUndefined(body.descriptor, 's_desc'); obj.getAsObj(body.descriptor, 'parser'); obj.getAsObj(body.descriptor, 'source'); return new Action(body.setup, body.descriptor, body.stat); diff --git a/application/client/src/app/ui/elements/navigator/component.ts b/application/client/src/app/ui/elements/navigator/component.ts index 9a247d0d1e..21b944da48 100644 --- a/application/client/src/app/ui/elements/navigator/component.ts +++ b/application/client/src/app/ui/elements/navigator/component.ts @@ -16,7 +16,7 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, CloseHandler } from './state'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { InputFilter } from '@elements/filter/component'; -import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionOrigin } from '@service/session/origin'; @Component({ selector: 'app-navigator', @@ -35,7 +35,7 @@ export class Navigator @ViewChild('filter') public filterInputRef!: InputFilter; @Input() close: CloseHandler | undefined; - @Input() public operation?: ObserveOperation; + @Input() public origin: SessionOrigin | undefined; public state!: State; @@ -48,17 +48,16 @@ export class Navigator } public ngAfterContentInit(): void { - // this.state = new State( - // this, - // (): HTMLInputElement | undefined => { - // this.detectChanges(); - // return this.filterInputRef !== undefined - // ? this.filterInputRef.getInputElementRef() - // : undefined; - // }, - // this.observe, - // ); - console.error(`Not implemented`); + this.state = new State( + this, + (): HTMLInputElement | undefined => { + this.detectChanges(); + return this.filterInputRef !== undefined + ? this.filterInputRef.getInputElementRef() + : undefined; + }, + this.origin, + ); this.close !== undefined && this.state.bind(this.close); } diff --git a/application/client/src/app/ui/elements/navigator/providers/entity.ts b/application/client/src/app/ui/elements/navigator/providers/entity.ts index 6b8649e3cb..5b7fb8dd5d 100644 --- a/application/client/src/app/ui/elements/navigator/providers/entity.ts +++ b/application/client/src/app/ui/elements/navigator/providers/entity.ts @@ -28,8 +28,7 @@ export class Entity extends PassiveMatchee { if (this.origin instanceof Action) { console.error(`Not Implemented`); return { - // criteria: this.origin.description().major, - criteria: '', + criteria: this.origin.description().major, }; } else { return { @@ -44,17 +43,12 @@ export class Entity extends PassiveMatchee { } { const major: string | undefined = this.getHtmlOf('html_criteria'); if (this.origin instanceof Action) { - console.error(`Not Implemented`); - return { - major: 'Not Implemented', - minor: 'Not Implemented', - }; - // return major === undefined - // ? this.origin.description() - // : { - // major, - // minor: this.origin.description().minor, - // }; + return major === undefined + ? this.origin.description() + : { + major, + minor: this.origin.description().minor, + }; } else { return { major: major === undefined ? this.origin.name : major, @@ -63,11 +57,11 @@ export class Entity extends PassiveMatchee { } } - // public hash(): string { - // if (this.origin instanceof Action) { - // return `${this.origin.description().major}-${this.origin.description().minor}`; - // } else { - // return `${this.origin.name}-${this.origin.parent}`; - // } - // } + public hash(): string { + if (this.origin instanceof Action) { + return `${this.origin.description().major}-${this.origin.description().minor}`; + } else { + return `${this.origin.name}-${this.origin.parent}`; + } + } } diff --git a/application/client/src/app/ui/elements/recent/state.ts b/application/client/src/app/ui/elements/recent/state.ts index 0caed351bb..3d031e8311 100644 --- a/application/client/src/app/ui/elements/recent/state.ts +++ b/application/client/src/app/ui/elements/recent/state.ts @@ -67,9 +67,7 @@ export class State extends Holder { .get() .then((actions: Action[]) => { this.actions = actions - .filter((action) => { - this.origin ? action.isSuitable(this.origin) : true; - }) + .filter((action) => (this.origin ? action.isSuitable(this.origin) : true)) .map((action) => new WrappedAction(action, this.matcher)); this.actions.sort((a: WrappedAction, b: WrappedAction) => { return b.action.stat.score().recent() >= a.action.stat.score().recent() diff --git a/application/platform/env/subscription.ts b/application/platform/env/subscription.ts index 9a6eaa118f..0e830a3e36 100644 --- a/application/platform/env/subscription.ts +++ b/application/platform/env/subscription.ts @@ -44,7 +44,7 @@ export function validateEventDesc(desc: IEventDesc, target: any): Error | undefi )}`, ); } - if (typeof desc.self !== 'string') { + if (typeof desc.self === 'object') { if (!(target instanceof desc.self)) { return new Error(`Expecting ${desc.self}, but has been gotten ${target}`); } else { @@ -80,7 +80,18 @@ export function validateEventDesc(desc: IEventDesc, target: any): Error | undefi (typeof target[name] === typeRef || typeRef === 'any') ) { valid = true; - } else if (typeof typeRef !== 'string' && target[name] instanceof typeRef) { + } else if (typeof typeRef === 'object' && typeRef === null && target[name] === null) { + valid = true; + } else if ( + typeof typeRef === 'object' && + typeRef !== null && + target[name] instanceof typeRef + ) { + valid = true; + } else if ( + (typeRef === null && target[name] === null) || + (typeRef === undefined && target[name] === undefined) + ) { valid = true; } }); From fcbd51952b49da3b071796836155748d27f98ed9 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 10 Jul 2025 15:58:52 +0200 Subject: [PATCH 088/121] Correct history source definitions --- .../app/service/history/definition.file.ts | 15 +++-- ...finition.concat.ts => definition.files.ts} | 31 +++++---- .../app/service/history/definition.stream.ts | 63 +++++++++---------- .../src/app/service/history/definition.ts | 14 ++--- .../client/src/app/service/history/session.ts | 33 +++++----- .../client/src/app/service/session/origin.ts | 16 +++++ 6 files changed, 90 insertions(+), 82 deletions(-) rename application/client/src/app/service/history/{definition.concat.ts => definition.files.ts} (63%) diff --git a/application/client/src/app/service/history/definition.file.ts b/application/client/src/app/service/history/definition.file.ts index d61a01759f..05c4d3cd3a 100644 --- a/application/client/src/app/service/history/definition.file.ts +++ b/application/client/src/app/service/history/definition.file.ts @@ -1,6 +1,6 @@ import { getParentFolder } from '@platform/types/files'; import { bridge } from '@service/bridge'; -import { SessionOrigin } from '@service/session/origin'; +import { ObserveOperation } from '@service/session/dependencies/stream'; import * as obj from '@platform/env/obj'; @@ -14,13 +14,12 @@ export interface IFileDesc { } export class FileDesc implements IFileDesc { - static async fromDataSource(origin: SessionOrigin): Promise { - return Promise.reject(new Error(`Not implemented`)); - // const file = source.origin.as<$.Origin.File.Configuration>($.Origin.File.Configuration); - // if (file === undefined) { - // return undefined; - // } - // return FileDesc.fromFilename(file.filename()); + static fromDataSource(operation: ObserveOperation): Promise { + const filename = operation.getOrigin().getFirstFilename(); + if (!filename || !operation.getOrigin().isFile()) { + return Promise.resolve(undefined); + } + return FileDesc.fromFilename(filename); } static async fromFilename(filename: string): Promise { const file = (await bridge.files().getByPathWithCache([filename]))[0]; diff --git a/application/client/src/app/service/history/definition.concat.ts b/application/client/src/app/service/history/definition.files.ts similarity index 63% rename from application/client/src/app/service/history/definition.concat.ts rename to application/client/src/app/service/history/definition.files.ts index ddecc12f5f..519764068f 100644 --- a/application/client/src/app/service/history/definition.concat.ts +++ b/application/client/src/app/service/history/definition.files.ts @@ -1,23 +1,20 @@ +import { ObserveOperation } from '@service/session/dependencies/stream'; import { IFileDesc, FileDesc } from './definition.file'; -import { SessionOrigin } from '@service/session/origin'; export class ConcatDesc { - static async fromDataSource(origin: SessionOrigin): Promise { - return Promise.reject(new Error(`Not implemented`)); - // const concat = source.origin.as<$.Origin.Concat.Configuration>( - // $.Origin.Concat.Configuration, - // ); - // if (concat === undefined) { - // return undefined; - // } - // const list: IFileDesc[] = []; - // for (const filename of concat.files()) { - // const desc = await FileDesc.fromFilename(filename); - // if (desc !== undefined) { - // list.push(desc); - // } - // } - // return list; + static async fromDataSource(operation: ObserveOperation): Promise { + const files = operation.getOrigin().getFiles(); + if (files === undefined) { + return undefined; + } + const list: IFileDesc[] = []; + for (const filename of files) { + const desc = await FileDesc.fromFilename(filename); + if (desc !== undefined) { + list.push(desc); + } + } + return list; } static fromMinifiedStr( src: Array<{ [key: string]: number | string }> | undefined, diff --git a/application/client/src/app/service/history/definition.stream.ts b/application/client/src/app/service/history/definition.stream.ts index 0a2ae91422..00f03ad202 100644 --- a/application/client/src/app/service/history/definition.stream.ts +++ b/application/client/src/app/service/history/definition.stream.ts @@ -1,53 +1,50 @@ -import { SessionOrigin } from '@service/session/origin'; +import { ObserveOperation } from '@service/session/dependencies/stream'; +import { SessionDescriptor } from '@platform/types/bindings'; import * as obj from '@platform/env/obj'; export interface IStreamDesc { - origin: SessionOrigin; + source: string; major: string; minor: string; } export class StreamDesc implements IStreamDesc { - static async fromDataSource(origin: SessionOrigin): Promise { - return Promise.reject(new Error(`Not implemented`)); - - // const stream = source.origin.as<$.Origin.Stream.Configuration>( - // $.Origin.Stream.Configuration, - // ); - // if (stream === undefined) { - // return undefined; - // } - // const def = stream.desc(); - // return { - // major: def.major, - // minor: def.minor, - // source: stream.instance.instance.alias(), - // }; + static fromDataSource(operation: ObserveOperation): Promise { + if (operation.getOrigin().isStream()) { + return Promise.resolve(undefined); + } + const descriptor = operation.getDescriptor(); + if (!descriptor) { + return Promise.resolve(undefined); + } + return Promise.resolve({ + major: descriptor.s_desc ? descriptor.s_desc : descriptor.source.name, + minor: descriptor.p_desc ? descriptor.p_desc : descriptor.parser.name, + source: descriptor.source.name, + }); } static fromMinifiedStr( src: { [key: string]: number | string } | undefined, ): StreamDesc | undefined { - console.error(`Not implemented`); - return undefined; - // return src === undefined - // ? undefined - // : new StreamDesc({ - // origin: obj.getAsNotEmptyString(src, 's') as $.Origin.Stream.Stream.Source, - // major: obj.getAsString(src, 'ma'), - // minor: obj.getAsString(src, 'mi'), - // }); + return src === undefined + ? undefined + : new StreamDesc({ + source: obj.getAsNotEmptyString(src, 's'), + major: obj.getAsString(src, 'ma'), + minor: obj.getAsString(src, 'mi'), + }); } - public origin: SessionOrigin; - public major: string; - public minor: string; + public readonly source: string; + public readonly major: string; + public readonly minor: string; - constructor(definition: IStreamDesc) { - this.origin = definition.origin; - this.major = definition.major; - this.minor = definition.minor; + constructor(desc: IStreamDesc) { + this.source = desc.source; + this.major = desc.major; + this.minor = desc.minor; } public isSame(stream: StreamDesc): boolean { diff --git a/application/client/src/app/service/history/definition.ts b/application/client/src/app/service/history/definition.ts index 0c7c95c9bb..c993a9159b 100644 --- a/application/client/src/app/service/history/definition.ts +++ b/application/client/src/app/service/history/definition.ts @@ -1,15 +1,15 @@ import { FileDesc, IFileDesc } from './definition.file'; import { StreamDesc, IStreamDesc } from './definition.stream'; -import { ConcatDesc } from './definition.concat'; +import { ConcatDesc } from './definition.files'; import { unique } from '@platform/env/sequence'; import { EntryConvertable, Entry } from '@platform/types/storage/entry'; import { error } from '@platform/log/utils'; import { Subject } from '@platform/env/subscription'; import { Equal } from '@platform/types/env/types'; import { Collections } from './collections'; +import { ObserveOperation } from '@service/session/dependencies/stream'; import * as obj from '@platform/env/obj'; -import { SessionOrigin } from '@service/session/origin'; export interface IDefinition { stream?: IStreamDesc; @@ -22,15 +22,15 @@ export interface GroupRelations { rank: number; caption?: string; } + export class Definition implements EntryConvertable, Equal { static from(entry: Entry): Definition { return Definition.fromMinifiedStr(JSON.parse(entry.content)); } - static async fromDataSource(origin: SessionOrigin): Promise { - console.error(`Not Implemented (parser isn't considered)`); - const file = await FileDesc.fromDataSource(origin); - const concat = await ConcatDesc.fromDataSource(origin); - const stream = await StreamDesc.fromDataSource(origin); + static async fromDataSource(operation: ObserveOperation): Promise { + const file = await FileDesc.fromDataSource(operation); + const concat = await ConcatDesc.fromDataSource(operation); + const stream = await StreamDesc.fromDataSource(operation); if (file !== undefined) { return new Definition({ file, diff --git a/application/client/src/app/service/history/session.ts b/application/client/src/app/service/history/session.ts index 18e4f4225b..0deb6ecfd7 100644 --- a/application/client/src/app/service/history/session.ts +++ b/application/client/src/app/service/history/session.ts @@ -66,23 +66,22 @@ export class HistorySession extends Subscriber { this.pendings.push(operation); return; } - console.error(`Not implemented`); - // Definition.fromDataSource(operation) - // .then((definition) => { - // definition = this.storage.definitions.update(definition); - // this.definitions.add(definition); - // this.collections.bind(definition); - // if (cli.isFiltersImported(this.session.uuid())) { - // // Filters are imported from CLI. Recent filters/charts should not - // // be applied - // return; - // } - // this.save(); - // this.check().all(); - // }) - // .catch((err: Error) => { - // this.log().error(`Fail to get definition of source: ${err.message}`); - // }); + Definition.fromDataSource(operation) + .then((definition) => { + definition = this.storage.definitions.update(definition); + this.definitions.add(definition); + this.collections.bind(definition); + if (cli.isFiltersImported(this.session.uuid())) { + // Filters are imported from CLI. Recent filters/charts should not + // be applied + return; + } + this.save(); + this.check().all(); + }) + .catch((err: Error) => { + this.log().error(`Fail to get definition of source: ${err.message}`); + }); } protected save() { diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 6183909453..c400352d69 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -161,6 +161,14 @@ export class SessionOrigin { return typeof (this.origin as { File: string }).File === 'string'; } + public isFiles(): boolean { + return (this.origin as { Files: string[] }).Files instanceof Array; + } + + public isStream(): boolean { + return typeof this.origin === 'string' && this.origin === 'Source'; + } + public isSameAction(other: SessionAction): boolean { if ((this.origin as { File: string }).File && (other as { File: string }).File) { return true; @@ -186,4 +194,12 @@ export class SessionOrigin { return undefined; } } + public getFiles(): string[] | undefined { + if ((this.origin as { Files: string[] }).Files) { + const files = (this.origin as { Files: string[] }).Files; + return files instanceof Array ? files : undefined; + } else { + return undefined; + } + } } From fe39131e937e9b39c48d86fb65e315ddee1567c7 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 10 Jul 2025 17:46:58 +0200 Subject: [PATCH 089/121] Correct recent actions checks --- .../app/service/history/definition.stream.ts | 11 +-- .../client/src/app/service/recent/action.ts | 6 +- application/client/src/app/service/session.ts | 96 ++++++------------- .../ui/elements/navigator/providers/entity.ts | 1 - 4 files changed, 36 insertions(+), 78 deletions(-) diff --git a/application/client/src/app/service/history/definition.stream.ts b/application/client/src/app/service/history/definition.stream.ts index 00f03ad202..40f74df7b0 100644 --- a/application/client/src/app/service/history/definition.stream.ts +++ b/application/client/src/app/service/history/definition.stream.ts @@ -19,8 +19,8 @@ export class StreamDesc implements IStreamDesc { return Promise.resolve(undefined); } return Promise.resolve({ - major: descriptor.s_desc ? descriptor.s_desc : descriptor.source.name, - minor: descriptor.p_desc ? descriptor.p_desc : descriptor.parser.name, + major: descriptor.parser.name, + minor: descriptor.s_desc ? descriptor.s_desc : descriptor.source.name, source: descriptor.source.name, }); } @@ -48,15 +48,12 @@ export class StreamDesc implements IStreamDesc { } public isSame(stream: StreamDesc): boolean { - console.error(`Not implemented`); - return false; - // return stream.source === this.source && this.major === stream.major; + return stream.source === this.source && this.major === stream.major; } public minify(): { [key: string]: number | string } { - console.error(`Not implemented`); return { - s: '', + s: this.source, mi: this.minor, ma: this.major, }; diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index 439f5d7446..4128dda889 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -27,7 +27,11 @@ export class Action { if (body.setup === undefined || typeof body.setup !== 'object') { throw new Error(`No origin object. It might be old format. No converting support`); } - obj.getAsObj(body.setup, 'origin'); + if (!body.setup.origin) { + throw new Error( + `No origin in setup object. It might be old format. No converting support`, + ); + } obj.getAsObj(body.setup, 'parser'); obj.getAsObj(body.setup, 'source'); if (body.descriptor === undefined || typeof body.descriptor !== 'object') { diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index e2bfe44a14..6b49b3eefc 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -240,6 +240,15 @@ export class Service extends Implementation { auto: (origin: SessionOrigin, session?: Session): Promise => { console.error(`Not implemented`); return Promise.reject(new Error(`Not implemented`)); + // Okay. Here actually we need make auto-run for text files for example. This requires some addition + // API on backend. We should send SessionAction.File or SessionAction.Files and try to detect on backend + // is possible to open without options or not. It means we are checking: + // 1) is only one source and only one parser to open this file(s) or maybe stream. + // 2) does parser and source have some options/settings + // 3) if only one parser and only one source and no options/settings are requred - we make preselection + // + // This workflow lived before on front-end, now it should lives on rustcore side. + // // return observe.locker().is() // ? this.initialize().observe(observe, session) // : this.initialize().configure(observe, session); @@ -290,20 +299,13 @@ export class Service extends Implementation { .observe() .start(origin) .then((uuid: string) => { - const error = this.bind(session.uuid(), uuid, true); + const error = + existed === undefined + ? this.bind(session.uuid(), uuid, true) + : undefined; if (error instanceof Error) { this.log().error(`Fail to bind session: ${error.message}`); } - // if (existed === undefined) { - // const error = this.bind( - // session.uuid(), - // observe.origin.desc().major, - // true, - // ); - // if (error instanceof Error) { - // this.log().error(`Fail to bind session: ${error.message}`); - // } - // } resolve(uuid); }) .catch((err: Error) => { @@ -319,48 +321,6 @@ export class Service extends Implementation { }); }); }); - - // const render = await getRender(observe); - // if (render instanceof Error) { - // throw render; - // } - // const session = - // existed !== undefined ? existed : await this.add(false).empty(render); - // return new Promise((resolve, reject) => { - // session.stream - // .observe() - // .start(observe) - // .then((uuid: string) => { - // if (existed === undefined) { - // const error = this.bind( - // session.uuid(), - // observe.origin.desc().major, - // true, - // ); - // if (error instanceof Error) { - // this.log().error(`Fail to bind session: ${error.message}`); - // } - // recent.add(observe).catch((err: Error) => { - // this.log().error( - // `Fail to save action as recent: ${err.message}`, - // ); - // }); - // } - // resolve(uuid); - // }) - // .catch((err: Error) => { - // if (existed !== undefined) { - // return reject(err); - // } - // this.kill(session.uuid()) - // .catch((closeErr: Error) => { - // this.log().error(`Fail to close session: ${closeErr.message}`); - // }) - // .finally(() => { - // reject(err); - // }); - // }); - // }); }, multiple: (files: File[]): Promise => { return new Promise((resolve, reject) => { @@ -371,26 +331,24 @@ export class Service extends Implementation { inputs: { files, setTitle: (title: string) => { - api?.setTitle(title); + api && api.setTitle(title); }, done: (origin: SessionOrigin) => { - console.error(`Not implemented`); - // .observe(observe) - // .then((session) => { - // resolve(session); - // }) - // .catch((err: Error) => { - // this.log().error( - // `Fail to setup observe: ${err.message}`, - // ); - // reject(err); - // }) - // .finally(() => { - // api?.close(); - // }); + this.initialize() + .observe(origin) + .then(resolve) + .catch((err: Error) => { + this.log().error( + `Fail to setup observe: ${err.message}`, + ); + reject(err); + }) + .finally(() => { + api && api.close(); + }); }, cancel: () => { - api?.close(); + api && api.close(); resolve(undefined); }, }, diff --git a/application/client/src/app/ui/elements/navigator/providers/entity.ts b/application/client/src/app/ui/elements/navigator/providers/entity.ts index 5b7fb8dd5d..4b2c80078c 100644 --- a/application/client/src/app/ui/elements/navigator/providers/entity.ts +++ b/application/client/src/app/ui/elements/navigator/providers/entity.ts @@ -26,7 +26,6 @@ export class Entity extends PassiveMatchee { public override asObj(): object { if (this.origin instanceof Action) { - console.error(`Not Implemented`); return { criteria: this.origin.description().major, }; From 95a11462f086a3bb2a4fe21d252f462eb3356fbb Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 11 Jul 2025 18:48:10 +0200 Subject: [PATCH 090/121] Resolve part of not implemented points --- .../app/service/session/dependencies/info.ts | 73 +++++-------------- .../service/session/dependencies/teamwork.ts | 31 ++++---- .../src/app/ui/elements/recent/component.ts | 9 --- .../client/src/app/ui/service/toolbar.ts | 16 ---- .../app/ui/views/sidebar/search/component.ts | 21 ++---- .../holder/src/service/sessions/holder.ts | 55 ++++---------- .../sessions/requests/session/export.raw.ts | 2 +- .../sessions/requests/session/export.ts | 2 +- 8 files changed, 55 insertions(+), 154 deletions(-) diff --git a/application/client/src/app/service/session/dependencies/info.ts b/application/client/src/app/service/session/dependencies/info.ts index c09c822477..08db86f164 100644 --- a/application/client/src/app/service/session/dependencies/info.ts +++ b/application/client/src/app/service/session/dependencies/info.ts @@ -19,62 +19,23 @@ export class Info { } public fromOperation(operation: ObserveOperation) { - const uuid = operation.uuid; - console.error(`Not Implemented`); - // if (observe.configuration.parser[Protocol.Dlt] !== undefined) { - // const cfg = observe.configuration.parser[Protocol.Dlt]; - // if (cfg.fibex_file_paths !== undefined) { - // if (cfg.fibex_file_paths.length === 1) { - // this.add( - // { - // caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, - // tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, - // }, - // `fibex_${uuid}`, - // ); - // } else if (cfg.fibex_file_paths.length > 1) { - // this.add( - // { - // caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, - // tooltip: cfg.fibex_file_paths.join('\n'), - // }, - // `fibex_${uuid}`, - // ); - // } - // } - // if (cfg.tz !== undefined) { - // this.add( - // { - // caption: `TZ: ${cfg.tz}`, - // tooltip: `Used TimeZone: ${cfg.tz}`, - // }, - // `tz_${uuid}`, - // ); - // } - // } else if (observe.configuration.parser[Protocol.SomeIp] !== undefined) { - // const cfg = observe.configuration.parser[Protocol.SomeIp]; - // if (cfg.fibex_file_paths !== undefined) { - // if (cfg.fibex_file_paths.length === 1) { - // this.add( - // { - // caption: `FIBEX: ${getFileName(cfg.fibex_file_paths[0])}`, - // tooltip: `Used FIBEX: ${cfg.fibex_file_paths[0]}`, - // }, - // `fibex_${uuid}`, - // ); - // } else if (cfg.fibex_file_paths.length > 1) { - // this.add( - // { - // caption: `FIBEX: ${cfg.fibex_file_paths.length} files`, - // tooltip: cfg.fibex_file_paths.join('\n'), - // }, - // `fibex_${uuid}`, - // ); - // } - // } - // } else if (observe.configuration.parser[Protocol.Text] !== undefined) { - // // ignore - // } + // TODO (Not implemented. API required): + // + // Purpose of this service + // + // When a session is created, auxiliary information may be displayed in the bottom toolbar. + // For example, for DLT and SomeIP files (or TCP/UDP connections), this could be information + // about the associated FIBEX file. + // + // In the previous version, the client had knowledge about the used parser/source, + // so this information was generated directly on the client side. + // + // Due to the updated paradigm, where the client "knows nothing" about the parsers + // and sources being used, an additional API on the rustcore side is required. + // This API provides the "messages" that need to be displayed in the toolbar. + // + // Currently, this functionality is disabled. + console.warn(`API for toolbar notes is not implemented yet`); } public add(block: IInfoBlock, uuid?: string): string { diff --git a/application/client/src/app/service/session/dependencies/teamwork.ts b/application/client/src/app/service/session/dependencies/teamwork.ts index c830b0b37a..373ac71c50 100644 --- a/application/client/src/app/service/session/dependencies/teamwork.ts +++ b/application/client/src/app/service/session/dependencies/teamwork.ts @@ -390,22 +390,21 @@ export class TeamWork extends Subscriber { if (this.checksum === null) { return; } - console.error(`Not implemented`); - // FileDesc.fromDataSource(operation) - // .then((desc) => { - // if (desc === undefined) { - // this.checksum = null; - // } else if (this.checksum === undefined) { - // this.checksum = desc.checksum; - // this.file().check(); - // } else { - // this.checksum = null; - // } - // }) - // .catch((err: Error) => { - // this.checksum = null; - // this.error().add(`Fail get chechsum of file: ${err.message}`); - // }); + FileDesc.fromDataSource(operation) + .then((desc) => { + if (desc === undefined) { + this.checksum = null; + } else if (this.checksum === undefined) { + this.checksum = desc.checksum; + this.file().check(); + } else { + this.checksum = null; + } + }) + .catch((err: Error) => { + this.checksum = null; + this.error().add(`Fail get chechsum of file: ${err.message}`); + }); }), history.subjects.get().created.subscribe((uuid: string) => { const session = history.get(uuid); diff --git a/application/client/src/app/ui/elements/recent/component.ts b/application/client/src/app/ui/elements/recent/component.ts index 0693758263..7a24360303 100644 --- a/application/client/src/app/ui/elements/recent/component.ts +++ b/application/client/src/app/ui/elements/recent/component.ts @@ -50,15 +50,6 @@ export class RecentActions extends ChangesDetector implements AfterContentInit, this.detectChanges(); }), ); - if (this.origin !== undefined) { - console.error(`Not Implemented`); - // this.env().subscriber.register( - // this.operation.subscribe(() => { - // this.state.reload(); - // this.detectChanges(); - // }), - // ); - } } public ngAfterViewInit(): void { diff --git a/application/client/src/app/ui/service/toolbar.ts b/application/client/src/app/ui/service/toolbar.ts index fb980a0e8a..741ac28ca1 100644 --- a/application/client/src/app/ui/service/toolbar.ts +++ b/application/client/src/app/ui/service/toolbar.ts @@ -10,7 +10,6 @@ import { Tabs } from './toolbar/tabs'; @SetupService(ui['toolbar']) export class Service extends Implementation { - private _emitter!: Emitter; private _channel!: Channel; private _services!: Services; private _available!: Tabs; @@ -18,10 +17,8 @@ export class Service extends Implementation { private _sessions: Map = new Map(); public override ready(): Promise { - this._emitter = ilc.emitter(this.getName(), this.log()); this._channel = ilc.channel(this.getName(), this.log()); this._services = ilc.services(this.getName(), this.log()); - this._channel.ux.hotkey(this._onHotKey.bind(this)); this._channel.session.change(this._onSessionChange.bind(this)); this._channel.session.closed(this._onSessionClosed.bind(this)); this._channel.ui.toolbar.view(this._onViewChange.bind(this)); @@ -137,19 +134,6 @@ export class Service extends Implementation { }); } - private _onHotKey(event: Declarations.HotkeyEvent) { - console.log(`Not implemented: ${event}`); - // if (this._active === undefined) { - // return; - // } - // const service: TabsService | undefined = this._sessions.get(this._active); - // if (service === undefined) { - // return; - // } - // LayoutStateService.toolbarMax(); - // service.setActive(UUIDs.search); - } - private _onSessionClosed(session: string) { const service: TabsService | undefined = this._sessions.get(session); if (service === undefined) { diff --git a/application/client/src/app/ui/views/sidebar/search/component.ts b/application/client/src/app/ui/views/sidebar/search/component.ts index 91a884a8ed..606631075e 100644 --- a/application/client/src/app/ui/views/sidebar/search/component.ts +++ b/application/client/src/app/ui/views/sidebar/search/component.ts @@ -54,20 +54,15 @@ export class Filters extends ChangesDetector implements OnDestroy, AfterContentI this._focused = false; } @HostListener('contextmenu', ['$event']) onContextMenu(event: MouseEvent) { - const items: IMenuItem[] = [ - { - caption: `Clear recent history`, - handler: () => { - this.log().debug(`Not implemented yet`); - }, - }, - ]; + const items: IMenuItem[] = []; this.providers.injectGeneralMenuItems(items); - contextmenu.show({ - items, - x: event.pageX, - y: event.pageY, - }); + if (items.length > 0) { + contextmenu.show({ + items, + x: event.pageX, + y: event.pageY, + }); + } dom.stop(event); } diff --git a/application/holder/src/service/sessions/holder.ts b/application/holder/src/service/sessions/holder.ts index 1bd4211372..547b4f351a 100644 --- a/application/holder/src/service/sessions/holder.ts +++ b/application/holder/src/service/sessions/holder.ts @@ -142,48 +142,19 @@ export class Holder { }; } - public getFileExt(): string | Error { - return new Error(`Not implemented`); - // const all = [ - // Array.from(this.observing.active.values()).map((o) => o.source), - // Array.from(this.observing.finished.values()), - // ].flat(); - // const files: Array = all - // .map((o) => o.origin.files()) - // .filter((f) => f !== undefined) - // .flat(); - // if (files.filter((f) => f === undefined).length > 0) { - // return new Error(`Streams arn't supported yet`); - // } - // const parsers: $.Parser.Protocol[] = []; - // all.forEach((observe) => { - // if (parsers.includes(observe.parser.alias())) { - // return; - // } - // parsers.push(observe.parser.alias()); - // }); - // if (parsers.length > 1) { - // return new Error(`Multiple parsers are used`); - // } else if (parsers.length === 0) { - // return new Error(`No parsers has been found`); - // } - // const exts: string[] = files - // .map((f) => path.extname(f as string)) - // .filter((ex) => ex.trim() !== ''); - // switch (parsers[0]) { - // case $.Parser.Protocol.Text: - // return `.txt`; - // case $.Parser.Protocol.Plugin: - // return exts.length === 0 ? '.plg' : exts[0]; - // case $.Parser.Protocol.Dlt: - // case $.Parser.Protocol.SomeIp: - // if (files.length === 0) { - // return new Error( - // `No assigned files are found. Exporting from stream into new session arn't supported`, - // ); - // } - // return exts.length === 0 ? '' : exts[0]; - // } + public getExportedFileExt(): string | Error { + // TODO (Not implemented. API required) + // Since in the new paradigm the client has no knowledge of which specific + // parser/source is being used, implementing this functionality requires an additional API. + // Specifically, rustcore should return the appropriate file extension for exporting data, + // based on the parser UUID. + // + // For example, if the active session is DLT over UDP, the file extension should be *.dlt. + // If it’s SomeIP over TCP, the expected extension would likely be *.pcap. + // + // The client cannot determine this reliably on its own, which is why an API is needed + // to associate the parser with the correct logic for exporting data. + return new Error(`getExportedFileExt requires API. Not implemented`); } public isShutdowning(): boolean { diff --git a/application/holder/src/service/sessions/requests/session/export.raw.ts b/application/holder/src/service/sessions/requests/session/export.raw.ts index 022a73be8d..846a88c620 100644 --- a/application/holder/src/service/sessions/requests/session/export.raw.ts +++ b/application/holder/src/service/sessions/requests/session/export.raw.ts @@ -24,7 +24,7 @@ export const handler = Requests.InjectLogger< if (request.dest !== undefined) { return request.dest; } else { - const ext = stored.getFileExt(); + const ext = stored.getExportedFileExt(); if (ext instanceof Error) { return ext; } diff --git a/application/holder/src/service/sessions/requests/session/export.ts b/application/holder/src/service/sessions/requests/session/export.ts index 0500fa0c23..d3d2a937e4 100644 --- a/application/holder/src/service/sessions/requests/session/export.ts +++ b/application/holder/src/service/sessions/requests/session/export.ts @@ -24,7 +24,7 @@ export const handler = Requests.InjectLogger< if (request.dest !== undefined) { return request.dest; } else { - const ext = stored.getFileExt(); + const ext = stored.getExportedFileExt(); if (ext instanceof Error) { return ext; } From 8890fb124c8be0b3c1a6a9cd20b72f8aeca5799e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 14 Jul 2025 14:26:58 +0200 Subject: [PATCH 091/121] Fix view of recent actions --- .../client/src/app/service/recent/action.ts | 2 +- .../src/app/ui/elements/navigator/styles.less | 3 ++ .../app/ui/elements/navigator/template.html | 36 +++++++++++++------ .../src/app/ui/elements/recent/action.ts | 13 +++++++ .../src/app/ui/elements/recent/styles.less | 29 ++++++++------- .../src/app/ui/elements/recent/template.html | 2 +- 6 files changed, 60 insertions(+), 25 deletions(-) diff --git a/application/client/src/app/service/recent/action.ts b/application/client/src/app/service/recent/action.ts index 4128dda889..2b6f1a1375 100644 --- a/application/client/src/app/service/recent/action.ts +++ b/application/client/src/app/service/recent/action.ts @@ -6,7 +6,7 @@ import { Stat, IStat } from './stat'; import { recent } from '@service/recent'; import { scope } from '@platform/env/scope'; import { Logger } from '@platform/log'; -import { SessionComponents, SessionOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; import { ObserveOperation } from '@service/session/dependencies/stream'; import { SessionDescriptor, SessionSetup } from '@platform/types/bindings'; import { hash } from '@platform/env/hash'; diff --git a/application/client/src/app/ui/elements/navigator/styles.less b/application/client/src/app/ui/elements/navigator/styles.less index 2337b087a0..c43bc7e6c6 100644 --- a/application/client/src/app/ui/elements/navigator/styles.less +++ b/application/client/src/app/ui/elements/navigator/styles.less @@ -118,6 +118,9 @@ app-navigator { & span.major { margin-right: 12px; } + & span.minor { + padding-left: 12px; + } & span.major, & span.minor { & > span { diff --git a/application/client/src/app/ui/elements/navigator/template.html b/application/client/src/app/ui/elements/navigator/template.html index 840eb1437c..8bc0e6d81c 100644 --- a/application/client/src/app/ui/elements/navigator/template.html +++ b/application/client/src/app/ui/elements/navigator/template.html @@ -4,15 +4,23 @@

{{collection.title}} - +
-
-

- - + (contextmenu)="ngItemContextMenu($event, entity)" + > +

+ +

@@ -21,7 +29,7 @@

{{statistics.title}}

-

{{info}}

+

{{info}}

@@ -34,10 +42,18 @@

{{state.getNoContentActions(collection.index).title}}

-
- +
+
- diff --git a/application/client/src/app/ui/elements/recent/action.ts b/application/client/src/app/ui/elements/recent/action.ts index 96955ee90f..250214bbb7 100644 --- a/application/client/src/app/ui/elements/recent/action.ts +++ b/application/client/src/app/ui/elements/recent/action.ts @@ -14,15 +14,28 @@ export class WrappedAction extends Matchee { this.action = action; } + protected getIcon(): string { + const origin = this.action.setup.origin; + if ((origin as { File: string }).File) { + return 'insert_drive_file'; + } else if ((origin as { Files: string[] }).Files) { + return 'folder_open'; + } else { + return 'compare_arrows'; + } + } + public description(): { major: string; minor: string; + icon: string; } { const major: string | undefined = this.getHtmlOf('html_major'); const minor: string | undefined = this.getHtmlOf('html_minor'); return { major: major === undefined ? this.action.description().major : major, minor: minor === undefined ? this.action.description().minor : minor, + icon: this.getIcon(), }; } diff --git a/application/client/src/app/ui/elements/recent/styles.less b/application/client/src/app/ui/elements/recent/styles.less index 72b72ad278..cfb91f0153 100644 --- a/application/client/src/app/ui/elements/recent/styles.less +++ b/application/client/src/app/ui/elements/recent/styles.less @@ -14,25 +14,28 @@ app-recent-actions { flex-direction: row; align-items: flex-start; & div.column_0 { - + & mat-icon { + height: 16px; + width: 16px; + font-size: 16px; + padding-right: 8px; + } } & div.column_1 { flex: auto; + & p.major { + color: var(--scheme-color-0); + } + & p.minor { + color: var(--scheme-color-2); + } + & * { + user-select: none; + cursor: default; + } } &:hover { background: var(--scheme-color-hover); - &::before { - background: var(--scheme-color-hover); - } - } - &::before { - position: absolute; - display: block; - width: ~"calc(100% + 30px)"; - content: ''; - top:0; - left: -15px; - height: 100%; } & p.description { position: relative; diff --git a/application/client/src/app/ui/elements/recent/template.html b/application/client/src/app/ui/elements/recent/template.html index ae60acaaee..d44a0c69ba 100644 --- a/application/client/src/app/ui/elements/recent/template.html +++ b/application/client/src/app/ui/elements/recent/template.html @@ -5,7 +5,7 @@ (click)="onDefaultAction(action.action)" (contextmenu)="onAllActions($event, action.action)" > -
IC
+
{{action.description().icon}}

{{action.description().major}}

{{action.description().minor}}

From 0e3656e4a088dab24a061199e0dfce5789df9bd7 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 15 Jul 2025 09:18:46 +0200 Subject: [PATCH 092/121] Remove manual validation of types of rust events --- .../src/api/components.provider.ts | 36 +----- .../ts-bindings/src/api/session.provider.ts | 105 +----------------- .../ts-bindings/src/api/tracker.provider.ts | 45 +------- .../src/native/native.components.ts | 18 +-- .../ts-bindings/src/native/native.session.ts | 10 +- .../ts-bindings/src/provider/provider.ts | 28 +---- 6 files changed, 14 insertions(+), 228 deletions(-) diff --git a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts index 805bc57d20..088b435e5c 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.provider.ts @@ -17,8 +17,6 @@ export interface IComponentsEvents { Destroyed: Subject; } -export interface IComponentsEventsConvertors {} - export interface IComponentsEventsSignatures { LoadingDone: 'LoadingDone'; LoadingErrors: 'LoadingErrors'; @@ -35,26 +33,9 @@ const SessionEventsSignatures: IComponentsEventsSignatures = { Destroyed: 'Destroyed', }; -export interface IComponentsEventsInterfaces { - LoadingDone: { self: 'object'; owner: 'string'; fields: typeof Array }; - LoadingErrors: { self: 'object'; owner: 'string'; errors: typeof Array }; - LoadingError: { self: 'object'; owner: 'string'; error: 'string'; fields: typeof Array }; - LoadingCancelled: { self: 'object'; owner: 'string'; fields: typeof Array }; - Destroyed: { self: null }; -} - -const SessionEventsInterfaces: IComponentsEventsInterfaces = { - LoadingDone: { self: 'object', owner: 'string', fields: Array }, - LoadingErrors: { self: 'object', owner: 'string', errors: Array }, - LoadingError: { self: 'object', owner: 'string', error: 'string', fields: Array }, - LoadingCancelled: { self: 'object', owner: 'string', fields: Array }, - Destroyed: { self: null }, -}; - export class ComponentsEventProvider extends Computation< IComponentsEvents, - IComponentsEventsSignatures, - IComponentsEventsInterfaces + IComponentsEventsSignatures > { private readonly _events: IComponentsEvents = { LoadingDone: new Subject(), @@ -64,8 +45,6 @@ export class ComponentsEventProvider extends Computation< Destroyed: new Subject(), }; - private readonly _convertors: IComponentsEventsConvertors = {}; - constructor(uuid: string) { super(uuid, protocol.decodeCallbackOptionsEvent); } @@ -81,17 +60,4 @@ export class ComponentsEventProvider extends Computation< public getEventsSignatures(): IComponentsEventsSignatures { return SessionEventsSignatures; } - - public getEventsInterfaces(): IComponentsEventsInterfaces { - return SessionEventsInterfaces; - } - - public getConvertor(event: keyof IComponentsEventsSignatures, data: T): T | O | Error { - const convertors = this._convertors as unknown as { [key: string]: (data: T) => T | O }; - if (typeof convertors[event] !== 'function') { - return data; - } else { - return convertors[event](data); - } - } } diff --git a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts index d6d88da579..8da4ba775d 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.provider.ts @@ -75,8 +75,6 @@ export interface ISessionEvents { OperationDone: Subject; } -export interface ISessionEventsConvertors {} - interface ISessionEventsSignatures { StreamUpdated: 'StreamUpdated'; FileRead: 'FileRead'; @@ -117,93 +115,7 @@ const SessionEventsSignatures: ISessionEventsSignatures = { OperationDone: 'OperationDone', }; -interface ISessionEventsInterfaces { - StreamUpdated: { self: 'number' }; - FileRead: { self: null }; - SearchUpdated: { self: 'object'; found: 'number'; stat: typeof Map }; - SearchValuesUpdated: { self: ['object', null] }; - SearchMapUpdated: { self: [typeof Array, null] }; - MapUpdated: { self: 'object'; map: typeof Array }; - IndexedMapUpdated: { self: 'object'; len: 'number' }; - MatchesUpdated: { self: 'object'; matches: typeof Array }; - Progress: { - self: 'object'; - uuid: 'string'; - progress: [ - { self: 'object'; total: 'number'; count: 'number' }, - { self: 'object'; type: 'string' }, - ]; - }; - AttachmentsUpdated: { self: 'object'; len: 'number'; attachment: typeof Object }; - SessionError: { self: 'object'; severity: 'string'; message: 'string'; kind: 'string' }; - OperationError: { - self: 'object'; - uuid: 'string'; - error: { self: 'object'; severity: 'string'; message: 'string'; kind: 'string' }; - }; - SessionDestroyed: { self: null }; - OperationStarted: { self: 'string' }; - SessionDescriptor: { - self: 'object'; - uuid: 'string'; - desc: { - self: 'object'; - parser: 'object'; - source: 'object'; - s_desc: ['string', null]; - p_desc: ['string', null]; - }; - }; - OperationProcessing: { self: 'string' }; - OperationDone: { self: 'object'; uuid: 'string'; result: 'any' }; -} - -const SessionEventsInterfaces: ISessionEventsInterfaces = { - StreamUpdated: { self: 'number' }, - FileRead: { self: null }, - SearchUpdated: { self: 'object', found: 'number', stat: Map }, - SearchValuesUpdated: { self: ['object', null] }, - SearchMapUpdated: { self: [Array, null] }, - MapUpdated: { self: 'object', map: Array }, - IndexedMapUpdated: { self: 'object', len: 'number' }, - MatchesUpdated: { self: 'object', matches: Array }, - Progress: { - self: 'object', - uuid: 'string', - progress: [ - { self: 'object', total: 'number', count: 'number' }, - { self: 'object', type: 'string' }, - ], - }, - AttachmentsUpdated: { self: 'object', len: 'number', attachment: Object }, - SessionError: { self: 'object', severity: 'string', message: 'string', kind: 'string' }, - OperationError: { - self: 'object', - uuid: 'string', - error: { self: 'object', severity: 'string', message: 'string', kind: 'string' }, - }, - SessionDestroyed: { self: null }, - OperationStarted: { self: 'string' }, - SessionDescriptor: { - self: 'object', - uuid: 'string', - desc: { - self: 'object', - parser: 'object', - source: 'object', - s_desc: ['string', null], - p_desc: ['string', null], - }, - }, - OperationProcessing: { self: 'string' }, - OperationDone: { self: 'object', uuid: 'string', result: 'any' }, -}; - -export class EventProvider extends Computation< - ISessionEvents, - ISessionEventsSignatures, - ISessionEventsInterfaces -> { +export class EventProvider extends Computation { private readonly _events: ISessionEvents = { StreamUpdated: new Subject(), FileRead: new Subject(), @@ -224,8 +136,6 @@ export class EventProvider extends Computation< OperationDone: new Subject(), }; - private readonly _convertors: ISessionEventsConvertors = {}; - constructor(uuid: string) { super(uuid, protocol.decodeCallbackEvent); } @@ -241,17 +151,4 @@ export class EventProvider extends Computation< public getEventsSignatures(): ISessionEventsSignatures { return SessionEventsSignatures; } - - public getEventsInterfaces(): ISessionEventsInterfaces { - return SessionEventsInterfaces; - } - - public getConvertor(event: keyof ISessionEventsSignatures, data: T): T | O | Error { - const convertors = this._convertors as unknown as { [key: string]: (data: T) => T | O }; - if (typeof convertors[event] !== 'function') { - return data; - } else { - return convertors[event](data); - } - } } diff --git a/application/apps/rustcore/ts-bindings/src/api/tracker.provider.ts b/application/apps/rustcore/ts-bindings/src/api/tracker.provider.ts index 84def0887f..cad92423f0 100644 --- a/application/apps/rustcore/ts-bindings/src/api/tracker.provider.ts +++ b/application/apps/rustcore/ts-bindings/src/api/tracker.provider.ts @@ -36,37 +36,7 @@ const SessionEventsSignatures: ISessionEventsSignatures = { Ticks: 'Ticks', }; -interface ISessionEventsInterfaces { - Started: { self: 'object'; uuid: 'string'; alias: 'string' }; - Stopped: { self: 'string' }; - Ticks: { - self: 'object'; - uuid: 'string'; - progress: [ - { self: 'object'; total: 'number'; count: 'number' }, - { self: 'object'; type: 'string' }, - ]; - }; -} - -const SessionEventsInterfaces: ISessionEventsInterfaces = { - Started: { self: 'object', uuid: 'string', alias: 'string' }, - Stopped: { self: 'string' }, - Ticks: { - self: 'object', - uuid: 'string', - progress: [ - { self: 'object', total: 'number', count: 'number' }, - { self: 'object', type: 'string' }, - ], - }, -}; - -export class EventProvider extends Computation< - ISessionEvents, - ISessionEventsSignatures, - ISessionEventsInterfaces -> { +export class EventProvider extends Computation { private readonly _events: ISessionEvents = { Started: new Subject(), Stopped: new Subject(), @@ -90,17 +60,4 @@ export class EventProvider extends Computation< public getEventsSignatures(): ISessionEventsSignatures { return SessionEventsSignatures; } - - public getEventsInterfaces(): ISessionEventsInterfaces { - return SessionEventsInterfaces; - } - - public getConvertor(event: keyof ISessionEventsSignatures, data: T): T | O | Error { - const convertors = this._convertors as unknown as { [key: string]: (data: T) => T | O }; - if (typeof convertors[event] !== 'function') { - return data; - } else { - return convertors[event](data); - } - } } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 30461a9155..884066fb18 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -16,11 +16,7 @@ import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; import { Computation } from '../provider/provider'; import { Subscriber } from 'platform/env/subscription'; -import { - IComponentsEvents, - IComponentsEventsSignatures, - IComponentsEventsInterfaces, -} from '../api/components.provider'; +import { IComponentsEvents, IComponentsEventsSignatures } from '../api/components.provider'; import { InvalidPluginEntity, @@ -86,20 +82,12 @@ type DestroyResolver = () => void; export class Base extends Subscriber { protected readonly logger: Logger = scope.getLogger(`Components`); protected readonly native: ComponentsNative; - protected readonly provider: Computation< - IComponentsEvents, - IComponentsEventsSignatures, - IComponentsEventsInterfaces - >; + protected readonly provider: Computation; private state: State = State.Created; private destroyResolver: DestroyResolver | undefined; constructor( - provider: Computation< - IComponentsEvents, - IComponentsEventsSignatures, - IComponentsEventsInterfaces - >, + provider: Computation, resolver: (err: Error | undefined) => void, ) { super(); diff --git a/application/apps/rustcore/ts-bindings/src/native/native.session.ts b/application/apps/rustcore/ts-bindings/src/native/native.session.ts index ab1ce962fa..990508aff1 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.session.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.session.ts @@ -22,14 +22,14 @@ import * as types from 'platform/types'; export type RustSessionConstructorImpl = new ( uuid: string, - provider: Computation, + provider: Computation, cb: (err: Error | undefined) => void, ) => T; export type TCanceler = () => void; // Create abstract class to declare available methods export abstract class RustSession extends RustSessionRequiered { - constructor(uuid: string, provider: Computation) { + constructor(uuid: string, provider: Computation) { super(); } @@ -373,7 +373,7 @@ export abstract class RustSessionNative { export function rustSessionFactory( uuid: string, - provider: Computation, + provider: Computation, ): Promise { return new Promise((resolve, reject) => { const session = new RustSessionConstructor(uuid, provider, (err: Error | undefined) => { @@ -393,11 +393,11 @@ export class RustSessionWrapper extends RustSession { private readonly _uuid: string; private readonly _native: RustSessionNative; private _assigned: boolean = false; - private _provider: Computation; + private _provider: Computation; constructor( uuid: string, - provider: Computation, + provider: Computation, cb: (err: Error | undefined) => void, ) { super(uuid, provider); diff --git a/application/apps/rustcore/ts-bindings/src/provider/provider.ts b/application/apps/rustcore/ts-bindings/src/provider/provider.ts index 0fa059ef36..483fdbf3ce 100644 --- a/application/apps/rustcore/ts-bindings/src/provider/provider.ts +++ b/application/apps/rustcore/ts-bindings/src/provider/provider.ts @@ -16,7 +16,7 @@ export interface IOrderStat { emitted: number; // Time of emitting event or operation duration: number; } -export abstract class Computation { +export abstract class Computation { private _destroyed: boolean = false; protected readonly uuid: string; protected readonly tracking: { @@ -77,10 +77,6 @@ export abstract class Computation public abstract getEventsSignatures(): IEventsSignatures; - public abstract getEventsInterfaces(): IEventsInterfaces; - - public abstract getConvertor(event: string, data: T): T | O | Error; - public debug(): { getEvents(): { unsupported: Subject; @@ -285,26 +281,8 @@ export abstract class Computation } else { this.debug().emit.event(event); } - const err: Error | undefined = validateEventDesc( - (this.getEventsInterfaces() as any)[event], - data, - ); - - if (err instanceof Error) { - this.debug().emit.error(`Error: ${error(err)}. Input: ${JSON.stringify(data)}`); - this.logger.error(`Failed to parse event "${event}" due error: ${error(err)}`); - } else { - const converted = data === null ? data : this.getConvertor(event, data); - if (converted instanceof Error) { - this.logger.error( - `Failed to convert results fro event "${event}" due error: ${error( - converted, - )}`, - ); - } - (this.getEvents() as any)[event].emit(converted); - this.logger.verbose(`Event "${event}" is processed`); - } + (this.getEvents() as any)[event].emit(data); + this.logger.verbose(`Event "${event}" is processed`); } }, 0); } From d037b4d3a36667aa310bf69fc09283f98a0431c3 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 15 Jul 2025 14:09:44 +0200 Subject: [PATCH 093/121] Add active/inactive observe lists --- .../dependencies/observing/operation.ts | 30 ++++++-- .../service/session/dependencies/stream.ts | 21 ++++++ .../ui/views/sidebar/observe/common/module.ts | 10 --- .../sidebar/observe/common/title/component.ts | 33 --------- .../sidebar/observe/common/title/styles.less | 23 ------ .../observe/common/title/template.html | 5 -- .../app/ui/views/sidebar/observe/component.ts | 10 ++- .../app/ui/views/sidebar/observe/module.ts | 8 +-- .../sidebar/observe/operation/component.ts | 71 +++++++++++++++++++ .../sidebar/observe/operation/styles.less | 38 ++++++++++ .../sidebar/observe/operation/template.html | 13 ++++ .../ui/views/sidebar/observe/template.html | 24 ++++++- 12 files changed, 200 insertions(+), 86 deletions(-) delete mode 100644 application/client/src/app/ui/views/sidebar/observe/common/module.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/common/title/component.ts delete mode 100644 application/client/src/app/ui/views/sidebar/observe/common/title/styles.less delete mode 100644 application/client/src/app/ui/views/sidebar/observe/common/title/template.html create mode 100644 application/client/src/app/ui/views/sidebar/observe/operation/component.ts create mode 100644 application/client/src/app/ui/views/sidebar/observe/operation/styles.less create mode 100644 application/client/src/app/ui/views/sidebar/observe/operation/template.html diff --git a/application/client/src/app/service/session/dependencies/observing/operation.ts b/application/client/src/app/service/session/dependencies/observing/operation.ts index a228787476..ef8f5818ab 100644 --- a/application/client/src/app/service/session/dependencies/observing/operation.ts +++ b/application/client/src/app/service/session/dependencies/observing/operation.ts @@ -1,15 +1,17 @@ import { SdeRequest, SdeResponse } from '@platform/types/sde'; -import { SessionSetup, SessionDescriptor } from '@platform/types/bindings'; +import { SessionDescriptor } from '@platform/types/bindings'; import { SessionOrigin } from '@service/session/origin'; +import { Subject } from '@platform/env/subscription'; type SdeAPIFunc = (msg: SdeRequest) => Promise; type RestartingAPIFunc = (setup: SessionOrigin) => Promise; type CancelAPIFunc = () => Promise; export enum State { - Started, - Running, - Done, + Started = 'started', + Running = 'running', + Done = 'finished', + Aborted = 'aborted', } export class ObserveOperation { @@ -20,8 +22,10 @@ export class ObserveOperation { protected restarting: RestartingAPIFunc | undefined; protected cancel: CancelAPIFunc | undefined; protected origin: SessionOrigin | undefined; + protected sourceId: number = -1; public state: State = State.Started; + public stateUpdateEvent: Subject = new Subject(); constructor(public readonly uuid: string) {} @@ -36,15 +40,26 @@ export class ObserveOperation { this.restarting = restarting; this.cancel = cancel; this.state = State.Running; + this.stateUpdateEvent.emit(); + } + + public destroy() { + this.stateUpdateEvent.destroy(); } public abort(): Promise { + if (this.state === State.Aborted || this.state === State.Done) { + return Promise.resolve(); + } if (!this.cancel) { return Promise.reject( new Error(`"abort" cannot be called, because ObserveOperation isn't bound`), ); } - return this.cancel(); + return this.cancel().finally(() => { + this.state = State.Aborted; + this.stateUpdateEvent.emit(); + }); } public restart(): Promise { @@ -58,12 +73,17 @@ export class ObserveOperation { public finish() { this.state = State.Done; + this.stateUpdateEvent.emit(); } public isRunning(): boolean { return this.state === State.Running; } + public isStopped(): boolean { + return this.state === State.Done || this.state == State.Aborted; + } + public isSame(operation: ObserveOperation): boolean { return this.uuid === operation.uuid; } diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index bed4606997..a04cf4d416 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -125,6 +125,12 @@ export class Stream extends Subscriber { this.unsubscribe(); this.subjects.destroy(); this.sde.destroy(); + this.observed.operations.forEach((operation) => { + operation.abort().catch((err: Error) => { + this.log().warn(`Fail to abort operation: ${err.message}`); + }); + operation.destroy(); + }); } public len(): number { @@ -216,6 +222,15 @@ export class Stream extends Subscriber { .finally(lockers.progress(`Creating session...`)); }, abort: (uuid: string): Promise => { + const operation = this.observed.operations.get(uuid); + if (!operation) { + return Promise.reject( + new Error(`Operation ${uuid} doesn't exist. Cannot abort`), + ); + } + if (operation.isStopped()) { + return Promise.resolve(); + } return new Promise((resolve, reject) => { Requests.IpcRequest.send( Requests.Observe.Abort.Response, @@ -238,6 +253,12 @@ export class Stream extends Subscriber { }); }, restart: (uuid: string, options: SessionOrigin): Promise => { + const operation = this.observed.operations.get(uuid); + if (!operation) { + return Promise.reject( + new Error(`Operation ${uuid} doesn't exist. Cannot restart`), + ); + } return this.observe() .abort(uuid) .then(() => { diff --git a/application/client/src/app/ui/views/sidebar/observe/common/module.ts b/application/client/src/app/ui/views/sidebar/observe/common/module.ts deleted file mode 100644 index a2f9f3f453..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/common/module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { Title } from './title/component'; - -@NgModule({ - imports: [CommonModule], - declarations: [Title], - exports: [Title], -}) -export class CommonObserveModule {} diff --git a/application/client/src/app/ui/views/sidebar/observe/common/title/component.ts b/application/client/src/app/ui/views/sidebar/observe/common/title/component.ts deleted file mode 100644 index ec30184e2e..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/common/title/component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; -import { Ilc, IlcInterface } from '@env/decorators/component'; - -export interface IButton { - icon: string; - handler: () => void; -} - -@Component({ - selector: 'app-views-observed-list-title', - templateUrl: './template.html', - styleUrls: ['./styles.less'], - standalone: false, -}) -@Ilc() -export class Title { - @Input() title!: string; - @Input() subtitle: string | undefined; - @Input() buttons: IButton[] = []; - @Input() opened!: boolean; - @Input() hideToggle: boolean | undefined; - @Output() toggled: EventEmitter = new EventEmitter(); - - public click(button: IButton) { - button.handler(); - } - - public toggle() { - this.opened = !this.opened; - this.toggled.emit(this.opened); - } -} -export interface Title extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/common/title/styles.less b/application/client/src/app/ui/views/sidebar/observe/common/title/styles.less deleted file mode 100644 index fa1135c628..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/common/title/styles.less +++ /dev/null @@ -1,23 +0,0 @@ -@import '../../../../../styles/variables.less'; - -:host { - position: relative; - display: flex; - padding: 0 24px; - margin: 0; - text-align: left; - align-items: center; - white-space: nowrap; - font-size: 0.9rem; - font-weight: 400; - span.title{ - color: var(--scheme-color-2); - } - span.subtitle{ - padding-left: 6px; - color: var(--scheme-color-3); - } - & span.filler { - flex: auto; - } -} diff --git a/application/client/src/app/ui/views/sidebar/observe/common/title/template.html b/application/client/src/app/ui/views/sidebar/observe/common/title/template.html deleted file mode 100644 index 62d2371e3c..0000000000 --- a/application/client/src/app/ui/views/sidebar/observe/common/title/template.html +++ /dev/null @@ -1,5 +0,0 @@ -{{title}} -({{subtitle}}) - - - diff --git a/application/client/src/app/ui/views/sidebar/observe/component.ts b/application/client/src/app/ui/views/sidebar/observe/component.ts index 8adaf27838..b4bc9f3e6a 100644 --- a/application/client/src/app/ui/views/sidebar/observe/component.ts +++ b/application/client/src/app/ui/views/sidebar/observe/component.ts @@ -26,6 +26,8 @@ export class Observed extends ChangesDetector implements AfterContentInit { public stream!: Stream; public operations: ObserveOperation[] = []; + public active: ObserveOperation[] = []; + public inactive: ObserveOperation[] = []; constructor(cdRef: ChangeDetectorRef) { super(cdRef); @@ -33,23 +35,27 @@ export class Observed extends ChangesDetector implements AfterContentInit { public ngAfterContentInit(): void { this.stream = this.session.stream; - this.operations = this.stream.observe().operations(); + this.update(); this.env().subscriber.register( this.stream.subjects.get().sources.subscribe(() => { this.update(); + this.detectChanges(); }), this.stream.subjects.get().finished.subscribe(() => { this.update(); + this.detectChanges(); }), this.stream.sde.subjects.get().selected.subscribe(() => { this.update(); + this.detectChanges(); }), ); } protected update() { this.operations = this.stream.observe().operations(); - this.detectChanges(); + this.active = this.operations.filter((operation) => operation.isRunning()); + this.inactive = this.operations.filter((operation) => !operation.isRunning()); } } export interface Observed extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/module.ts b/application/client/src/app/ui/views/sidebar/observe/module.ts index 022b18a280..24ad547eed 100644 --- a/application/client/src/app/ui/views/sidebar/observe/module.ts +++ b/application/client/src/app/ui/views/sidebar/observe/module.ts @@ -9,6 +9,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; import { AttachSourceMenuModule } from '@elements/menu.attachsource/module'; +import { Operation } from './operation/component'; @NgModule({ imports: [ @@ -17,17 +18,12 @@ import { AttachSourceMenuModule } from '@elements/menu.attachsource/module'; MatButtonModule, MatCardModule, MatExpansionModule, - // FileListModule, - // ProcessListModule, - // SerialListModule, - // TcpListModule, - // UdpListModule, MatMenuModule, MatIconModule, MatDividerModule, AttachSourceMenuModule, ], - declarations: [Observed], + declarations: [Observed, Operation], exports: [Observed], bootstrap: [Observed], }) diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/component.ts b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts new file mode 100644 index 0000000000..e1bb2bca5b --- /dev/null +++ b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts @@ -0,0 +1,71 @@ +import { Component, Input, AfterContentInit, ChangeDetectorRef } from '@angular/core'; +import { Ilc, IlcInterface } from '@env/decorators/component'; +import { State } from '@service/session/dependencies/observing/operation'; +import { ObserveOperation } from '@service/session/dependencies/stream'; +import { ChangesDetector } from '@ui/env/extentions/changes'; +import { getSourceColor } from '@ui/styles/colors'; + +@Component({ + selector: 'app-views-observed-operation', + templateUrl: './template.html', + styleUrls: ['./styles.less'], + standalone: false, +}) +@Ilc() +export class Operation extends ChangesDetector implements AfterContentInit { + @Input() operation!: ObserveOperation; + + public title!: string; + public subtitle: string | undefined = undefined; + public state!: State; + + protected update() { + const descriptor = this.operation.getDescriptor(); + this.state = this.operation.state; + if (descriptor) { + this.title = descriptor.s_desc ? descriptor.s_desc : descriptor.source.name; + this.subtitle = descriptor.p_desc ? descriptor.p_desc : descriptor.parser.name; + } else { + this.log().warn(`Descriptor of operation isn't available`); + this.title = this.operation.getOrigin().getTitle(); + this.subtitle = undefined; + } + } + + constructor(cdRef: ChangeDetectorRef) { + super(cdRef); + } + + public get State() { + return State; + } + + public ngAfterContentInit(): void { + this.update(); + this.env().subscriber.register( + this.operation.stateUpdateEvent.subscribe(() => { + this.update(); + this.detectChanges(); + }), + ); + } + + public stop() { + if (!this.operation.isRunning()) { + return; + } + this.operation.abort().catch((err: Error) => { + this.log().error(`Fail to stop operation: ${err.message}`); + }); + } + + public restart() { + if (this.operation.isRunning()) { + return; + } + this.operation.restart().catch((err: Error) => { + this.log().error(`Fail to restart operation: ${err.message}`); + }); + } +} +export interface Operation extends IlcInterface {} diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/styles.less b/application/client/src/app/ui/views/sidebar/observe/operation/styles.less new file mode 100644 index 0000000000..fd8dc0158f --- /dev/null +++ b/application/client/src/app/ui/views/sidebar/observe/operation/styles.less @@ -0,0 +1,38 @@ +@import '../../../../styles/variables.less'; + +:host { + position: relative; + display: flex; + flex-direction: row; + padding: 0 12px; + align-items: center; + &:hover { + background: var(--scheme-color-5); + } + & div.desc { + flex: auto; + & p.title { + color: var(--scheme-color-2); + } + & p.subtitle { + color: var(--scheme-color-3); + } + & p.status { + &.started { + color: var(--scheme-color-accent); + } + &.running { + color: var(--scheme-color-accent); + } + &.finished { + color: var(--scheme-color-2); + } + &.aborted { + color: var(--scheme-color-error-light); + } + } + } + & div.buttons { + + } +} diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/template.html b/application/client/src/app/ui/views/sidebar/observe/operation/template.html new file mode 100644 index 0000000000..f787f8df95 --- /dev/null +++ b/application/client/src/app/ui/views/sidebar/observe/operation/template.html @@ -0,0 +1,13 @@ +
+

{{title}}

+

+ {{subtitle}} +

+

{{state}}

+
+
+ stop + loop +
diff --git a/application/client/src/app/ui/views/sidebar/observe/template.html b/application/client/src/app/ui/views/sidebar/observe/template.html index 7898535fa1..b56ba8e49b 100644 --- a/application/client/src/app/ui/views/sidebar/observe/template.html +++ b/application/client/src/app/ui/views/sidebar/observe/template.html @@ -5,10 +5,30 @@
-
{{operation.getOrigin().getTitle()}}
+ + Active + +
+ + +
+
+ + + Inactive + +
+ + +
From ba9d151f9e0be8b989fb3786ba34622fbe08f55e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 15 Jul 2025 14:21:37 +0200 Subject: [PATCH 094/121] Fix source definition parsing --- application/apps/indexer/stypes/src/observe/mod.rs | 2 +- .../src/app/service/history/definition.stream.ts | 2 +- .../client/src/app/service/session/session.ts | 14 +++++++++++--- application/platform/types/bindings/observe.ts | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index 561e84e13d..cd28f19e21 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -22,7 +22,7 @@ use dlt_core::filtering::DltFilterConfig; derive(TS), ts(export, export_to = "observe.ts") )] -/// Описывает операцию, которая должна быть произведена +/// Described user basic action. Source means, user is selecting custom source & parser pub enum SessionAction { File(PathBuf), Files(Vec), diff --git a/application/client/src/app/service/history/definition.stream.ts b/application/client/src/app/service/history/definition.stream.ts index 40f74df7b0..3fd6701a2c 100644 --- a/application/client/src/app/service/history/definition.stream.ts +++ b/application/client/src/app/service/history/definition.stream.ts @@ -11,7 +11,7 @@ export interface IStreamDesc { export class StreamDesc implements IStreamDesc { static fromDataSource(operation: ObserveOperation): Promise { - if (operation.getOrigin().isStream()) { + if (!operation.getOrigin().isStream()) { return Promise.resolve(undefined); } const descriptor = operation.getDescriptor(); diff --git a/application/client/src/app/service/session/session.ts b/application/client/src/app/service/session/session.ts index d9a66828f0..df8ce09355 100644 --- a/application/client/src/app/service/session/session.ts +++ b/application/client/src/app/service/session/session.ts @@ -373,9 +373,17 @@ export class Session extends Base { if (!this._descriptor) { return; } - if (this._descriptor.s_desc && this._descriptor.p_desc) { - this._tab.setTitle(`${this._descriptor.s_desc} (${this._descriptor.p_desc})`); - } + this._tab.setTitle( + `${ + this._descriptor.s_desc + ? this._descriptor.s_desc + : this._descriptor.source.name + } (${ + this._descriptor.p_desc + ? this._descriptor.p_desc + : this._descriptor.parser.name + })`, + ); }, }; } diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 9acb566325..95e66d8809 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -163,7 +163,7 @@ export type SerialTransportConfig = { }; /** - * Описывает операцию, которая должна быть произведена + * Described user basic action. Source means, user is selecting custom source & parser */ export type SessionAction = | { File: string } From 9ad574e1f0f6f2668c2bd27dfec15675c86855c9 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 16 Jul 2025 14:45:56 +0200 Subject: [PATCH 095/121] Change source ids format and processing --- .../indexer/session/src/state/source_ids.rs | 3 +- .../indexer/stypes/bindings/miscellaneous.ts | 4 + .../apps/indexer/stypes/bindings/observe.ts | 332 ++++++++---------- .../indexer/stypes/src/miscellaneous/mod.rs | 2 + .../stypes/src/miscellaneous/proptest.rs | 6 +- .../ts-bindings/src/api/session.stream.ts | 5 +- .../ts-bindings/src/native/native.session.ts | 7 +- .../app/service/history/definition.stream.ts | 1 - .../dependencies/observing/operation.ts | 24 +- .../service/session/dependencies/stream.ts | 74 ++-- .../platform/ipc/request/observe/sources.ts | 8 +- application/platform/package.json | 1 - .../platform/types/bindings/miscellaneous.ts | 4 + application/platform/types/source.ts | 4 - 14 files changed, 235 insertions(+), 240 deletions(-) delete mode 100644 application/platform/types/source.ts diff --git a/application/apps/indexer/session/src/state/source_ids.rs b/application/apps/indexer/session/src/state/source_ids.rs index 5ad1e09482..d08f11e0c7 100644 --- a/application/apps/indexer/session/src/state/source_ids.rs +++ b/application/apps/indexer/session/src/state/source_ids.rs @@ -75,8 +75,9 @@ impl SourceIDs { pub fn get_sources_definitions(&self) -> Vec { self.sources .iter() - .map(|(id, (_uuid, descriptor))| stypes::SourceDefinition { + .map(|(id, (uuid, descriptor))| stypes::SourceDefinition { id: *id, + uuid: *uuid, descriptor: descriptor.clone(), }) .collect::>() diff --git a/application/apps/indexer/stypes/bindings/miscellaneous.ts b/application/apps/indexer/stypes/bindings/miscellaneous.ts index 2fa2be6197..5aa6ad279b 100644 --- a/application/apps/indexer/stypes/bindings/miscellaneous.ts +++ b/application/apps/indexer/stypes/bindings/miscellaneous.ts @@ -95,6 +95,10 @@ export type SourceDefinition = { * The unique identifier of the source. */ id: number, +/** + * Parent observe opeartion Uuid + */ +uuid: string, /** * The user-friendly name of the source for display purposes. */ diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 09fea880a8..7ada9061e8 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -1,9 +1,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { ComponentOptions } from './options'; -import type { IODataType } from './options'; -import type { PluginParserSettings } from './plugins'; +import type { ComponentOptions } from "./options"; +import type { IODataType } from "./options"; +import type { PluginParserSettings } from "./plugins"; -export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOptions }; +export type ComponentDef = { "Source": ComponentOptions } | { "Parser": ComponentOptions }; /** * Represents the type of a component within the system. @@ -12,57 +12,55 @@ export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOpt * functional role of the component. It is used to categorize components * according to their purpose in the data processing pipeline. */ -export type ComponentType = 'Parser' | 'Source'; +export type ComponentType = "Parser" | "Source"; /** * Settings for the DLT parser. */ -export type DltParserSettings = { - /** - * Configuration for filtering DLT messages. - */ - filter_config: DltFilterConfig; - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; - /** - * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. - */ - with_storage_header: boolean; - /** - * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. - */ - tz: string | null; -}; +export type DltParserSettings = { +/** + * Configuration for filtering DLT messages. + */ +filter_config: DltFilterConfig, +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, +/** + * Indicates whether the source contains a `StorageHeader`. Set to `true` if applicable. + */ +with_storage_header: boolean, +/** + * Timezone for timestamp adjustment. If specified, timestamps are converted to this timezone. + */ +tz: string | null, }; /** * Supported file formats for observation. */ -export type FileFormat = 'PcapNG' | 'PcapLegacy' | 'Text' | 'Binary'; - -export type Ident = { - /** - * A short, human-readable name of the component (parser/source), independent of context. - * Used on the client side to display available components. - */ - name: string; - /** - * A more detailed description of the component. This information is displayed in a separate - * area and can contain an extended explanation. - */ - desc: string; - /** - * The type of data the component produces or expects. - * For sources, this is the type of data they emit. - * For parsers, this is the type of data they expect as input. - */ - io: IODataType; - /** - * A unique identifier of the component. - */ - uuid: string; -}; +export type FileFormat = "PcapNG" | "PcapLegacy" | "Text" | "Binary"; + +export type Ident = { +/** + * A short, human-readable name of the component (parser/source), independent of context. + * Used on the client side to display available components. + */ +name: string, +/** + * A more detailed description of the component. This information is displayed in a separate + * area and can contain an extended explanation. + */ +desc: string, +/** + * The type of data the component produces or expects. + * For sources, this is the type of data they emit. + * For parsers, this is the type of data they expect as input. + */ +io: IODataType, +/** + * A unique identifier of the component. + */ +uuid: string, }; export type IdentList = Array; @@ -72,175 +70,151 @@ export type IdentList = Array; * - `interface`: The address of the local interface used to join the multicast group. * If set to `INADDR_ANY`, the system selects an appropriate interface. */ -export type MulticastInfo = { multiaddr: string; interface: string | null }; +export type MulticastInfo = { multiaddr: string, interface: string | null, }; /** * Options for observing data within a session. */ -export type ObserveOptions = { - /** - * The description of the data source. - */ - origin: ObserveOrigin; - /** - * The parser configuration to be applied. - */ - parser: ParserType; -}; +export type ObserveOptions = { +/** + * The description of the data source. + */ +origin: ObserveOrigin, +/** + * The parser configuration to be applied. + */ +parser: ParserType, }; /** * Describes the source of data for observation. */ -export type ObserveOrigin = - | { File: [string, FileFormat, string] } - | { Concat: Array<[string, FileFormat, string]> } - | { Stream: [string, Transport] }; +export type ObserveOrigin = { "File": [string, FileFormat, string] } | { "Concat": Array<[string, FileFormat, string]> } | { "Stream": [string, Transport] }; /** * Specifies the parser to be used for processing session data. */ -export type ParserType = - | { Dlt: DltParserSettings } - | { SomeIp: SomeIpParserSettings } - | { Text: null } - | { Plugin: PluginParserSettings }; +export type ParserType = { "Dlt": DltParserSettings } | { "SomeIp": SomeIpParserSettings } | { "Text": null } | { "Plugin": PluginParserSettings }; /** * Configuration for executing terminal commands. */ -export type ProcessTransportConfig = { - /** - * The working directory for the command. - */ - cwd: string; - /** - * The command to execute. - */ - command: string; - /** - * Environment variables. If empty, the default environment variables are used. - */ - envs: Map; -}; +export type ProcessTransportConfig = { +/** + * The working directory for the command. + */ +cwd: string, +/** + * The command to execute. + */ +command: string, +/** + * Environment variables. If empty, the default environment variables are used. + */ +envs: Map, }; /** * Configuration for serial port connections. */ -export type SerialTransportConfig = { - /** - * The path to the serial port. - */ - path: string; - /** - * The baud rate for the connection. - */ - baud_rate: number; - /** - * The number of data bits per frame. - */ - data_bits: number; - /** - * The flow control setting. - */ - flow_control: number; - /** - * The parity setting. - */ - parity: number; - /** - * The number of stop bits. - */ - stop_bits: number; - /** - * The delay in sending data, in milliseconds. - */ - send_data_delay: number; - /** - * Whether the connection is exclusive. - */ - exclusive: boolean; -}; - -export type SessionAction = - | { File: string } - | { Files: Array } - | { ExportRaw: [Array, Array<{ start: bigint; end: bigint }>, string] } - | 'Source'; - -export type SessionDescriptor = { - /** - * Identifier of the parser being used. Provides a general description of the parser. - */ - parser: Ident; - /** - * Identifier of the source being used. Provides a general description of the source. - */ - source: Ident; - /** - * Parser description in the context of the current session. - */ - p_desc: string | null; - /** - * Source description in the context of the current session. For example, instead of "UDP", it might be "UDP on 0.0.0.0::8888". - */ - s_desc: string | null; -}; - -export type SessionSetup = { - origin: SessionAction; - parser: ComponentOptions; - source: ComponentOptions; -}; +export type SerialTransportConfig = { +/** + * The path to the serial port. + */ +path: string, +/** + * The baud rate for the connection. + */ +baud_rate: number, +/** + * The number of data bits per frame. + */ +data_bits: number, +/** + * The flow control setting. + */ +flow_control: number, +/** + * The parity setting. + */ +parity: number, +/** + * The number of stop bits. + */ +stop_bits: number, +/** + * The delay in sending data, in milliseconds. + */ +send_data_delay: number, +/** + * Whether the connection is exclusive. + */ +exclusive: boolean, }; + +/** + * Described user basic action. Source means, user is selecting custom source & parser + */ +export type SessionAction = { "File": string } | { "Files": Array } | { "ExportRaw": [Array, Array<{ start: bigint, end: bigint, }>, string] } | "Source"; + +export type SessionDescriptor = { +/** + * Identifier of the parser being used. Provides a general description of the parser. + */ +parser: Ident, +/** + * Identifier of the source being used. Provides a general description of the source. + */ +source: Ident, +/** + * Parser description in the context of the current session. + */ +p_desc: string | null, +/** + * Source description in the context of the current session. For example, instead of "UDP", it might be "UDP on 0.0.0.0::8888". + */ +s_desc: string | null, }; + +export type SessionSetup = { origin: SessionAction, parser: ComponentOptions, source: ComponentOptions, }; /** * Settings for the SomeIp parser. */ -export type SomeIpParserSettings = { - /** - * Paths to FIBEX files for additional interpretation of `payload` content. - */ - fibex_file_paths: Array | null; -}; +export type SomeIpParserSettings = { +/** + * Paths to FIBEX files for additional interpretation of `payload` content. + */ +fibex_file_paths: Array | null, }; /** * Configuration for TCP connections. */ -export type TCPTransportConfig = { - /** - * The address to bind the TCP connection to. - */ - bind_addr: string; -}; +export type TCPTransportConfig = { +/** + * The address to bind the TCP connection to. + */ +bind_addr: string, }; /** * Describes the transport source for a session. */ -export type Transport = - | { Process: ProcessTransportConfig } - | { TCP: TCPTransportConfig } - | { UDP: UDPTransportConfig } - | { Serial: SerialTransportConfig }; +export type Transport = { "Process": ProcessTransportConfig } | { "TCP": TCPTransportConfig } | { "UDP": UDPTransportConfig } | { "Serial": SerialTransportConfig }; /** * Configuration for UDP connections. */ -export type UDPTransportConfig = { - /** - * The address to bind the UDP connection to. - */ - bind_addr: string; - /** - * A list of multicast configurations. - */ - multicast: Array; -}; +export type UDPTransportConfig = { +/** + * The address to bind the UDP connection to. + */ +bind_addr: string, +/** + * A list of multicast configurations. + */ +multicast: Array, }; /** * Configuration for UDP connections. */ -export type UdpConnectionInfo = { - /** - * A list of multicast addresses to listen on. - */ - multicast_addr: Array; -}; +export type UdpConnectionInfo = { +/** + * A list of multicast addresses to listen on. + */ +multicast_addr: Array, }; diff --git a/application/apps/indexer/stypes/src/miscellaneous/mod.rs b/application/apps/indexer/stypes/src/miscellaneous/mod.rs index 4a55b8021f..579b7e908a 100644 --- a/application/apps/indexer/stypes/src/miscellaneous/mod.rs +++ b/application/apps/indexer/stypes/src/miscellaneous/mod.rs @@ -55,6 +55,8 @@ pub struct Ranges(pub Vec); pub struct SourceDefinition { /// The unique identifier of the source. pub id: u16, + /// Parent observe opeartion Uuid + pub uuid: Uuid, /// The user-friendly name of the source for display purposes. pub descriptor: SessionDescriptor, } diff --git a/application/apps/indexer/stypes/src/miscellaneous/proptest.rs b/application/apps/indexer/stypes/src/miscellaneous/proptest.rs index ddbe82b44b..4d2c548689 100644 --- a/application/apps/indexer/stypes/src/miscellaneous/proptest.rs +++ b/application/apps/indexer/stypes/src/miscellaneous/proptest.rs @@ -45,7 +45,11 @@ impl Arbitrary for SourceDefinition { fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { (any::(), any::()) - .prop_map(|(id, descriptor)| SourceDefinition { id, descriptor }) + .prop_map(|(id, descriptor)| SourceDefinition { + id, + uuid: Uuid::new_v4(), + descriptor, + }) .boxed() } } diff --git a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts index 6cfdf5d299..fa8d748584 100644 --- a/application/apps/rustcore/ts-bindings/src/api/session.stream.ts +++ b/application/apps/rustcore/ts-bindings/src/api/session.stream.ts @@ -6,9 +6,8 @@ import { SdeRequest, SdeResponse } from 'platform/types/sde'; import { EventProvider } from '../api/session.provider'; import { Executors } from './executors/session.stream.executors'; import { EFileOptionsRequirements } from './executors/session.stream.observe.executor'; -import { GrabbedElement } from 'platform/types/bindings/miscellaneous'; +import { GrabbedElement, SourceDefinition } from 'platform/types/bindings/miscellaneous'; import { IRange } from 'platform/types/range'; -import { ISourceLink } from 'platform/types/source'; import { Attachment, IndexingMode } from 'platform/types/content'; import { SessionSetup } from 'platform/types/bindings'; import { TextExportOptions } from 'platform/types/exporting'; @@ -77,7 +76,7 @@ export class SessionStream { return this._session.getFileOptionsRequirements(filename); } - public getSourcesDefinitions(): Promise { + public getSourcesDefinitions(): Promise { return this._session.getSourcesDefinitions(); } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.session.ts b/application/apps/rustcore/ts-bindings/src/native/native.session.ts index 990508aff1..1425d05b10 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.session.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.session.ts @@ -3,14 +3,13 @@ import { RustSessionRequiered } from '../native/native.session.required'; import { TEventEmitter } from '../provider/provider.general'; import { Computation } from '../provider/provider'; import { IFilter } from 'platform/types/filter'; -import { GrabbedElement } from 'platform/types/bindings/miscellaneous'; +import { GrabbedElement, SourceDefinition } from 'platform/types/bindings/miscellaneous'; import { getNativeModule } from '../native/native'; import { EFileOptionsRequirements } from '../api/executors/session.stream.observe.executor'; import { Type, Source, NativeError } from '../interfaces/errors'; import { v4 as uuidv4 } from 'uuid'; import { getValidNum } from '../util/numbers'; import { IRange } from 'platform/types/range'; -import { ISourceLink } from 'platform/types/source'; import { IndexingMode, Attachment } from 'platform/types/content'; import { Logger, utils } from 'platform/log'; import { scope } from 'platform/env/scope'; @@ -87,7 +86,7 @@ export abstract class RustSession extends RustSessionRequiered { * Returns list of observed sources. * @returns { string } */ - public abstract getSourcesDefinitions(): Promise; + public abstract getSourcesDefinitions(): Promise; public abstract getUuid(): string; @@ -463,7 +462,7 @@ export class RustSessionWrapper extends RustSession { return this._native.getUuid(); } - public getSourcesDefinitions(): Promise { + public getSourcesDefinitions(): Promise { return new Promise((resolve, reject) => { this._provider.debug().emit.operation('getSourcesDefinitions'); this._native diff --git a/application/client/src/app/service/history/definition.stream.ts b/application/client/src/app/service/history/definition.stream.ts index 3fd6701a2c..6a991f13f2 100644 --- a/application/client/src/app/service/history/definition.stream.ts +++ b/application/client/src/app/service/history/definition.stream.ts @@ -1,5 +1,4 @@ import { ObserveOperation } from '@service/session/dependencies/stream'; -import { SessionDescriptor } from '@platform/types/bindings'; import * as obj from '@platform/env/obj'; diff --git a/application/client/src/app/service/session/dependencies/observing/operation.ts b/application/client/src/app/service/session/dependencies/observing/operation.ts index ef8f5818ab..edf0a3bb1f 100644 --- a/application/client/src/app/service/session/dependencies/observing/operation.ts +++ b/application/client/src/app/service/session/dependencies/observing/operation.ts @@ -1,5 +1,5 @@ import { SdeRequest, SdeResponse } from '@platform/types/sde'; -import { SessionDescriptor } from '@platform/types/bindings'; +import { SessionDescriptor, SourceDefinition } from '@platform/types/bindings'; import { SessionOrigin } from '@service/session/origin'; import { Subject } from '@platform/env/subscription'; @@ -22,7 +22,7 @@ export class ObserveOperation { protected restarting: RestartingAPIFunc | undefined; protected cancel: CancelAPIFunc | undefined; protected origin: SessionOrigin | undefined; - protected sourceId: number = -1; + protected sources: Map = new Map(); public state: State = State.Started; public stateUpdateEvent: Subject = new Subject(); @@ -43,6 +43,26 @@ export class ObserveOperation { this.stateUpdateEvent.emit(); } + public addSource(source: SourceDefinition): boolean { + if (this.sources.has(source.id)) { + return false; + } + this.sources.set(source.id, source); + return true; + } + + public getSourcesCount(): number { + return this.sources.size; + } + + public getSource(id: number): SourceDefinition | undefined { + return this.sources.get(id); + } + + public getFirstSourceKey(): number | undefined { + return this.sources.keys().next().value; + } + public destroy() { this.stateUpdateEvent.destroy(); } diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index a04cf4d416..d8c67d843d 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -3,7 +3,7 @@ import { Subscriber, Subjects, Subject } from '@platform/env/subscription'; import { Range, IRange } from '@platform/types/range'; import { cutUuid } from '@log/index'; import { Rank } from './rank'; -import { GrabbedElement } from '@platform/types/bindings/miscellaneous'; +import { GrabbedElement, SourceDefinition } from '@platform/types/bindings/miscellaneous'; import { ObserveOperation } from './observing/operation'; import { Info } from './info'; import { lockers } from '@ui/service/lockers'; @@ -11,7 +11,6 @@ import { Sde } from './observing/sde'; import { TextExportOptions } from '@platform/types/exporting'; import { SessionDescriptor } from '@platform/types/bindings'; import { SessionOrigin } from '../origin'; -import { ISourceLink } from '@platform/types/source'; import { recent } from '@service/recent'; import * as Requests from '@platform/ipc/request'; @@ -49,13 +48,7 @@ export class Stream extends Subscriber { private _uuid!: string; private _info!: Info; - public readonly observed: { - operations: Map; - map: Map; - } = { - operations: new Map(), - map: new Map(), - }; + public readonly operations: Map = new Map(); public readonly rank: Rank = new Rank(); public sde!: Sde; @@ -83,7 +76,7 @@ export class Stream extends Subscriber { return; } this.subjects.get().descriptor.emit(event.descriptor); - const operation = this.observed.operations.get(event.operation); + const operation = this.operations.get(event.operation); if (!operation) { this.log().error( `Event "Stream.SessionDescriptor" emmited for operation "${event.operation}", but there no started operation with same uuid.`, @@ -96,10 +89,7 @@ export class Stream extends Subscriber { if (event.session !== this._uuid) { return; } - this.observed.operations.set( - event.operation, - new ObserveOperation(event.operation), - ); + this.operations.set(event.operation, new ObserveOperation(event.operation)); }), ); this.register( @@ -107,7 +97,7 @@ export class Stream extends Subscriber { if (event.session !== this._uuid) { return; } - const operation = this.observed.operations.get(event.operation); + const operation = this.operations.get(event.operation); if (!operation) { this.log().error( `Event "Observe.Finished" emmited for operation "${event.operation}", but there no started operation with same uuid.`, @@ -115,7 +105,7 @@ export class Stream extends Subscriber { return; } operation.finish(); - this.sde.overwrite(this.observed.operations); + this.sde.overwrite(this.operations); this.subjects.get().finished.emit(operation); }), ); @@ -125,7 +115,7 @@ export class Stream extends Subscriber { this.unsubscribe(); this.subjects.destroy(); this.sde.destroy(); - this.observed.operations.forEach((operation) => { + this.operations.forEach((operation) => { operation.abort().catch((err: Error) => { this.log().warn(`Fail to abort operation: ${err.message}`); }); @@ -146,9 +136,9 @@ export class Stream extends Subscriber { isFileSource(): boolean; getSourceFileName(): string | undefined; descriptions: { - get(id: number): ISourceLink | undefined; - id(alias: string): number | undefined; - request(): Promise; + get(id: number): SourceDefinition | undefined; + id(uuid: string): number | undefined; + request(): Promise; count(): number; }; } { @@ -180,7 +170,7 @@ export class Stream extends Subscriber { ), ); } - const operation = this.observed.operations.get(operationUuid); + const operation = this.operations.get(operationUuid); if (!operation) { return Promise.reject( new Error( @@ -201,19 +191,16 @@ export class Stream extends Subscriber { .then((sources) => { let updated = false; sources.forEach((source) => { - if (!this.observed.map.has(source.id)) { - this.observed.map.set(source.id, source); - updated = true; - } + updated = updated ? updated : operation.addSource(source); }); updated && this.subjects.get().sources.emit(); }) .catch((err: Error) => { this.log().error(`Fail get sources description: ${err.message}`); }); - this.sde.overwrite(this.observed.operations); + this.sde.overwrite(this.operations); this.subjects.get().started.emit(operation); - if (this.observed.operations.size === 1) { + if (this.operations.size === 1) { // Only if it's the first operation, save as recent recent.add(operation); } @@ -222,7 +209,7 @@ export class Stream extends Subscriber { .finally(lockers.progress(`Creating session...`)); }, abort: (uuid: string): Promise => { - const operation = this.observed.operations.get(uuid); + const operation = this.operations.get(uuid); if (!operation) { return Promise.reject( new Error(`Operation ${uuid} doesn't exist. Cannot abort`), @@ -253,7 +240,7 @@ export class Stream extends Subscriber { }); }, restart: (uuid: string, options: SessionOrigin): Promise => { - const operation = this.observed.operations.get(uuid); + const operation = this.operations.get(uuid); if (!operation) { return Promise.reject( new Error(`Operation ${uuid} doesn't exist. Cannot restart`), @@ -276,7 +263,7 @@ export class Stream extends Subscriber { .then((response: Requests.Observe.List.Response) => { const operations: Map = new Map(); response.operations.forEach((uuid: string) => { - const operation = this.observed.operations.get(uuid); + const operation = this.operations.get(uuid); if (!operation) { this.log().error( `Fail to find operation ${uuid} in local scope`, @@ -295,7 +282,7 @@ export class Stream extends Subscriber { }); }, operations: (): ObserveOperation[] => { - return Array.from(this.observed.operations.values()); + return Array.from(this.operations.values()); }, isFileSource: (): boolean => { const sources = this.observe().operations(); @@ -312,16 +299,21 @@ export class Stream extends Subscriber { return sources[0].getOrigin().getFirstFilename(); }, descriptions: { - get: (id: number): ISourceLink | undefined => { - return this.observed.map.get(id); + get: (id: number): SourceDefinition | undefined => { + const operation = Array.from(this.operations.values()).find((operation) => { + return operation.getSource(id); + }); + return operation ? operation.getSource(id) : undefined; }, - id: (alias: string): number | undefined => { - const link = Array.from(this.observed.map.values()).find( - (s) => s.alias === alias, - ); - return link !== undefined ? link.id : undefined; + id: (uuid: string): number | undefined => { + console.error('Not implemented'); + // TODO: + // `uuid` - is an uuid of observe operation, but if it's Files (aka concat) + // we would have multiple sources (files) for a one operation + const operation = this.operations.get(uuid); + return operation ? operation.getFirstSourceKey() : undefined; }, - request: (): Promise => { + request: (): Promise => { return new Promise((resolve, reject) => { Requests.IpcRequest.send( Requests.Observe.SourcesDefinitionsList.Response, @@ -336,7 +328,9 @@ export class Stream extends Subscriber { }); }, count: (): number => { - return this.observed.map.size; + return Array.from(this.operations.values()) + .map((operation) => operation.getSourcesCount()) + .reduce((sum, a) => sum + a, 0); }, }, }; diff --git a/application/platform/ipc/request/observe/sources.ts b/application/platform/ipc/request/observe/sources.ts index 0bbce8e984..3c273a465a 100644 --- a/application/platform/ipc/request/observe/sources.ts +++ b/application/platform/ipc/request/observe/sources.ts @@ -1,5 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { ISourceLink } from '../../../types/source'; +import { SourceDefinition } from '../../../types/bindings/miscellaneous'; import * as validator from '../../../env/obj'; @@ -19,13 +19,13 @@ export interface Request extends Interface {} @Define({ name: 'SourcesListResponse' }) export class Response extends SignatureRequirement { public session: string; - public sources: ISourceLink[]; + public sources: SourceDefinition[]; - constructor(input: { session: string; sources: ISourceLink[] }) { + constructor(input: { session: string; sources: SourceDefinition[] }) { super(); validator.isObject(input); this.session = validator.getAsNotEmptyString(input, 'session'); - this.sources = validator.getAsArray(input, 'sources'); + this.sources = validator.getAsArray(input, 'sources'); } } diff --git a/application/platform/package.json b/application/platform/package.json index 61173b7daa..915ce74bf6 100644 --- a/application/platform/package.json +++ b/application/platform/package.json @@ -97,7 +97,6 @@ "./types/exporting": "./dist/types/exporting.js", "./types/sessionsetup": "./dist/types/sessionsetup/index.js", "./types/range": "./dist/types/range.js", - "./types/source": "./dist/types/source.js", "./types/hotkeys/map": "./dist/types/hotkeys/map.js", "./types/parsers": "./dist/types/parsers/index.js", "./types/parsers/dlt": "./dist/types/parsers/dlt.js", diff --git a/application/platform/types/bindings/miscellaneous.ts b/application/platform/types/bindings/miscellaneous.ts index 2c8c8c3cb0..e90acfc2a6 100644 --- a/application/platform/types/bindings/miscellaneous.ts +++ b/application/platform/types/bindings/miscellaneous.ts @@ -98,6 +98,10 @@ export type SourceDefinition = { * The unique identifier of the source. */ id: number; + /** + * Parent observe opeartion Uuid + */ + uuid: string; /** * The user-friendly name of the source for display purposes. */ diff --git a/application/platform/types/source.ts b/application/platform/types/source.ts deleted file mode 100644 index a138ce7549..0000000000 --- a/application/platform/types/source.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ISourceLink { - id: number; - alias: string; -} From 20d12f5d4acabc64f494d2c551af9e03bdb8ec8a Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 18 Jul 2025 09:35:31 +0200 Subject: [PATCH 096/121] Upgrade Components - less abstraction --- application/apps/indexer/Cargo.toml | 1 - .../apps/indexer/addons/dlt-tools/Cargo.toml | 1 - .../apps/indexer/addons/dlt-tools/src/lib.rs | 2 +- .../apps/indexer/components/src/lib.rs | 196 +++++++----------- .../components/src/{tys.rs => tys/mod.rs} | 141 +------------ .../apps/indexer/components/src/tys/parser.rs | 18 ++ .../apps/indexer/components/src/tys/source.rs | 20 ++ .../apps/indexer/definitions/Cargo.toml | 10 - .../apps/indexer/definitions/src/lib.rs | 12 -- application/apps/indexer/parsers/Cargo.toml | 2 +- .../src/parser.rs => parsers/src/api/mod.rs} | 11 +- .../src => parsers/src/api}/record.rs | 0 .../indexer/parsers/src/dlt/attachment.rs | 2 +- .../indexer/parsers/src/dlt/descriptor.rs | 158 +++++++------- .../apps/indexer/parsers/src/dlt/mod.rs | 6 +- .../indexer/parsers/src/dlt/raw/descriptor.rs | 31 +-- .../apps/indexer/parsers/src/dlt/raw/mod.rs | 2 +- application/apps/indexer/parsers/src/lib.rs | 30 ++- .../indexer/parsers/src/someip/descriptor.rs | 55 +++-- .../apps/indexer/parsers/src/someip/mod.rs | 24 +-- .../indexer/parsers/src/text/descriptor.rs | 27 ++- .../apps/indexer/parsers/src/text/mod.rs | 5 +- .../apps/indexer/plugins_host/Cargo.toml | 1 - .../plugins_host/src/parser_shared/mod.rs | 41 +++- .../src/parser_shared/plugin_parse_message.rs | 2 +- .../src/v0_1_0/parser/bindings.rs | 39 ++-- .../plugins_host/src/v0_1_0/parser/mod.rs | 18 +- application/apps/indexer/processor/Cargo.toml | 1 - .../indexer/processor/src/producer/mod.rs | 4 +- application/apps/indexer/session/Cargo.toml | 1 - .../indexer/session/src/components/mod.rs | 4 +- .../src/handlers/observe/export_raw.rs | 5 +- .../session/src/handlers/observe/mod.rs | 2 +- .../session/src/handlers/observe/session.rs | 6 +- .../apps/indexer/session/src/operations.rs | 2 +- .../apps/indexer/session/src/session.rs | 4 +- .../apps/indexer/session/src/state/api.rs | 4 +- .../indexer/session/src/state/attachments.rs | 4 +- .../apps/indexer/session/src/state/mod.rs | 2 +- application/apps/indexer/sources/Cargo.toml | 1 - .../src/source.rs => sources/src/api/mod.rs} | 7 + .../src/binary/pcap/legacy/descriptor.rs | 22 +- .../sources/src/binary/pcap/legacy/mod.rs | 4 +- .../sources/src/binary/pcap/ng/descriptor.rs | 22 +- .../indexer/sources/src/binary/pcap/ng/mod.rs | 4 +- .../sources/src/binary/raw/descriptor.rs | 54 +++-- .../indexer/sources/src/binary/raw/mod.rs | 2 +- .../indexer/sources/src/command/descriptor.rs | 54 ++--- .../apps/indexer/sources/src/command/mod.rs | 2 +- application/apps/indexer/sources/src/lib.rs | 33 ++- .../indexer/sources/src/serial/descriptor.rs | 115 +++++----- .../indexer/sources/src/serial/serialport.rs | 2 +- .../sources/src/socket/tcp/descriptor.rs | 55 ++--- .../indexer/sources/src/socket/tcp/mod.rs | 5 +- .../sources/src/socket/udp/descriptor.rs | 109 ++++------ .../indexer/sources/src/socket/udp/mod.rs | 2 +- 56 files changed, 603 insertions(+), 784 deletions(-) rename application/apps/indexer/components/src/{tys.rs => tys/mod.rs} (60%) create mode 100644 application/apps/indexer/components/src/tys/parser.rs create mode 100644 application/apps/indexer/components/src/tys/source.rs delete mode 100644 application/apps/indexer/definitions/Cargo.toml delete mode 100644 application/apps/indexer/definitions/src/lib.rs rename application/apps/indexer/{definitions/src/parser.rs => parsers/src/api/mod.rs} (95%) rename application/apps/indexer/{definitions/src => parsers/src/api}/record.rs (100%) rename application/apps/indexer/{definitions/src/source.rs => sources/src/api/mod.rs} (96%) diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index ea82b3c31d..5257ee3f6d 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -8,7 +8,6 @@ members = [ "addons/text_grep", "addons/bufread", "components", - "definitions", "indexer_base", "merging", "parsers", diff --git a/application/apps/indexer/addons/dlt-tools/Cargo.toml b/application/apps/indexer/addons/dlt-tools/Cargo.toml index f6e035fbb2..98cf2f6ae7 100644 --- a/application/apps/indexer/addons/dlt-tools/Cargo.toml +++ b/application/apps/indexer/addons/dlt-tools/Cargo.toml @@ -8,7 +8,6 @@ dlt-core.workspace = true log.workspace = true tokio = { workspace = true , features = ["full"] } tokio-util = { workspace = true, features = ["codec", "net"] } -definitions = { path = "../../definitions" } indexer_base = { path = "../../indexer_base" } parsers = { path = "../../parsers" } sources = { path = "../../sources" } diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index 198cb99021..af5ac98a37 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -15,8 +15,8 @@ extern crate indexer_base; #[macro_use] extern crate log; -use definitions::Attachment; use dlt_core::filtering::DltFilterConfig; +use parsers::api::*; use parsers::dlt::DltParser; use processor::producer::MessageProducer; use sources::binary::raw::BinaryByteSource; diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index 6f9e048bda..e63a27205d 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -56,130 +56,77 @@ pub use tys::*; /// are not retained by `Components`. /// pub struct Components { - components: HashMap>, + sources: HashMap, Box)>, + parsers: HashMap, Box)>, } impl Components { - /// Creates an empty `Components` registry. - /// - /// This method initializes the `Components` structure without any registered parsers or sources. - /// Components should be registered manually using `add_parser` or `add_source` before use. - /// - /// # Returns - /// A new instance of `Components` with no components registered. - pub fn new() -> Self { + pub fn new() -> Components { Self { - components: HashMap::new(), + sources: HashMap::new(), + parsers: HashMap::new(), } } - - /// Registers a parser component. - /// - /// This method adds a new parser to the registry. The parser is described by a `ComponentFactory`, - /// which defines how it should be instantiated and validated. - /// - /// # Arguments - /// * `descriptor` – A descriptor implementing `ComponentFactory

` that encapsulates parser metadata and behavior. - /// - /// # Returns - /// * `Ok(())` – If the parser was successfully registered. - /// * `Err(NativeError)` – If a component with the same UUID is already registered. - /// - /// # Errors - /// Returns a configuration error if a parser with the same UUID already exists in the registry. - pub fn add_parser + 'static>( + pub fn add_parser( &mut self, + factory: Factory

, descriptor: D, ) -> Result<(), stypes::NativeError> { let ident = descriptor.ident(); - if self.components.contains_key(&ident.uuid) { + if self.parsers.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.components - .insert(ident.uuid, Entry::Parser(Box::new(descriptor))); + self.parsers + .insert(ident.uuid, (factory, Box::new(descriptor))); Ok(()) } - /// Registers a source component. - /// - /// Similar to `add_parser`, this method registers a data source implementation. - /// The descriptor provides construction logic and compatibility metadata. - /// - /// # Arguments - /// * `descriptor` – A descriptor implementing `ComponentFactory` for the source. - /// - /// # Returns - /// * `Ok(())` – If the source was successfully registered. - /// * `Err(NativeError)` – If a component with the same UUID is already registered. - /// - /// # Errors - /// Returns a configuration error if a source with the same UUID already exists in the registry. - pub fn add_source + 'static>( + pub fn add_source( &mut self, + factory: Factory, descriptor: D, ) -> Result<(), stypes::NativeError> { - let ident = descriptor.ident(); - if self.components.contains_key(&ident.uuid) { + let ident: stypes::Ident = descriptor.ident(); + if self.sources.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.components - .insert(ident.uuid, Entry::Source(Box::new(descriptor))); + self.sources + .insert(ident.uuid, (factory, Box::new(descriptor))); Ok(()) } - /// Returns a list of registered components of the specified type that are compatible with a given session. - /// - /// This is used to retrieve a filtered list of component identifiers for parsers or sources based on their compatibility - /// with the current session context. - /// - /// # Arguments - /// * `target` – The desired component type (`Parser` or `Source`). - /// * `origin` – The session action (e.g., export, view) to determine compatibility. - /// - /// # Returns - /// * `Ok(Vec)` – List of matching component identifiers. - /// * `Err(NativeError)` – On internal logic failure (should be rare). - pub fn get_components( - &self, - target: &stypes::ComponentType, - origin: stypes::SessionAction, - ) -> Result, stypes::NativeError> { - Ok(self - .components + pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { + self.parsers .iter() - .filter_map(|(_, desc)| match target { - stypes::ComponentType::Parser => { - if let Entry::Parser(desc) = desc { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - } else { - None - } + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None } - stypes::ComponentType::Source => { - if let Entry::Source(desc) = desc { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - } else { - None - } + }) + .collect() + } + + pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { + self.sources + .iter() + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None } }) - .collect()) + .collect() } /// Returns configuration schemas for the specified component UUIDs. @@ -202,9 +149,9 @@ impl Components { origin: stypes::SessionAction, mut targets: Vec, ) -> Result, stypes::NativeError> { - let descriptors: Vec<&Entry> = self - .components - .iter() + let descriptors = self + .descriptors() + .into_iter() .filter_map(|(uuid, desc)| { if targets.contains(uuid) { targets.retain(|v| v != uuid); @@ -213,7 +160,7 @@ impl Components { None } }) - .collect(); + .collect::>(); if !targets.is_empty() { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -263,20 +210,11 @@ impl Components { &self, uuid: &Uuid, ) -> Result, stypes::NativeError> { - let Entry::Parser(descriptor) = self.components.get(&uuid).ok_or(stypes::NativeError { + let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find component {uuid}")), - })? - else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Fail to get render for {uuid}, because it isn't parser" - )), - }); - }; + })?; Ok(descriptor.get_render()) } @@ -289,7 +227,13 @@ impl Components { /// * `Some(Ident)` – If the parser or source has been found. /// * `None` – If no component is defined. pub fn get_ident(&self, uuid: &Uuid) -> Option { - self.components.get(&uuid).map(|entity| entity.ident()) + self.descriptors().into_iter().find_map(|(inner, desc)| { + if uuid == inner { + Some(desc.ident()) + } else { + None + } + }) } /// Validates the configuration fields for a specified component within a session context. @@ -314,11 +258,15 @@ impl Components { target: &Uuid, fields: &[stypes::Field], ) -> Result, stypes::NativeError> { - let descriptor = self.components.get(target).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find component {target}")), - })?; + let (_, descriptor) = self + .descriptors() + .into_iter() + .find(|(uuid, _)| *uuid == target) + .ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {target}")), + })?; descriptor.validate(origin, fields) } @@ -341,14 +289,16 @@ impl Components { &self, options: &stypes::SessionSetup, ) -> Result<(SessionDescriptor, S, P), stypes::NativeError> { - let Some(Entry::Parser(parser)) = self.components.get(&options.parser.uuid) else { + let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) + else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find parser {}", options.parser.uuid)), }); }; - let Some(Entry::Source(source)) = self.components.get(&options.source.uuid) else { + let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) + else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -356,10 +306,10 @@ impl Components { }); }; let mut descriptor = SessionDescriptor::new( - source.bound_ident(&options.origin, &options.source.fields), - source.bound_ident(&options.origin, &options.parser.fields), + source_descriptor.bound_ident(&options.origin, &options.source.fields), + parser_descriptor.bound_ident(&options.origin, &options.parser.fields), ); - let Some((parser, desc)) = parser.create(&options.origin, &options.parser.fields)? else { + let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -367,7 +317,7 @@ impl Components { }); }; descriptor.set_parser_desc(desc); - let Some((source, desc)) = source.create(&options.origin, &options.source.fields)? else { + let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -377,6 +327,20 @@ impl Components { descriptor.set_source_desc(desc); Ok((descriptor, source, parser)) } + + fn descriptors(&self) -> Vec<(&Uuid, &dyn CommonDescriptor)> { + [ + self.sources + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + self.parsers + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + ] + .concat() + } } impl fmt::Debug for Components { diff --git a/application/apps/indexer/components/src/tys.rs b/application/apps/indexer/components/src/tys/mod.rs similarity index 60% rename from application/apps/indexer/components/src/tys.rs rename to application/apps/indexer/components/src/tys/mod.rs index 483a738546..1f9494a109 100644 --- a/application/apps/indexer/components/src/tys.rs +++ b/application/apps/indexer/components/src/tys/mod.rs @@ -1,6 +1,11 @@ +mod parser; +mod source; + use std::{future::Future, pin::Pin}; use crate::*; +pub use parser::*; +pub use source::*; use stypes::SessionAction; use tokio_util::sync::CancellationToken; @@ -55,111 +60,10 @@ pub type StaticFieldsResult = Result, stypes::NativeError /// * `StaticFieldsResult` - A result containing static field data or a native error. pub type LazyFieldsTask = Pin + Send>>; -/// A wrapper for storing the full descriptor of a component. -/// -/// Although the internal value is stored as a `ComponentFactory`, it is important to note -/// that all factories are also required to implement `ComponentDescriptor`, i.e., -/// `ComponentFactory: ComponentDescriptor`. -/// Therefore, this enum represents a full component descriptor, not just a factory. -/// -/// Variants: -/// - `Parser`: A descriptor for a parser component. -/// - `Source`: A descriptor for a source component. -pub enum Entry { - Parser(Box>), - Source(Box>), -} - -impl ComponentDescriptor for Entry { - fn get_render(&self) -> Option { - match self { - Self::Source(inner) => inner.get_render(), - Self::Parser(inner) => inner.get_render(), - } - } - fn fields_getter(&self, origin: &stypes::SessionAction) -> FieldsResult { - match self { - Self::Source(inner) => inner.fields_getter(origin), - Self::Parser(inner) => inner.fields_getter(origin), - } - } - fn ident(&self) -> stypes::Ident { - match self { - Self::Source(inner) => inner.ident(), - Self::Parser(inner) => inner.ident(), - } - } - fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { - match self { - Self::Source(inner) => inner.is_compatible(origin), - Self::Parser(inner) => inner.is_compatible(origin), - } - } - fn is_ty(&self, ty: &stypes::ComponentType) -> bool { - match self { - Self::Source(inner) => inner.is_ty(ty), - Self::Parser(inner) => inner.is_ty(ty), - } - } - fn lazy_fields_getter( - &self, - origin: stypes::SessionAction, - cancel: CancellationToken, - ) -> LazyFieldsTask { - match self { - Self::Source(inner) => inner.lazy_fields_getter(origin, cancel), - Self::Parser(inner) => inner.lazy_fields_getter(origin, cancel), - } - } - fn ty(&self) -> stypes::ComponentType { - match self { - Self::Source(inner) => inner.ty(), - Self::Parser(inner) => inner.ty(), - } - } - fn validate( - &self, - origin: &stypes::SessionAction, - fields: &[stypes::Field], - ) -> Result, stypes::NativeError> { - match self { - Self::Source(inner) => inner.validate(origin, fields), - Self::Parser(inner) => inner.validate(origin, fields), - } - } -} - -/// Defines a factory interface for creating instances of components. -/// -/// In the application architecture, components (such as parsers, sources, etc.) -/// are not instantiated directly. Instead, component instantiation is handled -/// by the `Components` controller, which creates component instances based on -/// registrations made during system initialization. -/// -/// This level of indirection is a key architectural decision, as it allows -/// components to remain decoupled from the application core. This makes it -/// possible to replace or update components without modifying the core logic. -pub trait ComponentFactory: ComponentDescriptor + Sync + Send { - /// Creates an instance of the component of type `T`. - /// - /// # Arguments - /// * `origin` - A reference to a `SessionAction` representing the creation context. - /// For example, it could point to a file, a group of files, or any other data source. - /// * `options` - A slice of `stypes::Field` values representing component options or settings. - /// - /// # Returns - /// * `Ok(Some(T, Option))` if the component was successfully created, where `T` - an - /// instance of component; `Option` - self description for client (e.g. "TCP on 0.0.0.0:8888"). - /// * `Ok(None)` if the factory declined to create a component for the given context. - /// * `Err(stypes::NativeError)` if an error occurred during creation. - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { - Ok(None) - } -} +pub type Factory = fn( + &SessionAction, + &[stypes::Field], +) -> Result)>, stypes::NativeError>; /// Describes a component in terms of its identity, configuration schema, /// validation logic, support for lazy-loading configuration, and its type within the system. @@ -174,11 +78,7 @@ pub trait ComponentFactory: ComponentDescriptor + Sync + Send { /// The actual parser and source implementations remain hidden behind the descriptor, /// making it possible to swap, reconfigure, or isolate components without touching /// the application core. -pub trait ComponentDescriptor { - fn get_render(&self) -> Option { - None - } - +pub trait CommonDescriptor: Sync + Send { /// Check is component is campatible with given origin /// /// * `origin` - The source origin @@ -223,27 +123,6 @@ pub trait ComponentDescriptor { self.ident() } - /// Retrieves the type of the component. - /// - /// # Returns - /// - /// * `stypes::ComponentType` - The type of the component (e.g., parser, source). - fn ty(&self) -> stypes::ComponentType; - - /// Checks if the component's type matches the given type. - /// - /// # Arguments - /// - /// * `ty` - A reference to the type to check against. - /// - /// # Returns - /// - /// * `true` if the component type matches the given type. - /// * `false` otherwise. - fn is_ty(&self, ty: &stypes::ComponentType) -> bool { - &self.ty() == ty - } - /// Retrieves the static field descriptors for the component. /// /// This method returns a list of field descriptors that are directly available without diff --git a/application/apps/indexer/components/src/tys/parser.rs b/application/apps/indexer/components/src/tys/parser.rs new file mode 100644 index 0000000000..50ec2563cb --- /dev/null +++ b/application/apps/indexer/components/src/tys/parser.rs @@ -0,0 +1,18 @@ +use super::CommonDescriptor; + +/// Describes a component in terms of its identity, configuration schema, +/// validation logic, support for lazy-loading configuration, and its type within the system. +/// +/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples +/// the core system from concrete component implementations. Instead of referring +/// to component types directly (e.g., a specific parser or source), the application +/// interacts with components through their descriptors. +/// +/// This design enables a fully modular architecture where, for example, a session +/// can be created using a parser and source identified solely by their UUIDs. +/// The actual parser and source implementations remain hidden behind the descriptor, +/// making it possible to swap, reconfigure, or isolate components without touching +/// the application core. +pub trait ParserDescriptor: CommonDescriptor { + fn get_render(&self) -> Option; +} diff --git a/application/apps/indexer/components/src/tys/source.rs b/application/apps/indexer/components/src/tys/source.rs new file mode 100644 index 0000000000..71a0381ed9 --- /dev/null +++ b/application/apps/indexer/components/src/tys/source.rs @@ -0,0 +1,20 @@ +use super::CommonDescriptor; + +/// Describes a component in terms of its identity, configuration schema, +/// validation logic, support for lazy-loading configuration, and its type within the system. +/// +/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples +/// the core system from concrete component implementations. Instead of referring +/// to component types directly (e.g., a specific parser or source), the application +/// interacts with components through their descriptors. +/// +/// This design enables a fully modular architecture where, for example, a session +/// can be created using a parser and source identified solely by their UUIDs. +/// The actual parser and source implementations remain hidden behind the descriptor, +/// making it possible to swap, reconfigure, or isolate components without touching +/// the application core. +pub trait SourceDescriptor: CommonDescriptor { + fn is_sde_supported(&self, _origin: &stypes::SessionAction, _fields: &[stypes::Field]) -> bool { + false + } +} diff --git a/application/apps/indexer/definitions/Cargo.toml b/application/apps/indexer/definitions/Cargo.toml deleted file mode 100644 index 88035096fe..0000000000 --- a/application/apps/indexer/definitions/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "definitions" -version = "0.1.0" -edition = "2021" - -[dependencies] -thiserror.workspace = true -etherparse.workspace = true -serde = { workspace = true , features = ["derive"] } -stypes = { path = "../stypes", features=["rustcore"] } diff --git a/application/apps/indexer/definitions/src/lib.rs b/application/apps/indexer/definitions/src/lib.rs deleted file mode 100644 index 5f48db9804..0000000000 --- a/application/apps/indexer/definitions/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// The `definitions` crate describes the interfaces (traits) of the core system components: -/// - `Parser`: Responsible for interpreting incoming data. -/// - `Source`: Responsible for delivering data to the parser. -/// - `LogRecordOutput`: The result of parsing; represents the smallest unit of data in the system. -/// - `LogRecordWriter`: Responsible for writing data after processing; consumes `LogRecordOutput`. -mod parser; -mod record; -mod source; - -pub use parser::*; -pub use record::*; -pub use source::*; diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 06c207e029..82453fcf42 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -18,9 +18,9 @@ someip-messages = { git = "https://github.com/esrlabs/someip" } someip-payload = { git = "https://github.com/esrlabs/someip-payload" } uuid = { workspace = true , features = ["serde", "v4"] } tokio-util.workspace = true +thiserror.workspace = true stypes = { path = "../stypes", features=["rustcore"] } -definitions = { path = "../definitions" } file-tools = { path = "../addons/file-tools" } someip-tools = { path = "../addons/someip-tools" } components = { path = "../components" } diff --git a/application/apps/indexer/definitions/src/parser.rs b/application/apps/indexer/parsers/src/api/mod.rs similarity index 95% rename from application/apps/indexer/definitions/src/parser.rs rename to application/apps/indexer/parsers/src/api/mod.rs index 62e8b8a00b..93327ebbee 100644 --- a/application/apps/indexer/definitions/src/parser.rs +++ b/application/apps/indexer/parsers/src/api/mod.rs @@ -1,7 +1,16 @@ +mod record; + use crate::*; -use stypes::NativeError; +use stypes::{NativeError, SessionAction}; use thiserror::Error; +pub use record::*; + +pub type ParserFactory = fn( + &SessionAction, + &[stypes::Field], +) -> Result)>, stypes::NativeError>; + pub type ParseReturnIterator<'a> = Result>)> + 'a>, ParserError>; diff --git a/application/apps/indexer/definitions/src/record.rs b/application/apps/indexer/parsers/src/api/record.rs similarity index 100% rename from application/apps/indexer/definitions/src/record.rs rename to application/apps/indexer/parsers/src/api/record.rs diff --git a/application/apps/indexer/parsers/src/dlt/attachment.rs b/application/apps/indexer/parsers/src/dlt/attachment.rs index 46703f3676..70651cde4e 100644 --- a/application/apps/indexer/parsers/src/dlt/attachment.rs +++ b/application/apps/indexer/parsers/src/dlt/attachment.rs @@ -1,7 +1,7 @@ use dlt_core::dlt::{Argument, LogLevel, Message, MessageType, PayloadContent, Value}; use std::{collections::HashMap, path::PathBuf}; -use definitions::*; +use crate::*; const FT_START_TAG: &str = "FLST"; const FT_DATA_TAG: &str = "FLDA"; diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 4475f32260..7111c8ee6b 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -1,5 +1,5 @@ use crate::dlt::*; -use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; +use components::{CommonDescriptor, ParserDescriptor, StaticFieldResult}; use dlt_core::{ read::DltMessageReader, statistics::{ @@ -48,92 +48,60 @@ impl fmt::Display for StatFields { } } -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { - return Err(NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), - }); - } - let fibex_file_paths: &Vec = options - .extract_by_key(FIELD_FIBEX_FILES) - .ok_or(missed(FIELD_FIBEX_FILES))? - .value; - let dlt_metadata = dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { - fibex_file_paths: fibex_file_paths - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect(), - }); - let someip_metadata = FibexSomeipMetadata::from_fibex_files(fibex_file_paths); - let min_log_level = u8_to_log_level( - options - .extract_by_key(FIELD_LOG_LEVEL) - .ok_or(missed(FIELD_LOG_LEVEL))? - .value, - ) - .ok_or(missed(FIELD_LOG_LEVEL))?; - let stats: &HashMap> = options - .extract_by_key(FIELD_STATISTICS) - .ok_or(missed(FIELD_STATISTICS))? - .value; - let filter_config: ProcessedDltFilterConfig = ProcessedDltFilterConfig { - min_log_level: Some(min_log_level), - app_ids: stats - .get(&StatFields::AppIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - ecu_ids: stats - .get(&StatFields::EcuIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - context_ids: stats - .get(&StatFields::ContextIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - app_id_count: 0, - context_id_count: 0, - }; - Ok(Some(( - crate::Parser::Dlt(DltParser::new( - Some(filter_config), - dlt_metadata, - None, - someip_metadata, - // If it's source - no storage header expected - !matches!(origin, SessionAction::Source), - )), - Some("DLT".to_owned()), - ))) - } +pub fn factory( + origin: &SessionAction, + options: &[Field], +) -> Result)>, NativeError> { + let fibex_file_paths: &Vec = options + .extract_by_key(FIELD_FIBEX_FILES) + .ok_or(missed(FIELD_FIBEX_FILES))? + .value; + let dlt_metadata = dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { + fibex_file_paths: fibex_file_paths + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect(), + }); + let someip_metadata = FibexSomeipMetadata::from_fibex_files(fibex_file_paths); + let min_log_level = u8_to_log_level( + options + .extract_by_key(FIELD_LOG_LEVEL) + .ok_or(missed(FIELD_LOG_LEVEL))? + .value, + ) + .ok_or(missed(FIELD_LOG_LEVEL))?; + let stats: &HashMap> = options + .extract_by_key(FIELD_STATISTICS) + .ok_or(missed(FIELD_STATISTICS))? + .value; + let filter_config: ProcessedDltFilterConfig = ProcessedDltFilterConfig { + min_log_level: Some(min_log_level), + app_ids: stats + .get(&StatFields::AppIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + ecu_ids: stats + .get(&StatFields::EcuIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + context_ids: stats + .get(&StatFields::ContextIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + app_id_count: 0, + context_id_count: 0, + }; + Ok(Some(( + crate::Parsers::Dlt(DltParser::new( + Some(filter_config), + dlt_metadata, + None, + someip_metadata, + // If it's source - no storage header expected + !matches!(origin, SessionAction::Source), + )), + Some("DLT".to_owned()), + ))) } -impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> Option { - Some(stypes::OutputRender::Columns(vec![ - ("Datetime".to_owned(), 150), - ("ECUID".to_owned(), 80), - ("VERS".to_owned(), 80), - ("SID".to_owned(), 80), - ("MCNT".to_owned(), 80), - ("TMS".to_owned(), 80), - ("EID".to_owned(), 80), - ("APID".to_owned(), 80), - ("CTID".to_owned(), 80), - ("MSTP".to_owned(), 80), - ("PAYLOAD".to_owned(), 0), - ])) - } +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -331,11 +299,25 @@ impl ComponentDescriptor for Descriptor { })]) }) } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser - } } +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + Some(stypes::OutputRender::Columns(vec![ + ("Datetime".to_owned(), 150), + ("ECUID".to_owned(), 80), + ("VERS".to_owned(), 80), + ("SID".to_owned(), 80), + ("MCNT".to_owned(), 80), + ("TMS".to_owned(), 80), + ("EID".to_owned(), 80), + ("APID".to_owned(), 80), + ("CTID".to_owned(), 80), + ("MSTP".to_owned(), 80), + ("PAYLOAD".to_owned(), 0), + ])) + } +} fn u8_to_log_level(level: u8) -> Option { match level { 1 => Some(dlt::LogLevel::Fatal), diff --git a/application/apps/indexer/parsers/src/dlt/mod.rs b/application/apps/indexer/parsers/src/dlt/mod.rs index 1fcebac618..c88938340e 100644 --- a/application/apps/indexer/parsers/src/dlt/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/mod.rs @@ -4,8 +4,10 @@ pub mod fmt; pub mod raw; use self::{attachment::FtScanner, fmt::FormatOptions}; -use crate::{dlt::fmt::FormattableMessage, someip::FibexMetadata as FibexSomeipMetadata}; -use definitions::*; +use crate::{ + Attachment, LogRecordOutput, ParserError, SingleParser, dlt::fmt::FormattableMessage, + someip::FibexMetadata as FibexSomeipMetadata, +}; use dlt_core::{ dlt, parse::{DltParseError, dlt_message}, diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index c8ded05999..511cd9d028 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,5 +1,6 @@ use super::*; -use components::{ComponentDescriptor, ComponentFactory}; +use crate::*; +use components::{CommonDescriptor, ParserDescriptor}; use stypes::SessionAction; use tokio_util::sync::CancellationToken; @@ -10,20 +11,17 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { - Ok(Some(( - crate::Parser::DltRaw(DltRawParser::new(!matches!(origin, SessionAction::Source))), - Some("DLT".to_string()), - ))) - } +pub fn factory( + origin: &SessionAction, + _options: &[stypes::Field], +) -> Result)>, stypes::NativeError> { + Ok(Some(( + Parsers::DltRaw(DltRawParser::new(!matches!(origin, SessionAction::Source))), + Some("DLT".to_string()), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(..) | SessionAction::Files(..) | SessionAction::Source => { @@ -58,7 +56,10 @@ impl ComponentDescriptor for Descriptor { ) -> components::LazyFieldsTask { Box::pin(async move { Ok(Vec::new()) }) } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser +} + +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + None } } diff --git a/application/apps/indexer/parsers/src/dlt/raw/mod.rs b/application/apps/indexer/parsers/src/dlt/raw/mod.rs index b4084abc41..4de6a57088 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/mod.rs @@ -1,7 +1,7 @@ pub mod descriptor; use super::{MIN_MSG_LEN, map_dlt_err}; -use definitions::*; +use crate::*; pub use descriptor::*; use dlt_core::parse::dlt_consume_msg; pub use dlt_core::{ diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 30ccb801b1..68be59b914 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,13 +1,15 @@ // #![deny(unused_crate_dependencies)] use components::Components; -use definitions::ParseReturnIterator; +pub mod api; pub mod dlt; pub mod prelude; pub mod someip; pub mod text; -pub enum Parser { +pub use api::*; + +pub enum Parsers { DltRaw(dlt::raw::DltRawParser), Dlt(dlt::DltParser), SomeIp(someip::SomeipParser), @@ -24,7 +26,7 @@ pub enum Parser { * Into MessageProducer we should put exact instances of parsers instead enum-wrapper */ -impl definitions::Parser for Parser { +impl Parser for Parsers { fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { match self { Self::Dlt(inst) => inst.parse(input, timestamp), @@ -38,10 +40,22 @@ impl definitions::Parser for Parser { // TODO: this registration function will fail if some of parser would not be registred. That's wrong. // If some of parsers are failed, another parsers should still be registred as well -pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { - components.add_parser(dlt::descriptor::Descriptor::default())?; - components.add_parser(dlt::raw::descriptor::Descriptor::default())?; - components.add_parser(someip::descriptor::Descriptor::default())?; - components.add_parser(text::descriptor::Descriptor::default())?; +pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { + components.add_parser( + dlt::descriptor::factory, + dlt::descriptor::Descriptor::default(), + )?; + components.add_parser( + dlt::descriptor::factory, + dlt::raw::descriptor::Descriptor::default(), + )?; + components.add_parser( + someip::descriptor::factory, + someip::descriptor::Descriptor::default(), + )?; + components.add_parser( + text::descriptor::factory, + text::descriptor::Descriptor::default(), + )?; Ok(()) } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 6728ff2913..babc11d04f 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,4 +1,4 @@ -use components::{ComponentDescriptor, ComponentFactory}; +use components::{CommonDescriptor, ParserDescriptor}; use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; use super::SomeipParser; @@ -15,34 +15,17 @@ const FIELD_TZ: &str = "SOMEIP_PARSER_FIELD_TIMEZONE"; #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { - Ok(Some(( - crate::Parser::SomeIp(SomeipParser::new()), - Some("SomeIp".to_owned()), - ))) - } +pub fn factory( + _origin: &SessionAction, + _options: &[stypes::Field], +) -> Result)>, stypes::NativeError> { + Ok(Some(( + crate::Parsers::SomeIp(SomeipParser::new()), + Some("SomeIp".to_owned()), + ))) } -impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> Option { - Some(stypes::OutputRender::Columns(vec![ - ("SOME/IP".to_owned(), 50), - ("SERV".to_owned(), 50), - ("METH".to_owned(), 50), - ("LENG".to_owned(), 30), - ("CLID".to_owned(), 30), - ("SEID".to_owned(), 30), - ("IVER".to_owned(), 30), - ("MSTP".to_owned(), 30), - ("RETC".to_owned(), 30), - ("PAYLOAD".to_owned(), 0), - ])) - } +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -92,7 +75,21 @@ impl ComponentDescriptor for Descriptor { uuid: SOMEIP_PARSER_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser +} + +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + Some(stypes::OutputRender::Columns(vec![ + ("SOME/IP".to_owned(), 50), + ("SERV".to_owned(), 50), + ("METH".to_owned(), 50), + ("LENG".to_owned(), 30), + ("CLID".to_owned(), 30), + ("SEID".to_owned(), 30), + ("IVER".to_owned(), 30), + ("MSTP".to_owned(), 30), + ("RETC".to_owned(), 30), + ("PAYLOAD".to_owned(), 0), + ])) } } diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index cf6fab702a..3d49d5ea32 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -1,5 +1,15 @@ pub mod descriptor; -use definitions::*; +use crate::*; +use lazy_static::lazy_static; +use log::{debug, error}; +use regex::Regex; +use serde::Serialize; +use someip_messages::*; +use someip_payload::{ + fibex::{FibexModel, FibexParser, FibexReader, FibexServiceInterface, FibexTypeDeclaration}, + fibex2som::FibexTypes, + som::{SOMParser, SOMType}, +}; use std::{ borrow::Cow, cmp::Ordering, @@ -9,18 +19,6 @@ use std::{ sync::Mutex, }; -use someip_messages::*; -use someip_payload::{ - fibex::{FibexModel, FibexParser, FibexReader, FibexServiceInterface, FibexTypeDeclaration}, - fibex2som::FibexTypes, - som::{SOMParser, SOMType}, -}; - -use lazy_static::lazy_static; -use log::{debug, error}; -use regex::Regex; -use serde::Serialize; - /// Marker for a column separator in the output string. const COLUMN_SEP: &str = "\u{0004}"; // EOT /// Marker for a newline in the output string. diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 3bd71dee9f..2c13d37cf3 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,4 +1,4 @@ -use components::{ComponentDescriptor, ComponentFactory}; +use components::{CommonDescriptor, ParserDescriptor}; use file_tools::is_binary; use stypes::SessionAction; @@ -11,20 +11,14 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { - Ok(Some((crate::Parser::Text(StringTokenizer {}), None))) - } +pub fn factory( + _origin: &SessionAction, + _options: &[stypes::Field], +) -> Result)>, stypes::NativeError> { + Ok(Some((crate::Parsers::Text(StringTokenizer {}), None))) } -impl ComponentDescriptor for Descriptor { - fn get_render(&self) -> Option { - Some(stypes::OutputRender::PlaitText) - } +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &stypes::SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -49,7 +43,10 @@ impl ComponentDescriptor for Descriptor { uuid: TEXT_PARSER_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser +} + +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + Some(stypes::OutputRender::PlaitText) } } diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 8cb98dec3b..7a9aadcc4c 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -1,9 +1,8 @@ pub mod descriptor; -use std::borrow::Cow; - -use definitions::*; +use crate::*; use serde::Serialize; +use std::borrow::Cow; /// The most likely minimal bytes count needed to parse a text message. const MIN_MSG_LEN: usize = 1; diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index 9e0526a402..fee7d8b557 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -23,7 +23,6 @@ tokio-util.workspace = true parsers = { path = "../parsers" } sources = { path = "../sources" } stypes = { path = "../stypes" } -definitions = { path = "../definitions" } # TODO : Introduce shared crates and move dir_checksum to it. dir_checksum = { path = "../../../../cli/development-cli/dir_checksum" } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 4b6983e038..dc5db04873 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -2,7 +2,8 @@ use std::path::{Path, PathBuf}; use wasmtime::component::Component; -use definitions::{self as defs, ParseReturnIterator}; +use components::{CommonDescriptor, ParserDescriptor}; +use parsers::api::*; use stypes::{PluginInfo, SemanticVersion}; use crate::{ @@ -135,7 +136,7 @@ impl PluginErrorLimits { const INCOMPLETE_ERROR_LIMIT: usize = 50; } -impl defs::Parser for PluginsParser { +impl Parser for PluginsParser { fn parse<'a>(&'a mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { let res = match &mut self.parser { PlugVerParser::Ver010(parser) => parser.parse(input, timestamp), @@ -143,32 +144,32 @@ impl defs::Parser for PluginsParser { // Check for consecutive errors. match &res { - Ok(_) | Err(defs::ParserError::Unrecoverable(_)) | Err(defs::ParserError::Eof) => { + Ok(_) | Err(ParserError::Unrecoverable(_)) | Err(ParserError::Eof) => { self.errors_counter = 0; } - Err(defs::ParserError::Parse(err)) => { + Err(ParserError::Parse(err)) => { self.errors_counter += 1; if self.errors_counter > PluginErrorLimits::PARSE_ERROR_LIMIT { self.errors_counter = 0; - return Err(defs::ParserError::Unrecoverable(format!( + return Err(ParserError::Unrecoverable(format!( "Plugin parser returned more than \ {} recoverable parse errors consecutively\n. Parse Error: {err}", PluginErrorLimits::PARSE_ERROR_LIMIT ))); } } - Err(defs::ParserError::Incomplete) => { + Err(ParserError::Incomplete) => { self.errors_counter += 1; if self.errors_counter > PluginErrorLimits::INCOMPLETE_ERROR_LIMIT { self.errors_counter = 0; - return Err(defs::ParserError::Unrecoverable(format!( + return Err(ParserError::Unrecoverable(format!( "Plugin parser returned more than \ {} recoverable incomplete errors consecutively", PluginErrorLimits::INCOMPLETE_ERROR_LIMIT ))); } } - Err(defs::ParserError::Native(err)) => { + Err(ParserError::Native(err)) => { todo!("Not implemented") } } @@ -176,3 +177,27 @@ impl defs::Parser for PluginsParser { res } } + +#[derive(Default)] +struct Descriptor {} + +impl CommonDescriptor for Descriptor { + fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { + true + } + /// **ATTANTION** That's placeholder. Should be another way to delivery data + fn ident(&self) -> stypes::Ident { + stypes::Ident { + name: String::from("Plugin"), + desc: String::from("Plugin"), + io: stypes::IODataType::Any, + uuid: uuid::Uuid::new_v4(), + } + } +} + +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + Some(stypes::OutputRender::PlaitText) + } +} diff --git a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs index a9cd0ebdf6..2508d617f5 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs @@ -1,4 +1,4 @@ -use definitions::LogRecordOutput; +use parsers::api::LogRecordOutput; use serde::Serialize; /// Represent the message of the parsed item returned by plugins. diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs index 16df7936eb..d836de5644 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/bindings.rs @@ -1,6 +1,5 @@ +use parsers::api; use std::borrow::Cow; - -use definitions::{self as defs, LogRecordOutput}; use stypes::ParserRenderOptions; pub use self::chipmunk::parser::parse_types::*; @@ -42,22 +41,26 @@ impl From<&stypes::PluginParserGeneralSettings> for ParserConfig { } } -impl From for LogRecordOutput<'_> { +impl From for api::LogRecordOutput<'_> { fn from(value: ParseYield) -> Self { match value { ParseYield::Message(parsed_message) => parsed_message.into(), - ParseYield::Attachment(attachment) => LogRecordOutput::Attachment(attachment.into()), - ParseYield::MessageAndAttachment((msg, attachment)) => LogRecordOutput::Multiple(vec![ - msg.into(), - LogRecordOutput::Attachment(attachment.into()), - ]), + ParseYield::Attachment(attachment) => { + api::LogRecordOutput::Attachment(attachment.into()) + } + ParseYield::MessageAndAttachment((msg, attachment)) => { + api::LogRecordOutput::Multiple(vec![ + msg.into(), + api::LogRecordOutput::Attachment(attachment.into()), + ]) + } } } } -impl From for defs::Attachment { +impl From for api::Attachment { fn from(att: Attachment) -> Self { - defs::Attachment { + api::Attachment { data: att.data, name: att.name, size: att.size as usize, @@ -68,23 +71,23 @@ impl From for defs::Attachment { } } -impl From for defs::ParserError { +impl From for api::ParserError { fn from(err: ParseError) -> Self { match err { - ParseError::Unrecoverable(msg) => defs::ParserError::Unrecoverable(msg), - ParseError::Parse(msg) => defs::ParserError::Parse(msg), - ParseError::Incomplete => defs::ParserError::Incomplete, - ParseError::Eof => defs::ParserError::Eof, + ParseError::Unrecoverable(msg) => api::ParserError::Unrecoverable(msg), + ParseError::Parse(msg) => api::ParserError::Parse(msg), + ParseError::Incomplete => api::ParserError::Incomplete, + ParseError::Eof => api::ParserError::Eof, } } } -impl From for LogRecordOutput<'_> { +impl From for api::LogRecordOutput<'_> { fn from(msg: ParsedMessage) -> Self { match msg { - ParsedMessage::Line(msg) => LogRecordOutput::Message(msg.into()), + ParsedMessage::Line(msg) => api::LogRecordOutput::Message(msg.into()), ParsedMessage::Columns(columns) => { - LogRecordOutput::Columns(columns.into_iter().map(Cow::Owned).collect()) + api::LogRecordOutput::Columns(columns.into_iter().map(Cow::Owned).collect()) } } } diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 33314f4b25..70ab6cb661 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -4,7 +4,8 @@ mod bindings; mod parser_plugin_state; -use components::ComponentDescriptor; +use components::{CommonDescriptor, ParserDescriptor}; +use parsers::api::*; use stypes::{ParserRenderOptions, RenderOptions, SemanticVersion}; use tokio::runtime::Handle; use wasmtime::{ @@ -132,9 +133,7 @@ impl PluginParser { } } -use definitions::{self as defs, LogRecordOutput, ParseReturnIterator}; - -impl defs::Parser for PluginParser { +impl Parser for PluginParser { fn parse<'a>(&mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { // Calls on plugins must be async. To solve that we got the following solutions: // - `futures::executor::block_on(plugin_call)`: Blocks the current Tokio worker with a local @@ -155,7 +154,7 @@ impl defs::Parser for PluginParser { Ok(results) => results?, Err(call_err) => { // Wasmtime uses anyhow error, which provides error context in debug print only. - return Err(defs::ParserError::Unrecoverable(format!( + return Err(ParserError::Unrecoverable(format!( "Call parse on the plugin failed. Error: {call_err:?}" ))); } @@ -175,7 +174,7 @@ impl defs::Parser for PluginParser { #[derive(Default)] struct Descriptor {} -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, _origin: &stypes::SessionAction) -> bool { true } @@ -188,7 +187,10 @@ impl ComponentDescriptor for Descriptor { uuid: uuid::Uuid::new_v4(), } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Parser +} + +impl ParserDescriptor for Descriptor { + fn get_render(&self) -> Option { + Some(stypes::OutputRender::PlaitText) } } diff --git a/application/apps/indexer/processor/Cargo.toml b/application/apps/indexer/processor/Cargo.toml index 6ea4e562d2..fb774a3d71 100644 --- a/application/apps/indexer/processor/Cargo.toml +++ b/application/apps/indexer/processor/Cargo.toml @@ -26,7 +26,6 @@ text_grep = { path = "../addons/text_grep" } stypes = { path = "../stypes", features=["rustcore"] } parsers = { path = "../parsers" } sources = { path = "../sources" } -definitions = { path = "../definitions" } indexer_base = { path = "../indexer_base" } [dev-dependencies] diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 3149ea4990..fbfe77eedf 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -2,9 +2,9 @@ mod tests; pub mod sde; - -use definitions::*; use log::warn; +use parsers::api::*; +use sources::api::*; /// ********************************************************************************************** /// For Ammar: diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index f0c7cc11d7..3f62bb2ae3 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -28,7 +28,6 @@ walkdir.workspace = true indexer_base = { path = "../indexer_base" } plugins_host = { path = "../plugins_host/"} components = { path = "../components" } -definitions = { path = "../definitions" } stypes = { path = "../stypes", features=["rustcore"] } parsers = { path = "../parsers" } processor = { path = "../processor" } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 3b60c3f048..6d11e6116a 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -60,7 +60,7 @@ impl ComponentsSession { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Components = Components::new(); // Registre parsers parsers::registration(&mut components)?; // Registre sources @@ -179,7 +179,7 @@ impl ComponentsSession { } } Api::GetComponents(origin, ty, tx) => { - log_if_err(tx.send(components.get_components(&ty, origin))); + // log_if_err(tx.send(components.get_components(&ty, origin))); } // Client doesn't need any more field data. Loading task should be cancelled Api::CancelLoading(fields) => { diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index 4a01ade22e..94c81ab52f 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -1,7 +1,8 @@ use crate::operations::{OperationAPI, OperationResult}; -use definitions::{ByteSource, LogRecordOutput, LogRecordsBuffer, Parser}; use log::debug; +use parsers::api::*; use processor::producer::MessageProducer; +use sources::api::*; use std::{ fs::File, @@ -64,7 +65,7 @@ impl LogRecordsBuffer for ExportLogsBuffer { if let Some(first_item) = items.next() { self.bytes_buffer.extend_from_slice(first_item.as_bytes()); for item in items { - self.bytes_buffer.push(definitions::COLUMN_SENTINAL as u8); + self.bytes_buffer.push(parsers::api::COLUMN_SENTINAL as u8); self.bytes_buffer.extend_from_slice(item.as_bytes()); } } diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index 3fc6fc42bf..c619fc58ba 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -14,7 +14,7 @@ pub async fn observing( operation_api: OperationAPI, state: SessionStateAPI, options: SessionSetup, - components: Arc>, + components: Arc>, rx_sde: Option, ) -> OperationResult<()> { match &options.origin { diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index 81dc352255..309246bbbf 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -3,9 +3,11 @@ use crate::{ state::SessionStateAPI, tail, }; -use definitions::*; use log::{trace, warn}; +use parsers::api; +use parsers::api::*; use processor::producer::{MessageProducer, MessageStreamItem, sde::*}; +use sources::api::*; use std::time::Instant; use tokio::{select, sync::mpsc::Receiver}; @@ -70,7 +72,7 @@ impl LogRecordsBuffer for LogsBuffer { if let Some(first_item) = items.next() { self.text_buffer.push_str(&first_item); for item in items { - self.text_buffer.push(definitions::COLUMN_SENTINAL); + self.text_buffer.push(api::COLUMN_SENTINAL); self.text_buffer.push_str(&item); } } diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index 2d806d7710..c68351e37e 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -76,7 +76,7 @@ impl Operation { pub enum OperationKind { Observe( stypes::SessionSetup, - Arc>, + Arc>, ), Search { filters: Vec, diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index 9ae64dd5d4..d84e1d20e3 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -32,7 +32,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, - components: Arc>, + components: Arc>, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -56,7 +56,7 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Components = Components::new(); // Registre parsers parsers::registration(&mut components) .map_err(|err| stypes::ComputationError::NativeError(err))?; diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index 78f589a333..8902acbeb7 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -191,7 +191,7 @@ pub enum Api { SetDebugMode((bool, oneshot::Sender<()>)), NotifyCancelingOperation(Uuid), NotifyCanceledOperation(Uuid), - AddAttachment(definitions::Attachment), + AddAttachment(parsers::api::Attachment), GetAttachments(oneshot::Sender>), // Used for tests of error handeling ShutdownWithError, @@ -692,7 +692,7 @@ impl SessionStateAPI { pub fn add_attachment( &self, - origin: definitions::Attachment, + origin: parsers::api::Attachment, ) -> Result<(), stypes::NativeError> { self.tx_api.send(Api::AddAttachment(origin)).map_err(|e| { stypes::NativeError::channel( diff --git a/application/apps/indexer/session/src/state/attachments.rs b/application/apps/indexer/session/src/state/attachments.rs index 0d682f2014..6ef1a1655c 100644 --- a/application/apps/indexer/session/src/state/attachments.rs +++ b/application/apps/indexer/session/src/state/attachments.rs @@ -99,7 +99,7 @@ impl Attachments { } pub fn get_attch_from( - origin: definitions::Attachment, + origin: parsers::api::Attachment, store_folder: &PathBuf, ) -> Result { if !store_folder.exists() { @@ -146,7 +146,7 @@ impl Attachments { pub fn add( &mut self, - attachment: definitions::Attachment, + attachment: parsers::api::Attachment, ) -> Result { if let Some(dest) = self.dest.as_ref() { let uuid = Uuid::new_v4(); diff --git a/application/apps/indexer/session/src/state/mod.rs b/application/apps/indexer/session/src/state/mod.rs index 93606cce5a..fd297e9d99 100644 --- a/application/apps/indexer/session/src/state/mod.rs +++ b/application/apps/indexer/session/src/state/mod.rs @@ -495,7 +495,7 @@ impl SessionState { fn handle_add_attachment( &mut self, - origin: definitions::Attachment, + origin: parsers::api::Attachment, tx_callback_events: UnboundedSender, ) -> Result<(), stypes::NativeError> { let attachment = self.attachments.add(origin)?; diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index df80382885..0710edc59f 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -31,7 +31,6 @@ envvars.workspace = true file-tools = { path = "../addons/file-tools" } stypes = { path = "../stypes", features=["rustcore"] } components = { path = "../components" } -definitions = { path = "../definitions" } [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/definitions/src/source.rs b/application/apps/indexer/sources/src/api/mod.rs similarity index 96% rename from application/apps/indexer/definitions/src/source.rs rename to application/apps/indexer/sources/src/api/mod.rs index 7480884f58..c811a011d0 100644 --- a/application/apps/indexer/definitions/src/source.rs +++ b/application/apps/indexer/sources/src/api/mod.rs @@ -1,5 +1,12 @@ +use crate::*; +use stypes::SessionAction; use thiserror::Error; +pub type SourceFactory = fn( + &SessionAction, + &[stypes::Field], +) -> Result)>, stypes::NativeError>; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum TransportProtocol { TCP, diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index a17f74d596..ab3b14f431 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -1,7 +1,7 @@ -use components::{ComponentFactory, ComponentDescriptor}; +use components::{SourceDescriptor, CommonDescriptor}; use file_tools::is_binary; use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; - +use crate::*; use super::PcapLegacyByteSourceFromFile; const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -11,12 +11,10 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, + pub fn factory( origin: &SessionAction, _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { let filepath = match origin { SessionAction::File(file) => file, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(.. ) => { @@ -27,11 +25,11 @@ impl ComponentFactory for Descriptor { }) } }; - Ok(Some((crate::Source::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?), Some("Pcap".to_owned())))) + Ok(Some((Sources::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?), Some("Pcap".to_owned())))) } -} -impl ComponentDescriptor for Descriptor { + +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -60,11 +58,11 @@ impl ComponentDescriptor for Descriptor { uuid: PCAP_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } + } +impl SourceDescriptor for Descriptor {} + #[cfg(test)] mod tests { diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs index eb7e9b607d..8c56b871b3 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/mod.rs @@ -1,11 +1,11 @@ mod descriptor; use crate::binary::pcap::debug_block; +use crate::*; use bufread::DeqBuffer; -use definitions::*; use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; -use pcap_parser::{traits::PcapReaderIterator, LegacyPcapReader, PcapBlockOwned, PcapError}; +use pcap_parser::{LegacyPcapReader, PcapBlockOwned, PcapError, traits::PcapReaderIterator}; use std::{fs::File, io::Read, path::Path}; pub use descriptor::*; diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 3456ee4e3f..acd16736da 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -1,8 +1,8 @@ -use components::{ComponentFactory,ComponentDescriptor}; +use components::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; - use super::PcapngByteSourceFromFile; +use crate::*; const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, @@ -11,12 +11,10 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, + pub fn factory( origin: &SessionAction, _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { + ) -> Result)>, stypes::NativeError> { let filepath = match origin { SessionAction::File(file) => file, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { @@ -27,11 +25,11 @@ impl ComponentFactory for Descriptor { }) } }; - Ok(Some((crate::Source::PcapNg(PcapngByteSourceFromFile::new(filepath)?), Some("PcapNg".to_owned())))) + Ok(Some((Sources::PcapNg(PcapngByteSourceFromFile::new(filepath)?), Some("PcapNg".to_owned())))) } -} -impl ComponentDescriptor for Descriptor { + +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -60,8 +58,8 @@ impl ComponentDescriptor for Descriptor { uuid: PCAPNG_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } } +impl SourceDescriptor for Descriptor {} + + diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs index 8f27cbb850..d3ee00aee2 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs @@ -1,11 +1,11 @@ mod descriptor; use crate::binary::pcap::debug_block; +use crate::*; use bufread::DeqBuffer; -use definitions::*; use etherparse::{SlicedPacket, TransportSlice}; use log::{debug, error, trace}; -use pcap_parser::{traits::PcapReaderIterator, PcapBlockOwned, PcapError, PcapNGReader}; +use pcap_parser::{PcapBlockOwned, PcapError, PcapNGReader, traits::PcapReaderIterator}; use std::{fs::File, io::Read, path::Path}; pub use descriptor::*; diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 523aa2b943..51a39668ad 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -1,6 +1,6 @@ -use components::{ComponentDescriptor, ComponentFactory}; +use crate::*; +use components::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; - use stypes::SessionAction; const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -10,33 +10,30 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - _options: &[stypes::Field], - ) -> Result)>, stypes::NativeError> { - let filename = match origin { - SessionAction::File(filename) => filename, - SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some( - "origin isn't supported by this source (BinaryByteSource)".to_owned(), - ), - }); - } - }; +pub fn factory( + origin: &SessionAction, + _options: &[stypes::Field], +) -> Result)>, stypes::NativeError> { + let filename = match origin { + SessionAction::File(filename) => filename, + SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some( + "origin isn't supported by this source (BinaryByteSource)".to_owned(), + ), + }); + } + }; - Ok(Some(( - crate::Source::Raw(super::BinaryByteSourceFromFile::new(filename)?), - Some(filename.to_string_lossy().to_string()), - ))) - } + Ok(Some(( + Sources::Raw(super::BinaryByteSourceFromFile::new(filename)?), + Some(filename.to_string_lossy().to_string()), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { let files = match origin { SessionAction::File(filepath) => { @@ -60,7 +57,6 @@ impl ComponentDescriptor for Descriptor { uuid: BIN_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } } + +impl SourceDescriptor for Descriptor {} diff --git a/application/apps/indexer/sources/src/binary/raw/mod.rs b/application/apps/indexer/sources/src/binary/raw/mod.rs index ad9cc4896e..25e51a98d4 100644 --- a/application/apps/indexer/sources/src/binary/raw/mod.rs +++ b/application/apps/indexer/sources/src/binary/raw/mod.rs @@ -1,7 +1,7 @@ mod descriptor; +use crate::*; use bufread::BufReader; -use definitions::*; pub use descriptor::*; use std::{ fs::File, diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index cd08dd77a2..58c49e3386 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -1,5 +1,6 @@ use super::ProcessSource; -use components::{ComponentDescriptor, ComponentFactory}; +use crate::*; +use components::{CommonDescriptor, SourceDescriptor}; use std::{collections::HashMap, env}; use stypes::{ ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, @@ -17,40 +18,23 @@ const FIELD_SHELLS: &str = "COMMAND_FIELD_SHELLS"; #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { - return Err(NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), - }); - } - let command: String = options - .extract_by_key(FIELD_COMMAND) - .ok_or(missed(FIELD_COMMAND))? - .value; - Ok(Some(( - crate::Source::Process( - ProcessSource::new(&command, env::current_dir().unwrap(), HashMap::new()).unwrap(), - ), - Some(command), - ))) - } +pub fn factory( + origin: &SessionAction, + options: &[Field], +) -> Result)>, NativeError> { + let command: String = options + .extract_by_key(FIELD_COMMAND) + .ok_or(missed(FIELD_COMMAND))? + .value; + Ok(Some(( + Sources::Process( + ProcessSource::new(&command, env::current_dir().unwrap(), HashMap::new()).unwrap(), + ), + Some(command), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { let mut shells = vec![("".to_owned(), "Default".to_owned())]; if let Ok(profiles) = envvars::get_profiles() { @@ -111,9 +95,6 @@ impl ComponentDescriptor for Descriptor { uuid: TERM_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } fn validate( &self, _origin: &SessionAction, @@ -129,3 +110,4 @@ impl ComponentDescriptor for Descriptor { Ok(errors) } } +impl SourceDescriptor for Descriptor {} diff --git a/application/apps/indexer/sources/src/command/mod.rs b/application/apps/indexer/sources/src/command/mod.rs index 951795e5da..56f50a4046 100644 --- a/application/apps/indexer/sources/src/command/mod.rs +++ b/application/apps/indexer/sources/src/command/mod.rs @@ -1,8 +1,8 @@ mod descriptor; pub use descriptor::*; +use crate::*; use bufread::DeqBuffer; -use definitions::*; use regex::{Captures, Regex}; use shellexpand::tilde; use std::{collections::HashMap, ffi::OsString, path::PathBuf, process::Stdio}; diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 77fe8f344a..3f76309fd7 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,5 +1,7 @@ +pub mod api; + +pub use api::*; use components::Components; -use definitions::{ReloadInfo, SourceError, SourceFilter}; // Rust can't currently distinguish between dev and none-dev dependencies at the moment. There is // an open issue for this case: "https://github.com/rust-lang/rust/issues/129637" @@ -15,7 +17,7 @@ pub mod prelude; pub mod serial; pub mod socket; -pub enum Source { +pub enum Sources { Raw(binary::raw::BinaryByteSourceFromFile), Pcap(binary::pcap::legacy::PcapLegacyByteSourceFromFile), PcapNg(binary::pcap::ng::PcapngByteSourceFromFile), @@ -25,7 +27,7 @@ pub enum Source { Process(command::ProcessSource), } -impl definitions::ByteSource for Source { +impl ByteSource for Sources { fn consume(&mut self, offset: usize) { match self { Self::Raw(inner) => inner.consume(offset), @@ -107,13 +109,22 @@ impl definitions::ByteSource for Source { // TODO: this registration function will fail if some of source would not be registred. That's wrong. // If some of sources are failed, another sources should still be registred as well -pub fn registration

(components: &mut Components) -> Result<(), stypes::NativeError> { - components.add_source(binary::raw::Descriptor::default())?; - components.add_source(binary::pcap::legacy::Descriptor::default())?; - components.add_source(binary::pcap::ng::Descriptor::default())?; - components.add_source(socket::tcp::Descriptor::default())?; - components.add_source(socket::udp::Descriptor::default())?; - components.add_source(serial::descriptor::Descriptor::default())?; - components.add_source(command::Descriptor::default())?; +pub fn registration

(components: &mut Components) -> Result<(), stypes::NativeError> { + components.add_source(binary::raw::factory, binary::raw::Descriptor::default())?; + components.add_source( + binary::pcap::legacy::factory, + binary::pcap::legacy::Descriptor::default(), + )?; + components.add_source( + binary::pcap::ng::factory, + binary::pcap::ng::Descriptor::default(), + )?; + components.add_source(socket::tcp::factory, socket::tcp::Descriptor::default())?; + components.add_source(socket::udp::factory, socket::udp::Descriptor::default())?; + components.add_source( + serial::descriptor::factory, + serial::descriptor::Descriptor::default(), + )?; + components.add_source(command::factory, command::Descriptor::default())?; Ok(()) } diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 8675a36ed9..b7a51eb99f 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -1,12 +1,12 @@ -use components::{ComponentDescriptor, ComponentFactory, StaticFieldResult}; +use crate::prelude::SerialSource; +use crate::*; +use components::{CommonDescriptor, SourceDescriptor, StaticFieldResult}; use std::{collections::HashMap, str}; use stypes::{ ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; -use crate::prelude::SerialSource; - use super::serialport::SerialConfig; const SERIAL_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -35,69 +35,52 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { - return Err(NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), - }); - } - let path: String = options - .extract_by_key(FIELD_PATH) - .ok_or(missed(FIELD_PATH))? - .value; - let config = SerialConfig { - path: path.clone(), - baud_rate: options - .extract_by_key(FIELD_BAUD_RATE) - .ok_or(missed(FIELD_BAUD_RATE))? - .value, - data_bits: options - .extract_by_key(FIELD_DATA_BITS) - .ok_or(missed(FIELD_DATA_BITS))? - .value, - flow_control: options - .extract_by_key(FIELD_FLOW_CONTROL) - .ok_or(missed(FIELD_FLOW_CONTROL))? - .value, - parity: options - .extract_by_key(FIELD_PARITY) - .ok_or(missed(FIELD_PARITY))? - .value, - stop_bits: options - .extract_by_key(FIELD_STOP_BITS) - .ok_or(missed(FIELD_STOP_BITS))? - .value, - send_data_delay: options - .extract_by_key(FIELD_SEND_DATA_DELAY) - .ok_or(missed(FIELD_SEND_DATA_DELAY))? - .value, - exclusive: options - .extract_by_key(FIELD_EXCLUSIVE) - .ok_or(missed(FIELD_EXCLUSIVE))? - .value, - }; - Ok(Some(( - crate::Source::Serial(SerialSource::new(config)?), - Some(path), - ))) - } +pub fn factory( + origin: &SessionAction, + options: &[Field], +) -> Result)>, NativeError> { + let path: String = options + .extract_by_key(FIELD_PATH) + .ok_or(missed(FIELD_PATH))? + .value; + let config = SerialConfig { + path: path.clone(), + baud_rate: options + .extract_by_key(FIELD_BAUD_RATE) + .ok_or(missed(FIELD_BAUD_RATE))? + .value, + data_bits: options + .extract_by_key(FIELD_DATA_BITS) + .ok_or(missed(FIELD_DATA_BITS))? + .value, + flow_control: options + .extract_by_key(FIELD_FLOW_CONTROL) + .ok_or(missed(FIELD_FLOW_CONTROL))? + .value, + parity: options + .extract_by_key(FIELD_PARITY) + .ok_or(missed(FIELD_PARITY))? + .value, + stop_bits: options + .extract_by_key(FIELD_STOP_BITS) + .ok_or(missed(FIELD_STOP_BITS))? + .value, + send_data_delay: options + .extract_by_key(FIELD_SEND_DATA_DELAY) + .ok_or(missed(FIELD_SEND_DATA_DELAY))? + .value, + exclusive: options + .extract_by_key(FIELD_EXCLUSIVE) + .ok_or(missed(FIELD_EXCLUSIVE))? + .value, + }; + Ok(Some(( + Sources::Serial(SerialSource::new(config)?), + Some(path), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { @@ -116,9 +99,6 @@ impl ComponentDescriptor for Descriptor { uuid: SERIAL_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { @@ -253,3 +233,4 @@ impl ComponentDescriptor for Descriptor { Ok(errors) } } +impl SourceDescriptor for Descriptor {} diff --git a/application/apps/indexer/sources/src/serial/serialport.rs b/application/apps/indexer/sources/src/serial/serialport.rs index bd46098f0b..14cc89940f 100644 --- a/application/apps/indexer/sources/src/serial/serialport.rs +++ b/application/apps/indexer/sources/src/serial/serialport.rs @@ -1,6 +1,6 @@ +use crate::*; use bufread::DeqBuffer; use bytes::{BufMut, BytesMut}; -use definitions::*; use futures::{ SinkExt, stream::{SplitSink, SplitStream, StreamExt}, diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index c0883fa8eb..e6055e445a 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -1,9 +1,9 @@ +use crate::*; +use components::{CommonDescriptor, SourceDescriptor}; use std::collections::HashMap; - -use components::{ComponentDescriptor, ComponentFactory}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, - Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, NativeError, SessionAction, StaticFieldDesc, + ValueInput, missed_field_err as missed, }; use crate::prelude::TcpSource; @@ -18,38 +18,21 @@ const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { - return Err(NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), - }); - } - let addr: String = options - .extract_by_key(FIELD_IP_ADDR) - .ok_or(missed(FIELD_IP_ADDR))? - .value; - Ok(Some(( - crate::Source::Tcp(TcpSource::new(&addr, None, None)?), - Some(format!("TCP on {}", addr)), - ))) - } +pub fn factory( + origin: &SessionAction, + options: &[Field], +) -> Result)>, NativeError> { + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + Ok(Some(( + Sources::Tcp(TcpSource::new(&addr, None, None)?), + Some(format!("TCP on {}", addr)), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { @@ -68,9 +51,6 @@ impl ComponentDescriptor for Descriptor { uuid: TCP_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), @@ -102,3 +82,4 @@ impl ComponentDescriptor for Descriptor { Ok(errors) } } +impl SourceDescriptor for Descriptor {} diff --git a/application/apps/indexer/sources/src/socket/tcp/mod.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs index ab307a1557..7824aaa2bd 100644 --- a/application/apps/indexer/sources/src/socket/tcp/mod.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -1,14 +1,13 @@ mod descriptor; +use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; +use crate::*; use bufread::DeqBuffer; -use definitions::*; use reconnect::{ReconnectInfo, ReconnectResult, TcpReconnecter}; use socket2::{SockRef, TcpKeepalive}; use std::{net::SocketAddr, time::Duration}; use tokio::{net::TcpStream, runtime::Handle}; -use super::{BuffCapacityState, MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE, handle_buff_capacity}; - pub mod reconnect; pub use descriptor::*; diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 269e9b1f32..d9cc14a9eb 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -1,12 +1,12 @@ -use components::{ComponentDescriptor, ComponentFactory}; +use super::{MulticastInfo, UdpSource}; +use crate::*; +use components::{CommonDescriptor, SourceDescriptor}; use std::collections::HashMap; use stypes::{ ExtractAs, ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, StaticFieldDesc, Value, ValueInput, missed_field_err as missed, }; -use super::{MulticastInfo, UdpSource}; - const UDP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, ]); @@ -17,75 +17,58 @@ const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; #[derive(Default)] pub struct Descriptor {} -impl ComponentFactory for Descriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { +pub fn factory( + origin: &SessionAction, + options: &[Field], +) -> Result)>, NativeError> { + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + let multicast: &Vec = options + .extract_by_key(FIELD_MULTICAST_ADDR) + .ok_or(missed(FIELD_MULTICAST_ADDR))? + .value; + let mut multicasts = Vec::new(); + for field in multicast { + let pair: &Vec = field.extract_as().ok_or(missed("Multicast Addr"))?; + if pair.len() != 2 { return Err(NativeError { kind: NativeErrorKind::Configuration, severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), + message: Some("Invalid settings of multicast address".to_owned()), }); } - let addr: String = options - .extract_by_key(FIELD_IP_ADDR) - .ok_or(missed(FIELD_IP_ADDR))? - .value; - let multicast: &Vec = options - .extract_by_key(FIELD_MULTICAST_ADDR) - .ok_or(missed(FIELD_MULTICAST_ADDR))? - .value; - let mut multicasts = Vec::new(); - for field in multicast { - let pair: &Vec = field.extract_as().ok_or(missed("Multicast Addr"))?; - if pair.len() != 2 { - return Err(NativeError { + let addr: String = (&pair[0]).extract_as().ok_or(missed("Multicast"))?; + let interface: String = (&pair[1]).extract_as().ok_or(missed("Interface"))?; + multicasts.push(MulticastInfo { + multiaddr: addr + .parse::() + .map_err(|err| NativeError { kind: NativeErrorKind::Configuration, severity: Severity::ERROR, - message: Some("Invalid settings of multicast address".to_owned()), - }); - } - let addr: String = (&pair[0]).extract_as().ok_or(missed("Multicast"))?; - let interface: String = (&pair[1]).extract_as().ok_or(missed("Interface"))?; - multicasts.push(MulticastInfo { - multiaddr: addr - .parse::() - .map_err(|err| NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some(format!("Fail to parse IP: {err}")), - })?, - interface: if interface.is_empty() { - None - } else { - Some(interface) - }, - }) - } - Ok(Some(( - crate::Source::Udp( - UdpSource::new(&addr, multicasts).map_err(|err| NativeError { - kind: NativeErrorKind::Io, - severity: Severity::ERROR, - message: Some(err.to_string()), + message: Some(format!("Fail to parse IP: {err}")), })?, - ), - Some(format!("UDP on {addr}")), - ))) + interface: if interface.is_empty() { + None + } else { + Some(interface) + }, + }) } + Ok(Some(( + Sources::Udp( + UdpSource::new(&addr, multicasts).map_err(|err| NativeError { + kind: NativeErrorKind::Io, + severity: Severity::ERROR, + message: Some(err.to_string()), + })?, + ), + Some(format!("UDP on {addr}")), + ))) } -impl ComponentDescriptor for Descriptor { +impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { match origin { SessionAction::File(..) | SessionAction::Files(..) | SessionAction::ExportRaw(..) => { @@ -104,9 +87,6 @@ impl ComponentDescriptor for Descriptor { uuid: UDP_SOURCE_UUID, } } - fn ty(&self) -> stypes::ComponentType { - stypes::ComponentType::Source - } fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { @@ -205,3 +185,4 @@ impl ComponentDescriptor for Descriptor { Ok(errors) } } +impl SourceDescriptor for Descriptor {} diff --git a/application/apps/indexer/sources/src/socket/udp/mod.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs index d833724384..051768ec34 100644 --- a/application/apps/indexer/sources/src/socket/udp/mod.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -2,8 +2,8 @@ mod descriptor; use super::{MAX_BUFF_SIZE, MAX_DATAGRAM_SIZE}; use crate::socket::{BuffCapacityState, handle_buff_capacity}; +use crate::*; use bufread::DeqBuffer; -use definitions::*; use log::trace; use std::net::{IpAddr, Ipv4Addr}; use thiserror::Error; From 6f5672bc44567cce1f70dd323718ee388a4be4b6 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 18 Jul 2025 09:54:33 +0200 Subject: [PATCH 097/121] Remove enum from Components --- .../apps/indexer/components/src/lib.rs | 345 +----------------- application/apps/indexer/parsers/src/lib.rs | 20 - .../indexer/session/src/components/api.rs | 303 ++++++++++++++- .../indexer/session/src/components/mod.rs | 70 +++- .../session/src/handlers/observe/mod.rs | 4 +- .../apps/indexer/session/src/operations.rs | 7 +- .../apps/indexer/session/src/session.rs | 19 +- application/apps/indexer/sources/src/lib.rs | 23 -- 8 files changed, 375 insertions(+), 416 deletions(-) diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/components/src/lib.rs index e63a27205d..aefbc20857 100644 --- a/application/apps/indexer/components/src/lib.rs +++ b/application/apps/indexer/components/src/lib.rs @@ -1,350 +1,7 @@ mod scheme; mod tys; -use std::{collections::HashMap, fmt}; -use stypes::SessionDescriptor; -use tokio_util::sync::CancellationToken; -use uuid::Uuid; +use std::collections::HashMap; pub use scheme::*; pub use tys::*; - -/// The `Components` struct serves as a central registry and initialization hub for all components -/// used in the system, such as parsers and sources. It introduces a more flexible and modular -/// architecture compared to previous hardcoded solutions (prior to version 4.x.x). -/// -/// # Problem Statement -/// -/// In earlier versions, parsers and sources were explicitly enumerated and directly referenced -/// in the core system. This led to several architectural drawbacks: -/// -/// - Adding or removing a parser or source required changes to the core codebase. -/// - Component-related logic could not be easily isolated into self-contained modules. -/// - The frontend code had to mirror these changes, leading to tight coupling and redundancy. -/// -/// # Motivation -/// -/// Introducing the concept of a `Component` eliminates the need to hardcode system-wide knowledge -/// about available parsers and sources. Instead, it: -/// -/// - Allows independent development and isolation of each parser/source implementation. -/// - Ensures that adding, modifying, or removing a component does not impact the core, test suite, -/// or client code. -/// - Enables extensibility without requiring major refactoring of the core logic. -/// -/// # Implementation -/// -/// - At the session level, an instance of `Components` is created to register all available components. -/// - When initializing a session, the client provides a list of `Uuid`s representing the desired -/// components and their configurations. -/// - `Components` is responsible for initializing these components and handing them off for further use -/// (e.g., by `MessageProducer` or for data export). -/// -/// In addition, `Components` provides the following functionality: -/// -/// - Lists registered components (both parsers and sources). -/// - Provides the configuration schema for any component. -/// - Performs preliminary validation of the provided configuration. -/// -/// # Notes -/// -/// `Components` itself does not implement any teardown or destruction logic, as this is unnecessary: -/// -/// - It only stores `ComponentDescriptor` instances, which are lightweight and do not own the actual -/// component instances. -/// - Component instances are created on demand and immediately passed upwards for further use; they -/// are not retained by `Components`. -/// -pub struct Components { - sources: HashMap, Box)>, - parsers: HashMap, Box)>, -} - -impl Components { - pub fn new() -> Components { - Self { - sources: HashMap::new(), - parsers: HashMap::new(), - } - } - pub fn add_parser( - &mut self, - factory: Factory

, - descriptor: D, - ) -> Result<(), stypes::NativeError> { - let ident = descriptor.ident(); - if self.parsers.contains_key(&ident.uuid) { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), - }); - } - self.parsers - .insert(ident.uuid, (factory, Box::new(descriptor))); - Ok(()) - } - - pub fn add_source( - &mut self, - factory: Factory, - descriptor: D, - ) -> Result<(), stypes::NativeError> { - let ident: stypes::Ident = descriptor.ident(); - if self.sources.contains_key(&ident.uuid) { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), - }); - } - self.sources - .insert(ident.uuid, (factory, Box::new(descriptor))); - Ok(()) - } - - pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { - self.parsers - .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - }) - .collect() - } - - pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { - self.sources - .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - }) - .collect() - } - - /// Returns configuration schemas for the specified component UUIDs. - /// - /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI - /// and optionally define lazy-loaded settings. - /// - /// # Arguments - /// * `origin` – Session action describing the context in which components will be used. - /// * `targets` – A list of component UUIDs to fetch configuration for. - /// - /// # Returns - /// * `Ok(HashMap)` – Mapping from component UUIDs to their configuration schemes. - /// * `Err(NativeError)` – If any of the UUIDs are not found in the registry. - /// - /// # Errors - /// Returns a configuration error with a list of UUIDs that were not found in the registry. - pub fn get_options( - &self, - origin: stypes::SessionAction, - mut targets: Vec, - ) -> Result, stypes::NativeError> { - let descriptors = self - .descriptors() - .into_iter() - .filter_map(|(uuid, desc)| { - if targets.contains(uuid) { - targets.retain(|v| v != uuid); - Some(desc) - } else { - None - } - }) - .collect::>(); - if !targets.is_empty() { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Fail to find: {}", - targets - .iter() - .map(|v| v.to_string()) - .collect::>() - .join(", ") - )), - }); - } - let mut list: HashMap = HashMap::new(); - for desc in descriptors.into_iter() { - let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); - if options.has_lazy() { - let cancel = CancellationToken::new(); - options.set_lazy( - desc.ident(), - desc.lazy_fields_getter(origin.clone(), cancel.clone()), - &cancel, - ); - } - list.insert(desc.ident().uuid, options); - } - Ok(list) - } - - /// Returns the client-side render type for the specified component UUID. - /// - /// While typically associated with parsers (since they define the data format), - /// the system allows any component to provide rendering output hints. - /// - /// # Arguments - /// * `uuid` – The UUID of the target component. - /// - /// # Returns - /// * `Ok(Some(OutputRender))` – If the parser provides render information. - /// * `Ok(None)` – If no render is defined. - /// * `Err(NativeError)` – If the UUID is not found or the component is not a parser. - /// - /// # Errors - /// Returns a configuration error if the component is missing or not a parser. - pub fn get_output_render( - &self, - uuid: &Uuid, - ) -> Result, stypes::NativeError> { - let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find component {uuid}")), - })?; - Ok(descriptor.get_render()) - } - - /// Returns the Ident of component (parser or source). - /// - /// # Arguments - /// * `uuid` – The UUID of the target component. - /// - /// # Returns - /// * `Some(Ident)` – If the parser or source has been found. - /// * `None` – If no component is defined. - pub fn get_ident(&self, uuid: &Uuid) -> Option { - self.descriptors().into_iter().find_map(|(inner, desc)| { - if uuid == inner { - Some(desc.ident()) - } else { - None - } - }) - } - - /// Validates the configuration fields for a specified component within a session context. - /// - /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete - /// configurations before actual component instantiation. - /// - /// # Arguments - /// * `origin` – The session action context. - /// * `target` – The UUID of the component to validate. - /// * `fields` – A list of fields provided by the client. - /// - /// # Returns - /// * `Ok(HashMap)` – Empty if validation passed; otherwise, maps field UUIDs to error messages. - /// * `Err(NativeError)` – If the component UUID is not registered. - /// - /// # Errors - /// Returns a configuration error if the component is not found. - pub fn validate( - &self, - origin: &stypes::SessionAction, - target: &Uuid, - fields: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let (_, descriptor) = self - .descriptors() - .into_iter() - .find(|(uuid, _)| *uuid == target) - .ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find component {target}")), - })?; - descriptor.validate(origin, fields) - } - - /// Attempts to create instances of a parser and source component based on the session setup. - /// - /// This method creates fully initialized, ready-to-use instances of parser and source - /// based on the provided configuration. It also returns a `SessionDescriptor` - /// that contains display identifiers for tracking the session state. - /// - /// # Arguments - /// * `options` – Session setup data including parser/source UUIDs and user-defined field values. - /// - /// # Returns - /// * `Ok((SessionDescriptor, S, P))` – A tuple with session metadata, the source instance, and the parser instance. - /// * `Err(NativeError)` – If any of the components cannot be found or fail to initialize. - /// - /// # Errors - /// Returns configuration errors if components are missing or initialization fails. - pub fn setup( - &self, - options: &stypes::SessionSetup, - ) -> Result<(SessionDescriptor, S, P), stypes::NativeError> { - let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) - else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find parser {}", options.parser.uuid)), - }); - }; - let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) - else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find source {}", options.source.uuid)), - }); - }; - let mut descriptor = SessionDescriptor::new( - source_descriptor.bound_ident(&options.origin, &options.source.fields), - parser_descriptor.bound_ident(&options.origin, &options.parser.fields), - ); - let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to init parser {}", options.parser.uuid)), - }); - }; - descriptor.set_parser_desc(desc); - let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to init source {}", options.source.uuid)), - }); - }; - descriptor.set_source_desc(desc); - Ok((descriptor, source, parser)) - } - - fn descriptors(&self) -> Vec<(&Uuid, &dyn CommonDescriptor)> { - [ - self.sources - .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) - .collect::>(), - self.parsers - .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) - .collect::>(), - ] - .concat() - } -} - -impl fmt::Debug for Components { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Components") - } -} diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 68be59b914..404b369267 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -1,6 +1,5 @@ // #![deny(unused_crate_dependencies)] -use components::Components; pub mod api; pub mod dlt; pub mod prelude; @@ -40,22 +39,3 @@ impl Parser for Parsers { // TODO: this registration function will fail if some of parser would not be registred. That's wrong. // If some of parsers are failed, another parsers should still be registred as well -pub fn registration(components: &mut Components) -> Result<(), stypes::NativeError> { - components.add_parser( - dlt::descriptor::factory, - dlt::descriptor::Descriptor::default(), - )?; - components.add_parser( - dlt::descriptor::factory, - dlt::raw::descriptor::Descriptor::default(), - )?; - components.add_parser( - someip::descriptor::factory, - someip::descriptor::Descriptor::default(), - )?; - components.add_parser( - text::descriptor::factory, - text::descriptor::Descriptor::default(), - )?; - Ok(()) -} diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/components/api.rs index 74b24c8889..742287e9ce 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/components/api.rs @@ -1,8 +1,9 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt}; use components::{LazyLoadingResult, LazyLoadingTaskMeta}; -use stypes::{Ident, NativeError, SessionAction}; +use stypes::{Ident, NativeError, SessionAction, SessionDescriptor}; use tokio::sync::oneshot; +use tokio_util::sync::CancellationToken; use uuid::Uuid; /// Represents the various API commands for managing component sessions and operations. @@ -174,3 +175,301 @@ pub enum Api { /// Removes the plugin with the given directory path. RemovePlugin(String, oneshot::Sender>), } + +use components::*; +use parsers::{Parsers, api::*}; +use sources::{Sources, api::*}; + +pub struct Components { + sources: HashMap)>, + parsers: HashMap)>, +} + +impl Components { + pub fn new() -> Components { + Self { + sources: HashMap::new(), + parsers: HashMap::new(), + } + } + pub fn add_parser( + &mut self, + factory: ParserFactory, + descriptor: D, + ) -> Result<(), stypes::NativeError> { + let ident = descriptor.ident(); + if self.parsers.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.parsers + .insert(ident.uuid, (factory, Box::new(descriptor))); + Ok(()) + } + + pub fn add_source( + &mut self, + factory: SourceFactory, + descriptor: D, + ) -> Result<(), stypes::NativeError> { + let ident: stypes::Ident = descriptor.ident(); + if self.sources.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.sources + .insert(ident.uuid, (factory, Box::new(descriptor))); + Ok(()) + } + + pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { + self.parsers + .iter() + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + }) + .collect() + } + + pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { + self.sources + .iter() + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + }) + .collect() + } + + /// Returns configuration schemas for the specified component UUIDs. + /// + /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI + /// and optionally define lazy-loaded settings. + /// + /// # Arguments + /// * `origin` – Session action describing the context in which components will be used. + /// * `targets` – A list of component UUIDs to fetch configuration for. + /// + /// # Returns + /// * `Ok(HashMap)` – Mapping from component UUIDs to their configuration schemes. + /// * `Err(NativeError)` – If any of the UUIDs are not found in the registry. + /// + /// # Errors + /// Returns a configuration error with a list of UUIDs that were not found in the registry. + pub fn get_options( + &self, + origin: stypes::SessionAction, + mut targets: Vec, + ) -> Result, stypes::NativeError> { + let descriptors = self + .descriptors() + .into_iter() + .filter_map(|(uuid, desc)| { + if targets.contains(uuid) { + targets.retain(|v| v != uuid); + Some(desc) + } else { + None + } + }) + .collect::>(); + if !targets.is_empty() { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Fail to find: {}", + targets + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + )), + }); + } + let mut list: HashMap = HashMap::new(); + for desc in descriptors.into_iter() { + let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); + if options.has_lazy() { + let cancel = CancellationToken::new(); + options.set_lazy( + desc.ident(), + desc.lazy_fields_getter(origin.clone(), cancel.clone()), + &cancel, + ); + } + list.insert(desc.ident().uuid, options); + } + Ok(list) + } + + /// Returns the client-side render type for the specified component UUID. + /// + /// While typically associated with parsers (since they define the data format), + /// the system allows any component to provide rendering output hints. + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Ok(Some(OutputRender))` – If the parser provides render information. + /// * `Ok(None)` – If no render is defined. + /// * `Err(NativeError)` – If the UUID is not found or the component is not a parser. + /// + /// # Errors + /// Returns a configuration error if the component is missing or not a parser. + pub fn get_output_render( + &self, + uuid: &Uuid, + ) -> Result, stypes::NativeError> { + let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {uuid}")), + })?; + Ok(descriptor.get_render()) + } + + /// Returns the Ident of component (parser or source). + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Some(Ident)` – If the parser or source has been found. + /// * `None` – If no component is defined. + pub fn get_ident(&self, uuid: &Uuid) -> Option { + self.descriptors().into_iter().find_map(|(inner, desc)| { + if uuid == inner { + Some(desc.ident()) + } else { + None + } + }) + } + + /// Validates the configuration fields for a specified component within a session context. + /// + /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete + /// configurations before actual component instantiation. + /// + /// # Arguments + /// * `origin` – The session action context. + /// * `target` – The UUID of the component to validate. + /// * `fields` – A list of fields provided by the client. + /// + /// # Returns + /// * `Ok(HashMap)` – Empty if validation passed; otherwise, maps field UUIDs to error messages. + /// * `Err(NativeError)` – If the component UUID is not registered. + /// + /// # Errors + /// Returns a configuration error if the component is not found. + pub fn validate( + &self, + origin: &stypes::SessionAction, + target: &Uuid, + fields: &[stypes::Field], + ) -> Result, stypes::NativeError> { + let (_, descriptor) = self + .descriptors() + .into_iter() + .find(|(uuid, _)| *uuid == target) + .ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {target}")), + })?; + descriptor.validate(origin, fields) + } + + /// Attempts to create instances of a parser and source component based on the session setup. + /// + /// This method creates fully initialized, ready-to-use instances of parser and source + /// based on the provided configuration. It also returns a `SessionDescriptor` + /// that contains display identifiers for tracking the session state. + /// + /// # Arguments + /// * `options` – Session setup data including parser/source UUIDs and user-defined field values. + /// + /// # Returns + /// * `Ok((SessionDescriptor, S, P))` – A tuple with session metadata, the source instance, and the parser instance. + /// * `Err(NativeError)` – If any of the components cannot be found or fail to initialize. + /// + /// # Errors + /// Returns configuration errors if components are missing or initialization fails. + pub fn setup( + &self, + options: &stypes::SessionSetup, + ) -> Result<(SessionDescriptor, Sources, Parsers), stypes::NativeError> { + let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) + else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find parser {}", options.parser.uuid)), + }); + }; + let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) + else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find source {}", options.source.uuid)), + }); + }; + let mut descriptor = SessionDescriptor::new( + source_descriptor.bound_ident(&options.origin, &options.source.fields), + parser_descriptor.bound_ident(&options.origin, &options.parser.fields), + ); + let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init parser {}", options.parser.uuid)), + }); + }; + descriptor.set_parser_desc(desc); + let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init source {}", options.source.uuid)), + }); + }; + descriptor.set_source_desc(desc); + Ok((descriptor, source, parser)) + } + + fn descriptors(&self) -> Vec<(&Uuid, &dyn CommonDescriptor)> { + [ + self.sources + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + self.parsers + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + ] + .concat() + } +} + +impl fmt::Debug for Components { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Components") + } +} diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/components/mod.rs index 6d11e6116a..a695aaae50 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/components/mod.rs @@ -1,5 +1,8 @@ +mod api; +mod plugins; + use api::*; -use components::{Components, LazyLoadingResult, LazyLoadingTaskMeta}; +use components::{LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; use std::{collections::HashMap, sync::Arc}; use tokio::{ @@ -12,9 +15,6 @@ use tokio::{ }; use uuid::Uuid; -mod api; -mod plugins; - /// A controller responsible for managing all available components in the system (such as parsers, sources, etc.). /// /// This structure acts as a central registry that stores and manages the state of all components throughout @@ -60,15 +60,13 @@ impl ComponentsSession { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Components = Components::new(); // Registre parsers - parsers::registration(&mut components)?; + parsers_registration(&mut components)?; // Registre sources - sources::registration(&mut components)?; - plugins_manager.register_plugins(&mut components)?; - let plugins_manager = Arc::new(RwLock::new(plugins_manager)); + sources_registration(&mut components)?; let tx_api_inner = tx_api.clone(); let session = Self { tx_api }; task::spawn(async move { @@ -643,3 +641,57 @@ fn response>(res: Result, msg: S) -> Result Result<(), stypes::NativeError> { + components.add_parser( + parsers::dlt::descriptor::factory, + parsers::dlt::descriptor::Descriptor::default(), + )?; + components.add_parser( + parsers::dlt::descriptor::factory, + parsers::dlt::raw::descriptor::Descriptor::default(), + )?; + components.add_parser( + parsers::someip::descriptor::factory, + parsers::someip::descriptor::Descriptor::default(), + )?; + components.add_parser( + parsers::text::descriptor::factory, + parsers::text::descriptor::Descriptor::default(), + )?; + Ok(()) +} + +pub fn sources_registration(components: &mut Components) -> Result<(), stypes::NativeError> { + components.add_source( + sources::binary::raw::factory, + sources::binary::raw::Descriptor::default(), + )?; + components.add_source( + sources::binary::pcap::legacy::factory, + sources::binary::pcap::legacy::Descriptor::default(), + )?; + components.add_source( + sources::binary::pcap::ng::factory, + sources::binary::pcap::ng::Descriptor::default(), + )?; + components.add_source( + sources::socket::tcp::factory, + sources::socket::tcp::Descriptor::default(), + )?; + components.add_source( + sources::socket::udp::factory, + sources::socket::udp::Descriptor::default(), + )?; + components.add_source( + sources::serial::descriptor::factory, + sources::serial::descriptor::Descriptor::default(), + )?; + components.add_source( + sources::command::factory, + sources::command::Descriptor::default(), + )?; + Ok(()) +} diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index c619fc58ba..a7ac6a4087 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -2,10 +2,10 @@ mod export_raw; mod session; use crate::{ + components::Components, operations::{OperationAPI, OperationResult}, state::SessionStateAPI, }; -use components::Components; use processor::producer::{MessageProducer, sde::*}; use std::sync::Arc; use stypes::{SessionAction, SessionSetup}; @@ -14,7 +14,7 @@ pub async fn observing( operation_api: OperationAPI, state: SessionStateAPI, options: SessionSetup, - components: Arc>, + components: Arc, rx_sde: Option, ) -> OperationResult<()> { match &options.origin { diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index c68351e37e..068850bc67 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -1,5 +1,5 @@ +use crate::components::Components; use crate::{handlers, state::SessionStateAPI, tracker::OperationTrackerAPI}; -use components::Components; use log::{debug, error, warn}; use merging::merger::FileMergeOptions; use processor::producer::sde::{SdeReceiver, SdeSender}; @@ -74,10 +74,7 @@ impl Operation { #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum OperationKind { - Observe( - stypes::SessionSetup, - Arc>, - ), + Observe(stypes::SessionSetup, Arc), Search { filters: Vec, }, diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index d84e1d20e3..484ed9df8f 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -1,12 +1,9 @@ use crate::{ - operations, - operations::Operation, - state, - state::{IndexesMode, SessionStateAPI}, - tracker, - tracker::OperationTrackerAPI, + components::{Components, parsers_registration, sources_registration}, + operations::{self, Operation}, + state::{self, IndexesMode, SessionStateAPI}, + tracker::{self, OperationTrackerAPI}, }; -use components::Components; use futures::Future; use log::{debug, error, warn}; use processor::{grabber::LineRange, search::filter::SearchFilter}; @@ -32,7 +29,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, - components: Arc>, + components: Arc, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -56,12 +53,12 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Components = Components::new(); // Registre parsers - parsers::registration(&mut components) + parsers_registration(&mut components) .map_err(|err| stypes::ComputationError::NativeError(err))?; // Registre sources - sources::registration(&mut components) + sources_registration(&mut components) .map_err(|err| stypes::ComputationError::NativeError(err))?; // Create a session let session = Self { diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index 3f76309fd7..f68b4de50a 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,7 +1,6 @@ pub mod api; pub use api::*; -use components::Components; // Rust can't currently distinguish between dev and none-dev dependencies at the moment. There is // an open issue for this case: "https://github.com/rust-lang/rust/issues/129637" @@ -106,25 +105,3 @@ impl ByteSource for Sources { } } } - -// TODO: this registration function will fail if some of source would not be registred. That's wrong. -// If some of sources are failed, another sources should still be registred as well -pub fn registration

(components: &mut Components) -> Result<(), stypes::NativeError> { - components.add_source(binary::raw::factory, binary::raw::Descriptor::default())?; - components.add_source( - binary::pcap::legacy::factory, - binary::pcap::legacy::Descriptor::default(), - )?; - components.add_source( - binary::pcap::ng::factory, - binary::pcap::ng::Descriptor::default(), - )?; - components.add_source(socket::tcp::factory, socket::tcp::Descriptor::default())?; - components.add_source(socket::udp::factory, socket::udp::Descriptor::default())?; - components.add_source( - serial::descriptor::factory, - serial::descriptor::Descriptor::default(), - )?; - components.add_source(command::factory, command::Descriptor::default())?; - Ok(()) -} From 82ec6596c744d121097d0bbf0442f0920da067c8 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 18 Jul 2025 19:48:46 +0200 Subject: [PATCH 098/121] Rename Components to Register --- .../apps/indexer/session/src/handlers/observe/mod.rs | 4 ++-- application/apps/indexer/session/src/lib.rs | 2 +- application/apps/indexer/session/src/operations.rs | 4 ++-- .../session/src/{components => register}/api.rs | 10 +++++----- .../session/src/{components => register}/mod.rs | 8 ++++---- .../session/src/{components => register}/plugins.rs | 0 application/apps/indexer/session/src/session.rs | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) rename application/apps/indexer/session/src/{components => register}/api.rs (99%) rename application/apps/indexer/session/src/{components => register}/mod.rs (98%) rename application/apps/indexer/session/src/{components => register}/plugins.rs (100%) diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index a7ac6a4087..ebeadfa26f 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -2,8 +2,8 @@ mod export_raw; mod session; use crate::{ - components::Components, operations::{OperationAPI, OperationResult}, + register::Register, state::SessionStateAPI, }; use processor::producer::{MessageProducer, sde::*}; @@ -14,7 +14,7 @@ pub async fn observing( operation_api: OperationAPI, state: SessionStateAPI, options: SessionSetup, - components: Arc, + components: Arc, rx_sde: Option, ) -> OperationResult<()> { match &options.origin { diff --git a/application/apps/indexer/session/src/lib.rs b/application/apps/indexer/session/src/lib.rs index 4080942be5..f2116016c2 100644 --- a/application/apps/indexer/session/src/lib.rs +++ b/application/apps/indexer/session/src/lib.rs @@ -1,8 +1,8 @@ -pub mod components; mod handlers; pub mod operations; pub mod paths; pub mod progress; +pub mod register; pub mod session; pub mod state; pub mod tail; diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index 068850bc67..9429ce1050 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -1,4 +1,4 @@ -use crate::components::Components; +use crate::register::Register; use crate::{handlers, state::SessionStateAPI, tracker::OperationTrackerAPI}; use log::{debug, error, warn}; use merging::merger::FileMergeOptions; @@ -74,7 +74,7 @@ impl Operation { #[derive(Debug)] #[allow(clippy::large_enum_variant)] pub enum OperationKind { - Observe(stypes::SessionSetup, Arc), + Observe(stypes::SessionSetup, Arc), Search { filters: Vec, }, diff --git a/application/apps/indexer/session/src/components/api.rs b/application/apps/indexer/session/src/register/api.rs similarity index 99% rename from application/apps/indexer/session/src/components/api.rs rename to application/apps/indexer/session/src/register/api.rs index 742287e9ce..db2dd6b2ec 100644 --- a/application/apps/indexer/session/src/components/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -180,13 +180,13 @@ use components::*; use parsers::{Parsers, api::*}; use sources::{Sources, api::*}; -pub struct Components { +pub struct Register { sources: HashMap)>, parsers: HashMap)>, } -impl Components { - pub fn new() -> Components { +impl Register { + pub fn new() -> Register { Self { sources: HashMap::new(), parsers: HashMap::new(), @@ -468,8 +468,8 @@ impl Components { } } -impl fmt::Debug for Components { +impl fmt::Debug for Register { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Components") + write!(f, "Register") } } diff --git a/application/apps/indexer/session/src/components/mod.rs b/application/apps/indexer/session/src/register/mod.rs similarity index 98% rename from application/apps/indexer/session/src/components/mod.rs rename to application/apps/indexer/session/src/register/mod.rs index a695aaae50..1d94e7d36e 100644 --- a/application/apps/indexer/session/src/components/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -60,7 +60,7 @@ impl ComponentsSession { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Register = Register::new(); // Registre parsers parsers_registration(&mut components)?; // Registre sources @@ -591,7 +591,7 @@ impl ComponentsSession { /// * `res` - A `Result` that may contain an error. If `Err`, it will be logged. fn log_if_err(res: Result<(), E>) { if res.is_err() { - error!("[Components] Fail to send response to Api"); + error!("[Register] Fail to send response to Api"); } } @@ -644,7 +644,7 @@ fn response>(res: Result, msg: S) -> Result Result<(), stypes::NativeError> { +pub fn parsers_registration(components: &mut Register) -> Result<(), stypes::NativeError> { components.add_parser( parsers::dlt::descriptor::factory, parsers::dlt::descriptor::Descriptor::default(), @@ -664,7 +664,7 @@ pub fn parsers_registration(components: &mut Components) -> Result<(), stypes::N Ok(()) } -pub fn sources_registration(components: &mut Components) -> Result<(), stypes::NativeError> { +pub fn sources_registration(components: &mut Register) -> Result<(), stypes::NativeError> { components.add_source( sources::binary::raw::factory, sources::binary::raw::Descriptor::default(), diff --git a/application/apps/indexer/session/src/components/plugins.rs b/application/apps/indexer/session/src/register/plugins.rs similarity index 100% rename from application/apps/indexer/session/src/components/plugins.rs rename to application/apps/indexer/session/src/register/plugins.rs diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index 484ed9df8f..da918d2ddd 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -1,6 +1,6 @@ use crate::{ - components::{Components, parsers_registration, sources_registration}, operations::{self, Operation}, + register::{Register, parsers_registration, sources_registration}, state::{self, IndexesMode, SessionStateAPI}, tracker::{self, OperationTrackerAPI}, }; @@ -29,7 +29,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, - components: Arc, + components: Arc, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -53,7 +53,7 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Components = Components::new(); + let mut components: Register = Register::new(); // Registre parsers parsers_registration(&mut components) .map_err(|err| stypes::ComputationError::NativeError(err))?; From 593ef0fb7814a6e930760e90852e200c59f4c23c Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 21 Jul 2025 13:41:17 +0200 Subject: [PATCH 099/121] Restore parsers/sources unit tests --- .../apps/indexer/parsers/src/api/record.rs | 36 ++++ .../apps/indexer/parsers/src/someip/mod.rs | 174 +++++------------- .../apps/indexer/parsers/src/text/mod.rs | 8 +- .../indexer/sources/src/binary/pcap/ng/mod.rs | 2 +- .../apps/indexer/sources/src/command/mod.rs | 6 +- application/apps/indexer/sources/src/lib.rs | 3 +- .../indexer/sources/src/socket/tcp/mod.rs | 16 +- .../indexer/sources/src/socket/udp/mod.rs | 6 +- .../indexer/sources/src/tests/mock_read.rs | 41 +++++ .../apps/indexer/sources/src/tests/mod.rs | 34 ++++ 10 files changed, 173 insertions(+), 153 deletions(-) create mode 100644 application/apps/indexer/sources/src/tests/mock_read.rs create mode 100644 application/apps/indexer/sources/src/tests/mod.rs diff --git a/application/apps/indexer/parsers/src/api/record.rs b/application/apps/indexer/parsers/src/api/record.rs index 1fd4193c0b..9b624686a7 100644 --- a/application/apps/indexer/parsers/src/api/record.rs +++ b/application/apps/indexer/parsers/src/api/record.rs @@ -1,6 +1,9 @@ use serde::Serialize; use std::borrow::Cow; +#[cfg(test)] +use std::fmt; + pub const COLUMN_SENTINAL: char = '\u{0004}'; /// Some log records (e.g., in binary formats like DLT) may include attached files. @@ -101,6 +104,39 @@ pub enum LogRecordOutput<'a> { Multiple(Vec>), } +#[cfg(test)] +impl<'a> fmt::Display for LogRecordOutput<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogRecordOutput::Raw(inner) => { + write!( + f, + "{}", + &inner + .iter() + .map(|b| format!("{:02X}", b)) + .collect::() + ) + } + LogRecordOutput::Message(msg) => { + write!(f, "{msg}") + } + LogRecordOutput::Columns(inner) => { + write!(f, "{}", inner.join(&COLUMN_SENTINAL.to_string())) + } + LogRecordOutput::Multiple(inner) => { + for rec in inner { + write!(f, "{}", rec)?; + } + Ok(()) + } + LogRecordOutput::Attachment(inner) => { + write!(f, "{:?}", inner) + } + } + } +} + /// Defines an interface for buffering log records before they are persisted. /// /// An instance of a type implementing `LogRecordsBuffer` is passed to a `MessageProducer`. diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 3d49d5ea32..09ed2def5f 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -644,12 +644,7 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!("MCC", &format!("{}", item)); - assert_eq!("MCC", &format!("{:?}", item)); - } else { - panic!("unexpected parse yield"); - } + assert_eq!("MCC", &format!("{}", message.unwrap())); } #[test] @@ -668,12 +663,7 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!("MCS", &format!("{}", item)); - assert_eq!("MCS", &format!("{:?}", item)); - } else { - panic!("unexpected parse yield"); - } + assert_eq!("MCS", &format!("{}", message.unwrap())); } #[test] @@ -692,18 +682,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32772\u{4}8\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}[]", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32772 LENG:8 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 []", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32772\u{4}8\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}[]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -725,18 +707,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32772\u{4}8\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::emptyEvent ", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32772 LENG:8 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 TestService::emptyEvent ", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32772\u{4}8\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::emptyEvent ", + &format!("{}", message.unwrap()) + ); } #[test] @@ -756,18 +730,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}[01, 02]", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32773 LENG:10 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 [01, 02]", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}[01, 02]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -790,18 +756,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32773 LENG:10 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 TestService::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", + &format!("{}", message.unwrap()) + ); } #[test] @@ -824,18 +782,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}260\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}UnknownService [01, 02]", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:260 METH:32773 LENG:10 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 UnknownService [01, 02]", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}260\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}UnknownService [01, 02]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -858,18 +808,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}3\u{4}2\u{4}0\u{4}TestService<1?>::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32773 LENG:10 CLID:1 SEID:2 IVER:3 MSTP:2 RETC:0 TestService<1?>::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32773\u{4}10\u{4}1\u{4}2\u{4}3\u{4}2\u{4}0\u{4}TestService<1?>::testEvent {\u{6}\tvalue1 (UINT8) : 1,\u{6}\tvalue2 (UINT8) : 2,\u{6}}", + &format!("{}", message.unwrap()) + ); } #[test] @@ -892,18 +834,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32774\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::UnknownMethod [01, 02]", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32774 LENG:10 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 TestService::UnknownMethod [01, 02]", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32774\u{4}10\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::UnknownMethod [01, 02]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -926,18 +860,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "RPC\u{4}259\u{4}32773\u{4}9\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::testEvent 'SOME/IP Error: Parser exhausted at offset 1 for Object size 1' [01]", - &format!("{}", item) - ); - assert_eq!( - "RPC SERV:259 METH:32773 LENG:9 CLID:1 SEID:2 IVER:1 MSTP:2 RETC:0 TestService::testEvent 'SOME/IP Error: Parser exhausted at offset 1 for Object size 1' [01]", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "RPC\u{4}259\u{4}32773\u{4}9\u{4}1\u{4}2\u{4}1\u{4}2\u{4}0\u{4}TestService::testEvent 'SOME/IP Error: Parser exhausted at offset 1 for Object size 1' [01]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -959,18 +885,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "SD\u{4}65535\u{4}33024\u{4}20\u{4}0\u{4}0\u{4}1\u{4}2\u{4}0\u{4}Flags [C0]", - &format!("{}", item) - ); - assert_eq!( - "SD SERV:65535 METH:33024 LENG:20 CLID:0 SEID:0 IVER:1 MSTP:2 RETC:0 Flags [C0]", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "SD\u{4}65535\u{4}33024\u{4}20\u{4}0\u{4}0\u{4}1\u{4}2\u{4}0\u{4}Flags [C0]", + &format!("{}", message.unwrap()) + ); } #[test] @@ -1009,18 +927,10 @@ mod test { let (consumed, message) = items.pop().unwrap(); assert_eq!(consumed, input.len()); - if let ParseYield::Message(item) = message.unwrap() { - assert_eq!( - "SD\u{4}65535\u{4}33024\u{4}64\u{4}0\u{4}0\u{4}1\u{4}2\u{4}0\u{4}Flags [C0], Subscribe 259-456 v2 Inst 1 Ttl 3, Subscribe-Ack 259-456 v2 Inst 1 Ttl 3 UDP 127.0.0.1:30000", - &format!("{}", item) - ); - assert_eq!( - "SD SERV:65535 METH:33024 LENG:64 CLID:0 SEID:0 IVER:1 MSTP:2 RETC:0 Flags [C0], Subscribe 259-456 v2 Inst 1 Ttl 3, Subscribe-Ack 259-456 v2 Inst 1 Ttl 3 UDP 127.0.0.1:30000", - &format!("{:?}", item) - ); - } else { - panic!("unexpected parse yield"); - } + assert_eq!( + "SD\u{4}65535\u{4}33024\u{4}64\u{4}0\u{4}0\u{4}1\u{4}2\u{4}0\u{4}Flags [C0], Subscribe 259-456 v2 Inst 1 Ttl 3, Subscribe-Ack 259-456 v2 Inst 1 Ttl 3 UDP 127.0.0.1:30000", + &format!("{}", message.unwrap()) + ); } #[test] diff --git a/application/apps/indexer/parsers/src/text/mod.rs b/application/apps/indexer/parsers/src/text/mod.rs index 7a9aadcc4c..ada730bef4 100644 --- a/application/apps/indexer/parsers/src/text/mod.rs +++ b/application/apps/indexer/parsers/src/text/mod.rs @@ -59,14 +59,14 @@ mod tests { let content = b"hello\nworld\n"; let (consumed_1, first_msg) = parser.parse(content, None).unwrap().next().unwrap(); match first_msg { - Some(ParseYield::Message(StringMessage { content })) if content.eq("hello") => {} + Some(LogRecordOutput::Message(content)) if content.eq("hello") => {} _ => panic!("First message did not match"), } let rest_1 = &content[consumed_1..]; println!("rest_1 = {:?}", String::from_utf8_lossy(rest_1)); let (consumed_2, second_msg) = parser.parse(rest_1, None).unwrap().next().unwrap(); match second_msg { - Some(ParseYield::Message(StringMessage { content })) if content.eq("world") => {} + Some(LogRecordOutput::Message(content)) if content.eq("world") => {} _ => panic!("Second message did not match"), } let rest_2 = &rest_1[consumed_2..]; @@ -86,12 +86,12 @@ mod tests { let (_consumed_1, first_msg) = items_iter.next().unwrap(); match first_msg { - Some(ParseYield::Message(StringMessage { content })) if content.eq("hello") => {} + Some(LogRecordOutput::Message(content)) if content.eq("hello") => {} _ => panic!("First message did not match"), } let (_consumed_2, second_msg) = items_iter.next().unwrap(); match second_msg { - Some(ParseYield::Message(StringMessage { content })) if content.eq("world") => {} + Some(LogRecordOutput::Message(content)) if content.eq("world") => {} _ => panic!("Second message did not match"), } assert!(items_iter.next().is_none()); diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs index d3ee00aee2..417b9521a8 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/mod.rs @@ -215,7 +215,7 @@ mod tests { use crate::{ ByteSource, binary::pcap::ng::PcapngByteSource, - tests::{general_source_reload_test, mock_read::MockRepeatRead}, + tests::{MockRepeatRead, general_source_reload_test}, }; use env_logger; diff --git a/application/apps/indexer/sources/src/command/mod.rs b/application/apps/indexer/sources/src/command/mod.rs index 56f50a4046..fa717340dc 100644 --- a/application/apps/indexer/sources/src/command/mod.rs +++ b/application/apps/indexer/sources/src/command/mod.rs @@ -244,7 +244,7 @@ mod tests { command = "ls -lsa"; } let envs = HashMap::new(); - match ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs).await { + match ProcessSource::new(command, env::current_dir().unwrap(), envs) { Ok(mut process_source) => { while process_source .load(None) @@ -293,9 +293,7 @@ mod tests { } let envs = HashMap::new(); let mut process_source = - ProcessSource::new(command.to_string(), env::current_dir().unwrap(), envs) - .await - .unwrap(); + ProcessSource::new(command, env::current_dir().unwrap(), envs).unwrap(); general_source_reload_test(&mut process_source).await; } diff --git a/application/apps/indexer/sources/src/lib.rs b/application/apps/indexer/sources/src/lib.rs index f68b4de50a..72418f6d9e 100644 --- a/application/apps/indexer/sources/src/lib.rs +++ b/application/apps/indexer/sources/src/lib.rs @@ -1,5 +1,6 @@ pub mod api; - +#[cfg(test)] +pub mod tests; pub use api::*; // Rust can't currently distinguish between dev and none-dev dependencies at the moment. There is diff --git a/application/apps/indexer/sources/src/socket/tcp/mod.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs index 7824aaa2bd..f5fb6303f4 100644 --- a/application/apps/indexer/sources/src/socket/tcp/mod.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -211,7 +211,7 @@ mod tests { let send_handle = tokio::spawn(async move { accept_and_send(&listener, 100).await; }); - let mut tcp_source = TcpSource::new(SERVER, None, None).await?; + let mut tcp_source = TcpSource::new(SERVER, None, None)?; let receive_handle = tokio::spawn(async move { for msg in MESSAGES { tcp_source.load(None).await.expect("reload failed"); @@ -240,7 +240,7 @@ mod tests { tokio::spawn(async move { accept_and_send(&listener, 100).await; }); - let mut tcp_source = TcpSource::new(SERVER, None, None).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, None).unwrap(); general_source_reload_test(&mut tcp_source).await; } @@ -270,7 +270,7 @@ mod tests { } }); - let mut tcp_source = TcpSource::new(SERVER, None, None).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, None).unwrap(); while let Ok(Some(info)) = tcp_source.load(None).await { if info.available_bytes == 0 { @@ -298,7 +298,7 @@ mod tests { // Enable reconnect without configuring state channels. let rec_info = ReconnectInfo::new(1000, Duration::from_millis(20), None); - let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).unwrap(); let receive_handle = tokio::spawn(async move { // Byte source must receive same data twice without errors with active reconnect for _ in 0..2 { @@ -339,7 +339,7 @@ mod tests { let rec_info = ReconnectInfo::new(1000, Duration::from_millis(50), Some(state_tx)); - let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).unwrap(); // Tests reconnecting state messages. let reconnect_handler = tokio::spawn(async move { @@ -405,7 +405,7 @@ mod tests { const MAX_ATTEMPTS: usize = 7; let rec_info = ReconnectInfo::new(MAX_ATTEMPTS, Duration::from_millis(10), Some(state_tx)); - let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).unwrap(); // Tests reconnecting state messages. // Failed state message with MAX_ATTEMPTS is expected. @@ -469,7 +469,7 @@ mod tests { let (cancel_tx, mut cancel_rx) = tokio::sync::mpsc::channel(32); - let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).unwrap(); let cancel_handle = tokio::spawn(async move { // Keep sending notifications causing load method to be dropped while both @@ -534,7 +534,7 @@ mod tests { // Enable reconnect without configuring state channels. let rec_info = ReconnectInfo::new(1000, Duration::from_millis(30), None); - let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).await.unwrap(); + let mut tcp_source = TcpSource::new(SERVER, None, Some(rec_info)).unwrap(); let receive_handle = tokio::spawn(async move { // TCP source must receive three messages, reconnect then receive three diff --git a/application/apps/indexer/sources/src/socket/udp/mod.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs index 051768ec34..23f7e4200a 100644 --- a/application/apps/indexer/sources/src/socket/udp/mod.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -162,7 +162,7 @@ mod tests { .expect("could not send on socket"); } }); - let mut udp_source = UdpSource::new(RECEIVER, vec![]).await?; + let mut udp_source = UdpSource::new(RECEIVER, vec![])?; let receive_handle = tokio::spawn(async move { for msg in MESSAGES { udp_source.load(None).await.unwrap(); @@ -194,7 +194,7 @@ mod tests { .expect("could not send on socket"); } }); - let mut udp_source = UdpSource::new(RECEIVER, vec![]).await.unwrap(); + let mut udp_source = UdpSource::new(RECEIVER, vec![]).unwrap(); general_source_reload_test(&mut udp_source).await; } @@ -234,7 +234,7 @@ mod tests { } }); - let mut udp_source = UdpSource::new(RECEIVER, vec![]).await.unwrap(); + let mut udp_source = UdpSource::new(RECEIVER, vec![]).unwrap(); while let Ok(Some(info)) = udp_source.load(None).await { if info.newly_loaded_bytes == 0 { diff --git a/application/apps/indexer/sources/src/tests/mock_read.rs b/application/apps/indexer/sources/src/tests/mock_read.rs new file mode 100644 index 0000000000..897687eaa5 --- /dev/null +++ b/application/apps/indexer/sources/src/tests/mock_read.rs @@ -0,0 +1,41 @@ +use std::io::{Read, Seek}; + +#[derive(Debug, Clone, Default)] +/// Mock Class that provide different byte on each read call filling the whole given buffer with it +pub struct MockRead { + current_byte: u8, +} + +impl Read for MockRead { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + buf.fill(self.current_byte); + + self.current_byte = self.current_byte.wrapping_add(1); + + Ok(buf.len()) + } +} + +impl Seek for MockRead { + fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result { + panic!("Seek isn't called anywhere where MockRead is used yet"); + } +} + +#[derive(Debug, Clone)] +/// Mock Class that provide the given data an each read call. +pub struct MockRepeatRead { + data: Vec, +} + +impl MockRepeatRead { + pub fn new(data: Vec) -> Self { + Self { data } + } +} + +impl Read for MockRepeatRead { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + Read::read(&mut self.data.as_slice(), buf) + } +} diff --git a/application/apps/indexer/sources/src/tests/mod.rs b/application/apps/indexer/sources/src/tests/mod.rs new file mode 100644 index 0000000000..7eca18b6ae --- /dev/null +++ b/application/apps/indexer/sources/src/tests/mod.rs @@ -0,0 +1,34 @@ +mod mock_read; + +use crate::api::ByteSource; +pub use mock_read::*; + +/// Do general tests on the reload returns to insure the [`byte_source`] match the trait signature +/// and documentations +pub async fn general_source_reload_test(byte_source: &mut S) { + assert!( + byte_source.is_empty(), + "Source Reload Test must start with an empty source. Current Bytes count: {}", + byte_source.current_slice().len() + ); + + let load_1 = byte_source.load(None).await.unwrap().unwrap(); + // At the start the newly loaded and the available bytes should match + assert_eq!(load_1.newly_loaded_bytes, load_1.available_bytes); + + // Consume half of the available bytes + let half_read = load_1.available_bytes / 2; + byte_source.consume(half_read); + + let load_2 = byte_source.load(None).await.unwrap().unwrap(); + + let available_bytes_target = load_2.newly_loaded_bytes + (load_1.available_bytes - half_read); + + assert_eq!(available_bytes_target, load_2.available_bytes); + + // Length of loaded bytes and current slice must match when no filter is applied. + assert_eq!(byte_source.len(), byte_source.current_slice().len()); + + // available bytes must match the results of `len()` method. + assert_eq!(available_bytes_target, byte_source.len()); +} From dc3583333da57fffc48e0ebee2ac73938cd890f8 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 21 Jul 2025 16:35:38 +0200 Subject: [PATCH 100/121] Replace components crate with descriptor crate --- application/apps/indexer/Cargo.toml | 2 +- .../{components => descriptor}/Cargo.toml | 2 +- .../{components => descriptor}/src/lib.rs | 0 .../{components => descriptor}/src/scheme.rs | 0 .../{components => descriptor}/src/tys/mod.rs | 0 .../src/tys/parser.rs | 0 .../src/tys/source.rs | 0 application/apps/indexer/parsers/Cargo.toml | 2 +- .../indexer/parsers/src/dlt/descriptor.rs | 8 ++-- .../indexer/parsers/src/dlt/raw/descriptor.rs | 6 +-- .../indexer/parsers/src/someip/descriptor.rs | 4 +- .../indexer/parsers/src/text/descriptor.rs | 2 +- .../apps/indexer/plugins_host/Cargo.toml | 2 +- .../plugins_host/src/parser_shared/mod.rs | 2 +- .../plugins_host/src/v0_1_0/parser/mod.rs | 2 +- .../indexer/processor/src/producer/mod.rs | 3 -- .../processor/src/producer/tests/mock_read.rs | 41 ------------------- .../processor/src/producer/tests/mod.rs | 35 ---------------- application/apps/indexer/session/Cargo.toml | 2 +- .../apps/indexer/session/src/register/api.rs | 4 +- .../apps/indexer/session/src/register/mod.rs | 2 +- application/apps/indexer/sources/Cargo.toml | 2 +- .../src/binary/pcap/legacy/descriptor.rs | 2 +- .../sources/src/binary/pcap/ng/descriptor.rs | 2 +- .../sources/src/binary/raw/descriptor.rs | 2 +- .../indexer/sources/src/command/descriptor.rs | 4 +- .../indexer/sources/src/serial/descriptor.rs | 8 ++-- .../sources/src/socket/tcp/descriptor.rs | 4 +- .../sources/src/socket/udp/descriptor.rs | 4 +- 29 files changed, 36 insertions(+), 111 deletions(-) rename application/apps/indexer/{components => descriptor}/Cargo.toml (93%) rename application/apps/indexer/{components => descriptor}/src/lib.rs (100%) rename application/apps/indexer/{components => descriptor}/src/scheme.rs (100%) rename application/apps/indexer/{components => descriptor}/src/tys/mod.rs (100%) rename application/apps/indexer/{components => descriptor}/src/tys/parser.rs (100%) rename application/apps/indexer/{components => descriptor}/src/tys/source.rs (100%) delete mode 100644 application/apps/indexer/processor/src/producer/tests/mock_read.rs delete mode 100644 application/apps/indexer/processor/src/producer/tests/mod.rs diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index 5257ee3f6d..b6a36ab5d7 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -7,7 +7,7 @@ members = [ "addons/file-tools", "addons/text_grep", "addons/bufread", - "components", + "descriptor", "indexer_base", "merging", "parsers", diff --git a/application/apps/indexer/components/Cargo.toml b/application/apps/indexer/descriptor/Cargo.toml similarity index 93% rename from application/apps/indexer/components/Cargo.toml rename to application/apps/indexer/descriptor/Cargo.toml index bd1f854508..3b6378c4f5 100644 --- a/application/apps/indexer/components/Cargo.toml +++ b/application/apps/indexer/descriptor/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "components" +name = "descriptor" version = "0.1.0" edition = "2021" diff --git a/application/apps/indexer/components/src/lib.rs b/application/apps/indexer/descriptor/src/lib.rs similarity index 100% rename from application/apps/indexer/components/src/lib.rs rename to application/apps/indexer/descriptor/src/lib.rs diff --git a/application/apps/indexer/components/src/scheme.rs b/application/apps/indexer/descriptor/src/scheme.rs similarity index 100% rename from application/apps/indexer/components/src/scheme.rs rename to application/apps/indexer/descriptor/src/scheme.rs diff --git a/application/apps/indexer/components/src/tys/mod.rs b/application/apps/indexer/descriptor/src/tys/mod.rs similarity index 100% rename from application/apps/indexer/components/src/tys/mod.rs rename to application/apps/indexer/descriptor/src/tys/mod.rs diff --git a/application/apps/indexer/components/src/tys/parser.rs b/application/apps/indexer/descriptor/src/tys/parser.rs similarity index 100% rename from application/apps/indexer/components/src/tys/parser.rs rename to application/apps/indexer/descriptor/src/tys/parser.rs diff --git a/application/apps/indexer/components/src/tys/source.rs b/application/apps/indexer/descriptor/src/tys/source.rs similarity index 100% rename from application/apps/indexer/components/src/tys/source.rs rename to application/apps/indexer/descriptor/src/tys/source.rs diff --git a/application/apps/indexer/parsers/Cargo.toml b/application/apps/indexer/parsers/Cargo.toml index 82453fcf42..5617e2721f 100644 --- a/application/apps/indexer/parsers/Cargo.toml +++ b/application/apps/indexer/parsers/Cargo.toml @@ -23,7 +23,7 @@ thiserror.workspace = true stypes = { path = "../stypes", features=["rustcore"] } file-tools = { path = "../addons/file-tools" } someip-tools = { path = "../addons/someip-tools" } -components = { path = "../components" } +descriptor = { path = "../descriptor" } [dev-dependencies] stringreader = "0.1" diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 7111c8ee6b..3dd92962ef 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -1,5 +1,7 @@ use crate::dlt::*; -use components::{CommonDescriptor, ParserDescriptor, StaticFieldResult}; +use ::descriptor::{ + CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor, StaticFieldResult, +}; use dlt_core::{ read::DltMessageReader, statistics::{ @@ -117,7 +119,7 @@ impl CommonDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, origin: &SessionAction) -> FieldsResult { let mut options = vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), @@ -181,7 +183,7 @@ impl CommonDescriptor for Descriptor { &self, origin: SessionAction, cancel: CancellationToken, - ) -> components::LazyFieldsTask { + ) -> LazyFieldsTask { Box::pin(async move { let file_paths = match origin { SessionAction::File(fp) => { diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 511cd9d028..9ff9bc0940 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,6 +1,6 @@ use super::*; use crate::*; -use components::{CommonDescriptor, ParserDescriptor}; +use ::descriptor::{CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor}; use stypes::SessionAction; use tokio_util::sync::CancellationToken; @@ -35,7 +35,7 @@ impl CommonDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { Ok(Vec::new()) } fn ident(&self) -> stypes::Ident { @@ -53,7 +53,7 @@ impl CommonDescriptor for Descriptor { &self, _origin: SessionAction, _cancel: CancellationToken, - ) -> components::LazyFieldsTask { + ) -> LazyFieldsTask { Box::pin(async move { Ok(Vec::new()) }) } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index babc11d04f..7dd702027c 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,4 +1,4 @@ -use components::{CommonDescriptor, ParserDescriptor}; +use ::descriptor::{CommonDescriptor, FieldsResult, ParserDescriptor}; use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; use super::SomeipParser; @@ -44,7 +44,7 @@ impl CommonDescriptor for Descriptor { .unwrap_or_default() }) } - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_FIBEX_FILES.to_owned(), diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index 2c13d37cf3..ee8d6e2669 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,4 +1,4 @@ -use components::{CommonDescriptor, ParserDescriptor}; +use descriptor::{CommonDescriptor, ParserDescriptor}; use file_tools::is_binary; use stypes::SessionAction; diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index fee7d8b557..f77860cdc4 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -17,9 +17,9 @@ rand.workspace = true wasmtime = "33.0" wasmtime-wasi = "33.0" uuid = { workspace = true , features = ["serde", "v4"] } -components = { path = "../components" } # TODO AAZ: Remove when `DescriptorGetter::create` function is async. tokio-util.workspace = true +descriptor = { path = "../descriptor" } parsers = { path = "../parsers" } sources = { path = "../sources" } stypes = { path = "../stypes" } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index dc5db04873..7e85435d3c 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use wasmtime::component::Component; -use components::{CommonDescriptor, ParserDescriptor}; +use descriptor::{CommonDescriptor, ParserDescriptor}; use parsers::api::*; use stypes::{PluginInfo, SemanticVersion}; diff --git a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs index 70ab6cb661..7bc5099a16 100644 --- a/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs +++ b/application/apps/indexer/plugins_host/src/v0_1_0/parser/mod.rs @@ -4,7 +4,7 @@ mod bindings; mod parser_plugin_state; -use components::{CommonDescriptor, ParserDescriptor}; +use descriptor::{CommonDescriptor, ParserDescriptor}; use parsers::api::*; use stypes::{ParserRenderOptions, RenderOptions, SemanticVersion}; use tokio::runtime::Handle; diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index fbfe77eedf..883e978ea1 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -1,6 +1,3 @@ -#[cfg(test)] -mod tests; - pub mod sde; use log::warn; use parsers::api::*; diff --git a/application/apps/indexer/processor/src/producer/tests/mock_read.rs b/application/apps/indexer/processor/src/producer/tests/mock_read.rs deleted file mode 100644 index 897687eaa5..0000000000 --- a/application/apps/indexer/processor/src/producer/tests/mock_read.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::io::{Read, Seek}; - -#[derive(Debug, Clone, Default)] -/// Mock Class that provide different byte on each read call filling the whole given buffer with it -pub struct MockRead { - current_byte: u8, -} - -impl Read for MockRead { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - buf.fill(self.current_byte); - - self.current_byte = self.current_byte.wrapping_add(1); - - Ok(buf.len()) - } -} - -impl Seek for MockRead { - fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result { - panic!("Seek isn't called anywhere where MockRead is used yet"); - } -} - -#[derive(Debug, Clone)] -/// Mock Class that provide the given data an each read call. -pub struct MockRepeatRead { - data: Vec, -} - -impl MockRepeatRead { - pub fn new(data: Vec) -> Self { - Self { data } - } -} - -impl Read for MockRepeatRead { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - Read::read(&mut self.data.as_slice(), buf) - } -} diff --git a/application/apps/indexer/processor/src/producer/tests/mod.rs b/application/apps/indexer/processor/src/producer/tests/mod.rs deleted file mode 100644 index 200a02de44..0000000000 --- a/application/apps/indexer/processor/src/producer/tests/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -pub mod mock_read; - -pub use mock_read::MockRead; - -use crate::ByteSource; - -/// Do general tests on the reload returns to insure the [`byte_source`] match the trait signature -/// and documentations -pub async fn general_source_reload_test(byte_source: &mut S) { - assert!( - byte_source.is_empty(), - "Source Reload Test must start with an empty source. Current Bytes count: {}", - byte_source.current_slice().len() - ); - - let load_1 = byte_source.load(None).await.unwrap().unwrap(); - // At the start the newly loaded and the available bytes should match - assert_eq!(load_1.newly_loaded_bytes, load_1.available_bytes); - - // Consume half of the available bytes - let half_read = load_1.available_bytes / 2; - byte_source.consume(half_read); - - let load_2 = byte_source.load(None).await.unwrap().unwrap(); - - let available_bytes_target = load_2.newly_loaded_bytes + (load_1.available_bytes - half_read); - - assert_eq!(available_bytes_target, load_2.available_bytes); - - // Length of loaded bytes and current slice must match when no filter is applied. - assert_eq!(byte_source.len(), byte_source.current_slice().len()); - - // available bytes must match the results of `len()` method. - assert_eq!(available_bytes_target, byte_source.len()); -} diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index 3f62bb2ae3..ff6c2ff0f2 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -27,7 +27,7 @@ walkdir.workspace = true indexer_base = { path = "../indexer_base" } plugins_host = { path = "../plugins_host/"} -components = { path = "../components" } +descriptor = { path = "../descriptor" } stypes = { path = "../stypes", features=["rustcore"] } parsers = { path = "../parsers" } processor = { path = "../processor" } diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index db2dd6b2ec..55b007e801 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, fmt}; -use components::{LazyLoadingResult, LazyLoadingTaskMeta}; +use descriptor::{LazyLoadingResult, LazyLoadingTaskMeta}; use stypes::{Ident, NativeError, SessionAction, SessionDescriptor}; use tokio::sync::oneshot; use tokio_util::sync::CancellationToken; @@ -176,7 +176,7 @@ pub enum Api { RemovePlugin(String, oneshot::Sender>), } -use components::*; +use descriptor::*; use parsers::{Parsers, api::*}; use sources::{Sources, api::*}; diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index 1d94e7d36e..a9710732d4 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -2,7 +2,7 @@ mod api; mod plugins; use api::*; -use components::{LazyLoadingResult, LazyLoadingTaskMeta}; +use descriptor::{LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; use std::{collections::HashMap, sync::Arc}; use tokio::{ diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 0710edc59f..2c1a222491 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -30,7 +30,7 @@ envvars.workspace = true file-tools = { path = "../addons/file-tools" } stypes = { path = "../stypes", features=["rustcore"] } -components = { path = "../components" } +descriptor = { path = "../descriptor" } [dev-dependencies] env_logger.workspace = true diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index ab3b14f431..05ac87f1a6 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -1,4 +1,4 @@ -use components::{SourceDescriptor, CommonDescriptor}; +use descriptor::{SourceDescriptor, CommonDescriptor}; use file_tools::is_binary; use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; use crate::*; diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index acd16736da..2f0f39a9ce 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -1,4 +1,4 @@ -use components::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; use super::PcapngByteSourceFromFile; diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 51a39668ad..4b8e476c0c 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -1,5 +1,5 @@ use crate::*; -use components::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; use stypes::SessionAction; diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 58c49e3386..535acc4841 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -1,6 +1,6 @@ use super::ProcessSource; use crate::*; -use components::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; use std::{collections::HashMap, env}; use stypes::{ ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, @@ -35,7 +35,7 @@ pub fn factory( } impl CommonDescriptor for Descriptor { - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { let mut shells = vec![("".to_owned(), "Default".to_owned())]; if let Ok(profiles) = envvars::get_profiles() { shells = profiles diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index b7a51eb99f..378f5c461b 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -1,6 +1,8 @@ use crate::prelude::SerialSource; use crate::*; -use components::{CommonDescriptor, SourceDescriptor, StaticFieldResult}; +use descriptor::{ + CommonDescriptor, FieldsResult, LazyFieldsTask, SourceDescriptor, StaticFieldResult, +}; use std::{collections::HashMap, str}; use stypes::{ ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, NativeErrorKind, @@ -99,7 +101,7 @@ impl CommonDescriptor for Descriptor { uuid: SERIAL_SOURCE_UUID, } } - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_PATH.to_owned(), @@ -201,7 +203,7 @@ impl CommonDescriptor for Descriptor { &self, _origin: SessionAction, _cancel: tokio_util::sync::CancellationToken, - ) -> components::LazyFieldsTask { + ) -> LazyFieldsTask { Box::pin(async move { let ports = serialport::available_ports() .map_err(|e| stypes::ComputationError::IoOperation(e.to_string()))? diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index e6055e445a..fadff929e9 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -1,5 +1,5 @@ use crate::*; -use components::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; use std::collections::HashMap; use stypes::{ ExtractByKey, Extracted, Field, FieldDesc, NativeError, SessionAction, StaticFieldDesc, @@ -51,7 +51,7 @@ impl CommonDescriptor for Descriptor { uuid: TCP_SOURCE_UUID, } } - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { Ok(vec![FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), name: "IP address and port".to_owned(), diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index d9cc14a9eb..e026ab7502 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -1,6 +1,6 @@ use super::{MulticastInfo, UdpSource}; use crate::*; -use components::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; use std::collections::HashMap; use stypes::{ ExtractAs, ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, @@ -87,7 +87,7 @@ impl CommonDescriptor for Descriptor { uuid: UDP_SOURCE_UUID, } } - fn fields_getter(&self, _origin: &SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &SessionAction) -> FieldsResult { Ok(vec![ FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), From 87a20b4b29213c2f88dcb2299a374f536b0f0db0 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 22 Jul 2025 15:25:33 +0200 Subject: [PATCH 101/121] Restore session parser/source snap tests --- .../apps/indexer/descriptor/src/tys/mod.rs | 2 +- .../indexer/parsers/src/dlt/descriptor.rs | 24 ++++- .../indexer/parsers/src/someip/descriptor.rs | 27 +++++- .../indexer/parsers/src/text/descriptor.rs | 9 +- .../session/tests/snapshot_tests/mod.rs | 96 +++++++------------ .../snapshots/observe_dlt_session.snap | 23 ++++- .../observe_dlt_with_someip_session.snap | 25 +++-- .../observe_someip_bcapng_session.snap | 16 +++- .../observe_someip_legacy_session.snap | 16 +++- .../session/tests/snapshot_tests/utls.rs | 22 +---- .../src/binary/pcap/legacy/descriptor.rs | 8 +- .../sources/src/binary/pcap/ng/descriptor.rs | 8 +- .../sources/src/binary/raw/descriptor.rs | 9 +- 13 files changed, 177 insertions(+), 108 deletions(-) diff --git a/application/apps/indexer/descriptor/src/tys/mod.rs b/application/apps/indexer/descriptor/src/tys/mod.rs index 1f9494a109..3fcab8cefb 100644 --- a/application/apps/indexer/descriptor/src/tys/mod.rs +++ b/application/apps/indexer/descriptor/src/tys/mod.rs @@ -198,5 +198,5 @@ impl ValidationErrors { pub fn insert_field_bound_err(&mut self, field: &str, msg: String) { self.errors.insert(field.to_owned(), msg); } - pub fn insert_values_bound_err(&mut self, field: &str, idx: usize) {} + pub fn insert_values_bound_err(&mut self, _field: &str, _idx: usize) {} } diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 3dd92962ef..54f69db02b 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -13,8 +13,8 @@ use std::fmt; use std::fs::File; use std::{collections::HashMap, path::PathBuf}; use stypes::{ - ExtractByKey, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, Severity, - StaticFieldDesc, ValueInput, missed_field_err as missed, + ComponentOptions, ExtractByKey, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, Value, ValueInput, missed_field_err as missed, }; use tokio_util::sync::CancellationToken; @@ -331,3 +331,23 @@ fn u8_to_log_level(level: u8) -> Option { _ => None, } } + +pub fn get_default_options(fibex: Option>) -> ComponentOptions { + ComponentOptions { + uuid: DLT_PARSER_UUID, + fields: vec![ + Field { + id: FIELD_FIBEX_FILES.to_owned(), + value: Value::Files(fibex.unwrap_or(Vec::new())), + }, + Field { + id: FIELD_LOG_LEVEL.to_owned(), + value: Value::Number(6), + }, + Field { + id: FIELD_STATISTICS.to_owned(), + value: Value::KeyStrings(HashMap::new()), + }, + ], + } +} diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 7dd702027c..e2f645d1fa 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,5 +1,10 @@ +use crate::someip::FibexMetadata; use ::descriptor::{CommonDescriptor, FieldsResult, ParserDescriptor}; -use stypes::{FieldDesc, SessionAction, StaticFieldDesc, ValueInput}; +use std::path::PathBuf; +use stypes::{ + ComponentOptions, ExtractByKey, Field, FieldDesc, SessionAction, StaticFieldDesc, Value, + ValueInput, missed_field_err as missed, +}; use super::SomeipParser; @@ -17,10 +22,16 @@ pub struct Descriptor {} pub fn factory( _origin: &SessionAction, - _options: &[stypes::Field], + options: &[stypes::Field], ) -> Result)>, stypes::NativeError> { + let fibex_file_paths: &Vec = options + .extract_by_key(FIELD_FIBEX_FILES) + .ok_or(missed(FIELD_FIBEX_FILES))? + .value; Ok(Some(( - crate::Parsers::SomeIp(SomeipParser::new()), + crate::Parsers::SomeIp(SomeipParser { + fibex_metadata: FibexMetadata::from_fibex_files(fibex_file_paths), + }), Some("SomeIp".to_owned()), ))) } @@ -93,3 +104,13 @@ impl ParserDescriptor for Descriptor { ])) } } + +pub fn get_default_options(fibex: Option>) -> ComponentOptions { + ComponentOptions { + uuid: SOMEIP_PARSER_UUID, + fields: vec![Field { + id: FIELD_FIBEX_FILES.to_owned(), + value: Value::Files(fibex.unwrap_or(Vec::new())), + }], + } +} diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index ee8d6e2669..dbe63c89c0 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,6 +1,6 @@ use descriptor::{CommonDescriptor, ParserDescriptor}; use file_tools::is_binary; -use stypes::SessionAction; +use stypes::{ComponentOptions, SessionAction}; use super::StringTokenizer; @@ -50,3 +50,10 @@ impl ParserDescriptor for Descriptor { Some(stypes::OutputRender::PlaitText) } } + +pub fn get_default_options() -> ComponentOptions { + ComponentOptions { + uuid: TEXT_PARSER_UUID, + fields: Vec::new(), + } +} diff --git a/application/apps/indexer/session/tests/snapshot_tests/mod.rs b/application/apps/indexer/session/tests/snapshot_tests/mod.rs index f14b98fb08..3ae96d336a 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/mod.rs +++ b/application/apps/indexer/session/tests/snapshot_tests/mod.rs @@ -18,24 +18,25 @@ mod utls; use std::path::PathBuf; +use stypes::{SessionAction, SessionSetup}; use utls::*; #[tokio::test] async fn observe_dlt_session() { - let input = "../../../developing/resources/attachments.dlt"; - let parser_settings = stypes::DltParserSettings::default(); - let session_main_file = run_observe_session( - input, - stypes::FileFormat::Binary, - stypes::ParserType::Dlt(parser_settings.clone()), - ) - .await; + let setup = SessionSetup { + origin: SessionAction::File(PathBuf::from( + "../../../developing/resources/attachments.dlt", + )), + parser: parsers::dlt::descriptor::get_default_options(None), + source: sources::binary::raw::get_default_options(), + }; + let session_main_file = run_observe_session(setup.clone()).await; let session_files = SessionFiles::from_session_file(&session_main_file); insta::with_settings!({ description => "Snapshot for DLT file with text attachments.", - info => &parser_settings, + info => &setup, omit_expression => true, prepend_module_to_snapshot => false, }, { @@ -45,31 +46,20 @@ async fn observe_dlt_session() { #[tokio::test] async fn observe_dlt_with_someip_session() { - let input = "../../../developing/resources/someip.dlt"; - let fibex_file = "../../../developing/resources/someip.xml"; - - assert!( - PathBuf::from(fibex_file).exists(), - "Fibex file path doesn't exist. Path: {fibex_file}" - ); - - let parser_settings = stypes::DltParserSettings { - fibex_file_paths: Some(vec![String::from(fibex_file)]), - ..Default::default() + let setup = SessionSetup { + origin: SessionAction::File(PathBuf::from("../../../developing/resources/someip.dlt")), + parser: parsers::dlt::descriptor::get_default_options(Some(vec![PathBuf::from( + "../../../developing/resources/someip.xml", + )])), + source: sources::binary::raw::get_default_options(), }; - - let session_main_file = run_observe_session( - input, - stypes::FileFormat::Binary, - stypes::ParserType::Dlt(parser_settings.clone()), - ) - .await; + let session_main_file = run_observe_session(setup.clone()).await; let session_files = SessionFiles::from_session_file(&session_main_file); insta::with_settings!({ description => "Snapshot for DLT file with SomeIP network trace.", - info => &parser_settings, + info => &setup, omit_expression => true, prepend_module_to_snapshot => false, }, { @@ -79,30 +69,21 @@ async fn observe_dlt_with_someip_session() { #[tokio::test] async fn observe_someip_bcapng_session() { - let input = "../../../developing/resources/someip.pcapng"; - let fibex_file = "../../../developing/resources/someip.xml"; - - assert!( - PathBuf::from(fibex_file).exists(), - "Fibex file path doesn't exist. Path: {fibex_file}" - ); - - let parser_settings = stypes::SomeIpParserSettings { - fibex_file_paths: Some(vec![String::from(fibex_file)]), + let setup = SessionSetup { + origin: SessionAction::File(PathBuf::from("../../../developing/resources/someip.pcapng")), + parser: parsers::someip::descriptor::get_default_options(Some(vec![PathBuf::from( + "../../../developing/resources/someip.xml", + )])), + source: sources::binary::pcap::ng::get_default_options(), }; - let session_main_file = run_observe_session( - input, - stypes::FileFormat::PcapNG, - stypes::ParserType::SomeIp(parser_settings.clone()), - ) - .await; + let session_main_file = run_observe_session(setup.clone()).await; let session_files = SessionFiles::from_session_file(&session_main_file); insta::with_settings!({ description => "Snapshot for SomeIP file with Pcapng byte source.", - info => &parser_settings, + info => &setup, omit_expression => true, prepend_module_to_snapshot => false, }, { @@ -112,30 +93,21 @@ async fn observe_someip_bcapng_session() { #[tokio::test] async fn observe_someip_legacy_session() { - let input = "../../../developing/resources/someip.pcap"; - let fibex_file = "../../../developing/resources/someip.xml"; - - assert!( - PathBuf::from(fibex_file).exists(), - "Fibex file path doesn't exist. Path: {fibex_file}" - ); - - let parser_settings = stypes::SomeIpParserSettings { - fibex_file_paths: Some(vec![String::from(fibex_file)]), + let setup = SessionSetup { + origin: SessionAction::File(PathBuf::from("../../../developing/resources/someip.pcap")), + parser: parsers::someip::descriptor::get_default_options(Some(vec![PathBuf::from( + "../../../developing/resources/someip.xml", + )])), + source: sources::binary::pcap::legacy::get_default_options(), }; - let session_main_file = run_observe_session( - input, - stypes::FileFormat::PcapLegacy, - stypes::ParserType::SomeIp(parser_settings.clone()), - ) - .await; + let session_main_file = run_observe_session(setup.clone()).await; let session_files = SessionFiles::from_session_file(&session_main_file); insta::with_settings!({ description => "Snapshot for SomeIP file with Pcap Legacy byte source.", - info => &parser_settings, + info => &setup, omit_expression => true, prepend_module_to_snapshot => false, }, { diff --git a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_session.snap b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_session.snap index 5f62501694..b922fa16e8 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_session.snap +++ b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_session.snap @@ -1,12 +1,25 @@ --- source: session/tests/snapshot_tests/mod.rs +assertion_line: 43 description: Snapshot for DLT file with text attachments. info: - filter_config: ~ - fibex_file_paths: ~ - with_storage_header: true - tz: ~ -snapshot_kind: text + origin: + File: "../../../developing/resources/attachments.dlt" + parser: + fields: + - id: DLT_PARSER_FIELD_FIBEX_FILES + value: + Files: [] + - id: DLT_PARSER_FIELD_LOG_LEVEL + value: + Number: 6 + - id: DLT_PARSER_FIELD_STATISTICS + value: + KeyStrings: {} + uuid: 01010101-0101-0101-0101-010101010101 + source: + fields: [] + uuid: 08080808-0808-0808-0808-080808080808 --- session_file: - "1970-01-01T00:00:00.000000000Z\u0004ecu\u00040\u0004\u00040\u0004\u0004ecu1\u0004\u0004\u0004LogLevel INFO\u0004\u0005FLST\u0005129721424\u0005test1.txt\u00055\u0005date\u00051\u00050\u0005FLST" diff --git a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_with_someip_session.snap b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_with_someip_session.snap index 14e9a15ebf..df44813178 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_with_someip_session.snap +++ b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_dlt_with_someip_session.snap @@ -1,13 +1,26 @@ --- source: session/tests/snapshot_tests/mod.rs +assertion_line: 66 description: Snapshot for DLT file with SomeIP network trace. info: - filter_config: ~ - fibex_file_paths: - - "../../../developing/resources/someip.xml" - with_storage_header: true - tz: ~ -snapshot_kind: text + origin: + File: "../../../developing/resources/someip.dlt" + parser: + fields: + - id: DLT_PARSER_FIELD_FIBEX_FILES + value: + Files: + - "../../../developing/resources/someip.xml" + - id: DLT_PARSER_FIELD_LOG_LEVEL + value: + Number: 6 + - id: DLT_PARSER_FIELD_STATISTICS + value: + KeyStrings: {} + uuid: 01010101-0101-0101-0101-010101010101 + source: + fields: [] + uuid: 08080808-0808-0808-0808-080808080808 --- session_file: - "2024-02-20T13:17:26.713537000Z\u0004ECU1\u00041\u0004571\u0004204\u000428138506\u0004ECU1\u0004APP1\u0004C1\u0004IPC\u0004SOME/IP 0.0.0.0:0 >> INST:1 RPC SERV:123 METH:32773 LENG:16 CLID:0 SEID:58252 IVER:1 MSTP:2 RETC:0 TestService::timeEvent {\u0006\ttimestamp (INT64) : 1683656786973,\u0006}" diff --git a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_bcapng_session.snap b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_bcapng_session.snap index b68202b7f8..04a93d75ea 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_bcapng_session.snap +++ b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_bcapng_session.snap @@ -1,10 +1,20 @@ --- source: session/tests/snapshot_tests/mod.rs +assertion_line: 90 description: Snapshot for SomeIP file with Pcapng byte source. info: - fibex_file_paths: - - "../../../developing/resources/someip.xml" -snapshot_kind: text + origin: + File: "../../../developing/resources/someip.pcapng" + parser: + fields: + - id: SOMEIP_PARSER_FIELD_FIBEX_FILES + value: + Files: + - "../../../developing/resources/someip.xml" + uuid: 02020202-0202-0202-0202-020202020202 + source: + fields: [] + uuid: 09090909-0909-0909-0909-090909090909 --- session_file: - "SD\u000465535\u000433024\u000460\u00040\u00040\u00041\u00042\u00040\u0004Flags [C0], Offer 123 v1.0 Inst 1 Ttl 3 UDP 192.168.178.58:30000 TCP 192.168.178.58:30000" diff --git a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_legacy_session.snap b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_legacy_session.snap index 2557d86c56..d19debabd7 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_legacy_session.snap +++ b/application/apps/indexer/session/tests/snapshot_tests/snapshots/observe_someip_legacy_session.snap @@ -1,10 +1,20 @@ --- source: session/tests/snapshot_tests/mod.rs +assertion_line: 114 description: Snapshot for SomeIP file with Pcap Legacy byte source. info: - fibex_file_paths: - - "../../../developing/resources/someip.xml" -snapshot_kind: text + origin: + File: "../../../developing/resources/someip.pcap" + parser: + fields: + - id: SOMEIP_PARSER_FIELD_FIBEX_FILES + value: + Files: + - "../../../developing/resources/someip.xml" + uuid: 02020202-0202-0202-0202-020202020202 + source: + fields: [] + uuid: 10101010-1010-1010-1010-101010101010 --- session_file: - "SD\u000465535\u000433024\u000460\u00040\u00040\u00041\u00042\u00040\u0004Flags [C0], Offer 123 v1.0 Inst 1 Ttl 3 UDP 192.168.178.58:30000 TCP 192.168.178.58:30000" diff --git a/application/apps/indexer/session/tests/snapshot_tests/utls.rs b/application/apps/indexer/session/tests/snapshot_tests/utls.rs index 4e58501c72..0c6aad5ed6 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/utls.rs +++ b/application/apps/indexer/session/tests/snapshot_tests/utls.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use session::session::Session; use std::path::{Path, PathBuf}; +use stypes::SessionSetup; use uuid::Uuid; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -89,28 +90,11 @@ fn session_dir_form_file(session_file: &Path) -> PathBuf { /// and returning the path of the main session file. /// # Note: /// This function it made for test purposes with snapshots. -pub async fn run_observe_session>( - input: P, - file_format: stypes::FileFormat, - parser_type: stypes::ParserType, -) -> PathBuf { - let input: PathBuf = input.into(); - - assert!( - input.exists(), - "Input file doesn't exist. Path {}", - input.display() - ); - +pub async fn run_observe_session(setup: SessionSetup) -> PathBuf { let uuid = Uuid::new_v4(); let (session, mut receiver) = Session::new(uuid).await.expect("Session should be created"); - session - .observe( - uuid, - stypes::ObserveOptions::file(input.clone(), file_format, parser_type), - ) - .unwrap(); + session.observe(uuid, setup).unwrap(); while let Some(feedback) = receiver.recv().await { match feedback { diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 05ac87f1a6..2912b57f53 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -1,6 +1,6 @@ use descriptor::{SourceDescriptor, CommonDescriptor}; use file_tools::is_binary; -use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; +use stypes::{ComponentOptions, NativeError, NativeErrorKind, SessionAction, Severity}; use crate::*; use super::PcapLegacyByteSourceFromFile; @@ -63,6 +63,12 @@ impl CommonDescriptor for Descriptor { impl SourceDescriptor for Descriptor {} +pub fn get_default_options() -> ComponentOptions { + ComponentOptions { + uuid: PCAP_SOURCE_UUID, + fields: Vec::new(), + } +} #[cfg(test)] mod tests { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 2f0f39a9ce..1ab065624b 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -1,6 +1,6 @@ use descriptor::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; -use stypes::{NativeError, NativeErrorKind, SessionAction, Severity}; +use stypes::{ComponentOptions, NativeError, NativeErrorKind, SessionAction, Severity}; use super::PcapngByteSourceFromFile; use crate::*; @@ -63,3 +63,9 @@ impl CommonDescriptor for Descriptor { impl SourceDescriptor for Descriptor {} +pub fn get_default_options() -> ComponentOptions { + ComponentOptions { + uuid: PCAPNG_SOURCE_UUID, + fields: Vec::new(), + } +} \ No newline at end of file diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 4b8e476c0c..6d8329d479 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -1,7 +1,7 @@ use crate::*; use descriptor::{CommonDescriptor, SourceDescriptor}; use file_tools::is_binary; -use stypes::SessionAction; +use stypes::{ComponentOptions, SessionAction}; const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, @@ -60,3 +60,10 @@ impl CommonDescriptor for Descriptor { } impl SourceDescriptor for Descriptor {} + +pub fn get_default_options() -> ComponentOptions { + ComponentOptions { + uuid: BIN_SOURCE_UUID, + fields: Vec::new(), + } +} From d6b5b9ea006d73c38699d1250f12d14ed5d15d65 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Jul 2025 09:42:07 +0200 Subject: [PATCH 102/121] Update a documentation to code --- .../apps/indexer/descriptor/src/tys/mod.rs | 32 ++++++++- .../apps/indexer/descriptor/src/tys/parser.rs | 32 ++++++--- .../apps/indexer/descriptor/src/tys/source.rs | 34 +++++++--- .../apps/indexer/parsers/src/api/mod.rs | 31 +++++++++ .../apps/indexer/session/src/register/api.rs | 68 +++++++++++++++++++ .../apps/indexer/session/src/register/mod.rs | 6 +- .../apps/indexer/sources/src/api/mod.rs | 31 +++++++++ .../apps/rustcore/rs-bindings/Cargo.lock | 48 +++++-------- .../rs-bindings/src/js/components/mod.rs | 6 +- 9 files changed, 228 insertions(+), 60 deletions(-) diff --git a/application/apps/indexer/descriptor/src/tys/mod.rs b/application/apps/indexer/descriptor/src/tys/mod.rs index 3fcab8cefb..a7a7a65589 100644 --- a/application/apps/indexer/descriptor/src/tys/mod.rs +++ b/application/apps/indexer/descriptor/src/tys/mod.rs @@ -68,7 +68,7 @@ pub type Factory = fn( /// Describes a component in terms of its identity, configuration schema, /// validation logic, support for lazy-loading configuration, and its type within the system. /// -/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples +/// The `CommonDescriptor` trait serves as an abstraction layer that decouples /// the core system from concrete component implementations. Instead of referring /// to component types directly (e.g., a specific parser or source), the application /// interacts with components through their descriptors. @@ -189,14 +189,44 @@ pub trait CommonDescriptor: Sync + Send { } } +/// Represents validation errors that occurred during client-side input processing. +/// +/// This structure is used to convey validation failures back to the client. +/// The client typically sends a list of fields as `(UUID as string, value)` pairs. +/// During validation, if any field contains invalid input, an error message is added +/// to the `errors` map, keyed by the stringified UUID of the field. These messages +/// are then displayed to the user in the UI. #[derive(Debug, Default)] pub struct ValidationErrors { + /// A map of field UUIDs (as strings) to corresponding validation error messages. + /// + /// Each key represents a specific field for which validation failed, + /// and the value is the error message intended for the user. errors: HashMap, } impl ValidationErrors { + /// Inserts a validation error message associated with a specific field. + /// + /// This method is typically called when a field fails a validation check, + /// such as range bounds, format constraints, or required value checks. + /// + /// # Arguments + /// + /// * `field` - The string identifier of the field (usually a UUID). + /// * `msg` - The error message to associate with the field. pub fn insert_field_bound_err(&mut self, field: &str, msg: String) { self.errors.insert(field.to_owned(), msg); } + + /// Placeholder for reporting validation errors in a field that contains a list of values. + /// + /// This method is intended to be used when a specific value at a given index + /// in a collection (e.g., array, vector) is invalid. Currently, it is not implemented. + /// + /// # Arguments + /// + /// * `_field` - The string identifier of the field. + /// * `_idx` - The index of the invalid value in the list. pub fn insert_values_bound_err(&mut self, _field: &str, _idx: usize) {} } diff --git a/application/apps/indexer/descriptor/src/tys/parser.rs b/application/apps/indexer/descriptor/src/tys/parser.rs index 50ec2563cb..f8ccadfbcc 100644 --- a/application/apps/indexer/descriptor/src/tys/parser.rs +++ b/application/apps/indexer/descriptor/src/tys/parser.rs @@ -1,18 +1,28 @@ use super::CommonDescriptor; -/// Describes a component in terms of its identity, configuration schema, -/// validation logic, support for lazy-loading configuration, and its type within the system. +/// Defines parser-specific behavior and capabilities. /// -/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples -/// the core system from concrete component implementations. Instead of referring -/// to component types directly (e.g., a specific parser or source), the application -/// interacts with components through their descriptors. +/// This trait extends [`CommonDescriptor`] and provides an interface for +/// describing rendering behavior specific to a parser implementation. /// -/// This design enables a fully modular architecture where, for example, a session -/// can be created using a parser and source identified solely by their UUIDs. -/// The actual parser and source implementations remain hidden behind the descriptor, -/// making it possible to swap, reconfigure, or isolate components without touching -/// the application core. +/// A parser may define which render should be used to display its content. +/// However, not all parsers are intended for rendering; some may serve +/// other purposes such as exporting or transforming data and thus return `None` +/// instead of a rendering strategy. +/// +/// Implementors of this trait should document the semantics of `get_render()` +/// in the context of their specific parser functionality. pub trait ParserDescriptor: CommonDescriptor { + /// Returns the rendering strategy associated with this parser, if any. + /// + /// This method determines how (and whether) the parser's output should be + /// rendered in the user interface. If the parser is not intended to produce + /// visual output—e.g., when used for data export or structural analysis— + /// this function returns `None`. + /// + /// # Returns + /// + /// - `Some(OutputRender)` if the parser provides a specific rendering strategy. + /// - `None` if rendering is not applicable to this parser. fn get_render(&self) -> Option; } diff --git a/application/apps/indexer/descriptor/src/tys/source.rs b/application/apps/indexer/descriptor/src/tys/source.rs index 71a0381ed9..31526653dd 100644 --- a/application/apps/indexer/descriptor/src/tys/source.rs +++ b/application/apps/indexer/descriptor/src/tys/source.rs @@ -1,19 +1,31 @@ use super::CommonDescriptor; -/// Describes a component in terms of its identity, configuration schema, -/// validation logic, support for lazy-loading configuration, and its type within the system. +/// Defines source-specific capabilities and behaviors. /// -/// The `ComponentDescriptor` trait serves as an abstraction layer that decouples -/// the core system from concrete component implementations. Instead of referring -/// to component types directly (e.g., a specific parser or source), the application -/// interacts with components through their descriptors. +/// This trait extends [`CommonDescriptor`] to describe features unique to data sources. +/// One such feature is support for SDE (Source Data Exchange), which allows the user +/// to send data back into the source instance. This is typically applicable in interactive +/// or bidirectional sources. /// -/// This design enables a fully modular architecture where, for example, a session -/// can be created using a parser and source identified solely by their UUIDs. -/// The actual parser and source implementations remain hidden behind the descriptor, -/// making it possible to swap, reconfigure, or isolate components without touching -/// the application core. +/// For example, a `SerialPort` source may support SDE, enabling users to send commands +/// directly to the serial device. pub trait SourceDescriptor: CommonDescriptor { + /// Indicates whether the source supports Source Data Exchange (SDE). + /// + /// When SDE is supported, the system allows the user to transmit data to the source, + /// such as sending raw binary streams or textual commands. + /// + /// This is commonly used in sources that enable two-way communication. + /// If the source does not support SDE, this method should return `false`. + /// + /// # Arguments + /// + /// * `_origin` - The session action that triggered the check (e.g., from the UI or script). + /// * `_fields` - A list of fields relevant to the current context or form submission. + /// + /// # Returns + /// + /// `true` if SDE is supported by the source, `false` otherwise. fn is_sde_supported(&self, _origin: &stypes::SessionAction, _fields: &[stypes::Field]) -> bool { false } diff --git a/application/apps/indexer/parsers/src/api/mod.rs b/application/apps/indexer/parsers/src/api/mod.rs index 93327ebbee..c93094bab4 100644 --- a/application/apps/indexer/parsers/src/api/mod.rs +++ b/application/apps/indexer/parsers/src/api/mod.rs @@ -6,6 +6,37 @@ use thiserror::Error; pub use record::*; +/// Defines the function type used to construct a parser instance for a session. +/// +/// This factory function is stored in the [`Register`] and invoked when a new session +/// is initialized. It produces a parser instance based on the provided session context +/// and configuration fields. +/// +/// The return value is a tuple: +/// - [`Parsers`] – An enum-wrapped instance of the constructed parser. +/// - `Option` – A human-readable label that describes the configured parser in context. +/// +/// The difference between this label and the one returned by [`CommonDescriptor`] is that +/// this one is **context-aware**. While `CommonDescriptor` may return a general name such as +/// `"DLT parser"`, this factory may return something more specific like +/// `"DLT with fibex /few/fibex.xml"` depending on user configuration or input. +/// +/// # Arguments +/// +/// * `&SessionAction` – The session action context (e.g., user-triggered session creation). +/// * `&[stypes::Field]` – A list of configuration fields provided by the client or system. +/// +/// # Returns +/// +/// * `Ok(Some((Parsers, Option)))` – A constructed parser instance with an optional +/// context-specific name for display. +/// * `Ok(None)` – Indicates that no parser should be created. +/// * `Err(NativeError)` – If parser creation fails due to misconfiguration or internal error. +/// +/// # Errors +/// +/// Returns an `Err(NativeError)` if the parser cannot be created due to invalid configuration, +/// missing required fields, or runtime failure during instantiation. pub type ParserFactory = fn( &SessionAction, &[stypes::Field], diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index 55b007e801..356803567b 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -180,18 +180,54 @@ use descriptor::*; use parsers::{Parsers, api::*}; use sources::{Sources, api::*}; +/// Registry of all available parsers and sources in the system. +/// +/// This struct acts as a central repository that holds references to all registered +/// parser and source components, along with their factory functions and descriptors. +/// +/// During session setup, the appropriate factory functions and metadata can be +/// retrieved using UUIDs to create concrete parser and source instances, validate +/// configuration fields, and present component metadata to the user interface. pub struct Register { + /// A map of source component UUIDs to their factory functions and descriptors. + /// + /// Each entry contains: + /// - a [`SourceFactory`] used to instantiate the component, + /// - a boxed [`SourceDescriptor`] that describes its capabilities and configuration. sources: HashMap)>, + + /// A map of parser component UUIDs to their factory functions and descriptors. + /// + /// Each entry contains: + /// - a [`ParserFactory`] used to instantiate the component, + /// - a boxed [`ParserDescriptor`] that describes its capabilities and configuration. parsers: HashMap)>, } impl Register { + /// Creates an empty `Register` with no registered parsers or sources. + /// + /// This is typically used at system startup before components are added via + /// [`add_parser`] or [`add_source`]. pub fn new() -> Register { Self { sources: HashMap::new(), parsers: HashMap::new(), } } + + /// Registers a new parser component in the system. + /// + /// Associates a parser factory and descriptor with the parser's UUID. + /// If a parser with the same UUID is already registered, this will return an error. + /// + /// # Arguments + /// * `factory` – A function capable of instantiating the parser. + /// * `descriptor` – Metadata describing the parser's behavior and configuration. + /// + /// # Returns + /// * `Ok(())` – If the parser was successfully registered. + /// * `Err(NativeError)` – If a parser with the same UUID already exists. pub fn add_parser( &mut self, factory: ParserFactory, @@ -210,6 +246,18 @@ impl Register { Ok(()) } + /// Registers a new source component in the system. + /// + /// Associates a source factory and descriptor with the source's UUID. + /// If a source with the same UUID is already registered, this will return an error. + /// + /// # Arguments + /// * `factory` – A function capable of instantiating the source. + /// * `descriptor` – Metadata describing the source's behavior and configuration. + /// + /// # Returns + /// * `Ok(())` – If the source was successfully registered. + /// * `Err(NativeError)` – If a source with the same UUID already exists. pub fn add_source( &mut self, factory: SourceFactory, @@ -228,6 +276,16 @@ impl Register { Ok(()) } + /// Returns a list of parser identifiers compatible with the given session context. + /// + /// Filters registered parsers using their `is_compatible` logic and returns + /// only those that can be used for the given session action. + /// + /// # Arguments + /// * `origin` – The session action context to filter compatible parsers. + /// + /// # Returns + /// A list of identifiers (`Ident`) describing the matching parser components. pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { self.parsers .iter() @@ -241,6 +299,16 @@ impl Register { .collect() } + /// Returns a list of source identifiers compatible with the given session context. + /// + /// Filters registered sources using their `is_compatible` logic and returns + /// only those that can be used for the given session action. + /// + /// # Arguments + /// * `origin` – The session action context to filter compatible sources. + /// + /// # Returns + /// A list of identifiers (`Ident`) describing the matching source components. pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { self.sources .iter() diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index a9710732d4..08daf72c0d 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -24,14 +24,14 @@ use uuid::Uuid; /// /// * `tx_api` - An unbounded sender used for communicating with the API. It allows sending API-related messages /// or commands to manage components or respond to client requests. -pub struct ComponentsSession { +pub struct SessionRegister { tx_api: UnboundedSender, } -impl ComponentsSession { +impl SessionRegister { /// Creates a new components session. /// - /// This method initializes the `ComponentsSession` and returns a tuple containing the session instance + /// This method initializes the `SessionRegister` and returns a tuple containing the session instance /// and an unbounded receiver. The receiver acts as a feedback channel, allowing the delivery of /// asynchronous operation results to the client. /// diff --git a/application/apps/indexer/sources/src/api/mod.rs b/application/apps/indexer/sources/src/api/mod.rs index c811a011d0..0b6bc46fbf 100644 --- a/application/apps/indexer/sources/src/api/mod.rs +++ b/application/apps/indexer/sources/src/api/mod.rs @@ -2,6 +2,37 @@ use crate::*; use stypes::SessionAction; use thiserror::Error; +/// Defines the function type used to construct a source instance for a session. +/// +/// This factory function is stored in the [`Register`] and invoked when a new session +/// is initialized. It produces a source instance based on the provided session context +/// and configuration fields. +/// +/// The return value is a tuple: +/// - [`Sources`] – An enum-wrapped instance of the constructed source. +/// - `Option` – A human-readable label that describes the configured source in context. +/// +/// The key difference between this label and the one returned by [`CommonDescriptor`] is +/// that this one is **contextual**. While `CommonDescriptor` may return a generic name such +/// as `"Serial port connector"`, this factory may return a context-specific name like +/// `"Serial on /dev/tty001"` that reflects actual settings. +/// +/// # Arguments +/// +/// * `&SessionAction` – The session action context (e.g., user-triggered session creation). +/// * `&[stypes::Field]` – A list of configuration fields provided by the client or system. +/// +/// # Returns +/// +/// * `Ok(Some((Sources, Option)))` – A constructed source instance with an optional +/// context-specific name for display. +/// * `Ok(None)` – Indicates that no source should be created (e.g., conditionally skipped). +/// * `Err(NativeError)` – If source creation fails due to invalid data or internal error. +/// +/// # Errors +/// +/// Returns an `Err(NativeError)` if the source cannot be created due to misconfiguration, +/// validation failure, or runtime errors during instantiation. pub type SourceFactory = fn( &SessionAction, &[stypes::Field], diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 0f58236e6e..21a1246843 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -558,18 +558,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" -[[package]] -name = "components" -version = "0.1.0" -dependencies = [ - "log", - "stypes", - "thiserror 2.0.12", - "tokio", - "tokio-util", - "uuid", -] - [[package]] name = "concurrent-queue" version = "2.5.0" @@ -882,16 +870,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "definitions" -version = "0.1.0" -dependencies = [ - "etherparse", - "serde", - "stypes", - "thiserror 2.0.12", -] - [[package]] name = "derivative" version = "2.2.0" @@ -934,6 +912,18 @@ dependencies = [ "syn 2.0.102", ] +[[package]] +name = "descriptor" +version = "0.1.0" +dependencies = [ + "log", + "stypes", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "uuid", +] + [[package]] name = "destructure_traitobject" version = "0.2.0" @@ -2411,8 +2401,7 @@ version = "0.1.0" dependencies = [ "chrono", "chrono-tz", - "components", - "definitions", + "descriptor", "dlt-core", "file-tools", "lazy_static", @@ -2425,6 +2414,7 @@ dependencies = [ "someip-payload", "someip-tools", "stypes", + "thiserror 2.0.12", "tokio-util", "uuid", ] @@ -2519,8 +2509,7 @@ version = "0.1.0" dependencies = [ "anyhow", "blake3", - "components", - "definitions", + "descriptor", "dir_checksum", "dirs", "log", @@ -2600,7 +2589,6 @@ dependencies = [ "bincode", "bufread", "bytecount", - "definitions", "futures", "grep-regex", "grep-searcher", @@ -3056,9 +3044,8 @@ name = "session" version = "0.1.0" dependencies = [ "blake3", - "components", "crossbeam-channel", - "definitions", + "descriptor", "dirs", "dlt-core", "envvars", @@ -3203,8 +3190,7 @@ dependencies = [ "async-stream", "bufread", "bytes", - "components", - "definitions", + "descriptor", "envvars", "etherparse", "file-tools", diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 01d630b0c0..04af1efd97 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -1,12 +1,12 @@ use log::{debug, error}; use node_bindgen::{core::buffer::JSArrayBuffer, derive::node_bindgen}; -use session::components::ComponentsSession; +use session::register::SessionRegister; use std::{str::FromStr, thread}; use tokio::{runtime::Runtime, sync::oneshot}; use uuid::Uuid; struct Components { - session: Option, + session: Option, } #[node_bindgen] @@ -51,7 +51,7 @@ impl Components { let (tx_session, rx_session) = oneshot::channel(); thread::spawn(move || { rt.block_on(async { - match ComponentsSession::new().await { + match SessionRegister::new().await { Ok((session, mut rx_callback_events)) => { if tx_session.send(Some(session)).is_err() { error!("Cannot setup session instance"); From 028b2f120d55fa6cfd8fffd533732bbc751bc407 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Jul 2025 14:09:29 +0200 Subject: [PATCH 103/121] Restore dlt-tools tests --- .../apps/indexer/addons/dlt-tools/Cargo.toml | 3 + .../apps/indexer/addons/dlt-tools/src/lib.rs | 199 +++++++++--------- .../indexer/parsers/src/dlt/descriptor.rs | 7 +- .../apps/indexer/session/src/session.rs | 12 +- .../session/tests/snapshot_tests/mod.rs | 11 +- 5 files changed, 119 insertions(+), 113 deletions(-) diff --git a/application/apps/indexer/addons/dlt-tools/Cargo.toml b/application/apps/indexer/addons/dlt-tools/Cargo.toml index 98cf2f6ae7..09bcab0174 100644 --- a/application/apps/indexer/addons/dlt-tools/Cargo.toml +++ b/application/apps/indexer/addons/dlt-tools/Cargo.toml @@ -12,5 +12,8 @@ indexer_base = { path = "../../indexer_base" } parsers = { path = "../../parsers" } sources = { path = "../../sources" } processor = { path = "../../processor" } +stypes = { path = "../../stypes" } +session = { path = "../../session" } +uuid.workspace = true [dev-dependencies] diff --git a/application/apps/indexer/addons/dlt-tools/src/lib.rs b/application/apps/indexer/addons/dlt-tools/src/lib.rs index af5ac98a37..8ca2c6c081 100644 --- a/application/apps/indexer/addons/dlt-tools/src/lib.rs +++ b/application/apps/indexer/addons/dlt-tools/src/lib.rs @@ -10,83 +10,104 @@ // Dissemination of this information or reproduction of this material // is strictly forbidden unless prior written permission is obtained // from E.S.R.Labs. -extern crate indexer_base; - #[macro_use] extern crate log; -use dlt_core::filtering::DltFilterConfig; use parsers::api::*; -use parsers::dlt::DltParser; -use processor::producer::MessageProducer; -use sources::binary::raw::BinaryByteSource; +use processor::producer::{MessageProducer, MessageStreamItem}; +use session::session::Session; use std::{ + collections::HashMap, fs::File, - io::{BufReader, BufWriter, Write}, + io::{BufWriter, Write}, path::{Path, PathBuf}, }; +use stypes::{SessionAction, SessionSetup}; use tokio_util::sync::CancellationToken; +use uuid::Uuid; + +#[derive(Default)] +pub struct AttachmentsCollector { + pub attachments: Vec, + messages: usize, +} + +impl LogRecordsBuffer for AttachmentsCollector { + fn append(&mut self, record: LogRecordOutput<'_>) { + match record { + LogRecordOutput::Raw(..) + | LogRecordOutput::Message(..) + | LogRecordOutput::Columns(..) => { + self.messages += 1; + } + LogRecordOutput::Multiple(inner) => { + for rec in inner { + self.append(rec); + } + } + LogRecordOutput::Attachment(inner) => { + self.attachments.push(inner); + } + } + } + + async fn flush(&mut self) -> Result<(), stypes::NativeError> { + Ok(()) + } + + fn get_source_id(&self) -> u16 { + 0 + } +} pub async fn scan_dlt_ft( - input: PathBuf, - filter: Option, - with_storage_header: bool, + filename: PathBuf, + filters: Option>>, cancel: CancellationToken, ) -> Result, String> { - match File::open(input) { - Ok(input) => { - todo!("Not implemented") - // let reader = BufReader::new(&input); - // let source = BinaryByteSource::new(reader); - // let parser = DltParser::new( - // filter.map(|f| f.into()), - // None, - // None, - // None, - // with_storage_header, - // ); - // let mut producer = MessageProducer::new(parser, source); - - // let mut canceled = false; - - // let mut attachments = vec![]; - // loop { - // tokio::select! { - // // Check on events in current order ensuring cancel will be checked at first - // // as it's defined in the current unit tests. - // biased; - // _ = cancel.cancelled() => { - // debug!("scan canceled"); - // canceled = true; - // break; - // } - // items = producer.read_next_segment() => { - // match items { - // Some(items) => { - // for (_, item) in items { - // if let MessageStreamItem::Item(ParseYield::MessageAndAttachment((_msg, attachment))) = item { - // attachments.push(attachment.to_owned()); - // } else if let MessageStreamItem::Item(ParseYield::Attachment(attachment)) = item { - // attachments.push(attachment.to_owned()); - // } - // } - // } - // _ => { - // break; - // } - // } - // } - // } - // } - - // if canceled { - // return Ok(Vec::new()); - // } - - // Ok(attachments) + let setup = SessionSetup { + origin: SessionAction::File(filename), + parser: parsers::dlt::descriptor::get_default_options(None, filters), + source: sources::binary::raw::get_default_options(), + }; + let (session, receiver) = Session::new(Uuid::new_v4()) + .await + .map_err(|err| err.to_string())?; + let (_, source, parser) = session + .register + .setup(&setup) + .map_err(|err| err.to_string())?; + let mut collector = AttachmentsCollector::default(); + let mut producer = MessageProducer::new(parser, source, &mut collector); + let mut canceled = false; + loop { + tokio::select! { + // Check on events in current order ensuring cancel will be checked at first + // as it's defined in the current unit tests. + biased; + _ = cancel.cancelled() => { + debug!("scan canceled"); + canceled = true; + break; + } + item = producer.read_next_segment() => { + match item { + Some((_, MessageStreamItem::Done)) | None => { + break; + }, + Some(..) => { + continue; + }, + } + } } - Err(error) => Err(format!("failed to open file: {error}")), } + if canceled { + return Ok(Vec::new()); + } + // Keep receiver alive until here + drop(receiver); + Ok(collector.attachments) } pub fn extract_dlt_ft( @@ -128,14 +149,14 @@ mod tests { use super::*; use std::path::Path; - const DLT_FT_SAMPLE: &str = "../../../../../application/developing/resources/attachments.dlt"; + const DLT_FT_SAMPLE: &str = "../../../../developing/resources/attachments.dlt"; #[tokio::test] async fn test_scan_dlt_ft() { let input: PathBuf = Path::new(DLT_FT_SAMPLE).into(); let cancel = CancellationToken::new(); - match scan_dlt_ft(input, None, true, cancel).await { + match scan_dlt_ft(input, None, cancel).await { Ok(files) => { assert_eq!(files.len(), 3); assert_eq!("test1.txt", files.first().unwrap().name); @@ -155,7 +176,7 @@ mod tests { let cancel = CancellationToken::new(); cancel.cancel(); - match scan_dlt_ft(input, None, true, cancel).await { + match scan_dlt_ft(input, None, cancel).await { Ok(files) => { assert_eq!(files.len(), 0); } @@ -168,18 +189,10 @@ mod tests { #[tokio::test] async fn test_scan_dlt_ft_with_filter() { let input: PathBuf = Path::new(DLT_FT_SAMPLE).into(); - - let filter = DltFilterConfig { - min_log_level: None, - app_ids: None, - ecu_ids: Some(vec!["ecu2".to_string()]), - context_ids: None, - app_id_count: 0, - context_id_count: 0, - }; - + let mut filters = HashMap::new(); + filters.insert("ecu_ids".to_string(), vec!["ecu2".to_string()]); let cancel = CancellationToken::new(); - match scan_dlt_ft(input, Some(filter), true, cancel).await { + match scan_dlt_ft(input, Some(filters), cancel).await { Ok(files) => { assert_eq!(files.len(), 1); assert_eq!("test2.txt", files.first().unwrap().name); @@ -196,7 +209,7 @@ mod tests { let output = TempDir::new(); let cancel = CancellationToken::new(); - match scan_dlt_ft(input.clone(), None, true, cancel.clone()).await { + match scan_dlt_ft(input.clone(), None, cancel.clone()).await { Ok(files) => { match extract_dlt_ft( &output.dir, @@ -227,7 +240,7 @@ mod tests { let output = TempDir::new(); let cancel = CancellationToken::new(); - match scan_dlt_ft(input.clone(), None, true, cancel).await { + match scan_dlt_ft(input.clone(), None, cancel).await { Ok(files) => { let cancel = CancellationToken::new(); cancel.cancel(); @@ -250,18 +263,10 @@ mod tests { async fn test_extract_dlt_ft_with_filter() { let input: PathBuf = Path::new(DLT_FT_SAMPLE).into(); let output = TempDir::new(); - - let filter = DltFilterConfig { - min_log_level: None, - app_ids: None, - ecu_ids: Some(vec!["ecu2".to_string()]), - context_ids: None, - app_id_count: 0, - context_id_count: 0, - }; - + let mut filters = HashMap::new(); + filters.insert("ecu_ids".to_string(), vec!["ecu2".to_string()]); let cancel = CancellationToken::new(); - match scan_dlt_ft(input.clone(), Some(filter), true, cancel).await { + match scan_dlt_ft(input.clone(), Some(filters), cancel).await { Ok(files) => { let cancel = CancellationToken::new(); match extract_dlt_ft(&output.dir, FileExtractor::files_with_names(files), cancel) { @@ -287,7 +292,7 @@ mod tests { let output = TempDir::new(); let cancel = CancellationToken::new(); - match scan_dlt_ft(input.clone(), None, true, cancel).await { + match scan_dlt_ft(input.clone(), None, cancel).await { Ok(files) => { let cancel = CancellationToken::new(); match extract_dlt_ft( @@ -315,18 +320,10 @@ mod tests { async fn test_extract_dlt_ft_with_filtered_index() { let input: PathBuf = Path::new(DLT_FT_SAMPLE).into(); let output = TempDir::new(); - - let filter = DltFilterConfig { - min_log_level: None, - app_ids: None, - ecu_ids: Some(vec!["ecu2".to_string()]), - context_ids: None, - app_id_count: 0, - context_id_count: 0, - }; - + let mut filters = HashMap::new(); + filters.insert("ecu_ids".to_string(), vec!["ecu2".to_string()]); let cancel = CancellationToken::new(); - match scan_dlt_ft(input.clone(), Some(filter), true, cancel).await { + match scan_dlt_ft(input.clone(), Some(filters), cancel).await { Ok(files) => { let cancel = CancellationToken::new(); match extract_dlt_ft(&output.dir, FileExtractor::files_with_names(files), cancel) { diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 54f69db02b..e72d666e3e 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -332,7 +332,10 @@ fn u8_to_log_level(level: u8) -> Option { } } -pub fn get_default_options(fibex: Option>) -> ComponentOptions { +pub fn get_default_options( + fibex: Option>, + filters: Option>>, +) -> ComponentOptions { ComponentOptions { uuid: DLT_PARSER_UUID, fields: vec![ @@ -346,7 +349,7 @@ pub fn get_default_options(fibex: Option>) -> ComponentOptions { }, Field { id: FIELD_STATISTICS.to_owned(), - value: Value::KeyStrings(HashMap::new()), + value: Value::KeyStrings(filters.unwrap_or(HashMap::new())), }, ], } diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index da918d2ddd..ae76cbc814 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -29,7 +29,7 @@ pub struct Session { tx_operations: UnboundedSender, destroyed: CancellationToken, destroying: CancellationToken, - components: Arc, + pub register: Arc, pub state: SessionStateAPI, pub tracker: OperationTrackerAPI, } @@ -53,12 +53,12 @@ impl Session { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Register = Register::new(); + let mut register: Register = Register::new(); // Registre parsers - parsers_registration(&mut components) + parsers_registration(&mut register) .map_err(|err| stypes::ComputationError::NativeError(err))?; // Registre sources - sources_registration(&mut components) + sources_registration(&mut register) .map_err(|err| stypes::ComputationError::NativeError(err))?; // Create a session let session = Self { @@ -66,7 +66,7 @@ impl Session { tx_operations: tx_operations.clone(), destroyed: CancellationToken::new(), destroying: CancellationToken::new(), - components: Arc::new(components), + register: Arc::new(register), state: state_api.clone(), tracker: tracker_api.clone(), }; @@ -389,7 +389,7 @@ impl Session { self.tx_operations .send(Operation::new( operation_id, - operations::OperationKind::Observe(options, self.components.clone()), + operations::OperationKind::Observe(options, self.register.clone()), )) .map_err(|e| stypes::ComputationError::Communication(e.to_string())) } diff --git a/application/apps/indexer/session/tests/snapshot_tests/mod.rs b/application/apps/indexer/session/tests/snapshot_tests/mod.rs index 3ae96d336a..dd19cda891 100644 --- a/application/apps/indexer/session/tests/snapshot_tests/mod.rs +++ b/application/apps/indexer/session/tests/snapshot_tests/mod.rs @@ -27,7 +27,7 @@ async fn observe_dlt_session() { origin: SessionAction::File(PathBuf::from( "../../../developing/resources/attachments.dlt", )), - parser: parsers::dlt::descriptor::get_default_options(None), + parser: parsers::dlt::descriptor::get_default_options(None, None), source: sources::binary::raw::get_default_options(), }; let session_main_file = run_observe_session(setup.clone()).await; @@ -48,9 +48,12 @@ async fn observe_dlt_session() { async fn observe_dlt_with_someip_session() { let setup = SessionSetup { origin: SessionAction::File(PathBuf::from("../../../developing/resources/someip.dlt")), - parser: parsers::dlt::descriptor::get_default_options(Some(vec![PathBuf::from( - "../../../developing/resources/someip.xml", - )])), + parser: parsers::dlt::descriptor::get_default_options( + Some(vec![PathBuf::from( + "../../../developing/resources/someip.xml", + )]), + None, + ), source: sources::binary::raw::get_default_options(), }; let session_main_file = run_observe_session(setup.clone()).await; From 76d3ce1b869a11861e304d7892ceead301ccb951 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Jul 2025 14:19:35 +0200 Subject: [PATCH 104/121] Fix running of sources tests --- .../sources/src/binary/pcap/legacy/descriptor.rs | 4 ++-- .../apps/indexer/sources/src/socket/tcp/mod.rs | 16 ++++++++-------- .../apps/indexer/sources/src/socket/udp/mod.rs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 2912b57f53..94f682bff4 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -79,7 +79,7 @@ mod tests { binary::pcap::legacy::PcapLegacyByteSource, tests::general_source_reload_test, ByteSource, }; - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_read_one_message_from_pcap() { let _ = env_logger::try_init(); @@ -119,7 +119,7 @@ mod tests { assert_eq!(slice, udp_payload); } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_general_source_reload() { // This is part of the file "chipmunk/application/developing/resources". // In this test we just need enough bytes to call reload twice on it, and we will not diff --git a/application/apps/indexer/sources/src/socket/tcp/mod.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs index f5fb6303f4..4a1cd4ce0a 100644 --- a/application/apps/indexer/sources/src/socket/tcp/mod.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -202,7 +202,7 @@ mod tests { } } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_tcp_reload() -> Result<(), std::io::Error> { static SERVER: &str = "127.0.0.1:4000"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -231,7 +231,7 @@ mod tests { Ok(()) } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_general_source_reload() { static SERVER: &str = "127.0.0.1:4001"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -247,7 +247,7 @@ mod tests { /// Tests will send packets with fixed lengths while consuming /// half of the sent length, ensuring the source won't break. - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_source_buffer_overflow() { const SERVER: &str = "127.0.0.1:4002"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -280,7 +280,7 @@ mod tests { } } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn reconnect_no_msgs() { static SERVER: &str = "127.0.0.1:4003"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -319,7 +319,7 @@ mod tests { assert!(rec_res.is_ok()); } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn reconnect_with_state_msgs() { static SERVER: &str = "127.0.0.1:4004"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -391,7 +391,7 @@ mod tests { assert!(reconnect_res.is_ok()); } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn reconnect_fail() { static SERVER: &str = "127.0.0.1:4005"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -449,7 +449,7 @@ mod tests { /// Ensure load and reconnect functions are cancel safe by keep sending notifications /// in rapid interval while calling them. - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn load_reconnect_cancel_safe() { static SERVER: &str = "127.0.0.1:4006"; let listener = TcpListener::bind(&SERVER).await.unwrap(); @@ -516,7 +516,7 @@ mod tests { /// Ensure load and reconnect functions are cancel safe by keep calling it within a timeout /// function with rapid interval. - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn load_reconnect_cancel_safe_timeout() { static SERVER: &str = "127.0.0.1:4007"; let listener = TcpListener::bind(&SERVER).await.unwrap(); diff --git a/application/apps/indexer/sources/src/socket/udp/mod.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs index 23f7e4200a..b1b34c0dda 100644 --- a/application/apps/indexer/sources/src/socket/udp/mod.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -149,7 +149,7 @@ mod tests { static MESSAGES: &[&str] = &["one", "two", "three"]; - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_udp_reload() -> Result<(), UdpSourceError> { static SENDER: &str = "127.0.0.1:4000"; static RECEIVER: &str = "127.0.0.1:5000"; @@ -178,7 +178,7 @@ mod tests { Ok(()) } - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_general_source_reload() { static SENDER: &str = "127.0.0.1:4001"; static RECEIVER: &str = "127.0.0.1:5001"; @@ -205,7 +205,7 @@ mod tests { /// This test demonstrate that parsers which consume the bytes of one result at a /// time while miss parsing the whole bytes when the server isn't sending more data /// even that the buffer has bytes in it. - #[tokio::test] + #[tokio::test(flavor = "multi_thread")] async fn test_source_buffer_overflow() { const SENDER: &str = "127.0.0.1:4002"; const RECEIVER: &str = "127.0.0.1:5002"; From c9f385f05a7f64652cf824097f8a4ec1526ad5ff Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 23 Jul 2025 16:20:37 +0200 Subject: [PATCH 105/121] Update sources unit tests --- application/apps/indexer/sources/src/socket/tcp/mod.rs | 2 -- application/apps/indexer/sources/src/socket/udp/mod.rs | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/application/apps/indexer/sources/src/socket/tcp/mod.rs b/application/apps/indexer/sources/src/socket/tcp/mod.rs index 4a1cd4ce0a..5655b0d204 100644 --- a/application/apps/indexer/sources/src/socket/tcp/mod.rs +++ b/application/apps/indexer/sources/src/socket/tcp/mod.rs @@ -251,7 +251,6 @@ mod tests { async fn test_source_buffer_overflow() { const SERVER: &str = "127.0.0.1:4002"; let listener = TcpListener::bind(&SERVER).await.unwrap(); - const SENT_LEN: usize = MAX_DATAGRAM_SIZE; const CONSUME_LEN: usize = MAX_DATAGRAM_SIZE / 2; @@ -271,7 +270,6 @@ mod tests { }); let mut tcp_source = TcpSource::new(SERVER, None, None).unwrap(); - while let Ok(Some(info)) = tcp_source.load(None).await { if info.available_bytes == 0 { break; diff --git a/application/apps/indexer/sources/src/socket/udp/mod.rs b/application/apps/indexer/sources/src/socket/udp/mod.rs index b1b34c0dda..59dbbd511d 100644 --- a/application/apps/indexer/sources/src/socket/udp/mod.rs +++ b/application/apps/indexer/sources/src/socket/udp/mod.rs @@ -207,17 +207,16 @@ mod tests { /// even that the buffer has bytes in it. #[tokio::test(flavor = "multi_thread")] async fn test_source_buffer_overflow() { + // TODO: fix this test const SENDER: &str = "127.0.0.1:4002"; const RECEIVER: &str = "127.0.0.1:5002"; const SENT_LEN: usize = MAX_DATAGRAM_SIZE; const CONSUME_LEN: usize = MAX_DATAGRAM_SIZE / 2; - let send_socket = UdpSocket::bind(SENDER) .await .map_err(UdpSourceError::Io) .unwrap(); - // Spawn server in background. tokio::spawn(async move { let msg = [b'a'; SENT_LEN]; @@ -233,9 +232,7 @@ mod tests { total_sent += msg.len(); } }); - let mut udp_source = UdpSource::new(RECEIVER, vec![]).unwrap(); - while let Ok(Some(info)) = udp_source.load(None).await { if info.newly_loaded_bytes == 0 { println!( From 60463f189a4122638a6634076feeb8aebc484681 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 24 Jul 2025 11:33:11 +0200 Subject: [PATCH 106/121] Move bench tests to session --- application/apps/indexer/session/Cargo.toml | 41 +++++++++ .../benches/bench_utls.rs | 15 ++-- .../benches/dlt_producer.rs | 2 +- .../benches/mocks/mock_parser.rs | 87 +++++-------------- .../benches/mocks/mock_source.rs | 6 +- .../session/benches/mocks/mock_writer.rs | 54 ++++++++++++ .../{sources => session}/benches/mocks/mod.rs | 1 + .../benches/mocks_multi_parallel.rs | 2 +- .../benches/mocks_multi_producer.rs | 2 +- .../benches/mocks_once_parallel.rs | 2 +- .../benches/mocks_once_producer.rs | 2 +- .../benches/plugin_praser_producer.rs | 2 +- .../benches/someip_legacy_producer.rs | 3 +- .../benches/someip_producer.rs | 3 +- .../benches/text_producer.rs | 2 +- application/apps/indexer/sources/Cargo.toml | 36 -------- .../sources/src/socket/udp/descriptor.rs | 2 +- 17 files changed, 142 insertions(+), 120 deletions(-) rename application/apps/indexer/{sources => session}/benches/bench_utls.rs (93%) rename application/apps/indexer/{sources => session}/benches/dlt_producer.rs (97%) rename application/apps/indexer/{sources => session}/benches/mocks/mock_parser.rs (70%) rename application/apps/indexer/{sources => session}/benches/mocks/mock_source.rs (94%) create mode 100644 application/apps/indexer/session/benches/mocks/mock_writer.rs rename application/apps/indexer/{sources => session}/benches/mocks/mod.rs (66%) rename application/apps/indexer/{sources => session}/benches/mocks_multi_parallel.rs (98%) rename application/apps/indexer/{sources => session}/benches/mocks_multi_producer.rs (97%) rename application/apps/indexer/{sources => session}/benches/mocks_once_parallel.rs (98%) rename application/apps/indexer/{sources => session}/benches/mocks_once_producer.rs (97%) rename application/apps/indexer/{sources => session}/benches/plugin_praser_producer.rs (97%) rename application/apps/indexer/{sources => session}/benches/someip_legacy_producer.rs (94%) rename application/apps/indexer/{sources => session}/benches/someip_producer.rs (95%) rename application/apps/indexer/{sources => session}/benches/text_producer.rs (96%) diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index ff6c2ff0f2..a4ce49f00b 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -39,3 +39,44 @@ file-tools = { path = "../addons/file-tools" } lazy_static.workspace = true tempfile.workspace = true insta.workspace = true +env_logger.workspace = true +criterion = { workspace = true, features = ["async_tokio"] } +plugins_host = {path = "../plugins_host/"} +toml.workspace = true + +[[bench]] +name = "dlt_producer" +harness = false + +[[bench]] +name = "someip_producer" +harness = false + +[[bench]] +name = "someip_legacy_producer" +harness = false + +[[bench]] +name = "text_producer" +harness = false + +[[bench]] +name = "plugin_praser_producer" +harness = false + +[[bench]] +name = "mocks_once_producer" +harness = false + +[[bench]] +name = "mocks_once_parallel" +harness = false + +[[bench]] +name = "mocks_multi_producer" +harness = false + +[[bench]] +name = "mocks_multi_parallel" +harness = false + diff --git a/application/apps/indexer/sources/benches/bench_utls.rs b/application/apps/indexer/session/benches/bench_utls.rs similarity index 93% rename from application/apps/indexer/sources/benches/bench_utls.rs rename to application/apps/indexer/session/benches/bench_utls.rs index 0b43834f36..4d9853aa20 100644 --- a/application/apps/indexer/sources/benches/bench_utls.rs +++ b/application/apps/indexer/session/benches/bench_utls.rs @@ -10,8 +10,9 @@ use std::{ }; use criterion::Criterion; -use parsers::{LogMessage, MessageStreamItem}; -use sources::{binary::raw::BinaryByteSource, producer::MessageProducer}; +use parsers::{LogRecordOutput, LogRecordsBuffer, MessageStreamItem}; +use processor::producer::MessageProducer; +use sources::binary::raw::BinaryByteSource; pub const INPUT_SOURCE_ENV_VAR: &str = "CHIPMUNK_BENCH_SOURCE"; pub const CONFIG_ENV_VAR: &str = "CHIPMUNK_BENCH_CONFIG"; @@ -73,11 +74,13 @@ pub struct ProducerCounter { /// Run producer until the end converting messages into strings too, while counting all the /// different types of producer outputs to avoid unwanted compiler optimizations. -pub async fn run_producer(mut producer: MessageProducer) -> ProducerCounter +pub async fn run_producer<'a, P, S, B>( + mut producer: MessageProducer<'a, P, S, B>, +) -> ProducerCounter where - P: parsers::Parser, - B: sources::ByteSource, - T: LogMessage, + P: parsers::Parser, + S: sources::ByteSource, + B: LogRecordsBuffer, { let mut counter = ProducerCounter::default(); diff --git a/application/apps/indexer/sources/benches/dlt_producer.rs b/application/apps/indexer/session/benches/dlt_producer.rs similarity index 97% rename from application/apps/indexer/sources/benches/dlt_producer.rs rename to application/apps/indexer/session/benches/dlt_producer.rs index c803b6b0b7..00954b335e 100644 --- a/application/apps/indexer/sources/benches/dlt_producer.rs +++ b/application/apps/indexer/session/benches/dlt_producer.rs @@ -1,11 +1,11 @@ use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; +use processor::producer::MessageProducer; use std::path::PathBuf; use bench_utls::{ bench_standrad_config, create_binary_bytesource, get_config, read_binary, run_producer, }; use parsers::dlt::{self, DltParser}; -use sources::producer::MessageProducer; mod bench_utls; diff --git a/application/apps/indexer/sources/benches/mocks/mock_parser.rs b/application/apps/indexer/session/benches/mocks/mock_parser.rs similarity index 70% rename from application/apps/indexer/sources/benches/mocks/mock_parser.rs rename to application/apps/indexer/session/benches/mocks/mock_parser.rs index ac1dea36d0..2cd3dcdab2 100644 --- a/application/apps/indexer/sources/benches/mocks/mock_parser.rs +++ b/application/apps/indexer/session/benches/mocks/mock_parser.rs @@ -1,7 +1,6 @@ -use std::{fmt::Display, iter, marker::PhantomData}; +use std::{iter, marker::PhantomData}; -use parsers::{Attachment, LogMessage, Parser}; -use serde::Serialize; +use parsers::{Attachment, LogRecordOutput, ParseReturnIterator, Parser}; use std::hint::black_box; /// Empty type used as phantom data with mock parser to indicate that its [`Parser::parse()`] @@ -56,69 +55,44 @@ impl MockParser { } } -#[derive(Debug, Serialize)] -/// Return type of [`Parser::parse()`] method for [`MockParser`] -pub struct MockMessage { - content: String, -} - -impl Display for MockMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.content) - } -} - -impl LogMessage for MockMessage { - fn to_writer( - &self, - writer: &mut W, - ) -> Result { - let len = self.content.len(); - writer.write_all(self.content.as_bytes())?; - Ok(len) - } -} - impl MockParser { /// Method to replicate parse behavior with artificial if statements with black boxes to avoid /// any uncounted compiler optimization. #[inline(never)] - fn inner_parse( + fn inner_parse<'a>( counter: usize, max_count: usize, - input: &[u8], + input: &'a [u8], _timestamp: Option, - ) -> Result<(usize, Option>), parsers::Error> { + ) -> Result<(usize, Option>), parsers::ParserError> { // Return `Eof` Once the counter reaches max_count. if counter >= max_count { - const ERR: parsers::Error = parsers::Error::Eof; + const ERR: parsers::ParserError = parsers::ParserError::Eof; return Err(black_box(ERR)); } // Unnecessary check to convince the compiler that we are using the input. if input.is_empty() { - return Err(black_box(parsers::Error::Eof)); + return Err(black_box(parsers::ParserError::Eof)); } const MSG: &str = "msg"; // Unnecessary checks to convince the compiler that all return options are possible. if black_box(50) > black_box(60) { - Err(parsers::Error::Incomplete) + Err(parsers::ParserError::Incomplete) } else if black_box(50) > black_box(0) { // Only this value will be always returned if the calls counter still smaller than // the max value. Ok(( black_box(input.len()), - Some(parsers::ParseYield::Message(MockMessage { - content: black_box(MSG).into(), - })), + Some(LogRecordOutput::Message(black_box(MSG).into())), )) } else if black_box(20) > black_box(30) { Ok(( black_box(input.len()), - Some(parsers::ParseYield::Attachment(Attachment { + Some(LogRecordOutput::Attachment(Attachment { size: black_box(10), name: String::from(black_box(MSG)), data: Vec::new(), @@ -130,19 +104,17 @@ impl MockParser { } else { Ok(( black_box(input.len()), - Some(parsers::ParseYield::MessageAndAttachment(( - MockMessage { - content: black_box(MSG).into(), - }, - Attachment { + Some(LogRecordOutput::Multiple(vec![ + LogRecordOutput::Message(black_box(MSG).into()), + LogRecordOutput::Attachment(Attachment { size: black_box(10), name: String::from(black_box(MSG)), data: Vec::new(), messages: Vec::new(), created_date: None, modified_date: None, - }, - ))), + }), + ])), )) } } @@ -150,49 +122,35 @@ impl MockParser { // NOTE: Methods within trait implementation have inner non-async function that should never be // inline and the trait method should be always inline. This reduces the noise in the benchmarks. -impl Parser for MockParser { +impl Parser for MockParser { /// This will keep returning a valid item result withing an [`iter::once`] until the counter /// reaches max count then it will be return [`parsers::Error::Eof`] - fn parse( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result< - impl Iterator>)>, - parsers::Error, - > { + fn parse<'a>(&mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { self.counter += 1; let item = Self::inner_parse(self.counter, self.max_count, input, timestamp)?; - Ok(iter::once(item)) + Ok(Box::new(iter::once(item))) } } // NOTE: Methods within trait implementation have inner non-async function that should never be // inline and the trait method should be always inline. This reduces the noise in the benchmarks. -impl Parser for MockParser { +impl Parser for MockParser { /// This will keep returning an iterator of multiple valid items until the counter reaches max /// count then it will be return [`parsers::Error::Eof`] - fn parse( - &mut self, - input: &[u8], - timestamp: Option, - ) -> Result< - impl Iterator>)>, - parsers::Error, - > { + fn parse<'a>(&mut self, input: &'a [u8], timestamp: Option) -> ParseReturnIterator<'a> { self.counter += 1; if self.counter >= self.max_count { - const ERR: parsers::Error = parsers::Error::Eof; + const ERR: parsers::ParserError = parsers::ParserError::Eof; return Err(black_box(ERR)); } const REPEAT: usize = 10; let mut counter = 0; - let res = iter::from_fn(move || { + let iter = iter::from_fn(move || { counter += 1; if counter < black_box(REPEAT) { Self::inner_parse(self.counter, self.max_count, input, timestamp).ok() @@ -200,7 +158,6 @@ impl Parser for MockParser { None } }); - black_box(Ok(res)) } } diff --git a/application/apps/indexer/sources/benches/mocks/mock_source.rs b/application/apps/indexer/session/benches/mocks/mock_source.rs similarity index 94% rename from application/apps/indexer/sources/benches/mocks/mock_source.rs rename to application/apps/indexer/session/benches/mocks/mock_source.rs index 58744a20ab..d465ffd2e3 100644 --- a/application/apps/indexer/sources/benches/mocks/mock_source.rs +++ b/application/apps/indexer/session/benches/mocks/mock_source.rs @@ -61,10 +61,10 @@ impl ByteSource for MockByteSource { async fn load( &mut self, _filter: Option<&sources::SourceFilter>, - ) -> Result, sources::Error> { + ) -> Result, sources::SourceError> { #[inline(never)] - fn inner() -> Result, sources::Error> { - const AA: Result, sources::Error> = + fn inner() -> Result, sources::SourceError> { + const AA: Result, sources::SourceError> = Ok(Some(sources::ReloadInfo { available_bytes: 5, newly_loaded_bytes: 5, diff --git a/application/apps/indexer/session/benches/mocks/mock_writer.rs b/application/apps/indexer/session/benches/mocks/mock_writer.rs new file mode 100644 index 0000000000..ef7daad223 --- /dev/null +++ b/application/apps/indexer/session/benches/mocks/mock_writer.rs @@ -0,0 +1,54 @@ +use parsers::{Attachment, COLUMN_SENTINAL, LogRecordOutput, LogRecordsBuffer}; + +pub struct MockLogsBuffer { + messages: Vec, + attachments: Vec, + id: u16, +} + +impl MockLogsBuffer { + pub fn new(id: u16) -> Self { + Self { + messages: Vec::new(), + attachments: Vec::new(), + id, + } + } +} + +impl LogRecordsBuffer for MockLogsBuffer { + fn append(&mut self, record: LogRecordOutput<'_>) { + match record { + LogRecordOutput::Raw(inner) => { + self.messages.push( + inner + .iter() + .map(|b| format!("{:02X}", b)) + .collect::(), + ); + } + LogRecordOutput::Message(msg) => { + self.messages.push(msg.to_string()); + } + LogRecordOutput::Columns(inner) => { + self.messages.push(inner.join(&COLUMN_SENTINAL.to_string())); + } + LogRecordOutput::Multiple(inner) => { + for rec in inner { + self.append(rec); + } + } + LogRecordOutput::Attachment(inner) => { + self.attachments.push(inner); + } + } + } + + async fn flush(&mut self) -> Result<(), stypes::NativeError> { + Ok(()) + } + + fn get_source_id(&self) -> u16 { + self.id + } +} diff --git a/application/apps/indexer/sources/benches/mocks/mod.rs b/application/apps/indexer/session/benches/mocks/mod.rs similarity index 66% rename from application/apps/indexer/sources/benches/mocks/mod.rs rename to application/apps/indexer/session/benches/mocks/mod.rs index f03babe3a3..775430d4d8 100644 --- a/application/apps/indexer/sources/benches/mocks/mod.rs +++ b/application/apps/indexer/session/benches/mocks/mod.rs @@ -1,2 +1,3 @@ pub mod mock_parser; pub mod mock_source; +pub mod mock_writer; diff --git a/application/apps/indexer/sources/benches/mocks_multi_parallel.rs b/application/apps/indexer/session/benches/mocks_multi_parallel.rs similarity index 98% rename from application/apps/indexer/sources/benches/mocks_multi_parallel.rs rename to application/apps/indexer/session/benches/mocks_multi_parallel.rs index 9bead87b0c..84fd452ab4 100644 --- a/application/apps/indexer/sources/benches/mocks_multi_parallel.rs +++ b/application/apps/indexer/session/benches/mocks_multi_parallel.rs @@ -10,7 +10,7 @@ use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use bench_utls::{bench_standrad_config, run_producer}; use mocks::{mock_parser::MockParser, mock_source::MockByteSource}; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; mod bench_utls; mod mocks; diff --git a/application/apps/indexer/sources/benches/mocks_multi_producer.rs b/application/apps/indexer/session/benches/mocks_multi_producer.rs similarity index 97% rename from application/apps/indexer/sources/benches/mocks_multi_producer.rs rename to application/apps/indexer/session/benches/mocks_multi_producer.rs index ed38749318..072f2cb6e4 100644 --- a/application/apps/indexer/sources/benches/mocks_multi_producer.rs +++ b/application/apps/indexer/session/benches/mocks_multi_producer.rs @@ -6,7 +6,7 @@ use bench_utls::{bench_standrad_config, run_producer}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use mocks::{mock_parser::MockParser, mock_source::MockByteSource}; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; mod bench_utls; mod mocks; diff --git a/application/apps/indexer/sources/benches/mocks_once_parallel.rs b/application/apps/indexer/session/benches/mocks_once_parallel.rs similarity index 98% rename from application/apps/indexer/sources/benches/mocks_once_parallel.rs rename to application/apps/indexer/session/benches/mocks_once_parallel.rs index 373811f2fe..ab5018ab3e 100644 --- a/application/apps/indexer/sources/benches/mocks_once_parallel.rs +++ b/application/apps/indexer/session/benches/mocks_once_parallel.rs @@ -7,7 +7,7 @@ use bench_utls::{bench_standrad_config, run_producer}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use mocks::{mock_parser::MockParser, mock_source::MockByteSource}; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; use std::hint::black_box; mod bench_utls; mod mocks; diff --git a/application/apps/indexer/sources/benches/mocks_once_producer.rs b/application/apps/indexer/session/benches/mocks_once_producer.rs similarity index 97% rename from application/apps/indexer/sources/benches/mocks_once_producer.rs rename to application/apps/indexer/session/benches/mocks_once_producer.rs index 6ab489640a..ce4591e147 100644 --- a/application/apps/indexer/sources/benches/mocks_once_producer.rs +++ b/application/apps/indexer/session/benches/mocks_once_producer.rs @@ -6,7 +6,7 @@ use bench_utls::{bench_standrad_config, run_producer}; use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use mocks::{mock_parser::MockParser, mock_source::MockByteSource}; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; mod bench_utls; mod mocks; diff --git a/application/apps/indexer/sources/benches/plugin_praser_producer.rs b/application/apps/indexer/session/benches/plugin_praser_producer.rs similarity index 97% rename from application/apps/indexer/sources/benches/plugin_praser_producer.rs rename to application/apps/indexer/session/benches/plugin_praser_producer.rs index 5abf29cf00..20ac6f47f8 100644 --- a/application/apps/indexer/sources/benches/plugin_praser_producer.rs +++ b/application/apps/indexer/session/benches/plugin_praser_producer.rs @@ -3,7 +3,7 @@ use plugins_host::PluginsParser; use std::hint::black_box; use bench_utls::{bench_standrad_config, create_binary_bytesource, read_binary, run_producer}; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; mod bench_utls; diff --git a/application/apps/indexer/sources/benches/someip_legacy_producer.rs b/application/apps/indexer/session/benches/someip_legacy_producer.rs similarity index 94% rename from application/apps/indexer/sources/benches/someip_legacy_producer.rs rename to application/apps/indexer/session/benches/someip_legacy_producer.rs index a7f42387c7..32fc449536 100644 --- a/application/apps/indexer/sources/benches/someip_legacy_producer.rs +++ b/application/apps/indexer/session/benches/someip_legacy_producer.rs @@ -5,7 +5,8 @@ use std::{io::Cursor, path::PathBuf}; use bench_utls::{bench_standrad_config, get_config, read_binary, run_producer}; use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use parsers::someip::SomeipParser; -use sources::{binary::pcap::legacy::PcapLegacyByteSource, producer::MessageProducer}; +use processor::producer::MessageProducer; +use sources::binary::pcap::legacy::PcapLegacyByteSource; /// This benchmark covers parsing from SomeIP file using [`PcapLegacyByteSource`] byte source. /// It supports providing the path for a fibex file as additional configuration. diff --git a/application/apps/indexer/sources/benches/someip_producer.rs b/application/apps/indexer/session/benches/someip_producer.rs similarity index 95% rename from application/apps/indexer/sources/benches/someip_producer.rs rename to application/apps/indexer/session/benches/someip_producer.rs index 4e94abbc33..c0126dfd91 100644 --- a/application/apps/indexer/sources/benches/someip_producer.rs +++ b/application/apps/indexer/session/benches/someip_producer.rs @@ -3,7 +3,8 @@ use std::{io::Cursor, path::PathBuf}; use bench_utls::{bench_standrad_config, get_config, read_binary, run_producer}; use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use parsers::someip::SomeipParser; -use sources::{binary::pcap::ng::PcapngByteSource, producer::MessageProducer}; +use processor::producer::MessageProducer; +use sources::binary::pcap::ng::PcapngByteSource; mod bench_utls; diff --git a/application/apps/indexer/sources/benches/text_producer.rs b/application/apps/indexer/session/benches/text_producer.rs similarity index 96% rename from application/apps/indexer/sources/benches/text_producer.rs rename to application/apps/indexer/session/benches/text_producer.rs index 74f764184a..946fefd9fd 100644 --- a/application/apps/indexer/sources/benches/text_producer.rs +++ b/application/apps/indexer/session/benches/text_producer.rs @@ -2,7 +2,7 @@ use criterion::{BatchSize, Criterion, criterion_group, criterion_main}; use bench_utls::{bench_standrad_config, create_binary_bytesource, read_binary, run_producer}; use parsers::text::StringTokenizer; -use sources::producer::MessageProducer; +use processor::producer::MessageProducer; mod bench_utls; diff --git a/application/apps/indexer/sources/Cargo.toml b/application/apps/indexer/sources/Cargo.toml index 2c1a222491..5f642f985e 100644 --- a/application/apps/indexer/sources/Cargo.toml +++ b/application/apps/indexer/sources/Cargo.toml @@ -34,43 +34,7 @@ descriptor = { path = "../descriptor" } [dev-dependencies] env_logger.workspace = true -criterion = { workspace = true, features = ["async_tokio"] } plugins_host = {path = "../plugins_host/"} toml.workspace = true -[[bench]] -name = "dlt_producer" -harness = false - -[[bench]] -name = "someip_producer" -harness = false - -[[bench]] -name = "someip_legacy_producer" -harness = false - -[[bench]] -name = "text_producer" -harness = false - -[[bench]] -name = "plugin_praser_producer" -harness = false - -[[bench]] -name = "mocks_once_producer" -harness = false - -[[bench]] -name = "mocks_once_parallel" -harness = false - -[[bench]] -name = "mocks_multi_producer" -harness = false - -[[bench]] -name = "mocks_multi_parallel" -harness = false diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index e026ab7502..7655e3ac27 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -18,7 +18,7 @@ const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; pub struct Descriptor {} pub fn factory( - origin: &SessionAction, + _origin: &SessionAction, options: &[Field], ) -> Result)>, NativeError> { let addr: String = options From 435cbb499a06f73c34245592a50001ace3b25a56 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 24 Jul 2025 16:38:58 +0200 Subject: [PATCH 107/121] Move register to own crate --- application/apps/indexer/Cargo.toml | 1 + application/apps/indexer/register/Cargo.toml | 16 + application/apps/indexer/register/src/lib.rs | 371 ++++++++++++++++++ application/apps/indexer/session/Cargo.toml | 1 + .../session/src/handlers/observe/mod.rs | 2 +- .../apps/indexer/session/src/operations.rs | 2 +- .../apps/indexer/session/src/register/api.rs | 366 ----------------- .../apps/indexer/session/src/register/mod.rs | 1 + .../apps/indexer/session/src/session.rs | 3 +- 9 files changed, 394 insertions(+), 369 deletions(-) create mode 100644 application/apps/indexer/register/Cargo.toml create mode 100644 application/apps/indexer/register/src/lib.rs diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index b6a36ab5d7..eae850ff05 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -8,6 +8,7 @@ members = [ "addons/text_grep", "addons/bufread", "descriptor", + "register", "indexer_base", "merging", "parsers", diff --git a/application/apps/indexer/register/Cargo.toml b/application/apps/indexer/register/Cargo.toml new file mode 100644 index 0000000000..ec9ee5d44e --- /dev/null +++ b/application/apps/indexer/register/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "register" +version = "0.1.0" +edition = "2021" + +[dependencies] +thiserror.workspace = true +uuid = { workspace = true , features = ["serde", "v4"] } +tokio-util.workspace = true +tokio = { workspace = true , features = ["full"] } +log.workspace = true + +stypes = { path = "../stypes", features=["rustcore"] } +descriptor = { path = "../descriptor" } +parsers = { path = "../parsers" } +sources = { path = "../sources" } \ No newline at end of file diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs new file mode 100644 index 0000000000..8970bbc2a7 --- /dev/null +++ b/application/apps/indexer/register/src/lib.rs @@ -0,0 +1,371 @@ +use std::{collections::HashMap, fmt}; + +use stypes::SessionDescriptor; +use tokio_util::sync::CancellationToken; +use uuid::Uuid; + +use descriptor::*; +use parsers::{api::*, Parsers}; +use sources::{api::*, Sources}; + +/// Registry of all available parsers and sources in the system. +/// +/// This struct acts as a central repository that holds references to all registered +/// parser and source components, along with their factory functions and descriptors. +/// +/// During session setup, the appropriate factory functions and metadata can be +/// retrieved using UUIDs to create concrete parser and source instances, validate +/// configuration fields, and present component metadata to the user interface. +pub struct Register { + /// A map of source component UUIDs to their factory functions and descriptors. + /// + /// Each entry contains: + /// - a [`SourceFactory`] used to instantiate the component, + /// - a boxed [`SourceDescriptor`] that describes its capabilities and configuration. + sources: HashMap)>, + + /// A map of parser component UUIDs to their factory functions and descriptors. + /// + /// Each entry contains: + /// - a [`ParserFactory`] used to instantiate the component, + /// - a boxed [`ParserDescriptor`] that describes its capabilities and configuration. + parsers: HashMap)>, +} + +impl Register { + /// Creates an empty `Register` with no registered parsers or sources. + /// + /// This is typically used at system startup before components are added via + /// [`add_parser`] or [`add_source`]. + pub fn new() -> Register { + Self { + sources: HashMap::new(), + parsers: HashMap::new(), + } + } + + /// Registers a new parser component in the system. + /// + /// Associates a parser factory and descriptor with the parser's UUID. + /// If a parser with the same UUID is already registered, this will return an error. + /// + /// # Arguments + /// * `factory` – A function capable of instantiating the parser. + /// * `descriptor` – Metadata describing the parser's behavior and configuration. + /// + /// # Returns + /// * `Ok(())` – If the parser was successfully registered. + /// * `Err(NativeError)` – If a parser with the same UUID already exists. + pub fn add_parser( + &mut self, + factory: ParserFactory, + descriptor: D, + ) -> Result<(), stypes::NativeError> { + let ident = descriptor.ident(); + if self.parsers.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.parsers + .insert(ident.uuid, (factory, Box::new(descriptor))); + Ok(()) + } + + /// Registers a new source component in the system. + /// + /// Associates a source factory and descriptor with the source's UUID. + /// If a source with the same UUID is already registered, this will return an error. + /// + /// # Arguments + /// * `factory` – A function capable of instantiating the source. + /// * `descriptor` – Metadata describing the source's behavior and configuration. + /// + /// # Returns + /// * `Ok(())` – If the source was successfully registered. + /// * `Err(NativeError)` – If a source with the same UUID already exists. + pub fn add_source( + &mut self, + factory: SourceFactory, + descriptor: D, + ) -> Result<(), stypes::NativeError> { + let ident: stypes::Ident = descriptor.ident(); + if self.sources.contains_key(&ident.uuid) { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), + }); + } + self.sources + .insert(ident.uuid, (factory, Box::new(descriptor))); + Ok(()) + } + + /// Returns a list of parser identifiers compatible with the given session context. + /// + /// Filters registered parsers using their `is_compatible` logic and returns + /// only those that can be used for the given session action. + /// + /// # Arguments + /// * `origin` – The session action context to filter compatible parsers. + /// + /// # Returns + /// A list of identifiers (`Ident`) describing the matching parser components. + pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { + self.parsers + .iter() + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + }) + .collect() + } + + /// Returns a list of source identifiers compatible with the given session context. + /// + /// Filters registered sources using their `is_compatible` logic and returns + /// only those that can be used for the given session action. + /// + /// # Arguments + /// * `origin` – The session action context to filter compatible sources. + /// + /// # Returns + /// A list of identifiers (`Ident`) describing the matching source components. + pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { + self.sources + .iter() + .filter_map(|(_, (_, desc))| { + if desc.is_compatible(&origin) { + Some(desc.ident()) + } else { + None + } + }) + .collect() + } + + /// Returns configuration schemas for the specified component UUIDs. + /// + /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI + /// and optionally define lazy-loaded settings. + /// + /// # Arguments + /// * `origin` – Session action describing the context in which components will be used. + /// * `targets` – A list of component UUIDs to fetch configuration for. + /// + /// # Returns + /// * `Ok(HashMap)` – Mapping from component UUIDs to their configuration schemes. + /// * `Err(NativeError)` – If any of the UUIDs are not found in the registry. + /// + /// # Errors + /// Returns a configuration error with a list of UUIDs that were not found in the registry. + pub fn get_options( + &self, + origin: stypes::SessionAction, + mut targets: Vec, + ) -> Result, stypes::NativeError> { + let descriptors = self + .descriptors() + .into_iter() + .filter_map(|(uuid, desc)| { + if targets.contains(uuid) { + targets.retain(|v| v != uuid); + Some(desc) + } else { + None + } + }) + .collect::>(); + if !targets.is_empty() { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Fail to find: {}", + targets + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", ") + )), + }); + } + let mut list: HashMap = HashMap::new(); + for desc in descriptors.into_iter() { + let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); + if options.has_lazy() { + let cancel = CancellationToken::new(); + options.set_lazy( + desc.ident(), + desc.lazy_fields_getter(origin.clone(), cancel.clone()), + &cancel, + ); + } + list.insert(desc.ident().uuid, options); + } + Ok(list) + } + + /// Returns the client-side render type for the specified component UUID. + /// + /// While typically associated with parsers (since they define the data format), + /// the system allows any component to provide rendering output hints. + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Ok(Some(OutputRender))` – If the parser provides render information. + /// * `Ok(None)` – If no render is defined. + /// * `Err(NativeError)` – If the UUID is not found or the component is not a parser. + /// + /// # Errors + /// Returns a configuration error if the component is missing or not a parser. + pub fn get_output_render( + &self, + uuid: &Uuid, + ) -> Result, stypes::NativeError> { + let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {uuid}")), + })?; + Ok(descriptor.get_render()) + } + + /// Returns the Ident of component (parser or source). + /// + /// # Arguments + /// * `uuid` – The UUID of the target component. + /// + /// # Returns + /// * `Some(Ident)` – If the parser or source has been found. + /// * `None` – If no component is defined. + pub fn get_ident(&self, uuid: &Uuid) -> Option { + self.descriptors().into_iter().find_map(|(inner, desc)| { + if uuid == inner { + Some(desc.ident()) + } else { + None + } + }) + } + + /// Validates the configuration fields for a specified component within a session context. + /// + /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete + /// configurations before actual component instantiation. + /// + /// # Arguments + /// * `origin` – The session action context. + /// * `target` – The UUID of the component to validate. + /// * `fields` – A list of fields provided by the client. + /// + /// # Returns + /// * `Ok(HashMap)` – Empty if validation passed; otherwise, maps field UUIDs to error messages. + /// * `Err(NativeError)` – If the component UUID is not registered. + /// + /// # Errors + /// Returns a configuration error if the component is not found. + pub fn validate( + &self, + origin: &stypes::SessionAction, + target: &Uuid, + fields: &[stypes::Field], + ) -> Result, stypes::NativeError> { + let (_, descriptor) = self + .descriptors() + .into_iter() + .find(|(uuid, _)| *uuid == target) + .ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {target}")), + })?; + descriptor.validate(origin, fields) + } + + /// Attempts to create instances of a parser and source component based on the session setup. + /// + /// This method creates fully initialized, ready-to-use instances of parser and source + /// based on the provided configuration. It also returns a `SessionDescriptor` + /// that contains display identifiers for tracking the session state. + /// + /// # Arguments + /// * `options` – Session setup data including parser/source UUIDs and user-defined field values. + /// + /// # Returns + /// * `Ok((SessionDescriptor, S, P))` – A tuple with session metadata, the source instance, and the parser instance. + /// * `Err(NativeError)` – If any of the components cannot be found or fail to initialize. + /// + /// # Errors + /// Returns configuration errors if components are missing or initialization fails. + pub fn setup( + &self, + options: &stypes::SessionSetup, + ) -> Result<(SessionDescriptor, Sources, Parsers), stypes::NativeError> { + let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) + else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find parser {}", options.parser.uuid)), + }); + }; + let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) + else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find source {}", options.source.uuid)), + }); + }; + let mut descriptor = SessionDescriptor::new( + source_descriptor.bound_ident(&options.origin, &options.source.fields), + parser_descriptor.bound_ident(&options.origin, &options.parser.fields), + ); + let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init parser {}", options.parser.uuid)), + }); + }; + descriptor.set_parser_desc(desc); + let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to init source {}", options.source.uuid)), + }); + }; + descriptor.set_source_desc(desc); + Ok((descriptor, source, parser)) + } + + fn descriptors(&self) -> Vec<(&Uuid, &dyn CommonDescriptor)> { + [ + self.sources + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + self.parsers + .iter() + .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .collect::>(), + ] + .concat() + } +} + +impl fmt::Debug for Register { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Register") + } +} diff --git a/application/apps/indexer/session/Cargo.toml b/application/apps/indexer/session/Cargo.toml index a4ce49f00b..57fb1ee82b 100644 --- a/application/apps/indexer/session/Cargo.toml +++ b/application/apps/indexer/session/Cargo.toml @@ -33,6 +33,7 @@ parsers = { path = "../parsers" } processor = { path = "../processor" } sources = { path = "../sources" } merging = { path = "../merging" } +register = { path = "../register" } file-tools = { path = "../addons/file-tools" } [dev-dependencies] diff --git a/application/apps/indexer/session/src/handlers/observe/mod.rs b/application/apps/indexer/session/src/handlers/observe/mod.rs index ebeadfa26f..d954eef197 100644 --- a/application/apps/indexer/session/src/handlers/observe/mod.rs +++ b/application/apps/indexer/session/src/handlers/observe/mod.rs @@ -3,10 +3,10 @@ mod session; use crate::{ operations::{OperationAPI, OperationResult}, - register::Register, state::SessionStateAPI, }; use processor::producer::{MessageProducer, sde::*}; +use register::Register; use std::sync::Arc; use stypes::{SessionAction, SessionSetup}; diff --git a/application/apps/indexer/session/src/operations.rs b/application/apps/indexer/session/src/operations.rs index 9429ce1050..35ebd193d5 100644 --- a/application/apps/indexer/session/src/operations.rs +++ b/application/apps/indexer/session/src/operations.rs @@ -1,9 +1,9 @@ -use crate::register::Register; use crate::{handlers, state::SessionStateAPI, tracker::OperationTrackerAPI}; use log::{debug, error, warn}; use merging::merger::FileMergeOptions; use processor::producer::sde::{SdeReceiver, SdeSender}; use processor::search::filter::SearchFilter; +use register::Register; use serde::Serialize; use std::{ ops::RangeInclusive, diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index 356803567b..cd9aaae522 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -175,369 +175,3 @@ pub enum Api { /// Removes the plugin with the given directory path. RemovePlugin(String, oneshot::Sender>), } - -use descriptor::*; -use parsers::{Parsers, api::*}; -use sources::{Sources, api::*}; - -/// Registry of all available parsers and sources in the system. -/// -/// This struct acts as a central repository that holds references to all registered -/// parser and source components, along with their factory functions and descriptors. -/// -/// During session setup, the appropriate factory functions and metadata can be -/// retrieved using UUIDs to create concrete parser and source instances, validate -/// configuration fields, and present component metadata to the user interface. -pub struct Register { - /// A map of source component UUIDs to their factory functions and descriptors. - /// - /// Each entry contains: - /// - a [`SourceFactory`] used to instantiate the component, - /// - a boxed [`SourceDescriptor`] that describes its capabilities and configuration. - sources: HashMap)>, - - /// A map of parser component UUIDs to their factory functions and descriptors. - /// - /// Each entry contains: - /// - a [`ParserFactory`] used to instantiate the component, - /// - a boxed [`ParserDescriptor`] that describes its capabilities and configuration. - parsers: HashMap)>, -} - -impl Register { - /// Creates an empty `Register` with no registered parsers or sources. - /// - /// This is typically used at system startup before components are added via - /// [`add_parser`] or [`add_source`]. - pub fn new() -> Register { - Self { - sources: HashMap::new(), - parsers: HashMap::new(), - } - } - - /// Registers a new parser component in the system. - /// - /// Associates a parser factory and descriptor with the parser's UUID. - /// If a parser with the same UUID is already registered, this will return an error. - /// - /// # Arguments - /// * `factory` – A function capable of instantiating the parser. - /// * `descriptor` – Metadata describing the parser's behavior and configuration. - /// - /// # Returns - /// * `Ok(())` – If the parser was successfully registered. - /// * `Err(NativeError)` – If a parser with the same UUID already exists. - pub fn add_parser( - &mut self, - factory: ParserFactory, - descriptor: D, - ) -> Result<(), stypes::NativeError> { - let ident = descriptor.ident(); - if self.parsers.contains_key(&ident.uuid) { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), - }); - } - self.parsers - .insert(ident.uuid, (factory, Box::new(descriptor))); - Ok(()) - } - - /// Registers a new source component in the system. - /// - /// Associates a source factory and descriptor with the source's UUID. - /// If a source with the same UUID is already registered, this will return an error. - /// - /// # Arguments - /// * `factory` – A function capable of instantiating the source. - /// * `descriptor` – Metadata describing the source's behavior and configuration. - /// - /// # Returns - /// * `Ok(())` – If the source was successfully registered. - /// * `Err(NativeError)` – If a source with the same UUID already exists. - pub fn add_source( - &mut self, - factory: SourceFactory, - descriptor: D, - ) -> Result<(), stypes::NativeError> { - let ident: stypes::Ident = descriptor.ident(); - if self.sources.contains_key(&ident.uuid) { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), - }); - } - self.sources - .insert(ident.uuid, (factory, Box::new(descriptor))); - Ok(()) - } - - /// Returns a list of parser identifiers compatible with the given session context. - /// - /// Filters registered parsers using their `is_compatible` logic and returns - /// only those that can be used for the given session action. - /// - /// # Arguments - /// * `origin` – The session action context to filter compatible parsers. - /// - /// # Returns - /// A list of identifiers (`Ident`) describing the matching parser components. - pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { - self.parsers - .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - }) - .collect() - } - - /// Returns a list of source identifiers compatible with the given session context. - /// - /// Filters registered sources using their `is_compatible` logic and returns - /// only those that can be used for the given session action. - /// - /// # Arguments - /// * `origin` – The session action context to filter compatible sources. - /// - /// # Returns - /// A list of identifiers (`Ident`) describing the matching source components. - pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { - self.sources - .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) - } else { - None - } - }) - .collect() - } - - /// Returns configuration schemas for the specified component UUIDs. - /// - /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI - /// and optionally define lazy-loaded settings. - /// - /// # Arguments - /// * `origin` – Session action describing the context in which components will be used. - /// * `targets` – A list of component UUIDs to fetch configuration for. - /// - /// # Returns - /// * `Ok(HashMap)` – Mapping from component UUIDs to their configuration schemes. - /// * `Err(NativeError)` – If any of the UUIDs are not found in the registry. - /// - /// # Errors - /// Returns a configuration error with a list of UUIDs that were not found in the registry. - pub fn get_options( - &self, - origin: stypes::SessionAction, - mut targets: Vec, - ) -> Result, stypes::NativeError> { - let descriptors = self - .descriptors() - .into_iter() - .filter_map(|(uuid, desc)| { - if targets.contains(uuid) { - targets.retain(|v| v != uuid); - Some(desc) - } else { - None - } - }) - .collect::>(); - if !targets.is_empty() { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!( - "Fail to find: {}", - targets - .iter() - .map(|v| v.to_string()) - .collect::>() - .join(", ") - )), - }); - } - let mut list: HashMap = HashMap::new(); - for desc in descriptors.into_iter() { - let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); - if options.has_lazy() { - let cancel = CancellationToken::new(); - options.set_lazy( - desc.ident(), - desc.lazy_fields_getter(origin.clone(), cancel.clone()), - &cancel, - ); - } - list.insert(desc.ident().uuid, options); - } - Ok(list) - } - - /// Returns the client-side render type for the specified component UUID. - /// - /// While typically associated with parsers (since they define the data format), - /// the system allows any component to provide rendering output hints. - /// - /// # Arguments - /// * `uuid` – The UUID of the target component. - /// - /// # Returns - /// * `Ok(Some(OutputRender))` – If the parser provides render information. - /// * `Ok(None)` – If no render is defined. - /// * `Err(NativeError)` – If the UUID is not found or the component is not a parser. - /// - /// # Errors - /// Returns a configuration error if the component is missing or not a parser. - pub fn get_output_render( - &self, - uuid: &Uuid, - ) -> Result, stypes::NativeError> { - let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find component {uuid}")), - })?; - Ok(descriptor.get_render()) - } - - /// Returns the Ident of component (parser or source). - /// - /// # Arguments - /// * `uuid` – The UUID of the target component. - /// - /// # Returns - /// * `Some(Ident)` – If the parser or source has been found. - /// * `None` – If no component is defined. - pub fn get_ident(&self, uuid: &Uuid) -> Option { - self.descriptors().into_iter().find_map(|(inner, desc)| { - if uuid == inner { - Some(desc.ident()) - } else { - None - } - }) - } - - /// Validates the configuration fields for a specified component within a session context. - /// - /// Checks each field against the component’s declared rules. This helps catch invalid or incomplete - /// configurations before actual component instantiation. - /// - /// # Arguments - /// * `origin` – The session action context. - /// * `target` – The UUID of the component to validate. - /// * `fields` – A list of fields provided by the client. - /// - /// # Returns - /// * `Ok(HashMap)` – Empty if validation passed; otherwise, maps field UUIDs to error messages. - /// * `Err(NativeError)` – If the component UUID is not registered. - /// - /// # Errors - /// Returns a configuration error if the component is not found. - pub fn validate( - &self, - origin: &stypes::SessionAction, - target: &Uuid, - fields: &[stypes::Field], - ) -> Result, stypes::NativeError> { - let (_, descriptor) = self - .descriptors() - .into_iter() - .find(|(uuid, _)| *uuid == target) - .ok_or(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find component {target}")), - })?; - descriptor.validate(origin, fields) - } - - /// Attempts to create instances of a parser and source component based on the session setup. - /// - /// This method creates fully initialized, ready-to-use instances of parser and source - /// based on the provided configuration. It also returns a `SessionDescriptor` - /// that contains display identifiers for tracking the session state. - /// - /// # Arguments - /// * `options` – Session setup data including parser/source UUIDs and user-defined field values. - /// - /// # Returns - /// * `Ok((SessionDescriptor, S, P))` – A tuple with session metadata, the source instance, and the parser instance. - /// * `Err(NativeError)` – If any of the components cannot be found or fail to initialize. - /// - /// # Errors - /// Returns configuration errors if components are missing or initialization fails. - pub fn setup( - &self, - options: &stypes::SessionSetup, - ) -> Result<(SessionDescriptor, Sources, Parsers), stypes::NativeError> { - let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) - else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find parser {}", options.parser.uuid)), - }); - }; - let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) - else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to find source {}", options.source.uuid)), - }); - }; - let mut descriptor = SessionDescriptor::new( - source_descriptor.bound_ident(&options.origin, &options.source.fields), - parser_descriptor.bound_ident(&options.origin, &options.parser.fields), - ); - let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to init parser {}", options.parser.uuid)), - }); - }; - descriptor.set_parser_desc(desc); - let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some(format!("Fail to init source {}", options.source.uuid)), - }); - }; - descriptor.set_source_desc(desc); - Ok((descriptor, source, parser)) - } - - fn descriptors(&self) -> Vec<(&Uuid, &dyn CommonDescriptor)> { - [ - self.sources - .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) - .collect::>(), - self.parsers - .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) - .collect::>(), - ] - .concat() - } -} - -impl fmt::Debug for Register { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Register") - } -} diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index 08daf72c0d..18c2a7e84b 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -4,6 +4,7 @@ mod plugins; use api::*; use descriptor::{LazyLoadingResult, LazyLoadingTaskMeta}; use log::{debug, error}; +use register::Register; use std::{collections::HashMap, sync::Arc}; use tokio::{ sync::{ diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index ae76cbc814..cfcf2e358b 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -1,12 +1,13 @@ use crate::{ operations::{self, Operation}, - register::{Register, parsers_registration, sources_registration}, + register::{parsers_registration, sources_registration}, state::{self, IndexesMode, SessionStateAPI}, tracker::{self, OperationTrackerAPI}, }; use futures::Future; use log::{debug, error, warn}; use processor::{grabber::LineRange, search::filter::SearchFilter}; +use register::Register; use std::{ops::RangeInclusive, path::PathBuf, sync::Arc}; use tokio::{ join, From f7ee07fa0c2fb49cbe2a14f694838b0da7437014 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 24 Jul 2025 16:49:34 +0200 Subject: [PATCH 108/121] Adopt plugins to updated register --- application/apps/indexer/parsers/src/lib.rs | 2 +- .../apps/indexer/plugins_host/Cargo.toml | 1 + .../src/plugins_manager/descriptor.rs | 206 +++++++++--------- .../plugins_host/src/plugins_manager/mod.rs | 13 +- 4 files changed, 112 insertions(+), 110 deletions(-) diff --git a/application/apps/indexer/parsers/src/lib.rs b/application/apps/indexer/parsers/src/lib.rs index 404b369267..e0e474d866 100644 --- a/application/apps/indexer/parsers/src/lib.rs +++ b/application/apps/indexer/parsers/src/lib.rs @@ -16,7 +16,7 @@ pub enum Parsers { // NOTE: We can't reference plugins parser directly because of circular // references between parsers and plugins_host library. // TODO AAZ: This is a workaround until we find a proper solution. - Plugin(Box), + Plugin(Box), } /** diff --git a/application/apps/indexer/plugins_host/Cargo.toml b/application/apps/indexer/plugins_host/Cargo.toml index f77860cdc4..ac38db06e6 100644 --- a/application/apps/indexer/plugins_host/Cargo.toml +++ b/application/apps/indexer/plugins_host/Cargo.toml @@ -25,6 +25,7 @@ sources = { path = "../sources" } stypes = { path = "../stypes" } # TODO : Introduce shared crates and move dir_checksum to it. dir_checksum = { path = "../../../../cli/development-cli/dir_checksum" } +register = { path = "../register" } [dev-dependencies] criterion = { workspace = true, features = ["async_tokio"] } diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs index c356553d66..505b8c8f83 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs @@ -1,4 +1,4 @@ -use components::{ComponentDescriptor, ComponentFactory}; +use descriptor::{CommonDescriptor, ParserDescriptor, SourceDescriptor}; use stypes::{ Field, NativeError, NativeErrorKind, PluginConfigItem, PluginEntity, PluginParserSettings, SessionAction, Severity, StaticFieldDesc, @@ -19,7 +19,7 @@ impl PluginDescriptor { } } -impl ComponentDescriptor for PluginDescriptor { +impl CommonDescriptor for PluginDescriptor { fn ident(&self) -> stypes::Ident { let desc = self .entity @@ -35,32 +35,6 @@ impl ComponentDescriptor for PluginDescriptor { } } - fn ty(&self) -> stypes::ComponentType { - match self.entity.plugin_type { - stypes::PluginType::Parser => stypes::ComponentType::Parser, - stypes::PluginType::ByteSource => stypes::ComponentType::Source, - } - } - - fn get_render(&self) -> Option { - // TODO AAZ: API on plugins should change to match this changes. - match &self.entity.info.render_options { - stypes::RenderOptions::Parser(parser_render_options) => { - if let Some(opts) = parser_render_options.columns_options.as_ref() { - let cols: Vec<_> = opts - .columns - .iter() - .map(|col| (col.caption.to_owned(), col.width as usize)) - .collect(); - Some(stypes::OutputRender::Columns(cols)) - } else { - Some(stypes::OutputRender::PlaitText) - } - } - stypes::RenderOptions::ByteSource => None, - } - } - fn bound_ident( &self, _origin: &stypes::SessionAction, @@ -69,11 +43,7 @@ impl ComponentDescriptor for PluginDescriptor { self.ident() } - fn is_ty(&self, ty: &stypes::ComponentType) -> bool { - &self.ty() == ty - } - - fn fields_getter(&self, _origin: &stypes::SessionAction) -> components::FieldsResult { + fn fields_getter(&self, _origin: &stypes::SessionAction) -> descriptor::FieldsResult { let fields: Vec<_> = self .entity .info @@ -117,7 +87,7 @@ impl ComponentDescriptor for PluginDescriptor { &self, _origin: stypes::SessionAction, _cancel: CancellationToken, - ) -> components::LazyFieldsTask { + ) -> descriptor::LazyFieldsTask { Box::pin(async { Ok(Vec::new()) }) } @@ -134,77 +104,111 @@ impl ComponentDescriptor for PluginDescriptor { } } -impl ComponentFactory for PluginDescriptor { - fn create( - &self, - origin: &SessionAction, - options: &[Field], - ) -> Result)>, NativeError> { - let errors = self.validate(origin, options)?; - if !errors.is_empty() { - return Err(NativeError { - kind: NativeErrorKind::Configuration, - severity: Severity::ERROR, - message: Some( - errors - .values() - .map(String::as_str) - .collect::>() - .join("; "), - ), - }); +impl ParserDescriptor for PluginDescriptor { + fn get_render(&self) -> Option { + // TODO AAZ: API on plugins should change to match this changes. + match &self.entity.info.render_options { + stypes::RenderOptions::Parser(parser_render_options) => { + if let Some(opts) = parser_render_options.columns_options.as_ref() { + let cols: Vec<_> = opts + .columns + .iter() + .map(|col| (col.caption.to_owned(), col.width as usize)) + .collect(); + Some(stypes::OutputRender::Columns(cols)) + } else { + Some(stypes::OutputRender::PlaitText) + } + } + stypes::RenderOptions::ByteSource => None, } + } +} - let configs: Vec = options - .iter() - .map(|opt| { - use stypes::PluginConfigValue as PlVal; - - let val = match opt.value.clone() { - stypes::Value::Boolean(val) => PlVal::Boolean(val), - stypes::Value::Number(num) => PlVal::Integer(num as i32), - stypes::Value::String(txt) => PlVal::Text(txt), - stypes::Value::Directories(path_bufs) => PlVal::Directories(path_bufs), - stypes::Value::Files(path_bufs) => PlVal::Files(path_bufs), - unsupported => panic!("Config {unsupported:?} is unsupported by plugins"), - }; - PluginConfigItem::new(opt.id.to_owned(), val) - }) - .collect(); - - let settings = - PluginParserSettings::new(self.entity.dir_path.to_owned(), Default::default(), configs); - - //TODO AAZ: Temp solution by blocking here. - //Create function should be async - let parse_res = tokio::task::block_in_place(move || { - Handle::current().block_on(async move { - PluginsParser::initialize( - &settings.plugin_path, - &settings.general_settings, - settings.plugin_configs.clone(), - ) - .await - }) - })?; - - let parser = parsers::Parser::Plugin(Box::new(parse_res)); +pub fn parser_factory( + _origin: &SessionAction, + _options: &[Field], +) -> Result)>, NativeError> { + Err(NativeError { + severity: Severity::WARNING, + kind: NativeErrorKind::NotYetImplemented, + message: Some("Support for sources isn't implemented yet".into()), + }) +} - Ok(Some((parser, Some(self.entity.metadata.title.to_owned())))) +// impl ComponentFactory for PluginDescriptor { +// fn create( +// &self, +// origin: &SessionAction, +// options: &[Field], +// ) -> Result)>, NativeError> { +// let errors = self.validate(origin, options)?; +// if !errors.is_empty() { +// return Err(NativeError { +// kind: NativeErrorKind::Configuration, +// severity: Severity::ERROR, +// message: Some( +// errors +// .values() +// .map(String::as_str) +// .collect::>() +// .join("; "), +// ), +// }); +// } + +// let configs: Vec = options +// .iter() +// .map(|opt| { +// use stypes::PluginConfigValue as PlVal; + +// let val = match opt.value.clone() { +// stypes::Value::Boolean(val) => PlVal::Boolean(val), +// stypes::Value::Number(num) => PlVal::Integer(num as i32), +// stypes::Value::String(txt) => PlVal::Text(txt), +// stypes::Value::Directories(path_bufs) => PlVal::Directories(path_bufs), +// stypes::Value::Files(path_bufs) => PlVal::Files(path_bufs), +// unsupported => panic!("Config {unsupported:?} is unsupported by plugins"), +// }; +// PluginConfigItem::new(opt.id.to_owned(), val) +// }) +// .collect(); + +// let settings = +// PluginParserSettings::new(self.entity.dir_path.to_owned(), Default::default(), configs); + +// //TODO AAZ: Temp solution by blocking here. +// //Create function should be async +// let parse_res = tokio::task::block_in_place(move || { +// Handle::current().block_on(async move { +// PluginsParser::initialize( +// &settings.plugin_path, +// &settings.general_settings, +// settings.plugin_configs.clone(), +// ) +// .await +// }) +// })?; + +// let parser = parsers::Parser::Plugin(Box::new(parse_res)); + +// Ok(Some((parser, Some(self.entity.metadata.title.to_owned())))) +// } +// } + +impl SourceDescriptor for PluginDescriptor { + fn is_sde_supported(&self, _origin: &stypes::SessionAction, _fields: &[stypes::Field]) -> bool { + false } } -impl ComponentFactory for PluginDescriptor { - fn create( - &self, - _origin: &SessionAction, - _options: &[Field], - ) -> Result)>, NativeError> { - //TODO: Update once sources are implemented - Err(NativeError { - severity: Severity::WARNING, - kind: NativeErrorKind::NotYetImplemented, - message: Some("Support for sources isn't implemented yet".into()), - }) - } +pub fn source_factory( + _origin: &SessionAction, + _options: &[Field], +) -> Result)>, NativeError> { + Err(NativeError { + severity: Severity::WARNING, + kind: NativeErrorKind::NotYetImplemented, + message: Some("Support for sources isn't implemented yet".into()), + }) } diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs index cdc6ee30c5..c1ada8f583 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs @@ -14,10 +14,10 @@ use std::path::{Path, PathBuf}; use crate::plugins_shared::load::{WasmComponentInfo, load_and_inspect}; use cache::CacheManager; -use components::Components; -use descriptor::PluginDescriptor; +use descriptor::{PluginDescriptor, parser_factory, source_factory}; use load::{PluginEntityState, load_plugin}; use paths::extract_plugin_file_paths; +use register::Register; use stypes::{ ExtendedInvalidPluginEntity, ExtendedPluginEntity, InvalidPluginEntity, PluginEntity, PluginLogLevel, PluginRunData, PluginType, @@ -318,18 +318,15 @@ impl PluginsManager { Ok(()) } - pub fn register_plugins( - &self, - components: &mut Components, - ) -> Result<(), stypes::NativeError> { + pub fn register_plugins(&self, components: &mut Register) -> Result<(), stypes::NativeError> { for plugin in &self.installed_plugins { let plug_info = plugin.entity.clone(); match plugin.entity.plugin_type { PluginType::Parser => { - components.add_parser(PluginDescriptor::new(plug_info))?; + components.add_parser(parser_factory, PluginDescriptor::new(plug_info))?; } PluginType::ByteSource => { - components.add_source(PluginDescriptor::new(plug_info))?; + components.add_source(source_factory, PluginDescriptor::new(plug_info))?; } } } From 0a0fefe4f8728adebb1b3299e3c2e9eca8da05ca Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 24 Jul 2025 17:19:09 +0200 Subject: [PATCH 109/121] Partly resolve clippy warns --- .../indexer/parsers/src/dlt/descriptor.rs | 8 ++--- .../apps/indexer/parsers/src/dlt/fmt.rs | 8 ++--- .../indexer/parsers/src/dlt/raw/descriptor.rs | 2 +- .../apps/indexer/parsers/src/dlt/raw/mod.rs | 3 +- .../indexer/parsers/src/someip/descriptor.rs | 2 +- .../apps/indexer/parsers/src/someip/mod.rs | 4 +-- .../plugins_host/src/parser_shared/mod.rs | 2 +- .../src/parser_shared/plugin_parse_message.rs | 1 - .../src/plugins_manager/descriptor.rs | 8 ++--- .../indexer/processor/src/producer/mod.rs | 14 +++----- application/apps/indexer/register/src/lib.rs | 2 +- .../src/handlers/observe/export_raw.rs | 6 ++-- .../session/src/handlers/observe/session.rs | 10 ++---- .../apps/indexer/session/src/register/api.rs | 5 ++- .../apps/indexer/session/src/register/mod.rs | 33 ++++++++++--------- .../apps/indexer/session/src/session.rs | 12 +++---- .../apps/indexer/session/src/state/api.rs | 7 ---- .../src/binary/pcap/legacy/descriptor.rs | 2 +- .../sources/src/binary/pcap/ng/descriptor.rs | 2 +- .../indexer/sources/src/command/descriptor.rs | 6 ++-- .../apps/indexer/sources/src/command/mod.rs | 2 +- .../indexer/sources/src/serial/descriptor.rs | 6 ++-- .../sources/src/socket/tcp/descriptor.rs | 4 +-- .../sources/src/socket/udp/descriptor.rs | 2 +- .../indexer/stypes/src/options/extending.rs | 27 ++++++++------- 25 files changed, 76 insertions(+), 102 deletions(-) diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index e72d666e3e..faa6981437 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -115,7 +115,7 @@ impl CommonDescriptor for Descriptor { }; files.iter().any(|fp| { fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "dlt") + .map(|ext| ext.eq_ignore_ascii_case("dlt")) .unwrap_or_default() }) } @@ -262,7 +262,7 @@ impl CommonDescriptor for Descriptor { StatFields::EcuIds => &stat.ecu_ids, }; let mut entity: HashMap> = HashMap::new(); - inner.into_iter().for_each(|(id, levels)| { + inner.iter().for_each(|(id, levels)| { let mut map: HashMap = HashMap::new(); map.insert(NON_LOG.to_owned(), levels.non_log); map.insert(LOG_FATAL.to_owned(), levels.log_fatal); @@ -341,7 +341,7 @@ pub fn get_default_options( fields: vec![ Field { id: FIELD_FIBEX_FILES.to_owned(), - value: Value::Files(fibex.unwrap_or(Vec::new())), + value: Value::Files(fibex.unwrap_or_default()), }, Field { id: FIELD_LOG_LEVEL.to_owned(), @@ -349,7 +349,7 @@ pub fn get_default_options( }, Field { id: FIELD_STATISTICS.to_owned(), - value: Value::KeyStrings(filters.unwrap_or(HashMap::new())), + value: Value::KeyStrings(filters.unwrap_or_default()), }, ], } diff --git a/application/apps/indexer/parsers/src/dlt/fmt.rs b/application/apps/indexer/parsers/src/dlt/fmt.rs index 8609fa1e9a..837afc8ad3 100644 --- a/application/apps/indexer/parsers/src/dlt/fmt.rs +++ b/application/apps/indexer/parsers/src/dlt/fmt.rs @@ -501,7 +501,7 @@ impl<'a> FormattableMessage<'a> { fn info_from_metadata<'b>(&'b self, id: u32, data: &[u8]) -> Option> { let fibex = self.fibex_dlt_metadata.as_ref()?; - let md = extract_metadata(&fibex, id, self.message.extended_header.as_ref())?; + let md = extract_metadata(fibex, id, self.message.extended_header.as_ref())?; let msg_type: Option = message_type(&self.message, md.message_info.as_deref()); let app_id = md.application_id.as_deref().or_else(|| { self.message @@ -613,11 +613,7 @@ impl<'a> fmt::Display for FormattableMessage<'a> { }) { if let Some(slice) = slices.get(1) { - match SomeipParser::parse_message( - self.fibex_someip_metadata.as_deref(), - slice, - None, - ) { + match SomeipParser::parse_message(self.fibex_someip_metadata, slice, None) { Ok((_, message)) => { let prefix = slices.first().map_or_else(String::default, |s| { parse_prefix(s) diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 9ff9bc0940..7b712b6ac9 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -31,7 +31,7 @@ impl CommonDescriptor for Descriptor { }; files.iter().any(|fp| { fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "dlt") + .map(|ext| ext.eq_ignore_ascii_case("dlt")) .unwrap_or_default() }) } diff --git a/application/apps/indexer/parsers/src/dlt/raw/mod.rs b/application/apps/indexer/parsers/src/dlt/raw/mod.rs index 4de6a57088..39066821a1 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/mod.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/mod.rs @@ -29,8 +29,7 @@ impl DltRawParser { let (rest, consumed) = dlt_consume_msg(input).map_err(map_dlt_err)?; let msg = consumed.map(|c| &input[0..c as usize]); let total_consumed = input.len() - rest.len(); - let item = (total_consumed, msg.map(|m| m.into())); - Ok(item) + Ok((total_consumed, msg)) } } diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index e2f645d1fa..998a188f79 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -110,7 +110,7 @@ pub fn get_default_options(fibex: Option>) -> ComponentOptions { uuid: SOMEIP_PARSER_UUID, fields: vec![Field { id: FIELD_FIBEX_FILES.to_owned(), - value: Value::Files(fibex.unwrap_or(Vec::new())), + value: Value::Files(fibex.unwrap_or_default()), }], } } diff --git a/application/apps/indexer/parsers/src/someip/mod.rs b/application/apps/indexer/parsers/src/someip/mod.rs index 09ed2def5f..b5ddf6948f 100644 --- a/application/apps/indexer/parsers/src/someip/mod.rs +++ b/application/apps/indexer/parsers/src/someip/mod.rs @@ -36,7 +36,7 @@ pub struct FibexMetadata { impl FibexMetadata { /// Returns a new meta-data from the given fibex-files. - pub fn from_fibex_files(paths: &Vec) -> Option { + pub fn from_fibex_files(paths: &[PathBuf]) -> Option { let readers: Vec<_> = paths .iter() .filter_map(|path| FibexReader::from_file(path).ok()) @@ -192,7 +192,7 @@ impl SomeipParser { } /// Creates a new parser with the given files. - pub fn from_fibex_files(paths: &Vec) -> Self { + pub fn from_fibex_files(paths: &[PathBuf]) -> Self { SomeipParser { fibex_metadata: FibexMetadata::from_fibex_files(paths), } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs index 7e85435d3c..6dd7c7f6f0 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/mod.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/mod.rs @@ -169,7 +169,7 @@ impl Parser for PluginsParser { ))); } } - Err(ParserError::Native(err)) => { + Err(ParserError::Native(_err)) => { todo!("Not implemented") } } diff --git a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs index 2508d617f5..a8525d497d 100644 --- a/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs +++ b/application/apps/indexer/plugins_host/src/parser_shared/plugin_parse_message.rs @@ -1,4 +1,3 @@ -use parsers::api::LogRecordOutput; use serde::Serialize; /// Represent the message of the parsed item returned by plugins. diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs index 505b8c8f83..7a2059200c 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs @@ -1,13 +1,9 @@ use descriptor::{CommonDescriptor, ParserDescriptor, SourceDescriptor}; use stypes::{ - Field, NativeError, NativeErrorKind, PluginConfigItem, PluginEntity, PluginParserSettings, - SessionAction, Severity, StaticFieldDesc, + Field, NativeError, NativeErrorKind, PluginEntity, SessionAction, Severity, StaticFieldDesc, }; -use tokio::runtime::Handle; use tokio_util::sync::CancellationToken; -use crate::PluginsParser; - #[derive(Debug)] pub struct PluginDescriptor { entity: PluginEntity, @@ -70,7 +66,7 @@ impl CommonDescriptor for PluginDescriptor { desc: conf .description .as_ref() - .map_or_else(|| String::new(), |d| d.to_owned()), + .map_or_else(String::new, |d| d.to_owned()), //TODO AAZ: Map this to plugins API required: true, interface, diff --git a/application/apps/indexer/processor/src/producer/mod.rs b/application/apps/indexer/processor/src/producer/mod.rs index 883e978ea1..4d88088a23 100644 --- a/application/apps/indexer/processor/src/producer/mod.rs +++ b/application/apps/indexer/processor/src/producer/mod.rs @@ -135,9 +135,8 @@ impl<'a, P: Parser, D: ByteSource, B: LogRecordsBuffer> MessageProducer<'a, P, D // Reset skipped bytes since it had been counted here. skipped_bytes = 0; debug!( - "Extracted a valid message, consumed {} bytes (total used {} bytes)", - consumed, total_used_bytes - ); + "Extracted a valid message, consumed {consumed} bytes (total used {total_used_bytes} bytes)" + ); total_consumed += consumed; messages_received += 1; @@ -145,7 +144,7 @@ impl<'a, P: Parser, D: ByteSource, B: LogRecordsBuffer> MessageProducer<'a, P, D } (consumed, None) => { total_consumed += consumed; - trace!("None, consumed {} bytes", consumed); + trace!("None, consumed {consumed} bytes"); } }}) }) { @@ -190,10 +189,7 @@ impl<'a, P: Parser, D: ByteSource, B: LogRecordsBuffer> MessageProducer<'a, P, D } } Err(ParserError::Eof) => { - trace!( - "EOF reached...no more messages (skipped_bytes={})", - skipped_bytes - ); + trace!("EOF reached...no more messages (skipped_bytes={skipped_bytes})",); self.done = true; return None; } @@ -245,7 +241,7 @@ impl<'a, P: Parser, D: ByteSource, B: LogRecordsBuffer> MessageProducer<'a, P, D } return Some((0, MessageStreamItem::Done)); } - Err(ParserError::Native(err)) => { + Err(ParserError::Native(_err)) => { todo!("Not Implemented") } } diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index 8970bbc2a7..f54466e64b 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -231,7 +231,7 @@ impl Register { &self, uuid: &Uuid, ) -> Result, stypes::NativeError> { - let (_, descriptor) = self.parsers.get(&uuid).ok_or(stypes::NativeError { + let (_, descriptor) = self.parsers.get(uuid).ok_or(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find component {uuid}")), diff --git a/application/apps/indexer/session/src/handlers/observe/export_raw.rs b/application/apps/indexer/session/src/handlers/observe/export_raw.rs index 94c81ab52f..29b9ee2296 100644 --- a/application/apps/indexer/session/src/handlers/observe/export_raw.rs +++ b/application/apps/indexer/session/src/handlers/observe/export_raw.rs @@ -104,13 +104,15 @@ pub async fn run_producer( ) -> OperationResult { operation_api.processing(); let cancel = operation_api.cancellation_token(); - while let Some(..) = select! { + while select! { next_from_stream = producer.read_next_segment() => next_from_stream, _ = async { cancel.cancelled().await; debug!("exporting operation has been cancelled"); } => None, - } { + } + .is_some() + { // Do nothing. Writing happens on MessageProducer level } debug!("exporting is done"); diff --git a/application/apps/indexer/session/src/handlers/observe/session.rs b/application/apps/indexer/session/src/handlers/observe/session.rs index 309246bbbf..d6908ac596 100644 --- a/application/apps/indexer/session/src/handlers/observe/session.rs +++ b/application/apps/indexer/session/src/handlers/observe/session.rs @@ -55,12 +55,8 @@ impl LogRecordsBuffer for LogsBuffer { LogRecordOutput::Raw(inner) => { // TODO: Needs to be optimized. Also this use-case doesn't seem normal, should be some logs // because during observe we do not expect raw data - self.text_buffer.push_str( - &inner - .iter() - .map(|b| format!("{:02X}", b)) - .collect::(), - ); + self.text_buffer + .push_str(&inner.iter().map(|b| format!("{b:02X}")).collect::()); self.text_buffer.push('\n'); } LogRecordOutput::Message(msg) => { @@ -166,7 +162,7 @@ pub async fn run_producer( } { match next { Next::Parsed(_consumed, results) => match results { - MessageStreamItem::Parsed(results) => { + MessageStreamItem::Parsed(..) => { //Just continue } MessageStreamItem::Done => { diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index cd9aaae522..cab91a23ac 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -1,9 +1,8 @@ -use std::{collections::HashMap, fmt}; +use std::collections::HashMap; use descriptor::{LazyLoadingResult, LazyLoadingTaskMeta}; -use stypes::{Ident, NativeError, SessionAction, SessionDescriptor}; +use stypes::{Ident, NativeError, SessionAction}; use tokio::sync::oneshot; -use tokio_util::sync::CancellationToken; use uuid::Uuid; /// Represents the various API commands for managing component sessions and operations. diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index 18c2a7e84b..b1ad11d168 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -177,7 +177,7 @@ impl SessionRegister { } } } - Api::GetComponents(origin, ty, tx) => { + Api::GetComponents(_origin, _ty, _tx) => { // log_if_err(tx.send(components.get_components(&ty, origin))); } // Client doesn't need any more field data. Loading task should be cancelled @@ -643,54 +643,55 @@ fn response>(res: Result, msg: S) -> Result Result<(), stypes::NativeError> { - components.add_parser( +// use parsers::api::*; +// use sources::api::*; + +pub fn parsers_registration(register: &mut Register) -> Result<(), stypes::NativeError> { + register.add_parser( parsers::dlt::descriptor::factory, parsers::dlt::descriptor::Descriptor::default(), )?; - components.add_parser( + register.add_parser( parsers::dlt::descriptor::factory, parsers::dlt::raw::descriptor::Descriptor::default(), )?; - components.add_parser( + register.add_parser( parsers::someip::descriptor::factory, parsers::someip::descriptor::Descriptor::default(), )?; - components.add_parser( + register.add_parser( parsers::text::descriptor::factory, parsers::text::descriptor::Descriptor::default(), )?; Ok(()) } -pub fn sources_registration(components: &mut Register) -> Result<(), stypes::NativeError> { - components.add_source( +pub fn sources_registration(register: &mut Register) -> Result<(), stypes::NativeError> { + register.add_source( sources::binary::raw::factory, sources::binary::raw::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::binary::pcap::legacy::factory, sources::binary::pcap::legacy::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::binary::pcap::ng::factory, sources::binary::pcap::ng::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::socket::tcp::factory, sources::socket::tcp::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::socket::udp::factory, sources::socket::udp::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::serial::descriptor::factory, sources::serial::descriptor::Descriptor::default(), )?; - components.add_source( + register.add_source( sources::command::factory, sources::command::Descriptor::default(), )?; diff --git a/application/apps/indexer/session/src/session.rs b/application/apps/indexer/session/src/session.rs index cfcf2e358b..5a3d94b970 100644 --- a/application/apps/indexer/session/src/session.rs +++ b/application/apps/indexer/session/src/session.rs @@ -56,11 +56,9 @@ impl Session { ) = unbounded_channel(); let mut register: Register = Register::new(); // Registre parsers - parsers_registration(&mut register) - .map_err(|err| stypes::ComputationError::NativeError(err))?; + parsers_registration(&mut register).map_err(stypes::ComputationError::NativeError)?; // Registre sources - sources_registration(&mut register) - .map_err(|err| stypes::ComputationError::NativeError(err))?; + sources_registration(&mut register).map_err(stypes::ComputationError::NativeError)?; // Create a session let session = Self { uuid, @@ -446,9 +444,9 @@ impl Session { pub fn export_raw( &self, - operation_id: Uuid, - out_path: PathBuf, - ranges: Vec>, + _operation_id: Uuid, + _out_path: PathBuf, + _ranges: Vec>, ) -> Result<(), stypes::ComputationError> { // We should not ask client for stypes::SessionSetup and we should take it from state todo!("Not implemented") diff --git a/application/apps/indexer/session/src/state/api.rs b/application/apps/indexer/session/src/state/api.rs index 8902acbeb7..08ff3f6fba 100644 --- a/application/apps/indexer/session/src/state/api.rs +++ b/application/apps/indexer/session/src/state/api.rs @@ -291,13 +291,6 @@ impl SessionStateAPI { }) } - fn sync_exec_operation(&self, api: Api) -> Result<(), stypes::NativeError> { - let api_str = api.to_string(); - self.tx_api.send(api).map_err(|e| { - stypes::NativeError::channel(&format!("Failed to send to Api::{api_str}; error: {e}")) - }) - } - pub async fn grab(&self, range: LineRange) -> Result, stypes::NativeError> { let (tx, rx) = oneshot::channel(); self.exec_operation(Api::Grab((range.clone(), tx)), rx) diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 94f682bff4..2d42401ce9 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -42,7 +42,7 @@ impl CommonDescriptor for Descriptor { }; files.iter().any(|fp| { fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "pcap") + .map(|ext| ext.eq_ignore_ascii_case("pcap")) .unwrap_or_default() }) && // If at least some file doesn't exist or not binary - do not recommend this source diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 1ab065624b..86d8faf93f 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -42,7 +42,7 @@ impl CommonDescriptor for Descriptor { }; files.iter().any(|fp| { fp.extension() - .map(|ext| ext.to_ascii_lowercase() == "pcapng") + .map(|ext| ext.eq_ignore_ascii_case("pcapng")) .unwrap_or_default() }) && // If at least some file doesn't exist or not binary - do not recommend this source diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 535acc4841..34285f2a22 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -3,8 +3,8 @@ use crate::*; use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; use std::{collections::HashMap, env}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, - Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, NativeError, SessionAction, StaticFieldDesc, + ValueInput, missed_field_err as missed, }; const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -19,7 +19,7 @@ const FIELD_SHELLS: &str = "COMMAND_FIELD_SHELLS"; pub struct Descriptor {} pub fn factory( - origin: &SessionAction, + _origin: &SessionAction, options: &[Field], ) -> Result)>, NativeError> { let command: String = options diff --git a/application/apps/indexer/sources/src/command/mod.rs b/application/apps/indexer/sources/src/command/mod.rs index fa717340dc..2894f17c06 100644 --- a/application/apps/indexer/sources/src/command/mod.rs +++ b/application/apps/indexer/sources/src/command/mod.rs @@ -121,7 +121,7 @@ impl ProcessSource { cwd: PathBuf, envs: HashMap, ) -> Result { - let mut args = ProcessSource::parse_command(&command)?; + let mut args = ProcessSource::parse_command(command)?; let cmd = if args.is_empty() { return Err(ProcessError::Setup(format!( "Not command has been found in \"{command}\"" diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 378f5c461b..cac0604819 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -5,8 +5,8 @@ use descriptor::{ }; use std::{collections::HashMap, str}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, NativeErrorKind, - SessionAction, Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, SessionAction, + StaticFieldDesc, ValueInput, missed_field_err as missed, }; use super::serialport::SerialConfig; @@ -38,7 +38,7 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; pub struct Descriptor {} pub fn factory( - origin: &SessionAction, + _origin: &SessionAction, options: &[Field], ) -> Result)>, NativeError> { let path: String = options diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index fadff929e9..e79673465d 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -19,7 +19,7 @@ const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; pub struct Descriptor {} pub fn factory( - origin: &SessionAction, + _origin: &SessionAction, options: &[Field], ) -> Result)>, NativeError> { let addr: String = options @@ -28,7 +28,7 @@ pub fn factory( .value; Ok(Some(( Sources::Tcp(TcpSource::new(&addr, None, None)?), - Some(format!("TCP on {}", addr)), + Some(format!("TCP on {addr}")), ))) } diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 7655e3ac27..5b8e058da9 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -143,7 +143,7 @@ impl CommonDescriptor for Descriptor { .extract_by_key(FIELD_MULTICAST_ADDR) .ok_or(missed(FIELD_MULTICAST_ADDR))?; let mut errors = HashMap::new(); - for pair in multicast.value.into_iter() { + for pair in multicast.value.iter() { if let Value::Fields(pair) = &pair.value { if pair.len() != 2 { errors.insert( diff --git a/application/apps/indexer/stypes/src/options/extending.rs b/application/apps/indexer/stypes/src/options/extending.rs index 84f75d2820..800a2531f9 100644 --- a/application/apps/indexer/stypes/src/options/extending.rs +++ b/application/apps/indexer/stypes/src/options/extending.rs @@ -37,12 +37,11 @@ where fields .iter() .find(|field| field.id == key) - .map(|field| { + .and_then(|field| { field .extract_as() .map(|v| Extracted::new(field.id.as_str(), v)) }) - .flatten() } /// Trait that provides typed extraction from a collection of `Field` objects using a string key. @@ -56,61 +55,61 @@ pub trait ExtractByKey { impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl ExtractByKey for &[Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl<'l> ExtractByKey<&'l Vec> for &'l [Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option>> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } impl<'l> ExtractByKey<&'l Vec> for &'l [Field] { fn extract_by_key<'a>(&'a self, key: &str) -> Option>> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } @@ -119,7 +118,7 @@ impl<'l> ExtractByKey<&'l HashMap>> for &'l [Field] { &'a self, key: &str, ) -> Option>>> { - extract_from_fields_by_key(&self, key) + extract_from_fields_by_key(self, key) } } /// A trait that provides type-specific extraction logic from a `Field`. @@ -178,7 +177,7 @@ impl ExtractAs for &Field { impl ExtractAs for &Field { fn extract_as(&self) -> Option { match &self.value { - Value::Number(value) => (*value).try_into().map(|v| v).ok(), + Value::Number(value) => (*value).try_into().ok(), Value::Boolean(..) | Value::Directories(..) | Value::Directory(..) From d6d160c03e2049ba7b1b82f5247c4bf304508110 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 25 Jul 2025 14:16:19 +0200 Subject: [PATCH 110/121] Restore get_components method --- application/apps/indexer/register/src/lib.rs | 30 +++++++++++++++++++ .../apps/indexer/session/src/register/mod.rs | 20 ++++++------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index f54466e64b..27486a204d 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -150,6 +150,36 @@ impl Register { .collect() } + /// Returns the identifiers of all registered components of a given type. + /// + /// This method retrieves the list of identifiers (`Ident`) for all components + /// that match the specified [`ComponentType`], such as parsers or sources. The + /// results can be filtered based on the provided [`SessionAction`] origin, + /// allowing context-specific queries. + /// + /// # Arguments + /// + /// * `ty` - The type of component to retrieve (e.g., `Parser` or `Source`). + /// * `origin` - The session origin used to filter components by their scope or context. + /// + /// # Returns + /// + /// A `Vec` containing the identifiers of all matching components. + /// + /// # See Also + /// + /// [`get_parsers`], [`get_sources`] + pub fn get_components( + &self, + ty: stypes::ComponentType, + origin: stypes::SessionAction, + ) -> Vec { + match ty { + stypes::ComponentType::Parser => self.get_parsers(origin), + stypes::ComponentType::Source => self.get_sources(origin), + } + } + /// Returns configuration schemas for the specified component UUIDs. /// /// For each component (parser or source), this method returns the `OptionsScheme` used to render configuration UI diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index b1ad11d168..702dec46ca 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -61,13 +61,13 @@ impl SessionRegister { UnboundedSender, UnboundedReceiver, ) = unbounded_channel(); - let mut components: Register = Register::new(); + let mut register: Register = Register::new(); // Registre parsers - parsers_registration(&mut components)?; + parsers_registration(&mut register)?; // Registre sources - plugins_manager.register_plugins(&mut components)?; + plugins_manager.register_plugins(&mut register)?; let plugins_manager = Arc::new(RwLock::new(plugins_manager)); - sources_registration(&mut components)?; + sources_registration(&mut register)?; let tx_api_inner = tx_api.clone(); let session = Self { tx_api }; task::spawn(async move { @@ -76,17 +76,17 @@ impl SessionRegister { while let Some(msg) = rx_api.recv().await { match msg { Api::GetOutputRender(uuid, tx) => { - log_if_err(tx.send(components.get_output_render(&uuid))); + log_if_err(tx.send(register.get_output_render(&uuid))); } Api::GetIdent(uuid, tx) => { - log_if_err(tx.send(components.get_ident(&uuid))); + log_if_err(tx.send(register.get_ident(&uuid))); } Api::GetOptions { origin, targets, tx, } => { - let mut options = match components.get_options(origin, targets) { + let mut options = match register.get_options(origin, targets) { Ok(options) => options, Err(err) => { log_if_err(tx.send(Err(err))); @@ -177,8 +177,8 @@ impl SessionRegister { } } } - Api::GetComponents(_origin, _ty, _tx) => { - // log_if_err(tx.send(components.get_components(&ty, origin))); + Api::GetComponents(origin, ty, tx) => { + log_if_err(tx.send(Ok(register.get_components(ty, origin)))); } // Client doesn't need any more field data. Loading task should be cancelled Api::CancelLoading(fields) => { @@ -189,7 +189,7 @@ impl SessionRegister { } } Api::Validate(origin, target, fields, tx) => { - log_if_err(tx.send(components.validate(&origin, &target, &fields))); + log_if_err(tx.send(register.validate(&origin, &target, &fields))); } Api::Shutdown(tx) => { // Cancel / kill pending tasks From 73dca6fee22632ad632e6a0a3be3df28754f6a2b Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Fri, 25 Jul 2025 14:58:06 +0200 Subject: [PATCH 111/121] Add SDE support API (close #2365) --- .../apps/indexer/descriptor/src/tys/source.rs | 2 +- .../src/plugins_manager/descriptor.rs | 2 +- application/apps/indexer/register/src/lib.rs | 32 ++++++++++++++ .../apps/indexer/session/src/register/api.rs | 21 +++++++++- .../apps/indexer/session/src/register/mod.rs | 34 ++++++++++++++- .../indexer/sources/src/command/descriptor.rs | 6 ++- .../indexer/sources/src/serial/descriptor.rs | 6 ++- .../apps/rustcore/rs-bindings/Cargo.lock | 17 ++++++++ .../rs-bindings/src/js/components/mod.rs | 42 +++++++++++++++++++ .../ts-bindings/src/api/components.ts | 4 ++ .../src/native/native.components.ts | 9 ++++ .../client/src/app/service/components.ts | 27 ++++++++++++ .../client/src/app/service/session/origin.ts | 7 ++-- application/holder/src/service/components.ts | 7 ++++ .../holder/src/service/components/index.ts | 1 + .../service/components/is_sde_supported.ts | 25 +++++++++++ .../platform/ipc/request/components/index.ts | 1 + .../request/components/is_sde_supported.ts | 31 ++++++++++++++ 18 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 application/holder/src/service/components/is_sde_supported.ts create mode 100644 application/platform/ipc/request/components/is_sde_supported.ts diff --git a/application/apps/indexer/descriptor/src/tys/source.rs b/application/apps/indexer/descriptor/src/tys/source.rs index 31526653dd..004429738d 100644 --- a/application/apps/indexer/descriptor/src/tys/source.rs +++ b/application/apps/indexer/descriptor/src/tys/source.rs @@ -26,7 +26,7 @@ pub trait SourceDescriptor: CommonDescriptor { /// # Returns /// /// `true` if SDE is supported by the source, `false` otherwise. - fn is_sde_supported(&self, _origin: &stypes::SessionAction, _fields: &[stypes::Field]) -> bool { + fn is_sde_supported(&self, _origin: &stypes::SessionAction) -> bool { false } } diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs index 7a2059200c..bb3b5b6137 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs @@ -193,7 +193,7 @@ pub fn parser_factory( // } impl SourceDescriptor for PluginDescriptor { - fn is_sde_supported(&self, _origin: &stypes::SessionAction, _fields: &[stypes::Field]) -> bool { + fn is_sde_supported(&self, _origin: &stypes::SessionAction) -> bool { false } } diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index 27486a204d..1e239b869a 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -269,6 +269,38 @@ impl Register { Ok(descriptor.get_render()) } + /// Checks whether the specified source supports the Source Data Exchange (SDE) mechanism. + /// + /// The SDE mechanism determines whether the user is allowed to send data to the source. + /// This method queries the source descriptor by its UUID and evaluates SDE support + /// in the context of the given session origin. + /// + /// # Arguments + /// + /// * `uuid` - The unique identifier of the source component. + /// * `origin` - The session context used to evaluate access permissions. + /// + /// # Returns + /// + /// `Ok(true)` if the source supports SDE; `Ok(false)` otherwise. + /// Returns an error if the source with the specified UUID is not found. + /// + /// # Errors + /// + /// Returns [`NativeError`] with kind [`Configuration`] if the source is not registered. + pub fn is_sde_supported( + &self, + uuid: &Uuid, + origin: &stypes::SessionAction, + ) -> Result { + let (_, descriptor) = self.sources.get(uuid).ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {uuid}")), + })?; + Ok(descriptor.is_sde_supported(origin)) + } + /// Returns the Ident of component (parser or source). /// /// # Arguments diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index cab91a23ac..c531ba4911 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -117,6 +117,26 @@ pub enum Api { stypes::ComponentType, oneshot::Sender, NativeError>>, ), + /// A request to check whether a source supports the Source Data Exchange (SDE) mechanism. + /// + /// This variant is used to query if the specified source, identified by its `Uuid`, allows data + /// to be sent to it in the given session context. The result of the check is returned asynchronously + /// through the provided `oneshot::Sender`. + /// + /// # Arguments + /// + /// * `SessionAction` - The session context in which the SDE support should be evaluated. + /// * `Uuid` - The unique identifier of the source component. + /// * `oneshot::Sender>` - A channel sender used to return + /// the result of the check asynchronously: + /// - `Ok(true)` if SDE is supported, + /// - `Ok(false)` if not supported, + /// - `Err(NativeError)` if the source was not found or another error occurred. + IsSdeSupported( + SessionAction, + Uuid, + oneshot::Sender>, + ), /// Validates the configuration for correctness. /// /// # Arguments @@ -139,7 +159,6 @@ pub enum Api { Vec, oneshot::Sender, NativeError>>, ), - /// Get all information of the installed plugins . InstalledPluginsList(oneshot::Sender>), /// Get all information of invalid plugins . diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index 702dec46ca..dc7deaa5ea 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -180,6 +180,9 @@ impl SessionRegister { Api::GetComponents(origin, ty, tx) => { log_if_err(tx.send(Ok(register.get_components(ty, origin)))); } + Api::IsSdeSupported(origin, uuid, tx) => { + log_if_err(tx.send(register.is_sde_supported(&uuid, &origin))); + } // Client doesn't need any more field data. Loading task should be cancelled Api::CancelLoading(fields) => { for (_, (meta, _)) in tasks.iter() { @@ -359,11 +362,40 @@ impl SessionRegister { let (tx, rx) = oneshot::channel(); send( self.tx_api.send(Api::GetComponents(origin, ty, tx)), - "Fail to send Api::GetParsers", + "Fail to send Api::GetComponents", )?; response(rx.await, "Fail to get response from Api::GetComponents")? } + /// Asynchronously checks whether the specified source supports the Source Data Exchange (SDE) mechanism. + /// + /// This is a high-level wrapper that sends an [`Api::IsSdeSupported`] request to the internal API + /// and awaits the result. The method is asynchronous and returns either the result of the check + /// or an error if the request fails or the source is not found. + /// + /// # Arguments + /// + /// * `uuid` — The unique identifier of the source component to check. + /// * `origin` — The session context used to evaluate SDE permissions. + /// + /// # Returns + /// + /// * `Ok(true)` — if the source supports SDE. + /// * `Ok(false)` — if the source does not support SDE. + /// * `Err(NativeError)` — if the request could not be sent or the response failed. + pub async fn is_sde_supported( + &self, + uuid: Uuid, + origin: stypes::SessionAction, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::IsSdeSupported(origin, uuid, tx)), + "Fail to send Api::IsSdeSupported", + )?; + response(rx.await, "Fail to get response from Api::IsSdeSupported")? + } + /// Aborts the lazy loading tasks for the specified fields. /// /// This method sends a cancellation request for ongoing lazy loading tasks associated with the given field IDs. diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 34285f2a22..102026097d 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -110,4 +110,8 @@ impl CommonDescriptor for Descriptor { Ok(errors) } } -impl SourceDescriptor for Descriptor {} +impl SourceDescriptor for Descriptor { + fn is_sde_supported(&self, _origin: &stypes::SessionAction) -> bool { + true + } +} diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index cac0604819..961e58309d 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -235,4 +235,8 @@ impl CommonDescriptor for Descriptor { Ok(errors) } } -impl SourceDescriptor for Descriptor {} +impl SourceDescriptor for Descriptor { + fn is_sde_supported(&self, _origin: &stypes::SessionAction) -> bool { + true + } +} diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 21a1246843..05f6d2578d 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2515,6 +2515,7 @@ dependencies = [ "log", "parsers", "rand 0.9.1", + "register", "serde", "serde_json", "sources", @@ -2831,6 +2832,21 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "register" +version = "0.1.0" +dependencies = [ + "descriptor", + "log", + "parsers", + "sources", + "stypes", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "uuid", +] + [[package]] name = "rustc-demangle" version = "0.1.25" @@ -3059,6 +3075,7 @@ dependencies = [ "parsers", "plugins_host", "processor", + "register", "rustc-hash", "serde", "serde_json", diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 04af1efd97..87278400f5 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -122,6 +122,48 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + /// Checks whether a source supports the Source Data Exchange (SDE) mechanism. + /// + /// This is an asynchronous Node.js-exposed method that receives a source UUID and a serialized + /// [`SessionAction`] buffer. It decodes the session context, parses the UUID, and delegates + /// the actual check to the internal session logic. This method allows JavaScript code + /// to determine if sending data to a specific source is permitted. + /// + /// # Arguments + /// + /// * `uuid` - A string representation of the source UUID. + /// * `origin` - A JavaScript `ArrayBuffer` containing a serialized [`SessionAction`] object. + /// + /// # Returns + /// + /// A `Promise` resolving to `true` if SDE is supported, `false` otherwise. + /// + /// # Errors + /// + /// Returns a [`ComputationError`] in the following cases: + /// - If the session is not available, + /// - If decoding the `origin` buffer fails, + /// - If the UUID is invalid, + /// - If the underlying native API returns an error. + #[node_bindgen] + async fn is_sde_supported( + &self, + uuid: String, + origin: JSArrayBuffer, + ) -> Result { + let origin = + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let uuid = Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .is_sde_supported(uuid, origin) + .await + .map_err(stypes::ComputationError::NativeError) + } + /// Retrieves the configuration options for a list of components. /// /// This method sends an asynchronous request to obtain the settings of the specified components. diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 6b4d51a986..405059ad09 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -91,6 +91,10 @@ export class Components extends Subscriber { return this.native.getOutputRender(uuid); } + public isSdeSupported(uuid: String, origin: SessionAction): Promise { + return this.native.isSdeSupported(uuid, origin); + } + public getIdent(uuid: string): Promise { return this.native.getIdent(uuid); } diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index 884066fb18..dbd3423e9e 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -36,6 +36,8 @@ export abstract class ComponentsNative { public abstract getComponents(origin: Uint8Array, ty: Uint8Array): Promise; + public abstract isSdeSupported(uuid: String, origin: Uint8Array): Promise; + public abstract getOptions(origin: Uint8Array, targets: string[]): Promise; public abstract getOutputRender(uuid: String): Promise; @@ -196,6 +198,13 @@ export class Base extends Subscriber { }); }); } + public isSdeSupported(uuid: String, origin: SessionAction): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return this.native.isSdeSupported(uuid, protocol.encodeSessionAction(origin)); + } public getOptions(origin: SessionAction, targets: string[]): Promise { const err = this.getSessionAccessErr(); diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index fdc27c6ea3..de3d94e5bf 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -165,6 +165,33 @@ export class Service extends Implementation { }); } + /** + * Checks whether the specified source supports the Source Data Exchange (SDE) mechanism. + * + * The SDE mechanism allows sending data *to* a source. This method verifies + * if the given source component (by UUID) permits such operations + * in the context of the provided session. + * + * @param uuid - The unique identifier of the source component. + * @param origin - The session context used to evaluate SDE permissions. + * @returns A promise that resolves to `true` if SDE is supported by the source, or `false` otherwise. + */ + public isSdeSupported(uuid: string, origin: SessionAction): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.IsSdeSupported.Response, + new Requests.Components.IsSdeSupported.Request({ + uuid, + origin, + }), + ) + .then((response) => { + resolve(response.support); + }) + .catch(reject); + }); + } + /** * Validates the provided configuration fields for the specified component. * diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index c400352d69..0f7195dc53 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -152,9 +152,10 @@ export class SessionOrigin { } public isSdeSupported(): Promise { - /// TODO: add API implementation - console.error(`Not Implemented`); - return Promise.resolve(true); + if (!this.components || !this.components.source) { + return Promise.reject(new Error(`Source isn't defined`)); + } + return components.isSdeSupported(this.components.source.uuid, this.origin); } public isFile(): boolean { diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index 23304d4607..53a0aa6429 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -57,6 +57,13 @@ export class Service extends Implementation { Requests.Components.GetOutputRender.Request, RequestHandlers.GetOutputRender.handler, ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.IsSdeSupported.Request, + RequestHandlers.IsSdeSupported.handler, + ), electron .ipc() .respondent( diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts index fdecafd385..ab9476e535 100644 --- a/application/holder/src/service/components/index.ts +++ b/application/holder/src/service/components/index.ts @@ -6,3 +6,4 @@ export * as GetIdent from './get_ident'; export * as Abort from './abort'; export * as Validate from './validate'; export * as Plugins from './plugins'; +export * as IsSdeSupported from './is_sde_supported'; diff --git a/application/holder/src/service/components/is_sde_supported.ts b/application/holder/src/service/components/is_sde_supported.ts new file mode 100644 index 0000000000..8be91c246a --- /dev/null +++ b/application/holder/src/service/components/is_sde_supported.ts @@ -0,0 +1,25 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { FieldDesc, OutputRender } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.IsSdeSupported.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.IsSdeSupported.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .isSdeSupported(request.uuid, request.origin) + .then((support: boolean) => { + resolve(new Requests.Components.IsSdeSupported.Response({ support })); + }) + .catch(reject); + }); + }, +); diff --git a/application/platform/ipc/request/components/index.ts b/application/platform/ipc/request/components/index.ts index c00c9be07c..ed2f18a627 100644 --- a/application/platform/ipc/request/components/index.ts +++ b/application/platform/ipc/request/components/index.ts @@ -5,3 +5,4 @@ export * as GetIdent from './get_ident'; export * as GetOutputRender from './get_output_render'; export * as Abort from './abort'; export * as Validate from './validate'; +export * as IsSdeSupported from './is_sde_supported'; diff --git a/application/platform/ipc/request/components/is_sde_supported.ts b/application/platform/ipc/request/components/is_sde_supported.ts new file mode 100644 index 0000000000..d31591b080 --- /dev/null +++ b/application/platform/ipc/request/components/is_sde_supported.ts @@ -0,0 +1,31 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { OutputRender, SessionAction } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'IsSdeSupportedRequest' }) +export class Request extends SignatureRequirement { + public uuid: string; + public origin: SessionAction; + + constructor(input: { uuid: string; origin: SessionAction }) { + super(); + validator.isObject(input); + this.uuid = validator.getAsNotEmptyString(input, 'uuid'); + this.origin = input.origin; + } +} +export interface Request extends Interface {} + +@Define({ name: 'IsSdeSupportedResponse' }) +export class Response extends SignatureRequirement { + public support: boolean; + + constructor(input: { support: boolean }) { + super(); + validator.isObject(input); + this.support = validator.getAsBool(input, 'support'); + } +} + +export interface Response extends Interface {} From 494f10bba760870d00e7dacf224a62be87b554f3 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 30 Jul 2025 15:04:28 +0200 Subject: [PATCH 112/121] Add API: get default options & compatible setup --- README.md | 8 +- .../apps/indexer/addons/file-tools/src/lib.rs | 5 + .../apps/indexer/descriptor/src/tys/mod.rs | 29 ++++ .../apps/indexer/descriptor/src/tys/parser.rs | 2 +- .../indexer/parsers/src/dlt/descriptor.rs | 6 +- .../indexer/parsers/src/someip/descriptor.rs | 10 +- .../indexer/parsers/src/text/descriptor.rs | 3 + application/apps/indexer/register/Cargo.toml | 3 +- application/apps/indexer/register/src/lib.rs | 164 +++++++++++++++++- .../apps/indexer/session/src/register/api.rs | 36 ++++ .../apps/indexer/session/src/register/mod.rs | 112 +++++++++++- .../sources/src/socket/tcp/descriptor.rs | 2 +- .../sources/src/socket/udp/descriptor.rs | 2 +- .../apps/indexer/stypes/bindings/observe.ts | 2 + .../apps/indexer/stypes/bindings/options.ts | 2 + .../indexer/stypes/src/error/extending.rs | 7 + .../indexer/stypes/src/observe/extending.rs | 5 + .../apps/indexer/stypes/src/observe/mod.rs | 12 ++ .../apps/indexer/stypes/src/observe/nodejs.rs | 1 + .../apps/indexer/stypes/src/options/mod.rs | 11 +- .../apps/indexer/stypes/src/options/nodejs.rs | 1 + .../indexer/stypes/src/plugins/proptest.rs | 5 +- application/apps/protocol/src/lib.rs | 2 + .../apps/rustcore/rs-bindings/Cargo.lock | 1 + .../rs-bindings/src/js/components/mod.rs | 92 +++++++++- .../ts-bindings/src/api/components.ts | 10 ++ .../ts-bindings/src/interfaces/errors.ts | 1 + .../src/native/native.components.ts | 65 +++++++ .../client/src/app/service/components.ts | 65 ++++++- .../app/ui/env/directives/dragdrop.file.ts | 2 +- application/holder/src/service/components.ts | 14 ++ .../components/get_compatible_setup.ts | 25 +++ .../service/components/get_default_options.ts | 25 +++ .../holder/src/service/components/index.ts | 2 + .../service/components/is_sde_supported.ts | 1 - .../components/get_compatible_setup.ts | 29 ++++ .../request/components/get_default_options.ts | 31 ++++ .../platform/ipc/request/components/index.ts | 2 + .../request/components/is_sde_supported.ts | 2 +- .../platform/types/bindings/observe.ts | 2 + .../platform/types/bindings/options.ts | 2 + docs/charts.md | 2 +- docs/index.md | 6 +- docs/installation.md | 2 +- docs/teamwork.md | 2 +- 45 files changed, 780 insertions(+), 33 deletions(-) create mode 100644 application/holder/src/service/components/get_compatible_setup.ts create mode 100644 application/holder/src/service/components/get_default_options.ts create mode 100644 application/platform/ipc/request/components/get_compatible_setup.ts create mode 100644 application/platform/ipc/request/components/get_default_options.ts diff --git a/README.md b/README.md index 1dd5a47237..3d3fbbee71 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![](https://github.com/esrlabs/chipmunk/actions/workflows/release_next.yml/badge.svg)](https://github.com/esrlabs/chipmunk/actions/workflows/release_next.yml) [![](https://github.com/esrlabs/chipmunk/actions/workflows/lint_master.yml/badge.svg)](https://github.com/esrlabs/chipmunk/actions/workflows/lint_master.yml) -`chipmunk` is one of the fastest desktop applications for viewing log files, with no limitations on file size. 1 GB, 2 GB, 10 GB? `chipmunk` is limited only by your disk space — nothing more. With no caching and no unnecessary copying, files of any size open with the same speed. But `chipmunk` goes beyond just working with files: it also allows you to create network connections to collect logs via TCP, UDP, Serial, or from the output of a running command. +`chipmunk` is one of the fastest desktop applications for viewing log files, with no limitations on file size. 1 GB, 2 GB, 10 GB? `chipmunk` is limited only by your disk space - nothing more. With no caching and no unnecessary copying, files of any size open with the same speed. But `chipmunk` goes beyond just working with files: it also allows you to create network connections to collect logs via TCP, UDP, Serial, or from the output of a running command. ## Automotive and Network Traces @@ -19,7 +19,7 @@ Additionally, `chipmunk` allows you to work with DLT traces both as standalone f - Serial Port - Output from a command or program -For each source, you can assign a parser — for example, to collect DLT packets over a UDP connection for analysis or to save them as a standalone trace file. +For each source, you can assign a parser - for example, to collect DLT packets over a UDP connection for analysis or to save them as a standalone trace file. Another key feature is the ability to launch any command or program and collect its output, which can be analyzed in real time as it's generated. @@ -29,7 +29,7 @@ At its core, `chipmunk` is a log analysis tool. It goes beyond simple search que ![filters_create](./docs/assets/search/filters_create.gif) -The search engine works dynamically — results are updated in real time as new data is added. If you're connected to a live data source, your active filters will continuously update the search results as new logs arrive. +The search engine works dynamically - results are updated in real time as new data is added. If you're connected to a live data source, your active filters will continuously update the search results as new logs arrive. ## Metrics, Measurements, and Graphs @@ -47,6 +47,6 @@ See more details in the [documentation](https://esrlabs.github.io/chipmunk/) abo ## Contributing -We welcome contributions of all kinds — bug reports, performance improvements, documentation fixes, or new features. +We welcome contributions of all kinds - bug reports, performance improvements, documentation fixes, or new features. [Click here to view it](https://esrlabs.github.io/chipmunk/contributing/welcome/) \ No newline at end of file diff --git a/application/apps/indexer/addons/file-tools/src/lib.rs b/application/apps/indexer/addons/file-tools/src/lib.rs index c4288c79f7..51b39b660a 100644 --- a/application/apps/indexer/addons/file-tools/src/lib.rs +++ b/application/apps/indexer/addons/file-tools/src/lib.rs @@ -13,6 +13,11 @@ pub fn is_binary(file_path: String) -> Result { Ok(from_utf8(&buffer).map_or(true, |_file_content| false)) } +pub fn is_path_binary>(file_path: P) -> Result { + let buffer = fetch_starting_chunk(file_path.as_ref())?; + Ok(from_utf8(&buffer).map_or(true, |_file_content| false)) +} + fn fetch_starting_chunk(file_path: &Path) -> Result> { let bytes_to_read: u64 = (metadata(file_path)?.len().max(1) - 1).min(BYTES_TO_READ); diff --git a/application/apps/indexer/descriptor/src/tys/mod.rs b/application/apps/indexer/descriptor/src/tys/mod.rs index a7a7a65589..0ace81e497 100644 --- a/application/apps/indexer/descriptor/src/tys/mod.rs +++ b/application/apps/indexer/descriptor/src/tys/mod.rs @@ -187,6 +187,35 @@ pub trait CommonDescriptor: Sync + Send { ) -> Result, stypes::NativeError> { Ok(HashMap::new()) } + + /// Indicates whether the component (parser or source) provides default options. + /// + /// This method is used to determine if a component can be automatically selected + /// and instantiated in response to a user request, without requiring manual configuration. + /// For example, when a user opens a plain text file, there may be no need to configure + /// the source or parser manually. + /// + /// The method receives a [`SessionAction`] representing the context of the request. + /// If the component supports default configuration, it returns `Some(Vec)`, + /// where the fields can be used to create an instance of the component. + /// + /// If the component **must** be explicitly configured by the user (i.e., it cannot + /// be used blindly), the method returns `None`. + /// If the component has **no settings at all**, it returns `Some(Vec::new())` - + /// meaning it can be safely used without configuration. + /// + /// # Returns + /// + /// * `Some(Vec)` - Default fields to use for instantiating the component. + /// * `Some(Vec::new())` - The component has no settings but is still valid for auto-selection. + /// * `None` - The component requires explicit configuration and cannot be auto-selected. + /// + /// # Important + /// + /// Returning `None` will **exclude** the component from automatic selection logic. + fn get_default_options(&self, _origin: &stypes::SessionAction) -> Option> { + None + } } /// Represents validation errors that occurred during client-side input processing. diff --git a/application/apps/indexer/descriptor/src/tys/parser.rs b/application/apps/indexer/descriptor/src/tys/parser.rs index f8ccadfbcc..a0e65a6ea5 100644 --- a/application/apps/indexer/descriptor/src/tys/parser.rs +++ b/application/apps/indexer/descriptor/src/tys/parser.rs @@ -17,7 +17,7 @@ pub trait ParserDescriptor: CommonDescriptor { /// /// This method determines how (and whether) the parser's output should be /// rendered in the user interface. If the parser is not intended to produce - /// visual output—e.g., when used for data export or structural analysis— + /// visual output-e.g., when used for data export or structural analysis- /// this function returns `None`. /// /// # Returns diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index faa6981437..18b19bc439 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -124,7 +124,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_LOG_LEVEL.to_owned(), name: "Log Level".to_owned(), - desc: "Log Level — defines the minimum severity level of log messages to be displayed. Messages with lower severity (e.g., Debug or Verbose) will be filtered out. For example, if \"Error\" is selected, only messages with level \"Error\" and above (e.g., Fatal) will be shown, while lower levels such as \"Warning\" or \"Debug\" will be ignored.".to_owned(), + desc: "Log Level - defines the minimum severity level of log messages to be displayed. Messages with lower severity (e.g., Debug or Verbose) will be filtered out. For example, if \"Error\" is selected, only messages with level \"Error\" and above (e.g., Fatal) will be shown, while lower levels such as \"Warning\" or \"Debug\" will be ignored.".to_owned(), required: true, interface: ValueInput::NamedNumbers( vec![ @@ -142,7 +142,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_FIBEX_FILES.to_owned(), name: "Fibex Files".to_owned(), - desc: "Fibex Files — allows the user to attach one or more XML files containing the schema definitions for decoding the DLT payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), + desc: "Fibex Files - allows the user to attach one or more XML files containing the schema definitions for decoding the DLT payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), required: true, interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), binding: None, @@ -150,7 +150,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_TZ.to_owned(), name: "Timezone".to_owned(), - desc: "Timezone — a display-only setting used for visualizing timestamps. If a timezone is specified, all DLT timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), + desc: "Timezone - a display-only setting used for visualizing timestamps. If a timezone is specified, all DLT timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), required: true, interface: ValueInput::Timezone, binding: None, diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 998a188f79..1b2c1c3adb 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -60,7 +60,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_FIBEX_FILES.to_owned(), name: "Fibex Files".to_owned(), - desc: "Fibex Files — allows the user to attach one or more XML files containing the schema definitions for decoding the payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), + desc: "Fibex Files - allows the user to attach one or more XML files containing the schema definitions for decoding the payload. These files are used to interpret the payload contents according to the provided descriptions. If decoding fails or no matching schema is found, the payload will be displayed as-is, in raw hexadecimal form.".to_owned(), required: true, interface: ValueInput::Files(vec!["xml".to_owned(), "*".to_owned()]), binding: None, @@ -68,7 +68,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_TZ.to_owned(), name: "Timezone".to_owned(), - desc: "Timezone — a display-only setting used for visualizing timestamps. If a timezone is specified, all timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), + desc: "Timezone - a display-only setting used for visualizing timestamps. If a timezone is specified, all timestamps will be adjusted accordingly to improve readability. This setting does not modify the underlying data and is used purely for presentation purposes.".to_owned(), required: true, interface: ValueInput::Timezone, binding: None, @@ -86,6 +86,12 @@ impl CommonDescriptor for Descriptor { uuid: SOMEIP_PARSER_UUID, } } + fn get_default_options(&self, origin: &stypes::SessionAction) -> Option> { + Some(vec![Field { + id: FIELD_FIBEX_FILES.to_owned(), + value: Value::Files(Vec::new()), + }]) + } } impl ParserDescriptor for Descriptor { diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index dbe63c89c0..ede8261079 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -43,6 +43,9 @@ impl CommonDescriptor for Descriptor { uuid: TEXT_PARSER_UUID, } } + fn get_default_options(&self, origin: &stypes::SessionAction) -> Option> { + Some(vec![]) + } } impl ParserDescriptor for Descriptor { diff --git a/application/apps/indexer/register/Cargo.toml b/application/apps/indexer/register/Cargo.toml index ec9ee5d44e..0d40240b19 100644 --- a/application/apps/indexer/register/Cargo.toml +++ b/application/apps/indexer/register/Cargo.toml @@ -13,4 +13,5 @@ log.workspace = true stypes = { path = "../stypes", features=["rustcore"] } descriptor = { path = "../descriptor" } parsers = { path = "../parsers" } -sources = { path = "../sources" } \ No newline at end of file +sources = { path = "../sources" } +file-tools = { path = "../addons/file-tools" } \ No newline at end of file diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index 1e239b869a..891437905d 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, fmt}; -use stypes::SessionDescriptor; +use file_tools::is_path_binary; +use stypes::{NativeError, SessionDescriptor}; use tokio_util::sync::CancellationToken; use uuid::Uuid; @@ -353,6 +354,167 @@ impl Register { descriptor.validate(origin, fields) } + /// Returns a list of source and parser components that are compatible with the given session context + /// and can be used without explicit configuration. + /// + /// This method is designed to support "quick start" scenarios, where the user initiates a session + /// (e.g., opening a file) without manually selecting or configuring components. It filters available + /// sources and parsers based on the following criteria: + /// + /// - The component must declare compatibility with the provided [`SessionAction`]. + /// - The component must support default options (i.e., `get_default_options()` returns `Some`). + /// - The component's IO type must match the detected IO data type inferred from the session context + /// (e.g., plain text or raw binary). + /// + /// This method is typically used to automatically select a default setup when the user opens + /// a file (such as a text or DLT file), allowing immediate parsing and display without prompting + /// for configuration. + /// + /// # Arguments + /// + /// * `origin` - The session context (e.g., a file or set of files) used to infer compatibility and IO type. + /// + /// # Returns + /// + /// A [`ComponentsList`] containing identifiers of all matching sources and parsers. + /// If no fully compatible combination is found, an empty list is returned. + /// + /// # Errors + /// + /// Returns a [`NativeError`] if binary detection fails for one or more files. + /// + /// # Behavior + /// + /// - For a single file: detects whether the file is binary or plain text. + /// - For multiple files: only returns components if all are plain text, or all are binary. + /// Mixed types result in an empty list. + /// - For unsupported session types (e.g., `Source`, `ExportRaw`), returns an empty list. + pub fn get_compatible_setup( + &self, + origin: &stypes::SessionAction, + ) -> Result { + let io_type = match origin { + stypes::SessionAction::File(filename) => { + if is_path_binary(filename).map_err(|err| NativeError::io(&err.to_string()))? { + stypes::IODataType::Raw + } else { + stypes::IODataType::PlaitText + } + } + stypes::SessionAction::Files(files) => { + let mut bin_count = 0; + for filename in files { + bin_count += if is_path_binary(filename) + .map_err(|err| NativeError::io(&err.to_string()))? + { + 1 + } else { + 0 + }; + } + if files.len() == bin_count { + stypes::IODataType::Raw + } else if !files.is_empty() && bin_count == 0 { + stypes::IODataType::PlaitText + } else { + return Ok(stypes::ComponentsList::default()); + } + } + stypes::SessionAction::Source | stypes::SessionAction::ExportRaw(..) => { + return Ok(stypes::ComponentsList::default()) + } + }; + let sources: Vec = self + .sources + .iter() + .filter_map(|(_, (_, entity))| { + if entity.is_compatible(origin) + && entity.get_default_options(origin).is_some() + && entity.ident().io == io_type + { + Some(entity.ident()) + } else { + None + } + }) + .collect(); + let parsers: Vec = self + .parsers + .iter() + .filter_map(|(_, (_, entity))| { + if entity.is_compatible(origin) + && entity.get_default_options(origin).is_some() + && entity.ident().io == io_type + { + Some(entity.ident()) + } else { + None + } + }) + .collect(); + + Ok(stypes::ComponentsList { parsers, sources }) + } + + /// Returns the default configuration options for the specified component, or an error if none are available. + /// + /// This method retrieves the default options for a parser or source component in the context of + /// the given [`SessionAction`]. It is used to determine whether the component can be instantiated + /// automatically, without requiring manual configuration by the user. + /// + /// Unlike earlier versions, this method no longer returns `Option`. If the component does not + /// support default options (i.e., it **must** be explicitly configured), the method returns a + /// [`NativeError`] indicating that it cannot be used in automatic setup scenarios. + /// + /// # Arguments + /// + /// * `origin` - The session context used to evaluate compatibility and scope. + /// * `target` - The UUID of the component for which default options are requested. + /// + /// # Returns + /// + /// * `Ok(FieldList)` - A list of fields to be used for instantiating the component. + /// If the list is empty, it means the component has no configurable options, but can still be used by default. + /// * `Err(NativeError)` - If the component: + /// - is not registered, + /// - does not support default options (and therefore cannot be used blindly). + /// + /// # Semantics + /// + /// - Empty `FieldList` (`FieldList(vec![])`) means the component has no settings but **is safe to use as default**. + /// - An error means the component **requires explicit configuration** and must not be auto-selected. + /// + /// # Errors + /// + /// Returns a [`NativeError`] with kind `Configuration` if: + /// - The component is not found, + /// - The component does not support default options in the given session context. + pub fn get_default_options( + &self, + origin: &stypes::SessionAction, + target: &Uuid, + ) -> Result { + let (_, descriptor) = self + .descriptors() + .into_iter() + .find(|(uuid, _)| *uuid == target) + .ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!("Fail to find component {target}")), + })?; + descriptor + .get_default_options(origin) + .map(|fields| stypes::FieldList(fields)) + .ok_or(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some(format!( + "Component {target} doesn't support default options" + )), + }) + } + /// Attempts to create instances of a parser and source component based on the session setup. /// /// This method creates fully initialized, ready-to-use instances of parser and source diff --git a/application/apps/indexer/session/src/register/api.rs b/application/apps/indexer/session/src/register/api.rs index c531ba4911..7dcbd694e4 100644 --- a/application/apps/indexer/session/src/register/api.rs +++ b/application/apps/indexer/session/src/register/api.rs @@ -137,6 +137,42 @@ pub enum Api { Uuid, oneshot::Sender>, ), + /// API request to resolve all components compatible with a given session context. + /// + /// This variant is used to automatically select sources and parsers that: + /// - Are compatible with the provided [`SessionAction`], + /// - Support default options (i.e., can be used without user configuration), + /// - Match the inferred IO data type (e.g., plain text or binary). + /// + /// This request is typically used to implement default behavior when the user + /// opens a file or data stream without specifying any components. + /// + /// # Fields + /// + /// - [`SessionAction`] - The session context used to infer IO type and compatibility. + /// - [`oneshot::Sender>`] - The response channel + /// returning a list of matching component identifiers or an error. + GetCompatibleSetup( + SessionAction, + oneshot::Sender>, + ), + /// API request to obtain the default configuration options for a specific component. + /// + /// This variant is used to retrieve the default `FieldList` associated with a source or parser + /// in the context of the given [`SessionAction`]. If the component cannot be used + /// without explicit configuration (i.e., it does not support defaults), an error is returned. + /// + /// # Fields + /// + /// - [`SessionAction`] - The session context that affects the component’s option set. + /// - [`Uuid`] - The unique identifier of the component. + /// - [`oneshot::Sender>`] - The response channel returning + /// the default options or an error if the component is not suitable for automatic setup. + GetDefaultOptions( + SessionAction, + Uuid, + oneshot::Sender>, + ), /// Validates the configuration for correctness. /// /// # Arguments diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index dc7deaa5ea..b788c9a769 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -183,6 +183,12 @@ impl SessionRegister { Api::IsSdeSupported(origin, uuid, tx) => { log_if_err(tx.send(register.is_sde_supported(&uuid, &origin))); } + Api::GetCompatibleSetup(origin, tx) => { + log_if_err(tx.send(register.get_compatible_setup(&origin))); + } + Api::GetDefaultOptions(origin, target, tx) => { + log_if_err(tx.send(register.get_default_options(&origin, &target))); + } // Client doesn't need any more field data. Loading task should be cancelled Api::CancelLoading(fields) => { for (_, (meta, _)) in tasks.iter() { @@ -205,7 +211,6 @@ impl SessionRegister { log_if_err(tx.send(())); break; } - Api::InstalledPluginsList(tx) => { // let plugs_ref_clone = Arc::clone(&plugins_manager); log_if_err(tx.send(plugins::installed_plugins_list(&plugins_manager).await)) @@ -375,14 +380,14 @@ impl SessionRegister { /// /// # Arguments /// - /// * `uuid` — The unique identifier of the source component to check. - /// * `origin` — The session context used to evaluate SDE permissions. + /// * `uuid` - The unique identifier of the source component to check. + /// * `origin` - The session context used to evaluate SDE permissions. /// /// # Returns /// - /// * `Ok(true)` — if the source supports SDE. - /// * `Ok(false)` — if the source does not support SDE. - /// * `Err(NativeError)` — if the request could not be sent or the response failed. + /// * `Ok(true)` - if the source supports SDE. + /// * `Ok(false)` - if the source does not support SDE. + /// * `Err(NativeError)` - if the request could not be sent or the response failed. pub async fn is_sde_supported( &self, uuid: Uuid, @@ -396,6 +401,101 @@ impl SessionRegister { response(rx.await, "Fail to get response from Api::IsSdeSupported")? } + /// Returns a list of source and parser components that are compatible with the given session context + /// and can be used without explicit configuration. + /// + /// This method is designed to support "quick start" scenarios, where the user initiates a session + /// (e.g., opening a file) without manually selecting or configuring components. It filters available + /// sources and parsers based on the following criteria: + /// + /// - The component must declare compatibility with the provided [`SessionAction`]. + /// - The component must support default options (i.e., `get_default_options()` returns `Some`). + /// - The component's IO type must match the detected IO data type inferred from the session context + /// (e.g., plain text or raw binary). + /// + /// This method is typically used to automatically select a default setup when the user opens + /// a file (such as a text or DLT file), allowing immediate parsing and display without prompting + /// for configuration. + /// + /// # Arguments + /// + /// * `origin` - The session context (e.g., a file or set of files) used to infer compatibility and IO type. + /// + /// # Returns + /// + /// A [`ComponentsList`] containing identifiers of all matching sources and parsers. + /// If no fully compatible combination is found, an empty list is returned. + /// + /// # Errors + /// + /// Returns a [`NativeError`] if binary detection fails for one or more files. + /// + /// # Behavior + /// + /// - For a single file: detects whether the file is binary or plain text. + /// - For multiple files: only returns components if all are plain text, or all are binary. + /// Mixed types result in an empty list. + /// - For unsupported session types (e.g., `Source`, `ExportRaw`), returns an empty list. + pub async fn get_compatible_setup( + &self, + origin: stypes::SessionAction, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetCompatibleSetup(origin, tx)), + "Fail to send Api::GetCompatibleSetup", + )?; + response( + rx.await, + "Fail to get response from Api::GetCompatibleSetup", + )? + } + + /// Returns the default configuration options for the specified component, or an error if none are available. + /// + /// This method retrieves the default options for a parser or source component in the context of + /// the given [`SessionAction`]. It is used to determine whether the component can be instantiated + /// automatically, without requiring manual configuration by the user. + /// + /// Unlike earlier versions, this method no longer returns `Option`. If the component does not + /// support default options (i.e., it **must** be explicitly configured), the method returns a + /// [`NativeError`] indicating that it cannot be used in automatic setup scenarios. + /// + /// # Arguments + /// + /// * `origin` - The session context used to evaluate compatibility and scope. + /// * `target` - The UUID of the component for which default options are requested. + /// + /// # Returns + /// + /// * `Ok(FieldList)` - A list of fields to be used for instantiating the component. + /// If the list is empty, it means the component has no configurable options, but can still be used by default. + /// * `Err(NativeError)` - If the component: + /// - is not registered, + /// - does not support default options (and therefore cannot be used blindly). + /// + /// # Semantics + /// + /// - Empty `FieldList` (`FieldList(vec![])`) means the component has no settings but **is safe to use as default**. + /// - An error means the component **requires explicit configuration** and must not be auto-selected. + /// + /// # Errors + /// + /// Returns a [`NativeError`] with kind `Configuration` if: + /// - The component is not found, + /// - The component does not support default options in the given session context. + pub async fn get_default_options( + &self, + origin: stypes::SessionAction, + target: Uuid, + ) -> Result { + let (tx, rx) = oneshot::channel(); + send( + self.tx_api.send(Api::GetDefaultOptions(origin, target, tx)), + "Fail to send Api::GetDefaultOptions", + )?; + response(rx.await, "Fail to get response from Api::GetDefaultOptions")? + } /// Aborts the lazy loading tasks for the specified fields. /// /// This method sends a cancellation request for ongoing lazy loading tasks associated with the given field IDs. diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index e79673465d..45d1f2b1bf 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -55,7 +55,7 @@ impl CommonDescriptor for Descriptor { Ok(vec![FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), name: "IP address and port".to_owned(), - desc: "Specifies the target address of the remote TCP server. The value must include both the IP address and the port, using the format IP:PORT — for example, 192.168.0.100:8888. This field is mandatory, and the parser will attempt to connect to the given endpoint exactly as specified. Use 0.0.0.0:PORT to bind to all local interfaces, if applicable.".to_owned(), + desc: "Specifies the target address of the remote TCP server. The value must include both the IP address and the port, using the format IP:PORT - for example, 192.168.0.100:8888. This field is mandatory, and the parser will attempt to connect to the given endpoint exactly as specified. Use 0.0.0.0:PORT to bind to all local interfaces, if applicable.".to_owned(), required: true, interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), binding: None, diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 5b8e058da9..45a687eff8 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -92,7 +92,7 @@ impl CommonDescriptor for Descriptor { FieldDesc::Static(StaticFieldDesc { id: FIELD_IP_ADDR.to_owned(), name: "IP address and port".to_owned(), - desc: "Specifies the local address and port on which the application should listen for incoming UDP packets. The value must include both the IP address and the port, using the format IP:PORT — for example, 0.0.0.0:9000. This field is mandatory, and determines the network interface and port bound by the listener. Use 0.0.0.0:PORT to listen on all available interfaces.".to_owned(), + desc: "Specifies the local address and port on which the application should listen for incoming UDP packets. The value must include both the IP address and the port, using the format IP:PORT - for example, 0.0.0.0:9000. This field is mandatory, and determines the network interface and port bound by the listener. Use 0.0.0.0:PORT to listen on all available interfaces.".to_owned(), required: true, interface: ValueInput::String(String::new(), "0.0.0.0:8888".to_owned()), binding: None, diff --git a/application/apps/indexer/stypes/bindings/observe.ts b/application/apps/indexer/stypes/bindings/observe.ts index 7ada9061e8..46c13f9813 100644 --- a/application/apps/indexer/stypes/bindings/observe.ts +++ b/application/apps/indexer/stypes/bindings/observe.ts @@ -14,6 +14,8 @@ export type ComponentDef = { "Source": ComponentOptions } | { "Parser": Componen */ export type ComponentType = "Parser" | "Source"; +export type ComponentsList = { parsers: Array, sources: Array, }; + /** * Settings for the DLT parser. */ diff --git a/application/apps/indexer/stypes/bindings/options.ts b/application/apps/indexer/stypes/bindings/options.ts index c44794ee53..a81f5c3641 100644 --- a/application/apps/indexer/stypes/bindings/options.ts +++ b/application/apps/indexer/stypes/bindings/options.ts @@ -13,6 +13,8 @@ export type Field = { id: string, value: Value, }; export type FieldDesc = { "Static": StaticFieldDesc } | { "Lazy": LazyFieldDesc }; +export type FieldList = Array; + export type FieldLoadingError = { id: string, err: string, }; export type FieldsValidationErrors = { errors: Map, }; diff --git a/application/apps/indexer/stypes/src/error/extending.rs b/application/apps/indexer/stypes/src/error/extending.rs index 6cbc536f97..0b5813d355 100644 --- a/application/apps/indexer/stypes/src/error/extending.rs +++ b/application/apps/indexer/stypes/src/error/extending.rs @@ -18,6 +18,13 @@ impl NativeError { message: Some(String::from(msg)), } } + pub fn io(msg: &str) -> Self { + NativeError { + severity: Severity::ERROR, + kind: NativeErrorKind::Io, + message: Some(String::from(msg)), + } + } } impl Severity { diff --git a/application/apps/indexer/stypes/src/observe/extending.rs b/application/apps/indexer/stypes/src/observe/extending.rs index fe5e4851fd..8ab2607c3f 100644 --- a/application/apps/indexer/stypes/src/observe/extending.rs +++ b/application/apps/indexer/stypes/src/observe/extending.rs @@ -3,6 +3,11 @@ use std::net::IpAddr; use crate::*; use thiserror::Error; +impl ComponentsList { + pub fn new(sources: Vec, parsers: Vec) -> Self { + Self { parsers, sources } + } +} impl SessionSetup { pub fn inherit(&self, origin: SessionAction) -> Self { Self { diff --git a/application/apps/indexer/stypes/src/observe/mod.rs b/application/apps/indexer/stypes/src/observe/mod.rs index cd28f19e21..529df7bed1 100644 --- a/application/apps/indexer/stypes/src/observe/mod.rs +++ b/application/apps/indexer/stypes/src/observe/mod.rs @@ -42,6 +42,18 @@ pub enum ComponentDef { Parser(ComponentOptions), } +#[derive(Default, Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "observe.ts") +)] +pub struct ComponentsList { + pub parsers: Vec, + pub sources: Vec, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/indexer/stypes/src/observe/nodejs.rs b/application/apps/indexer/stypes/src/observe/nodejs.rs index 4fb005cb46..5d22c1d810 100644 --- a/application/apps/indexer/stypes/src/observe/nodejs.rs +++ b/application/apps/indexer/stypes/src/observe/nodejs.rs @@ -15,3 +15,4 @@ try_into_js!(ObserveOptions); try_into_js!(Ident); try_into_js!(IdentList); try_into_js!(SessionAction); +try_into_js!(ComponentsList); diff --git a/application/apps/indexer/stypes/src/options/mod.rs b/application/apps/indexer/stypes/src/options/mod.rs index f8af259af4..b3e8e8b0f5 100644 --- a/application/apps/indexer/stypes/src/options/mod.rs +++ b/application/apps/indexer/stypes/src/options/mod.rs @@ -22,7 +22,7 @@ pub enum OutputRender { PlaitText, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[extend::encode_decode] #[cfg_attr( all(test, feature = "test_and_gen"), @@ -129,6 +129,15 @@ pub struct Field { pub value: Value, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[extend::encode_decode] +#[cfg_attr( + all(test, feature = "test_and_gen"), + derive(TS), + ts(export, export_to = "options.ts") +)] +pub struct FieldList(pub Vec); + #[derive(Debug, Serialize, Deserialize, Clone)] #[extend::encode_decode] #[cfg_attr( diff --git a/application/apps/indexer/stypes/src/options/nodejs.rs b/application/apps/indexer/stypes/src/options/nodejs.rs index cc97da3336..cea5dcb48b 100644 --- a/application/apps/indexer/stypes/src/options/nodejs.rs +++ b/application/apps/indexer/stypes/src/options/nodejs.rs @@ -9,3 +9,4 @@ try_into_js!(CallbackOptionsEvent); try_into_js!(ComponentsOptionsList); try_into_js!(FieldsValidationErrors); try_into_js!(OutputRender); +try_into_js!(FieldList); diff --git a/application/apps/indexer/stypes/src/plugins/proptest.rs b/application/apps/indexer/stypes/src/plugins/proptest.rs index b974ad6369..939d9adf64 100644 --- a/application/apps/indexer/stypes/src/plugins/proptest.rs +++ b/application/apps/indexer/stypes/src/plugins/proptest.rs @@ -245,7 +245,10 @@ impl Arbitrary for InvalidPluginEntity { fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { (any::(), any::()) - .prop_map(|(dir_path, plugin_type)| Self::new(dir_path, plugin_type)) + .prop_map(|(dir_path, plugin_type)| Self { + dir_path, + plugin_type, + }) .boxed() } } diff --git a/application/apps/protocol/src/lib.rs b/application/apps/protocol/src/lib.rs index 74ed010a1c..ef2cd02708 100644 --- a/application/apps/protocol/src/lib.rs +++ b/application/apps/protocol/src/lib.rs @@ -213,3 +213,5 @@ gen_encode_decode_fns!(ComponentOptions); gen_encode_decode_fns!(SessionSetup); gen_encode_decode_fns!(SessionDescriptor); gen_encode_decode_fns!(OutputRender); +gen_encode_decode_fns!(ComponentsList); +gen_encode_decode_fns!(FieldList); diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index 05f6d2578d..d5bd9c0f39 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -2837,6 +2837,7 @@ name = "register" version = "0.1.0" dependencies = [ "descriptor", + "file-tools", "log", "parsers", "sources", diff --git a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs index 87278400f5..29aaea5ebd 100644 --- a/application/apps/rustcore/rs-bindings/src/js/components/mod.rs +++ b/application/apps/rustcore/rs-bindings/src/js/components/mod.rs @@ -18,7 +18,7 @@ impl Components { /// Initializes a new session bound to the `Components` instance at the core level. /// On the client side, there should only be a single global session; multiple instances of - /// `Components` are not required—only one is necessary. + /// `Components` are not required-only one is necessary. /// /// The `Components` structure maintains the list of all available components in the system, /// specifically parsers and data sources. It is responsible for: @@ -164,6 +164,96 @@ impl Components { .map_err(stypes::ComputationError::NativeError) } + /// Returns a list of compatible source and parser components that can be used without configuration. + /// + /// This method is exposed to JavaScript and is typically used in "quick start" workflows, + /// such as opening a file without specifying any component settings. It decodes the session context + /// from the provided binary buffer and delegates the lookup to the internal API. + /// + /// The result includes only those components that: + /// - Are compatible with the provided session (`SessionAction`); + /// - Support default options (i.e., do not require explicit configuration); + /// - Match the inferred IO type (e.g., text vs binary). + /// + /// # Arguments + /// + /// * `origin` – A serialized [`SessionAction`] (as `ArrayBuffer` from JavaScript), + /// describing the context of the session (e.g., file path, multiple files). + /// + /// # Returns + /// + /// A `Promise` resolving to a [`ComponentsList`] object containing matching sources and parsers. + /// If no compatible components are found, the list will be empty. + /// + /// # Errors + /// + /// Returns a [`ComputationError`] in the following cases: + /// - Invalid or undecodable session data (`Decoding`); + /// - Session not initialized (`SessionUnavailable`); + /// - Internal failure in component resolution (`NativeError`). + #[node_bindgen] + async fn get_compatible_setup( + &self, + origin: JSArrayBuffer, + ) -> Result { + let origin = + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_compatible_setup(origin) + .await + .map_err(stypes::ComputationError::NativeError) + } + + /// Retrieves the default configuration options for a specific component, + /// if it can be used without explicit user configuration. + /// + /// This method is exposed to JavaScript and is typically used when attempting to automatically + /// initialize a source or parser. It decodes the session context and validates the component UUID, + /// then queries whether the component provides default options for that context. + /// + /// If the component requires manual configuration, the method returns an error. + /// + /// # Arguments + /// + /// * `origin` – A serialized [`SessionAction`] passed from JavaScript as `ArrayBuffer`. + /// This provides context such as the file being opened or the session type. + /// * `uuid` – The string representation of the component’s UUID. + /// + /// # Returns + /// + /// A `Promise` resolving to a [`FieldList`] object representing the component’s default settings. + /// If the component has no configurable fields, the list will be empty (`FieldList([])`). + /// + /// # Errors + /// + /// Returns a [`ComputationError`] in the following cases: + /// - Invalid or undecodable session data (`Decoding`); + /// - Malformed or invalid UUID (`InvalidData`); + /// - Session is not initialized (`SessionUnavailable`); + /// - The component does not support default options (`NativeError`). + #[node_bindgen] + async fn get_default_options( + &self, + origin: JSArrayBuffer, + uuid: String, + ) -> Result { + let origin = + stypes::SessionAction::decode(&origin).map_err(stypes::ComputationError::Decoding)?; + let uuid = Uuid::from_str(&uuid).map_err(|_| stypes::ComputationError::InvalidData)?; + let session = self + .session + .as_ref() + .ok_or(stypes::ComputationError::SessionUnavailable)?; + session + .get_default_options(origin, uuid) + .await + .map_err(stypes::ComputationError::NativeError) + } + /// Retrieves the configuration options for a list of components. /// /// This method sends an asynchronous request to obtain the settings of the specified components. diff --git a/application/apps/rustcore/ts-bindings/src/api/components.ts b/application/apps/rustcore/ts-bindings/src/api/components.ts index 405059ad09..863d3819e0 100644 --- a/application/apps/rustcore/ts-bindings/src/api/components.ts +++ b/application/apps/rustcore/ts-bindings/src/api/components.ts @@ -5,9 +5,11 @@ import { Base as Native } from '../native/native.components'; import { ComponentsEventProvider, IComponentsEvents } from '../api/components.provider'; import { Subscriber } from 'platform/env/subscription'; import { + ComponentsList, ComponentsOptionsList, Field, FieldDesc, + FieldList, FieldsValidationErrors, Ident, OutputRender, @@ -95,6 +97,14 @@ export class Components extends Subscriber { return this.native.isSdeSupported(uuid, origin); } + public getCompatibleSetup(origin: SessionAction): Promise { + return this.native.getCompatibleSetup(origin); + } + + public getDefaultOptions(origin: SessionAction, target: string): Promise { + return this.native.getDefaultOptions(origin, target); + } + public getIdent(uuid: string): Promise { return this.native.getIdent(uuid); } diff --git a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts index d57af3dcb6..4fc906d584 100644 --- a/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts +++ b/application/apps/rustcore/ts-bindings/src/interfaces/errors.ts @@ -74,6 +74,7 @@ export enum Source { GetIndexedLen = 'GetIndexedLen', getAroundIndexes = 'getAroundIndexes', ComponentsOptions = 'ComponentsOptions', + ComponentsList = 'ComponentsList', GetIdent = 'GetIdent', ComponentsValidate = 'ComponentsValidate', IdentList = 'IdentList', diff --git a/application/apps/rustcore/ts-bindings/src/native/native.components.ts b/application/apps/rustcore/ts-bindings/src/native/native.components.ts index dbd3423e9e..f7d0cb779d 100644 --- a/application/apps/rustcore/ts-bindings/src/native/native.components.ts +++ b/application/apps/rustcore/ts-bindings/src/native/native.components.ts @@ -11,6 +11,8 @@ import { FieldsValidationErrors, OutputRender, Ident, + ComponentsList, + FieldList, } from 'platform/types/bindings'; import { Logger, utils } from 'platform/log'; import { TEventEmitter } from '../provider/provider.general'; @@ -38,6 +40,10 @@ export abstract class ComponentsNative { public abstract isSdeSupported(uuid: String, origin: Uint8Array): Promise; + public abstract getCompatibleSetup(origin: Uint8Array): Promise; + + public abstract getDefaultOptions(origin: Uint8Array, target: string): Promise; + public abstract getOptions(origin: Uint8Array, targets: string[]): Promise; public abstract getOutputRender(uuid: String): Promise; @@ -198,6 +204,7 @@ export class Base extends Subscriber { }); }); } + public isSdeSupported(uuid: String, origin: SessionAction): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { @@ -206,6 +213,64 @@ export class Base extends Subscriber { return this.native.isSdeSupported(uuid, protocol.encodeSessionAction(origin)); } + public getCompatibleSetup(origin: SessionAction): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native; + this.native + .getCompatibleSetup(protocol.encodeSessionAction(origin)) + .then((buf: Uint8Array) => { + try { + resolve(protocol.decodeComponentsList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error( + `Fail to decode message: ${utils.error(err)}`, + ), + ), + Type.InvalidOutput, + Source.ComponentsList, + ), + ); + } + }); + }); + } + + public getDefaultOptions(origin: SessionAction, target: string): Promise { + const err = this.getSessionAccessErr(); + if (err instanceof Error) { + return Promise.reject(err); + } + return new Promise((resolve, reject) => { + this.native; + this.native + .getDefaultOptions(protocol.encodeSessionAction(origin), target) + .then((buf: Uint8Array) => { + try { + resolve(protocol.decodeFieldList(buf)); + } catch (err) { + reject( + new NativeError( + new Error( + this.logger.error( + `Fail to decode message: ${utils.error(err)}`, + ), + ), + Type.InvalidOutput, + Source.ComponentsList, + ), + ); + } + }); + }); + } + public getOptions(origin: SessionAction, targets: string[]): Promise { const err = this.getSessionAccessErr(); if (err instanceof Error) { diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index de3d94e5bf..35bfb18985 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -11,7 +11,15 @@ import { import * as Events from '@platform/ipc/event/index'; import * as Requests from '@platform/ipc/request/index'; -import { Field, FieldDesc, Ident, OutputRender, SessionAction } from '@platform/types/bindings'; +import { + ComponentsList, + Field, + FieldDesc, + FieldList, + Ident, + OutputRender, + SessionAction, +} from '@platform/types/bindings'; @SetupService(services['components']) export class Service extends Implementation { @@ -192,6 +200,61 @@ export class Service extends Implementation { }); } + /** + * Finds all components (sources and parsers) that are compatible with the given session + * and can be used without requiring manual configuration. + * + * This method is typically used in "quick start" workflows, such as when a user opens a file + * and the system needs to automatically determine which components can handle it. + * Only components that support default options and match the detected I/O type (e.g. text or binary) + * are included in the result. + * + * @param origin - The session context used to infer compatibility and I/O type. + * @returns A promise that resolves to a list of compatible component identifiers. + */ + public getCompatibleSetup(origin: SessionAction): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetCompatibleSetup.Response, + new Requests.Components.GetCompatibleSetup.Request({ + origin, + }), + ) + .then((response) => { + resolve(response.components); + }) + .catch(reject); + }); + } + + /** + * Retrieves the default configuration options for the specified component. + * + * If the component supports usage without explicit configuration, this method returns a list + * of default fields. An empty list indicates that the component has no configurable options + * but can still be used as default. + * If the component cannot be used without configuration, the promise will be rejected. + * + * @param uuid - The unique identifier of the component (source or parser). + * @param origin - The session context used to resolve the appropriate default options. + * @returns A promise that resolves to a list of fields (possibly empty) for automatic setup. + */ + public getDefaultOptions(uuid: string, origin: SessionAction): Promise { + return new Promise((resolve, reject) => { + Requests.IpcRequest.send( + Requests.Components.GetDefaultOptions.Response, + new Requests.Components.GetDefaultOptions.Request({ + uuid, + origin, + }), + ) + .then((response) => { + resolve(response.fields); + }) + .catch(reject); + }); + } + /** * Validates the provided configuration fields for the specified component. * diff --git a/application/client/src/app/ui/env/directives/dragdrop.file.ts b/application/client/src/app/ui/env/directives/dragdrop.file.ts index 6542a45868..bb528ff5b7 100644 --- a/application/client/src/app/ui/env/directives/dragdrop.file.ts +++ b/application/client/src/app/ui/env/directives/dragdrop.file.ts @@ -63,7 +63,7 @@ export class MatDragDropFileFeatureDirective implements OnDestroy { constructor(protected readonly element: ElementRef) { // Note: The files property of DataTransfer objects can only be accessed from within - // the drop event. For all other events, the files property will be empty — because + // the drop event. For all other events, the files property will be empty - because // its underlying data store will be in a protected mode. // https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/files // diff --git a/application/holder/src/service/components.ts b/application/holder/src/service/components.ts index 53a0aa6429..da8c1e3d24 100644 --- a/application/holder/src/service/components.ts +++ b/application/holder/src/service/components.ts @@ -64,6 +64,20 @@ export class Service extends Implementation { Requests.Components.IsSdeSupported.Request, RequestHandlers.IsSdeSupported.handler, ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetCompatibleSetup.Request, + RequestHandlers.GetCompatibleSetup.handler, + ), + electron + .ipc() + .respondent( + this.getName(), + Requests.Components.GetDefaultOptions.Request, + RequestHandlers.GetDefaultOptions.handler, + ), electron .ipc() .respondent( diff --git a/application/holder/src/service/components/get_compatible_setup.ts b/application/holder/src/service/components/get_compatible_setup.ts new file mode 100644 index 0000000000..ad0c20bf0d --- /dev/null +++ b/application/holder/src/service/components/get_compatible_setup.ts @@ -0,0 +1,25 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { ComponentsList } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetCompatibleSetup.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetCompatibleSetup.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .getCompatibleSetup(request.origin) + .then((components: ComponentsList) => { + resolve(new Requests.Components.GetCompatibleSetup.Response({ components })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/get_default_options.ts b/application/holder/src/service/components/get_default_options.ts new file mode 100644 index 0000000000..25b498e697 --- /dev/null +++ b/application/holder/src/service/components/get_default_options.ts @@ -0,0 +1,25 @@ +import { CancelablePromise } from 'platform/env/promise'; +import { Logger } from 'platform/log'; +import { components } from '@service/components'; +import { FieldList } from 'platform/types/bindings'; + +import * as Requests from 'platform/ipc/request'; + +export const handler = Requests.InjectLogger< + Requests.Components.GetDefaultOptions.Request, + CancelablePromise +>( + ( + _log: Logger, + request: Requests.Components.GetDefaultOptions.Request, + ): CancelablePromise => { + return new CancelablePromise((resolve, reject) => { + components.components + .getDefaultOptions(request.origin, request.uuid) + .then((fields: FieldList) => { + resolve(new Requests.Components.GetDefaultOptions.Response({ fields })); + }) + .catch(reject); + }); + }, +); diff --git a/application/holder/src/service/components/index.ts b/application/holder/src/service/components/index.ts index ab9476e535..c1865e3d5a 100644 --- a/application/holder/src/service/components/index.ts +++ b/application/holder/src/service/components/index.ts @@ -7,3 +7,5 @@ export * as Abort from './abort'; export * as Validate from './validate'; export * as Plugins from './plugins'; export * as IsSdeSupported from './is_sde_supported'; +export * as GetCompatibleSetup from './get_compatible_setup'; +export * as GetDefaultOptions from './get_default_options'; diff --git a/application/holder/src/service/components/is_sde_supported.ts b/application/holder/src/service/components/is_sde_supported.ts index 8be91c246a..76257dbfb8 100644 --- a/application/holder/src/service/components/is_sde_supported.ts +++ b/application/holder/src/service/components/is_sde_supported.ts @@ -1,7 +1,6 @@ import { CancelablePromise } from 'platform/env/promise'; import { Logger } from 'platform/log'; import { components } from '@service/components'; -import { FieldDesc, OutputRender } from 'platform/types/bindings'; import * as Requests from 'platform/ipc/request'; diff --git a/application/platform/ipc/request/components/get_compatible_setup.ts b/application/platform/ipc/request/components/get_compatible_setup.ts new file mode 100644 index 0000000000..4c936aabcb --- /dev/null +++ b/application/platform/ipc/request/components/get_compatible_setup.ts @@ -0,0 +1,29 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { ComponentsList, SessionAction } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetCompatibleSetupRequest' }) +export class Request extends SignatureRequirement { + public origin: SessionAction; + + constructor(input: { origin: SessionAction }) { + super(); + validator.isObject(input); + this.origin = input.origin; + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetCompatibleSetupResponse' }) +export class Response extends SignatureRequirement { + public components: ComponentsList; + + constructor(input: { components: ComponentsList }) { + super(); + validator.isObject(input); + this.components = validator.getAsObj(input, 'components'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/get_default_options.ts b/application/platform/ipc/request/components/get_default_options.ts new file mode 100644 index 0000000000..3a4f6c0e51 --- /dev/null +++ b/application/platform/ipc/request/components/get_default_options.ts @@ -0,0 +1,31 @@ +import { Define, Interface, SignatureRequirement } from '../declarations'; +import { FieldList, SessionAction } from '../../../types/bindings'; + +import * as validator from '../../../env/obj'; + +@Define({ name: 'GetDefaultOptionsRequest' }) +export class Request extends SignatureRequirement { + public uuid: string; + public origin: SessionAction; + + constructor(input: { uuid: string; origin: SessionAction }) { + super(); + validator.isObject(input); + this.uuid = validator.getAsNotEmptyString(input, 'uuid'); + this.origin = input.origin; + } +} +export interface Request extends Interface {} + +@Define({ name: 'GetDefaultOptionsResponse' }) +export class Response extends SignatureRequirement { + public fields: FieldList; + + constructor(input: { fields: FieldList }) { + super(); + validator.isObject(input); + this.fields = validator.getAsArray(input, 'fields'); + } +} + +export interface Response extends Interface {} diff --git a/application/platform/ipc/request/components/index.ts b/application/platform/ipc/request/components/index.ts index ed2f18a627..11f1ab8ab1 100644 --- a/application/platform/ipc/request/components/index.ts +++ b/application/platform/ipc/request/components/index.ts @@ -6,3 +6,5 @@ export * as GetOutputRender from './get_output_render'; export * as Abort from './abort'; export * as Validate from './validate'; export * as IsSdeSupported from './is_sde_supported'; +export * as GetCompatibleSetup from './get_compatible_setup'; +export * as GetDefaultOptions from './get_default_options'; diff --git a/application/platform/ipc/request/components/is_sde_supported.ts b/application/platform/ipc/request/components/is_sde_supported.ts index d31591b080..2e210fee75 100644 --- a/application/platform/ipc/request/components/is_sde_supported.ts +++ b/application/platform/ipc/request/components/is_sde_supported.ts @@ -1,5 +1,5 @@ import { Define, Interface, SignatureRequirement } from '../declarations'; -import { OutputRender, SessionAction } from '../../../types/bindings'; +import { SessionAction } from '../../../types/bindings'; import * as validator from '../../../env/obj'; diff --git a/application/platform/types/bindings/observe.ts b/application/platform/types/bindings/observe.ts index 95e66d8809..cba0ebaeea 100644 --- a/application/platform/types/bindings/observe.ts +++ b/application/platform/types/bindings/observe.ts @@ -15,6 +15,8 @@ export type ComponentDef = { Source: ComponentOptions } | { Parser: ComponentOpt */ export type ComponentType = 'Parser' | 'Source'; +export type ComponentsList = { parsers: Array; sources: Array }; + /** * Settings for the DLT parser. */ diff --git a/application/platform/types/bindings/options.ts b/application/platform/types/bindings/options.ts index dca267db4d..8aee577bc9 100644 --- a/application/platform/types/bindings/options.ts +++ b/application/platform/types/bindings/options.ts @@ -18,6 +18,8 @@ export type Field = { id: string; value: Value }; export type FieldDesc = { Static: StaticFieldDesc } | { Lazy: LazyFieldDesc }; +export type FieldList = Array; + export type FieldLoadingError = { id: string; err: string }; export type FieldsValidationErrors = { errors: Map }; diff --git a/docs/charts.md b/docs/charts.md index ab3834a95d..da84315737 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -1,6 +1,6 @@ In addition to search, `chipmunk` provides tools for analyzing metrics and any kind of numerical data. Users can define regular expressions to extract specific values, which are then used to generate charts. -A key advantage of `chipmunk` is its near insensitivity to data volume — it performs equally well when processing a few hundred values or several million. +A key advantage of `chipmunk` is its near insensitivity to data volume - it performs equally well when processing a few hundred values or several million. ### Chart creating diff --git a/docs/index.md b/docs/index.md index 04a8085c66..ad5f4a6914 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,7 @@ [![](https://github.com/esrlabs/chipmunk/actions/workflows/release_next.yml/badge.svg)](https://github.com/esrlabs/chipmunk/actions/workflows/release_next.yml) [![](https://github.com/esrlabs/chipmunk/actions/workflows/lint_master.yml/badge.svg)](https://github.com/esrlabs/chipmunk/actions/workflows/lint_master.yml) -`chipmunk` is one of the fastest desktop applications for viewing log files, with no limitations on file size. 1 GB, 2 GB, 10 GB? `chipmunk` is limited only by your disk space — nothing more. With no caching and no unnecessary copying, files of any size open with the same speed. But `chipmunk` goes beyond just working with files: it also allows you to create network connections to collect logs via TCP, UDP, Serial, or from the output of a running command. +`chipmunk` is one of the fastest desktop applications for viewing log files, with no limitations on file size. 1 GB, 2 GB, 10 GB? `chipmunk` is limited only by your disk space - nothing more. With no caching and no unnecessary copying, files of any size open with the same speed. But `chipmunk` goes beyond just working with files: it also allows you to create network connections to collect logs via TCP, UDP, Serial, or from the output of a running command. ## Automotive and Network Traces @@ -19,7 +19,7 @@ Additionally, `chipmunk` allows you to work with DLT traces both as standalone f - Serial Port - Output from a command or program -For each source, you can assign a parser — for example, to collect DLT packets over a UDP connection for analysis or to save them as a standalone trace file. +For each source, you can assign a parser - for example, to collect DLT packets over a UDP connection for analysis or to save them as a standalone trace file. Another key feature is the ability to launch any command or program and collect its output, which can be analyzed in real time as it's generated. @@ -29,7 +29,7 @@ At its core, `chipmunk` is a log analysis tool. It goes beyond simple search que ![filters_create](assets/search/filters_create.gif) -The search engine works dynamically — results are updated in real time as new data is added. If you're connected to a live data source, your active filters will continuously update the search results as new logs arrive. +The search engine works dynamically - results are updated in real time as new data is added. If you're connected to a live data source, your active filters will continuously update the search results as new logs arrive. ## Metrics, Measurements, and Graphs diff --git a/docs/installation.md b/docs/installation.md index 2ee301b49e..51088b5636 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,5 +1,5 @@ -`chipmunk` is distributed as a portable version and does not require any installation — just download and run. Additionally: +`chipmunk` is distributed as a portable version and does not require any installation - just download and run. Additionally: - It does not install or depend on any external libraries or runtime environments (with a small exception for Windows). - It includes a built-in update mechanism (you will be notified when a new version is available). diff --git a/docs/teamwork.md b/docs/teamwork.md index 69606972da..24d9d7c317 100644 --- a/docs/teamwork.md +++ b/docs/teamwork.md @@ -1,4 +1,4 @@ -`chipmunk` supports collaborative work on log file analysis. However, it remains a fully standalone application that requires no installation or server infrastructure. Staying true to this philosophy, we use GitHub repositories to enable shared access to data — providing both secure data exchange and access control. +`chipmunk` supports collaborative work on log file analysis. However, it remains a fully standalone application that requires no installation or server infrastructure. Staying true to this philosophy, we use GitHub repositories to enable shared access to data - providing both secure data exchange and access control. ## Before start From abdaffd297ca3f05bc30b2c9de467d515887a7ac Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 4 Aug 2025 14:23:13 +0200 Subject: [PATCH 113/121] Move factory back to trait --- .../apps/indexer/descriptor/src/tys/parser.rs | 36 ++++ .../apps/indexer/descriptor/src/tys/source.rs | 36 ++++ .../indexer/parsers/src/dlt/descriptor.rs | 124 ++++++++------ .../indexer/parsers/src/dlt/raw/descriptor.rs | 23 ++- .../indexer/parsers/src/someip/descriptor.rs | 52 ++++-- .../indexer/parsers/src/text/descriptor.rs | 17 +- .../src/plugins_manager/descriptor.rs | 158 +++++++++--------- .../plugins_host/src/plugins_manager/mod.rs | 6 +- application/apps/indexer/register/src/lib.rs | 151 ++++++++--------- .../apps/indexer/session/src/register/mod.rs | 55 ++---- .../src/binary/pcap/legacy/descriptor.rs | 10 +- .../sources/src/binary/pcap/ng/descriptor.rs | 12 +- .../sources/src/binary/raw/descriptor.rs | 45 ++--- .../indexer/sources/src/command/descriptor.rs | 51 ++++-- .../indexer/sources/src/serial/descriptor.rs | 110 +++++++----- .../sources/src/socket/tcp/descriptor.rs | 47 ++++-- .../sources/src/socket/udp/descriptor.rs | 99 ++++++----- 17 files changed, 591 insertions(+), 441 deletions(-) diff --git a/application/apps/indexer/descriptor/src/tys/parser.rs b/application/apps/indexer/descriptor/src/tys/parser.rs index a0e65a6ea5..cf0d72e230 100644 --- a/application/apps/indexer/descriptor/src/tys/parser.rs +++ b/application/apps/indexer/descriptor/src/tys/parser.rs @@ -1,5 +1,41 @@ use super::CommonDescriptor; +/// This trait fully defines a parser entity (as it inherits the implementation of auxiliary traits) +/// and is used by the `Register` to store parser descriptors. +/// +/// ## Design Note +/// +/// In one of the earlier implementations, there was an attempt to separate the factory method (`create`) +/// into an independent entity. In other words, instead of storing +/// `HashMap<..., Box>` in the `Register`, the idea was to store +/// `HashMap<..., (FactoryFnPointer, Box)>` - i.e., separating the factory from the descriptor. +/// +/// This approach has clear drawbacks: +/// - First, it breaks the relationship between the `ParserDescriptor` and the factory. For example, +/// the compiler would not raise an error if a textual parser is registered with a descriptor +/// meant for a DLT parser. +/// - Second (and more critically), it prevents passing `self` into the factory method, which is essential +/// both for proper error validation and for cases where the descriptor itself contains +/// key information required for parser instantiation. This is especially relevant for plugins, +/// where the physical path to the plugin file (stored in the descriptor) must be accessible to the factory. +/// +/// Due to these factors, the decision was made to return to the original design, where the factory +/// is represented as a trait tightly coupled with the rest of the descriptor logic. +/// +/// ## Generic Type +/// +/// The trait is generic over type `T`, which always refers to the `parsers::Parsers` type. +/// The use of a generic parameter here serves only one purpose: to avoid cyclic dependencies +/// within the solution, while also preventing the need to excessively split the codebase +/// into small crates. +pub trait ParserFactory: ParserDescriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError>; +} + /// Defines parser-specific behavior and capabilities. /// /// This trait extends [`CommonDescriptor`] and provides an interface for diff --git a/application/apps/indexer/descriptor/src/tys/source.rs b/application/apps/indexer/descriptor/src/tys/source.rs index 004429738d..3d5f11c626 100644 --- a/application/apps/indexer/descriptor/src/tys/source.rs +++ b/application/apps/indexer/descriptor/src/tys/source.rs @@ -1,5 +1,41 @@ use super::CommonDescriptor; +/// This trait fully defines a source entity (as it inherits the implementation of auxiliary traits) +/// and is used by the `Register` to store source descriptors. +/// +/// ## Design Note +/// +/// In one of the earlier implementations, there was an attempt to separate the factory method (`create`) +/// into an independent entity. In other words, instead of storing +/// `HashMap<..., Box>` in the `Register`, the idea was to store +/// `HashMap<..., (FactoryFnPointer, Box)>` - i.e., separating the factory from the descriptor. +/// +/// This approach has clear drawbacks: +/// - First, it breaks the relationship between the `SourceDescriptor` and the factory. For example, +/// the compiler would not raise an error if a tcp source is registered with a descriptor +/// meant for a serial source. +/// - Second (and more critically), it prevents passing `self` into the factory method, which is essential +/// both for proper error validation and for cases where the descriptor itself contains +/// key information required for source instantiation. This is especially relevant for plugins, +/// where the physical path to the plugin file (stored in the descriptor) must be accessible to the factory. +/// +/// Due to these factors, the decision was made to return to the original design, where the factory +/// is represented as a trait tightly coupled with the rest of the descriptor logic. +/// +/// ## Generic Type +/// +/// The trait is generic over type `T`, which always refers to the `sources::Sources` type. +/// The use of a generic parameter here serves only one purpose: to avoid cyclic dependencies +/// within the solution, while also preventing the need to excessively split the codebase +/// into small crates. +pub trait SourceFactory: SourceDescriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError>; +} + /// Defines source-specific capabilities and behaviors. /// /// This trait extends [`CommonDescriptor`] to describe features unique to data sources. diff --git a/application/apps/indexer/parsers/src/dlt/descriptor.rs b/application/apps/indexer/parsers/src/dlt/descriptor.rs index 18b19bc439..d28a75d06b 100644 --- a/application/apps/indexer/parsers/src/dlt/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/descriptor.rs @@ -1,6 +1,7 @@ -use crate::dlt::*; +use crate::{Parsers, dlt::*}; use ::descriptor::{ - CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor, StaticFieldResult, + CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor, ParserFactory, + StaticFieldResult, }; use dlt_core::{ read::DltMessageReader, @@ -50,57 +51,74 @@ impl fmt::Display for StatFields { } } -pub fn factory( - origin: &SessionAction, - options: &[Field], -) -> Result)>, NativeError> { - let fibex_file_paths: &Vec = options - .extract_by_key(FIELD_FIBEX_FILES) - .ok_or(missed(FIELD_FIBEX_FILES))? - .value; - let dlt_metadata = dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { - fibex_file_paths: fibex_file_paths - .iter() - .map(|p| p.to_string_lossy().to_string()) - .collect(), - }); - let someip_metadata = FibexSomeipMetadata::from_fibex_files(fibex_file_paths); - let min_log_level = u8_to_log_level( - options - .extract_by_key(FIELD_LOG_LEVEL) - .ok_or(missed(FIELD_LOG_LEVEL))? - .value, - ) - .ok_or(missed(FIELD_LOG_LEVEL))?; - let stats: &HashMap> = options - .extract_by_key(FIELD_STATISTICS) - .ok_or(missed(FIELD_STATISTICS))? - .value; - let filter_config: ProcessedDltFilterConfig = ProcessedDltFilterConfig { - min_log_level: Some(min_log_level), - app_ids: stats - .get(&StatFields::AppIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - ecu_ids: stats - .get(&StatFields::EcuIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - context_ids: stats - .get(&StatFields::ContextIds.to_string()) - .map(|fields| fields.iter().cloned().collect()), - app_id_count: 0, - context_id_count: 0, - }; - Ok(Some(( - crate::Parsers::Dlt(DltParser::new( - Some(filter_config), - dlt_metadata, - None, - someip_metadata, - // If it's source - no storage header expected - !matches!(origin, SessionAction::Source), - )), - Some("DLT".to_owned()), - ))) +impl ParserFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let fibex_file_paths: &Vec = options + .extract_by_key(FIELD_FIBEX_FILES) + .ok_or(missed(FIELD_FIBEX_FILES))? + .value; + let dlt_metadata = dlt_core::fibex::gather_fibex_data(dlt_core::fibex::FibexConfig { + fibex_file_paths: fibex_file_paths + .iter() + .map(|p| p.to_string_lossy().to_string()) + .collect(), + }); + let someip_metadata = FibexSomeipMetadata::from_fibex_files(fibex_file_paths); + let min_log_level = u8_to_log_level( + options + .extract_by_key(FIELD_LOG_LEVEL) + .ok_or(missed(FIELD_LOG_LEVEL))? + .value, + ) + .ok_or(missed(FIELD_LOG_LEVEL))?; + let stats: &HashMap> = options + .extract_by_key(FIELD_STATISTICS) + .ok_or(missed(FIELD_STATISTICS))? + .value; + let filter_config: ProcessedDltFilterConfig = ProcessedDltFilterConfig { + min_log_level: Some(min_log_level), + app_ids: stats + .get(&StatFields::AppIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + ecu_ids: stats + .get(&StatFields::EcuIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + context_ids: stats + .get(&StatFields::ContextIds.to_string()) + .map(|fields| fields.iter().cloned().collect()), + app_id_count: 0, + context_id_count: 0, + }; + Ok(Some(( + crate::Parsers::Dlt(DltParser::new( + Some(filter_config), + dlt_metadata, + None, + someip_metadata, + // If it's source - no storage header expected + !matches!(origin, SessionAction::Source), + )), + Some("DLT".to_owned()), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs index 7b712b6ac9..9bf2f022b2 100644 --- a/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs +++ b/application/apps/indexer/parsers/src/dlt/raw/descriptor.rs @@ -1,6 +1,8 @@ use super::*; use crate::*; -use ::descriptor::{CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor}; +use ::descriptor::{ + CommonDescriptor, FieldsResult, LazyFieldsTask, ParserDescriptor, ParserFactory, +}; use stypes::SessionAction; use tokio_util::sync::CancellationToken; @@ -11,14 +13,17 @@ const DLT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -pub fn factory( - origin: &SessionAction, - _options: &[stypes::Field], -) -> Result)>, stypes::NativeError> { - Ok(Some(( - Parsers::DltRaw(DltRawParser::new(!matches!(origin, SessionAction::Source))), - Some("DLT".to_string()), - ))) +impl ParserFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + _options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + Ok(Some(( + Parsers::DltRaw(DltRawParser::new(!matches!(origin, SessionAction::Source))), + Some("DLT".to_string()), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/parsers/src/someip/descriptor.rs b/application/apps/indexer/parsers/src/someip/descriptor.rs index 1b2c1c3adb..9d74e78db6 100644 --- a/application/apps/indexer/parsers/src/someip/descriptor.rs +++ b/application/apps/indexer/parsers/src/someip/descriptor.rs @@ -1,9 +1,10 @@ -use crate::someip::FibexMetadata; +use crate::{Parsers, someip::FibexMetadata}; use ::descriptor::{CommonDescriptor, FieldsResult, ParserDescriptor}; +use descriptor::ParserFactory; use std::path::PathBuf; use stypes::{ - ComponentOptions, ExtractByKey, Field, FieldDesc, SessionAction, StaticFieldDesc, Value, - ValueInput, missed_field_err as missed, + ComponentOptions, ExtractByKey, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, Value, ValueInput, missed_field_err as missed, }; use super::SomeipParser; @@ -20,20 +21,37 @@ const FIELD_TZ: &str = "SOMEIP_PARSER_FIELD_TIMEZONE"; #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - options: &[stypes::Field], -) -> Result)>, stypes::NativeError> { - let fibex_file_paths: &Vec = options - .extract_by_key(FIELD_FIBEX_FILES) - .ok_or(missed(FIELD_FIBEX_FILES))? - .value; - Ok(Some(( - crate::Parsers::SomeIp(SomeipParser { - fibex_metadata: FibexMetadata::from_fibex_files(fibex_file_paths), - }), - Some("SomeIp".to_owned()), - ))) +impl ParserFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let fibex_file_paths: &Vec = options + .extract_by_key(FIELD_FIBEX_FILES) + .ok_or(missed(FIELD_FIBEX_FILES))? + .value; + Ok(Some(( + crate::Parsers::SomeIp(SomeipParser { + fibex_metadata: FibexMetadata::from_fibex_files(fibex_file_paths), + }), + Some("SomeIp".to_owned()), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/parsers/src/text/descriptor.rs b/application/apps/indexer/parsers/src/text/descriptor.rs index ede8261079..7803266425 100644 --- a/application/apps/indexer/parsers/src/text/descriptor.rs +++ b/application/apps/indexer/parsers/src/text/descriptor.rs @@ -1,7 +1,9 @@ -use descriptor::{CommonDescriptor, ParserDescriptor}; +use descriptor::{CommonDescriptor, ParserDescriptor, ParserFactory}; use file_tools::is_binary; use stypes::{ComponentOptions, SessionAction}; +use crate::Parsers; + use super::StringTokenizer; const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -11,11 +13,14 @@ const TEXT_PARSER_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - _options: &[stypes::Field], -) -> Result)>, stypes::NativeError> { - Ok(Some((crate::Parsers::Text(StringTokenizer {}), None))) +impl ParserFactory for Descriptor { + fn create( + &self, + _origin: &stypes::SessionAction, + _options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + Ok(Some((crate::Parsers::Text(StringTokenizer {}), None))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs index bb3b5b6137..79eac5cd5a 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/descriptor.rs @@ -1,9 +1,15 @@ -use descriptor::{CommonDescriptor, ParserDescriptor, SourceDescriptor}; +use descriptor::{ + CommonDescriptor, ParserDescriptor, ParserFactory, SourceDescriptor, SourceFactory, +}; use stypes::{ - Field, NativeError, NativeErrorKind, PluginEntity, SessionAction, Severity, StaticFieldDesc, + Field, NativeError, NativeErrorKind, PluginConfigItem, PluginEntity, PluginParserSettings, + SessionAction, Severity, StaticFieldDesc, }; +use tokio::runtime::Handle; use tokio_util::sync::CancellationToken; +use crate::PluginsParser; + #[derive(Debug)] pub struct PluginDescriptor { entity: PluginEntity, @@ -121,76 +127,65 @@ impl ParserDescriptor for PluginDescriptor { } } -pub fn parser_factory( - _origin: &SessionAction, - _options: &[Field], -) -> Result)>, NativeError> { - Err(NativeError { - severity: Severity::WARNING, - kind: NativeErrorKind::NotYetImplemented, - message: Some("Support for sources isn't implemented yet".into()), - }) -} +impl ParserFactory for PluginDescriptor { + fn create( + &self, + origin: &SessionAction, + options: &[Field], + ) -> Result)>, NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + + let configs: Vec = options + .iter() + .map(|opt| { + use stypes::PluginConfigValue as PlVal; + + let val = match opt.value.clone() { + stypes::Value::Boolean(val) => PlVal::Boolean(val), + stypes::Value::Number(num) => PlVal::Integer(num as i32), + stypes::Value::String(txt) => PlVal::Text(txt), + stypes::Value::Directories(path_bufs) => PlVal::Directories(path_bufs), + stypes::Value::Files(path_bufs) => PlVal::Files(path_bufs), + unsupported => panic!("Config {unsupported:?} is unsupported by plugins"), + }; + PluginConfigItem::new(opt.id.to_owned(), val) + }) + .collect(); + + let settings = + PluginParserSettings::new(self.entity.dir_path.to_owned(), Default::default(), configs); + + //TODO AAZ: Temp solution by blocking here. + //Create function should be async + let parse_res = tokio::task::block_in_place(move || { + Handle::current().block_on(async move { + PluginsParser::initialize( + &settings.plugin_path, + &settings.general_settings, + settings.plugin_configs.clone(), + ) + .await + }) + })?; -// impl ComponentFactory for PluginDescriptor { -// fn create( -// &self, -// origin: &SessionAction, -// options: &[Field], -// ) -> Result)>, NativeError> { -// let errors = self.validate(origin, options)?; -// if !errors.is_empty() { -// return Err(NativeError { -// kind: NativeErrorKind::Configuration, -// severity: Severity::ERROR, -// message: Some( -// errors -// .values() -// .map(String::as_str) -// .collect::>() -// .join("; "), -// ), -// }); -// } - -// let configs: Vec = options -// .iter() -// .map(|opt| { -// use stypes::PluginConfigValue as PlVal; - -// let val = match opt.value.clone() { -// stypes::Value::Boolean(val) => PlVal::Boolean(val), -// stypes::Value::Number(num) => PlVal::Integer(num as i32), -// stypes::Value::String(txt) => PlVal::Text(txt), -// stypes::Value::Directories(path_bufs) => PlVal::Directories(path_bufs), -// stypes::Value::Files(path_bufs) => PlVal::Files(path_bufs), -// unsupported => panic!("Config {unsupported:?} is unsupported by plugins"), -// }; -// PluginConfigItem::new(opt.id.to_owned(), val) -// }) -// .collect(); - -// let settings = -// PluginParserSettings::new(self.entity.dir_path.to_owned(), Default::default(), configs); - -// //TODO AAZ: Temp solution by blocking here. -// //Create function should be async -// let parse_res = tokio::task::block_in_place(move || { -// Handle::current().block_on(async move { -// PluginsParser::initialize( -// &settings.plugin_path, -// &settings.general_settings, -// settings.plugin_configs.clone(), -// ) -// .await -// }) -// })?; - -// let parser = parsers::Parser::Plugin(Box::new(parse_res)); - -// Ok(Some((parser, Some(self.entity.metadata.title.to_owned())))) -// } -// } + let parser = parsers::Parsers::Plugin(Box::new(parse_res)); + + Ok(Some((parser, Some(self.entity.metadata.title.to_owned())))) + } +} impl SourceDescriptor for PluginDescriptor { fn is_sde_supported(&self, _origin: &stypes::SessionAction) -> bool { @@ -198,13 +193,16 @@ impl SourceDescriptor for PluginDescriptor { } } -pub fn source_factory( - _origin: &SessionAction, - _options: &[Field], -) -> Result)>, NativeError> { - Err(NativeError { - severity: Severity::WARNING, - kind: NativeErrorKind::NotYetImplemented, - message: Some("Support for sources isn't implemented yet".into()), - }) +impl SourceFactory for PluginDescriptor { + fn create( + &self, + _origin: &stypes::SessionAction, + _options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + Err(NativeError { + severity: Severity::WARNING, + kind: NativeErrorKind::NotYetImplemented, + message: Some("Support for sources isn't implemented yet".into()), + }) + } } diff --git a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs index c1ada8f583..b385bea7eb 100644 --- a/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs +++ b/application/apps/indexer/plugins_host/src/plugins_manager/mod.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use crate::plugins_shared::load::{WasmComponentInfo, load_and_inspect}; use cache::CacheManager; -use descriptor::{PluginDescriptor, parser_factory, source_factory}; +use descriptor::PluginDescriptor; use load::{PluginEntityState, load_plugin}; use paths::extract_plugin_file_paths; use register::Register; @@ -323,10 +323,10 @@ impl PluginsManager { let plug_info = plugin.entity.clone(); match plugin.entity.plugin_type { PluginType::Parser => { - components.add_parser(parser_factory, PluginDescriptor::new(plug_info))?; + components.add_parser(PluginDescriptor::new(plug_info))?; } PluginType::ByteSource => { - components.add_source(source_factory, PluginDescriptor::new(plug_info))?; + components.add_source(PluginDescriptor::new(plug_info))?; } } } diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index 891437905d..780488847a 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -6,31 +6,26 @@ use tokio_util::sync::CancellationToken; use uuid::Uuid; use descriptor::*; -use parsers::{api::*, Parsers}; -use sources::{api::*, Sources}; +use parsers::Parsers; +use sources::Sources; -/// Registry of all available parsers and sources in the system. +/// The `Register` struct holds information about all available source and parser components +/// in the system. It is used as a central registry during initialization and component creation. /// -/// This struct acts as a central repository that holds references to all registered -/// parser and source components, along with their factory functions and descriptors. +/// Each entry associates a unique identifier (`Uuid`) with the corresponding factory object. +/// These factories are responsible for creating concrete instances of sources and parsers +/// and also act as their descriptors by implementing the associated traits. /// -/// During session setup, the appropriate factory functions and metadata can be -/// retrieved using UUIDs to create concrete parser and source instances, validate -/// configuration fields, and present component metadata to the user interface. +/// - `sources`: A map of source factories. Each factory implements `SourceFactory` +/// and provides metadata and construction logic for a specific source type. +/// - `parsers`: A map of parser factories. Each factory implements `ParserFactory` +/// and defines both the parser metadata and instantiation logic. +/// +/// By storing trait objects (`Box`), the registry supports dynamic extensibility, +/// including plugin-based components. pub struct Register { - /// A map of source component UUIDs to their factory functions and descriptors. - /// - /// Each entry contains: - /// - a [`SourceFactory`] used to instantiate the component, - /// - a boxed [`SourceDescriptor`] that describes its capabilities and configuration. - sources: HashMap)>, - - /// A map of parser component UUIDs to their factory functions and descriptors. - /// - /// Each entry contains: - /// - a [`ParserFactory`] used to instantiate the component, - /// - a boxed [`ParserDescriptor`] that describes its capabilities and configuration. - parsers: HashMap)>, + sources: HashMap>>, + parsers: HashMap>>, } impl Register { @@ -45,24 +40,22 @@ impl Register { } } - /// Registers a new parser component in the system. + /// Registers a new parser factory in the registry. /// - /// Associates a parser factory and descriptor with the parser's UUID. - /// If a parser with the same UUID is already registered, this will return an error. + /// The factory must implement the `ParserFactory` trait and will be stored + /// under its associated UUID. If a parser with the same UUID has already been registered, + /// an error of kind `Configuration` will be returned. /// - /// # Arguments - /// * `factory` – A function capable of instantiating the parser. - /// * `descriptor` – Metadata describing the parser's behavior and configuration. + /// # Type Parameters + /// - `D`: A concrete type that implements `ParserFactory`. /// - /// # Returns - /// * `Ok(())` – If the parser was successfully registered. - /// * `Err(NativeError)` – If a parser with the same UUID already exists. - pub fn add_parser( + /// # Errors + /// Returns `NativeError` if a parser with the same UUID is already registered. + pub fn add_parser + 'static>( &mut self, - factory: ParserFactory, - descriptor: D, + factory: D, ) -> Result<(), stypes::NativeError> { - let ident = descriptor.ident(); + let ident = factory.ident(); if self.parsers.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -70,29 +63,26 @@ impl Register { message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.parsers - .insert(ident.uuid, (factory, Box::new(descriptor))); + self.parsers.insert(ident.uuid, Box::new(factory)); Ok(()) } - /// Registers a new source component in the system. + /// Registers a new source factory in the registry. /// - /// Associates a source factory and descriptor with the source's UUID. - /// If a source with the same UUID is already registered, this will return an error. + /// The factory must implement the `SourceFactory` trait and will be stored + /// under its associated UUID. If a source with the same UUID has already been registered, + /// an error of kind `Configuration` will be returned. /// - /// # Arguments - /// * `factory` – A function capable of instantiating the source. - /// * `descriptor` – Metadata describing the source's behavior and configuration. + /// # Type Parameters + /// - `D`: A concrete type that implements `SourceFactory`. /// - /// # Returns - /// * `Ok(())` – If the source was successfully registered. - /// * `Err(NativeError)` – If a source with the same UUID already exists. - pub fn add_source( + /// # Errors + /// Returns `NativeError` if a source with the same UUID is already registered. + pub fn add_source + 'static>( &mut self, - factory: SourceFactory, - descriptor: D, + factory: D, ) -> Result<(), stypes::NativeError> { - let ident: stypes::Ident = descriptor.ident(); + let ident: stypes::Ident = factory.ident(); if self.sources.contains_key(&ident.uuid) { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, @@ -100,8 +90,7 @@ impl Register { message: Some(format!("{} ({}) already registred", ident.name, ident.uuid)), }); } - self.sources - .insert(ident.uuid, (factory, Box::new(descriptor))); + self.sources.insert(ident.uuid, Box::new(factory)); Ok(()) } @@ -118,9 +107,9 @@ impl Register { pub fn get_parsers(&self, origin: stypes::SessionAction) -> Vec { self.parsers .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) + .filter_map(|(_, entity)| { + if entity.is_compatible(&origin) { + Some(entity.ident()) } else { None } @@ -141,9 +130,9 @@ impl Register { pub fn get_sources(&self, origin: stypes::SessionAction) -> Vec { self.sources .iter() - .filter_map(|(_, (_, desc))| { - if desc.is_compatible(&origin) { - Some(desc.ident()) + .filter_map(|(_, entity)| { + if entity.is_compatible(&origin) { + Some(entity.ident()) } else { None } @@ -204,10 +193,10 @@ impl Register { let descriptors = self .descriptors() .into_iter() - .filter_map(|(uuid, desc)| { + .filter_map(|(uuid, entity)| { if targets.contains(uuid) { targets.retain(|v| v != uuid); - Some(desc) + Some(entity) } else { None } @@ -228,17 +217,17 @@ impl Register { }); } let mut list: HashMap = HashMap::new(); - for desc in descriptors.into_iter() { - let mut options = OptionsScheme::new(desc.fields_getter(&origin)?); + for entity in descriptors.into_iter() { + let mut options = OptionsScheme::new(entity.fields_getter(&origin)?); if options.has_lazy() { let cancel = CancellationToken::new(); options.set_lazy( - desc.ident(), - desc.lazy_fields_getter(origin.clone(), cancel.clone()), + entity.ident(), + entity.lazy_fields_getter(origin.clone(), cancel.clone()), &cancel, ); } - list.insert(desc.ident().uuid, options); + list.insert(entity.ident().uuid, options); } Ok(list) } @@ -262,7 +251,7 @@ impl Register { &self, uuid: &Uuid, ) -> Result, stypes::NativeError> { - let (_, descriptor) = self.parsers.get(uuid).ok_or(stypes::NativeError { + let descriptor = self.parsers.get(uuid).ok_or(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find component {uuid}")), @@ -294,7 +283,7 @@ impl Register { uuid: &Uuid, origin: &stypes::SessionAction, ) -> Result { - let (_, descriptor) = self.sources.get(uuid).ok_or(stypes::NativeError { + let descriptor = self.sources.get(uuid).ok_or(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find component {uuid}")), @@ -311,9 +300,9 @@ impl Register { /// * `Some(Ident)` – If the parser or source has been found. /// * `None` – If no component is defined. pub fn get_ident(&self, uuid: &Uuid) -> Option { - self.descriptors().into_iter().find_map(|(inner, desc)| { + self.descriptors().into_iter().find_map(|(inner, entity)| { if uuid == inner { - Some(desc.ident()) + Some(entity.ident()) } else { None } @@ -427,7 +416,7 @@ impl Register { let sources: Vec = self .sources .iter() - .filter_map(|(_, (_, entity))| { + .filter_map(|(_, entity)| { if entity.is_compatible(origin) && entity.get_default_options(origin).is_some() && entity.ident().io == io_type @@ -441,7 +430,7 @@ impl Register { let parsers: Vec = self .parsers .iter() - .filter_map(|(_, (_, entity))| { + .filter_map(|(_, entity)| { if entity.is_compatible(origin) && entity.get_default_options(origin).is_some() && entity.ident().io == io_type @@ -534,16 +523,14 @@ impl Register { &self, options: &stypes::SessionSetup, ) -> Result<(SessionDescriptor, Sources, Parsers), stypes::NativeError> { - let Some((parser_factory, parser_descriptor)) = self.parsers.get(&options.parser.uuid) - else { + let Some(parser_factory) = self.parsers.get(&options.parser.uuid) else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, message: Some(format!("Fail to find parser {}", options.parser.uuid)), }); }; - let Some((source_factory, source_descriptor)) = self.sources.get(&options.source.uuid) - else { + let Some(source_factory) = self.sources.get(&options.source.uuid) else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -551,10 +538,12 @@ impl Register { }); }; let mut descriptor = SessionDescriptor::new( - source_descriptor.bound_ident(&options.origin, &options.source.fields), - parser_descriptor.bound_ident(&options.origin, &options.parser.fields), + source_factory.bound_ident(&options.origin, &options.source.fields), + parser_factory.bound_ident(&options.origin, &options.parser.fields), ); - let Some((parser, desc)) = parser_factory(&options.origin, &options.parser.fields)? else { + let Some((parser, desc)) = + parser_factory.create(&options.origin, &options.parser.fields)? + else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -562,7 +551,9 @@ impl Register { }); }; descriptor.set_parser_desc(desc); - let Some((source, desc)) = source_factory(&options.origin, &options.source.fields)? else { + let Some((source, desc)) = + source_factory.create(&options.origin, &options.source.fields)? + else { return Err(stypes::NativeError { severity: stypes::Severity::ERROR, kind: stypes::NativeErrorKind::Configuration, @@ -577,11 +568,11 @@ impl Register { [ self.sources .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .map(|(uuid, entity)| (uuid, entity.as_ref() as &dyn CommonDescriptor)) .collect::>(), self.parsers .iter() - .map(|(uuid, (_, desc))| (uuid, desc.as_ref() as &dyn CommonDescriptor)) + .map(|(uuid, entity)| (uuid, entity.as_ref() as &dyn CommonDescriptor)) .collect::>(), ] .concat() diff --git a/application/apps/indexer/session/src/register/mod.rs b/application/apps/indexer/session/src/register/mod.rs index b788c9a769..33d46d6714 100644 --- a/application/apps/indexer/session/src/register/mod.rs +++ b/application/apps/indexer/session/src/register/mod.rs @@ -779,53 +779,20 @@ fn response>(res: Result, msg: S) -> Result Result<(), stypes::NativeError> { - register.add_parser( - parsers::dlt::descriptor::factory, - parsers::dlt::descriptor::Descriptor::default(), - )?; - register.add_parser( - parsers::dlt::descriptor::factory, - parsers::dlt::raw::descriptor::Descriptor::default(), - )?; - register.add_parser( - parsers::someip::descriptor::factory, - parsers::someip::descriptor::Descriptor::default(), - )?; - register.add_parser( - parsers::text::descriptor::factory, - parsers::text::descriptor::Descriptor::default(), - )?; + register.add_parser(parsers::dlt::descriptor::Descriptor::default())?; + register.add_parser(parsers::dlt::raw::descriptor::Descriptor::default())?; + register.add_parser(parsers::someip::descriptor::Descriptor::default())?; + register.add_parser(parsers::text::descriptor::Descriptor::default())?; Ok(()) } pub fn sources_registration(register: &mut Register) -> Result<(), stypes::NativeError> { - register.add_source( - sources::binary::raw::factory, - sources::binary::raw::Descriptor::default(), - )?; - register.add_source( - sources::binary::pcap::legacy::factory, - sources::binary::pcap::legacy::Descriptor::default(), - )?; - register.add_source( - sources::binary::pcap::ng::factory, - sources::binary::pcap::ng::Descriptor::default(), - )?; - register.add_source( - sources::socket::tcp::factory, - sources::socket::tcp::Descriptor::default(), - )?; - register.add_source( - sources::socket::udp::factory, - sources::socket::udp::Descriptor::default(), - )?; - register.add_source( - sources::serial::descriptor::factory, - sources::serial::descriptor::Descriptor::default(), - )?; - register.add_source( - sources::command::factory, - sources::command::Descriptor::default(), - )?; + register.add_source(sources::binary::raw::Descriptor::default())?; + register.add_source(sources::binary::pcap::legacy::Descriptor::default())?; + register.add_source(sources::binary::pcap::ng::Descriptor::default())?; + register.add_source(sources::socket::tcp::Descriptor::default())?; + register.add_source(sources::socket::udp::Descriptor::default())?; + register.add_source(sources::serial::descriptor::Descriptor::default())?; + register.add_source(sources::command::Descriptor::default())?; Ok(()) } diff --git a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs index 2d42401ce9..98ae3ba1c5 100644 --- a/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/legacy/descriptor.rs @@ -1,4 +1,4 @@ -use descriptor::{SourceDescriptor, CommonDescriptor}; +use descriptor::{CommonDescriptor, SourceDescriptor, SourceFactory}; use file_tools::is_binary; use stypes::{ComponentOptions, NativeError, NativeErrorKind, SessionAction, Severity}; use crate::*; @@ -11,8 +11,10 @@ const PCAP_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} - pub fn factory( - origin: &SessionAction, +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, _options: &[stypes::Field], ) -> Result)>, stypes::NativeError> { let filepath = match origin { @@ -27,7 +29,7 @@ pub struct Descriptor {} }; Ok(Some((Sources::Pcap(PcapLegacyByteSourceFromFile::new(filepath)?), Some("Pcap".to_owned())))) } - +} impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { diff --git a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs index 86d8faf93f..03782887c5 100644 --- a/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/pcap/ng/descriptor.rs @@ -1,4 +1,4 @@ -use descriptor::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, SourceDescriptor, SourceFactory}; use file_tools::is_binary; use stypes::{ComponentOptions, NativeError, NativeErrorKind, SessionAction, Severity}; use super::PcapngByteSourceFromFile; @@ -11,11 +11,13 @@ const PCAPNG_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} - pub fn factory( - origin: &SessionAction, +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, _options: &[stypes::Field], ) -> Result)>, stypes::NativeError> { - let filepath = match origin { + let filepath = match origin { SessionAction::File(file) => file, SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { return Err(NativeError { @@ -27,7 +29,7 @@ pub struct Descriptor {} }; Ok(Some((Sources::PcapNg(PcapngByteSourceFromFile::new(filepath)?), Some("PcapNg".to_owned())))) } - +} impl CommonDescriptor for Descriptor { fn is_compatible(&self, origin: &SessionAction) -> bool { diff --git a/application/apps/indexer/sources/src/binary/raw/descriptor.rs b/application/apps/indexer/sources/src/binary/raw/descriptor.rs index 6d8329d479..1ff632aecb 100644 --- a/application/apps/indexer/sources/src/binary/raw/descriptor.rs +++ b/application/apps/indexer/sources/src/binary/raw/descriptor.rs @@ -1,5 +1,5 @@ use crate::*; -use descriptor::{CommonDescriptor, SourceDescriptor}; +use descriptor::{CommonDescriptor, SourceDescriptor, SourceFactory}; use file_tools::is_binary; use stypes::{ComponentOptions, SessionAction}; @@ -10,27 +10,30 @@ const BIN_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ #[derive(Default)] pub struct Descriptor {} -pub fn factory( - origin: &SessionAction, - _options: &[stypes::Field], -) -> Result)>, stypes::NativeError> { - let filename = match origin { - SessionAction::File(filename) => filename, - SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { - return Err(stypes::NativeError { - severity: stypes::Severity::ERROR, - kind: stypes::NativeErrorKind::Configuration, - message: Some( - "origin isn't supported by this source (BinaryByteSource)".to_owned(), - ), - }); - } - }; +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + _options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let filename = match origin { + SessionAction::File(filename) => filename, + SessionAction::Files(..) | SessionAction::Source | SessionAction::ExportRaw(..) => { + return Err(stypes::NativeError { + severity: stypes::Severity::ERROR, + kind: stypes::NativeErrorKind::Configuration, + message: Some( + "origin isn't supported by this source (BinaryByteSource)".to_owned(), + ), + }); + } + }; - Ok(Some(( - Sources::Raw(super::BinaryByteSourceFromFile::new(filename)?), - Some(filename.to_string_lossy().to_string()), - ))) + Ok(Some(( + Sources::Raw(super::BinaryByteSourceFromFile::new(filename)?), + Some(filename.to_string_lossy().to_string()), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/sources/src/command/descriptor.rs b/application/apps/indexer/sources/src/command/descriptor.rs index 102026097d..a2e7b79f6b 100644 --- a/application/apps/indexer/sources/src/command/descriptor.rs +++ b/application/apps/indexer/sources/src/command/descriptor.rs @@ -1,10 +1,10 @@ use super::ProcessSource; use crate::*; -use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor, SourceFactory}; use std::{collections::HashMap, env}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, NativeError, SessionAction, StaticFieldDesc, - ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; const TERM_SOURCE_UUID: uuid::Uuid = uuid::Uuid::from_bytes([ @@ -18,20 +18,37 @@ const FIELD_SHELLS: &str = "COMMAND_FIELD_SHELLS"; #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - options: &[Field], -) -> Result)>, NativeError> { - let command: String = options - .extract_by_key(FIELD_COMMAND) - .ok_or(missed(FIELD_COMMAND))? - .value; - Ok(Some(( - Sources::Process( - ProcessSource::new(&command, env::current_dir().unwrap(), HashMap::new()).unwrap(), - ), - Some(command), - ))) +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let command: String = options + .extract_by_key(FIELD_COMMAND) + .ok_or(missed(FIELD_COMMAND))? + .value; + Ok(Some(( + Sources::Process( + ProcessSource::new(&command, env::current_dir().unwrap(), HashMap::new()).unwrap(), + ), + Some(command), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/sources/src/serial/descriptor.rs b/application/apps/indexer/sources/src/serial/descriptor.rs index 961e58309d..8b4d6203f2 100644 --- a/application/apps/indexer/sources/src/serial/descriptor.rs +++ b/application/apps/indexer/sources/src/serial/descriptor.rs @@ -1,12 +1,13 @@ use crate::prelude::SerialSource; use crate::*; use descriptor::{ - CommonDescriptor, FieldsResult, LazyFieldsTask, SourceDescriptor, StaticFieldResult, + CommonDescriptor, FieldsResult, LazyFieldsTask, SourceDescriptor, SourceFactory, + StaticFieldResult, }; use std::{collections::HashMap, str}; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, SessionAction, - StaticFieldDesc, ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, LazyFieldDesc, NativeError, NativeErrorKind, + SessionAction, Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; use super::serialport::SerialConfig; @@ -37,49 +38,66 @@ const FIELD_PORTS_LIST: &str = "SERIAL_SOURCE_PORTS_LIST_FIELD"; #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - options: &[Field], -) -> Result)>, NativeError> { - let path: String = options - .extract_by_key(FIELD_PATH) - .ok_or(missed(FIELD_PATH))? - .value; - let config = SerialConfig { - path: path.clone(), - baud_rate: options - .extract_by_key(FIELD_BAUD_RATE) - .ok_or(missed(FIELD_BAUD_RATE))? - .value, - data_bits: options - .extract_by_key(FIELD_DATA_BITS) - .ok_or(missed(FIELD_DATA_BITS))? - .value, - flow_control: options - .extract_by_key(FIELD_FLOW_CONTROL) - .ok_or(missed(FIELD_FLOW_CONTROL))? - .value, - parity: options - .extract_by_key(FIELD_PARITY) - .ok_or(missed(FIELD_PARITY))? - .value, - stop_bits: options - .extract_by_key(FIELD_STOP_BITS) - .ok_or(missed(FIELD_STOP_BITS))? - .value, - send_data_delay: options - .extract_by_key(FIELD_SEND_DATA_DELAY) - .ok_or(missed(FIELD_SEND_DATA_DELAY))? - .value, - exclusive: options - .extract_by_key(FIELD_EXCLUSIVE) - .ok_or(missed(FIELD_EXCLUSIVE))? - .value, - }; - Ok(Some(( - Sources::Serial(SerialSource::new(config)?), - Some(path), - ))) +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let path: String = options + .extract_by_key(FIELD_PATH) + .ok_or(missed(FIELD_PATH))? + .value; + let config = SerialConfig { + path: path.clone(), + baud_rate: options + .extract_by_key(FIELD_BAUD_RATE) + .ok_or(missed(FIELD_BAUD_RATE))? + .value, + data_bits: options + .extract_by_key(FIELD_DATA_BITS) + .ok_or(missed(FIELD_DATA_BITS))? + .value, + flow_control: options + .extract_by_key(FIELD_FLOW_CONTROL) + .ok_or(missed(FIELD_FLOW_CONTROL))? + .value, + parity: options + .extract_by_key(FIELD_PARITY) + .ok_or(missed(FIELD_PARITY))? + .value, + stop_bits: options + .extract_by_key(FIELD_STOP_BITS) + .ok_or(missed(FIELD_STOP_BITS))? + .value, + send_data_delay: options + .extract_by_key(FIELD_SEND_DATA_DELAY) + .ok_or(missed(FIELD_SEND_DATA_DELAY))? + .value, + exclusive: options + .extract_by_key(FIELD_EXCLUSIVE) + .ok_or(missed(FIELD_EXCLUSIVE))? + .value, + }; + Ok(Some(( + Sources::Serial(SerialSource::new(config)?), + Some(path), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs index 45d1f2b1bf..ecc222fd94 100644 --- a/application/apps/indexer/sources/src/socket/tcp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/tcp/descriptor.rs @@ -1,9 +1,9 @@ use crate::*; -use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor, SourceFactory}; use std::collections::HashMap; use stypes::{ - ExtractByKey, Extracted, Field, FieldDesc, NativeError, SessionAction, StaticFieldDesc, - ValueInput, missed_field_err as missed, + ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, SessionAction, + Severity, StaticFieldDesc, ValueInput, missed_field_err as missed, }; use crate::prelude::TcpSource; @@ -18,18 +18,35 @@ const FIELD_IP_ADDR: &str = "TCP_SOURCE_FIELD_IP_ADDR"; #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - options: &[Field], -) -> Result)>, NativeError> { - let addr: String = options - .extract_by_key(FIELD_IP_ADDR) - .ok_or(missed(FIELD_IP_ADDR))? - .value; - Ok(Some(( - Sources::Tcp(TcpSource::new(&addr, None, None)?), - Some(format!("TCP on {addr}")), - ))) +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { + return Err(NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), + }); + } + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + Ok(Some(( + Sources::Tcp(TcpSource::new(&addr, None, None)?), + Some(format!("TCP on {addr}")), + ))) + } } impl CommonDescriptor for Descriptor { diff --git a/application/apps/indexer/sources/src/socket/udp/descriptor.rs b/application/apps/indexer/sources/src/socket/udp/descriptor.rs index 45a687eff8..9d987679be 100644 --- a/application/apps/indexer/sources/src/socket/udp/descriptor.rs +++ b/application/apps/indexer/sources/src/socket/udp/descriptor.rs @@ -1,6 +1,6 @@ use super::{MulticastInfo, UdpSource}; use crate::*; -use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor}; +use descriptor::{CommonDescriptor, FieldsResult, SourceDescriptor, SourceFactory}; use std::collections::HashMap; use stypes::{ ExtractAs, ExtractByKey, Extracted, Field, FieldDesc, NativeError, NativeErrorKind, @@ -17,55 +17,72 @@ const FIELD_MULTICAST_ADDR: &str = "UDP_SOURCE_FIELD_MULTICAST_ADDR"; #[derive(Default)] pub struct Descriptor {} -pub fn factory( - _origin: &SessionAction, - options: &[Field], -) -> Result)>, NativeError> { - let addr: String = options - .extract_by_key(FIELD_IP_ADDR) - .ok_or(missed(FIELD_IP_ADDR))? - .value; - let multicast: &Vec = options - .extract_by_key(FIELD_MULTICAST_ADDR) - .ok_or(missed(FIELD_MULTICAST_ADDR))? - .value; - let mut multicasts = Vec::new(); - for field in multicast { - let pair: &Vec = field.extract_as().ok_or(missed("Multicast Addr"))?; - if pair.len() != 2 { +impl SourceFactory for Descriptor { + fn create( + &self, + origin: &stypes::SessionAction, + options: &[stypes::Field], + ) -> Result)>, stypes::NativeError> { + let errors = self.validate(origin, options)?; + if !errors.is_empty() { return Err(NativeError { kind: NativeErrorKind::Configuration, severity: Severity::ERROR, - message: Some("Invalid settings of multicast address".to_owned()), + message: Some( + errors + .values() + .map(String::as_str) + .collect::>() + .join("; "), + ), }); } - let addr: String = (&pair[0]).extract_as().ok_or(missed("Multicast"))?; - let interface: String = (&pair[1]).extract_as().ok_or(missed("Interface"))?; - multicasts.push(MulticastInfo { - multiaddr: addr - .parse::() - .map_err(|err| NativeError { + let addr: String = options + .extract_by_key(FIELD_IP_ADDR) + .ok_or(missed(FIELD_IP_ADDR))? + .value; + let multicast: &Vec = options + .extract_by_key(FIELD_MULTICAST_ADDR) + .ok_or(missed(FIELD_MULTICAST_ADDR))? + .value; + let mut multicasts = Vec::new(); + for field in multicast { + let pair: &Vec = field.extract_as().ok_or(missed("Multicast Addr"))?; + if pair.len() != 2 { + return Err(NativeError { kind: NativeErrorKind::Configuration, severity: Severity::ERROR, - message: Some(format!("Fail to parse IP: {err}")), + message: Some("Invalid settings of multicast address".to_owned()), + }); + } + let addr: String = (&pair[0]).extract_as().ok_or(missed("Multicast"))?; + let interface: String = (&pair[1]).extract_as().ok_or(missed("Interface"))?; + multicasts.push(MulticastInfo { + multiaddr: addr + .parse::() + .map_err(|err| NativeError { + kind: NativeErrorKind::Configuration, + severity: Severity::ERROR, + message: Some(format!("Fail to parse IP: {err}")), + })?, + interface: if interface.is_empty() { + None + } else { + Some(interface) + }, + }) + } + Ok(Some(( + Sources::Udp( + UdpSource::new(&addr, multicasts).map_err(|err| NativeError { + kind: NativeErrorKind::Io, + severity: Severity::ERROR, + message: Some(err.to_string()), })?, - interface: if interface.is_empty() { - None - } else { - Some(interface) - }, - }) + ), + Some(format!("UDP on {addr}")), + ))) } - Ok(Some(( - Sources::Udp( - UdpSource::new(&addr, multicasts).map_err(|err| NativeError { - kind: NativeErrorKind::Io, - severity: Severity::ERROR, - message: Some(err.to_string()), - })?, - ), - Some(format!("UDP on {addr}")), - ))) } impl CommonDescriptor for Descriptor { From c23f37bf30581a18c20ef851ee5dcac956406d43 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 6 Aug 2025 16:14:33 +0200 Subject: [PATCH 114/121] Fix an issue with stream reference in observed list --- application/client/src/app/service/session/origin.ts | 4 ++-- .../ui/views/sidebar/observe/operation/component.ts | 6 ++++++ .../ui/views/sidebar/observe/operation/template.html | 2 +- .../src/app/ui/views/sidebar/observe/template.html | 12 ++++++++++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 0f7195dc53..054b9acd51 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -152,10 +152,10 @@ export class SessionOrigin { } public isSdeSupported(): Promise { - if (!this.components || !this.components.source) { + if (!this.options || !this.options.source) { return Promise.reject(new Error(`Source isn't defined`)); } - return components.isSdeSupported(this.components.source.uuid, this.origin); + return components.isSdeSupported(this.options.source.uuid, this.origin); } public isFile(): boolean { diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/component.ts b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts index e1bb2bca5b..d52427ebf2 100644 --- a/application/client/src/app/ui/views/sidebar/observe/operation/component.ts +++ b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts @@ -4,6 +4,7 @@ import { State } from '@service/session/dependencies/observing/operation'; import { ObserveOperation } from '@service/session/dependencies/stream'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { getSourceColor } from '@ui/styles/colors'; +import { Stream } from '@service/session/dependencies/stream'; @Component({ selector: 'app-views-observed-operation', @@ -14,6 +15,7 @@ import { getSourceColor } from '@ui/styles/colors'; @Ilc() export class Operation extends ChangesDetector implements AfterContentInit { @Input() operation!: ObserveOperation; + @Input() stream!: Stream; public title!: string; public subtitle: string | undefined = undefined; @@ -50,6 +52,10 @@ export class Operation extends ChangesDetector implements AfterContentInit { ); } + public select() { + this.stream.sde.selecting().select(this.operation.uuid); + } + public stop() { if (!this.operation.isRunning()) { return; diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/template.html b/application/client/src/app/ui/views/sidebar/observe/operation/template.html index f787f8df95..d8d68a5ba4 100644 --- a/application/client/src/app/ui/views/sidebar/observe/operation/template.html +++ b/application/client/src/app/ui/views/sidebar/observe/operation/template.html @@ -1,4 +1,4 @@ -

+

{{title}}

{{subtitle}} diff --git a/application/client/src/app/ui/views/sidebar/observe/template.html b/application/client/src/app/ui/views/sidebar/observe/template.html index b56ba8e49b..ee7c0d8f04 100644 --- a/application/client/src/app/ui/views/sidebar/observe/template.html +++ b/application/client/src/app/ui/views/sidebar/observe/template.html @@ -14,7 +14,11 @@ Active

- +
@@ -28,7 +32,11 @@ Inactive
- +
From 9827e1961faf8f2fd2bf4e1673b6324a0a58cdfd Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 6 Aug 2025 16:37:23 +0200 Subject: [PATCH 115/121] Put color of source back into observed list --- .../app/ui/views/sidebar/observe/operation/component.ts | 7 +++++++ .../src/app/ui/views/sidebar/observe/operation/styles.less | 7 +++++++ .../app/ui/views/sidebar/observe/operation/template.html | 1 + 3 files changed, 15 insertions(+) diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/component.ts b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts index d52427ebf2..052677e21b 100644 --- a/application/client/src/app/ui/views/sidebar/observe/operation/component.ts +++ b/application/client/src/app/ui/views/sidebar/observe/operation/component.ts @@ -20,6 +20,7 @@ export class Operation extends ChangesDetector implements AfterContentInit { public title!: string; public subtitle: string | undefined = undefined; public state!: State; + public color!: string; protected update() { const descriptor = this.operation.getDescriptor(); @@ -32,6 +33,12 @@ export class Operation extends ChangesDetector implements AfterContentInit { this.title = this.operation.getOrigin().getTitle(); this.subtitle = undefined; } + this.color = this.getSourceColor(); + } + + protected getSourceColor(): string { + const id = this.stream.observe().descriptions.id(this.operation.uuid); + return id === undefined ? '' : getSourceColor(id); } constructor(cdRef: ChangeDetectorRef) { diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/styles.less b/application/client/src/app/ui/views/sidebar/observe/operation/styles.less index fd8dc0158f..5e48b4bef8 100644 --- a/application/client/src/app/ui/views/sidebar/observe/operation/styles.less +++ b/application/client/src/app/ui/views/sidebar/observe/operation/styles.less @@ -11,6 +11,13 @@ } & div.desc { flex: auto; + & span.source-color { + position: absolute; + width: 2px; + height: 100%; + left: 0px; + border-radius: 1px; + } & p.title { color: var(--scheme-color-2); } diff --git a/application/client/src/app/ui/views/sidebar/observe/operation/template.html b/application/client/src/app/ui/views/sidebar/observe/operation/template.html index d8d68a5ba4..e2a8f71398 100644 --- a/application/client/src/app/ui/views/sidebar/observe/operation/template.html +++ b/application/client/src/app/ui/views/sidebar/observe/operation/template.html @@ -1,4 +1,5 @@
+

{{title}}

{{subtitle}} From 724315d9de225e0dc08d83b90682f7beb64b4e1f Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Thu, 7 Aug 2025 18:58:04 +0200 Subject: [PATCH 116/121] Fix source color linking --- .../src/app/service/session/dependencies/stream.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index d8c67d843d..bc6abd7a24 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -190,9 +190,12 @@ export class Stream extends Subscriber { .descriptions.request() .then((sources) => { let updated = false; - sources.forEach((source) => { - updated = updated ? updated : operation.addSource(source); - }); + sources + .filter((source) => source.uuid === operationUuid) + .forEach((source) => { + const added = operation.addSource(source); + updated = updated ? updated : added; + }); updated && this.subjects.get().sources.emit(); }) .catch((err: Error) => { @@ -306,7 +309,6 @@ export class Stream extends Subscriber { return operation ? operation.getSource(id) : undefined; }, id: (uuid: string): number | undefined => { - console.error('Not implemented'); // TODO: // `uuid` - is an uuid of observe operation, but if it's Files (aka concat) // we would have multiple sources (files) for a one operation From 059866375d99b63a419a3165f365a4ac505c7ca0 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Mon, 11 Aug 2025 11:42:10 +0200 Subject: [PATCH 117/121] Switch to indexed map (Register) --- application/apps/indexer/Cargo.toml | 1 + application/apps/indexer/register/Cargo.toml | 1 + application/apps/indexer/register/src/lib.rs | 9 +++++---- application/apps/rustcore/rs-bindings/Cargo.lock | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/application/apps/indexer/Cargo.toml b/application/apps/indexer/Cargo.toml index eae850ff05..d3c6cc4813 100644 --- a/application/apps/indexer/Cargo.toml +++ b/application/apps/indexer/Cargo.toml @@ -46,6 +46,7 @@ toml = "0.8" blake3 = "1.8" serialport = "4.6" etherparse = "0.18" +indexmap = "2.10" ## Development Dependencies ## # Support for `html_reports` needs running the benchmarks via `cargo-criterion` tool. diff --git a/application/apps/indexer/register/Cargo.toml b/application/apps/indexer/register/Cargo.toml index 0d40240b19..f8b1178621 100644 --- a/application/apps/indexer/register/Cargo.toml +++ b/application/apps/indexer/register/Cargo.toml @@ -9,6 +9,7 @@ uuid = { workspace = true , features = ["serde", "v4"] } tokio-util.workspace = true tokio = { workspace = true , features = ["full"] } log.workspace = true +indexmap.workspace = true stypes = { path = "../stypes", features=["rustcore"] } descriptor = { path = "../descriptor" } diff --git a/application/apps/indexer/register/src/lib.rs b/application/apps/indexer/register/src/lib.rs index 780488847a..6c3571d9c9 100644 --- a/application/apps/indexer/register/src/lib.rs +++ b/application/apps/indexer/register/src/lib.rs @@ -1,6 +1,7 @@ use std::{collections::HashMap, fmt}; use file_tools::is_path_binary; +use indexmap::IndexMap; use stypes::{NativeError, SessionDescriptor}; use tokio_util::sync::CancellationToken; use uuid::Uuid; @@ -24,8 +25,8 @@ use sources::Sources; /// By storing trait objects (`Box`), the registry supports dynamic extensibility, /// including plugin-based components. pub struct Register { - sources: HashMap>>, - parsers: HashMap>>, + sources: IndexMap>>, + parsers: IndexMap>>, } impl Register { @@ -35,8 +36,8 @@ impl Register { /// [`add_parser`] or [`add_source`]. pub fn new() -> Register { Self { - sources: HashMap::new(), - parsers: HashMap::new(), + sources: IndexMap::new(), + parsers: IndexMap::new(), } } diff --git a/application/apps/rustcore/rs-bindings/Cargo.lock b/application/apps/rustcore/rs-bindings/Cargo.lock index d5bd9c0f39..6b564494da 100644 --- a/application/apps/rustcore/rs-bindings/Cargo.lock +++ b/application/apps/rustcore/rs-bindings/Cargo.lock @@ -1694,9 +1694,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -2838,6 +2838,7 @@ version = "0.1.0" dependencies = [ "descriptor", "file-tools", + "indexmap", "log", "parsers", "sources", From 31144b3e8640eaacc8a48ba4aeec6cae95562023 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 12 Aug 2025 20:33:33 +0200 Subject: [PATCH 118/121] Fix for API access on session configure --- application/client/src/app/service/session.ts | 110 +++++++++++++----- 1 file changed, 83 insertions(+), 27 deletions(-) diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 6b49b3eefc..4b5c6edad6 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -21,6 +21,8 @@ import { File } from '@platform/types/files'; import { SetupObserve } from '@tabs/setup/component'; import { bridge } from '@service/bridge'; import { SessionOrigin } from './session/origin'; +import { popup, Vertical, Horizontal } from '@ui/service/popup'; +import { IApi } from '@ui/tabs/setup/state'; export { Session, TabControls, UnboundTab, Base }; @@ -223,7 +225,8 @@ export class Service extends Implementation { public initialize(): { suggest(filename: string, session?: Session): Promise; auto(origin: SessionOrigin, session?: Session): Promise; - configure(origin: SessionOrigin, session?: Session): Promise; + configure(origin: SessionOrigin): Promise; + attach(session: Session): Promise; observe(origin: SessionOrigin, session?: Session): Promise; multiple(files: File[]): Promise; } { @@ -253,40 +256,93 @@ export class Service extends Implementation { // ? this.initialize().observe(observe, session) // : this.initialize().configure(observe, session); }, - configure: (origin: SessionOrigin, session?: Session): Promise => { + configure: (origin: SessionOrigin): Promise => { return new Promise((resolve) => { + const inputs: { + origin: SessionOrigin; + api: IApi; + } = { + origin, + api: { + finish: (origin: SessionOrigin): Promise => { + return new Promise((success, failed) => { + this.initialize() + .observe(origin) + .then((session) => { + success(session); + api?.close(); + resolve(session); + }) + .catch((err: Error) => { + failed(err); + }); + }); + }, + cancel: (): void => { + api?.close(); + resolve(undefined); + }, + }, + }; const api = this.add().tab({ name: origin.getTitle(), content: { factory: SetupObserve, - inputs: { - origin, - api: { - finish: (origin: SessionOrigin): Promise => { - return new Promise((success, failed) => { - this.initialize() - .observe(origin, session) - .then((session) => { - success(); - api?.close(); - resolve(session); - }) - .catch((err: Error) => { - failed(err); - }); + inputs, + }, + active: true, + }); + }); + }, + attach: (session: Session): Promise => { + const origin = session.stream.getOrigin(); + if (!origin) { + return Promise.reject(new Error(`No origin to attach new one`)); + } + const parser = origin.components?.parser?.uuid; + if (!parser) { + return Promise.reject(new Error(`No original parser to attach new one`)); + } + return new Promise((resolve) => { + const inputs: { + origin: SessionOrigin; + parser: string; + api: IApi; + } = { + origin, + parser, + api: { + finish: (origin: SessionOrigin): Promise => { + return new Promise((success, failed) => { + this.initialize() + .observe(origin) + .then((session) => { + success(session); + instance.close(); + resolve(session); + }) + .catch((err: Error) => { + failed(err); }); - }, - cancel: (): void => { - api?.close(); - resolve(undefined); - }, - tab: (): TabControls => { - return api as unknown as TabControls; - }, - }, + }); + }, + cancel: (): void => { + instance.close(); + resolve(undefined); }, }, - active: true, + }; + const instance = popup.open({ + component: { + factory: SetupObserve, + inputs, + }, + position: { + vertical: Vertical.center, + horizontal: Horizontal.center, + }, + closeOnKey: 'Escape', + uuid: session.uuid(), }); }); }, From 6f8bcf3c55e628f534ac164caf792c710c973fb4 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Tue, 12 Aug 2025 20:34:09 +0200 Subject: [PATCH 119/121] Provide popup with attaching (draft) --- .../service/session/dependencies/stream.ts | 9 ++++ .../elements/menu.attachsource/component.ts | 53 +++++++++++++------ .../client/src/app/ui/tabs/setup/component.ts | 19 ++++--- .../client/src/app/ui/tabs/setup/state.ts | 19 +++++-- .../src/app/ui/tabs/setup/template.html | 8 +-- 5 files changed, 77 insertions(+), 31 deletions(-) diff --git a/application/client/src/app/service/session/dependencies/stream.ts b/application/client/src/app/service/session/dependencies/stream.ts index bc6abd7a24..b4ef741cb7 100644 --- a/application/client/src/app/service/session/dependencies/stream.ts +++ b/application/client/src/app/service/session/dependencies/stream.ts @@ -49,6 +49,8 @@ export class Stream extends Subscriber { private _info!: Info; public readonly operations: Map = new Map(); + // Original (initial) session origin + public origin: SessionOrigin | undefined; public readonly rank: Rank = new Rank(); public sde!: Sde; @@ -123,6 +125,10 @@ export class Stream extends Subscriber { }); } + public getOrigin(): SessionOrigin | undefined { + return this.origin; + } + public len(): number { return this._len; } @@ -207,6 +213,9 @@ export class Stream extends Subscriber { // Only if it's the first operation, save as recent recent.add(operation); } + if (!this.origin) { + this.origin = origin; + } return response.session; }) .finally(lockers.progress(`Creating session...`)); diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index b8877e4b91..bd71072c93 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -6,10 +6,11 @@ import { ViewEncapsulation, ChangeDetectionStrategy, } from '@angular/core'; -import { Session } from '@service/session'; +import { session, Session } from '@service/session'; import { Ilc, IlcInterface } from '@env/decorators/component'; import { ChangesDetector } from '@ui/env/extentions/changes'; - +import { popup, Vertical, Horizontal } from '@ui/service/popup'; +import { SetupObserve } from '@tabs/setup/component'; import { SessionOrigin } from '@service/session/origin'; @Component({ @@ -27,9 +28,27 @@ export class AttachSourceMenu extends ChangesDetector { public actions: Array<{ icon: string; title: string; handler: () => void } | null> = [ { icon: 'note_add', - title: 'Attach Files', + title: 'Attach new', handler: () => { - throw new Error(`Not implemented!`); + session.initialize().attach(this.session); + // const instance = popup.open({ + // component: { + // factory: SetupObserve, + // inputs: { + // attachment: this.attachment, + // embedded: false, + // close: () => { + // instance.close(); + // }, + // }, + // }, + // position: { + // vertical: Vertical.center, + // horizontal: Horizontal.center, + // }, + // closeOnKey: 'Escape', + // uuid: this.attachment.uuid, + // }); }, }, null, @@ -37,27 +56,27 @@ export class AttachSourceMenu extends ChangesDetector { icon: 'input', title: 'Connect TCP', handler: () => { - this.ilc() - .services.system.session.initialize() - .configure(SessionOrigin.source(), this.session); + // this.ilc() + // .services.system.session.initialize() + // .configure(SessionOrigin.source(), this.session); }, }, { icon: 'input', title: 'Connect UDP', handler: () => { - this.ilc() - .services.system.session.initialize() - .configure(SessionOrigin.source(), this.session); + // this.ilc() + // .services.system.session.initialize() + // .configure(SessionOrigin.source(), this.session); }, }, { icon: 'settings_input_composite', title: 'Connect Serial', handler: () => { - this.ilc() - .services.system.session.initialize() - .configure(SessionOrigin.source(), this.session); + // this.ilc() + // .services.system.session.initialize() + // .configure(SessionOrigin.source(), this.session); }, }, null, @@ -65,9 +84,9 @@ export class AttachSourceMenu extends ChangesDetector { icon: 'minimize', title: 'Command', handler: () => { - this.ilc() - .services.system.session.initialize() - .configure(SessionOrigin.source(), this.session); + // this.ilc() + // .services.system.session.initialize() + // .configure(SessionOrigin.source(), this.session); }, }, ]; @@ -84,7 +103,7 @@ export class AttachSourceMenu extends ChangesDetector { disabled: (): boolean => { // return this.session.observed.getNewSourceError() instanceof Error; console.error(`Not implemented`); - return true; + return false; }, error: (): string | undefined => { // const error = this.session.observed.getNewSourceError(); diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index e257d3ff0f..238859a812 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -11,7 +11,7 @@ import { Ilc, IlcInterface } from '@env/decorators/component'; import { Initial } from '@env/decorators/initial'; import { ChangesDetector } from '@ui/env/extentions/changes'; import { State, IApi } from './state'; -import { SessionComponents, SessionOrigin } from '@service/session/origin'; +import { SessionOrigin } from '@service/session/origin'; import { session } from '@service/session'; @Component({ @@ -29,6 +29,8 @@ export class SetupObserve { @Input() api!: IApi; @Input() origin!: SessionOrigin; + /// Preselected parser + @Input() parser: string | undefined; public state!: State; @@ -41,7 +43,7 @@ export class SetupObserve } public ngAfterContentInit(): void { - this.state = new State(this.origin); + this.state = new State(this.origin, this.parser); this.state.load(); } @@ -68,15 +70,18 @@ export class SetupObserve this.log().debug(`Cannot start session, because: ${error.message}`); return; } - session - .initialize() - .observe(this.origin) + this.api + .finish(this.origin) .then((uuid: string) => { - console.log(`Session has been created: ${uuid}`); + this.log().debug(`Session has been created: ${uuid}`); }) .catch((err: Error) => { - console.error(err.message); + this.log().error(`Fail to create session: ${err.message}`); }); } + + public onCancel() { + this.api.cancel(); + } } export interface SetupObserve extends IlcInterface {} diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index 3a5d1e6b62..f855769cdd 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -7,9 +7,8 @@ import { Proivder } from './provider'; import { SessionOrigin } from '@service/session/origin'; export interface IApi { - finish(): Promise; + finish(origin: SessionOrigin): Promise; cancel(): void; - tab(): TabControls; } export interface ComponentDescription { @@ -54,12 +53,18 @@ export class State extends Subscriber { source: undefined, parser: undefined, }; + public preselected: { + parser: string | undefined; + } = { + parser: undefined, + }; public locked: boolean = false; protected readonly logger = new Logger(`Setup`); - constructor(protected readonly origin: SessionOrigin) { + constructor(protected readonly origin: SessionOrigin, parser: string | undefined) { super(); + this.preselected.parser = parser; } public destroy() { this.unsubscribe(); @@ -103,7 +108,13 @@ export class State extends Subscriber { .parsers() .then((parsers: Ident[]) => { this.parsers = parsers; - if (this.parsers.length > 0) { + if ( + this.preselected.parser && + this.parsers.find((parser) => parser.uuid === this.preselected.parser) + ) { + this.selected.parser = this.preselected.parser; + } else if (this.parsers.length > 0) { + this.preselected.parser = undefined; this.selected.parser = this.parsers[0].uuid; } this.subjects.get().parsers.emit(this.parsers); diff --git a/application/client/src/app/ui/tabs/setup/template.html b/application/client/src/app/ui/tabs/setup/template.html index 7de78ed4e8..e3e23d040e 100644 --- a/application/client/src/app/ui/tabs/setup/template.html +++ b/application/client/src/app/ui/tabs/setup/template.html @@ -19,12 +19,14 @@ {{parser.name}} @@ -40,7 +42,7 @@ > Start - +

From daf6fc5871e7ecc757872235cc10213ace18a36e Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 13 Aug 2025 13:44:23 +0200 Subject: [PATCH 120/121] Fix dialogs and allow attaching observe operations --- .../client/src/app/service/actions/jumpto.ts | 2 +- application/client/src/app/service/hotkeys.ts | 6 +-- application/client/src/app/service/session.ts | 9 +++- .../session/dependencies/comments/index.ts | 2 +- .../client/src/app/service/session/origin.ts | 2 +- .../entry/inner/complex/timezone/component.ts | 2 +- .../app/ui/layout/popups/popup/component.ts | 44 ++++++++++++++++--- .../app/ui/layout/popups/popup/styles.less | 2 - .../client/src/app/ui/service/popup/popup.ts | 7 ++- 9 files changed, 58 insertions(+), 18 deletions(-) diff --git a/application/client/src/app/service/actions/jumpto.ts b/application/client/src/app/service/actions/jumpto.ts index 7c3835be90..d76f45a768 100644 --- a/application/client/src/app/service/actions/jumpto.ts +++ b/application/client/src/app/service/actions/jumpto.ts @@ -31,7 +31,7 @@ export class Action extends Base { horizontal: Horizontal.center, }, closeOnKey: 'Escape', - width: 350, + size: { width: 350 }, uuid: 'Ctrl + G', blur: false, }); diff --git a/application/client/src/app/service/hotkeys.ts b/application/client/src/app/service/hotkeys.ts index b0de1258ee..1978612a59 100644 --- a/application/client/src/app/service/hotkeys.ts +++ b/application/client/src/app/service/hotkeys.ts @@ -124,7 +124,7 @@ export class Service extends Implementation { }, closeOnKey: '*', uuid: '?', - width: 500, + size: { width: 500 }, }); }), ); @@ -143,7 +143,7 @@ export class Service extends Implementation { horizontal: Horizontal.center, }, closeOnKey: 'Escape', - width: 350, + size: { width: 350 }, uuid: 'Ctrl + G', blur: false, }); @@ -161,7 +161,7 @@ export class Service extends Implementation { horizontal: Horizontal.center, }, closeOnKey: 'Escape', - width: 450, + size: { width: 450 }, uuid: 'Ctrl + P', }); }), diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 4b5c6edad6..9b2b0461f1 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -314,8 +314,9 @@ export class Service extends Implementation { api: { finish: (origin: SessionOrigin): Promise => { return new Promise((success, failed) => { - this.initialize() - .observe(origin) + session.stream + .observe() + .start(origin) .then((session) => { success(session); instance.close(); @@ -341,6 +342,10 @@ export class Service extends Implementation { vertical: Vertical.center, horizontal: Horizontal.center, }, + size: { + height: 0.8, + width: 0.9, + }, closeOnKey: 'Escape', uuid: session.uuid(), }); diff --git a/application/client/src/app/service/session/dependencies/comments/index.ts b/application/client/src/app/service/session/dependencies/comments/index.ts index c0cd9101e7..2f9ae9868e 100644 --- a/application/client/src/app/service/session/dependencies/comments/index.ts +++ b/application/client/src/app/service/session/dependencies/comments/index.ts @@ -434,7 +434,7 @@ export class Comments extends Subscriber { horizontal: Horizontal.center, }, closeOnKey: 'Escape', - width: 450, + size: { width: 450 }, uuid: 'add_new_comment', }); } diff --git a/application/client/src/app/service/session/origin.ts b/application/client/src/app/service/session/origin.ts index 054b9acd51..18cf955c13 100644 --- a/application/client/src/app/service/session/origin.ts +++ b/application/client/src/app/service/session/origin.ts @@ -41,7 +41,7 @@ export class SessionOrigin { static fromSessionSetup(setup: SessionSetup, descriptor: SessionDescriptor): SessionOrigin { return new SessionOrigin( setup.origin, - new SessionComponents().setSource(descriptor.source).setParser(descriptor.source), + new SessionComponents().setSource(descriptor.source).setParser(descriptor.parser), new ComponentsOptions().setParser(setup.parser).setSource(setup.source), ); } diff --git a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts index 1ac8ca7305..8bcc22355a 100644 --- a/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts +++ b/application/client/src/app/ui/elements/scheme/entry/inner/complex/timezone/component.ts @@ -55,7 +55,7 @@ export class TimezoneSelector extends ChangesDetector implements AfterContentIni }, }, closeOnKey: 'Escape', - width: 350, + size: { width: 350 }, uuid: 'app-elements-timezone-selector', }) .subjects.get() diff --git a/application/client/src/app/ui/layout/popups/popup/component.ts b/application/client/src/app/ui/layout/popups/popup/component.ts index 7924da041d..bc5de99e09 100644 --- a/application/client/src/app/ui/layout/popups/popup/component.ts +++ b/application/client/src/app/ui/layout/popups/popup/component.ts @@ -24,6 +24,9 @@ export class LayoutPopup extends ChangesDetector implements AfterViewInit, After @Input() public popup!: Popup; @Input() public close!: () => void; + public width: string = 'auto'; + public height: string = 'auto'; + @HostBinding('class') get cssClassEdited() { if (this.popup.options.position === undefined) { return `v-bottom h-center`; @@ -55,6 +58,26 @@ export class LayoutPopup extends ChangesDetector implements AfterViewInit, After : this.popup.options.component.inputs; this.popup.options.component.inputs.close = this.close; this.popup.options.component.inputs.popup = this.popup; + const options = this.popup.options; + if (!options) { + return; + } + if (options.size && options.size.width) { + const abs = options.size.width <= 1; + this.width = `${abs ? options.size.width * 100 : options.size.width}${ + abs ? '%' : 'px' + }`; + } else { + this.width = 'auto'; + } + if (options.size && options.size.height) { + const abs = options.size.height <= 1; + this.height = `${abs ? options.size.height * 100 : options.size.height}${ + abs ? '%' : 'px' + }`; + } else { + this.height = 'auto'; + } } public ngAfterViewInit(): void { @@ -83,12 +106,21 @@ export class LayoutPopup extends ChangesDetector implements AfterViewInit, After ); } - public ngStyle(): { width: string } { - if (this.popup.options === undefined || this.popup.options.width === undefined) { - return { width: 'auto' }; - } else { - return { width: `${this.popup.options.width}px` }; - } + public ngStyle(): { + width: string; + height: string; + maxWidth: string; + maxHeight: string; + position: string; + } { + return { + width: this.width, + height: this.height, + maxWidth: this.width.endsWith('%') ? 'none' : '70%', + maxHeight: this.height.endsWith('%') ? 'none' : '80%', + position: + this.width.endsWith('%') || this.height.endsWith('%') ? 'absolute' : 'relative', + }; } } export interface LayoutPopup extends IlcInterface {} diff --git a/application/client/src/app/ui/layout/popups/popup/styles.less b/application/client/src/app/ui/layout/popups/popup/styles.less index bc2c34659f..740ad7d26a 100644 --- a/application/client/src/app/ui/layout/popups/popup/styles.less +++ b/application/client/src/app/ui/layout/popups/popup/styles.less @@ -12,8 +12,6 @@ & .container { background: var(--scheme-color-5); box-shadow: 3px 3px 4px rgba(0,0,0,0.4); - max-width: 70%; - max-height: 80%; overflow-x: hidden; overflow-y: auto; } diff --git a/application/client/src/app/ui/service/popup/popup.ts b/application/client/src/app/ui/service/popup/popup.ts index 1a34b84c22..7539da4082 100644 --- a/application/client/src/app/ui/service/popup/popup.ts +++ b/application/client/src/app/ui/service/popup/popup.ts @@ -21,7 +21,12 @@ export interface Position { export interface PopupOptions { closable?: boolean; - width?: number; + size?: { + // If >=1 consider as %; if 1< consider as px. + width?: number; + // If >=1 consider as %; if 1< consider as px. + height?: number; + }; position?: Position; closeOnKey?: string; closeOnBGClick?: boolean; From df8957443b2607077514fe97fbc58954d982a6e0 Mon Sep 17 00:00:00 2001 From: DmitryAstafyev Date: Wed, 13 Aug 2025 17:38:31 +0200 Subject: [PATCH 121/121] Add IO compatible sources list --- .../client/src/app/service/components.ts | 67 ++++++++ application/client/src/app/service/session.ts | 13 +- .../elements/menu.attachsource/component.ts | 146 ++++++++---------- .../ui/elements/menu.attachsource/module.ts | 4 +- .../elements/menu.attachsource/template.html | 9 +- .../client/src/app/ui/elements/tree/module.ts | 2 - .../client/src/app/ui/tabs/setup/component.ts | 5 +- .../client/src/app/ui/tabs/setup/state.ts | 132 +++++++--------- 8 files changed, 209 insertions(+), 169 deletions(-) diff --git a/application/client/src/app/service/components.ts b/application/client/src/app/service/components.ts index 35bfb18985..0ab49440a1 100644 --- a/application/client/src/app/service/components.ts +++ b/application/client/src/app/service/components.ts @@ -17,6 +17,7 @@ import { FieldDesc, FieldList, Ident, + IODataType, OutputRender, SessionAction, } from '@platform/types/bindings'; @@ -312,6 +313,7 @@ export class Service extends Implementation { * @returns A promise that resolves to an array of `Ident` objects representing parser components. */ parsers(): Promise; + sourcesForParser(uuid: string): Promise; } { return { sources: (): Promise => { @@ -342,6 +344,32 @@ export class Service extends Implementation { .catch(reject); }); }, + sourcesForParser: (uuid: string): Promise => { + return Promise.all([ + components + .get(origin) + .sources() + .catch((err: Error) => { + return Promise.reject( + new Error(`Fail to get sources list: ${err.message}`), + ); + }), + components + .get(origin) + .parsers() + .catch((err: Error) => { + return Promise.reject( + new Error(`Fail to get parsers list: ${err.message}`), + ); + }), + ]).then(([sources, parsers]: [Ident[], Ident[]]) => { + const parser = parsers.find((p) => p.uuid === uuid); + if (!parser) { + return Promise.reject(new Error(`Fail to find parser ${uuid}`)); + } + return sources.filter((source) => isIOCompatible(source.io, parser.io)); + }); + }, }; } @@ -371,5 +399,44 @@ export class Service extends Implementation { }); } } + +export function isIOCompatible(a: IODataType, b: IODataType): boolean { + if (a === 'Any' || b === 'Any') { + return true; + } + if (typeof a === 'string' && typeof b === 'string') { + return a == b; + } + const aMtl: + | { + Multiple: Array; + } + | undefined = + typeof a === 'object' + ? (a as { + Multiple: Array; + }) + : undefined; + const bMtl: + | { + Multiple: Array; + } + | undefined = + typeof b === 'object' + ? (b as { + Multiple: Array; + }) + : undefined; + if (aMtl && bMtl) { + return aMtl.Multiple.find((a) => bMtl.Multiple.includes(a)) !== undefined; + } else if (aMtl) { + return aMtl.Multiple.includes(b); + } else if (bMtl) { + return bMtl.Multiple.includes(a); + } else { + return false; + } +} + export interface Service extends Interface {} export const components = register(new Service()); diff --git a/application/client/src/app/service/session.ts b/application/client/src/app/service/session.ts index 9b2b0461f1..82d29afc55 100644 --- a/application/client/src/app/service/session.ts +++ b/application/client/src/app/service/session.ts @@ -226,7 +226,7 @@ export class Service extends Implementation { suggest(filename: string, session?: Session): Promise; auto(origin: SessionOrigin, session?: Session): Promise; configure(origin: SessionOrigin): Promise; - attach(session: Session): Promise; + attach(session: Session, defSourceUuid: string | undefined): Promise; observe(origin: SessionOrigin, session?: Session): Promise; multiple(files: File[]): Promise; } { @@ -294,7 +294,10 @@ export class Service extends Implementation { }); }); }, - attach: (session: Session): Promise => { + attach: ( + session: Session, + defSourceUuid: string | undefined, + ): Promise => { const origin = session.stream.getOrigin(); if (!origin) { return Promise.reject(new Error(`No origin to attach new one`)); @@ -303,14 +306,20 @@ export class Service extends Implementation { if (!parser) { return Promise.reject(new Error(`No original parser to attach new one`)); } + const source = origin.components?.source?.uuid; + if (!source) { + return Promise.reject(new Error(`No original source to attach new one`)); + } return new Promise((resolve) => { const inputs: { origin: SessionOrigin; parser: string; + source: string; api: IApi; } = { origin, parser, + source: defSourceUuid ? defSourceUuid : source, api: { finish: (origin: SessionOrigin): Promise => { return new Promise((success, failed) => { diff --git a/application/client/src/app/ui/elements/menu.attachsource/component.ts b/application/client/src/app/ui/elements/menu.attachsource/component.ts index bd71072c93..42e9c8db27 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/component.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/component.ts @@ -5,6 +5,7 @@ import { ElementRef, ViewEncapsulation, ChangeDetectionStrategy, + AfterContentInit, } from '@angular/core'; import { session, Session } from '@service/session'; import { Ilc, IlcInterface } from '@env/decorators/component'; @@ -12,6 +13,14 @@ import { ChangesDetector } from '@ui/env/extentions/changes'; import { popup, Vertical, Horizontal } from '@ui/service/popup'; import { SetupObserve } from '@tabs/setup/component'; import { SessionOrigin } from '@service/session/origin'; +import { components } from '@service/components'; +import { Ident } from '@platform/types/bindings'; + +interface Action { + icon: string; + title: string; + handler: () => void; +} @Component({ selector: 'app-attach-new-source-menu', @@ -22,71 +31,23 @@ import { SessionOrigin } from '@service/session/origin'; standalone: false, }) @Ilc() -export class AttachSourceMenu extends ChangesDetector { +export class AttachSourceMenu extends ChangesDetector implements AfterContentInit { @Input() session!: Session; - public actions: Array<{ icon: string; title: string; handler: () => void } | null> = [ - { - icon: 'note_add', - title: 'Attach new', - handler: () => { - session.initialize().attach(this.session); - // const instance = popup.open({ - // component: { - // factory: SetupObserve, - // inputs: { - // attachment: this.attachment, - // embedded: false, - // close: () => { - // instance.close(); - // }, - // }, - // }, - // position: { - // vertical: Vertical.center, - // horizontal: Horizontal.center, - // }, - // closeOnKey: 'Escape', - // uuid: this.attachment.uuid, - // }); - }, - }, - null, - { - icon: 'input', - title: 'Connect TCP', - handler: () => { - // this.ilc() - // .services.system.session.initialize() - // .configure(SessionOrigin.source(), this.session); - }, - }, - { - icon: 'input', - title: 'Connect UDP', - handler: () => { - // this.ilc() - // .services.system.session.initialize() - // .configure(SessionOrigin.source(), this.session); - }, - }, - { - icon: 'settings_input_composite', - title: 'Connect Serial', - handler: () => { - // this.ilc() - // .services.system.session.initialize() - // .configure(SessionOrigin.source(), this.session); - }, - }, - null, + public loading: boolean = true; + public error: string | undefined; + + protected logAndSetErr(msg: string) { + this.error = msg; + this.log().error(msg); + } + + public actions: Array = [ { - icon: 'minimize', - title: 'Command', + icon: '', + title: 'Attach New', handler: () => { - // this.ilc() - // .services.system.session.initialize() - // .configure(SessionOrigin.source(), this.session); + session.initialize().attach(this.session, undefined); }, }, ]; @@ -95,23 +56,52 @@ export class AttachSourceMenu extends ChangesDetector { super(cdRef); } - public attach(): { - disabled(): boolean; - error(): string | undefined; - } { - return { - disabled: (): boolean => { - // return this.session.observed.getNewSourceError() instanceof Error; - console.error(`Not implemented`); - return false; - }, - error: (): string | undefined => { - // const error = this.session.observed.getNewSourceError(); - // return error instanceof Error ? error.message : undefined; - console.error(`Not implemented`); - return undefined; - }, - }; + public ngAfterContentInit(): void { + const origin = this.session.stream.getOrigin(); + if (!origin) { + this.logAndSetErr(`No origin linked to the session`); + return; + } + const parser = origin.components?.parser?.uuid; + if (!parser) { + this.logAndSetErr(`No parser linked to the origin of the session`); + return; + } + const source = origin.components?.source?.uuid; + if (!source) { + this.logAndSetErr(`No source linked to the origin of the session`); + return; + } + components + .get(origin.origin) + .sourcesForParser(parser) + .then((sources: Ident[]) => { + if (sources.length <= 1) { + return; + } + this.actions.push(null); + this.actions.push( + ...sources + .filter((src) => src.uuid !== source) + .map((src) => { + return { + icon: '', + title: src.name, + handler: () => { + session.initialize().attach(this.session, src.uuid); + }, + }; + }), + ); + this.detectChanges(); + }) + .catch((err: Error) => { + this.logAndSetErr(err.message); + }) + .finally(() => { + this.loading = false; + this.detectChanges(); + }); } } export interface AttachSourceMenu extends IlcInterface {} diff --git a/application/client/src/app/ui/elements/menu.attachsource/module.ts b/application/client/src/app/ui/elements/menu.attachsource/module.ts index 952da46e9b..6d7553fda6 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/module.ts +++ b/application/client/src/app/ui/elements/menu.attachsource/module.ts @@ -4,8 +4,8 @@ import { ContainersModule } from '@elements/containers/module'; import { MatButtonModule } from '@angular/material/button'; import { AttachSourceMenu } from './component'; import { MatMenuModule } from '@angular/material/menu'; -import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @NgModule({ imports: [ @@ -13,8 +13,8 @@ import { MatDividerModule } from '@angular/material/divider'; ContainersModule, MatButtonModule, MatMenuModule, - MatIconModule, MatDividerModule, + MatProgressSpinnerModule, ], declarations: [AttachSourceMenu], exports: [AttachSourceMenu], diff --git a/application/client/src/app/ui/elements/menu.attachsource/template.html b/application/client/src/app/ui/elements/menu.attachsource/template.html index 0dd71808d3..b63cfec2d5 100644 --- a/application/client/src/app/ui/elements/menu.attachsource/template.html +++ b/application/client/src/app/ui/elements/menu.attachsource/template.html @@ -1,14 +1,13 @@ -

{{attach().error()}}

- +

{{error}}

+ + - +
- diff --git a/application/client/src/app/ui/elements/tree/module.ts b/application/client/src/app/ui/elements/tree/module.ts index 68e9df68c0..d85253f6bb 100644 --- a/application/client/src/app/ui/elements/tree/module.ts +++ b/application/client/src/app/ui/elements/tree/module.ts @@ -5,7 +5,6 @@ import { CdkTreeModule } from '@angular/cdk/tree'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; -import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { FilterInputModule } from '@elements/filter/module'; import { ElementsTreeSelector } from './component'; @@ -21,7 +20,6 @@ import { InputListenerDirective } from '@ui/env/directives/input'; MatButtonModule, MatIconModule, FilterInputModule, - MatProgressSpinnerModule, ], declarations: [ElementsTreeSelector, InputListenerDirective], exports: [ElementsTreeSelector], diff --git a/application/client/src/app/ui/tabs/setup/component.ts b/application/client/src/app/ui/tabs/setup/component.ts index 238859a812..07e2fb8c06 100644 --- a/application/client/src/app/ui/tabs/setup/component.ts +++ b/application/client/src/app/ui/tabs/setup/component.ts @@ -31,6 +31,8 @@ export class SetupObserve @Input() origin!: SessionOrigin; /// Preselected parser @Input() parser: string | undefined; + /// Preselected source + @Input() source: string | undefined; public state!: State; @@ -43,8 +45,7 @@ export class SetupObserve } public ngAfterContentInit(): void { - this.state = new State(this.origin, this.parser); - this.state.load(); + this.state = new State(this.origin, this.parser, this.source); } public ngAfterViewInit(): void { diff --git a/application/client/src/app/ui/tabs/setup/state.ts b/application/client/src/app/ui/tabs/setup/state.ts index f855769cdd..c046f9e294 100644 --- a/application/client/src/app/ui/tabs/setup/state.ts +++ b/application/client/src/app/ui/tabs/setup/state.ts @@ -1,8 +1,7 @@ import { Ident, IODataType } from '@platform/types/bindings'; -import { components } from '@service/components'; +import { components, isIOCompatible } from '@service/components'; import { Logger } from '@env/logs'; import { Subscriber, Subject, Subjects } from '@platform/env/subscription'; -import { TabControls } from '@service/session'; import { Proivder } from './provider'; import { SessionOrigin } from '@service/session/origin'; @@ -55,16 +54,24 @@ export class State extends Subscriber { }; public preselected: { parser: string | undefined; + source: string | undefined; } = { parser: undefined, + source: undefined, }; public locked: boolean = false; protected readonly logger = new Logger(`Setup`); - constructor(protected readonly origin: SessionOrigin, parser: string | undefined) { + constructor( + protected readonly origin: SessionOrigin, + parser: string | undefined, + source: string | undefined, + ) { super(); this.preselected.parser = parser; + this.preselected.source = source; + this.load(); } public destroy() { this.unsubscribe(); @@ -87,43 +94,50 @@ export class State extends Subscriber { }); return undefined; } - public load() { - components - .get(this.origin.getDef()) - .sources() - .then((sources: Ident[]) => { - this.sources = sources; - if (this.sources.length > 0) { - this.selected.source = this.sources[0].uuid; - } - this.subjects.get().sources.emit(this.sources); - this.change().source(); - }) - .catch((err: Error) => { - this.logger.error(`Fail to get sources list: ${err.message}`); - this.subjects.get().error.emit(err.message); - }); - components - .get(this.origin.getDef()) - .parsers() - .then((parsers: Ident[]) => { - this.parsers = parsers; - if ( - this.preselected.parser && - this.parsers.find((parser) => parser.uuid === this.preselected.parser) - ) { - this.selected.parser = this.preselected.parser; - } else if (this.parsers.length > 0) { - this.preselected.parser = undefined; - this.selected.parser = this.parsers[0].uuid; - } - this.subjects.get().parsers.emit(this.parsers); - this.change().parser(); - }) - .catch((err: Error) => { - this.logger.error(`Fail to get parsers list: ${err.message}`); - this.subjects.get().error.emit(err.message); - }); + private async load() { + const [sources, parsers]: [Ident[] | void, Ident[] | void] = await Promise.all([ + components + .get(this.origin.getDef()) + .sources() + .catch((err: Error) => { + this.logger.error(`Fail to get sources list: ${err.message}`); + this.subjects.get().error.emit(err.message); + }), + components + .get(this.origin.getDef()) + .parsers() + .catch((err: Error) => { + this.logger.error(`Fail to get parsers list: ${err.message}`); + this.subjects.get().error.emit(err.message); + }), + ]); + if (!sources || !parsers) { + return; + } + this.sources = sources; + if ( + this.preselected.source && + this.sources.find((source) => source.uuid === this.preselected.source) + ) { + this.selected.source = this.preselected.source; + } else if (this.sources.length > 0) { + this.preselected.source = undefined; + this.selected.source = this.sources[0].uuid; + } + this.parsers = parsers; + if ( + this.preselected.parser && + this.parsers.find((parser) => parser.uuid === this.preselected.parser) + ) { + this.selected.parser = this.preselected.parser; + } else if (this.parsers.length > 0) { + this.preselected.parser = undefined; + this.selected.parser = this.parsers[0].uuid; + } + this.subjects.get().sources.emit(this.sources); + this.subjects.get().parsers.emit(this.parsers); + this.change().source(); + this.change().parser(); } public change(): { @@ -235,41 +249,3 @@ export class State extends Subscriber { this.change().parser(); } } - -function isIOCompatible(a: IODataType, b: IODataType): boolean { - if (a === 'Any' || b === 'Any') { - return true; - } - if (typeof a === 'string' && typeof b === 'string') { - return a == b; - } - const aMtl: - | { - Multiple: Array; - } - | undefined = - typeof a === 'object' - ? (a as { - Multiple: Array; - }) - : undefined; - const bMtl: - | { - Multiple: Array; - } - | undefined = - typeof b === 'object' - ? (b as { - Multiple: Array; - }) - : undefined; - if (aMtl && bMtl) { - return aMtl.Multiple.find((a) => bMtl.Multiple.includes(a)) !== undefined; - } else if (aMtl) { - return aMtl.Multiple.includes(b); - } else if (bMtl) { - return bMtl.Multiple.includes(a); - } else { - return false; - } -}