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();
89 throw std::runtime_error(
"StateMachine: data is not yet known for scope '" +
scope->
id +
"'");
92void StateMachine::initiateBoundaryEvents(
Token* token) {
97 if ( activity->loopCharacteristics.has_value() &&
113 for (
auto node : activity->boundaryEvents ) {
115 initiateBoundaryEvent(token,node);
121 tokens.push_back( std::make_shared<Token>(
this,node,token->
status) );
122 auto createdToken =
tokens.back().get();
125 createdToken->advanceToEntered();
128void StateMachine::initiateEventSubprocesses(
Token* token) {
132 if ( !
data.has_value() ) {
133 throw std::runtime_error(
"StateMachine: required data at '" + eventSubProcess->id +
"' not yet available" );
139 pendingEventSubProcess->run(token->
status);
143void StateMachine::createMultiInstanceActivityTokens(
Token* token) {
145 assert( extensionElements !=
nullptr );
147 std::vector< std::map< const Model::Attribute*, std::optional<BPMNOS::number> > > valueMaps;
149 if ( extensionElements->loopCardinality.has_value() ) {
150 auto getLoopCardinality = [token,extensionElements]() -> std::optional<BPMNOS::number> {
151 if (!extensionElements->loopCardinality.value()->expression) {
154 return extensionElements->loopCardinality.value()->expression->execute(token->
status, *token->
data, token->
globals);
157 if (
auto loopCardinality = getLoopCardinality();
158 loopCardinality.has_value()
160 valueMaps.resize( (
size_t)loopCardinality.value() );
163 throw std::runtime_error(
"StateMachine: cannot determine cardinality for multi-instance activity '" + token->
node->
id +
"'" );
167 if ( extensionElements->messageDefinitions.size() ) {
168 if ( valueMaps.empty() ) {
169 valueMaps.resize(extensionElements->messageDefinitions.size());
171 else if ( valueMaps.size() != extensionElements->messageDefinitions.size() ) {
172 throw std::runtime_error(
"StateMachine: cardinality and number of messages inconsistent for multi-instance activity '" + token->
node->
id +
"'" );
176 if ( valueMaps.empty() ) {
177 throw std::runtime_error(
"StateMachine: no instances created for multi-instance activity '" + token->
node->
id +
"'" );
180 if ( extensionElements->loopIndex.has_value() && extensionElements->loopIndex.value()->expression ) {
182 if (
auto attribute = extensionElements->loopIndex.value()->expression->isAttribute() ) {
183 for (
size_t i = 0; i < valueMaps.size(); i++ ) {
184 valueMaps[i][attribute] = i + 1;
188 throw std::runtime_error(
"StateMachine: no attribute provided for loop index parameter of multi-instance activity '" + token->
node->
id +
"'" );
194 assert( activity->loopCharacteristics.has_value() );
200 Token* tokenCopy =
nullptr;
202 for (
auto valueMap : valueMaps ) {
207 tokens.push_back( std::make_shared<Token>( token ) );
211 if ( !
data.has_value() ) {
212 throw std::runtime_error(
"StateMachine: required data at '" + token->
node->
id +
"' not yet available" );
226 for (
auto [attribute,value] : valueMap ) {
227 tokens.back().get()->status[attribute->index] = value;
234 tokens.back().get()->awaitEntryEvent();
244 tokens.back().get()->awaitEntryEvent();
247 tokenCopy =
tokens.back().get();
259void StateMachine::deleteMultiInstanceActivityToken(
Token* token) {
268 auto& tokenAwaitingMultiInstanceExit =
const_cast<SystemState*
>(
systemState)->tokenAwaitingMultiInstanceExit;
269 auto it = tokenAwaitingMultiInstanceExit.find(token);
271 if ( it != tokenAwaitingMultiInstanceExit.end() ) {
272 auto waitingToken = it->second;
273 waitingToken->awaitEntryEvent();
274 tokenAwaitingMultiInstanceExit.erase(it);
287 if (
auto it1 = tokensAtActivityInstance.find(mainToken);
288 it1 != tokensAtActivityInstance.end()
290 if ( it1->second.empty() ) {
292 tokensAtActivityInstance.erase(it1);
294 if ( !activity->boundaryEvents.empty() ) {
296 deleteTokensAwaitingBoundaryEvent( mainToken );
300 if (
auto it2 = exitStatusAtActivityInstance.find(mainToken);
301 it2 != exitStatusAtActivityInstance.end()
305 exitStatusAtActivityInstance.erase(it2);
309 if ( mainToken->node->outgoing.empty() ) {
310 engine->commands.emplace_back(std::bind(&Token::advanceToDone,mainToken), mainToken);
313 engine->commands.emplace_back(std::bind(&Token::advanceToDeparting,mainToken), mainToken);
318 assert(!
"cannot find tokens created for multi instance activity");
328void StateMachine::deleteAdHocSubProcessToken(
Token* token) {
329 if (
tokens.size() > 1 ) {
349void StateMachine::deleteNonInterruptingEventSubProcess(
StateMachine* eventSubProcess) {
355void StateMachine::deleteCompensationEventSubProcess(
StateMachine* eventSubProcess) {
360void StateMachine::clearObsoleteTokens() {
361 for (
auto token :
tokens ) {
367void StateMachine::interruptActivity(
Token* token) {
372 if ( activity->loopCharacteristics.has_value() &&
378 for (
auto activeToken : it->second ) {
388 activeToken->withdraw();
397 deleteTokensAwaitingBoundaryEvent(token);
402void StateMachine::deleteTokensAwaitingBoundaryEvent(
Token* token) {
406 auto it = tokensAwaitingBoundaryEvent.find(token);
407 if ( it != tokensAwaitingBoundaryEvent.end() ) {
408 for (
auto waitingToken : it->second ) {
409 waitingToken->withdraw();
412 tokensAwaitingBoundaryEvent.erase(it);
416void StateMachine::registerRecipient() {
420 for (
auto& [message_ptr] : it->second ) {
421 if (
auto message = message_ptr.lock() ) {
430void StateMachine::unregisterRecipient() {
433 ( eventSubProcess && !eventSubProcess->startEvent->isInterrupting )
439 for (
auto& [message_ptr] : it->second ) {
440 if (
auto message = message_ptr.lock() ) {
454 assert( status.size() >= 1 );
455 assert(
data.size() >= 1 );
466 tokens.push_back( std::make_shared<Token>(
this,
nullptr,std::move(status)) );
475 tokens.push_back( std::make_shared<Token>(
this,startNode,std::move(status)) );
479 for (
auto token :
tokens ) {
490 if ( !values.has_value() ) {
491 throw std::runtime_error(
"StateMachine: status of event subprocess '" + token->
node->
parent->
id +
"' not known at initialization");
494 token->
status.insert(token->
status.end(), values.value().begin(), values.value().end());
496 if ( !startEvent->isInterrupting ) {
500 auto counter = ++context->instantiations[token->
node];
511 token->advanceFromCreated();
515void StateMachine::createChild(
Token* parent,
const BPMN::Scope* scope,
Values data, std::optional<BPMNOS::number> instance) {
522 std::shared_ptr<Token> compensationToken = std::make_shared<Token>(
this,compensateBoundaryEvent, status);
531 if ( !
data.has_value() ) {
532 throw std::runtime_error(
"StateMachine: required data at '" + eventSubProcess->
id +
"' not yet available" );
541void StateMachine::compensateActivity(
Token* token) {
544 assert( compensationNode !=
nullptr );
545 if (
auto compensationActivity = compensationNode->represents<
BPMN::Activity>() ) {
547 token->
node = compensationActivity;
551 if ( !
data.has_value() ) {
552 throw std::runtime_error(
"StateMachine: required data at '" + token->
node->
id +
"' not yet available" );
554 createChild( token,
scope, std::move(
data.value()) );
556 engine->
commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
560std::vector<Token*> StateMachine::createTokenCopies(
Token* token,
const std::vector<BPMN::SequenceFlow*>& sequenceFlows) {
561 std::vector<Token*> tokenCopies;
563 for ( [[maybe_unused]]
auto _ : sequenceFlows ) {
564 tokens.push_back( std::make_shared<Token>( token ) );
565 tokenCopies.push_back(
tokens.back().get());
569 for (
size_t i = 0; i < sequenceFlows.size(); i++ ) {
570 auto tokenCopy = tokenCopies[i];
572 engine->
commands.emplace_back(std::bind(&Token::advanceToDeparted,tokenCopy,sequenceFlows[i]), tokenCopy);
577void StateMachine::createMergedToken(
const BPMN::FlowNode* gateway) {
578 auto gatewayIt =
const_cast<SystemState*
>(
systemState)->tokensAwaitingGatewayActivation[
this].find(gateway);
579 auto& [key,arrivedTokens] = *gatewayIt;
582 std::shared_ptr<Token> mergedToken = std::make_shared<Token>(arrivedTokens);
585 for (
auto arrivedToken : arrivedTokens ) {
591 tokens.push_back(std::move(mergedToken));
594 auto token =
tokens.back().get();
596 engine->
commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
600void StateMachine::handleDivergingGateway(
Token* token) {
609 auto tokenCopies = createTokenCopies(token, token->
node->
outgoing);
613 for (
auto tokenCopy : tokenCopies ) {
614 tokenAtEventBasedGateway[tokenCopy] = token;
615 tokensAwaitingEvent[token].push_back(tokenCopy);
619 throw std::runtime_error(
"StateMachine: diverging gateway type of node '" + token->
node->
id +
"' not yet supported");
623void StateMachine::handleEventBasedGatewayActivation(
Token* token) {
624 auto tokenAtEventBasedGateway =
const_cast<SystemState*
>(
systemState)->tokenAtEventBasedGateway.at(token);
625 auto waitingTokens =
const_cast<SystemState*
>(
systemState)->tokensAwaitingEvent.at(tokenAtEventBasedGateway);
627 for (
auto waitingToken : waitingTokens ) {
628 if ( waitingToken != token ) {
629 waitingToken->withdraw();
638void StateMachine::handleEscalation(
Token* token) {
645 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
646 return eventSubProcess->startEvent->represents<BPMN::EscalationStartEvent>();
653 auto eventToken = it->get()->tokens.front().get();
655 eventToken->setStatus(token->
status);
656 eventToken->advanceToCompleted();
664 activity->loopCharacteristics.has_value() &&
669 mainToken->setStatus(token->
status);
670 handleEscalation(mainToken);
676 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[token];
677 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
679 eventToken->setStatus(token->
status);
680 eventToken->advanceToCompleted();
692 engine->commands.emplace_back(std::bind(&StateMachine::handleEscalation,parent,
parentToken),
parentToken);
696void StateMachine::handleFailure(
Token* token) {
706 if ( activity->isForCompensation ) {
711 activity->loopCharacteristics.has_value() &&
717 mainToken->setStatus(token->
status);
718 handleFailure(mainToken);
726 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[token];
727 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
729 eventToken->setStatus(token->
status);
730 eventToken->advanceToCompleted();
738 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
739 return eventSubProcess->startEvent->represents<BPMN::ErrorStartEvent>();
743 auto eventToken = it->get()->tokens.front().get();
745 eventToken->setStatus(token->
status);
747 clearObsoleteTokens();
748 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,eventToken), eventToken);
760 assert(
tokens.size() == 1);
761 assert(token =
tokens.front().get());
773Token* StateMachine::findTokenAwaitingErrorBoundaryEvent(
Token* activityToken) {
774 auto& tokensAwaitingBoundaryEvent =
const_cast<SystemState*
>(
systemState)->tokensAwaitingBoundaryEvent[activityToken];
775 for (
auto eventToken : tokensAwaitingBoundaryEvent) {
783void StateMachine::attemptGatewayActivation(
const BPMN::FlowNode* node) {
787 auto& [key,arrivedTokens] = *gatewayIt;
788 if ( arrivedTokens.size() == node->
incoming.size() ) {
791 engine->
commands.emplace_back(std::bind(&StateMachine::createMergedToken,
this,node),
this);
795 throw std::runtime_error(
"StateMachine: converging gateway type of node '" + node->
id +
"' not yet supported");
799void StateMachine::shutdown() {
813 engine->commands.emplace_back(std::bind(&StateMachine::deleteNonInterruptingEventSubProcess,context,
this),
this);
817 completeCompensationEventSubProcess();
833 unregisterRecipient();
841 parent->compensableSubProcesses.push_back(shared_from_this());
856 parent->interruptingEventSubProcess.reset();
862 auto token = context->parentToken;
863 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,token), token);
869void StateMachine::attemptShutdown() {
882 for (
auto& token :
tokens ) {
892 eventSubProcess->clearObsoleteTokens();
898 engine->
commands.emplace_back(std::bind(&StateMachine::shutdown,
this),
this);
911 auto it = std::find_if(
914 [&activity](
const std::shared_ptr<Token>& token) ->
bool {
916 return ( token.get()->node->as<BPMN::BoundaryEvent>()->attachedTo == activity );
920 result.push_back(*it);
932void StateMachine::advanceTokenWaitingForCompensation(
Token* waitingToken) {
939 tokens.emplace_back(waitingToken->shared_from_this());
948 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,waitingToken), waitingToken);
952 assert( waitingToken->
owner ==
this);
954 engine->commands.emplace_back(std::bind(&StateMachine::handleFailure,
this,waitingToken), waitingToken);
958 assert(!
"unexpected state of waiting token");
962void StateMachine::completeCompensationActivity(
Token* token) {
966 auto& tokenAwaitingCompensationActivity =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationActivity;
967 auto waitingToken = tokenAwaitingCompensationActivity.at(token);
968 const_cast<StateMachine*
>(waitingToken->
owner)->advanceTokenWaitingForCompensation( waitingToken );
969 tokenAwaitingCompensationActivity.erase(token);
976void StateMachine::completeCompensationEventSubProcess() {
977 auto& tokenAwaitingCompensationEventSubProcess =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationEventSubProcess;
978 auto waitingToken = tokenAwaitingCompensationEventSubProcess.at(
this);
979 const_cast<StateMachine*
>(waitingToken->
owner)->advanceTokenWaitingForCompensation( waitingToken );
980 tokenAwaitingCompensationEventSubProcess.erase(
this);
984 engine->commands.emplace_back(std::bind(&StateMachine::deleteCompensationEventSubProcess,context,
this),
this);
988void StateMachine::compensate(
Tokens compensations,
Token* waitingToken) {
992 auto& tokenAwaitingCompensationActivity =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationActivity;
993 auto& tokenAwaitingCompensationEventSubProcess =
const_cast<SystemState*
>(
systemState)->tokenAwaitingCompensationEventSubProcess;
995 auto it = compensations.rbegin();
997 auto compensationToken = it->get();
1000 const_cast<StateMachine*
>(compensationToken->owner)->
tokens.push_back(std::move(*it));
1003 tokens.push_back(std::move(*it));
1010 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,compensationToken), compensationToken);
1014 while ( it != compensations.rend() ) {
1018 tokenAwaitingCompensationEventSubProcess[
const_cast<StateMachine*
>(compensationToken->owner)] = it->get();
1021 tokenAwaitingCompensationActivity[compensationToken] = it->get();
1023 compensationToken = it->get();
1028 tokenAwaitingCompensationEventSubProcess[
const_cast<StateMachine*
>(compensationToken->owner)] = waitingToken;
1031 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.
void setStatus(const BPMNOS::Values &other)
Copies all elements except the instance id from other to status
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