BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
StateMachine.cpp
Go to the documentation of this file.
1#include "Engine.h"
2#include "StateMachine.h"
3#include "Token.h"
4#include "SystemState.h"
5#include "Event.h"
10#include "bpmn++.h"
11#include <cassert>
12#include <ranges>
13
14using namespace BPMNOS::Execution;
15
16StateMachine::StateMachine(const SystemState* systemState, const BPMN::Process* process, Values dataAttributes)
17 : systemState(systemState)
18 , process(process)
19 , scope(process)
20 , root(this)
21 , instance( dataAttributes.size() ? dataAttributes[BPMNOS::Model::ExtensionElements::Index::Instance] : -1 )
22 , parentToken(nullptr)
23 , ownedData(dataAttributes)
24 , data(SharedValues(ownedData))
25{
26 assert( instance.has_value() && instance.value() >= 0 );
28}
29
30StateMachine::StateMachine(const SystemState* systemState, const BPMN::Scope* scope, Token* parentToken, Values dataAttributes, std::optional<BPMNOS::number> instance )
31 : systemState(systemState)
32 , process(parentToken->owner->process)
33 , scope(scope)
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)
38 , data(SharedValues(parentToken->owner->data,ownedData))
39{
40
42/*
43std::cerr << "child StateMachine(" << scope->id << "/" << this << " @ " << parentToken << ")" << " owned by: " << parentToken->owner << std::endl;
44//std::cerr << data.size() << "[" <<BPMNOS::Model::ExtensionElements::Index::Instance <<"] = " << data[BPMNOS::Model::ExtensionElements::Index::Instance].value_or(-1) << "/" << ( data.size() ? (int)data[BPMNOS::Model::ExtensionElements::Index::Instance].get().value_or(-1) : -1 ) << "/" << instance << "/" << this->instance <<std::endl;
45std::cerr << "data: ";
46for ( auto& attribute : data ) {
47std::cerr << (int)attribute.get().value() << ", ";
48}
49std::cerr << std::endl;
50*/
51 assert( this->instance.has_value() && this->instance.value() >= 0 );
52}
53
55 : systemState(other->systemState)
56 , process(other->process)
57 , scope(other->scope)
58 , root(other->root)
59 , instance( other->instance )
60 , parentToken(other->parentToken)
61 , ownedData(other->ownedData)
62 , data(SharedValues(parentToken->owner->data,ownedData))
63{
64//std::cerr << "oStateMachine(" << scope->id << "/" << this << " @ " << parentToken << ")" << " owned by :" << parentToken->owner << std::endl;
66}
67
69//std::cerr << "~StateMachine(" << scope->id << "/" << this << " @ " << parentToken << ")" << std::endl;
70 const_cast<SystemState*>(systemState)->tokensAwaitingGatewayActivation.erase(this);
71 const_cast<SystemState*>(systemState)->tokenAwaitingCompensationEventSubProcess.erase(this);
72 if ( scope ) {
73 unregisterRecipient();
74 }
75}
76
78/*
79 if ( systemState->assumedTime ) {
80 return systemState->scenario->getAnticipatedData(token->node, token->status, systemState->currentTime );
81 }
82 else {
83 auto values = systemState->scenario->getKnownData(token->node, token->status, systemState->currentTime );
84 if ( values ) {
85 return values.value();
86 }
87 }
88*/
89 throw std::runtime_error("StateMachine: data is not yet known for scope '" + scope->id + "'");
90}
91
92void StateMachine::initiateBoundaryEvents(Token* token) {
93//std::cerr << "initiateBoundaryEvents: " << token->node->id << std::endl;
94 auto activity = token->node->as<BPMN::Activity>();
95 assert( activity);
96
97 if ( activity->loopCharacteristics.has_value() &&
98 activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard
99 ) {
100 // determine main token waiting for all instances
101 auto mainToken = const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.at(token);
102
103 if ( const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent[mainToken].empty() ) {
104 // create boundary event tokens for the main token
105 token = mainToken;
106 }
107 else {
108 // tokens at boundary events have already been created
109 return;
110 }
111 }
112
113 for ( auto node : activity->boundaryEvents ) {
114 if ( !node->represents<BPMN::CompensateBoundaryEvent>() ) {
115 initiateBoundaryEvent(token,node);
116 }
117 }
118}
119
120void StateMachine::initiateBoundaryEvent(Token* token, const BPMN::FlowNode* node) {
121 tokens.push_back( std::make_shared<Token>(this,node,token->status) );
122 auto createdToken = tokens.back().get();
123 const_cast<SystemState*>(systemState)->tokenAssociatedToBoundaryEventToken[createdToken] = token;
124 const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent[token].push_back(createdToken);
125 createdToken->advanceToEntered();
126}
127
128void StateMachine::initiateEventSubprocesses(Token* token) {
129//std::cerr << "initiate " << scope->eventSubProcesses.size() << " eventSubprocesses for token at " << (token->node ? token->node->id : process->id ) << "/" << parentToken << "/" << token << " owned by " << token->owner << std::endl;
130 for ( auto& eventSubProcess : scope->eventSubProcesses ) {
131 auto data = systemState->getDataAttributes(root,eventSubProcess);
132 if ( !data.has_value() ) {
133 throw std::runtime_error("StateMachine: required data at '" + eventSubProcess->id +"' not yet available" );
134 }
135 pendingEventSubProcesses.push_back(std::make_shared<StateMachine>( systemState, eventSubProcess, parentToken, std::move(data.value()) ) );
136 auto pendingEventSubProcess = pendingEventSubProcesses.back().get();
137//std::cerr << "Pending event subprocess has parent: " << pendingEventSubProcess->parentToken->jsonify().dump() << std::endl;
138
139 pendingEventSubProcess->run(token->status);
140 }
141}
142
143void StateMachine::createMultiInstanceActivityTokens(Token* token) {
144 auto extensionElements = token->node->extensionElements->represents<const BPMNOS::Model::ExtensionElements>();
145 assert( extensionElements != nullptr );
146
147 std::vector< std::map< const Model::Attribute*, std::optional<BPMNOS::number> > > valueMaps;
148
149 if ( extensionElements->loopCardinality.has_value() ) {
150 auto getLoopCardinality = [token,extensionElements]() -> std::optional<BPMNOS::number> {
151 if (!extensionElements->loopCardinality.value()->expression) {
152 return std::nullopt;
153 }
154 return extensionElements->loopCardinality.value()->expression->execute(token->status, *token->data, token->globals);
155 };
156 // use provided cardinality to determine number of tokens
157 if ( auto loopCardinality = getLoopCardinality();
158 loopCardinality.has_value()
159 ) {
160 valueMaps.resize( (size_t)loopCardinality.value() );
161 }
162 else {
163 throw std::runtime_error("StateMachine: cannot determine cardinality for multi-instance activity '" + token->node->id +"'" );
164 }
165 }
166
167 if ( extensionElements->messageDefinitions.size() ) {
168 if ( valueMaps.empty() ) {
169 valueMaps.resize(extensionElements->messageDefinitions.size());
170 }
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 +"'" );
173 }
174 }
175
176 if ( valueMaps.empty() ) {
177 throw std::runtime_error("StateMachine: no instances created for multi-instance activity '" + token->node->id +"'" );
178 }
179
180 if ( extensionElements->loopIndex.has_value() && extensionElements->loopIndex.value()->expression ) {
181 // set value of loop index attribute for each instance
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;
185 }
186 }
187 else {
188 throw std::runtime_error("StateMachine: no attribute provided for loop index parameter of multi-instance activity '" + token->node->id +"'" );
189 }
190 }
191
192 auto activity = token->node->represents<BPMN::Activity>();
193 assert( activity );
194 assert( activity->loopCharacteristics.has_value() );
195
196 // create token copies
199
200 Token* tokenCopy = nullptr;
201 size_t counter = 0;
202 for ( auto valueMap : valueMaps ) {
203 counter++;
204 // disambiguate instance id
206
207 tokens.push_back( std::make_shared<Token>( token ) );
208 if ( auto scope = token->node->represents<BPMN::Scope>() ) {
209 // create state machine for each multi-instance subprocess
210 auto data = systemState->getDataAttributes(root,token->node);
211 if ( !data.has_value() ) {
212 throw std::runtime_error("StateMachine: required data at '" + token->node->id +"' not yet available" );
213 }
214
215 // create child state machine with disambiguated instance identifier
216 createChild( tokens.back().get(), scope, std::move(data.value()), BPMNOS::to_number(instanceId,BPMNOS::ValueType::STRING) );
217 }
218 else {
219 // create child fake state machine with disambiguated instance identifier
220 createChild( tokens.back().get(), nullptr, {}, BPMNOS::to_number(instanceId,BPMNOS::ValueType::STRING) );
221 // ensure that data is set appropriately
222 tokens.back()->data = &tokens.back()->owned->data;
223 }
224
225 // update status of token copy
226 for ( auto [attribute,value] : valueMap ) {
227 tokens.back().get()->status[attribute->index] = value;
228 }
229
230 if ( activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::MultiInstanceSequential ) {
231 if ( !tokenCopy ) {
232 // for sequential multi-instance activities only the first token awaits entry event
233 tokens.back().get()->update(Token::State::READY);
234 tokens.back().get()->awaitEntryEvent();
235 }
236 else {
237 // newly created tokens have to wait for previous token copy
238 const_cast<SystemState*>(systemState)->tokenAwaitingMultiInstanceExit[tokenCopy] = tokens.back().get();
239 }
240 }
241 else if ( activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::MultiInstanceParallel ) {
242 // for parallel multi-instance activities all new tokens await entry event
243 tokens.back().get()->update(Token::State::READY);
244 tokens.back().get()->awaitEntryEvent();
245 }
246
247 tokenCopy = tokens.back().get();
248 const_cast<SystemState*>(systemState)->tokensAtActivityInstance[token].push_back(tokenCopy);
249 const_cast<SystemState*>(systemState)->exitStatusAtActivityInstance[token] = {};
250 const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity[tokenCopy] = token;
251 }
252
253 assert( tokenCopy );
254
255 // change state of original token
256 token->update(Token::State::WAITING);
257}
258
259void StateMachine::deleteMultiInstanceActivityToken(Token* token) {
260 auto engine = const_cast<Engine*>(systemState->engine);
261 auto mainToken = const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.at(token);
262
263 auto activity = token->node->represents<BPMN::Activity>();
264 assert( activity );
265
266 if ( activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::MultiInstanceSequential ) {
267 // advance next token for sequential multi-instance activity
268 auto& tokenAwaitingMultiInstanceExit = const_cast<SystemState*>(systemState)->tokenAwaitingMultiInstanceExit;
269 auto it = tokenAwaitingMultiInstanceExit.find(token);
270
271 if ( it != tokenAwaitingMultiInstanceExit.end() ) {
272 auto waitingToken = it->second;
273 waitingToken->awaitEntryEvent();
274 tokenAwaitingMultiInstanceExit.erase(it);
275 }
276 }
277
278 // record exit status
279 const_cast<SystemState*>(systemState)->exitStatusAtActivityInstance[mainToken].push_back(token->status);
280 // remove token
281 const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.erase(token);
282 erase_ptr<Token>(const_cast<SystemState*>(systemState)->tokensAtActivityInstance[mainToken],token);
284
285 // advance main token when last multi-instance token exited
286 auto& tokensAtActivityInstance = const_cast<SystemState*>(systemState)->tokensAtActivityInstance;
287 if ( auto it1 = tokensAtActivityInstance.find(mainToken);
288 it1 != tokensAtActivityInstance.end()
289 ) {
290 if ( it1->second.empty() ) {
291 // last multi-instance token exited
292 tokensAtActivityInstance.erase(it1);
293
294 if ( !activity->boundaryEvents.empty() ) {
295 // remove tokens at boundary events
296 deleteTokensAwaitingBoundaryEvent( mainToken );
297 }
298
299 auto& exitStatusAtActivityInstance = const_cast<SystemState*>(systemState)->exitStatusAtActivityInstance;
300 if ( auto it2 = exitStatusAtActivityInstance.find(mainToken);
301 it2 != exitStatusAtActivityInstance.end()
302 ) {
303 // merge status
304 mainToken->status = BPMNOS::mergeValues(it2->second);
305 exitStatusAtActivityInstance.erase(it2);
306 }
307
308 // advance main token
309 if ( mainToken->node->outgoing.empty() ) {
310 engine->commands.emplace_back(std::bind(&Token::advanceToDone,mainToken), mainToken);
311 }
312 else {
313 engine->commands.emplace_back(std::bind(&Token::advanceToDeparting,mainToken), mainToken);
314 }
315 }
316 }
317 else {
318 assert(!"cannot find tokens created for multi instance activity");
319 }
320}
321// TODO: handle failure events
322// TODO: handle single-instance compensations
323// TODO: handle parallel compensations
324// TODO: set value from csv input
325// TODO: type vector (must be immutable)
326// TODO: check conditions for immutable
327
328void StateMachine::deleteAdHocSubProcessToken(Token* token) {
329 if ( tokens.size() > 1 ) {
331 }
332 else {
333 attemptShutdown();
334 }
335}
336
337/*
338void StateMachine::deleteChild(StateMachine* child) {
339//std::cerr << "delete child '" << child->scope->id << "' of '" << scope->id << "'" << std::endl;
340 if ( child->scope->represents<BPMN::SubProcess>() ) {
341// erase_ptr<StateMachine>(subProcesses, child); /// TODO: check
342 }
343 else {
344 interruptingEventSubProcess.reset();
345 }
346}
347*/
348
349void StateMachine::deleteNonInterruptingEventSubProcess(StateMachine* eventSubProcess) {
350//std::cerr << "deleteNonInterruptingEventSubProcess" << std::endl;
352 attemptShutdown();
353}
354
355void StateMachine::deleteCompensationEventSubProcess(StateMachine* eventSubProcess) {
356//std::cerr << compensationEventSubProcesses.size() << "deleteCompensationEventSubProcess: " << scope->id << "/" << eventSubProcess->scope->id << "/" << this << std::endl;
358}
359
360void StateMachine::clearObsoleteTokens() {
361 for ( auto token : tokens ) {
362 token->withdraw();
363 }
364 tokens.clear();
365}
366
367void StateMachine::interruptActivity(Token* token) {
368//std::cerr << "interrupt activity " << token->node->id << std::endl;
369 auto activity = token->node->represents<BPMN::Activity>();
370 assert( activity );
371
372 if ( activity->loopCharacteristics.has_value() &&
373 activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard
374 ) {
375 // withdraw all tokens for multi-instance activity
376 auto it = const_cast<SystemState*>(systemState)->tokensAtActivityInstance.find(token);
377 assert ( it != const_cast<SystemState*>(systemState)->tokensAtActivityInstance.end() );
378 for ( auto activeToken : it->second ) {
379//std::cerr << "withdraw token at " << activeToken->node->id << "/" << activeToken << std::endl;
380 if ( activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::MultiInstanceSequential ) {
381 const_cast<SystemState*>(systemState)->tokenAwaitingMultiInstanceExit.erase(activeToken);
382 }
383
384 const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.erase(activeToken);
385 if ( activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::MultiInstanceParallel
386 || activeToken->state != Token::State::READY
387 ) {
388 activeToken->withdraw();
389 }
390 erase_ptr<Token>(tokens, activeToken);
391 }
392 const_cast<SystemState*>(systemState)->tokensAtActivityInstance.erase(it);
393
394 // remove all exit status for multi-instance activity
395 const_cast<SystemState*>(systemState)->exitStatusAtActivityInstance.erase(token);
396 }
397 deleteTokensAwaitingBoundaryEvent(token);
398 token->withdraw();
399 erase_ptr<Token>(tokens, token);
400}
401
402void StateMachine::deleteTokensAwaitingBoundaryEvent(Token* token) {
403//std::cerr << "deleteTokensAwaitingBoundaryEvent " << token->node->id << std::endl;
404 // delete all tokens awaiting boundary event
405 auto& tokensAwaitingBoundaryEvent = const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent;
406 auto it = tokensAwaitingBoundaryEvent.find(token);
407 if ( it != tokensAwaitingBoundaryEvent.end() ) {
408 for ( auto waitingToken : it->second ) {
409 waitingToken->withdraw();
410 erase_ptr<Token>(tokens, waitingToken);
411 }
412 tokensAwaitingBoundaryEvent.erase(it);
413 }
414}
415
416void StateMachine::registerRecipient() {
417 if ( auto it = const_cast<SystemState*>(systemState)->unsent.find((long unsigned int)instance.value());
418 it != const_cast<SystemState*>(systemState)->unsent.end()
419 ) {
420 for ( auto& [message_ptr] : it->second ) {
421 if ( auto message = message_ptr.lock() ) {
422 const_cast<SystemState*>(systemState)->inbox[this].emplace_back(message->weak_from_this());
423 const_cast<SystemState*>(systemState)->outbox[message->origin].emplace_back(message->weak_from_this());
424 }
425 }
426 const_cast<SystemState*>(systemState)->unsent.erase(it);
427 }
428}
429
430void StateMachine::unregisterRecipient() {
431 if ( auto eventSubProcess = scope->represents<BPMN::EventSubProcess>();
432 !parentToken ||
433 ( eventSubProcess && !eventSubProcess->startEvent->isInterrupting )
434 ) {
435 // delete all messages directed to state machine
436 if ( auto it = const_cast<SystemState*>(systemState)->inbox.find(this);
437 it != const_cast<SystemState*>(systemState)->inbox.end()
438 ) {
439 for ( auto& [message_ptr] : it->second ) {
440 if ( auto message = message_ptr.lock() ) {
441 // withdraw message
442 message->state = Message::State::WITHDRAWN;
443 systemState->engine->notify(message.get());
444 erase_ptr(const_cast<SystemState*>(systemState)->messages, message.get());
445 }
446 }
447 const_cast<SystemState*>(systemState)->inbox.erase(it);
448 }
449 }
450}
451
452
454 assert( status.size() >= 1 );
455 assert( data.size() >= 1 );
456 assert( data[BPMNOS::Model::ExtensionElements::Index::Instance].get().has_value() );
457 assert( status[BPMNOS::Model::ExtensionElements::Index::Timestamp].has_value() );
458
459//std::cerr << "Run " << scope->id << "/" << this << "/" << parentToken << std::endl;
460 if ( !parentToken ) {
461 // state machine without parent token represents a process
462//std::cerr << "Start process " << process->id << std::endl;
463 auto instanceId = (long unsigned int)data[BPMNOS::Model::ExtensionElements::Index::Instance].get().value();
464 const_cast<SystemState*>(systemState)->archive[ instanceId ] = weak_from_this();
465
466 tokens.push_back( std::make_shared<Token>(this,nullptr,std::move(status)) );
467 registerRecipient();
468
469 // create child that will own tokens flowing through the process
470 createChild(tokens.back().get(),scope, {});
471
472 }
473 else {
474 for ( auto startNode : scope->startNodes ) {
475 tokens.push_back( std::make_shared<Token>(this,startNode,std::move(status)) );
476 }
477 }
478
479 for ( auto token : tokens ) {
480 if ( token->node ) {
481 if ( auto startEvent = token->node->represents<BPMN::TypedStartEvent>() ) {
482 // get new attribute values
483 auto values = systemState->getStatusAttributes( root, token->node->parent );
484/*
485 std::optional<BPMNOS::Values> values = ( systemState->assumedTime.has_value() ?
486 systemState->scenario->getAnticipatedValues(token->node->parent, token->status, systemState->currentTime ) :
487 systemState->scenario->getKnownValues(token->node->parent, token->status, systemState->currentTime )
488 );
489*/
490 if ( !values.has_value() ) {
491 throw std::runtime_error("StateMachine: status of event subprocess '" + token->node->parent->id + "' not known at initialization");
492 }
493
494 token->status.insert(token->status.end(), values.value().begin(), values.value().end());
495
496 if ( !startEvent->isInterrupting ) {
497 // token instantiates non-interrupting event subprocess
498 // get instantiation counter from context
499 auto context = const_cast<StateMachine*>(parentToken->owned.get());
500 auto counter = ++context->instantiations[token->node];
501 // disambiguate instance id
504 const_cast<SystemState*>(systemState)->archive[ (long unsigned int)data[BPMNOS::Model::ExtensionElements::Index::Instance].get().value() ] = weak_from_this();
505 registerRecipient();
506 }
507 }
508//std::cerr << "Initial token: >" << token->jsonify().dump() << "<" << std::endl;
509 }
510 // advance token
511 token->advanceFromCreated();
512 }
513}
514
515void StateMachine::createChild(Token* parent, const BPMN::Scope* scope, Values data, std::optional<BPMNOS::number> instance) {
516//std::cerr << "Create child from " << this << std::endl;
517 parent->owned = std::make_shared<StateMachine>(systemState, scope, parent, std::move(data), instance.value_or( (*parent->data)[BPMNOS::Model::ExtensionElements::Index::Instance].get().value() ) );
518// parent->owned->run(parent->status);
519}
520
521void StateMachine::createCompensationTokenForBoundaryEvent(const BPMN::BoundaryEvent* compensateBoundaryEvent, BPMNOS::Values status) {
522 std::shared_ptr<Token> compensationToken = std::make_shared<Token>(this,compensateBoundaryEvent, status);
523 compensationToken->update(Token::State::BUSY);
524 compensationTokens.push_back(std::move(compensationToken));
525}
526
527void StateMachine::createCompensationEventSubProcess(const BPMN::EventSubProcess* eventSubProcess, BPMNOS::Values status) {
528//std::cerr << "createCompensationEventSubProcess: " << eventSubProcess->id << "/" << scope->id << "/" << parentToken->owner->scope->id <<std::endl;
529 // create state machine for compensation event subprocess
530 auto data = systemState->getDataAttributes(root,eventSubProcess);
531 if ( !data.has_value() ) {
532 throw std::runtime_error("StateMachine: required data at '" + eventSubProcess->id +"' not yet available" );
533 }
534 compensationEventSubProcesses.push_back(std::make_shared<StateMachine>( systemState, eventSubProcess, parentToken, std::move(data.value()) ) );
535 // create token at start event of compensation event subprocess
536 std::shared_ptr<Token> compensationToken = std::make_shared<Token>(compensationEventSubProcesses.back().get(), eventSubProcess->startEvent, status );
537 compensationToken->update(Token::State::BUSY);
538 compensationTokens.push_back(std::move(compensationToken));
539}
540
541void StateMachine::compensateActivity(Token* token) {
542 auto compensationNode = token->node->as<BPMN::BoundaryEvent>()->attachedTo->compensatedBy;
543//std::cerr << "compensationNode: " << compensationNode->id << std::endl;
544 assert( compensationNode != nullptr );
545 if ( auto compensationActivity = compensationNode->represents<BPMN::Activity>() ) {
546 // move token to compensation activity
547 token->node = compensationActivity;
548 auto engine = const_cast<Engine*>(systemState->engine);
549 if ( auto scope = token->node->represents<BPMN::Scope>() ) {
551 if ( !data.has_value() ) {
552 throw std::runtime_error("StateMachine: required data at '" + token->node->id +"' not yet available" );
553 }
554 createChild( token, scope, std::move(data.value()) );
555 }
556 engine->commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
557 }
558}
559
560std::vector<Token*> StateMachine::createTokenCopies(Token* token, const std::vector<BPMN::SequenceFlow*>& sequenceFlows) {
561 std::vector<Token*> tokenCopies;
562 // create a token copy for each new destination
563 for ( [[maybe_unused]] auto _ : sequenceFlows ) {
564 tokens.push_back( std::make_shared<Token>( token ) );
565 tokenCopies.push_back(tokens.back().get());
566 }
567
568 // advance all token copies
569 for (size_t i = 0; i < sequenceFlows.size(); i++ ) {
570 auto tokenCopy = tokenCopies[i];
571 auto engine = const_cast<Engine*>(systemState->engine);
572 engine->commands.emplace_back(std::bind(&Token::advanceToDeparted,tokenCopy,sequenceFlows[i]), tokenCopy);
573 }
574 return tokenCopies;
575}
576
577void StateMachine::createMergedToken(const BPMN::FlowNode* gateway) {
578 auto gatewayIt = const_cast<SystemState*>(systemState)->tokensAwaitingGatewayActivation[this].find(gateway);
579 auto& [key,arrivedTokens] = *gatewayIt;
580
581 // create merged token
582 std::shared_ptr<Token> mergedToken = std::make_shared<Token>(arrivedTokens);
583
584 // remove tokens
585 for ( auto arrivedToken : arrivedTokens ) {
586 erase_ptr<Token>(tokens,arrivedToken);
587 }
588 const_cast<SystemState*>(systemState)->tokensAwaitingGatewayActivation[this].erase(gatewayIt);
589
590 // add merged token
591 tokens.push_back(std::move(mergedToken));
592
593 // advance merged token
594 auto token = tokens.back().get();
595 auto engine = const_cast<Engine*>(systemState->engine);
596 engine->commands.emplace_back(std::bind(&Token::advanceToEntered,token), token);
597}
598
599
600void StateMachine::handleDivergingGateway(Token* token) {
601 if ( token->node->represents<BPMN::ParallelGateway>() ) {
602 // create token copies and advance them
603 createTokenCopies(token, token->node->outgoing);
604 // remove original token
606 }
607 else if ( token->node->represents<BPMN::EventBasedGateway>() ) {
608 // create token copies and advance them
609 auto tokenCopies = createTokenCopies(token, token->node->outgoing);
610 auto& tokenAtEventBasedGateway = const_cast<SystemState*>(systemState)->tokenAtEventBasedGateway;
611 auto& tokensAwaitingEvent = const_cast<SystemState*>(systemState)->tokensAwaitingEvent;
612
613 for ( auto tokenCopy : tokenCopies ) {
614 tokenAtEventBasedGateway[tokenCopy] = token;
615 tokensAwaitingEvent[token].push_back(tokenCopy);
616 }
617 }
618 else {
619 throw std::runtime_error("StateMachine: diverging gateway type of node '" + token->node->id + "' not yet supported");
620 }
621}
622
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);
626 // remove all other waiting tokens
627 for ( auto waitingToken : waitingTokens ) {
628 if ( waitingToken != token ) {
629 waitingToken->withdraw();
630 erase_ptr<Token>(tokens,waitingToken);
631 }
632 }
633 // remove token at event-based gateway
634 tokenAtEventBasedGateway->update(Token::State::COMPLETED);
635 erase_ptr<Token>(tokens,tokenAtEventBasedGateway);
636}
637
638void StateMachine::handleEscalation(Token* token) {
639//std::cerr << "handleEscalation " << (token->node ? token->node->id : process->id ) << std::endl;
640 if ( !parentToken ) {
641 return;
642 }
643
644 auto it = std::find_if(pendingEventSubProcesses.begin(), pendingEventSubProcesses.end(), [](std::shared_ptr<StateMachine>& stateMachine) {
645 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
646 return eventSubProcess->startEvent->represents<BPMN::EscalationStartEvent>();
647 });
648
649 auto engine = const_cast<Engine*>(systemState->engine);
650
651 if ( it != pendingEventSubProcesses.end() ) {
652 // trigger event subprocess
653 auto eventToken = it->get()->tokens.front().get();
654//std::cerr << "found event-subprocess catching escalation:" << eventToken << "/" << eventToken->owner << std::endl;
655 eventToken->setStatus(token->status);
656 eventToken->advanceToCompleted();
657
658 return;
659 }
660
661 if ( auto activity = token->node->represents<BPMN::Activity>();
662 activity &&
663 token->state != Token::State::WAITING &&
664 activity->loopCharacteristics.has_value() &&
665 activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard
666 ) {
667 // handle failure at main token waiting for all instances
668 auto mainToken = const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.at(token);
669 mainToken->setStatus(token->status);
670 handleEscalation(mainToken);
671 return;
672 }
673
674 // find escalation boundary event
675 if ( token->node ) {
676 auto& tokensAwaitingBoundaryEvent = const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent[token];
677 for ( auto eventToken : tokensAwaitingBoundaryEvent) {
678 if ( eventToken->node->represents<BPMN::EscalationBoundaryEvent>() ) {
679 eventToken->setStatus(token->status);
680 eventToken->advanceToCompleted();
681 return;
682 }
683 }
684 }
685
686 // update status of parent token with that of current token
688 parentToken->update(parentToken->state);
689
690//std::cerr << "bubbble up escalation" << std::endl;
691 auto parent = const_cast<StateMachine*>(parentToken->owner);
692 engine->commands.emplace_back(std::bind(&StateMachine::handleEscalation,parent,parentToken), parentToken);
693
694}
695
696void StateMachine::handleFailure(Token* token) {
697//std::cerr << scope->id << " handles failure at " << (token->node ? token->node->id : process->id ) << "/" << parentToken << "/" << token << std::endl;
698 auto engine = const_cast<Engine*>(systemState->engine);
699
700// assert( !token->owned );
701
702//std::cerr << "check whether failure is caught" << std::endl;
703
704 if ( token->node ) {
705 if ( auto activity = token->node->represents<BPMN::Activity>() ) {
706 if ( activity->isForCompensation ) {
707 // compensation activity failed, clear all other compensations
708 compensationTokens.clear();
709 }
710 else if ( token->state != Token::State::WAITING &&
711 activity->loopCharacteristics.has_value() &&
712 activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard
713 ) {
714 // handle failure at main token waiting for all instances
715 auto mainToken = const_cast<SystemState*>(systemState)->tokenAtMultiInstanceActivity.at(token);
716//std::cerr << "handle failure at main token waiting for all instances " << std::endl;
717 mainToken->setStatus(token->status);
718 handleFailure(mainToken);
719 return;
720 }
721 }
722 }
723
724 // find error boundary event at token node
725 if ( token->node ) {
726 auto& tokensAwaitingBoundaryEvent = const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent[token];
727 for ( auto eventToken : tokensAwaitingBoundaryEvent) {
728 if ( eventToken->node->represents<BPMN::ErrorBoundaryEvent>() ) {
729 eventToken->setStatus(token->status);
730 eventToken->advanceToCompleted();
731 return;
732 }
733 }
734 }
735
736 // find event-subprocess catching error
737 auto it = std::find_if(pendingEventSubProcesses.begin(), pendingEventSubProcesses.end(), [](std::shared_ptr<StateMachine>& stateMachine) {
738 auto eventSubProcess = stateMachine->scope->as<BPMN::EventSubProcess>();
739 return eventSubProcess->startEvent->represents<BPMN::ErrorStartEvent>();
740 });
741 if ( it != pendingEventSubProcesses.end() ) {
742 // trigger event subprocess
743 auto eventToken = it->get()->tokens.front().get();
744 // update status of event token with that of current token
745 eventToken->setStatus(token->status);
746 // remove all tokens
747 clearObsoleteTokens();
748 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,eventToken), eventToken);
749
750 return;
751 }
752
753 // failure is not caught
754//std::cerr << "uncaught failure" << std::endl;
755
756
757 if ( !parentToken ) {
758//std::cerr << "process has failed" << std::endl;
759 // failure at process before state machine containing flow elements has been created
760 assert(tokens.size() == 1);
761 assert(token = tokens.front().get());
762 engine->commands.emplace_back(std::bind(&Engine::deleteInstance,engine,this), this);
763 return;
764 }
765
766 // bubble up error
767
768 // update status of parent token with that of current token
770 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,parentToken), parentToken);
771}
772
773Token* StateMachine::findTokenAwaitingErrorBoundaryEvent(Token* activityToken) {
774 auto& tokensAwaitingBoundaryEvent = const_cast<SystemState*>(systemState)->tokensAwaitingBoundaryEvent[activityToken];
775 for ( auto eventToken : tokensAwaitingBoundaryEvent) {
776 if ( eventToken->node->represents<BPMN::ErrorBoundaryEvent>() ) {
777 return eventToken;
778 }
779 }
780 return nullptr;
781}
782
783void StateMachine::attemptGatewayActivation(const BPMN::FlowNode* node) {
784 if ( node->represents<BPMN::ParallelGateway>() ) {
785
786 auto gatewayIt = const_cast<SystemState*>(systemState)->tokensAwaitingGatewayActivation[this].find(node);
787 auto& [key,arrivedTokens] = *gatewayIt;
788 if ( arrivedTokens.size() == node->incoming.size() ) {
789 // create merged token and advance it
790 auto engine = const_cast<Engine*>(systemState->engine);
791 engine->commands.emplace_back(std::bind(&StateMachine::createMergedToken,this,node), this);
792 }
793 }
794 else {
795 throw std::runtime_error("StateMachine: converging gateway type of node '" + node->id + "' not yet supported");
796 }
797}
798
799void StateMachine::shutdown() {
800 auto engine = const_cast<Engine*>(systemState->engine);
801//std::cerr << "shutdown: " << scope->id << std::endl;
802 assert( tokens.size() );
803 BPMNOS::Values mergedStatus = Token::mergeStatus(tokens);
804
805 if ( auto eventSubProcess = scope->represents<BPMN::EventSubProcess>() ) {
806 // update global objective
809
810 if (!eventSubProcess->startEvent->isInterrupting ) {
811 // delete non-interrupting event subprocess
812 auto context = const_cast<StateMachine*>(parentToken->owned.get());
813 engine->commands.emplace_back(std::bind(&StateMachine::deleteNonInterruptingEventSubProcess,context,this), this);
814 return;
815 }
816 else if ( eventSubProcess->startEvent->represents<BPMN::CompensateStartEvent>() ) {
817 completeCompensationEventSubProcess();
818 return;
819 }
820 }
821
822//std::cerr << "start shutdown: " << BPMNOS::to_string(instance.value(),STRING) << std::endl;
823
824 // update status of parent token (if it doesn't have a sequential performer)
825 if ( parentToken &&
827 ) {
828 parentToken->setStatus(mergedStatus);
829 }
830 tokens.clear();
831
832 // ensure that messages to state machine are removed
833 unregisterRecipient();
834
835 if ( auto subProcess = scope->represents<BPMN::SubProcess>();
836 subProcess && subProcess->compensatedBy
837 ) {
838 if ( subProcess->compensatedBy->represents<BPMN::EventSubProcess>() ) {
839 auto parent = const_cast<StateMachine*>(parentToken->owner);
840 // move state machine pointer to ensure it is not deleted
841 parent->compensableSubProcesses.push_back(shared_from_this());
842 }
843 }
844
845 if ( !parentToken ) {
846//std::cerr << "delete root: " << BPMNOS::to_string(instance.value(),STRING) << std::endl;
847 // delete root state machine (and all descendants)
848 engine->commands.emplace_back(std::bind(&Engine::deleteInstance,engine,this), this);
849 }
850 else {
851//std::cerr << "delete child: " << scope->id << std::endl;
852 auto parent = const_cast<StateMachine*>(parentToken->owner);
853 if ( auto eventSubProcess = scope->represents<BPMN::EventSubProcess>();
854 eventSubProcess && eventSubProcess->startEvent->isInterrupting
855 ) {
856 parent->interruptingEventSubProcess.reset();
857 }
858// engine->commands.emplace_back(std::bind(&StateMachine::deleteChild,parent,this), this);
859
860 // advance parent token to completed
861 auto context = const_cast<StateMachine*>(parentToken->owned.get());
862 auto token = context->parentToken;
863 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,token), token);
864 }
865
866//std::cerr << "shutdown (done): " << scope->id <<std::endl;
867}
868
869void StateMachine::attemptShutdown() {
870//std::cerr << "attemptShutdown: " << scope->id << "/" << this << std::endl;
872//std::cerr << "wait for completion of interrupting event subprocess" << interruptingEventSubProcess->scope->id << std::endl;
873 // wait for completion of interrupting event subprocess
874 return;
875 }
876
878 // wait until last event subprocess is completed
879 return;
880 }
881
882 for ( auto& token : tokens ) {
883 if ( token->state != Token::State::DONE ) {
884 return;
885 }
886 }
887
888 // all tokens are in DONE state
889
890 // no new event subprocesses can be triggered
891 for ( auto eventSubProcess : pendingEventSubProcesses ) {
892 eventSubProcess->clearObsoleteTokens();
893 }
895
896 auto engine = const_cast<Engine*>(systemState->engine);
897//std::cerr << "Shutdown with " << engine->commands.size() << " prior commands" << std::endl;
898 engine->commands.emplace_back(std::bind(&StateMachine::shutdown,this), this);
899}
900
902//std::cerr << scope->id << " getCompensationTokens of " << ( activity ? activity->id : std::string("all activities") ) << std::endl;
903 Tokens result;
904 if ( compensationTokens.empty() ) {
905//std::cerr << "no compensation token" << std::endl;
906 return result;
907 }
908
909 if ( activity ) {
910 // find compensation within context
911 auto it = std::find_if(
912 compensationTokens.begin(),
913 compensationTokens.end(),
914 [&activity](const std::shared_ptr<Token>& token) -> bool {
915 // check if compensation token is at compensation boundary event
916 return ( token.get()->node->as<BPMN::BoundaryEvent>()->attachedTo == activity );
917 }
918 );
919 if ( it != compensationTokens.end() ) {
920 result.push_back(*it);
921 }
922 }
923 else {
924 result = compensationTokens;
925 }
926
927//std::cerr << "compensating " << result.size() << " tokens" << std::endl;
928 return result;
929}
930
931
932void StateMachine::advanceTokenWaitingForCompensation(Token* waitingToken) {
933//std::cerr << scope->id << " -> advanceTokenWaitingForCompensation: " << waitingToken->node->id << "/" << Token::stateName[(int)waitingToken->state]<< "/" << waitingToken << std::endl;
934 auto engine = const_cast<Engine*>(systemState->engine);
935 if ( waitingToken->state == Token::State::BUSY ) {
936
937 // erase from compensation tokens and move to tokens
938 if ( waitingToken->node->represents<BPMN::CompensateBoundaryEvent>() ) {
939 tokens.emplace_back(waitingToken->shared_from_this());
941 }
942 else if ( waitingToken->node->represents<BPMN::CompensateStartEvent>() ) {
943 const_cast<StateMachine*>(waitingToken->owner)->tokens.emplace_back(waitingToken->shared_from_this());
945 }
946
947//std::cerr << "Continue with advanceToCompleted: " << waitingToken->node->id << std::endl;
948 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,waitingToken), waitingToken);
949 }
950 else if ( waitingToken->state == Token::State::FAILING ) {
951//std::cerr << "Continue with terminate: " << waitingToken->owner->scope->id << std::endl;
952 assert( waitingToken->owner == this);
953// engine->commands.emplace_back(std::bind(&StateMachine::terminate,this), this);
954 engine->commands.emplace_back(std::bind(&StateMachine::handleFailure,this,waitingToken), waitingToken);
955 }
956 else {
957// std::cerr << waitingToken->node->id << " has state: " << Token::stateName[(int)waitingToken->state] << std::endl;
958 assert(!"unexpected state of waiting token");
959 }
960}
961
962void StateMachine::completeCompensationActivity(Token* token) {
963 // token is still the compensation token
964
965 // advance waiting 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);
970
971 // remove token
972//std::cerr << scope->id << " -> Compensation completed: " << token->node->id << "/" << token << "/" << tokens.size() << "/" << compensationTokens.size() << std::endl;
974}
975
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);
981 auto engine = const_cast<Engine*>(systemState->engine);
982 // erase state machine
983 auto context = const_cast<StateMachine*>(parentToken->owned.get());
984 engine->commands.emplace_back(std::bind(&StateMachine::deleteCompensationEventSubProcess,context,this), this);
985}
986
987
988void StateMachine::compensate(Tokens compensations, Token* waitingToken) {
989//std::cerr << "\ncompensate: " << scope->id << " compensate " << compensations.size() << " tokens before continuing with " << waitingToken->node->id << std::endl;
990
991 auto engine = const_cast<Engine*>(systemState->engine);
992 auto& tokenAwaitingCompensationActivity = const_cast<SystemState*>(systemState)->tokenAwaitingCompensationActivity;
993 auto& tokenAwaitingCompensationEventSubProcess = const_cast<SystemState*>(systemState)->tokenAwaitingCompensationEventSubProcess;
994
995 auto it = compensations.rbegin();
996 // advance last compensation token
997 auto compensationToken = it->get();
998
999 if ( compensationToken->node->represents<BPMN::CompensateStartEvent>() ) {
1000 const_cast<StateMachine*>(compensationToken->owner)->tokens.push_back(std::move(*it));
1001 }
1002 else {
1003 tokens.push_back(std::move(*it));
1004 }
1005//std::cerr << engine->commands.size() <<" Compensate " << compensationToken->node->id << "/scope: " << compensationToken->owner->scope->id << std::endl;
1006
1007 // erase compensation token and move to active tokens
1008 erase_ptr<Token>(compensationTokens,compensationToken);
1009
1010 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,compensationToken), compensationToken);
1011
1012 // go to next compensation token
1013 it++;
1014 while ( it != compensations.rend() ) {
1015//std::cerr << engine->commands.size() <<"+Compensate " << compensationToken->node->id << "/scope: " << compensationToken->owner->scope->id << std::endl;
1016 // create awaiters for all other compensations
1017 if ( compensationToken->node->represents<BPMN::CompensateStartEvent>() ) {
1018 tokenAwaitingCompensationEventSubProcess[const_cast<StateMachine*>(compensationToken->owner)] = it->get();
1019 }
1020 else {
1021 tokenAwaitingCompensationActivity[compensationToken] = it->get();
1022 }
1023 compensationToken = it->get();
1024 it++;
1025 }
1026 // create awaiter for waiting token
1027 if ( compensationToken->node->represents<BPMN::CompensateStartEvent>() ) {
1028 tokenAwaitingCompensationEventSubProcess[const_cast<StateMachine*>(compensationToken->owner)] = waitingToken;
1029 }
1030 else {
1031 tokenAwaitingCompensationActivity[compensationToken] = waitingToken;
1032 }
1033}
1034
void deleteInstance(StateMachine *instance)
Method removing completed instance.
Definition Engine.cpp:124
std::list< Command > commands
List of commands to be executed.
Definition Engine.h:91
void notify(const Observable *observable) const
Definition Notifier.cpp:10
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.
Definition SystemState.h:21
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...
Definition SystemState.h:92
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.
Definition SystemState.h:87
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.
Definition Token.h:35
const BPMN::FlowNode * node
Definition Token.h:46
const StateMachine * owner
State machine owning the token.
Definition Token.h:44
std::shared_ptr< StateMachine > owned
State machine owned by the token.
Definition Token.h:45
void setStatus(const BPMNOS::Values &other)
Copies all elements except the instance id from other to status
Definition Token.cpp:156
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
Definition Token.h:58
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...
Definition Scenario.h:53
Node * compensatedBy
Pointer to compensation activity or compensation event sub-process.
Definition bpmn++.h:17384
std::unique_ptr< ExtensionElements > extensionElements
Definition bpmn++.h:16299
std::string id
Id of element.
Definition bpmn++.h:16298
Base class for all boundary events attached to an Activity.
Definition bpmn++.h:17200
Scope * parent
Reference to the parent node.
Definition bpmn++.h:16568
T * as()
Casts the element to the specified type T.
Definition bpmn++.h:16253
T * represents()
Attempts to cast the element to the specified type T.
Definition bpmn++.h:16236
TypedStartEvent * startEvent
Definition bpmn++.h:16610
Base class for BPMN elements that may contain incoming and outgoing sequence flows.
Definition bpmn++.h:16670
std::vector< SequenceFlow * > incoming
Vector containing all incoming sequence flows of the node.
Definition bpmn++.h:16679
std::vector< SequenceFlow * > outgoing
Vector containing all outgoing sequence flows of the node.
Definition bpmn++.h:16682
Base class for BPMN elements that may contain a ChildNode elements.
Definition bpmn++.h:16510
std::vector< FlowNode * > startNodes
Vector containing all flow nodes that may start execution of the scope.
Definition bpmn++.h:16525
std::vector< EventSubProcess * > eventSubProcesses
Vector containing pointers to all event subprocesses within the scope of the nodes.
Definition bpmn++.h:16522
Base class for all start events with an event definition.
Definition bpmn++.h:16856
void erase_ptr(std::vector< std::unique_ptr< T > > &container, const T *elementPtr)
Erase a specific element from a vector of unique pointers.
Definition erase.h:19
std::vector< std::shared_ptr< Token > > Tokens
Definition Token.h:19
std::string to_string(number numericValue, const ValueType &type)
Converts a number to a string.
Definition Number.cpp:150
number to_number(const std::string &valueString, const ValueType &type)
Converts a string to a number.
Definition Number.cpp:57
BPMNOS::Values mergeValues(const std::vector< BPMNOS::Values > &valueSets)
Definition Number.cpp:166
@ STRING
Definition Value.h:9