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