use crate::{
BusinessProcessModelAndNotation,
element::BPMNElement,
marking::{BPMNRootMarking, BPMNSubMarking, Token},
semantics::TransitionIndex,
traits::processable::Processable,
};
use anyhow::{Result, anyhow};
use bitvec::{bitvec, prelude::Lsb0, vec::BitVec};
use ebi_activity_key::Activity;
use ebi_arithmetic::Fraction;
pub trait Transitionable {
fn number_of_transitions(&self, marking: &BPMNSubMarking) -> usize;
fn enabled_transitions(
&self,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<BitVec>;
fn execute_transition(
&self,
transition_index: TransitionIndex,
root_marking: &mut BPMNRootMarking,
sub_marking: &mut BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<()>;
fn transition_activity(
&self,
transition_index: TransitionIndex,
marking: &BPMNSubMarking,
) -> Option<Activity>;
fn transition_debug(
&self,
transition_index: TransitionIndex,
marking: &BPMNSubMarking,
bpmn: &BusinessProcessModelAndNotation,
) -> Option<String>;
fn transition_probabilistic_penalty(
&self,
transition_index: TransitionIndex,
marking: &BPMNSubMarking,
parent: &dyn Processable,
) -> Option<Fraction>;
fn transition_2_consumed_tokens(
&self,
transition_index: TransitionIndex,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<Token>>;
fn transition_2_produced_tokens(
&self,
transition_index: TransitionIndex,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<Token>>;
}
impl Transitionable for Vec<BPMNElement> {
fn number_of_transitions(&self, marking: &BPMNSubMarking) -> usize {
self.iter().map(|x| x.number_of_transitions(marking)).sum()
}
fn enabled_transitions(
&self,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<BitVec> {
let mut result = bitvec![];
for element in self {
result.extend(element.enabled_transitions(root_marking, sub_marking, parent, bpmn)?)
}
Ok(result)
}
fn execute_transition(
&self,
mut transition_index: TransitionIndex,
root_marking: &mut BPMNRootMarking,
sub_marking: &mut BPMNSubMarking,
parent: &dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<()> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(sub_marking);
if transition_index < number_of_transitions {
return element.execute_transition(
transition_index,
root_marking,
sub_marking,
parent,
bpmn,
);
}
transition_index -= number_of_transitions;
}
Err(anyhow!("transition not found"))
}
fn transition_activity(
&self,
mut transition_index: TransitionIndex,
sub_marking: &BPMNSubMarking,
) -> Option<Activity> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(sub_marking);
if transition_index < number_of_transitions {
return element.transition_activity(transition_index, sub_marking);
}
transition_index -= number_of_transitions;
}
None
}
fn transition_debug(
&self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
bpmn: &BusinessProcessModelAndNotation,
) -> Option<String> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(marking);
if transition_index < number_of_transitions {
return element.transition_debug(transition_index, marking, bpmn);
}
transition_index -= number_of_transitions;
}
None
}
fn transition_probabilistic_penalty(
&self,
mut transition_index: TransitionIndex,
marking: &BPMNSubMarking,
parent: &dyn Processable,
) -> Option<Fraction> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(marking);
if transition_index < number_of_transitions {
return element.transition_probabilistic_penalty(transition_index, marking, parent);
}
transition_index -= number_of_transitions;
}
None
}
fn transition_2_consumed_tokens<'a>(
&'a self,
mut transition_index: TransitionIndex,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &'a dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<Token>> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(sub_marking);
if transition_index < number_of_transitions {
return element.transition_2_consumed_tokens(
transition_index,
root_marking,
sub_marking,
parent,
bpmn,
);
}
transition_index -= number_of_transitions;
}
Err(anyhow!("Transition does not exist."))
}
fn transition_2_produced_tokens<'a>(
&'a self,
mut transition_index: TransitionIndex,
root_marking: &BPMNRootMarking,
sub_marking: &BPMNSubMarking,
parent: &'a dyn Processable,
bpmn: &BusinessProcessModelAndNotation,
) -> Result<Vec<Token>> {
for element in self.iter() {
let number_of_transitions = element.number_of_transitions(sub_marking);
if transition_index < number_of_transitions {
return element.transition_2_consumed_tokens(
transition_index,
root_marking,
sub_marking,
parent,
bpmn,
);
}
transition_index -= number_of_transitions;
}
Err(anyhow!("Transition does not exist."))
}
}
macro_rules! execute_transition_parallel_split {
($self:ident, $sub_marking:ident) => {
for outgoing_sequence_flow in &$self.outgoing_sequence_flows {
$sub_marking.sequence_flow_2_tokens[*outgoing_sequence_flow] += 1;
}
};
}
pub(crate) use execute_transition_parallel_split;
macro_rules! number_of_transitions_xor_join_only {
($s:ident) => {
$s.incoming_sequence_flows.len().max(1)
};
}
pub(crate) use number_of_transitions_xor_join_only;
macro_rules! enabledness_xor_join_only {
($self:ident, $sub_marking:ident) => {
{
let mut result = bitvec![0;$self.incoming_sequence_flows.len().max(1)];
if $self.incoming_sequence_flows.len() >= 1 {
for (transition_index, incoming_sequence_flow) in
$self.incoming_sequence_flows.iter().enumerate()
{
if $sub_marking.sequence_flow_2_tokens[*incoming_sequence_flow] >= 1 {
result.set(transition_index, true);
}
}
} else {
if $sub_marking.element_index_2_tokens[$self.local_index] >= 1 {
result.set(0, true);
}
}
result
}
};
}
pub(crate) use enabledness_xor_join_only;
macro_rules! execute_transition_xor_join_consume {
($self: ident, $sub_marking:ident, $transition_index:expr) => {
if $self.incoming_sequence_flows.len() >= 1 {
$sub_marking.sequence_flow_2_tokens
[$self.incoming_sequence_flows[$transition_index]] -= 1;
} else {
$sub_marking.element_index_2_tokens[$self.local_index] -= 1;
}
};
}
pub(crate) use execute_transition_xor_join_consume;
macro_rules! execute_transition_message_produce {
($self:ident, $root_marking:ident, $bpmn:ident) => {
if let Some(message_flow_index) = $self.outgoing_message_flow {
{
let target = $bpmn.message_flow_index_2_target(message_flow_index)?;
if !target.incoming_messages_are_ignored() {
$root_marking.message_flow_2_tokens[message_flow_index] += 1;
}
}
}
};
}
pub(crate) use execute_transition_message_produce;
macro_rules! transition_2_consumed_tokens_xor_join {
($self:ident, $transition_index:expr, $parent:ident) => {
if $self.incoming_sequence_flows.len() >= 1 {
let sequence_flow_index = $self.incoming_sequence_flows[$transition_index];
vec![Token::SequenceFlow(
$parent.sequence_flows_non_recursive()[sequence_flow_index].global_index,
)]
} else {
vec![Token::Element($self.global_index)]
}
};
}
pub(crate) use transition_2_consumed_tokens_xor_join;
macro_rules! transition_2_produced_tokens_concurrent_split {
($self:ident, $parent:ident) => {
$self
.outgoing_sequence_flows()
.iter()
.filter_map(|sequence_flow_id| {
Some(Token::SequenceFlow(
$parent
.sequence_flows_non_recursive()
.get(*sequence_flow_id)?
.global_index,
))
})
.collect::<Vec<_>>()
};
}
pub(crate) use transition_2_produced_tokens_concurrent_split;
macro_rules! transition_2_consumed_tokens_message {
($self:ident, $bpmn:ident) => {
if let Some(message_flow_index) = $self.incoming_message_flow {
let source = $bpmn.message_flow_index_2_source(message_flow_index)?;
if !source.outgoing_message_flows_always_have_tokens() {
if !source.outgoing_messages_cannot_be_removed() {
vec![Token::MessageFlow(
$bpmn.message_flows[message_flow_index].global_index,
)]
} else {
vec![]
}
} else {
vec![]
}
} else {
vec![]
}
};
}
pub(crate) use transition_2_consumed_tokens_message;
macro_rules! transition_2_produced_tokens_message {
($self:ident, $bpmn:ident) => {
if let Some(message_flow_index) = $self.outgoing_message_flow {
{
let target = $bpmn.message_flow_index_2_target(message_flow_index)?;
if !target.incoming_messages_are_ignored() {
vec![Token::MessageFlow(
$bpmn.message_flows[message_flow_index].global_index,
)]
} else {
vec![]
}
}
} else {
vec![]
}
};
}
pub(crate) use transition_2_produced_tokens_message;