17 : systemState(systemState)
21 , instance( dataAttributes.size() ? dataAttributes[
BPMNOS::Model::ExtensionElements::Index::Instance] : -1 )
22 , parentToken(nullptr)
23 , ownedData(dataAttributes)
31 : systemState(systemState)
32 , process(parentToken->owner->process)
34 , root(parentToken->owner->root)
35 , instance(instance.value_or( (*parentToken->data)[
BPMNOS::Model::ExtensionElements::Index::Instance].get().value() ) )
36 , parentToken(parentToken)
37 , ownedData(dataAttributes)
51 assert( this->instance.has_value() && this->instance.value() >= 0 );
55 : systemState(other->systemState)
56 , process(other->process)
59 , instance( other->instance )
60 , parentToken(other->parentToken)
61 , ownedData(other->ownedData)
73 unregisterRecipient();
78 throw std::runtime_error(
"StateMachine: data is not yet known for scope '" +
scope->
id +
"'");
81void StateMachine::initiateBoundaryEvents(
Token* token) {
86 if ( activity->loopCharacteristics.has_value() &&
102 for (
auto node : activity->boundaryEvents ) {
104 initiateBoundaryEvent(token,node);
110 tokens.push_back( std::make_shared<Token>(
this,node,token->
status) );
111 auto createdToken =
tokens.back().get();
114 createdToken->advanceToEntered();
117void StateMachine::initiateEventSubprocesses(
Token* token) {
121 if ( !
data.has_value() ) {
122 throw std::runtime_error(
"StateMachine: required data at '" + eventSubProcess->id +
"' not yet available" );
128 pendingEventSubProcess->run(token->
status);
132void StateMachine::createMultiInstanceActivityTokens(
Token* token) {
134 assert( extensionElements !=
nullptr );
136 std::vector< std::map< const Model::Attribute*, std::optional<BPMNOS::number> > > valueMaps;
138 if ( extensionElements->loopCardinality.has_value() ) {
139 auto getLoopCardinality = [token,extensionElements]() -> std::optional<BPMNOS::number> {
140 if (!extensionElements->loopCardinality.value()->expression) {
143 return extensionElements->loopCardinality.value()->expression->execute(token->
status, *token->
data, token->
globals);
146 if (
auto loopCardinality = getLoopCardinality();
147 loopCardinality.has_value()
149 valueMaps.resize( (
size_t)loopCardinality.value() );
152 throw std::runtime_error(
"StateMachine: cannot determine cardinality for multi-instance activity '" + token->
node->
id +
"'" );
156 if ( extensionElements->messageDefinitions.size() ) {
157 if ( valueMaps.empty() ) {
158 valueMaps.resize(extensionElements->messageDefinitions.size());
160 else if ( valueMaps.size() != extensionElements->messageDefinitions.size() ) {
161 throw std::runtime_error(
"StateMachine: cardinality and number of messages inconsistent for multi-instance activity '" + token->
node->
id +
"'" );
165 if ( valueMaps.empty() ) {
166 throw std::runtime_error(
"StateMachine: no instances created for multi-instance activity '" + token->
node->
id +
"'" );
169 if ( extensionElements->loopIndex.has_value() && extensionElements->loopIndex.value()->expression ) {
171 if (
auto attribute = extensionElements->loopIndex.value()->expression->isAttribute() ) {
172 for (
size_t i = 0; i < valueMaps.size(); i++ ) {
173 valueMaps[i][attribute] = i + 1;
177 throw std::runtime_error(
"StateMachine: no attribute provided for loop index parameter of multi-instance activity '" + token->
node->
id +
"'" );
183 assert( activity->loopCharacteristics.has_value() );
189 Token* tokenCopy =
nullptr;
191 for (
auto valueMap : valueMaps ) {
196 tokens.push_back( std::make_shared<Token>( token ) );
200 if ( !
data.has_value() ) {
201 throw std::runtime_error(
"StateMachine: required data at '" + token->
node->
id +
"' not yet available" );
219 for (
auto [attribute,value] : valueMap ) {
220 tokens.back().get()->status[attribute->index] = value;
228 tokens.back().get()->awaitEntryEvent();
239 tokens.back().get()->awaitEntryEvent();
242 tokenCopy =
tokens.back().get();
254void StateMachine::deleteMultiInstanceActivityToken(
Token* token) {
263 auto& tokenAwaitingMultiInstanceExit =
const_cast<SystemState*
>(
systemState)->tokenAwaitingMultiInstanceExit;
264 auto it = tokenAwaitingMultiInstanceExit.find(token);
266 if ( it != tokenAwaitingMultiInstanceExit.end() ) {
267 auto waitingToken = it->second;
268 waitingToken->awaitEntryEvent();
269 tokenAwaitingMultiInstanceExit.erase(it);
282 if (
auto it1 = tokensAtActivityInstance.find(mainToken);
283 it1 != tokensAtActivityInstance.end()
285 if ( it1->second.empty() ) {
287 tokensAtActivityInstance.erase(it1);
289 if ( !activity->boundaryEvents.empty() ) {
291 deleteTokensAwaitingBoundaryEvent( mainToken );
295 if (
auto it2 = exitStatusAtActivityInstance.find(mainToken);
296 it2 != exitStatusAtActivityInstance.end()
300 exitStatusAtActivityInstance.erase(it2);
304 if ( mainToken->node->outgoing.empty() ) {
305 engine->commands.emplace_back(std::bind(&Token::advanceToDone,mainToken), mainToken);
308 engine->commands.emplace_back(std::bind(&Token::advanceToDeparting,mainToken), mainToken);
313 assert(!
"cannot find tokens created for multi instance activity");
323void StateMachine::deleteAdHocSubProcessToken(
Token* token) {
324 if (
tokens.size() > 1 ) {
344void StateMachine::deleteNonInterruptingEventSubProcess(
StateMachine* eventSubProcess) {
350void StateMachine::deleteCompensationEventSubProcess(
StateMachine* eventSubProcess) {
355void StateMachine::clearObsoleteTokens() {
356 for (
auto token :
tokens ) {
362void StateMachine::interruptActivity(
Token* token) {
367 if ( activity->loopCharacteristics.has_value() &&
373 for (
auto activeToken : it->second ) {
383 activeToken->withdraw();
392 deleteTokensAwaitingBoundaryEvent(token);
397void StateMachine::deleteTokensAwaitingBoundaryEvent(
Token* token) {
401 auto it = tokensAwaitingBoundaryEvent.find(token);
402 if ( it != tokensAwaitingBoundaryEvent.end() ) {
403 for (
auto waitingToken : it->second ) {
404 waitingToken->withdraw();
407 tokensAwaitingBoundaryEvent.erase(it);
411void StateMachine::registerRecipient() {
415 for (
auto& [message_ptr] : it->second ) {
416 if (
auto message = message_ptr.lock() ) {
425void StateMachine::unregisterRecipient() {
430 ( eventSubProcess && !eventSubProcess->startEvent->isInterrupting )
436 for (
auto& [message_ptr] : it->second ) {
437 if (
auto message = message_ptr.lock() ) {
451 assert( status.size() >= 1 );
452 assert(
data.size() >= 1 );
463 tokens.push_back( std::make_shared<Token>(
this,
nullptr,std::move(status)) );
472 tokens.push_back( std::make_shared<Token>(
this,startNode,std::move(status)) );
476 for (
auto token :
tokens ) {
481 if ( !values.has_value() ) {
482 throw std::runtime_error(
"StateMachine: status of event subprocess '" + token->
node->
parent->
id +
"' not known at initialization");
485 token->
status.insert(token->
status.end(), values.value().begin(), values.value().end());
487 if ( !startEvent->isInterrupting ) {
491 auto counter = ++context->instantiations[token->
node];
502 token->advanceFromCreated();
506void StateMachine::createChild(
Token* parent,
const BPMN::Scope* scope,
Values data, std::optional<BPMNOS::number> instance) {
513 std::shared_ptr<Token> compensationToken = std::make_shared<Token>(
this,compensateBoundaryEvent, status);
522 if ( !
data.has_value() ) {
523 throw std::runtime_error(
"StateMachine: required data at '" + eventSubProcess->
id +
"' not yet available" );
532void StateMachine::compensateActivity(
Token* token) {
535 assert( compensationNode !=
nullptr );
536 if (
auto compensationActivity = compensationNode->represents<
BPMN::Activity>() ) {
538 token->
node = compensationActivity;
542 if ( !
data.has_value() ) {
543 throw std::runtime_error(
"StateMachine: required data at '" + token->
node->
id +
"' not yet available" );
545 createChild( token,
scope, std::move(
data.value()) );
547 engine->
commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
551std::vector<Token*> StateMachine::createTokenCopies(
Token* token,
const std::vector<BPMN::SequenceFlow*>& sequenceFlows) {
552 std::vector<Token*> tokenCopies;
554 for ( [[maybe_unused]]
auto _ : sequenceFlows ) {
555 tokens.push_back( std::make_shared<Token>( token ) );
556 tokenCopies.push_back(
tokens.back().get());
560 for (
size_t i = 0; i < sequenceFlows.size(); i++ ) {
561 auto tokenCopy = tokenCopies[i];
563 engine->
commands.emplace_back(std::bind(&Token::advanceToDeparted,tokenCopy,sequenceFlows[i]), tokenCopy);
568void StateMachine::createMergedToken(
const BPMN::FlowNode* gateway) {
569 auto gatewayIt =
const_cast<SystemState*
>(
systemState)->tokensAwaitingGatewayActivation[
this].find(gateway);
570 auto& [key,arrivedTokens] = *gatewayIt;
573 std::shared_ptr<Token> mergedToken = std::make_shared<Token>(arrivedTokens);
576 for (
auto arrivedToken : arrivedTokens ) {
582 tokens.push_back(std::move(mergedToken));
585 auto token =
tokens.back().get();
587 engine->
commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
591void StateMachine::handleDivergingGateway(
Token* token) {
600 auto tokenCopies = createTokenCopies(token, token->
node->
outgoing);
604 for (
auto tokenCopy : tokenCopies ) {
605 tokenAtEventBasedGateway[tokenCopy] = token;
606 tokensAwaitingEvent[token].push_back(tokenCopy);
610 throw std::runtime_error(
"StateMachine: diverging gateway type of node '" + token->
node->
id +
"' not yet supported");
614void StateMachine::handleEventBasedGatewayActivation(
Token* token) {
615 auto tokenAtEventBasedGateway =
const_cast<SystemState*
>(
systemState)->tokenAtEventBasedGateway.at(token);
616 auto waitingTokens =
const_cast<SystemState*
>(
systemState)->tokensAwaitingEvent.at(tokenAtEventBasedGateway);
618 for (
auto waitingToken : waitingTokens ) {
619 if ( waitingToken != token ) {
620 waitingToken->withdraw();
629void StateMachine::handleEscalation(
Token* token) {
636 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
637 return eventSubProcess->startEvent->represents<BPMN::EscalationStartEvent>();
644 auto eventToken = it->get()->tokens.front().get();
646 eventToken->status = token->
status;
647 eventToken->advanceToCompleted();
655 activity->loopCharacteristics.has_value() &&
660 mainToken->status = token->
status;
661 handleEscalation(mainToken);
667 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[token];
668 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
670 eventToken->status = token->
status;
671 eventToken->advanceToCompleted();
683 engine->commands.emplace_back(std::bind(&StateMachine::handleEscalation,parent,
parentToken),
parentToken);
687void StateMachine::handleFailure(
Token* token) {
697 if ( activity->isForCompensation ) {
702 activity->loopCharacteristics.has_value() &&
708 mainToken->status = token->
status;
709 handleFailure(mainToken);
717 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[token];
718 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
720 eventToken->status = token->
status;
721 eventToken->advanceToCompleted();
729 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
730 return eventSubProcess->startEvent->represents<BPMN::ErrorStartEvent>();
734 auto eventToken = it->get()->tokens.front().get();
736 eventToken->status = token->
status;
738 clearObsoleteTokens();
739 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,eventToken), eventToken);
751 assert(
tokens.size() == 1);
752 assert(token =
tokens.front().get());
764Token* StateMachine::findTokenAwaitingErrorBoundaryEvent(
Token* activityToken) {
765 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[activityToken];
766 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
774void StateMachine::attemptGatewayActivation(
const BPMN::FlowNode* node) {
778 auto& [key,arrivedTokens] = *gatewayIt;
779 if ( arrivedTokens.size() == node->
incoming.size() ) {
782 engine->
commands.emplace_back(std::bind(&StateMachine::createMergedToken,
this,node),
this);
786 throw std::runtime_error(
"StateMachine: converging gateway type of node '" + node->
id +
"' not yet supported");
790void StateMachine::shutdown() {
804 engine->commands.emplace_back(std::bind(&StateMachine::deleteNonInterruptingEventSubProcess,context,
this),
this);
808 completeCompensationEventSubProcess();
824 unregisterRecipient();
832 parent->compensableSubProcesses.push_back(shared_from_this());
847 parent->interruptingEventSubProcess.reset();
853 auto token = context->parentToken;
854 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,token), token);
860void StateMachine::attemptShutdown() {
868 for (
auto& token :
tokens ) {
878 eventSubProcess->clearObsoleteTokens();
889 engine->
commands.emplace_back(std::bind(&StateMachine::shutdown,
this),
this);
902 auto it = std::find_if(
905 [&activity](
const std::shared_ptr<Token>& token) ->
bool {
907 return ( token.get()->node->as<BPMN::BoundaryEvent>()->attachedTo == activity );
911 result.push_back(*it);
923void StateMachine::advanceTokenWaitingForCompensation(
Token* waitingToken) {
930 tokens.emplace_back(waitingToken->shared_from_this());
939 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,waitingToken), waitingToken);
943 assert( waitingToken->
owner ==
this);
945 engine->commands.emplace_back(std::bind(&StateMachine::handleFailure,
this,waitingToken), waitingToken);
949 assert(!
"unexpected state of waiting token");
953void StateMachine::completeCompensationActivity(
Token* token) {
957 auto& tokenAwaitingCompensationActivity =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationActivity;
958 auto waitingToken = tokenAwaitingCompensationActivity.at(token);
959 const_cast<StateMachine*
>(waitingToken->
owner)->advanceTokenWaitingForCompensation( waitingToken );
960 tokenAwaitingCompensationActivity.erase(token);
967void StateMachine::completeCompensationEventSubProcess() {
968 auto& tokenAwaitingCompensationEventSubProcess =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationEventSubProcess;
969 auto waitingToken = tokenAwaitingCompensationEventSubProcess.at(
this);
970 const_cast<StateMachine*
>(waitingToken->
owner)->advanceTokenWaitingForCompensation( waitingToken );
971 tokenAwaitingCompensationEventSubProcess.erase(
this);
975 engine->commands.emplace_back(std::bind(&StateMachine::deleteCompensationEventSubProcess,context,
this),
this);
979void StateMachine::compensate(
Tokens compensations,
Token* waitingToken) {
983 auto& tokenAwaitingCompensationActivity =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationActivity;
984 auto& tokenAwaitingCompensationEventSubProcess =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationEventSubProcess;
986 auto it = compensations.rbegin();
988 auto compensationToken = it->get();
991 const_cast<StateMachine*
>(compensationToken->owner)->
tokens.push_back(std::move(*it));
994 tokens.push_back(std::move(*it));
1001 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,compensationToken), compensationToken);
1005 while ( it != compensations.rend() ) {
1009 tokenAwaitingCompensationEventSubProcess[
const_cast<StateMachine*
>(compensationToken->owner)] = it->get();
1012 tokenAwaitingCompensationActivity[compensationToken] = it->get();
1014 compensationToken = it->get();
1019 tokenAwaitingCompensationEventSubProcess[
const_cast<StateMachine*
>(compensationToken->owner)] = waitingToken;
1022 tokenAwaitingCompensationActivity[compensationToken] = waitingToken;
void deleteInstance(StateMachine *instance)
Method removing completed instance.
std::list< Command > commands
List of commands to be executed.
void notify(const Observable *observable) const
Represents a state machine for BPMN execution of a scope in the model.
const BPMN::Scope * scope
Pointer to the current scope.
StateMachine(const SystemState *systemState, const BPMN::Process *process, Values dataAttributes)
StateMachines pendingEventSubProcesses
Container with state machines of all inactive event subprocesses that may be triggered.
const SystemState * systemState
StateMachines compensationEventSubProcesses
Container with state machines created for a compensation event subprocesses of a child subprocess.
StateMachines nonInterruptingEventSubProcesses
Container with state machines of all active event subprocesses that are not interrupting.
const StateMachine * root
Pointer to the root state machine.
Values getData(const BPMN::Scope *scope)
std::shared_ptr< StateMachine > interruptingEventSubProcess
State machines representing an active event subprocess that is interrupting.
void run(Values status)
Create initial token and advance it.
std::optional< BPMNOS::number > instance
Numeric representation of instance id (TODO: can we const this?)
Tokens tokens
Container with all tokens within the scope of the state machine.
Tokens compensationTokens
Container with all tokens created for a compensation activity.
SharedValues data
Container holding references to all data attributes.
Tokens getCompensationTokens(const BPMN::Activity *activity=nullptr) const
Returns the compensation tokens for a given activity or for all activities.
A class representing the state that the execution or simulation of a given scenario is in.
std::unordered_map< Token *, std::vector< Token * > > tokensAtActivityInstance
Map holding all tokens representing an active instance of a multi-instance (or loop) activity for eac...
std::optional< BPMNOS::Values > getStatusAttributes(const StateMachine *root, const BPMN::Node *node) const
std::unordered_map< StateMachine *, auto_list< std::weak_ptr< Message > > > inbox
Map holding the undelivered correspondence associated with a state machine which will be withdrawn wh...
std::optional< BPMNOS::Values > getDataAttributes(const StateMachine *root, const BPMN::Node *node) const
std::unordered_map< long unsigned int, auto_list< std::weak_ptr< Message > > > unsent
Map holding unsent messages with recipient that isn't instantiated yet.
std::unordered_map< Token *, std::vector< BPMNOS::Values > > exitStatusAtActivityInstance
Map holding the exit status of all instances of a multi-instance (or loop) activity for each token wa...
Represents a token running through a (sub)process.
const BPMN::FlowNode * node
const StateMachine * owner
State machine owning the token.
std::shared_ptr< StateMachine > owned
State machine owned by the token.
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
Class holding extension elements representing execution data for nodes.
BPMNOS::number getContributionToObjective(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
Returns the contribution to the objective by the attributes declared for the node.
bool hasSequentialPerformer
Boolean indicating whether element has a performer with name "Sequential".
static constexpr char delimiters[]
Delimiters used for disambiguation of identifiers of non-interrupting event subprocesses and multi-in...
Node * compensatedBy
Pointer to compensation activity or compensation event sub-process.
@ MultiInstanceSequential
std::unique_ptr< ExtensionElements > extensionElements
std::string id
Id of element.
Base class for all boundary events attached to an Activity.
Scope * parent
Reference to the parent node.
T * as()
Casts the element to the specified type T.
T * represents()
Attempts to cast the element to the specified type T.
TypedStartEvent * startEvent
Base class for BPMN elements that may contain incoming and outgoing sequence flows.
std::vector< SequenceFlow * > incoming
Vector containing all incoming sequence flows of the node.
std::vector< SequenceFlow * > outgoing
Vector containing all outgoing sequence flows of the node.
Base class for BPMN elements that may contain a ChildNode elements.
std::vector< FlowNode * > startNodes
Vector containing all flow nodes that may start execution of the scope.
std::vector< EventSubProcess * > eventSubProcesses
Vector containing pointers to all event subprocesses within the scope of the nodes.
Base class for all start events with an event definition.
void erase_ptr(std::vector< std::unique_ptr< T > > &container, const T *elementPtr)
Erase a specific element from a vector of unique pointers.
std::vector< std::shared_ptr< Token > > Tokens
std::string to_string(number numericValue, const ValueType &type)
Converts a number to a string.
number to_number(const std::string &valueString, const ValueType &type)
Converts a string to a number.
BPMNOS::Values mergeValues(const std::vector< BPMNOS::Values > &valueSets)
static constexpr size_t Instance
static constexpr size_t Timestamp