Skip to main content

ebi_objects/conversions/
to_business_process_model_and_notation.rs

1use crate::{
2    DeterministicFiniteAutomaton, DirectlyFollowsGraph, DirectlyFollowsModel, LabelledPetriNet, PetriNetMarkupLanguage, ProcessTree, ProcessTreeMarkupLanguage, StochasticDeterministicFiniteAutomaton, StochasticDirectlyFollowsModel, StochasticLabelledPetriNet, StochasticNondeterministicFiniteAutomaton, StochasticProcessTree
3};
4use ebi_bpmn::{
5    BusinessProcessModelAndNotation,
6    creator::{BPMNCreator, GatewayType},
7};
8
9impl From<LabelledPetriNet> for BusinessProcessModelAndNotation {
10    fn from(value: LabelledPetriNet) -> Self {
11        let mut c = BPMNCreator::new();
12        let process = c.add_process();
13
14        //transform places to xor gateways
15        let mut place_2_xor_gateway = vec![];
16        for _ in 0..value.get_number_of_places() {
17            place_2_xor_gateway.push(c.add_gateway_unchecked(process, GatewayType::Exclusive));
18        }
19
20        //transform transitions to input and output elements
21        for transition in 0..value.get_number_of_transitions() {
22            //store whether there are multiple input or output arcs
23            let out_1 = value.transition2output_places[transition].len() == 1
24                && value.transition2output_places_cardinality[transition][0] == 1;
25            let in_1 = value.transition2input_places[transition].len() == 1
26                && value.transition2input_places_cardinality[transition][0] == 1;
27
28            if let Some(activity) = value.get_transition_label(transition) {
29                //labelled transition
30                let task = c.add_task_unchecked(process, activity);
31
32                //incoming sequence flows
33                if in_1 {
34                    //only a single incoming arc -> no need for a gateway
35                    let pre_place =
36                        place_2_xor_gateway[value.transition2input_places[transition][0]];
37                    c.add_sequence_flow_unchecked(process, pre_place, task);
38                } else {
39                    //multiple incoming arcs -> need a parallel gateway
40                    let gateway = c.add_gateway_unchecked(process, GatewayType::Parallel);
41                    for (output_place, cardinality) in value.transition2output_places[transition]
42                        .iter()
43                        .zip(value.transition2output_places_cardinality[transition].iter())
44                    {
45                        let output_gateway = place_2_xor_gateway[*output_place];
46                        for _ in 0..*cardinality {
47                            c.add_sequence_flow_unchecked(process, gateway, output_gateway);
48                        }
49                    }
50                }
51
52                //outgoing sequence flows
53                for (output_place, cardinality) in value.transition2output_places[transition]
54                    .iter()
55                    .zip(value.transition2output_places_cardinality[transition].iter())
56                {
57                    let output_gateway = place_2_xor_gateway[*output_place];
58                    for _ in 0..*cardinality {
59                        c.add_sequence_flow_unchecked(process, task, output_gateway);
60                    }
61                }
62            } else {
63                //silent transition
64                match (in_1, out_1) {
65                    (true, true) => {
66                        //single input, single output arc
67
68                        //no gateway, just sequence flow
69                        let pre_place =
70                            place_2_xor_gateway[value.transition2input_places[transition][0]];
71                        let post_place =
72                            place_2_xor_gateway[value.transition2output_places[transition][0]];
73                        c.add_sequence_flow_unchecked(process, pre_place, post_place);
74                    }
75                    (true, false) => {
76                        let gateway = c.add_gateway_unchecked(process, GatewayType::Parallel);
77
78                        //incoming sequence flows
79                        for (input_place, cardinality) in value.transition2input_places[transition]
80                            .iter()
81                            .zip(value.transition2input_places_cardinality[transition].iter())
82                        {
83                            let input_gateway = place_2_xor_gateway[*input_place];
84                            for _ in 0..*cardinality {
85                                c.add_sequence_flow_unchecked(process, input_gateway, gateway);
86                            }
87                        }
88
89                        //outgoing sequence flows
90                        for (output_place, cardinality) in value.transition2output_places
91                            [transition]
92                            .iter()
93                            .zip(value.transition2output_places_cardinality[transition].iter())
94                        {
95                            let output_gateway = place_2_xor_gateway[*output_place];
96                            for _ in 0..*cardinality {
97                                c.add_sequence_flow_unchecked(process, gateway, output_gateway);
98                            }
99                        }
100                    }
101                    (false, true) => {
102                        let gateway = c.add_gateway_unchecked(process, GatewayType::Exclusive);
103
104                        //incoming sequence flows
105                        for (input_place, cardinality) in value.transition2input_places[transition]
106                            .iter()
107                            .zip(value.transition2input_places_cardinality[transition].iter())
108                        {
109                            let input_gateway = place_2_xor_gateway[*input_place];
110                            for _ in 0..*cardinality {
111                                c.add_sequence_flow_unchecked(process, input_gateway, gateway);
112                            }
113                        }
114
115                        //outgoing sequence flows
116                        for (output_place, cardinality) in value.transition2output_places
117                            [transition]
118                            .iter()
119                            .zip(value.transition2output_places_cardinality[transition].iter())
120                        {
121                            let output_gateway = place_2_xor_gateway[*output_place];
122                            for _ in 0..*cardinality {
123                                c.add_sequence_flow_unchecked(process, gateway, output_gateway);
124                            }
125                        }
126                    }
127                    (false, false) => {
128                        //multiple inputs, multiple outputs
129                        let input = c.add_gateway_unchecked(process, GatewayType::Exclusive);
130                        let output = c.add_gateway_unchecked(process, GatewayType::Parallel);
131                        c.add_sequence_flow_unchecked(process, input, output);
132
133                        //incoming sequence flows
134                        for (input_place, cardinality) in value.transition2input_places[transition]
135                            .iter()
136                            .zip(value.transition2input_places_cardinality[transition].iter())
137                        {
138                            let input_gateway = place_2_xor_gateway[*input_place];
139                            for _ in 0..*cardinality {
140                                c.add_sequence_flow_unchecked(process, input_gateway, input);
141                            }
142                        }
143
144                        //outgoing sequence flows
145                        for (output_place, cardinality) in value.transition2output_places
146                            [transition]
147                            .iter()
148                            .zip(value.transition2output_places_cardinality[transition].iter())
149                        {
150                            let output_gateway = place_2_xor_gateway[*output_place];
151                            for _ in 0..*cardinality {
152                                c.add_sequence_flow_unchecked(process, output, output_gateway);
153                            }
154                        }
155                    }
156                }
157            }
158        }
159
160        //structural correctness guaranteed
161        c.to_bpmn().unwrap()
162    }
163}
164
165macro_rules! via_lpn {
166    ($t:ident) => {
167        impl From<$t> for BusinessProcessModelAndNotation {
168            fn from(value: $t) -> Self {
169                LabelledPetriNet::from(value).into()
170            }
171        }
172    };
173}
174
175via_lpn!(DeterministicFiniteAutomaton);
176via_lpn!(DirectlyFollowsGraph);
177via_lpn!(DirectlyFollowsModel);
178via_lpn!(StochasticDirectlyFollowsModel);
179via_lpn!(ProcessTree);
180via_lpn!(PetriNetMarkupLanguage);
181via_lpn!(ProcessTreeMarkupLanguage);
182via_lpn!(StochasticProcessTree);
183via_lpn!(StochasticLabelledPetriNet);
184via_lpn!(StochasticDeterministicFiniteAutomaton);
185via_lpn!(StochasticNondeterministicFiniteAutomaton);
186
187#[cfg(test)]
188mod tests {
189    use crate::StochasticLabelledPetriNet;
190    use ebi_bpmn::BusinessProcessModelAndNotation;
191    use std::fs;
192
193    #[test]
194    fn slpn_2_bpmn() {
195        let fin = fs::read_to_string("testfiles/a-aa-bb.slpn").unwrap();
196        let slpn = fin.parse::<StochasticLabelledPetriNet>().unwrap();
197
198        let _bpmn = BusinessProcessModelAndNotation::from(slpn);
199    }
200}