From ecdfff7a558856397e6aaa5335a9dabce5cd8e0f Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 12 Jun 2026 19:39:59 +0300 Subject: [PATCH 01/35] Generate powershell script for grpc models (I dont know if it works) --- scripts/windows/generate_grpc_models.ps1 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 scripts/windows/generate_grpc_models.ps1 diff --git a/scripts/windows/generate_grpc_models.ps1 b/scripts/windows/generate_grpc_models.ps1 new file mode 100644 index 000000000..04f0c6d6e --- /dev/null +++ b/scripts/windows/generate_grpc_models.ps1 @@ -0,0 +1,18 @@ +$ErrorActionPreference = "Stop" + +Push-Location "$PSScriptRoot/../../Ficus/src/python/infra" +python generate_models.py ficus.grpc_pipelines ../ficus/grpc_pipelines/ ../../../protos +Pop-Location + +$env:GOPATH = "$HOME/go" +$env:PATH = "$env:PATH;$env:GOPATH/bin" + +Push-Location "$PSScriptRoot/../../Ficus/src/go/grpcmodels" +protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative --proto_path=../../../protos/ ../../../protos/*.proto +Pop-Location + +Push-Location "$PSScriptRoot/../../Ficus/src/front/FicusFrontend/FicusDashboard/Npm" +Get-ChildItem "../../../../../protos/*.proto" | ForEach-Object { + & .\node_modules\.bin\proto-loader-gen-types.ps1 --longs=Number --enums=number --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=./src/protos/ "../../../../../protos/$($_.Name)" --inputTemplate "%s_DONTUSE" --outputTemplate "%s" +} +Pop-Location \ No newline at end of file From 207531d5922a34e57b5b12e4e0d9b869223ff330 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 4 Jul 2026 19:52:29 +0300 Subject: [PATCH 02/35] Fix models generation on windows, add windows script to solution folder --- All.sln | 1 + Ficus/src/python/infra/generate_models.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/All.sln b/All.sln index 0d8a575e7..af0c5dbd3 100644 --- a/All.sln +++ b/All.sln @@ -125,6 +125,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{92C02EAF-93FB-4A2C-A235-8541D893163C}" ProjectSection(SolutionItems) = preProject scripts\macos\generate_grpc_models.sh = scripts\macos\generate_grpc_models.sh + scripts\windows\generate_grpc_models.ps1 = scripts\windows\generate_grpc_models.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{2918C7E3-3FC8-48A5-ADDF-8358E888B40C}" diff --git a/Ficus/src/python/infra/generate_models.py b/Ficus/src/python/infra/generate_models.py index 763909631..44331cf49 100644 --- a/Ficus/src/python/infra/generate_models.py +++ b/Ficus/src/python/infra/generate_models.py @@ -15,7 +15,11 @@ def generate_models(): os.mkdir(models_dir) - python = '/usr/local/bin/python3' + if os.name == 'nt': + python = 'python' + else: + python = '/usr/local/bin/python3' + os.popen(f'{python} -m pip install grpcio-tools').read() packages = [] From 7bd055823d5a0fc7f445cda0f2f590c84c47ae18 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 4 Jul 2026 20:09:06 +0300 Subject: [PATCH 03/35] Try to promote rust version --- Ficus/docker/run/Run.RustFicusBackend.Dockerfile | 2 +- Ficus/docker/tests/Tests.Rust.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Ficus/docker/run/Run.RustFicusBackend.Dockerfile b/Ficus/docker/run/Run.RustFicusBackend.Dockerfile index efd4daa6c..bf4d31c46 100644 --- a/Ficus/docker/run/Run.RustFicusBackend.Dockerfile +++ b/Ficus/docker/run/Run.RustFicusBackend.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.92.0 as build +FROM rust:1.93.0 as build RUN apt update -y && apt upgrade -y RUN apt-get update -y diff --git a/Ficus/docker/tests/Tests.Rust.Dockerfile b/Ficus/docker/tests/Tests.Rust.Dockerfile index 602cd5f18..70287f3fe 100644 --- a/Ficus/docker/tests/Tests.Rust.Dockerfile +++ b/Ficus/docker/tests/Tests.Rust.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.92.0 as build +FROM rust:1.93.0 as build RUN apt update -y && apt upgrade -y RUN apt-get update -y From aac06a618d17ebd6011cef428742d9086a68e0f0 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 5 Jul 2026 21:23:04 +0300 Subject: [PATCH 04/35] Don't pass `pipeline_parts` everywhere --- .../ficus/src/pipelines/activities_parts.rs | 8 ++--- Ficus/src/rust/ficus/src/pipelines/context.rs | 29 +++++-------------- .../ficus/src/pipelines/pipeline_parts.rs | 10 ++----- .../tests/pipelines/pipeline_parts_tests.rs | 11 +++---- .../src/grpc/kafka/streaming/t2/processors.rs | 5 ++-- 5 files changed, 23 insertions(+), 40 deletions(-) diff --git a/Ficus/src/rust/ficus/src/pipelines/activities_parts.rs b/Ficus/src/rust/ficus/src/pipelines/activities_parts.rs index 7dfdb0728..8694e5ae0 100644 --- a/Ficus/src/rust/ficus/src/pipelines/activities_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/activities_parts.rs @@ -34,7 +34,7 @@ use crate::{ NARROW_ACTIVITIES_KEY, PATH_KEY, PATTERNS_DISCOVERY_STRATEGY_KEY, PATTERNS_KEY, PATTERNS_KIND_KEY, PIPELINE_KEY, REGEX_KEY, REGEXES_KEY, REPEAT_SETS_KEY, TRACE_ACTIVITIES_KEY, UNDEF_ACTIVITY_HANDLING_STRATEGY_KEY, UNDERLYING_EVENTS_COUNT_KEY, }, - pipeline_parts::PipelineParts, + pipeline_parts::{PIPELINE_PARTS, PipelineParts}, }, utils::{ log_serialization_format::LogSerializationFormat, @@ -225,7 +225,7 @@ impl PipelineParts { let adjusting_mode = *Self::get_user_data(config, &ADJUSTING_MODE_KEY)?; let log = Self::get_user_data(old_context, &EVENT_LOG_KEY)?; - let mut new_context = PipelineContext::empty_from(old_context); + let mut new_context = PipelineContext::empty(); if adjusting_mode == AdjustingMode::FromUnattachedSubTraces { if let Ok(activities) = Self::get_user_data(old_context, &TRACE_ACTIVITIES_KEY) { @@ -243,9 +243,7 @@ impl PipelineParts { old_activities.push(new_activity.clone()); } - old_context - .pipeline_parts() - .unwrap() + PIPELINE_PARTS .create_add_unattached_events_part(config.clone()) .execute(old_context, infra)?; diff --git a/Ficus/src/rust/ficus/src/pipelines/context.rs b/Ficus/src/rust/ficus/src/pipelines/context.rs index 4decc5ca3..620427b78 100644 --- a/Ficus/src/rust/ficus/src/pipelines/context.rs +++ b/Ficus/src/rust/ficus/src/pipelines/context.rs @@ -1,3 +1,4 @@ +use super::errors::pipeline_errors::PipelinePartExecutionError; use crate::{ pipelines::pipeline_parts::PipelineParts, utils::{ @@ -8,10 +9,9 @@ use crate::{ }, }, }; +use getset::Getters; use std::{any::Any, sync::Arc}; -use super::errors::pipeline_errors::PipelinePartExecutionError; - pub trait LogMessageHandler: Send + Sync { fn handle(&self, message: &str) -> Result<(), PipelinePartExecutionError>; } @@ -41,29 +41,20 @@ impl PipelineInfrastructure { } } -#[derive(Clone, Default)] -pub struct PipelineContext<'a> { +#[derive(Clone, Getters)] +pub struct PipelineContext { user_data: UserDataImpl, - pipeline_parts: Option<&'a PipelineParts>, } -impl<'a> PipelineContext<'a> { - pub fn new_with_logging(parts: &'a PipelineParts) -> Self { - Self { - user_data: Default::default(), - pipeline_parts: Some(parts), - } - } - - pub fn empty_from(other: &'a PipelineContext) -> Self { +impl PipelineContext { + pub fn empty() -> Self { Self { user_data: Default::default(), - pipeline_parts: other.pipeline_parts, } } } -impl<'a> UserData for PipelineContext<'a> { +impl UserData for PipelineContext { fn len(&self) -> usize { self.user_data.len() } @@ -101,11 +92,7 @@ impl<'a> UserData for PipelineContext<'a> { } } -impl<'a> PipelineContext<'a> { - pub fn pipeline_parts(&self) -> Option<&PipelineParts> { - self.pipeline_parts - } - +impl PipelineContext { pub fn devastate_user_data(self) -> UserDataImpl { self.user_data } diff --git a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs index 40f199897..e7e1b3e0c 100644 --- a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs @@ -15,7 +15,7 @@ use crate::{ }; use derive_new::new; use fancy_regex::Regex; -use std::collections::HashMap; +use std::{cell::LazyCell, collections::HashMap}; pub struct PipelineParts { names_to_parts: HashMap, @@ -54,14 +54,10 @@ macro_rules! pipeline_part { }; } -impl Default for PipelineParts { - fn default() -> Self { - Self::new() - } -} +pub const PIPELINE_PARTS: LazyCell = LazyCell::new(|| PipelineParts::new()); impl PipelineParts { - pub fn new() -> Self { + fn new() -> Self { let parts = vec![ Self::read_log_from_xes(), Self::write_log_to_xes(), diff --git a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs index a1bf2bcc6..37a87093f 100644 --- a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs +++ b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs @@ -1,4 +1,7 @@ -use ficus::{pipelines::pipeline_parts::PipelineParts, vecs}; +use ficus::{ + pipelines::pipeline_parts::{PIPELINE_PARTS, PipelineParts}, + vecs, +}; fn get_test_parts_names() -> Vec { vecs![ @@ -97,18 +100,16 @@ fn get_test_parts_names() -> Vec { #[test] fn test_pipeline_parts() { - let parts = PipelineParts::new(); let names = get_test_parts_names(); for name in names { - assert!(parts.find_part(name.as_str()).is_some()); + assert!(PIPELINE_PARTS.find_part(name.as_str()).is_some()); } } #[test] fn test_pipeline_parts_count() { - let parts = PipelineParts::new(); let names = get_test_parts_names(); - assert_eq!(parts.len(), names.len()); + assert_eq!(PIPELINE_PARTS.len(), names.len()); } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs index 78f405f60..0db84007c 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs @@ -13,6 +13,7 @@ use ficus::{ pipelines::{ context::{PipelineContext, PipelineInfrastructure}, keys::context_keys::EVENT_LOG_KEY, + pipeline_parts::PipelineParts, pipelines::PipelinePart, }, utils::user_data::user_data::UserData, @@ -55,7 +56,8 @@ impl T2StreamingProcessor { }; let xes_trace = if let Some(preprocessing_pipeline) = self.trace_preprocessing_pipeline.as_ref() { - let mut preprocessing_context = PipelineContext::default(); + let mut preprocessing_context = PipelineContext::empty(); + let mut log = XesEventLogImpl::default(); log.push(Rc::new(RefCell::new(xes_trace))); @@ -65,7 +67,6 @@ impl T2StreamingProcessor { let preprocessing_pipeline = ServicePipelineExecutionContext::new( preprocessing_pipeline, &initial_context_values, - context.execution_dto.pipeline_parts.clone(), context.execution_dto.events_handler.clone(), ); From 72b46f4cb88f942bd27bca2cb5d18b28447e4252 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 5 Jul 2026 21:34:02 +0300 Subject: [PATCH 05/35] After-rebase fixes --- .../tests/pipelines/pipeline_parts_tests.rs | 5 +---- .../ficus_backend/src/grpc/backend_service.rs | 14 ++++-------- .../src/grpc/kafka/grpc_kafka_service.rs | 7 ++---- .../src/grpc/kafka/kafka_service.rs | 22 +++++-------------- .../ficus_backend/src/grpc/kafka/models.rs | 8 ++----- .../src/grpc/pipeline_executor.rs | 17 ++++---------- 6 files changed, 19 insertions(+), 54 deletions(-) diff --git a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs index 37a87093f..545fe9008 100644 --- a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs +++ b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs @@ -1,7 +1,4 @@ -use ficus::{ - pipelines::pipeline_parts::{PIPELINE_PARTS, PipelineParts}, - vecs, -}; +use ficus::{pipelines::pipeline_parts::PIPELINE_PARTS, vecs}; fn get_test_parts_names() -> Vec { vecs![ diff --git a/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs index befa2ab13..3625b9b03 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs @@ -9,7 +9,7 @@ use crate::{ }, grpc::{context_values_service::ContextValueService, pipeline_executor::ServicePipelineExecutionContext}, }; -use ficus::pipelines::pipeline_parts::PipelineParts; +use ficus::pipelines::pipeline_parts::{PIPELINE_PARTS, PipelineParts}; use futures::Stream; use std::{pin::Pin, sync::Arc}; use tokio::sync::mpsc::{self, Sender}; @@ -21,15 +21,11 @@ pub(super) type GrpcSender = Sender, - pipeline_parts: Arc, } impl FicusService { pub fn new(cv_service: Arc) -> Self { - Self { - cv_service, - pipeline_parts: Arc::new(PipelineParts::new()), - } + Self { cv_service } } } @@ -41,7 +37,6 @@ impl GrpcBackendService for FicusService { &self, request: Request, ) -> Result, Status> { - let pipeline_parts = self.pipeline_parts.clone(); let (sender, receiver) = mpsc::channel(4); let context_values = match self.cv_service.reclaim_context_values(&request.get_ref().context_values_ids) { @@ -59,7 +54,7 @@ impl GrpcBackendService for FicusService { let sender = Arc::new(GrpcPipelineEventsHandler::new(sender)); let sender = sender as Arc; - let context = ServicePipelineExecutionContext::new(grpc_pipeline, &context_values, pipeline_parts, sender); + let context = ServicePipelineExecutionContext::new(grpc_pipeline, &context_values, sender); match context.execute_grpc_pipeline_and_fill_context_values(|_| Ok(()), cv_service) { Ok(uuid) => { @@ -85,8 +80,7 @@ impl GrpcBackendService for FicusService { async fn get_backend_info(&self, _: Request<()>) -> Result, Status> { Ok(Response::new(GrpcFicusBackendInfo { name: "RUST_FICUS_BACKEND".to_string(), - pipeline_parts: self - .pipeline_parts + pipeline_parts: PIPELINE_PARTS .pipeline_parts_descriptors() .into_iter() .map(|d| GrpcPipelinePartDescriptor { name: d.name() }) diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs index 138550ada..b15262c8f 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs @@ -34,16 +34,13 @@ use uuid::Uuid; pub struct GrpcKafkaServiceImpl { cv_service: Arc, kafka_service: Arc, - pipeline_parts: Arc, } impl GrpcKafkaServiceImpl { pub fn new(cv_service: Arc) -> Self { - let pipeline_parts = Arc::new(PipelineParts::default()); Self { cv_service: cv_service.clone(), - kafka_service: Arc::new(KafkaService::new(pipeline_parts.clone(), cv_service)), - pipeline_parts, + kafka_service: Arc::new(KafkaService::new(cv_service)), } } } @@ -213,7 +210,7 @@ impl GrpcKafkaService for GrpcKafkaServiceImpl { let handler = DelegatingEventsHandler::new(vec![kafka_handler, grpc_handler]); let handler = Arc::new(handler) as Arc; - let dto = PipelineExecutionDto::new(self.pipeline_parts.clone(), handler); + let dto = PipelineExecutionDto::new(handler); let context_values = match self .cv_service diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs index f1beb9561..d96a46940 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs @@ -90,16 +90,14 @@ impl KafkaSubscription { } pub struct KafkaService { - pipeline_parts: Arc, subscriptions_to_execution_requests: Arc>>, cv_service: Arc, logger: ConsoleLogMessageHandler, } impl KafkaService { - pub fn new(pipeline_parts: Arc, cv_service: Arc) -> Self { + pub fn new(cv_service: Arc) -> Self { Self { - pipeline_parts, subscriptions_to_execution_requests: Arc::new(Mutex::new(HashMap::new())), logger: ConsoleLogMessageHandler::new(), cv_service, @@ -228,10 +226,7 @@ impl KafkaService { continue; }; - let execution_dto = PipelineExecutionDto::new( - Arc::new(PipelineParts::new()), - Arc::new(EmptyPipelineEventsHandler::new()) as Arc, - ); + let execution_dto = PipelineExecutionDto::new(Arc::new(EmptyPipelineEventsHandler::new()) as Arc); let trace_processing_context = KafkaTraceProcessingContext { execution_dto, trace }; @@ -284,7 +279,7 @@ impl KafkaService { }; let handler = handler as Arc; - let execution_dto = PipelineExecutionDto::new(Arc::new(PipelineParts::new()), handler); + let execution_dto = PipelineExecutionDto::new(handler); let context = Self::create_pipeline_execution_context(&pipeline.request, &execution_dto); let result = context.execute_grpc_pipeline_and_fill_context_values( @@ -341,7 +336,7 @@ impl KafkaService { streaming_config: StreamingConfiguration, ) -> KafkaSubscriptionPipeline { let handler = Arc::new(handler) as Arc; - let dto = PipelineExecutionDto::new(self.pipeline_parts.clone(), handler); + let dto = PipelineExecutionDto::new(handler); KafkaSubscriptionPipeline::new(request, dto, pipeline_name, streaming_config.create_processor()) } @@ -375,7 +370,7 @@ impl KafkaService { context_values: &'a Vec, dto: &PipelineExecutionDto, ) -> ServicePipelineExecutionContext<'a> { - ServicePipelineExecutionContext::new(pipeline, context_values, dto.pipeline_parts.clone(), dto.events_handler.clone()) + ServicePipelineExecutionContext::new(pipeline, context_values, dto.events_handler.clone()) } fn create_pipeline_execution_context<'a>( @@ -384,12 +379,7 @@ impl KafkaService { ) -> ServicePipelineExecutionContext<'a> { let grpc_pipeline = pipeline_req.pipeline.as_ref().expect("Pipeline should be supplied"); - ServicePipelineExecutionContext::new( - grpc_pipeline, - &pipeline_req.initial_context, - dto.pipeline_parts.clone(), - dto.events_handler.clone(), - ) + ServicePipelineExecutionContext::new(grpc_pipeline, &pipeline_req.initial_context, dto.events_handler.clone()) } pub(super) fn create_kafka_events_handler(producer_metadata: Option<&GrpcKafkaConnectionMetadata>) -> Result { diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs index 865876d9a..50ac6f6d0 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs @@ -69,16 +69,12 @@ impl Display for XesFromBxesKafkaTraceCreatingError { #[derive(Clone)] pub struct PipelineExecutionDto { - pub(super) pipeline_parts: Arc, pub(super) events_handler: Arc, } impl PipelineExecutionDto { - pub fn new(pipeline_parts: Arc, events_handler: Arc) -> Self { - Self { - pipeline_parts, - events_handler, - } + pub fn new(events_handler: Arc) -> Self { + Self { events_handler } } } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/pipeline_executor.rs b/Ficus/src/rust/ficus_backend/src/grpc/pipeline_executor.rs index 247eec2b7..ea2528025 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/pipeline_executor.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/pipeline_executor.rs @@ -13,7 +13,7 @@ use ficus::{ context::{LogMessageHandler, PipelineContext, PipelineInfrastructure}, errors::pipeline_errors::PipelinePartExecutionError, keys::context_keys::{EXECUTION_ID_KEY, find_context_key}, - pipeline_parts::PipelineParts, + pipeline_parts::PIPELINE_PARTS, pipelines::{DefaultPipelinePart, Pipeline, PipelinePart}, }, utils::{ @@ -27,7 +27,6 @@ use uuid::Uuid; pub(super) struct ServicePipelineExecutionContext<'a> { grpc_pipeline: &'a GrpcPipeline, context_values: &'a Vec, - pipeline_parts: Arc, handler: Arc, log_message_handler: Arc, } @@ -36,7 +35,6 @@ impl<'a> ServicePipelineExecutionContext<'a> { pub fn new( grpc_pipeline: &'a GrpcPipeline, context_values: &'a Vec, - pipeline_parts: Arc, handler: Arc, ) -> Self { let log_message_handler = Self::create_log_message_handler(handler.clone()); @@ -44,7 +42,6 @@ impl<'a> ServicePipelineExecutionContext<'a> { Self { grpc_pipeline, context_values, - pipeline_parts, handler, log_message_handler, } @@ -70,10 +67,6 @@ impl<'a> ServicePipelineExecutionContext<'a> { self.grpc_pipeline } - pub fn parts(&self) -> &PipelineParts { - &self.pipeline_parts - } - pub fn context_values(&self) -> &Vec { self.context_values } @@ -86,7 +79,6 @@ impl<'a> ServicePipelineExecutionContext<'a> { Self { grpc_pipeline: new_grpc_pipeline, context_values: self.context_values, - pipeline_parts: self.pipeline_parts.clone(), handler: self.handler.clone(), log_message_handler: self.log_message_handler.clone(), } @@ -211,14 +203,13 @@ impl<'a> ServicePipelineExecutionContext<'a> { } } - self - .parts() + PIPELINE_PARTS .find_part(&grpc_default_part.name) .map(|default_part| Box::new(default_part(Box::new(part_config)))) } - pub(super) fn create_initial_context(&'a self) -> PipelineContext<'a> { - let mut pipeline_context = PipelineContext::new_with_logging(self.parts()); + pub(super) fn create_initial_context(&'a self) -> PipelineContext { + let mut pipeline_context = PipelineContext::empty(); for value in self.context_values() { let key = find_context_key(&value.key.as_ref().unwrap().name).unwrap(); From 00f57dbbed82aba20b92a68d5dad0a0c5576e8c9 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 6 Jul 2026 23:06:50 +0300 Subject: [PATCH 06/35] Remove explicit `Debug` impls --- .../rust/ficus/src/event_log/xes/xes_event.rs | 13 ++----------- .../features/discovery/timeline/discovery.rs | 7 +------ .../src/pipelines/errors/pipeline_errors.rs | 18 ++---------------- Ficus/src/rust/ficus/src/utils/xml_utils.rs | 11 +---------- 4 files changed, 6 insertions(+), 43 deletions(-) diff --git a/Ficus/src/rust/ficus/src/event_log/xes/xes_event.rs b/Ficus/src/rust/ficus/src/event_log/xes/xes_event.rs index 6f9aa8d7e..c35900f10 100644 --- a/Ficus/src/rust/ficus/src/event_log/xes/xes_event.rs +++ b/Ficus/src/rust/ficus/src/event_log/xes/xes_event.rs @@ -9,23 +9,14 @@ use crate::{ }, }; use chrono::{DateTime, Utc}; -use std::{ - collections::HashMap, - fmt::{Debug, Formatter}, - sync::Arc, -}; +use std::{collections::HashMap, fmt::Debug, sync::Arc}; +#[derive(Debug)] pub struct XesEventImpl { event_base: EventBase, payload: Option, EventPayloadValue>>, } -impl Debug for XesEventImpl { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(self.name_pointer()) - } -} - impl XesEventImpl { pub fn new_all_fields(name: Arc, timestamp: DateTime, payload: Option, EventPayloadValue>>) -> Self { Self { diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs index 6284d0a5b..1ff42485a 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs @@ -74,16 +74,11 @@ pub struct TraceThreadEvent { stamp: i64, } +#[derive(Debug)] pub enum LogThreadsDiagramError { NotSupportedEventStamp, } -impl Debug for LogThreadsDiagramError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - Display::fmt(self, f) - } -} - impl Display for LogThreadsDiagramError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { diff --git a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs index 2f32a9190..edef591f8 100644 --- a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs +++ b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs @@ -28,6 +28,7 @@ impl PipelinePartExecutionError { } } +#[derive(Debug)] pub struct MissingContextError { context_key_name: String, } @@ -44,14 +45,7 @@ impl Display for MissingContextError { } } -impl Debug for MissingContextError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("MissingContextError") - .field("context_key_name", &self.context_key_name) - .finish() - } -} - +#[derive(Debug)] pub struct RawPartExecutionError { message: String, } @@ -62,14 +56,6 @@ impl Display for RawPartExecutionError { } } -impl Debug for RawPartExecutionError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PipelinePartExecutionError") - .field("message", &self.message) - .finish() - } -} - impl Error for RawPartExecutionError {} impl RawPartExecutionError { diff --git a/Ficus/src/rust/ficus/src/utils/xml_utils.rs b/Ficus/src/rust/ficus/src/utils/xml_utils.rs index eaf461f9b..b735ac18c 100644 --- a/Ficus/src/rust/ficus/src/utils/xml_utils.rs +++ b/Ficus/src/rust/ficus/src/utils/xml_utils.rs @@ -11,6 +11,7 @@ use std::{ sync::Arc, }; +#[derive(Debug)] pub enum XmlWriteError { FromUt8Error(FromUtf8Error), IOError(io::Error), @@ -27,16 +28,6 @@ impl Display for XmlWriteError { } } -impl Debug for XmlWriteError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::FromUt8Error(arg0) => f.debug_tuple("FromUt8Error").field(arg0).finish(), - Self::IOError(arg0) => f.debug_tuple("IOError").field(arg0).finish(), - Self::WriterError(arg0) => f.debug_tuple("WriterError").field(arg0).finish(), - } - } -} - impl Error for XmlWriteError {} pub fn write_empty(writer: &mut Writer, tag_name: &str, attrs: &Vec<(&str, &str)>) -> Result<(), XmlWriteError> From 1d8b1c2833de197b8f4135c9939c0e5d47736b94 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 6 Jul 2026 23:11:07 +0300 Subject: [PATCH 07/35] Remove explicit `Error` impls --- .../src/rust/ficus/src/features/discovery/timeline/discovery.rs | 2 -- .../discovery/timeline/software_data/extractors/core.rs | 2 -- Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs | 2 -- Ficus/src/rust/ficus/src/utils/xml_utils.rs | 2 -- 4 files changed, 8 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs index 1ff42485a..c41d32423 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs @@ -87,8 +87,6 @@ impl Display for LogThreadsDiagramError { } } -impl Error for LogThreadsDiagramError {} - impl From for PipelinePartExecutionError { fn from(val: LogThreadsDiagramError) -> Self { PipelinePartExecutionError::Raw(RawPartExecutionError::new(val.to_string())) diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs index 927db07ec..f5f8f9bc9 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs @@ -28,8 +28,6 @@ impl Display for SoftwareDataExtractionError { } } -impl Error for SoftwareDataExtractionError {} - pub trait EventGroupSoftwareDataExtractor { fn extract(&self, software_data: &mut SoftwareData, event_group: &EventGroup) -> Result<(), SoftwareDataExtractionError> { let events = event_group diff --git a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs index edef591f8..77ca0d106 100644 --- a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs +++ b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs @@ -56,8 +56,6 @@ impl Display for RawPartExecutionError { } } -impl Error for RawPartExecutionError {} - impl RawPartExecutionError { pub fn new(message: String) -> Self { Self { message } diff --git a/Ficus/src/rust/ficus/src/utils/xml_utils.rs b/Ficus/src/rust/ficus/src/utils/xml_utils.rs index b735ac18c..e2e0cf9cd 100644 --- a/Ficus/src/rust/ficus/src/utils/xml_utils.rs +++ b/Ficus/src/rust/ficus/src/utils/xml_utils.rs @@ -28,8 +28,6 @@ impl Display for XmlWriteError { } } -impl Error for XmlWriteError {} - pub fn write_empty(writer: &mut Writer, tag_name: &str, attrs: &Vec<(&str, &str)>) -> Result<(), XmlWriteError> where T: std::io::Write, From 56279d2a4a6a630f4681443c3b921c17e997e331 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 6 Jul 2026 23:14:55 +0300 Subject: [PATCH 08/35] Remove unused imports --- .../src/features/discovery/timeline/discovery.rs | 1 - .../timeline/software_data/extractors/core.rs | 1 - Ficus/src/rust/ficus/src/pipelines/context.rs | 13 +++++-------- .../ficus/src/pipelines/errors/pipeline_errors.rs | 5 +---- Ficus/src/rust/ficus/src/utils/xml_utils.rs | 1 - .../rust/ficus_backend/src/grpc/backend_service.rs | 2 +- .../src/grpc/kafka/grpc_kafka_service.rs | 9 +-------- .../src/rust/ficus_backend/src/grpc/kafka/models.rs | 1 - .../src/grpc/kafka/streaming/t2/processors.rs | 1 - 9 files changed, 8 insertions(+), 26 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs index c41d32423..1fe81f126 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/discovery.rs @@ -15,7 +15,6 @@ use getset::{Getters, MutGetters}; use std::{ cell::RefCell, collections::HashMap, - error::Error, fmt::{Debug, Display, Formatter}, ops::Deref, rc::Rc, diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs index f5f8f9bc9..ce7e99818 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/software_data/extractors/core.rs @@ -4,7 +4,6 @@ use crate::{ }; use std::{ cell::RefCell, - error::Error, fmt::{Debug, Display, Formatter}, rc::Rc, str::FromStr, diff --git a/Ficus/src/rust/ficus/src/pipelines/context.rs b/Ficus/src/rust/ficus/src/pipelines/context.rs index 620427b78..14d1d3640 100644 --- a/Ficus/src/rust/ficus/src/pipelines/context.rs +++ b/Ficus/src/rust/ficus/src/pipelines/context.rs @@ -1,12 +1,9 @@ use super::errors::pipeline_errors::PipelinePartExecutionError; -use crate::{ - pipelines::pipeline_parts::PipelineParts, - utils::{ - performance::performance_cookie::PerformanceLogger, - user_data::{ - keys::{DefaultKey, Key}, - user_data::{UserData, UserDataImpl}, - }, +use crate::utils::{ + performance::performance_cookie::PerformanceLogger, + user_data::{ + keys::{DefaultKey, Key}, + user_data::{UserData, UserDataImpl}, }, }; use getset::Getters; diff --git a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs index 77ca0d106..dbcb3bfe5 100644 --- a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs +++ b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs @@ -1,7 +1,4 @@ -use std::{ - error::Error, - fmt::{Debug, Display, Formatter}, -}; +use std::fmt::{Debug, Display, Formatter}; #[derive(Debug)] pub enum PipelinePartExecutionError { diff --git a/Ficus/src/rust/ficus/src/utils/xml_utils.rs b/Ficus/src/rust/ficus/src/utils/xml_utils.rs index e2e0cf9cd..b823ef60b 100644 --- a/Ficus/src/rust/ficus/src/utils/xml_utils.rs +++ b/Ficus/src/rust/ficus/src/utils/xml_utils.rs @@ -4,7 +4,6 @@ use quick_xml::{ }; use std::{ cell::RefCell, - error::Error, fmt::{Debug, Display}, io, string::FromUtf8Error, diff --git a/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs index 3625b9b03..c8d2cb88c 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/backend_service.rs @@ -9,7 +9,7 @@ use crate::{ }, grpc::{context_values_service::ContextValueService, pipeline_executor::ServicePipelineExecutionContext}, }; -use ficus::pipelines::pipeline_parts::{PIPELINE_PARTS, PipelineParts}; +use ficus::pipelines::pipeline_parts::PIPELINE_PARTS; use futures::Stream; use std::{pin::Pin, sync::Arc}; use tokio::sync::mpsc::{self, Sender}; diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs index b15262c8f..fb66b0522 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs @@ -16,14 +16,7 @@ use crate::{ kafka::{kafka_service::KafkaService, models::PipelineExecutionDto}, }, }; -use ficus::{ - features::cases::CaseName, - pipelines::{ - keys::context_keys::{CASE_NAME_KEY, PIPELINE_ID_KEY, PIPELINE_NAME_KEY, PROCESS_NAME_KEY, SUBSCRIPTION_ID_KEY, SUBSCRIPTION_NAME_KEY}, - pipeline_parts::PipelineParts, - }, - utils::user_data::user_data::UserData, -}; +use ficus::{features::cases::CaseName, pipelines::keys::context_keys, utils::user_data::user_data::UserData}; use futures::Stream; use std::{pin::Pin, sync::Arc}; use tokio::sync::mpsc; diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs index 50ac6f6d0..6755fc5dd 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/models.rs @@ -9,7 +9,6 @@ use ficus::{ context::PipelineContext, errors::pipeline_errors::PipelinePartExecutionError, keys::context_keys::{CASE_NAME_KEY, PROCESS_NAME_KEY, UNSTRUCTURED_METADATA_KEY}, - pipeline_parts::PipelineParts, }, utils::user_data::user_data::UserData, }; diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs index 0db84007c..735f1078a 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/streaming/t2/processors.rs @@ -13,7 +13,6 @@ use ficus::{ pipelines::{ context::{PipelineContext, PipelineInfrastructure}, keys::context_keys::EVENT_LOG_KEY, - pipeline_parts::PipelineParts, pipelines::PipelinePart, }, utils::user_data::user_data::UserData, From 2643efa8dea8474076844ca632071c26d5432d84 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 7 Jul 2026 22:27:06 +0300 Subject: [PATCH 09/35] Fix compilation --- .../ficus_backend/src/grpc/kafka/grpc_kafka_service.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs index fb66b0522..3e9577a13 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs @@ -16,7 +16,13 @@ use crate::{ kafka::{kafka_service::KafkaService, models::PipelineExecutionDto}, }, }; -use ficus::{features::cases::CaseName, pipelines::keys::context_keys, utils::user_data::user_data::UserData}; +use ficus::{ + features::cases::CaseName, + pipelines::keys::context_keys::{ + CASE_NAME_KEY, PIPELINE_ID_KEY, PIPELINE_NAME_KEY, PROCESS_NAME_KEY, SUBSCRIPTION_ID_KEY, SUBSCRIPTION_NAME_KEY, + }, + utils::user_data::user_data::UserData, +}; use futures::Stream; use std::{pin::Pin, sync::Arc}; use tokio::sync::mpsc; From b75e1a68c76e3a137d19193d6d8c42bceaaafc0c Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 13 Jul 2026 21:08:39 +0300 Subject: [PATCH 10/35] Some cleanups --- .../ficus/src/features/clustering/error.rs | 20 +++---------------- Ficus/src/rust/ficus/src/utils/silhouette.rs | 2 +- .../src/grpc/context_values_service.rs | 12 +++++------ .../src/grpc/get_context_pipeline.rs | 5 +---- .../ficus_backend/src/grpc/logs_handler.rs | 2 +- 5 files changed, 12 insertions(+), 29 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/clustering/error.rs b/Ficus/src/rust/ficus/src/features/clustering/error.rs index 61a45cef7..413af1798 100644 --- a/Ficus/src/rust/ficus/src/features/clustering/error.rs +++ b/Ficus/src/rust/ficus/src/features/clustering/error.rs @@ -2,10 +2,10 @@ use crate::{ pipelines::errors::pipeline_errors::{PipelinePartExecutionError, RawPartExecutionError}, utils::dataset::dataset::LabeledDataset, }; -use std::fmt::Display; pub type ClusteringResult = Result; +#[derive(Debug)] pub enum ClusteringError { NoRepeatSet, FailedToCreateNdArray, @@ -14,21 +14,7 @@ pub enum ClusteringError { } impl From for PipelinePartExecutionError { - fn from(val: ClusteringError) -> Self { - PipelinePartExecutionError::Raw(RawPartExecutionError::new(val.to_string())) - } -} - -impl Display for ClusteringError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str( - match self { - Self::NoRepeatSet => "NoRepeatSet".to_owned(), - Self::FailedToCreateNdArray => "FailedToCreateNdArray".to_owned(), - Self::FailedToCalculateSilhouetteScore => "FailedToCalculateSilhouetteScore".to_owned(), - Self::RawError(message) => message.clone(), - } - .as_str(), - ) + fn from(err: ClusteringError) -> Self { + PipelinePartExecutionError::Raw(RawPartExecutionError::new(format!("{err:?}"))) } } diff --git a/Ficus/src/rust/ficus/src/utils/silhouette.rs b/Ficus/src/rust/ficus/src/utils/silhouette.rs index 98dc47466..a8614c9c2 100644 --- a/Ficus/src/rust/ficus/src/utils/silhouette.rs +++ b/Ficus/src/rust/ficus/src/utils/silhouette.rs @@ -13,7 +13,7 @@ impl Display for SilhouetteScoreError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { SilhouetteScoreError::NotEnoughSamples => f.write_str("Not enough samples for silhouette score"), - &SilhouetteScoreError::InappropriateLabelsCount => f.write_str("Labels count should be 2 <= n_labels <= n_samples - 1"), + SilhouetteScoreError::InappropriateLabelsCount => f.write_str("Labels count should be 2 <= n_labels <= n_samples - 1"), } } } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs index e7ac77a25..067945bf7 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs @@ -180,6 +180,12 @@ impl GrpcContextValuesService for GrpcContextValueService { } } + async fn drop_context_values(&self, request: Request) -> Result, Status> { + self.cv_service.prune_context_values(&request.get_ref().ids); + + Ok(Response::new(())) + } + async fn get_context_value_id(&self, request: Request) -> Result, Status> { let key_name = &request.get_ref().key.as_ref().unwrap().name; match find_context_key(key_name) { @@ -202,10 +208,4 @@ impl GrpcContextValuesService for GrpcContextValueService { }) }) } - - async fn drop_context_values(&self, request: Request) -> Result, Status> { - self.cv_service.prune_context_values(&request.get_ref().ids); - - Ok(Response::new(())) - } } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs b/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs index f723defea..63def0aff 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs @@ -105,10 +105,7 @@ impl GetContextValuePipelinePart { } fn value_or_default(context: &PipelineContext, key: &DefaultContextKey, default_factory: impl Fn() -> T) -> T { - match context.concrete(key.key()) { - None => default_factory(), - Some(value) => value.clone(), - } + context.concrete(key.key()).cloned().unwrap_or_else(default_factory) } fn value_or_none(context: &PipelineContext, key: &DefaultContextKey) -> Option { diff --git a/Ficus/src/rust/ficus_backend/src/grpc/logs_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/logs_handler.rs index 32517b4d1..bb0d8b4ad 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/logs_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/logs_handler.rs @@ -22,7 +22,7 @@ impl GrpcLogMessageHandlerImpl { } #[derive(Clone)] -pub struct ConsoleLogMessageHandler {} +pub struct ConsoleLogMessageHandler; impl LogMessageHandler for ConsoleLogMessageHandler { fn handle(&self, message: &str) -> Result<(), PipelinePartExecutionError> { From 51e5a601492a6409eaac58582d8283ee84cbe14d Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 30 Aug 2026 22:55:59 +0300 Subject: [PATCH 11/35] WIP on ECFG -> Petri Nets transformation --- .../features/discovery/ecfg/adjustments.rs | 1 + .../ficus/src/features/discovery/ecfg/mod.rs | 1 + .../features/discovery/ecfg/to_petri_net.rs | 67 +++++++++++++++++++ .../features/discovery/petri_net/petri_net.rs | 13 ++-- 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs index 2e7df2c36..faa6a84ab 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs @@ -134,6 +134,7 @@ fn connect_added_merged_node_to_graph(nodes_ids: &NeededNodesIds, added_node: &u added_node, NodesConnectionData::new(None, start_node_edge_weight, None), ); + graph.connect_nodes( added_node, &nodes_ids.end_node, diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/mod.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/mod.rs index fbe066916..4c90b818e 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/mod.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/mod.rs @@ -6,3 +6,4 @@ pub mod discovery_xes; pub mod log_prepare; pub mod models; pub mod root_sequence; +pub mod to_petri_net; diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs new file mode 100644 index 000000000..917d5b788 --- /dev/null +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -0,0 +1,67 @@ +use crate::{ + features::{ + discovery::petri_net::{petri_net::DefaultPetriNet, place::Place, transition::Transition}, + mutations::mutations::ARTIFICIAL_START_EVENT_NAME, + }, + utils::graph::graph::DefaultGraph, +}; +use std::{ + collections::{HashMap, VecDeque}, + sync::Arc, +}; + +pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result { + if graph.all_nodes().is_empty() { + return Ok(Default::default()); + } + + let start_node = graph + .nodes + .iter() + .find(|n| graph.incoming_edges(n.0).is_empty()) + .map(|n| n.0) + .copied() + .expect("must contain start node"); + + let mut petri_net = DefaultPetriNet::default(); + let start_place = Place::with_name(ARTIFICIAL_START_EVENT_NAME.to_string()); + let start_place = petri_net.add_place(start_place); + + let mut q = VecDeque::new(); + q.push_back((start_place, start_node)); + + let mut nodes_to_places = HashMap::::default(); + + while let Some((from_place, node)) = q.pop_front() { + let node = graph.node(&node).expect("must be in graph"); + let name = Arc::clone(node.data.as_ref().expect("must have name for all transitions")); + let t_id = if let Some(t) = petri_net + .get_outgoing_transitions(&from_place) + .iter() + .find(|t| t.name() == name.as_ref()) + { + t.id() + } else { + println!("{from_place:?}, {name:?}, {:?}", petri_net.get_outgoing_transitions(&from_place)); + petri_net.add_transition(Transition::empty(name.clone(), false, None)) + }; + + let to_place = nodes_to_places.get(&node.id()).copied().unwrap_or_else(|| { + let next_id = petri_net.all_places().len(); + petri_net.add_place(Place::with_name(format!("From-{name}-{next_id}"))) + }); + + if !petri_net.transition(&t_id).outgoing_arcs().iter().any(|a| a.place_id() == to_place) { + petri_net.connect_place_to_transition(&from_place, &t_id, None); + petri_net.connect_transition_to_place(&t_id, &to_place, None); + + nodes_to_places.insert(node.id, to_place); + } + + for &out_node in graph.outgoing_nodes(node.id()) { + q.push_back((to_place, out_node)); + } + } + + Ok(petri_net) +} diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/petri_net.rs index af5e1eb8b..1efeddd46 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/petri_net.rs @@ -10,6 +10,11 @@ struct PlaceTransitions { outgoing_transitions: Vec, } +pub const EMPTY_PLACE_TRANSITIONS: &PlaceTransitions = &PlaceTransitions { + incoming_transitions: vec![], + outgoing_transitions: vec![], +}; + #[derive(Debug, Default)] pub struct PetriNet where @@ -50,10 +55,10 @@ where id } - pub fn connect_place_to_transition(&mut self, from_place_id: &u64, to_transition_index: &u64, arc_data: Option) { + pub fn connect_place_to_transition(&mut self, from_place_id: &u64, to_transition_id: &u64, arc_data: Option) { self .transitions - .get_mut(to_transition_index) + .get_mut(to_transition_id) .unwrap() .add_incoming_arc(from_place_id, arc_data); @@ -63,7 +68,7 @@ where .get_mut(from_place_id) .unwrap() .outgoing_transitions - .push(*to_transition_index); + .push(*to_transition_id); } fn init_places_transitions(&mut self, place_id: &u64) { @@ -160,7 +165,7 @@ where } fn get_place_transitions(&self, place_id: &u64) -> &PlaceTransitions { - self.places_to_transitions.get(place_id).unwrap() + self.places_to_transitions.get(place_id).unwrap_or(EMPTY_PLACE_TRANSITIONS) } fn map_transitions(&self, ids: &[u64]) -> Vec<&Transition> { From 161f0f1219ee0a2ab257c5e5c5959c3596234c80 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 31 Aug 2026 21:09:16 +0300 Subject: [PATCH 12/35] WIP on ECFG -> Petri Nets transformation --- .../features/discovery/ecfg/to_petri_net.rs | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index 917d5b788..a7ccc2095 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -9,6 +9,7 @@ use std::{ collections::{HashMap, VecDeque}, sync::Arc, }; +use std::fmt::format; pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result { if graph.all_nodes().is_empty() { @@ -24,43 +25,36 @@ pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result .expect("must contain start node"); let mut petri_net = DefaultPetriNet::default(); - let start_place = Place::with_name(ARTIFICIAL_START_EVENT_NAME.to_string()); - let start_place = petri_net.add_place(start_place); - let mut q = VecDeque::new(); - q.push_back((start_place, start_node)); + let mut next_place_id = 0; + let mut nodes_data = HashMap::new(); - let mut nodes_to_places = HashMap::::default(); - - while let Some((from_place, node)) = q.pop_front() { - let node = graph.node(&node).expect("must be in graph"); + for node in graph.all_nodes() { let name = Arc::clone(node.data.as_ref().expect("must have name for all transitions")); - let t_id = if let Some(t) = petri_net - .get_outgoing_transitions(&from_place) - .iter() - .find(|t| t.name() == name.as_ref()) - { - t.id() - } else { - println!("{from_place:?}, {name:?}, {:?}", petri_net.get_outgoing_transitions(&from_place)); - petri_net.add_transition(Transition::empty(name.clone(), false, None)) - }; + let t_id = petri_net.add_transition(Transition::empty(name.clone(), false, None)); + + let in_place = petri_net.add_place(Place::with_name(format!("{next_place_id}"))); + let out_place = petri_net.add_place(Place::with_name(format!("{}", next_place_id + 1))); + + petri_net.connect_place_to_transition(&in_place, &t_id, None); + petri_net.connect_transition_to_place(&t_id, &out_place, None); + + nodes_data.insert(node.id, (in_place, out_place)); + next_place_id += 2; + } - let to_place = nodes_to_places.get(&node.id()).copied().unwrap_or_else(|| { - let next_id = petri_net.all_places().len(); - petri_net.add_place(Place::with_name(format!("From-{name}-{next_id}"))) - }); + let mut s_t_idx = 0; + for edge in graph.all_edges() { + let from_place = nodes_data[edge.from_node()].1; + let to_place = nodes_data[edge.to_node()].0; - if !petri_net.transition(&t_id).outgoing_arcs().iter().any(|a| a.place_id() == to_place) { - petri_net.connect_place_to_transition(&from_place, &t_id, None); - petri_net.connect_transition_to_place(&t_id, &to_place, None); + let name = Arc::from(format!("{s_t_idx}")); + let s_t = petri_net.add_transition(Transition::empty(name, true, None)); - nodes_to_places.insert(node.id, to_place); - } + petri_net.connect_place_to_transition(&from_place, &s_t, None); + petri_net.connect_transition_to_place(&s_t, &to_place, None); - for &out_node in graph.outgoing_nodes(node.id()) { - q.push_back((to_place, out_node)); - } + s_t_idx += 1; } Ok(petri_net) From 1240b33670e44b3bb7705db207bb39f3b57a57af Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 1 Sep 2026 21:38:31 +0300 Subject: [PATCH 13/35] Better silent transitions --- .../src/features/discovery/alpha/alpha.rs | 8 ++---- .../alpha_plus_plus_nfc.rs | 2 +- .../features/discovery/ecfg/to_petri_net.rs | 27 +++---------------- .../discovery/heuristic/heuristic_miner.rs | 7 +++-- .../discovery/petri_net/transition.rs | 23 +++++++++++++--- 5 files changed, 29 insertions(+), 38 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha.rs b/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha.rs index dbd9dba5f..6e8e9576b 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha.rs @@ -68,11 +68,7 @@ fn add_one_length_loops(provider: &impl AlphaPlusRelationsProvider, original_log } } - let id = petri_net.add_transition(Transition::empty( - transition_name.to_owned(), - false, - Some(transition_name.to_owned()), - )); + let id = petri_net.add_transition(Transition::empty(transition_name.to_owned(), Some(transition_name.to_owned()))); let place_id = match petri_net.find_place_id_by_name(alpha_set.to_string().as_str()) { Some(found_place_id) => found_place_id, @@ -196,7 +192,7 @@ fn create_petri_net(info: &dyn EventLogInfo, alpha_sets: Vec<&AlphaSet>) -> Defa let mut petri_net = PetriNet::default(); let mut event_classes_to_transition_ids = HashMap::new(); for class in info.all_event_classes() { - let id = petri_net.add_transition(Transition::empty(class.to_owned(), false, Some(class.to_owned()))); + let id = petri_net.add_transition(Transition::empty(class.to_owned(), Some(class.to_owned()))); event_classes_to_transition_ids.insert(class.as_ref(), id); } diff --git a/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha_plus_plus_nfc/alpha_plus_plus_nfc.rs b/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha_plus_plus_nfc/alpha_plus_plus_nfc.rs index b44a1d193..80a0be905 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha_plus_plus_nfc/alpha_plus_plus_nfc.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/alpha/alpha_plus_plus_nfc/alpha_plus_plus_nfc.rs @@ -195,7 +195,7 @@ pub fn discover_petri_net_alpha_plus_plus_nfc(log: &TLog) -> Def let mut resulting_net = DefaultPetriNet::default(); let mut transitions_to_ids = HashMap::new(); for transition in info.all_event_classes().iter().copied().chain(one_length_loop_transitions.iter()) { - let id = resulting_net.add_transition(Transition::empty((*transition).to_owned(), false, Some((*transition).to_owned()))); + let id = resulting_net.add_transition(Transition::empty((*transition).to_owned(), Some((*transition).to_owned()))); transitions_to_ids.insert(transition, id); } diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index a7ccc2095..a5373f0f2 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -1,29 +1,14 @@ use crate::{ - features::{ - discovery::petri_net::{petri_net::DefaultPetriNet, place::Place, transition::Transition}, - mutations::mutations::ARTIFICIAL_START_EVENT_NAME, - }, + features::discovery::petri_net::{petri_net::DefaultPetriNet, place::Place, transition::Transition}, utils::graph::graph::DefaultGraph, }; -use std::{ - collections::{HashMap, VecDeque}, - sync::Arc, -}; -use std::fmt::format; +use std::{collections::HashMap, sync::Arc}; pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result { if graph.all_nodes().is_empty() { return Ok(Default::default()); } - let start_node = graph - .nodes - .iter() - .find(|n| graph.incoming_edges(n.0).is_empty()) - .map(|n| n.0) - .copied() - .expect("must contain start node"); - let mut petri_net = DefaultPetriNet::default(); let mut next_place_id = 0; @@ -31,7 +16,7 @@ pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result for node in graph.all_nodes() { let name = Arc::clone(node.data.as_ref().expect("must have name for all transitions")); - let t_id = petri_net.add_transition(Transition::empty(name.clone(), false, None)); + let t_id = petri_net.add_transition(Transition::empty(name.clone(), Some(name.clone()))); let in_place = petri_net.add_place(Place::with_name(format!("{next_place_id}"))); let out_place = petri_net.add_place(Place::with_name(format!("{}", next_place_id + 1))); @@ -43,18 +28,14 @@ pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result next_place_id += 2; } - let mut s_t_idx = 0; for edge in graph.all_edges() { let from_place = nodes_data[edge.from_node()].1; let to_place = nodes_data[edge.to_node()].0; - let name = Arc::from(format!("{s_t_idx}")); - let s_t = petri_net.add_transition(Transition::empty(name, true, None)); + let s_t = petri_net.add_transition(Transition::silent()); petri_net.connect_place_to_transition(&from_place, &s_t, None); petri_net.connect_transition_to_place(&s_t, &to_place, None); - - s_t_idx += 1; } Ok(petri_net) diff --git a/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs b/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs index 8bbe9b72c..aa43d5a3d 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs @@ -46,7 +46,7 @@ pub fn discover_petri_net_heuristic( fn construct_heuristic_petri_net(provider: &HeuristicMinerRelationsProvider, petri_net: &mut DefaultPetriNet) { let mut classes_to_ids = HashMap::new(); for class in provider.log_info().all_event_classes() { - let id = petri_net.add_transition(Transition::empty(class.to_owned(), false, Some(class.to_owned()))); + let id = petri_net.add_transition(Transition::empty(class.to_owned(), Some(class.to_owned()))); classes_to_ids.insert(class.to_owned(), id); } @@ -92,8 +92,7 @@ fn construct_heuristic_petri_net(provider: &HeuristicMinerRelationsProvider, pet petri_net.connect_transition_to_place(classes_to_ids.get(first_class).unwrap(), &post_place_id, None); for group in ¶llel_groups { - let name = Arc::::from(format!("silent_start_{first_class}")); - let id = petri_net.add_transition(Transition::empty(name.to_owned(), true, Some(name.to_owned()))); + let id = petri_net.add_transition(Transition::silent()); petri_net.connect_place_to_transition(&post_place_id, &id, None); for el in group.set().iter() { @@ -103,7 +102,7 @@ fn construct_heuristic_petri_net(provider: &HeuristicMinerRelationsProvider, pet used.insert(*el); } - } + }; for follower in &followers { if !used.contains(follower) { diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/transition.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/transition.rs index 16a7d5afa..7cc1c5571 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/transition.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/transition.rs @@ -37,17 +37,32 @@ where } } +impl Transition, ()> { + pub fn silent() -> Self { + const NAME: &str = "tau"; + + Self { + id: next_id(), + name: Arc::from(NAME), + silent_transition: true, + incoming_arcs: vec![], + outgoing_arcs: vec![], + data: Some(Arc::from(NAME)), + } + } +} + impl Transition where TTransitionData: ToString, { - pub fn empty(name: Arc, silent_transition: bool, data: Option) -> Self { + pub fn empty(name: Arc, data: Option) -> Self { Self { id: next_id(), name, - silent_transition, - incoming_arcs: Vec::new(), - outgoing_arcs: Vec::new(), + silent_transition: false, + incoming_arcs: vec![], + outgoing_arcs: vec![], data, } } From 9a23fec0c90a0cdd585d59396805230d1db1d561 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 1 Sep 2026 22:07:55 +0300 Subject: [PATCH 14/35] Write silent transitions tags --- .../discovery/petri_net/pnml_serialization.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs index e80b3874f..a65b1f5bc 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs @@ -2,6 +2,7 @@ use crate::{ features::discovery::petri_net::{arc::PetriNetArc, petri_net::PetriNet, place::Place, transition::Transition}, utils::xml_utils::{StartEndElementCookie, XmlWriteError}, }; +use log::trace; use quick_xml::{ Writer, events::{BytesText, Event}, @@ -16,6 +17,14 @@ const NET_TAG_NAME: &str = "net"; const TEXT_TAG_NAME: &str = "text"; const NAME_TAG_NAME: &str = "name"; +const TOOL_SPECIFIC_TAG_NAME: &str = "toolspecific"; +const TOOL_ATTR_NAME: &str = "tool"; +const PROM_VALUE: &str = "ProM"; +const VERSION_ATTR_NAME: &str = "version"; +const VERSION_VALUE: &str = "6.4"; +const ACTIVITY_ATTR_NAME: &str = "activity"; +const SILENT_ACTIVITY: &str = "$invisible$"; + const ID_ATTR_NAME: &str = "id"; const SOURCE_ATTR_NAME: &str = "source"; const TARGET_ATTR_NAME: &str = "target"; @@ -116,6 +125,18 @@ where drop(name); } + if transition.is_silent() { + let _ = StartEndElementCookie::new_with_attrs( + writer, + TOOL_SPECIFIC_TAG_NAME, + &vec![ + (TOOL_ATTR_NAME, PROM_VALUE), + (VERSION_ATTR_NAME, VERSION_VALUE), + (ACTIVITY_ATTR_NAME, SILENT_ACTIVITY), + ], + ); + } + drop(cookie) } From 589c8d81330942dfe19d35798d6d431705dba9e7 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Thu, 3 Sep 2026 20:31:49 +0300 Subject: [PATCH 15/35] WIP: add tests --- .../ficus/tests/discovery/ecfg_discovery.rs | 208 +++++++++++++++--- 1 file changed, 177 insertions(+), 31 deletions(-) diff --git a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs index cd2f0e748..d27b1f08d 100644 --- a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs +++ b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs @@ -1,11 +1,16 @@ +use crate::test_core::{gold_based_test::execute_test_with_gold, test_paths::get_serialized_petri_nets_gold_path}; use ficus::{ - features::discovery::ecfg::{ - context::DiscoveryContext, - discovery::discover_ecfg, - models::{EventWithUniqueId, RootSequenceKind}, - root_sequence::discover_root_sequence, + features::discovery::{ + ecfg::{ + context::DiscoveryContext, + discovery::discover_ecfg, + models::{EventWithUniqueId, RootSequenceKind}, + root_sequence::discover_root_sequence, + to_petri_net::convert_to_petri_net, + }, + petri_net::pnml_serialization::serialize_to_pnml, }, - utils::user_data::user_data::UserDataImpl, + utils::{graph::graph::DefaultGraph, user_data::user_data::UserDataImpl}, vecs, }; use std::sync::Arc; @@ -28,6 +33,14 @@ pub fn test_ecfg_1() { ); } +#[test] +pub fn test_ecfg_to_petri_net_1() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_1", + vec![vecs!["A", "B", "C", "D", "E"], vecs!["A", "B", "D", "E"]], + ); +} + #[test] pub fn test_ecfg_2() { execute_ecfg_discovery_test( @@ -47,6 +60,14 @@ pub fn test_ecfg_2() { ); } +#[test] +pub fn test_ecfg_to_petri_net_2() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_2", + vec![vecs!["A", "B", "C", "D", "E"], vecs!["A", "X", "Y", "E"]], + ); +} + #[test] pub fn test_ecfg_3() { execute_ecfg_discovery_test( @@ -67,16 +88,34 @@ pub fn test_ecfg_3() { ) } +#[test] +pub fn test_ecfg_to_petri_net_3() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_3", + vec![vecs!["A"], vecs!["B"], vecs!["C"], vecs!["D"], vecs!["E"]], + ); +} + #[test] pub fn test_ecfg_4() { execute_ecfg_discovery_test(vec![], vec![], vec![]) } +#[test] +pub fn test_ecfg_to_petri_net_4() { + execute_to_petri_net_test("test_ecfg_to_petri_net_4", vec![]); +} + #[test] pub fn test_ecfg_5() { execute_ecfg_discovery_test(vec![vecs![]], vecs!["START", "END"], vec!["[START]--[END]"]) } +#[test] +pub fn test_ecfg_to_petri_net_5() { + execute_to_petri_net_test("test_ecfg_to_petri_net_5", vec![vecs![]]); +} + #[test] pub fn test_ecfg_6() { execute_ecfg_discovery_test( @@ -109,6 +148,17 @@ pub fn test_ecfg_6() { ) } +#[test] +pub fn test_ecfg_to_petri_net_6() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_6", + vec![ + vecs!["A", "X", "B", "Y", "C", "Z", "D", "W", "E"], + vecs!["X", "A", "Y", "B", "Z", "C", "W", "D"], + ], + ); +} + #[test] pub fn test_ecfg_7() { execute_ecfg_discovery_test( @@ -138,6 +188,17 @@ pub fn test_ecfg_7() { ); } +#[test] +pub fn test_ecfg_to_petri_net_7() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_7", + vec![ + vecs!["X", "A", "Y", "B", "Z", "C", "W", "D", "Z", "E"], + vecs!["A", "B", "C", "D", "E"], + ], + ); +} + #[test] pub fn test_ecfg_8() { execute_ecfg_discovery_test( @@ -163,6 +224,19 @@ pub fn test_ecfg_8() { ); } +#[test] +pub fn test_ecfg_to_petri_net_8() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_8", + vec![ + vecs!["A", "B", "C", "D", "E"], + vecs!["A", "X", "B", "C", "D", "E"], + vecs!["A", "X", "C", "D", "E"], + vecs!["A", "X", "D", "E"], + ], + ); +} + #[test] pub fn test_ecfg_9() { execute_ecfg_discovery_test( @@ -193,6 +267,20 @@ pub fn test_ecfg_9() { ) } +#[test] +pub fn test_ecfg_to_petri_net_9() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_9", + vec![ + vecs!["A", "B", "C", "D", "E"], + vecs!["A", "X", "Y", "Z", "W", "B", "C", "D", "E"], + vecs!["A", "Y", "Z", "W", "B", "C", "D", "E"], + vecs!["A", "Z", "W", "B", "C", "D", "E"], + vecs!["A", "X", "B", "C", "D", "E"], + ], + ); +} + #[test] pub fn test_ecfg_10() { execute_ecfg_discovery_test( @@ -225,6 +313,19 @@ pub fn test_ecfg_10() { ) } +#[test] +pub fn test_ecfg_to_petri_net_10() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_10", + vec![ + vecs!["A", "B", "C", "D", "E"], + vecs!["A", "X", "Y", "Z", "W", "B", "C", "D", "E"], + vecs!["A", "y", "z", "w", "B", "C", "D", "E"], + vecs!["A", "V", "B", "C", "D", "E"], + ], + ); +} + #[test] pub fn test_ecfg_11() { execute_ecfg_discovery_test( @@ -344,7 +445,71 @@ pub fn test_ecfg_11() { ) } -fn execute_ecfg_discovery_test(mut traces: Vec>, gold_root_sequence: Vec, gold_graph_edges: Vec<&str>) { +#[test] +pub fn test_ecfg_to_petri_net_11() { + execute_to_petri_net_test( + "test_ecfg_to_petri_net_11", + vec![ + vecs!["5", "6", "7", "8", "0"], + vecs!["13", "1", "0", "9", "14"], + vecs![ + "13", "Loop[6]", "Loop[7]", "8", "Loop[15]", "18", "19", "20", "21", "17", "22", "Loop[23]", "Loop[24]", "8", "Loop[16]", "14" + ], + vecs!["13", "Loop[6]", "Loop[7]", "9", "Loop[26]", "23", "Loop[24]", "27", "8", "14"], + vecs![ + "5", "7", "0", "28", "26", "Loop[23]", "Loop[24]", "8", "16", "0", "28", "20", "21", "10" + ], + vecs!["5", "Loop[6]", "29", "7", "8", "Loop[17]", "11", "28", "8", "Loop[16]", "10"], + vecs![ + "5", "Loop[7]", "8", "0", "20", "21", "18", "19", "20", "21", "Loop[17]", "22", "Loop[26]", "8", "Loop[16]", "10" + ], + vecs![ + "13", "Loop[6]", "29", "Loop[7]", "8", "15", "30", "Loop[26]", "Loop[23]", "Loop[24]", "Loop[27]", "8", "Loop[0]", "28", "20", + "21", "16", "14" + ], + vecs![ + "5", "7", "31", "15", "18", "19", "20", "21", "Loop[17]", "11", "28", "Loop[26]", "23", "8", "Loop[16]", "10" + ], + ], + ); +} + +fn execute_ecfg_discovery_test(traces: Vec>, gold_root_sequence: Vec, gold_graph_edges: Vec<&str>) { + let graph = discover_ecfg_internal(traces, Some(gold_root_sequence)); + + let test_result = graph.serialize_edges_deterministic(false); + + let gold = gold_graph_edges.join("\n"); + + if test_result != gold { + let mut tgraph = DirectedGraph::new(); + tgraph.add_nodes(graph.all_nodes().into_iter().map(|n| (*n.id(), n.data().unwrap().to_owned()))); + tgraph.add_edges(graph.all_edges().into_iter().map(|e| (*e.from_node(), *e.to_node()))); + + let tconfig = Config::new(ValueFormatter::new(), 10).default_colors(); + + termgraph::display(&tgraph, &tconfig); + + println!("GOLD:"); + println!("{}", gold); + + println!("TEST RESULT:"); + println!("{}", test_result); + + assert!(false); + } +} + +fn execute_to_petri_net_test(test_name: &str, traces: Vec>) { + execute_test_with_gold(get_serialized_petri_nets_gold_path(test_name), || { + let graph = discover_ecfg_internal(traces, None); + let petri_net = convert_to_petri_net(&graph).ok().unwrap(); + + serialize_to_pnml(&petri_net, false).ok().unwrap() + }); +} + +fn discover_ecfg_internal(mut traces: Vec>, gold_root_sequence: Option>) -> DefaultGraph { const START: &'static str = "START"; const END: &'static str = "END"; @@ -355,7 +520,10 @@ fn execute_ecfg_discovery_test(mut traces: Vec>, gold_root_sequence: let root_sequence_kind = RootSequenceKind::FindBest; let root_sequence = discover_root_sequence(&traces, root_sequence_kind); - assert_eq!(root_sequence, gold_root_sequence); + + if let Some(gold_root_sequence) = gold_root_sequence { + assert_eq!(root_sequence, gold_root_sequence); + } let name_extractor = |s: &String| Arc::from(s.to_owned()); @@ -377,27 +545,5 @@ fn execute_ecfg_discovery_test(mut traces: Vec>, gold_root_sequence: .map(|t| t.into_iter().map(|e| EventWithUniqueId::new(e)).collect()) .collect(); - let graph = discover_ecfg(&traces, &context, false, None).ok().unwrap().graph_move(); - - let test_result = graph.serialize_edges_deterministic(false); - - let gold = gold_graph_edges.join("\n"); - - if test_result != gold { - let mut tgraph = DirectedGraph::new(); - tgraph.add_nodes(graph.all_nodes().into_iter().map(|n| (*n.id(), n.data().unwrap().to_owned()))); - tgraph.add_edges(graph.all_edges().into_iter().map(|e| (*e.from_node(), *e.to_node()))); - - let tconfig = Config::new(ValueFormatter::new(), 10).default_colors(); - - termgraph::display(&tgraph, &tconfig); - - println!("GOLD:"); - println!("{}", gold); - - println!("TEST RESULT:"); - println!("{}", test_result); - - assert!(false); - } + discover_ecfg(&traces, &context, false, None).ok().unwrap().graph_move() } From a730c5bea7670649e67ea0fc076d3c15dbb15e3c Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 4 Sep 2026 14:26:42 +0300 Subject: [PATCH 16/35] Add backend ConvertEcfgToPetriNet pipeline part --- .../ficus/src/features/discovery/ecfg/to_petri_net.rs | 2 +- Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs | 9 +++++++++ .../rust/ficus/src/pipelines/errors/pipeline_errors.rs | 4 ++-- Ficus/src/rust/ficus/src/pipelines/parts_names.rs | 1 + Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs | 1 + Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs | 4 ++-- .../rust/ficus/tests/pipelines/pipeline_parts_tests.rs | 3 ++- 7 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index a5373f0f2..ca5a8e2a6 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -4,7 +4,7 @@ }; use std::{collections::HashMap, sync::Arc}; -pub fn convert_to_petri_net(graph: &DefaultGraph) -> Result { +pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result { if graph.all_nodes().is_empty() { return Ok(Default::default()); } diff --git a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs index 777e65d95..b33b3e90c 100644 --- a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs @@ -1,3 +1,4 @@ +use crate::features::discovery::ecfg::to_petri_net::convert_ecfg_to_petri_net; use crate::{ features::{ analysis::{ @@ -211,4 +212,12 @@ impl PipelineParts { Err(err) => Err(PipelinePartExecutionError::Raw(RawPartExecutionError::new(err.to_string()))), } }); + + pipeline_part!(convert_ecfg_to_petri_net, |context: &mut PipelineContext, _, _| { + let graph = Self::get_user_data(context, &GRAPH_KEY)?; + let petri_net = convert_ecfg_to_petri_net(graph).map_err(|err| PipelinePartExecutionError::new_raw("failed to convert ECFG to Petri net"))?; + + context.put_concrete(PETRI_NET_KEY.key(), petri_net); + Ok(()) + }); } diff --git a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs index dbcb3bfe5..1ec54f10e 100644 --- a/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs +++ b/Ficus/src/rust/ficus/src/pipelines/errors/pipeline_errors.rs @@ -20,8 +20,8 @@ impl Display for PipelinePartExecutionError { } impl PipelinePartExecutionError { - pub fn new_raw(error: String) -> Self { - PipelinePartExecutionError::Raw(RawPartExecutionError::new(error)) + pub fn new_raw(error: impl Into) -> Self { + PipelinePartExecutionError::Raw(RawPartExecutionError::new(error.into())) } } diff --git a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs index 45af14388..d0b398711 100644 --- a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs +++ b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs @@ -58,6 +58,7 @@ impl PipelineParts { pub const DISCOVER_ECFG: &'static str = "DiscoverECFG"; pub const DISCOVER_LOOPS_STRICT: &'static str = "DiscoverLoopsStrict"; pub const DISCOVER_TRACES_TIMELINE_DIAGRAM: &'static str = "DiscoverTracesTimelineDiagram"; + pub const CONVERT_ECFG_TO_PETRI_NET: &'static str = "ConvertEcfgToPetriNet"; pub const PREPARE_SOFTWARE_EVENT_LOG: &'static str = "PrepareSoftwareEventLog"; pub const SHORTEN_ALLOCATION_TYPE: &'static str = "ShortenAllocationType"; diff --git a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs index e7e1b3e0c..528228fb9 100644 --- a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs @@ -149,6 +149,7 @@ impl PipelineParts { Self::clear_graphs(), Self::terminate_if_empty_log(), Self::create_ocel_annotation_for_dag(), + Self::convert_ecfg_to_petri_net(), ]; let mut names_to_parts = HashMap::new(); diff --git a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs index d27b1f08d..4462578a3 100644 --- a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs +++ b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs @@ -6,7 +6,7 @@ use ficus::{ discovery::discover_ecfg, models::{EventWithUniqueId, RootSequenceKind}, root_sequence::discover_root_sequence, - to_petri_net::convert_to_petri_net, + to_petri_net::convert_ecfg_to_petri_net, }, petri_net::pnml_serialization::serialize_to_pnml, }, @@ -503,7 +503,7 @@ fn execute_ecfg_discovery_test(traces: Vec>, gold_root_sequence: Vec fn execute_to_petri_net_test(test_name: &str, traces: Vec>) { execute_test_with_gold(get_serialized_petri_nets_gold_path(test_name), || { let graph = discover_ecfg_internal(traces, None); - let petri_net = convert_to_petri_net(&graph).ok().unwrap(); + let petri_net = convert_ecfg_to_petri_net(&graph).ok().unwrap(); serialize_to_pnml(&petri_net, false).ok().unwrap() }); diff --git a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs index 545fe9008..335c7cddc 100644 --- a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs +++ b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs @@ -91,7 +91,8 @@ fn get_test_parts_names() -> Vec { "AddGraphToGraphs", "ClearGraphs", "TerminateIfEmptyLog", - "CreateOcelAnnotationForDag" + "CreateOcelAnnotationForDag", + "ConvertEcfgToPetriNet" ] } From 8eed09e33c111360c9d7e3aba04edd0361fd49c5 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 4 Sep 2026 14:27:03 +0300 Subject: [PATCH 17/35] cargo fmt --- .../src/features/discovery/heuristic/heuristic_miner.rs | 2 +- Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs b/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs index aa43d5a3d..440868e87 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/heuristic/heuristic_miner.rs @@ -102,7 +102,7 @@ fn construct_heuristic_petri_net(provider: &HeuristicMinerRelationsProvider, pet used.insert(*el); } - }; + } for follower in &followers { if !used.contains(follower) { diff --git a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs index b33b3e90c..3639ba606 100644 --- a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs @@ -1,4 +1,3 @@ -use crate::features::discovery::ecfg::to_petri_net::convert_ecfg_to_petri_net; use crate::{ features::{ analysis::{ @@ -11,7 +10,7 @@ use crate::{ alpha_plus_plus_nfc::alpha_plus_plus_nfc::discover_petri_net_alpha_plus_plus_nfc, providers::{alpha_plus_provider::AlphaPlusRelationsProviderImpl, alpha_provider::DefaultAlphaRelationsProvider}, }, - ecfg::discovery_xes::discover_ecfg_from_event_log, + ecfg::{discovery_xes::discover_ecfg_from_event_log, to_petri_net::convert_ecfg_to_petri_net}, fuzzy::fuzzy_miner::discover_graph_fuzzy, heuristic::heuristic_miner::discover_petri_net_heuristic, petri_net::{marking::ensure_initial_marking, pnml_serialization::serialize_to_pnml_file}, @@ -215,7 +214,8 @@ impl PipelineParts { pipeline_part!(convert_ecfg_to_petri_net, |context: &mut PipelineContext, _, _| { let graph = Self::get_user_data(context, &GRAPH_KEY)?; - let petri_net = convert_ecfg_to_petri_net(graph).map_err(|err| PipelinePartExecutionError::new_raw("failed to convert ECFG to Petri net"))?; + let petri_net = + convert_ecfg_to_petri_net(graph).map_err(|err| PipelinePartExecutionError::new_raw("failed to convert ECFG to Petri net"))?; context.put_concrete(PETRI_NET_KEY.key(), petri_net); Ok(()) From cf89438c1a84ce889a676b9e84d3dd6c5c1b5463 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 4 Sep 2026 15:23:48 +0300 Subject: [PATCH 18/35] Add frontend pipeline part --- Ficus/src/python/ficus/grpc_pipelines/constants.py | 1 + Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py | 4 ++++ Ficus/src/rust/ficus/src/pipelines/parts_names.rs | 2 +- Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Ficus/src/python/ficus/grpc_pipelines/constants.py b/Ficus/src/python/ficus/grpc_pipelines/constants.py index b8066135c..02d72fab6 100644 --- a/Ficus/src/python/ficus/grpc_pipelines/constants.py +++ b/Ficus/src/python/ficus/grpc_pipelines/constants.py @@ -152,6 +152,7 @@ const_discover_ecfg = 'DiscoverECFG' const_discover_loops_strict = 'DiscoverLoopsStrict' const_discover_traces_timeline_diagram = 'DiscoverTracesTimelineDiagram' +const_convert_ecfg_to_petri_net = 'ConvertECFGToPetriNet' const_prepare_software_log = 'PrepareSoftwareEventLog' const_shorten_allocation_type = 'ShortenAllocationType' diff --git a/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py b/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py index 0fe4ae778..81efa96bf 100644 --- a/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py +++ b/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py @@ -262,3 +262,7 @@ def to_grpc_part(self) -> GrpcPipelinePartBase: append_bool_value(config, const_merge_sequences_of_events, self.merge_sequences_of_events) return GrpcPipelinePartBase(defaultPart=create_default_pipeline_part(const_discover_ecfg, config)) + +class ConvertECFGToPetriNet(PipelinePart): + def to_grpc_part(self) -> GrpcPipelinePartBase: + return GrpcPipelinePartBase(defaultPart=create_default_pipeline_part(const_convert_ecfg_to_petri_net)) diff --git a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs index d0b398711..5dec58941 100644 --- a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs +++ b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs @@ -58,7 +58,7 @@ impl PipelineParts { pub const DISCOVER_ECFG: &'static str = "DiscoverECFG"; pub const DISCOVER_LOOPS_STRICT: &'static str = "DiscoverLoopsStrict"; pub const DISCOVER_TRACES_TIMELINE_DIAGRAM: &'static str = "DiscoverTracesTimelineDiagram"; - pub const CONVERT_ECFG_TO_PETRI_NET: &'static str = "ConvertEcfgToPetriNet"; + pub const CONVERT_ECFG_TO_PETRI_NET: &'static str = "ConvertECFGToPetriNet"; pub const PREPARE_SOFTWARE_EVENT_LOG: &'static str = "PrepareSoftwareEventLog"; pub const SHORTEN_ALLOCATION_TYPE: &'static str = "ShortenAllocationType"; diff --git a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs index 335c7cddc..744644175 100644 --- a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs +++ b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs @@ -92,7 +92,7 @@ fn get_test_parts_names() -> Vec { "ClearGraphs", "TerminateIfEmptyLog", "CreateOcelAnnotationForDag", - "ConvertEcfgToPetriNet" + "ConvertECFGToPetriNet" ] } From c73693150f724cc4ba64cedde5aefffa16551443 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 4 Sep 2026 19:55:53 +0300 Subject: [PATCH 19/35] Serialize markings too --- .../features/discovery/ecfg/to_petri_net.rs | 30 +++++++-- .../features/discovery/petri_net/marking.rs | 2 +- .../discovery/petri_net/pnml_serialization.rs | 66 ++++++++++++++++--- .../ficus/src/pipelines/discovery_parts.rs | 4 +- 4 files changed, 86 insertions(+), 16 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index ca5a8e2a6..082bac788 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -1,10 +1,25 @@ -use crate::{ +use crate::features::discovery::petri_net::marking::{Marking, SingleMarking}; +use crate::features::mutations::mutations::{ARTIFICIAL_END_EVENT_NAME, ARTIFICIAL_START_EVENT_NAME}; +use crate::{ features::discovery::petri_net::{petri_net::DefaultPetriNet, place::Place, transition::Transition}, utils::graph::graph::DefaultGraph, }; -use std::{collections::HashMap, sync::Arc}; +use std::collections::HashMap; +use std::fmt::{Display, Formatter}; -pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result { +pub enum GraphToPetriNetConversionError { + NodeDataIsEmpty(u64), +} + +impl Display for GraphToPetriNetConversionError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + GraphToPetriNetConversionError::NodeDataIsEmpty(id) => f.write_str(&format!("node {id} does not have data")) + } + } +} + +pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result { if graph.all_nodes().is_empty() { return Ok(Default::default()); } @@ -15,7 +30,7 @@ pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result Result &Vec { + pub fn active_places(&self) -> &[SingleMarking] { &self.active_places } } diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs index a65b1f5bc..2fda5be8e 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs @@ -2,7 +2,6 @@ use crate::{ features::discovery::petri_net::{arc::PetriNetArc, petri_net::PetriNet, place::Place, transition::Transition}, utils::xml_utils::{StartEndElementCookie, XmlWriteError}, }; -use log::trace; use quick_xml::{ Writer, events::{BytesText, Event}, @@ -25,6 +24,11 @@ const VERSION_VALUE: &str = "6.4"; const ACTIVITY_ATTR_NAME: &str = "activity"; const SILENT_ACTIVITY: &str = "$invisible$"; +const INITIAL_MARKING_TAG: &str = "initialMarking"; +const FINAL_MARKINGS_TAG: &str = "finalmarkings"; +const MARKING_TAG: &str = "marking"; +const ID_REF_ATTR: &str = "idref"; + const ID_ATTR_NAME: &str = "id"; const SOURCE_ATTR_NAME: &str = "source"; const TARGET_ATTR_NAME: &str = "target"; @@ -61,6 +65,7 @@ where write_places(net, &writer, use_names_as_ids)?; write_transitions(net, &writer, use_names_as_ids)?; write_arcs(net, &writer, use_names_as_ids)?; + write_final_markings(net, &writer)?; drop(net_cookie); drop(pnml_cookie); @@ -84,16 +89,65 @@ where places.sort_by(|left, right| left.name().cmp(right.name())); for place in places { - let _ = StartEndElementCookie::new_with_attrs( + let cookie = StartEndElementCookie::new_with_attrs( writer, PLACE_TAG_NAME, &vec![(ID_ATTR_NAME, create_place_id(place, use_names_as_ids).as_str())], )?; + + let marking = net.initial_marking().and_then(|m| m.active_places().iter().find(|m| m.place_id() == place.id())); + if let Some(m) = marking { + let i_m_cookie = StartEndElementCookie::new(writer, INITIAL_MARKING_TAG)?; + let count_cookie = StartEndElementCookie::new(writer, TEXT_TAG_NAME)?; + + write_text(writer, &m.tokens_count().to_string())?; + + drop(count_cookie); + drop(i_m_cookie); + } + + drop(cookie); + } + + Ok(()) +} + +fn write_final_markings( + net: &PetriNet, + writer: &RefCell>>>, +) -> Result<(), XmlWriteError> { + let Some(marking) = net.final_marking() else { return Ok(()); }; + + let f_m_cookie = StartEndElementCookie::new(writer, FINAL_MARKINGS_TAG)?; + for m in marking.active_places() { + let m_cookie = StartEndElementCookie::new(writer, MARKING_TAG)?; + let p_cookie = StartEndElementCookie::new_with_attrs( + writer, + PLACE_TAG_NAME, + &vec![(ID_REF_ATTR, &m.place_id().to_string())], + )?; + + let t_cookie = StartEndElementCookie::new(writer, TEXT_TAG_NAME); + + write_text(writer, &m.tokens_count().to_string())?; + + drop(t_cookie); + drop(p_cookie); + drop(m_cookie); } + drop(f_m_cookie); + Ok(()) } +fn write_text(writer: &RefCell>>>, text: &str) -> Result<(), XmlWriteError> { + writer + .borrow_mut() + .write_event(Event::Text(BytesText::new(text))) + .map_err(|e| XmlWriteError::WriterError(quick_xml::Error::Io(std::sync::Arc::new(e)))) +} + fn write_transitions( net: &PetriNet, writer: &RefCell>>>, @@ -113,13 +167,7 @@ where let name = StartEndElementCookie::new(writer, NAME_TAG_NAME); let text = StartEndElementCookie::new(writer, TEXT_TAG_NAME); - match writer - .borrow_mut() - .write_event(Event::Text(BytesText::new(data.to_string().as_str()))) - { - Ok(()) => {} - Err(error) => return Err(XmlWriteError::WriterError(quick_xml::Error::Io(std::sync::Arc::new(error)))), - }; + write_text(writer, &data.to_string())?; drop(text); drop(name); diff --git a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs index 3639ba606..1c704819b 100644 --- a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs @@ -214,8 +214,8 @@ impl PipelineParts { pipeline_part!(convert_ecfg_to_petri_net, |context: &mut PipelineContext, _, _| { let graph = Self::get_user_data(context, &GRAPH_KEY)?; - let petri_net = - convert_ecfg_to_petri_net(graph).map_err(|err| PipelinePartExecutionError::new_raw("failed to convert ECFG to Petri net"))?; + let petri_net = convert_ecfg_to_petri_net(graph); + let petri_net = petri_net.map_err(|err| PipelinePartExecutionError::new_raw(err.to_string()))?; context.put_concrete(PETRI_NET_KEY.key(), petri_net); Ok(()) From 6f4093f63c17316a328733e03e73d6931a3a746b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 5 Sep 2026 14:47:36 +0300 Subject: [PATCH 20/35] Make date optional for xes logs --- .../ficus/src/event_log/xes/reader/xes_log_trace_reader.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ficus/src/rust/ficus/src/event_log/xes/reader/xes_log_trace_reader.rs b/Ficus/src/rust/ficus/src/event_log/xes/reader/xes_log_trace_reader.rs index ec3790d88..5bb8b6cf5 100644 --- a/Ficus/src/rust/ficus/src/event_log/xes/reader/xes_log_trace_reader.rs +++ b/Ficus/src/rust/ficus/src/event_log/xes/reader/xes_log_trace_reader.rs @@ -63,10 +63,10 @@ impl<'a> TraceXesEventLogIterator<'a> { match self.reader.borrow_mut().read_event_into(&mut self.buffer) { Ok(quick_xml::events::Event::End(end)) => match end.name().0 { EVENT_TAG_NAME => { - name.as_ref()?; - date?; + let name = name?; + let date = date.unwrap_or(DateTime::::MIN_UTC); - let event = XesEventImpl::new_all_fields(name.unwrap(), date.unwrap(), Some(payload)); + let event = XesEventImpl::new_all_fields(name, date, Some(payload)); return Some(event); } _ => continue, From 0372073de84223a386a3e4c125956baba3e993fb Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 5 Sep 2026 14:54:31 +0300 Subject: [PATCH 21/35] Better perf during replay --- .../features/discovery/ecfg/adjustments.rs | 45 +++++--------- .../src/features/discovery/ecfg/context.rs | 8 +++ .../src/features/discovery/ecfg/discovery.rs | 61 ++++++++++++------- .../features/discovery/ecfg/discovery_xes.rs | 8 +-- .../src/features/discovery/ecfg/models.rs | 17 +++--- .../ficus/tests/discovery/ecfg_discovery.rs | 4 +- 6 files changed, 77 insertions(+), 66 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs index faa6a84ab..2c81d6bb3 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs @@ -6,7 +6,7 @@ use crate::{ NODE_CORRESPONDING_TRACE_DATA_KEY, NODE_INNER_GRAPH_KEY, NODE_SOFTWARE_DATA_KEY, NODE_START_END_ACTIVITIES_TIMES_KEY, NODE_START_END_ACTIVITY_TIME_KEY, }, - discovery::{EVENT_UNIQUE_ID_KEY, replay_sequence_with_history}, + discovery::replay_sequence_with_history, models::{ActivityStartEndTimeData, DiscoverECFGError, EventWithUniqueId, NodeAdditionalDataContainer}, }, petri_net::annotations::{PERFORMANCE_ANNOTATION_INFO_KEY, PerformanceAnnotationInfo, PerformanceMap}, @@ -289,8 +289,8 @@ pub fn adjust_connections( for trace in log { for i in 0..trace.len() - 1 { - let first_name = name_extractor(trace[i].event()); - let second_name = name_extractor(trace[i + 1].event()); + let first_name = name_extractor(&trace[i].event); + let second_name = name_extractor(&trace[i + 1].event); *df_relations.entry((Some(first_name), Some(second_name))).or_insert(0) += 1usize; } @@ -313,13 +313,14 @@ pub fn adjust_connections( } pub fn adjust_weights( + context: &mut DiscoveryContext, log: &Vec>>, graph: &mut DefaultGraph, start_node_id: u64, ) -> Result<(), DiscoverECFGError> { let mut edges_weights = HashMap::new(); for trace in log { - let replay_history = replay_sequence_with_history(graph, start_node_id, &trace[1..])?; + let replay_history = replay_sequence_with_history(context, graph, start_node_id, &trace[1..])?; for i in 0..replay_history.len() - 1 { let from_node = replay_history[i]; let to_node = replay_history[i + 1]; @@ -335,44 +336,28 @@ pub fn adjust_weights( Ok(()) } -pub fn find_next_node(graph: &DefaultGraph, current_node: u64, next_event_id: u64) -> Result { - let next_nodes = graph - .outgoing_nodes(¤t_node) - .into_iter() - .filter_map(|n| { - match graph - .node(n) - .unwrap() - .user_data() - .get(EVENT_UNIQUE_ID_KEY.key()) - .unwrap_or(&vec![]) - .contains(&next_event_id) - { - true => Some(*n), - false => None, - } - }) - .collect::>(); - - if next_nodes.len() != 1 { - Err(DiscoverECFGError::NotSingleCandidateForNextNode) - } else { - Ok(*next_nodes.first().unwrap()) +pub fn find_next_node(event_ids_to_node_ids: &HashMap, graph: &DefaultGraph, current_node: u64, next_event_id: u64) -> Result { + let next_node_id = event_ids_to_node_ids.get(&next_event_id).copied().ok_or(DiscoverECFGError::EventIdIsNotAssignedToNode)?; + + if !graph.are_nodes_connected(¤t_node, &next_node_id) { + return Err(DiscoverECFGError::NodesAreNotConnectedDuringReplay); } + + Ok(next_node_id) } pub fn adjust_edges_data( - context: &DiscoveryContext, + context: &mut DiscoveryContext, log: &Vec>>, graph: &mut DefaultGraph, start_node_id: u64, ) -> Result<(), DiscoverECFGError> { for trace in log { - let replay_history = replay_sequence_with_history(graph, start_node_id, &trace[1..])?; + let replay_history = replay_sequence_with_history(context, graph, start_node_id, &trace[1..])?; for i in 0..replay_history.len() - 1 { let edge = graph.edge_mut(&replay_history[i], &replay_history[i + 1]).unwrap(); - context.event_to_edge_data_transfer()(trace[i].event(), edge.user_data_mut()) + context.event_to_edge_data_transfer()(&trace[i].event, edge.user_data_mut()) } } diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs index 3bfe2408e..11d31c4c4 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs @@ -1,4 +1,6 @@ use crate::{features::discovery::ecfg::models::RootSequenceKind, utils::user_data::user_data::UserDataImpl}; +use std::cell::RefCell; +use std::collections::HashMap; use std::sync::Arc; type NameExtractor<'a, T> = &'a dyn Fn(&T) -> Arc; @@ -12,6 +14,7 @@ pub struct DiscoveryContext<'a, T> { root_sequence_kind: RootSequenceKind, event_to_node_info_transfer: NodeDataTransfer<'a, T>, event_to_edge_data_transfer: EdgeDataTransfer<'a, T>, + pub(crate) event_ids_to_node_ids: HashMap } impl<'a, T> DiscoveryContext<'a, T> { @@ -28,21 +31,26 @@ impl<'a, T> DiscoveryContext<'a, T> { root_sequence_kind, event_to_node_info_transfer, event_to_edge_data_transfer, + event_ids_to_node_ids: Default::default(), } } pub fn name_extractor(&self) -> NameExtractor<'a, T> { self.name_extractor } + pub fn artificial_start_end_events_factory(&self) -> ArtificialStartEnd<'a, T> { self.artificial_start_end_events_factory } + pub fn root_sequence_kind(&self) -> RootSequenceKind { self.root_sequence_kind } + pub fn event_to_graph_node_info_transfer(&self) -> NodeDataTransfer<'a, T> { self.event_to_node_info_transfer } + pub fn event_to_edge_data_transfer(&self) -> EdgeDataTransfer<'a, T> { self.event_to_edge_data_transfer } diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs index 85c2979aa..e51c7650f 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs @@ -68,7 +68,7 @@ impl ECFGDiscoveryResult { pub fn discover_ecfg( log: &Vec>>, - context: &DiscoveryContext, + context: &mut DiscoveryContext, merge_sequences_of_events: bool, performance_map: Option, ) -> Result { @@ -85,7 +85,7 @@ pub fn discover_ecfg( adjust_connections(context, log, &mut result.graph); if let Some(start_node_id) = result.start_node_id { - adjust_weights(log, &mut result.graph, start_node_id)?; + adjust_weights(context, log, &mut result.graph, start_node_id)?; adjust_edges_data(context, log, &mut result.graph, start_node_id)?; } @@ -108,7 +108,7 @@ fn add_start_end_nodes_ids_to_user_data(result: &mut ECFGDiscoveryResult) { fn discover_ecfg_internal( log: &Vec>>, - context: &DiscoveryContext, + context: &mut DiscoveryContext, first_iteration: bool, ) -> Result { let root_sequence = discover_root_sequence(log, context.root_sequence_kind()); @@ -132,7 +132,7 @@ fn discover_ecfg_internal( fn handle_recursion_exit_case( log: &Vec>>, root_sequence: &[EventWithUniqueId], - context: &DiscoveryContext, + context: &mut DiscoveryContext, ) -> ECFGDiscoveryResult { let mut graph = DefaultGraph::default(); @@ -140,8 +140,8 @@ fn handle_recursion_exit_case( let end_node = create_new_graph_node(&mut graph, root_sequence.last().unwrap(), false, context, false); for trace in log { - transfer_unique_event_id(graph.node_mut(&start_node).unwrap(), trace.first().unwrap()); - transfer_unique_event_id(graph.node_mut(&end_node).unwrap(), trace.last().unwrap()); + transfer_unique_event_id(graph.node_mut(&start_node).unwrap(), trace.first().unwrap(), context); + transfer_unique_event_id(graph.node_mut(&end_node).unwrap(), trace.last().unwrap(), context); } for trace in log { @@ -162,11 +162,12 @@ pub(super) fn create_new_graph_node( graph: &mut DefaultGraph, event: &EventWithUniqueId, is_root_sequence: bool, - context: &DiscoveryContext, + context: &mut DiscoveryContext, transfer_context_values: bool, ) -> u64 { let name_extractor = context.name_extractor(); - let node_id = graph.add_node(Some(name_extractor(event.event()))); + let node_id = graph.add_node(Some(name_extractor(&event.event))); + context.event_ids_to_node_ids.insert(event.id, node_id); if transfer_context_values { transfer_user_data(graph, event, node_id, is_root_sequence, context); @@ -180,20 +181,26 @@ fn transfer_user_data( event: &EventWithUniqueId, node_id: u64, is_root_sequence: bool, - context: &DiscoveryContext, + context: &mut DiscoveryContext, ) { let node = graph.node_mut(&node_id).unwrap(); let transfer = context.event_to_graph_node_info_transfer(); - transfer(event.event(), node.user_data_mut(), is_root_sequence); + transfer(&event.event, node.user_data_mut(), is_root_sequence); - transfer_unique_event_id(node, event); + transfer_unique_event_id(node, event, context); } -fn transfer_unique_event_id(node: &mut GraphNode>, event: &EventWithUniqueId) { +fn transfer_unique_event_id( + node: &mut GraphNode>, + event: &EventWithUniqueId, + context: &mut DiscoveryContext, +) { + context.event_ids_to_node_ids.insert(event.id, node.id); + if let Some(node_ids) = node.user_data_mut().concrete_mut(EVENT_UNIQUE_ID_KEY.key()) { - node_ids.push(*event.id()); + node_ids.push(event.id); } else { - node.user_data_mut().put_concrete(EVENT_UNIQUE_ID_KEY.key(), vec![*event.id()]); + node.user_data_mut().put_concrete(EVENT_UNIQUE_ID_KEY.key(), vec![event.id]); } } @@ -201,7 +208,7 @@ fn initialize_lcs_graph_with_root_sequence( log: &Vec>>, root_sequence: &Vec>, graph: &mut DefaultGraph, - context: &DiscoveryContext, + context: &mut DiscoveryContext, is_first_iteration_root_sequence: bool, ) -> Vec { let mut prev_node_id = None; @@ -240,7 +247,7 @@ fn adjust_lcs_graph_with_traces( root_sequence: &Vec>, root_sequence_node_ids: &[u64], graph: &mut DefaultGraph, - context: &DiscoveryContext, + context: &mut DiscoveryContext, ) -> Result<(), DiscoverECFGError> { let mut adjustments = HashMap::new(); for trace in traces { @@ -303,7 +310,7 @@ fn adjust_lcs_graph_with_traces( fn add_adjustments_to_graph( adjustments: &[(u64, Vec<(u64, Vec>>)>)], graph: &mut DefaultGraph, - context: &DiscoveryContext, + context: &mut DiscoveryContext, ) -> Result<(), DiscoverECFGError> { for (start_root_node_id, adjustments) in adjustments { let adjustment_log = create_log_from_adjustments(adjustments, context.artificial_start_end_events_factory()); @@ -370,7 +377,7 @@ fn merge_subgraph_into_model( graph: &mut DefaultGraph, sub_graph: DefaultGraph, start_graph_node_id: u64, - context: &DiscoveryContext, + context: &mut DiscoveryContext, ) -> Result<(), DiscoverECFGError> { let (start_node_id, end_node_id) = find_start_end_node_ids(&sub_graph, context.name_extractor(), context.artificial_start_end_events_factory()); @@ -378,10 +385,18 @@ fn merge_subgraph_into_model( for node in sub_graph.all_nodes() { if *node.id() != start_node_id && *node.id() != end_node_id { + let new_node = graph.add_node_with_user_data(node.data.clone(), node.user_data().clone()); + sub_graph_nodes_to_nodes.insert( *node.id(), - graph.add_node_with_user_data(node.data.clone(), node.user_data().clone()), + new_node, ); + + if let Some(evt_ids) = graph.node(&new_node).unwrap().user_data().concrete(EVENT_UNIQUE_ID_KEY.key()) { + for event_id in evt_ids { + context.event_ids_to_node_ids.insert(*event_id, new_node); + } + } } } @@ -403,7 +418,7 @@ fn merge_subgraph_into_model( for (end_node_id, log) in adjustments { for trace in log { - let final_node = replay_sequence(graph, start_graph_node_id, trace.as_slice())?; + let final_node = replay_sequence(graph, &context.event_ids_to_node_ids, start_graph_node_id, trace.as_slice())?; graph.connect_nodes(&final_node, end_node_id, NodesConnectionData::default()); } } @@ -413,6 +428,7 @@ fn merge_subgraph_into_model( fn replay_sequence( graph: &DefaultGraph, + event_ids_to_node_ids: &HashMap, start_node_id: u64, sequence: &[EventWithUniqueId], ) -> Result { @@ -428,7 +444,7 @@ fn replay_sequence( return Ok(current_node_id); } - let next_node = find_next_node(graph, current_node_id, *sequence[event_index].id())?; + let next_node = find_next_node(event_ids_to_node_ids, graph, current_node_id, sequence[event_index].id)?; replay_states.push_back((next_node, event_index + 1)); } } @@ -445,6 +461,7 @@ impl ReplayHistoryEntry { } pub(super) fn replay_sequence_with_history( + context: &mut DiscoveryContext, graph: &DefaultGraph, start_node_id: u64, sequence: &[EventWithUniqueId], @@ -475,7 +492,7 @@ pub(super) fn replay_sequence_with_history( return Ok(history); } - let next_node = find_next_node(graph, current_node_id, *sequence[event_index].id())?; + let next_node = find_next_node(&context.event_ids_to_node_ids, graph, current_node_id, sequence[event_index].id)?; replay_history.push(ReplayHistoryEntry::new(next_node, Some(history_end_index))); replay_states.push_back((next_node, event_index + 1, replay_history.len() - 1)); diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery_xes.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery_xes.rs index ab771c812..1a043f95f 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery_xes.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery_xes.rs @@ -64,7 +64,7 @@ pub fn discover_ecfg_from_event_log( transfer_data_from_event_to_edge_user_data(event, user_data_impl); }; - let context = DiscoveryContext::new( + let mut context = DiscoveryContext::new( &name_extractor, &artificial_start_end_events_factory, root_sequence_kind, @@ -82,8 +82,8 @@ pub fn discover_ecfg_from_event_log( .map(|t| t.into_iter().map(EventWithUniqueId::new).collect()) .collect(); - let mut result = discover_ecfg(&log, &context, merge_sequences_of_events, Some(performance_map))?; - discover_graphs_for_patterns(result.graph_mut(), &context); + let mut result = discover_ecfg(&log, &mut context, merge_sequences_of_events, Some(performance_map))?; + discover_graphs_for_patterns(result.graph_mut(), &mut context); Ok(result.graph_move()) } @@ -152,7 +152,7 @@ fn initialize_patterns_infos(log: &Vec>>>) { } } -fn discover_graphs_for_patterns(graph: &mut DefaultGraph, context: &DiscoveryContext>>) { +fn discover_graphs_for_patterns(graph: &mut DefaultGraph, context: &mut DiscoveryContext>>) { for node in graph.all_nodes_mut() { let user_data = node.user_data_mut(); diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs index f75792556..0f1f9ab3c 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs @@ -5,12 +5,14 @@ use std::{ str::FromStr, sync::atomic::{AtomicU64, Ordering}, }; +use std::fmt::Write; #[derive(Debug)] pub enum DiscoverECFGError { NoArtificialStartEndEvents, FailedToReplaySequence, - NotSingleCandidateForNextNode, + EventIdIsNotAssignedToNode, + NodesAreNotConnectedDuringReplay } #[derive(Clone, Copy)] @@ -40,7 +42,8 @@ impl Display for DiscoverECFGError { match self { DiscoverECFGError::NoArtificialStartEndEvents => f.write_str("All traces in event log must have artificial start-end events"), DiscoverECFGError::FailedToReplaySequence => f.write_str("Failed to replay sequence of events on part of a graph"), - DiscoverECFGError::NotSingleCandidateForNextNode => f.write_str("There were several or zero candidates for next node during replay"), + DiscoverECFGError::NodesAreNotConnectedDuringReplay => f.write_str("Two nodes that should be connected are not connected"), + DiscoverECFGError::EventIdIsNotAssignedToNode => f.write_str("Some event id was not assigned to node id"), } } } @@ -123,12 +126,10 @@ impl CorrespondingTraceData { } } -#[derive(Clone, Debug, Getters)] +#[derive(Clone, Debug)] pub struct EventWithUniqueId { - #[getset(get = "pub")] - event: T, - #[getset(get = "pub")] - id: u64, + pub(crate) event: T, + pub(crate) id: u64, } impl EventWithUniqueId { @@ -144,7 +145,7 @@ impl EventWithUniqueId { impl PartialEq for EventWithUniqueId { fn eq(&self, other: &Self) -> bool { - self.event().eq(other.event()) + self.event.eq(&other.event) } } diff --git a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs index 4462578a3..85e23620e 100644 --- a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs +++ b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs @@ -532,7 +532,7 @@ fn discover_ecfg_internal(mut traces: Vec>, gold_root_sequence: Opti let factory = || (START.to_string(), END.to_string()); - let context = DiscoveryContext::new( + let mut context = DiscoveryContext::new( &name_extractor, &factory, root_sequence_kind, @@ -545,5 +545,5 @@ fn discover_ecfg_internal(mut traces: Vec>, gold_root_sequence: Opti .map(|t| t.into_iter().map(|e| EventWithUniqueId::new(e)).collect()) .collect(); - discover_ecfg(&traces, &context, false, None).ok().unwrap().graph_move() + discover_ecfg(&traces, &mut context, false, None).ok().unwrap().graph_move() } From 27dc7f03a1f8df3d29041ccbf3fe3f0246636ede Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 5 Sep 2026 18:26:54 +0300 Subject: [PATCH 22/35] Use slices --- .../rust/ficus/src/utils/distance/distance.rs | 30 +++++++++---------- Ficus/src/rust/ficus/src/utils/lcs.rs | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Ficus/src/rust/ficus/src/utils/distance/distance.rs b/Ficus/src/rust/ficus/src/utils/distance/distance.rs index bfab9002f..f9337c84f 100644 --- a/Ficus/src/rust/ficus/src/utils/distance/distance.rs +++ b/Ficus/src/rust/ficus/src/utils/distance/distance.rs @@ -103,11 +103,11 @@ pub struct LevenshteinDistance; impl Distance for LevenshteinDistance { fn distance(&self, a: ArrayView, b: ArrayView) -> f64 { - let a_vec = a.iter().copied().collect::>(); - let b_vec = b.iter().copied().collect::>(); + let a = a.as_slice().unwrap(); + let b = b.as_slice().unwrap(); - let a_len = Self::get_levenshtein_matrix_dimension_length(&a_vec); - let b_len = Self::get_levenshtein_matrix_dimension_length(&b_vec); + let a_len = Self::get_levenshtein_matrix_dimension_length(a); + let b_len = Self::get_levenshtein_matrix_dimension_length(b); let mut matrix = vec![vec![0f64]]; for i in 0..a_len { @@ -120,7 +120,7 @@ impl Distance for LevenshteinDistance { for j in 1..b_len { for i in 1..a_len { - let number = if a_vec.get(i - 1).unwrap() == b_vec.get(j - 1).unwrap() { + let number = if a[i - 1] == b[j - 1] { matrix[j - 1][i - 1] } else { matrix[j - 1][i].min(matrix[j][i - 1]).min(matrix[j - 1][i - 1]) + 1.0 @@ -135,12 +135,12 @@ impl Distance for LevenshteinDistance { } impl LevenshteinDistance { - fn get_levenshtein_matrix_dimension_length(vec: &Vec) -> usize { - find_first_zero_index(vec) + 2 + fn get_levenshtein_matrix_dimension_length(slice: &[f64]) -> usize { + find_first_zero_index(slice) + 2 } } -fn find_first_zero_index(vec: &Vec) -> usize { +fn find_first_zero_index(vec: &[f64]) -> usize { vec.iter().position(|x| x.is_zero()).unwrap_or(vec.len() - 1) } @@ -149,8 +149,8 @@ pub struct LengthDistance; impl Distance for LengthDistance { fn distance(&self, a: ArrayView, b: ArrayView) -> f64 { - let a_len = find_first_zero_index(&a.into_iter().copied().collect()); - let b_len = find_first_zero_index(&b.into_iter().copied().collect()); + let a_len = find_first_zero_index(a.as_slice().unwrap()); + let b_len = find_first_zero_index(b.as_slice().unwrap()); (a_len.max(b_len) - a_len.min(b_len)) as f64 } @@ -161,13 +161,13 @@ pub struct LCSDistance; impl Distance for LCSDistance { fn distance(&self, a: ArrayView, b: ArrayView) -> f64 { - let a_vec = a.iter().copied().collect::>(); - let b_vec = b.iter().copied().collect::>(); + let a = a.as_slice().unwrap(); + let b = b.as_slice().unwrap(); - let a_len = find_first_zero_index(&a_vec) + 1; - let b_len = find_first_zero_index(&b_vec) + 1; + let a_len = find_first_zero_index(a) + 1; + let b_len = find_first_zero_index(b) + 1; - let lcs = find_longest_common_subsequence_length(&a_vec, &b_vec, a_len, b_len); + let lcs = find_longest_common_subsequence_length(a, b, a_len, b_len); calculate_lcs_distance(lcs, a_len, b_len) } diff --git a/Ficus/src/rust/ficus/src/utils/lcs.rs b/Ficus/src/rust/ficus/src/utils/lcs.rs index e3abbfbd5..30d1b2088 100644 --- a/Ficus/src/rust/ficus/src/utils/lcs.rs +++ b/Ficus/src/rust/ficus/src/utils/lcs.rs @@ -1,6 +1,6 @@ use std::cmp::max; -pub fn find_longest_common_subsequence_length(first: &Vec, second: &Vec, first_len: usize, second_len: usize) -> usize { +pub fn find_longest_common_subsequence_length(first: &[T], second: &[T], first_len: usize, second_len: usize) -> usize { build_longest_common_subsequence_matrix(first, second, first_len, second_len)[first_len][second_len] as usize } From 2a3a6bb636aae8f48c12a1c91a760699eb37640e Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 5 Sep 2026 20:57:24 +0300 Subject: [PATCH 23/35] Add bytes serialization to PNML --- .../python/ficus/grpc_pipelines/constants.py | 1 + .../ficus/grpc_pipelines/discovery_parts.py | 22 +++++++++++++ .../features/discovery/ecfg/adjustments.rs | 12 +++++-- .../src/features/discovery/ecfg/context.rs | 6 ++-- .../src/features/discovery/ecfg/discovery.rs | 5 +-- .../src/features/discovery/ecfg/models.rs | 5 ++- .../features/discovery/ecfg/to_petri_net.rs | 25 ++++++++++----- .../discovery/petri_net/pnml_serialization.rs | 14 ++++----- .../ficus/src/pipelines/discovery_parts.rs | 31 +++++++++++++++---- .../rust/ficus/src/pipelines/parts_names.rs | 1 + .../ficus/src/pipelines/pipeline_parts.rs | 1 + .../tests/pipelines/pipeline_parts_tests.rs | 3 +- 12 files changed, 92 insertions(+), 34 deletions(-) diff --git a/Ficus/src/python/ficus/grpc_pipelines/constants.py b/Ficus/src/python/ficus/grpc_pipelines/constants.py index 02d72fab6..2843b1fcd 100644 --- a/Ficus/src/python/ficus/grpc_pipelines/constants.py +++ b/Ficus/src/python/ficus/grpc_pipelines/constants.py @@ -153,6 +153,7 @@ const_discover_loops_strict = 'DiscoverLoopsStrict' const_discover_traces_timeline_diagram = 'DiscoverTracesTimelineDiagram' const_convert_ecfg_to_petri_net = 'ConvertECFGToPetriNet' +const_serialize_petri_net_bytes = 'SerializePetriNetBytes' const_prepare_software_log = 'PrepareSoftwareEventLog' const_shorten_allocation_type = 'ShortenAllocationType' diff --git a/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py b/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py index 81efa96bf..0f1c1c4b2 100644 --- a/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py +++ b/Ficus/src/python/ficus/grpc_pipelines/discovery_parts.py @@ -266,3 +266,25 @@ def to_grpc_part(self) -> GrpcPipelinePartBase: class ConvertECFGToPetriNet(PipelinePart): def to_grpc_part(self) -> GrpcPipelinePartBase: return GrpcPipelinePartBase(defaultPart=create_default_pipeline_part(const_convert_ecfg_to_petri_net)) + +class SerializePetriNetPnmlBytes(PipelinePartWithCallback): + def __init__(self, save_path: str, use_names_as_ids:bool = False): + super().__init__() + self.save_path = save_path + self.use_names_as_ids = use_names_as_ids + + def to_grpc_part(self) -> GrpcPipelinePartBase: + config = GrpcPipelinePartConfiguration() + append_bool_value(config, const_pnml_use_names_as_ids, self.use_names_as_ids) + + part = create_complex_get_context_part(self.uuid, + self.__class__.__name__, + [const_bytes], + const_serialize_petri_net_bytes, + config) + + return GrpcPipelinePartBase(complexContextRequestPart=part) + + def execute_callback(self, values: dict[str, GrpcContextValue]): + file_bytes = from_grpc_bytes(values[const_bytes].bytes) + write_file_bytes(self.save_path, file_bytes.bytes) diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs index 2c81d6bb3..325fb5426 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/adjustments.rs @@ -336,8 +336,16 @@ pub fn adjust_weights( Ok(()) } -pub fn find_next_node(event_ids_to_node_ids: &HashMap, graph: &DefaultGraph, current_node: u64, next_event_id: u64) -> Result { - let next_node_id = event_ids_to_node_ids.get(&next_event_id).copied().ok_or(DiscoverECFGError::EventIdIsNotAssignedToNode)?; +pub fn find_next_node( + event_ids_to_node_ids: &HashMap, + graph: &DefaultGraph, + current_node: u64, + next_event_id: u64, +) -> Result { + let next_node_id = event_ids_to_node_ids + .get(&next_event_id) + .copied() + .ok_or(DiscoverECFGError::EventIdIsNotAssignedToNode)?; if !graph.are_nodes_connected(¤t_node, &next_node_id) { return Err(DiscoverECFGError::NodesAreNotConnectedDuringReplay); diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs index 11d31c4c4..155ab336e 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/context.rs @@ -1,7 +1,5 @@ use crate::{features::discovery::ecfg::models::RootSequenceKind, utils::user_data::user_data::UserDataImpl}; -use std::cell::RefCell; -use std::collections::HashMap; -use std::sync::Arc; +use std::{cell::RefCell, collections::HashMap, sync::Arc}; type NameExtractor<'a, T> = &'a dyn Fn(&T) -> Arc; type ArtificialStartEnd<'a, T> = &'a dyn Fn() -> (T, T); @@ -14,7 +12,7 @@ pub struct DiscoveryContext<'a, T> { root_sequence_kind: RootSequenceKind, event_to_node_info_transfer: NodeDataTransfer<'a, T>, event_to_edge_data_transfer: EdgeDataTransfer<'a, T>, - pub(crate) event_ids_to_node_ids: HashMap + pub(crate) event_ids_to_node_ids: HashMap, } impl<'a, T> DiscoveryContext<'a, T> { diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs index e51c7650f..5dfab3412 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/discovery.rs @@ -387,10 +387,7 @@ fn merge_subgraph_into_model( if *node.id() != start_node_id && *node.id() != end_node_id { let new_node = graph.add_node_with_user_data(node.data.clone(), node.user_data().clone()); - sub_graph_nodes_to_nodes.insert( - *node.id(), - new_node, - ); + sub_graph_nodes_to_nodes.insert(*node.id(), new_node); if let Some(evt_ids) = graph.node(&new_node).unwrap().user_data().concrete(EVENT_UNIQUE_ID_KEY.key()) { for event_id in evt_ids { diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs index 0f1f9ab3c..1a20dfc42 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/models.rs @@ -1,18 +1,17 @@ use derive_new::new; use getset::Getters; use std::{ - fmt::{Debug, Display, Formatter}, + fmt::{Debug, Display, Formatter, Write}, str::FromStr, sync::atomic::{AtomicU64, Ordering}, }; -use std::fmt::Write; #[derive(Debug)] pub enum DiscoverECFGError { NoArtificialStartEndEvents, FailedToReplaySequence, EventIdIsNotAssignedToNode, - NodesAreNotConnectedDuringReplay + NodesAreNotConnectedDuringReplay, } #[derive(Clone, Copy)] diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index 082bac788..92af9c471 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -1,11 +1,19 @@ -use crate::features::discovery::petri_net::marking::{Marking, SingleMarking}; -use crate::features::mutations::mutations::{ARTIFICIAL_END_EVENT_NAME, ARTIFICIAL_START_EVENT_NAME}; use crate::{ - features::discovery::petri_net::{petri_net::DefaultPetriNet, place::Place, transition::Transition}, + features::{ + discovery::petri_net::{ + marking::{Marking, SingleMarking}, + petri_net::DefaultPetriNet, + place::Place, + transition::Transition, + }, + mutations::mutations::{ARTIFICIAL_END_EVENT_NAME, ARTIFICIAL_START_EVENT_NAME}, + }, utils::graph::graph::DefaultGraph, }; -use std::collections::HashMap; -use std::fmt::{Display, Formatter}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter}, +}; pub enum GraphToPetriNetConversionError { NodeDataIsEmpty(u64), @@ -14,7 +22,7 @@ pub enum GraphToPetriNetConversionError { impl Display for GraphToPetriNetConversionError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - GraphToPetriNetConversionError::NodeDataIsEmpty(id) => f.write_str(&format!("node {id} does not have data")) + GraphToPetriNetConversionError::NodeDataIsEmpty(id) => f.write_str(&format!("node {id} does not have data")), } } } @@ -30,7 +38,10 @@ pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result( net: &PetriNet, writer: &RefCell>>>, ) -> Result<(), XmlWriteError> { - let Some(marking) = net.final_marking() else { return Ok(()); }; + let Some(marking) = net.final_marking() else { + return Ok(()); + }; let f_m_cookie = StartEndElementCookie::new(writer, FINAL_MARKINGS_TAG)?; for m in marking.active_places() { let m_cookie = StartEndElementCookie::new(writer, MARKING_TAG)?; - let p_cookie = StartEndElementCookie::new_with_attrs( - writer, - PLACE_TAG_NAME, - &vec![(ID_REF_ATTR, &m.place_id().to_string())], - )?; + let p_cookie = StartEndElementCookie::new_with_attrs(writer, PLACE_TAG_NAME, &vec![(ID_REF_ATTR, &m.place_id().to_string())])?; let t_cookie = StartEndElementCookie::new(writer, TEXT_TAG_NAME); diff --git a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs index 1c704819b..52a2368a4 100644 --- a/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/discovery_parts.rs @@ -13,7 +13,10 @@ use crate::{ ecfg::{discovery_xes::discover_ecfg_from_event_log, to_petri_net::convert_ecfg_to_petri_net}, fuzzy::fuzzy_miner::discover_graph_fuzzy, heuristic::heuristic_miner::discover_petri_net_heuristic, - petri_net::{marking::ensure_initial_marking, pnml_serialization::serialize_to_pnml_file}, + petri_net::{ + marking::ensure_initial_marking, + pnml_serialization::{serialize_to_pnml, serialize_to_pnml_file}, + }, relations::triangle_relation::OfflineTriangleRelation, }, }, @@ -22,11 +25,11 @@ use crate::{ context::PipelineContext, errors::pipeline_errors::{PipelinePartExecutionError, RawPartExecutionError}, keys::context_keys::{ - AND_THRESHOLD_KEY, ATTRIBUTE_KEY, BINARY_FREQUENCY_SIGNIFICANCE_THRESHOLD_KEY, DEPENDENCY_THRESHOLD_KEY, EDGE_CUTOFF_THRESHOLD_KEY, - EVENT_LOG_INFO_KEY, EVENT_LOG_KEY, GRAPH_KEY, LOOP_LENGTH_TWO_THRESHOLD_KEY, MERGE_SEQUENCES_OF_EVENTS_KEY, - NODE_CUTOFF_THRESHOLD_KEY, PATH_KEY, PETRI_NET_KEY, PNML_USE_NAMES_AS_IDS_KEY, POSITIVE_OBSERVATIONS_THRESHOLD_KEY, - PRESERVE_THRESHOLD_KEY, RATIO_THRESHOLD_KEY, RELATIVE_TO_BEST_THRESHOLD_KEY, ROOT_SEQUENCE_KIND_KEY, THREAD_ATTRIBUTE_KEY, - UNARY_FREQUENCY_THRESHOLD_KEY, UTILITY_RATE_KEY, + AND_THRESHOLD_KEY, ATTRIBUTE_KEY, BINARY_FREQUENCY_SIGNIFICANCE_THRESHOLD_KEY, BYTES_KEY, DEPENDENCY_THRESHOLD_KEY, + EDGE_CUTOFF_THRESHOLD_KEY, EVENT_LOG_INFO_KEY, EVENT_LOG_KEY, GRAPH_KEY, LOOP_LENGTH_TWO_THRESHOLD_KEY, + MERGE_SEQUENCES_OF_EVENTS_KEY, NODE_CUTOFF_THRESHOLD_KEY, PATH_KEY, PETRI_NET_KEY, PNML_USE_NAMES_AS_IDS_KEY, + POSITIVE_OBSERVATIONS_THRESHOLD_KEY, PRESERVE_THRESHOLD_KEY, RATIO_THRESHOLD_KEY, RELATIVE_TO_BEST_THRESHOLD_KEY, + ROOT_SEQUENCE_KIND_KEY, THREAD_ATTRIBUTE_KEY, UNARY_FREQUENCY_THRESHOLD_KEY, UTILITY_RATE_KEY, }, pipeline_parts::PipelineParts, pipelines::PipelinePartFactory, @@ -67,6 +70,22 @@ impl PipelineParts { } }); + pipeline_part!( + serialize_petri_net_bytes, + |context: &mut PipelineContext, _, config: &UserDataImpl| { + let petri_net = Self::get_user_data(context, &PETRI_NET_KEY)?; + let use_names_as_ids = *Self::get_user_data(config, &PNML_USE_NAMES_AS_IDS_KEY)?; + + match serialize_to_pnml(petri_net, use_names_as_ids) { + Ok(content) => { + context.put_concrete::>(BYTES_KEY.key(), content.as_bytes().to_vec()); + Ok(()) + } + Err(err) => Err(PipelinePartExecutionError::Raw(RawPartExecutionError::new(err.to_string()))), + } + } + ); + pipeline_part!(discover_petri_net_alpha_plus, |context: &mut PipelineContext, _, _| { Self::do_discover_petri_net_alpha_plus(context, false) }); diff --git a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs index 5dec58941..e62f275b8 100644 --- a/Ficus/src/rust/ficus/src/pipelines/parts_names.rs +++ b/Ficus/src/rust/ficus/src/pipelines/parts_names.rs @@ -59,6 +59,7 @@ impl PipelineParts { pub const DISCOVER_LOOPS_STRICT: &'static str = "DiscoverLoopsStrict"; pub const DISCOVER_TRACES_TIMELINE_DIAGRAM: &'static str = "DiscoverTracesTimelineDiagram"; pub const CONVERT_ECFG_TO_PETRI_NET: &'static str = "ConvertECFGToPetriNet"; + pub const SERIALIZE_PETRI_NET_BYTES: &'static str = "SerializePetriNetBytes"; pub const PREPARE_SOFTWARE_EVENT_LOG: &'static str = "PrepareSoftwareEventLog"; pub const SHORTEN_ALLOCATION_TYPE: &'static str = "ShortenAllocationType"; diff --git a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs index 528228fb9..5d83da686 100644 --- a/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs +++ b/Ficus/src/rust/ficus/src/pipelines/pipeline_parts.rs @@ -150,6 +150,7 @@ impl PipelineParts { Self::terminate_if_empty_log(), Self::create_ocel_annotation_for_dag(), Self::convert_ecfg_to_petri_net(), + Self::serialize_petri_net_bytes(), ]; let mut names_to_parts = HashMap::new(); diff --git a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs index 744644175..71fdb4243 100644 --- a/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs +++ b/Ficus/src/rust/ficus/tests/pipelines/pipeline_parts_tests.rs @@ -92,7 +92,8 @@ fn get_test_parts_names() -> Vec { "ClearGraphs", "TerminateIfEmptyLog", "CreateOcelAnnotationForDag", - "ConvertECFGToPetriNet" + "ConvertECFGToPetriNet", + "SerializePetriNetBytes" ] } From d8e2b89042dd47f8b52894ec583bdd1e82b39570 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sat, 5 Sep 2026 22:18:38 +0300 Subject: [PATCH 24/35] Fixing some tests --- .../discovery/petri_net/pnml_serialization.rs | 7 ++++-- .../ficus/tests/petri_nets/replay_tests.rs | 4 ++-- .../pnml_petri_nets/alpha_simple_test_1.gold | 10 ++++++++ .../pnml_petri_nets/alpha_simple_test_2.gold | 10 ++++++++ .../pnml_petri_nets/alpha_simple_test_3.gold | 10 ++++++++ .../heuristic_miner_test_2.gold | 12 ++++++---- .../heuristic_miner_test_3.gold | 12 ++++++---- .../heuristic_miner_test_4.gold | 12 ++++++---- .../heuristic_miner_test_5.gold | 12 ++++++---- .../heuristic_miner_test_7.gold | 24 +++++++++++-------- .../heuristic_miner_test_8.gold | 12 ++++++---- .../pnml_petri_nets/test_serialization_1.gold | 10 ++++++++ 12 files changed, 96 insertions(+), 39 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs index 7ef0d88e7..817e24a48 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs @@ -65,7 +65,7 @@ where write_places(net, &writer, use_names_as_ids)?; write_transitions(net, &writer, use_names_as_ids)?; write_arcs(net, &writer, use_names_as_ids)?; - write_final_markings(net, &writer)?; + write_final_markings(net, &writer, use_names_as_ids)?; drop(net_cookie); drop(pnml_cookie); @@ -98,6 +98,7 @@ where let marking = net .initial_marking() .and_then(|m| m.active_places().iter().find(|m| m.place_id() == place.id())); + if let Some(m) = marking { let i_m_cookie = StartEndElementCookie::new(writer, INITIAL_MARKING_TAG)?; let count_cookie = StartEndElementCookie::new(writer, TEXT_TAG_NAME)?; @@ -117,6 +118,7 @@ where fn write_final_markings( net: &PetriNet, writer: &RefCell>>>, + use_names_as_ids: bool, ) -> Result<(), XmlWriteError> { let Some(marking) = net.final_marking() else { return Ok(()); @@ -125,7 +127,8 @@ fn write_final_markings( let f_m_cookie = StartEndElementCookie::new(writer, FINAL_MARKINGS_TAG)?; for m in marking.active_places() { let m_cookie = StartEndElementCookie::new(writer, MARKING_TAG)?; - let p_cookie = StartEndElementCookie::new_with_attrs(writer, PLACE_TAG_NAME, &vec![(ID_REF_ATTR, &m.place_id().to_string())])?; + let place_name = create_place_id(net.place(&m.place_id()), use_names_as_ids); + let p_cookie = StartEndElementCookie::new_with_attrs(writer, PLACE_TAG_NAME, &vec![(ID_REF_ATTR, &place_name)])?; let t_cookie = StartEndElementCookie::new(writer, TEXT_TAG_NAME); diff --git a/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs b/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs index 3910318c2..049ac0714 100644 --- a/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs +++ b/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs @@ -39,8 +39,8 @@ pub fn test_silent_transitions_replay() { ensure_initial_marking(&log, &mut petri_net); let expected_transitions = vec![ - Some(vecs!["A", "silent_start_A", "B", "C", "D"]), - Some(vecs!["A", "silent_start_A", "C", "B", "D"]), + Some(vecs!["A", "tau", "B", "C", "D"]), + Some(vecs!["A", "tau", "C", "B", "D"]), ]; execute_test_with_replay(&petri_net, &log, expected_transitions); diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold index f916e4e85..08a094bcb 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold @@ -7,6 +7,9 @@ + + 1 + @@ -35,5 +38,12 @@ + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold index 99485b161..dea296892 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold @@ -5,6 +5,9 @@ + + 1 + @@ -43,5 +46,12 @@ + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold index 99485b161..dea296892 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold @@ -5,6 +5,9 @@ + + 1 + @@ -43,5 +46,12 @@ + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold index 4d64e9fd6..b3ba18619 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold @@ -37,10 +37,12 @@ E - + - silent_start_A + tau + + @@ -72,11 +74,11 @@ - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold index 2237f7725..5bdadd92f 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold @@ -42,10 +42,12 @@ F - + - silent_start_A + tau + + @@ -67,11 +69,11 @@ - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold index da8b6b629..cef05e08b 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold @@ -51,10 +51,12 @@ G - + - silent_start_A + tau + + @@ -84,11 +86,11 @@ - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold index cacf65abc..25093f025 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold @@ -35,10 +35,12 @@ E - + - silent_start_A + tau + + @@ -56,11 +58,11 @@ - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold index 38ac968b0..dac24385b 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold @@ -53,15 +53,19 @@ G - + - silent_start_A + tau + + - + - silent_start_B + tau + + @@ -89,17 +93,17 @@ - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold index dab76bc9c..d609d9fcc 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold @@ -42,10 +42,12 @@ F - + - silent_start_B + tau + + @@ -67,11 +69,11 @@ - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold index 99485b161..dea296892 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold @@ -5,6 +5,9 @@ + + 1 + @@ -43,5 +46,12 @@ + + + + 1 + + + \ No newline at end of file From 61495383b9c7341437ff07762ee6961086100281 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 6 Sep 2026 09:47:01 +0300 Subject: [PATCH 25/35] Fixing petri nets serialization --- .../discovery/petri_net/pnml_serialization.rs | 80 +++++-------------- .../ficus/tests/discovery/ecfg_discovery.rs | 2 +- .../alpha_plus_plus_nfc_test_1.gold | 36 ++++----- .../alpha_plus_plus_nfc_test_2.gold | 20 ++--- .../alpha_plus_plus_nfc_test_3.gold | 24 +++--- .../alpha_plus_plus_nfc_test_4.gold | 32 ++++---- .../alpha_plus_plus_nfc_test_5.gold | 20 ++--- .../alpha_plus_plus_nfc_test_6.gold | 20 ++--- .../alpha_plus_plus_nfc_test_7.gold | 28 +++---- .../alpha_plus_plus_nfc_test_8.gold | 24 +++--- .../alpha_plus_plus_nfc_test_9.gold | 24 +++--- .../pnml_petri_nets/alpha_simple_test_1.gold | 12 +-- .../pnml_petri_nets/alpha_simple_test_2.gold | 12 +-- .../pnml_petri_nets/alpha_simple_test_3.gold | 12 +-- .../heuristic_miner_test_2.gold | 36 ++++----- .../heuristic_miner_test_3.gold | 26 +++--- .../heuristic_miner_test_4.gold | 34 ++++---- .../heuristic_miner_test_5.gold | 22 ++--- .../heuristic_miner_test_6.gold | 14 ++-- .../heuristic_miner_test_7.gold | 38 ++++----- .../heuristic_miner_test_8.gold | 26 +++--- .../heuristic_miner_test_9.gold | 18 ++--- .../pnml_petri_nets/test_serialization_1.gold | 12 +-- 23 files changed, 264 insertions(+), 308 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs index 817e24a48..7e827d87c 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs @@ -214,41 +214,31 @@ fn write_arcs( where TTransitionData: ToString, { - for transition in created_ordered_transitions_list(net) { - write_incoming_arcs(net, transition, writer, use_names_as_ids)?; - write_outgoing_arcs(net, transition, writer, use_names_as_ids)?; + let mut all_arcs = vec![]; + for transition in net.all_transitions() { + all_arcs.extend(patch_arcs_list(transition.outgoing_arcs(), use_names_as_ids, |arc| { + create_arc_name( + create_transition_id(transition, use_names_as_ids), + create_place_id(net.place(&arc.place_id()), use_names_as_ids), + ) + })); + + all_arcs.extend(patch_arcs_list(transition.incoming_arcs(), use_names_as_ids, |arc| { + create_arc_name( + create_place_id(net.place(&arc.place_id()), use_names_as_ids), + create_transition_id(transition, use_names_as_ids), + ) + })); } - Ok(()) -} + all_arcs.sort_by(|(_, n1), (_, n2)| n1.cmp(n2)); -fn write_incoming_arcs( - net: &PetriNet, - transition: &Transition, - writer: &RefCell>>>, - use_names_as_ids: bool, -) -> Result<(), XmlWriteError> -where - TTransitionData: ToString, -{ - let incoming_arcs = patch_arcs_list(transition.incoming_arcs(), use_names_as_ids, |arc| { - create_arc_name( - create_place_id(net.place(&arc.place_id()), use_names_as_ids), - create_transition_id(transition, use_names_as_ids), - ) - }); - - for arc in &incoming_arcs { + for (_, name) in all_arcs { StartEndElementCookie::new_with_attrs( writer, ARC_TAG_NAME, &vec![ - (ID_ATTR_NAME, arc.1.as_str()), - ( - SOURCE_ATTR_NAME, - create_place_id(net.place(&arc.0.place_id()), use_names_as_ids).as_str(), - ), - (TARGET_ATTR_NAME, create_transition_id(transition, use_names_as_ids).as_str()), + (ID_ATTR_NAME, &name), ], )?; } @@ -278,40 +268,6 @@ fn patch_arcs_list( arcs } -fn write_outgoing_arcs( - net: &PetriNet, - transition: &Transition, - writer: &RefCell>>>, - use_names_as_ids: bool, -) -> Result<(), XmlWriteError> -where - TTransitionData: ToString, -{ - let outgoing_arcs = patch_arcs_list(transition.outgoing_arcs(), use_names_as_ids, |arc| { - create_arc_name( - create_transition_id(transition, use_names_as_ids), - create_place_id(net.place(&arc.place_id()), use_names_as_ids), - ) - }); - - for arc in outgoing_arcs { - StartEndElementCookie::new_with_attrs( - writer, - ARC_TAG_NAME, - &vec![ - (ID_ATTR_NAME, arc.1.as_str()), - ( - TARGET_ATTR_NAME, - create_place_id(net.place(&arc.0.place_id()), use_names_as_ids).as_str(), - ), - (SOURCE_ATTR_NAME, create_transition_id(transition, use_names_as_ids).as_str()), - ], - )?; - } - - Ok(()) -} - fn create_place_id(place: &Place, use_names_as_ids: bool) -> String { match use_names_as_ids { true => place.name().to_owned(), diff --git a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs index 85e23620e..e20b87f7a 100644 --- a/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs +++ b/Ficus/src/rust/ficus/tests/discovery/ecfg_discovery.rs @@ -505,7 +505,7 @@ fn execute_to_petri_net_test(test_name: &str, traces: Vec>) { let graph = discover_ecfg_internal(traces, None); let petri_net = convert_ecfg_to_petri_net(&graph).ok().unwrap(); - serialize_to_pnml(&petri_net, false).ok().unwrap() + serialize_to_pnml(&petri_net, true).ok().unwrap() }); } diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_1.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_1.gold index 600f5849e..d3e686729 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_1.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_1.gold @@ -45,41 +45,41 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_2.gold index b9fbd48e8..64eaeb343 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_2.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_2.gold @@ -33,25 +33,25 @@ E - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_3.gold index 91f5a89e0..d26b98422 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_3.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_3.gold @@ -38,29 +38,29 @@ F - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_4.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_4.gold index 84d301363..8144a5cc9 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_4.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_4.gold @@ -47,37 +47,37 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_5.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_5.gold index 9f9fe3fe6..339f68c81 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_5.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_5.gold @@ -33,25 +33,25 @@ E - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_6.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_6.gold index 7a7f11aa2..d24aac3d5 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_6.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_6.gold @@ -33,25 +33,25 @@ E - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_7.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_7.gold index 090a744f1..ddca6c86f 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_7.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_7.gold @@ -47,33 +47,33 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_8.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_8.gold index 9d433a4f5..d837e7d4e 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_8.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_8.gold @@ -40,29 +40,29 @@ F - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_9.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_9.gold index 083943027..45c21508c 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_9.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_plus_plus_nfc_test_9.gold @@ -35,29 +35,29 @@ E - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold index 08a094bcb..d31944727 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_1.gold @@ -26,17 +26,17 @@ C - + - + - + - + - + - + diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold index dea296892..b55752b48 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_2.gold @@ -34,17 +34,17 @@ E - + - + - + - + - + - + diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold index dea296892..b55752b48 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/alpha_simple_test_3.gold @@ -34,17 +34,17 @@ E - + - + - + - + - + - + diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold index b3ba18619..a00fad86c 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_2.gold @@ -44,41 +44,41 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold index 5bdadd92f..245811552 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_3.gold @@ -49,31 +49,31 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold index cef05e08b..cbe3fa6f3 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_4.gold @@ -58,39 +58,39 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold index 25093f025..d42c07420 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_5.gold @@ -42,27 +42,27 @@ - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_6.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_6.gold index fc775f4cf..c24dc829a 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_6.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_6.gold @@ -31,19 +31,19 @@ E - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold index dac24385b..fbcf7eda7 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_7.gold @@ -67,43 +67,43 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold index d609d9fcc..3fe22d70e 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_8.gold @@ -49,31 +49,31 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_9.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_9.gold index 93cc9fd28..4f41960bc 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_9.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/heuristic_miner_test_9.gold @@ -33,23 +33,23 @@ E - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold index dea296892..b55752b48 100644 --- a/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_serialization_1.gold @@ -34,17 +34,17 @@ E - + - + - + - + - + - + From 9498c4064172b182e4e37a75e0e642a28eb2e2e1 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 6 Sep 2026 19:05:39 +0300 Subject: [PATCH 26/35] Fix places names in ECFG to Petri net conversion, update gold --- .../features/discovery/ecfg/to_petri_net.rs | 6 +- .../test_ecfg_to_petri_net_1.gold | 172 ++ .../test_ecfg_to_petri_net_10.gold | 386 ++++ .../test_ecfg_to_petri_net_11.gold | 1730 +++++++++++++++++ .../test_ecfg_to_petri_net_2.gold | 220 +++ .../test_ecfg_to_petri_net_3.gold | 205 ++ .../test_ecfg_to_petri_net_4.gold | 4 + .../test_ecfg_to_petri_net_5.gold | 41 + .../test_ecfg_to_petri_net_6.gold | 408 ++++ .../test_ecfg_to_petri_net_7.gold | 336 ++++ .../test_ecfg_to_petri_net_8.gold | 218 +++ .../test_ecfg_to_petri_net_9.gold | 301 +++ 12 files changed, 4023 insertions(+), 4 deletions(-) create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_1.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_10.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_11.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_2.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_3.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_4.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_5.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_6.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_7.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_8.gold create mode 100644 Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_9.gold diff --git a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs index 92af9c471..9cc336741 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/ecfg/to_petri_net.rs @@ -34,7 +34,6 @@ pub fn convert_ecfg_to_petri_net(graph: &DefaultGraph) -> Result Result Result + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_10.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_10.gold new file mode 100644 index 000000000..ea85718ad --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_10.gold @@ -0,0 +1,386 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + V + + + + + W + + + + + X + + + + + Y + + + + + Z + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + w + + + + + y + + + + + z + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_11.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_11.gold new file mode 100644 index 000000000..ab94c3fc1 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_11.gold @@ -0,0 +1,1730 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + 0 + + + + + 1 + + + + + 10 + + + + + 11 + + + + + 13 + + + + + 14 + + + + + 14 + + + + + 14 + + + + + 14 + + + + + 15 + + + + + 15 + + + + + 16 + + + + + 16 + + + + + 17 + + + + + 18 + + + + + 19 + + + + + 20 + + + + + 20 + + + + + 21 + + + + + 21 + + + + + 22 + + + + + 23 + + + + + 26 + + + + + 27 + + + + + 28 + + + + + 28 + + + + + 28 + + + + + 28 + + + + + 29 + + + + + 29 + + + + + 30 + + + + + 31 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 8 + + + + + 8 + + + + + 9 + + + + + 9 + + + + + END + + + + + Loop[0] + + + + + Loop[15] + + + + + Loop[16] + + + + + Loop[17] + + + + + Loop[23] + + + + + Loop[23] + + + + + Loop[23] + + + + + Loop[24] + + + + + Loop[24] + + + + + Loop[24] + + + + + Loop[24] + + + + + Loop[26] + + + + + Loop[26] + + + + + Loop[27] + + + + + Loop[6] + + + + + Loop[6] + + + + + Loop[7] + + + + + START + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_2.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_2.gold new file mode 100644 index 000000000..fa2c3f385 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_2.gold @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + X + + + + + Y + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_3.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_3.gold new file mode 100644 index 000000000..29550ea5c --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_3.gold @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_4.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_4.gold new file mode 100644 index 000000000..7358fbef2 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_4.gold @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_5.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_5.gold new file mode 100644 index 000000000..b564cf23e --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_5.gold @@ -0,0 +1,41 @@ + + + + + + + + + + + + + END + + + + + START + + + + + tau + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_6.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_6.gold new file mode 100644 index 000000000..4a17ef1a8 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_6.gold @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + W + + + + + W + + + + + X + + + + + X + + + + + Y + + + + + Y + + + + + Z + + + + + Z + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_7.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_7.gold new file mode 100644 index 000000000..01bfe97c1 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_7.gold @@ -0,0 +1,336 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + W + + + + + X + + + + + Y + + + + + Z + + + + + Z + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_8.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_8.gold new file mode 100644 index 000000000..587cd7cf5 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_8.gold @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + X + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_9.gold b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_9.gold new file mode 100644 index 000000000..6644a6757 --- /dev/null +++ b/Ficus/test_data/gold/rust/pnml_petri_nets/test_ecfg_to_petri_net_9.gold @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + D + + + + + E + + + + + END + + + + + START + + + + + W + + + + + X + + + + + Y + + + + + Z + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + tau + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 93308ff53d40cb5a0c1db0b8bfdff0b7ce344f56 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Sun, 6 Sep 2026 20:51:41 +0300 Subject: [PATCH 27/35] Check if there is last group --- .../ficus/src/features/discovery/timeline/events_groups.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/timeline/events_groups.rs b/Ficus/src/rust/ficus/src/features/discovery/timeline/events_groups.rs index e47579e1a..543dabe31 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/timeline/events_groups.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/timeline/events_groups.rs @@ -72,7 +72,9 @@ pub fn discover_events_groups( last_stamp = Some(*event.stamp()); } - add_to_groups(last_trace_group.clone(), last_seen_point); + if last_trace_group.is_some() { + add_to_groups(last_trace_group, last_seen_point); + } groups } From f2e08e27601ca329665407cf7d456264cbc1cbae Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 7 Sep 2026 20:55:01 +0300 Subject: [PATCH 28/35] Fix not sending context values for offline kafka pipelines --- .../src/grpc/context_values_service.rs | 13 +++- .../grpc/events/delegating_events_handler.rs | 5 +- .../src/grpc/events/events_handler.rs | 17 +++- .../src/grpc/events/grpc_events_handler.rs | 39 +++++++++- .../src/grpc/events/kafka_events_handler.rs | 11 ++- .../src/grpc/get_context_pipeline.rs | 18 +++-- .../src/grpc/kafka/grpc_kafka_service.rs | 77 +++++++++++++------ .../src/grpc/kafka/kafka_service.rs | 15 +++- 8 files changed, 148 insertions(+), 47 deletions(-) diff --git a/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs index 067945bf7..1fe0c49fe 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/context_values_service.rs @@ -1,7 +1,8 @@ use crate::{ ficus_proto::{ GrpcContextKey, GrpcContextKeyValue, GrpcContextValuePart, GrpcDropContextValuesRequest, GrpcGetAllContextValuesResult, - GrpcGetContextValueRequest, GrpcGuid, grpc_context_values_service_server::GrpcContextValuesService, + GrpcGetContextValueRequest, GrpcGuid, GrpcPipelineExecutionRequest, GrpcPipelinePartExecutionResult, + grpc_context_values_service_server::GrpcContextValuesService, }, grpc::converters::context_value_from_bytes, }; @@ -21,6 +22,7 @@ use uuid::Uuid; pub struct ContextValueService { context_values: Mutex>, contexts: Mutex>>, + offline_cv: Mutex>>, } impl ContextValueService { @@ -28,6 +30,7 @@ impl ContextValueService { Self { context_values: Default::default(), contexts: Default::default(), + offline_cv: Default::default(), } } @@ -106,6 +109,14 @@ impl ContextValueService { }, } } + + pub fn add_offline_context_values(&self, sub_id: Uuid, pipeline_id: Uuid, values: Vec) { + self.offline_cv.lock().unwrap().insert((sub_id, pipeline_id), values); + } + + pub fn get_offline_context_values(&self, sub_id: Uuid, pipeline_id: Uuid) -> Option> { + self.offline_cv.lock().unwrap().get(&(sub_id, pipeline_id)).cloned() + } } pub struct GrpcContextValueService { diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs index b99413f06..a9b7d9328 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs @@ -1,11 +1,12 @@ +use std::sync::Arc; use crate::grpc::events::events_handler::{PipelineEvent, PipelineEventsHandler}; pub struct DelegatingEventsHandler { - handlers: Vec>, + handlers: Vec>, } impl DelegatingEventsHandler { - pub fn new(handlers: Vec>) -> Self { + pub fn new(handlers: Vec>) -> Self { Self { handlers } } } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs index fb4c9fe65..35fcac062 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs @@ -1,6 +1,6 @@ use std::any::Any; - -use crate::grpc::events::kafka_events_handler::ProcessCaseMetadata; +use std::cell::Cell; +use crate::{ficus_proto::GrpcPipelinePartExecutionResult, grpc::events::kafka_events_handler::ProcessCaseMetadata}; use ficus::utils::context_key::ContextKey; use uuid::Uuid; @@ -25,6 +25,12 @@ impl PipelineEventsHandler for EmptyPipelineEventsHandler { } } +pub trait PipelineEventsHandlerWithRecords { + fn drain_recorded_events(&self) -> Option> { + None + } +} + pub struct GetContextValuesEvent<'a> { pub process_case_metadata: ProcessCaseMetadata, pub pipeline_part_name: String, @@ -33,13 +39,18 @@ pub struct GetContextValuesEvent<'a> { pub key_values: Vec<(&'a dyn ContextKey, &'a dyn Any)>, } +pub enum PipelinePartExecResult<'a> { + Default(GetContextValuesEvent<'a>), + Recorded(Cell) +} + pub enum PipelineFinalResult { Success(Uuid), Error(String), } pub enum PipelineEvent<'a> { - GetContextValuesEvent(GetContextValuesEvent<'a>), + GetContextValuesEvent(PipelinePartExecResult<'a>), LogMessage(String), FinalResult(PipelineFinalResult), ProcessCaseMetadata(ProcessCaseMetadata), diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/grpc_events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/grpc_events_handler.rs index f63296f15..d8c3111a7 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/grpc_events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/grpc_events_handler.rs @@ -1,6 +1,9 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex}; -use super::events_handler::{GetContextValuesEvent, PipelineEvent, PipelineEventsHandler, PipelineFinalResult}; +use super::events_handler::{ + GetContextValuesEvent, PipelineEvent, PipelineEventsHandler, PipelineEventsHandlerWithRecords, PipelineFinalResult, + PipelinePartExecResult, +}; use crate::{ ficus_proto::{ GrpcGuid, GrpcPipelineFinalResult, GrpcPipelinePartExecutionResult, GrpcPipelinePartLogMessage, GrpcPipelinePartResult, @@ -17,27 +20,55 @@ use ficus::pipelines::context::LogMessageHandler; pub struct GrpcPipelineEventsHandler { sender: Arc, console_logs_handler: ConsoleLogMessageHandler, + record_exec_results: Mutex>>, } impl GrpcPipelineEventsHandler { pub fn new(sender: GrpcSender) -> Self { + Self::new_internal(sender, false) + } + + fn new_internal(sender: GrpcSender, record_exec_results: bool) -> Self { Self { sender: Arc::new(sender), console_logs_handler: ConsoleLogMessageHandler::new(), + record_exec_results: Mutex::new(record_exec_results.then(|| vec![])), } } + + pub fn new_record_results(sender: GrpcSender) -> Self { + Self::new_internal(sender, true) + } +} + +impl PipelineEventsHandlerWithRecords for GrpcPipelineEventsHandler { + fn drain_recorded_events(&self) -> Option> { + self.record_exec_results.lock().unwrap().as_mut().map(|v| std::mem::take(v)) + } } impl PipelineEventsHandler for GrpcPipelineEventsHandler { fn handle(&self, event: &PipelineEvent) { let result = match event { - PipelineEvent::GetContextValuesEvent(event) => self.create_get_context_values_event(event), + PipelineEvent::GetContextValuesEvent(event) => match event { + PipelinePartExecResult::Default(event) => { + let event = self.create_get_context_values_event(event); + if let Some(records) = self.record_exec_results.lock().unwrap().as_mut() { + records.push(event.clone()); + } + + event + } + PipelinePartExecResult::Recorded(event) => event.take(), + }, PipelineEvent::LogMessage(message) => self.create_log_message_result(message), PipelineEvent::FinalResult(result) => self.create_final_result(match result { PipelineFinalResult::Success(uuid) => ExecutionResult::Success(GrpcGuid { guid: uuid.to_string() }), PipelineFinalResult::Error(error_message) => ExecutionResult::Error(error_message.to_string()), }), - PipelineEvent::ProcessCaseMetadata(_) => unreachable!(), + PipelineEvent::ProcessCaseMetadata(_) => { + return; + } }; if !self.is_alive() { diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs index d881fa6e7..ebde28399 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs @@ -1,4 +1,4 @@ -use super::events_handler::{PipelineEvent, PipelineEventsHandler, PipelineFinalResult}; +use super::events_handler::{GetContextValuesEvent, PipelineEvent, PipelineEventsHandler, PipelineFinalResult}; use crate::{ ficus_proto::{GrpcCaseName, GrpcGuid, GrpcKafkaConnectionMetadata, GrpcKafkaUpdate, GrpcProcessCaseMetadata, GrpcStringKeyValue}, grpc::logs_handler::ConsoleLogMessageHandler, @@ -11,6 +11,8 @@ use rdkafka::{ producer::{BaseProducer, BaseRecord}, }; use std::sync::Arc; +use std::time::Duration; +use rdkafka::util::Timeout; use uuid::Uuid; pub struct PipelineEventsProducer { @@ -42,7 +44,10 @@ impl PipelineEventsProducer { .payload(&encoded_message); let result = match self.producer.send(record) { - Ok(_) => Ok(()), + Ok(_) => { + self.producer.poll(Timeout::After(Duration::from_millis(50))); + Ok(()) + } Err(err) => Err(err.0), }; @@ -67,7 +72,7 @@ impl KafkaEventsHandler { impl PipelineEventsHandler for KafkaEventsHandler { fn handle(&self, event: &PipelineEvent) { match event { - PipelineEvent::GetContextValuesEvent(_) => {} + PipelineEvent::GetContextValuesEvent(_) => {}, PipelineEvent::LogMessage(_) => {} PipelineEvent::FinalResult(result) => match result { PipelineFinalResult::Success(_) => {} diff --git a/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs b/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs index 63def0aff..34c60734f 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/get_context_pipeline.rs @@ -1,7 +1,7 @@ use std::{any::Any, sync::Arc}; use uuid::Uuid; -use super::events::events_handler::{GetContextValuesEvent, PipelineEvent, PipelineEventsHandler}; +use super::events::events_handler::{GetContextValuesEvent, PipelineEvent, PipelineEventsHandler, PipelinePartExecResult}; use crate::grpc::events::kafka_events_handler::ProcessCaseMetadata; use ficus::{ features::cases::CaseName, @@ -68,13 +68,15 @@ impl GetContextValuePipelinePart { Some(execution_id) => execution_id, }; - sender.handle(&PipelineEvent::GetContextValuesEvent(GetContextValuesEvent { - process_case_metadata, - pipeline_part_name, - pipeline_part_id: uuid, - execution_id, - key_values, - })); + sender.handle(&PipelineEvent::GetContextValuesEvent(PipelinePartExecResult::Default( + GetContextValuesEvent { + process_case_metadata, + pipeline_part_name, + pipeline_part_id: uuid, + execution_id, + key_values, + }, + ))); Ok(()) }), diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs index 3e9577a13..434137b88 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/grpc_kafka_service.rs @@ -10,8 +10,9 @@ use crate::{ context_values_service::ContextValueService, events::{ delegating_events_handler::DelegatingEventsHandler, - events_handler::{PipelineEvent, PipelineEventsHandler, PipelineFinalResult}, + events_handler::{PipelineEvent, PipelineEventsHandler, PipelineEventsHandlerWithRecords, PipelineFinalResult}, grpc_events_handler::GrpcPipelineEventsHandler, + kafka_events_handler::ProcessCaseMetadata, }, kafka::{kafka_service::KafkaService, models::PipelineExecutionDto}, }, @@ -204,10 +205,11 @@ impl GrpcKafkaService for GrpcKafkaServiceImpl { ) -> Result, Status> { let (sender, receiver) = mpsc::channel(4); let kafka_handler = KafkaService::create_kafka_events_handler(request.get_ref().producer_metadata.as_ref())?; - let kafka_handler = Box::new(kafka_handler) as Box; - let grpc_handler = Box::new(GrpcPipelineEventsHandler::new(sender)) as Box; + let kafka_handler = Arc::new(kafka_handler) as Arc; - let handler = DelegatingEventsHandler::new(vec![kafka_handler, grpc_handler]); + let grpc_handler = Arc::new(GrpcPipelineEventsHandler::new_record_results(sender)); + + let handler = DelegatingEventsHandler::new(vec![kafka_handler, Arc::clone(&grpc_handler) as Arc]); let handler = Arc::new(handler) as Arc; let dto = PipelineExecutionDto::new(handler); @@ -222,6 +224,9 @@ impl GrpcKafkaService for GrpcKafkaServiceImpl { } }; + let cv_service = Arc::clone(&self.cv_service); + let kafka_service = Arc::clone(&self.kafka_service); + tokio::task::spawn_blocking(move || { let pipeline = request .get_ref() @@ -236,33 +241,57 @@ impl GrpcKafkaService for GrpcKafkaServiceImpl { let case_info = request.case_info.as_ref().expect("Case info must be supplied"); let case_name: Arc = case_info.case_name.clone().into(); let process_name = case_info.process_name.clone(); - let pipeline_id = Uuid::parse_str(request.pipeline_id.as_ref().expect("Must be supplied").guid.as_str()); - let subscription_id = Uuid::parse_str(request.subscription_id.as_ref().expect("Must be supplied").guid.as_str()); + let pipeline_id = Uuid::parse_str(request.pipeline_id.as_ref().expect("Must be supplied").guid.as_str()).unwrap(); + let subscription_id = Uuid::parse_str(request.subscription_id.as_ref().expect("Must be supplied").guid.as_str()).unwrap(); let pipeline_name = request.pipeline_name.clone(); let subscription_name = request.subscription_name.clone(); let context = KafkaService::create_pipeline_execution_context_from_proxy(pipeline, &context_values, &dto); - let execution_result = context.execute_grpc_pipeline(move |context| { - context.put_concrete(SUBSCRIPTION_ID_KEY.key(), subscription_id.unwrap()); - context.put_concrete(PIPELINE_ID_KEY.key(), pipeline_id.unwrap()); - context.put_concrete(SUBSCRIPTION_NAME_KEY.key(), subscription_name.into()); - context.put_concrete(PIPELINE_NAME_KEY.key(), pipeline_name.into()); - - context.put_concrete(PROCESS_NAME_KEY.key(), process_name.into()); - context.put_concrete( - CASE_NAME_KEY.key(), - CaseName { - display_name: case_name.clone(), - name_parts: vec![case_name], - }, - ); - - Ok(()) - }); + let execution_result = context.execute_grpc_pipeline_and_fill_context_values( + |context| { + context.put_concrete(SUBSCRIPTION_ID_KEY.key(), subscription_id); + context.put_concrete(PIPELINE_ID_KEY.key(), pipeline_id); + context.put_concrete(SUBSCRIPTION_NAME_KEY.key(), subscription_name.clone().into()); + context.put_concrete(PIPELINE_NAME_KEY.key(), pipeline_name.clone().into()); + + context.put_concrete(PROCESS_NAME_KEY.key(), process_name.clone().into()); + context.put_concrete( + CASE_NAME_KEY.key(), + CaseName { + display_name: case_name.clone(), + name_parts: vec![case_name], + }, + ); + + Ok(()) + }, + Arc::clone(&cv_service), + ); match execution_result { - Ok((uuid, _)) => { + Ok(uuid) => { + let handler = grpc_handler as Arc; + + let events = handler.drain_recorded_events().expect("must be some"); + cv_service.add_offline_context_values(subscription_id, pipeline_id, events); + + let case_name = Arc::from(case_info.case_name.as_str()); + let metadata = ProcessCaseMetadata { + pipeline_id: Some(pipeline_id), + pipeline_name: Some(Arc::from(pipeline_name.as_str())), + subscription_name: Some(Arc::from(subscription_name.as_str())), + subscription_id: Some(subscription_id), + process_name: Arc::from(case_info.process_name.as_str()), + metadata: vec![], + case_name: CaseName { + display_name: Arc::clone(&case_name), + name_parts: vec![case_name], + }, + }; + + dto.events_handler.handle(&PipelineEvent::ProcessCaseMetadata(metadata)); + dto .events_handler .handle(&PipelineEvent::FinalResult(PipelineFinalResult::Success(uuid))); diff --git a/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs b/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs index d96a46940..f15b2dd46 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/kafka/kafka_service.rs @@ -6,7 +6,7 @@ use crate::{ grpc::{ context_values_service::ContextValueService, events::{ - events_handler::{EmptyPipelineEventsHandler, PipelineEvent, PipelineEventsHandler, PipelineFinalResult}, + events_handler::{EmptyPipelineEventsHandler, PipelineEvent, PipelineEventsHandler, PipelineFinalResult, PipelinePartExecResult}, grpc_events_handler::GrpcPipelineEventsHandler, kafka_events_handler::{KafkaEventsHandler, PipelineEventsProducer, ProcessCaseMetadata}, }, @@ -36,7 +36,8 @@ use ficus::{ use log::{debug, error, warn}; use rdkafka::{ClientConfig, error::KafkaError}; use std::{ - collections::HashMap, + cell::Cell, + collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; use tonic::Status; @@ -265,6 +266,16 @@ impl KafkaService { case_name: &str, handler: Arc, ) -> Result { + if let Some(values) = self.cv_service.get_offline_context_values(sub_id, pipeline_id) { + for event in values { + handler.handle(&PipelineEvent::GetContextValuesEvent(PipelinePartExecResult::Recorded(Cell::new( + event, + )))); + } + + return Ok(Uuid::new_v4()); + } + let map = self.subscriptions_to_execution_requests.lock().expect("Must acquire lock"); let Some(kafka_subscription) = map.get(&sub_id).cloned() else { warn!("Subscription {} not found. Map: {:?}", sub_id, map.keys()); From dc65d459b1d6453c7d7b097e862765fea00c0de6 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 7 Sep 2026 20:55:19 +0300 Subject: [PATCH 29/35] cargo fmt --- .../features/discovery/petri_net/pnml_serialization.rs | 8 +------- Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs | 5 +---- .../src/grpc/events/delegating_events_handler.rs | 2 +- .../rust/ficus_backend/src/grpc/events/events_handler.rs | 5 ++--- .../ficus_backend/src/grpc/events/kafka_events_handler.rs | 7 +++---- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs index 7e827d87c..2247b5208 100644 --- a/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs +++ b/Ficus/src/rust/ficus/src/features/discovery/petri_net/pnml_serialization.rs @@ -234,13 +234,7 @@ where all_arcs.sort_by(|(_, n1), (_, n2)| n1.cmp(n2)); for (_, name) in all_arcs { - StartEndElementCookie::new_with_attrs( - writer, - ARC_TAG_NAME, - &vec![ - (ID_ATTR_NAME, &name), - ], - )?; + StartEndElementCookie::new_with_attrs(writer, ARC_TAG_NAME, &vec![(ID_ATTR_NAME, &name)])?; } Ok(()) diff --git a/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs b/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs index 049ac0714..2365486b7 100644 --- a/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs +++ b/Ficus/src/rust/ficus/tests/petri_nets/replay_tests.rs @@ -38,10 +38,7 @@ pub fn test_silent_transitions_replay() { let mut petri_net = discover_petri_net_heuristic(&info, &triangle_relation, 0.0, 0, 1.0, 0.1, 0.5); ensure_initial_marking(&log, &mut petri_net); - let expected_transitions = vec![ - Some(vecs!["A", "tau", "B", "C", "D"]), - Some(vecs!["A", "tau", "C", "B", "D"]), - ]; + let expected_transitions = vec![Some(vecs!["A", "tau", "B", "C", "D"]), Some(vecs!["A", "tau", "C", "B", "D"])]; execute_test_with_replay(&petri_net, &log, expected_transitions); } diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs index a9b7d9328..ea408d91a 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/delegating_events_handler.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use crate::grpc::events::events_handler::{PipelineEvent, PipelineEventsHandler}; +use std::sync::Arc; pub struct DelegatingEventsHandler { handlers: Vec>, diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs index 35fcac062..6e78f987c 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/events_handler.rs @@ -1,7 +1,6 @@ -use std::any::Any; -use std::cell::Cell; use crate::{ficus_proto::GrpcPipelinePartExecutionResult, grpc::events::kafka_events_handler::ProcessCaseMetadata}; use ficus::utils::context_key::ContextKey; +use std::{any::Any, cell::Cell}; use uuid::Uuid; pub trait PipelineEventsHandler: Send + Sync { @@ -41,7 +40,7 @@ pub struct GetContextValuesEvent<'a> { pub enum PipelinePartExecResult<'a> { Default(GetContextValuesEvent<'a>), - Recorded(Cell) + Recorded(Cell), } pub enum PipelineFinalResult { diff --git a/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs b/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs index ebde28399..a53b611d2 100644 --- a/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs +++ b/Ficus/src/rust/ficus_backend/src/grpc/events/kafka_events_handler.rs @@ -9,10 +9,9 @@ use rdkafka::{ ClientConfig, error::KafkaError, producer::{BaseProducer, BaseRecord}, + util::Timeout, }; -use std::sync::Arc; -use std::time::Duration; -use rdkafka::util::Timeout; +use std::{sync::Arc, time::Duration}; use uuid::Uuid; pub struct PipelineEventsProducer { @@ -72,7 +71,7 @@ impl KafkaEventsHandler { impl PipelineEventsHandler for KafkaEventsHandler { fn handle(&self, event: &PipelineEvent) { match event { - PipelineEvent::GetContextValuesEvent(_) => {}, + PipelineEvent::GetContextValuesEvent(_) => {} PipelineEvent::LogMessage(_) => {} PipelineEvent::FinalResult(result) => match result { PipelineFinalResult::Success(_) => {} From a127d39d4e094660b53a7eeb32e0d0d6a102477b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 7 Sep 2026 21:36:25 +0300 Subject: [PATCH 30/35] Fix execution results grouping --- .../Components/CaseInfo/CaseInfo.razor | 10 ++--- .../Services/Cases/ISubscriptionsService.cs | 39 +++++++++++-------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/Ficus/src/front/FicusFrontend/FicusDashboard/Components/CaseInfo/CaseInfo.razor b/Ficus/src/front/FicusFrontend/FicusDashboard/Components/CaseInfo/CaseInfo.razor index a34e1041c..2ba5010f3 100644 --- a/Ficus/src/front/FicusFrontend/FicusDashboard/Components/CaseInfo/CaseInfo.razor +++ b/Ficus/src/front/FicusFrontend/FicusDashboard/Components/CaseInfo/CaseInfo.razor @@ -21,7 +21,7 @@ @if (myIsLoading) {
- +
return; @@ -48,7 +48,7 @@
- @foreach (var (index, (_, results)) in myPipelinePartsExecutionResults.OrderBy(x => x.Value.Order).Index()) + @foreach (var (index, results) in myPipelinePartsExecutionResults.OrderBy(x => x.Order).Index()) { myPipelinePartsExecutionResults = - new Dictionary(); + + private IReadOnlyList myPipelinePartsExecutionResults = []; private bool myIsLoading; - + private Lifetime myCurrentLifetime; private ProcessCaseData? myProcessCaseData; diff --git a/Ficus/src/front/FicusFrontend/FicusDashboard/Services/Cases/ISubscriptionsService.cs b/Ficus/src/front/FicusFrontend/FicusDashboard/Services/Cases/ISubscriptionsService.cs index 59808cfb5..fba87f717 100644 --- a/Ficus/src/front/FicusFrontend/FicusDashboard/Services/Cases/ISubscriptionsService.cs +++ b/Ficus/src/front/FicusFrontend/FicusDashboard/Services/Cases/ISubscriptionsService.cs @@ -12,7 +12,7 @@ public interface ISubscriptionsService ISignal AnyPipelineSubEntityUpdated { get; } void StartUpdatesRequestingRoutine(CancellationToken token); - Task> GetCaseExecutionResult(ProcessCaseData data); + Task> GetCaseExecutionResult(ProcessCaseData data); } public class SubscriptionsService( @@ -54,7 +54,7 @@ public void StartUpdatesRequestingRoutine(CancellationToken token) }, token); } - public async Task> GetCaseExecutionResult(ProcessCaseData data) + public async Task> GetCaseExecutionResult(ProcessCaseData data) { var result = await client.GetPipelineCaseContextValueAsync(new GrpcGetPipelineCaseContextValuesRequest { @@ -68,24 +68,29 @@ public async Task> GetCa ProcessName = data.ProcessData.ProcessName }); - return result.ContextValues.Select((value, order) => - { - var id = value.PipelinePartInfo.Id.ToGuid(); - - var results = value.ExecutionResults.Select(r => new PipelinePartExecutionResult + return result.ContextValues + .Select((x, order) => (Id: x.PipelinePartInfo.Id.ToGuid(), Order: order, Parts: x)) + .GroupBy(x => x.Id) + .Select(group => { - ContextValues = r.ContextValues.Select(c => new ContextValueWrapper(c)).ToList() - }).ToList(); + var results = group.SelectMany(x => x.Parts.ExecutionResults).Select(r => new PipelinePartExecutionResult + { + ContextValues = [.. r.ContextValues.Select(c => new ContextValueWrapper(c))] + }).ToList(); - var partResults = new PipelinePartExecutionResults - { - PipelinePartName = value.PipelinePartInfo.Name, - Order = (uint)order, - Results = new ViewableList(results) - }; + var (_, order, parts) = group.First(); + var info = parts.PipelinePartInfo; + + var partResults = new PipelinePartExecutionResults + { + PipelinePartName = info.Name, + Order = (uint)order, + Results = new ViewableList(results) + }; - return (id, partResults); - }).ToDictionary(); + return partResults; + }) + .ToList(); } private void ProcessState(GrpcSubscriptionAndPipelinesStateResponse reponse) From a4414da9567967250fd23b00ed11cb8e6323e12b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 7 Sep 2026 21:59:08 +0300 Subject: [PATCH 31/35] Update CMAKE version to 4.2.0, update Visual Stusio generator to VS 18 2026, build ProcfilerBuildTasks in run script --- .run/windows/ProcfilerOnline.cmd | 1 + Procfiler/src/cpp/offline/CMakeLists.txt | 2 +- Procfiler/src/cpp/online/CMakeLists.txt | 2 +- Procfiler/src/dotnet/ProcfilerBuildTasks/BuildCppProcfiler.cs | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.run/windows/ProcfilerOnline.cmd b/.run/windows/ProcfilerOnline.cmd index 78dfefafc..0b48992a8 100644 --- a/.run/windows/ProcfilerOnline.cmd +++ b/.run/windows/ProcfilerOnline.cmd @@ -5,6 +5,7 @@ set OnlineProcfilerSettings__KafkaSettings__TopicName=my-topic set OnlineProcfilerSettings__KafkaSettings__BootstrapServers=localhost:9092 dotnet clean %PM_IDE_ROOT%/All.sln +dotnet build %PM_IDE_ROOT%/ProcfilerBuildTasks/ -c Release dotnet build %PM_IDE_ROOT%/All.sln -c Release dotnet build %PM_IDE_ROOT%/Procfiler/src/dotnet/ProcfilerLoggerProvider/ -c Release dotnet build %PM_IDE_ROOT%/Procfiler/test_data/source/WFWebApp -c Release diff --git a/Procfiler/src/cpp/offline/CMakeLists.txt b/Procfiler/src/cpp/offline/CMakeLists.txt index 47f4b64d3..53c1080c4 100644 --- a/Procfiler/src/cpp/offline/CMakeLists.txt +++ b/Procfiler/src/cpp/offline/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14.5) +cmake_minimum_required(VERSION 4.2.0) add_compile_options(-fpermissive) project(Procfiler) diff --git a/Procfiler/src/cpp/online/CMakeLists.txt b/Procfiler/src/cpp/online/CMakeLists.txt index 40de572e0..360b8b702 100644 --- a/Procfiler/src/cpp/online/CMakeLists.txt +++ b/Procfiler/src/cpp/online/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14.5) +cmake_minimum_required(VERSION 4.2.0) add_compile_options(-fpermissive) project(ProcfilerOnline) diff --git a/Procfiler/src/dotnet/ProcfilerBuildTasks/BuildCppProcfiler.cs b/Procfiler/src/dotnet/ProcfilerBuildTasks/BuildCppProcfiler.cs index 0f50c1e60..67822d916 100644 --- a/Procfiler/src/dotnet/ProcfilerBuildTasks/BuildCppProcfiler.cs +++ b/Procfiler/src/dotnet/ProcfilerBuildTasks/BuildCppProcfiler.cs @@ -109,7 +109,7 @@ private bool InitializeCmakeProject() RedirectStandardOutput = true, Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch { - true => $"-S {CppProcfilerFolderPath} -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 17 2022\"", + true => $"-S {CppProcfilerFolderPath} -DCMAKE_BUILD_TYPE=Release -G \"Visual Studio 18 2026\"", false => $"-S {CppProcfilerFolderPath} -DCMAKE_BUILD_TYPE=Release -G Ninja" } } From 7cd8a406ea14766a566b732da45ab159dfd14715 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 8 Sep 2026 20:37:48 +0300 Subject: [PATCH 32/35] Update Ficus integration tests gold --- .../petri_net.gold | 38 ++++++++----- .../petri_net.gold | 26 ++++++--- .../petri_net.gold | 30 ++++++---- .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 38 ++++++++----- .../petri_net.gold | 22 ++++++-- .../petri_net.gold | 22 ++++++-- .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 30 ++++++---- .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 42 ++++++++------ .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 30 ++++++---- .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 46 +++++++++------ .../petri_net.gold | 42 ++++++++------ .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 34 +++++++---- .../petri_net.gold | 28 +++++----- .../petri_net.gold | 32 +++++------ .../petri_net.gold | 56 +++++++++---------- .../petri_net.gold | 48 ++++++++-------- .../petri_net.gold | 32 +++++------ .../petri_net.gold | 40 ++++++------- .../petri_net.gold | 36 ++++++------ .../petri_net.gold | 30 +++++----- .../petri_net.gold | 24 ++++---- .../petri_net.gold | 32 +++++------ .../petri_net.gold | 40 ++++++------- .../petri_net.gold | 36 ++++++------ .../petri_net.gold | 8 +-- 35 files changed, 731 insertions(+), 511 deletions(-) diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha/petri_net.gold index 650d5d4b9..b60581b4d 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha/petri_net.gold @@ -13,6 +13,9 @@ + + 1 + @@ -49,33 +52,40 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha2/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha2/petri_net.gold index 54b60e5e4..90a1fefd6 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha2/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha2/petri_net.gold @@ -9,6 +9,9 @@ + + 1 + @@ -35,21 +38,28 @@ C - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha3/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha3/petri_net.gold index afe21db44..8b15b1ac4 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha3/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha3/petri_net.gold @@ -11,6 +11,9 @@ + + 1 + @@ -42,25 +45,32 @@ D - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha4/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha4/petri_net.gold index dd824a4ec..b7e45b3a6 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha4/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha4/petri_net.gold @@ -17,6 +17,9 @@ + + 1 + @@ -58,41 +61,48 @@ F - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha5/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha5/petri_net.gold index 78f3f80bd..6cea9f9d9 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha5/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha5/petri_net.gold @@ -15,6 +15,9 @@ + + 1 + @@ -51,41 +54,48 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha6/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha6/petri_net.gold index 416d45724..9afd43351 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha6/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha6/petri_net.gold @@ -15,6 +15,9 @@ + + 1 + @@ -51,33 +54,40 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha7/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha7/petri_net.gold index 09df24a53..36d001393 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha7/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha7/petri_net.gold @@ -7,6 +7,9 @@ + + 1 + @@ -38,17 +41,24 @@ D - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha8/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha8/petri_net.gold index 09df24a53..36d001393 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha8/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha8/petri_net.gold @@ -7,6 +7,9 @@ + + 1 + @@ -38,17 +41,24 @@ D - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus/petri_net.gold index 212c8077f..c660d4a55 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus/petri_net.gold @@ -11,6 +11,9 @@ + + 1 + @@ -42,29 +45,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus2/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus2/petri_net.gold index 3a7e65b44..049b7d237 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus2/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus2/petri_net.gold @@ -9,6 +9,9 @@ + + 1 + @@ -35,25 +38,32 @@ C - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus4/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus4/petri_net.gold index dd824a4ec..b7e45b3a6 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus4/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus4/petri_net.gold @@ -17,6 +17,9 @@ + + 1 + @@ -58,41 +61,48 @@ F - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus5/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus5/petri_net.gold index 78f3f80bd..6cea9f9d9 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus5/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus5/petri_net.gold @@ -15,6 +15,9 @@ + + 1 + @@ -51,41 +54,48 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus6/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus6/petri_net.gold index dab455040..de689e0ed 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus6/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus6/petri_net.gold @@ -17,6 +17,9 @@ + + 1 + @@ -53,37 +56,44 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus7/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus7/petri_net.gold index 1cecc6f7b..70e1a269f 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus7/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus7/petri_net.gold @@ -13,6 +13,9 @@ + + 1 + @@ -44,29 +47,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus8/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus8/petri_net.gold index 9bf1cec8b..1c5c11601 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus8/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus8/petri_net.gold @@ -13,6 +13,9 @@ + + 1 + @@ -44,29 +47,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus/petri_net.gold index 212c8077f..c660d4a55 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus/petri_net.gold @@ -11,6 +11,9 @@ + + 1 + @@ -42,29 +45,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus2/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus2/petri_net.gold index 3a7e65b44..049b7d237 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus2/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus2/petri_net.gold @@ -9,6 +9,9 @@ + + 1 + @@ -35,25 +38,32 @@ C - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus4/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus4/petri_net.gold index dd824a4ec..b7e45b3a6 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus4/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus4/petri_net.gold @@ -17,6 +17,9 @@ + + 1 + @@ -58,41 +61,48 @@ F - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus5/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus5/petri_net.gold index 78f3f80bd..6cea9f9d9 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus5/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus5/petri_net.gold @@ -15,6 +15,9 @@ + + 1 + @@ -51,41 +54,48 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus6/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus6/petri_net.gold index dab455040..de689e0ed 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus6/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus6/petri_net.gold @@ -17,6 +17,9 @@ + + 1 + @@ -53,37 +56,44 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus7/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus7/petri_net.gold index 1cecc6f7b..70e1a269f 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus7/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus7/petri_net.gold @@ -13,6 +13,9 @@ + + 1 + @@ -44,29 +47,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus8/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus8/petri_net.gold index 9bf1cec8b..1c5c11601 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus8/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus8/petri_net.gold @@ -13,6 +13,9 @@ + + 1 + @@ -44,29 +47,36 @@ D - + - + - + - + - + - + - + - + - + - + - + - + + + + + 1 + + + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc/petri_net.gold index 4854c558f..fb25ee7bf 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc/petri_net.gold @@ -47,33 +47,33 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_10/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_10/petri_net.gold index 2689b2bc9..c6dd7c0a7 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_10/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_10/petri_net.gold @@ -49,37 +49,37 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_11/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_11/petri_net.gold index e65d31bfe..00380cffd 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_11/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_11/petri_net.gold @@ -72,61 +72,61 @@ H - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_13/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_13/petri_net.gold index 411a24c89..f68944693 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_13/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_13/petri_net.gold @@ -61,53 +61,53 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_2/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_2/petri_net.gold index d9dea2406..6e896955f 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_2/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_2/petri_net.gold @@ -47,37 +47,37 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_3/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_3/petri_net.gold index 2c5a8ca9c..6c799d8cc 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_3/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_3/petri_net.gold @@ -61,45 +61,45 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_4/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_4/petri_net.gold index c22a7ff7a..f0a5cde99 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_4/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_4/petri_net.gold @@ -52,41 +52,41 @@ F - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_5/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_5/petri_net.gold index 31396c0f9..84e9ef711 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_5/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_5/petri_net.gold @@ -47,35 +47,35 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_6/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_6/petri_net.gold index ff59a4b1e..435542202 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_6/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_6/petri_net.gold @@ -43,29 +43,29 @@ E - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_7/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_7/petri_net.gold index d2b8c6476..052856512 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_7/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_7/petri_net.gold @@ -47,37 +47,37 @@ E - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_8/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_8/petri_net.gold index b63671119..797d8cb3c 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_8/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_8/petri_net.gold @@ -61,45 +61,45 @@ G - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_9/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_9/petri_net.gold index 7578f17de..559acdc12 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_9/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_alpha_plus_plus_nfc_9/petri_net.gold @@ -54,41 +54,41 @@ F - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_heuristic/petri_net.gold b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_heuristic/petri_net.gold index 71290b0fd..c327bc7ec 100644 --- a/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_heuristic/petri_net.gold +++ b/Ficus/test_data/gold/python/petri_nets/test_discover_petri_net_heuristic/petri_net.gold @@ -34,13 +34,13 @@ D - + - + - + - + \ No newline at end of file From 9f24598aa7f193c75bfaf2c4becefeba90ca4068 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 8 Sep 2026 20:44:33 +0300 Subject: [PATCH 33/35] Try to update to VS 2026 in CI --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4ba593e40..6c31edef3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: jobs: dotnet-bxes-unit-tests: - runs-on: windows-2022 + runs-on: windows-2025-vs202 steps: - uses: actions/checkout@v4 @@ -25,7 +25,7 @@ jobs: run: dotnet test ./bxes/src/csharp/Bxes.Tests rust-bxes-unit-tests: - runs-on: windows-2022 + runs-on: windows-2025-vs2026 steps: - uses: actions/checkout@v4 @@ -37,7 +37,7 @@ jobs: RUSTFLAGS: "" dotnet-procfiler-unit-tests: - runs-on: windows-2022 + runs-on: windows-2025-vs2026 steps: - uses: actions/checkout@v4 From f38358aa0d6ae5f496470e83b945744a017f64a9 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 8 Sep 2026 20:52:11 +0300 Subject: [PATCH 34/35] Fix typo --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6c31edef3..273de1d81 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ on: jobs: dotnet-bxes-unit-tests: - runs-on: windows-2025-vs202 + runs-on: windows-2025-vs2026 steps: - uses: actions/checkout@v4 From 0e7e113159cb9f345f1bcfc3aca83e1cc8ea49e9 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 8 Sep 2026 21:00:22 +0300 Subject: [PATCH 35/35] Explicitly install cmake version --- .github/workflows/tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 273de1d81..a0f84bb38 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -86,6 +86,11 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install CMake + uses: ssrobins/install-cmake@v1 + with: + version: 4.2.0 + - name: Setup docker uses: docker/setup-qemu-action@v3