BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
Token.cpp
Go to the documentation of this file.
1#include "Token.h"
2#include "StateMachine.h"
3#include "Engine.h"
4#include "DecisionRequest.h"
6#include "DataUpdate.h"
17#include <cassert>
18#include <iostream>
19
20using namespace BPMNOS::Execution;
21
22Token::Token(const StateMachine* owner, const BPMN::FlowNode* node, const Values& status)
23 : owner(owner)
24 , owned(nullptr)
25 , node(node)
26 , sequenceFlow(nullptr)
27 , state(State::CREATED)
28 , status(status)
29 , data(&const_cast<StateMachine*>(owner)->data)
30 , globals(const_cast<SystemState*>(owner->systemState)->globals)
31 , performing(nullptr)
32{
33}
34
35Token::Token(const Token* other)
36 : owner(other->owner)
37 , owned(nullptr)
38 , node(other->node)
39 , sequenceFlow(nullptr)
40 , state(other->state)
41 , status(other->status)
42 , data(&const_cast<StateMachine*>(owner)->data)
43 , globals(const_cast<SystemState*>(owner->systemState)->globals)
44 , performing(nullptr)
45{
46}
47
48Token::Token(const std::vector<Token*>& others)
49 : owner(others.front()->owner)
50 , owned(nullptr)
51 , node(others.front()->node)
52 , sequenceFlow(nullptr)
53 , state(others.front()->state)
54 , status(mergeStatus(others))
55 , data(&const_cast<StateMachine*>(owner)->data)
56 , globals(const_cast<SystemState*>(owner->systemState)->globals)
57 , performing(nullptr)
58{
59}
60
62//std::cerr << "~Token(" << (node ? node->id : owner->process->id ) << "/" << this << ")" << std::endl;
63 auto systemState = const_cast<SystemState*>(owner->systemState);
64 if ( node) {
65 if ( auto activity = node->represents<BPMN::Activity>(); activity && !activity->boundaryEvents.empty() ) {
66 auto engine = const_cast<Engine*>(owner->systemState->engine);
67 auto stateMachine = const_cast<StateMachine*>(owner);
68 engine->commands.emplace_back(std::bind(&StateMachine::deleteTokensAwaitingBoundaryEvent,stateMachine,this), this);
69 }
70
72 systemState->tokenAssociatedToBoundaryEventToken.erase(this);
73 }
74
79 ) {
80 systemState->tokenAwaitingCompensationActivity.erase(this);
81 }
82
84 systemState->tokensAwaitingEvent.erase(this);
85 }
87 systemState->tokenAtEventBasedGateway.erase(this);
88 }
89
90 if ( auto activity = node->represents<BPMN::Activity>() ) {
91 if ( activity->represents<BPMN::SendTask>() ) {
92 auto it = systemState->messageAwaitingDelivery.find(this);
93 if ( it != systemState->messageAwaitingDelivery.end() ) {
94 auto message = it->second.lock();
95 if ( message ) {
96 // withdraw message
97 message->state = Message::State::WITHDRAWN;
98 owner->systemState->engine->notify(message.get());
99 erase_ptr<Message>(systemState->messages, message.get());
100 }
101 systemState->messageAwaitingDelivery.erase(it);
102 }
103 }
104
105 // release performer when activity fails
106 if ( state == State::READY && activity->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>() ) {
107 auto tokenAtSequentialPerformer = getSequentialPerformerToken();
108 if ( tokenAtSequentialPerformer && tokenAtSequentialPerformer->performing == this ) {
109 releaseSequentialPerformer();
110 }
111 }
112
113 if ( activity->loopCharacteristics.has_value() &&
114 !activity->isForCompensation
115 ) {
116 if ( activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard ) {
117 if ( state == State::WAITING ) {
118 systemState->tokensAtActivityInstance.erase(this);
119 systemState->exitStatusAtActivityInstance.erase(this);
120 }
121 else {
122 systemState->tokenAtMultiInstanceActivity.erase(this);
123 if ( activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::MultiInstanceSequential ) {
124 systemState->tokenAwaitingMultiInstanceExit.erase(this);
125 }
126 }
127 }
128 }
129 }
130
131 if ( performing ) {
132 performing = nullptr;
134 }
135 }
136}
137
138
140 if ( !node ) {
141 return owner->process->extensionElements->as<const BPMNOS::Model::ExtensionElements>()->attributeRegistry;
142 }
143
144 if ( auto extensionElements = node->extensionElements->represents<const BPMNOS::Model::ExtensionElements>() ) {
145 return extensionElements->attributeRegistry;
146 }
147
148 // return attribute registry of parent for nodes without extension elements
149 if ( !owner->parentToken ) {
150 throw std::runtime_error("Token: cannot determine attribute registry");
151 }
152
154}
155
159
160nlohmann::ordered_json Token::jsonify() const {
161 nlohmann::ordered_json jsonObject;
162 jsonObject["processId"] = owner->process->id;
163 jsonObject["instanceId"] = BPMNOS::to_string(getInstanceId(),STRING);
164 if ( node ) {
165 jsonObject["nodeId"] = node->id;
166 }
167 if ( sequenceFlow ) {
168 jsonObject["sequenceFlowId"] = sequenceFlow->id;
169 }
170 jsonObject["state"] = stateName[(int)state];
171 jsonObject["status"] = nlohmann::ordered_json::object();
172
173 auto& attributeRegistry = getAttributeRegistry();
174 for (auto attribute : attributeRegistry.statusAttributes ) {
175 if ( attribute->index >= status.size() ) {
176 // skip attribute that is not yet included in status
177 continue;
178 }
179
180 auto statusValue = attributeRegistry.getValue(attribute,status,*data,globals);
181 if ( !statusValue.has_value() ) {
182 jsonObject["status"][attribute->name] = nullptr ;
183 }
184 else if ( attribute->type == BOOLEAN) {
185 bool value = (bool)statusValue.value();
186 jsonObject["status"][attribute->name] = value ;
187 }
188 else if ( attribute->type == INTEGER) {
189 int value = (int)statusValue.value();
190 jsonObject["status"][attribute->name] = value ;
191 }
192 else if ( attribute->type == DECIMAL) {
193 double value = (double)statusValue.value();
194 jsonObject["status"][attribute->name] = value ;
195 }
196 else if ( attribute->type == STRING) {
197 std::string value = BPMNOS::to_string(statusValue.value(),attribute->type);
198 jsonObject["status"][attribute->name] = value ;
199 }
200 else if ( attribute->type == COLLECTION) {
201 std::string value = BPMNOS::to_string(statusValue.value(),attribute->type);
202 jsonObject["status"][attribute->name] = value ;
203 }
204 }
205
206//std::cerr << jsonObject << std::endl;
207 assert(data);
208 if ( data->size() ) {
209 jsonObject["data"] = nlohmann::ordered_json::object();
210
211 for (auto attribute : attributeRegistry.dataAttributes ) {
212 if ( attribute->index >= data->size() ) {
213 // skip attribute that is not yet included in data
214 continue;
215 }
216
217 auto dataValue = attributeRegistry.getValue(attribute,status,*data,globals);
218 if ( !dataValue.has_value() ) {
219 jsonObject["data"][attribute->name] = nullptr ;
220 }
221 else if ( attribute->type == BOOLEAN) {
222 bool value = (bool)dataValue.value();
223 jsonObject["data"][attribute->name] = value ;
224 }
225 else if ( attribute->type == INTEGER) {
226 int value = (int)dataValue.value();
227 jsonObject["data"][attribute->name] = value ;
228 }
229 else if ( attribute->type == DECIMAL) {
230 double value = (double)dataValue.value();
231 jsonObject["data"][attribute->name] = value ;
232 }
233 else if ( attribute->type == STRING) {
234 std::string value = BPMNOS::to_string(dataValue.value(),attribute->type);
235 jsonObject["data"][attribute->name] = value ;
236 }
237 else if ( attribute->type == COLLECTION) {
238 std::string value = BPMNOS::to_string(dataValue.value(),attribute->type);
239 jsonObject["data"][attribute->name] = value ;
240 }
241 }
242 }
243
244 if ( globals.size() ) {
245 jsonObject["globals"] = nlohmann::ordered_json::object();
246
247 for (auto attribute : attributeRegistry.globalAttributes ) {
248 auto globalValue = globals[attribute->index];
249 if ( !globalValue.has_value() ) {
250 jsonObject["globals"][attribute->name] = nullptr ;
251 }
252 else if ( attribute->type == BOOLEAN) {
253 bool value = (bool)globalValue.value();
254 jsonObject["globals"][attribute->name] = value ;
255 }
256 else if ( attribute->type == INTEGER) {
257 int value = (int)globalValue.value();
258 jsonObject["globals"][attribute->name] = value ;
259 }
260 else if ( attribute->type == DECIMAL) {
261 double value = (double)globalValue.value();
262 jsonObject["globals"][attribute->name] = value ;
263 }
264 else if ( attribute->type == STRING) {
265 std::string value = BPMNOS::to_string(globalValue.value(),attribute->type);
266 jsonObject["globals"][attribute->name] = value ;
267 }
268 else if ( attribute->type == COLLECTION) {
269 std::string value = BPMNOS::to_string(globalValue.value(),attribute->type);
270 jsonObject["globals"][attribute->name] = value ;
271 }
272 }
273 }
274
275 return jsonObject;
276}
277
278
279bool Token::entryIsFeasible() const {
280 if ( !node ) {
281//std::cerr << stateName[(int)state] << "/" << owner->scope->id <<std::endl;
284 }
285
288}
289
290bool Token::completionIsFeasible() const {
291 if ( !node ) {
292//std::cerr << stateName[(int)state] << "/" << owner->scope->id <<std::endl;
295 }
296
298 return node->extensionElements->as<BPMNOS::Model::ExtensionElements>()->feasibleCompletion(status,*data,globals);
299}
300
301bool Token::exitIsFeasible() const {
302 if ( !node ) {
303//std::cerr << stateName[(int)state] << "/" << owner->scope->id <<std::endl;
306 }
307
310}
311
312void Token::advanceFromCreated() {
313//std::cerr << "advanceFromCreated: " << jsonify().dump() << std::endl;
314 if ( !node ) {
315 // tokens at process advance to entered
316 advanceToEntered();
317 return;
318 }
319
320 // token is at a node
321 if ( node->represents<BPMN::Activity>() ) {
322 awaitReadyEvent();
323 }
324 else {
325 advanceToEntered();
326 }
327}
328
329void Token::advanceToReady() {
330//std::cerr << "advanceToReady: " << jsonify().dump() << std::endl;
332 throw std::runtime_error("Token: ready timestamp at node '" + node->id + "' is larger than current time");
333 }
334
335 if ( owned ) {
336 // ensure that data is set appropriately
337 data = &owned->data;
338 }
339
340 update(State::READY);
341
342 if ( auto activity = node->represents<BPMN::Activity>();
343 activity &&
344 activity->loopCharacteristics.has_value()
345 ) {
346 if ( activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard ) {
347 // delegate creation of token copies for multi-instance activity to owner
348 auto stateMachine = const_cast<StateMachine*>(owner);
349 auto engine = const_cast<Engine*>(owner->systemState->engine);
350 engine->commands.emplace_back(std::bind(&StateMachine::createMultiInstanceActivityTokens,stateMachine,this), this);
351 return;
352 }
353 }
354
355//std::cerr << "->awaitEntryEvent" << std::endl;
356 awaitEntryEvent();
357}
358
359void Token::computeInitialValues( const BPMNOS::Model::ExtensionElements* extensionElements ) {
361}
362
363void Token::advanceToEntered() {
364//std::cerr << "advanceToEntered: " << jsonify().dump() << std::endl;
365
367 if ( node ) {
368 throw std::runtime_error("Token: entry timestamp at node '" + node->id + "' is larger than current time");
369 }
370 else {
371 throw std::runtime_error("Token: entry timestamp for process '" + owner->process->id + "' is larger than current time");
372 }
373 }
374
375 if ( !node ) {
376//std::cerr << "!node" << std::endl;
377 auto extensionElements = owner->process->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
378 assert( extensionElements );
379
380 computeInitialValues( extensionElements );
381 }
382 else {
383 if ( auto activity = node->represents<BPMN::Activity>() ) {
384 auto extensionElements = node->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
385 assert( extensionElements );
386
387 if (
388 activity->loopCharacteristics.has_value() &&
389 activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::Standard
390 ) {
391//std::cerr << "initialize or increment loop index for standard loop" << std::endl;
392 // initialize or increment loop index for standard loop
393 if ( extensionElements->loopIndex.has_value() && extensionElements->loopIndex.value()->expression ) {
394 auto& attributeRegistry = getAttributeRegistry();
395 auto attribute = extensionElements->loopIndex.value()->expression->isAttribute();
396 if ( auto index = attributeRegistry.getValue( attribute, status, *data, globals); index.has_value() ) {
397 // increment existing value
398 attributeRegistry.setValue(attribute, status, *data, globals, (unsigned int)index.value() + 1);
399 }
400 else {
401 // initialize non-existing value
402 attributeRegistry.setValue(attribute, status, *data, globals, 1);
403 }
404 }
405 else if ( extensionElements->loopMaximum.has_value() ) {
406 throw std::runtime_error("Token: no attribute provided for loop index parameter of standard loop activity '" + node->id +"' with loop maximum" );
407 }
408 }
409
410 computeInitialValues( extensionElements );
411//std::cerr << jsonify() << std::endl;
412 }
413 }
414
415
416 update(State::ENTERED);
417//std::cerr << "updatedToEntered" << std::endl;
418
419 if ( const BPMN::Activity* activity = (node ? node->represents<BPMN::Activity>() : nullptr);
420 activity &&
421 activity->loopCharacteristics.has_value() &&
422 owned
423 ) {
424 // register state machines of multi-instance activities
425 const_cast<SystemState*>(owner->systemState)->archive[ (long unsigned int)owned->instance.value() ] = owned->weak_from_this();
426 owned->registerRecipient();
427 }
428
429//std::cerr << jsonify().dump() << std::endl;
430
431 auto engine = const_cast<Engine*>(owner->systemState->engine);
432
433
435 // initiate event subprocesses after entering untyped start event
436 auto stateMachine = const_cast<StateMachine*>(owner);
437 engine->commands.emplace_back(std::bind(&StateMachine::initiateEventSubprocesses,stateMachine,this), this);
438 }
439
440
441 // only check feasibility for processes and activities
442 // feasibility of all other tokens must have been validated before
443 // (also for newly created or merged tokens)
444 if ( !node ) {
445 // check restrictions
446 if ( !entryIsFeasible() ) {
447 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
448 return;
449 }
450
451 // tokens entering a process advance to busy state
452 engine->commands.emplace_back(std::bind(&Token::advanceToBusy,this), this);
453 }
454 else if ( auto activity = node->represents<BPMN::Activity>() ) {
455//std::cerr << "activity" << std::endl;
456
457 auto stateMachine = const_cast<StateMachine*>(owner);
458
459 // create tokens at boundary events
460 if ( activity->boundaryEvents.size() ) {
461 engine->commands.emplace_back(std::bind(&StateMachine::initiateBoundaryEvents,stateMachine,this), this);
462 }
463
464//std::cerr << "check restrictions: " << entryIsFeasible() << std::endl;
465 // check restrictions
466 if ( !entryIsFeasible() ) {
467 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
468 return;
469 }
470
471 // advance to busy state
472 engine->commands.emplace_back(std::bind(&Token::advanceToBusy,this), this);
473 }
475 ) {
476//std::cerr << "advance to busy state" << std::endl;
477 // tokens entering a catching event automatically
478 // advance to busy state
479 engine->commands.emplace_back(std::bind(&Token::advanceToBusy,this), this);
480 }
481 else if ( node->represents<BPMN::EventBasedGateway>() ) {
482 // tokens entering an event-based gateway automatically
483 // advance to busy state
484 engine->commands.emplace_back(std::bind(&Token::advanceToBusy,this), this);
485 }
486 else if ( node->represents<BPMN::ErrorEndEvent>() ) {
487 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
488 return;
489 }
490 else {
492 // update status and delegate control to state machine
493 // if applicable, control will be delgated back to token
494 engine->commands.emplace_back(std::bind(&StateMachine::handleEscalation,const_cast<StateMachine*>(owner),this), this);
495 }
496 else if ( node->represents<BPMN::SignalThrowEvent>() ) {
497 // determine signal name
498 emitSignal();
499 }
500 else if ( node->represents<BPMN::MessageThrowEvent>() ) {
501 assert( !node->represents<BPMN::SendTask>() );
502 sendMessage();
503 }
504 else if ( auto compensateThrowEvent = node->represents<BPMN::CompensateThrowEvent>() ) {
505 auto context = const_cast<StateMachine*>(owner->parentToken->owned.get());
506
507 if ( auto eventSubProcess = node->parent->represents<BPMN::EventSubProcess>();
508 eventSubProcess && eventSubProcess->startEvent->represents<BPMN::CompensateStartEvent>()
509 ) {
510//std::cerr << "try to update context " << context->compensableSubProcesses.size() << std::endl;
511 // compensation is triggered from within a compensation event subprocess
512 // find the compensable subprocess and update context
513 auto it = std::find_if(
514 context->compensableSubProcesses.begin(),
515 context->compensableSubProcesses.end(),
516 [&eventSubProcess](const std::shared_ptr<StateMachine>& stateMachine) -> bool {
517 // check if compensation event subprocess belongs to compensable subprocess
518 return ( stateMachine->scope->compensationEventSubProcess == eventSubProcess );
519 }
520 );
521 context = ( it != context->compensableSubProcesses.end() ? it->get() : nullptr );
522 }
523
524 if ( context ) {
525//std::cerr << "compensate context " << context->scope->id << std::endl;
526 if ( auto compensations = context->getCompensationTokens(compensateThrowEvent->activity);
527 compensations.size()
528 ) {
529 // advance to busy and await compensations
530 engine->commands.emplace_back(std::bind(&Token::update,this,State::BUSY), this);
531 context->compensate( std::move(compensations), this );
532 return;
533 }
534 }
535 // nothing to compensate, continue with token flow
536 }
537
538 // tokens entering any other node automatically advance to done or
539 // departed state
540 if ( node->outgoing.empty() ) {
541 engine->commands.emplace_back(std::bind(&Token::advanceToDone,this), this);
542 return;
543 }
544 advanceToDeparting();
545 }
546}
547
548void Token::advanceToBusy() {
549//std::cerr << "advanceToBusy: " << jsonify().dump() << std::endl;
550
551 if (
552 node &&
556 ) {
557 if ( auto extensionElements = node->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
558 extensionElements && extensionElements->operators.size()
559 ) {
560 // apply operators for regular tasks (if timestamp is in the future, updated status is an expectation)
561 auto now = owner->systemState->getTime();
563 extensionElements->applyOperators(status,*data,globals);
565 throw std::runtime_error("Token: timestamp at node '" + node->id + "' is deleted");
566 }
568std::cerr << status[BPMNOS::Model::ExtensionElements::Index::Timestamp].value() << " != " << now << std::endl;
569 throw std::runtime_error("Token: Operators for task '" + node->id + "' attempt to modify timestamp");
570 }
571 // notify about data update
572 if ( extensionElements->dataUpdate.global ) {
573 owner->systemState->engine->notify( DataUpdate( extensionElements->dataUpdate.attributes ) );
574 }
575 else {
576 owner->systemState->engine->notify( DataUpdate( owner->root->instance.value(), extensionElements->dataUpdate.attributes ) );
577 }
578 }
579 }
580
581 update(State::BUSY);
582
583 if ( !node ) {
584 // token is at process
585 auto scope = owner->process->as<BPMN::Scope>();
586 if ( scope->startNodes.empty() ) {
587 auto engine = const_cast<Engine*>(owner->systemState->engine);
588 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
589 }
590 else {
591 if ( scope->startNodes.size() == 1 ) {
592//std::cerr << "create child statemachine" << std::endl;
593 // create child statemachine
594 auto engine = const_cast<Engine*>(owner->systemState->engine);
595 assert( owned );
596 engine->commands.emplace_back(std::bind(&StateMachine::run,owned.get(),status), owned.get());
597 }
598 else {
599 throw std::runtime_error("Token: process '" + scope->id + "' has multiple start nodes");
600 }
601 return;
602 }
603 }
605 auto scope = node->as<BPMN::Scope>();
606 if ( scope->startNodes.empty() ) {
607 auto engine = const_cast<Engine*>(owner->systemState->engine);
608 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
609 }
610 else {
611 // create child statemachine
612 auto engine = const_cast<Engine*>(owner->systemState->engine);
613 assert( owned );
614 engine->commands.emplace_back(std::bind(&StateMachine::run,owned.get(),status), owned.get());
615 }
616 }
617 else if ( node->represents<BPMN::SubProcess>() ) {
618 auto scope = node->as<BPMN::Scope>();
619 if ( scope->startNodes.empty() ) {
620//std::cerr << "skip child statemachine at node " << node->id << std::endl;
621 auto engine = const_cast<Engine*>(owner->systemState->engine);
622 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
623 }
624 else {
625 if ( scope->startNodes.size() == 1 ) {
626//std::cerr << "create child statemachine at node " << node->id << std::endl;
627 // create child statemachine
628 auto engine = const_cast<Engine*>(owner->systemState->engine);
629 assert( owned );
630 engine->commands.emplace_back(std::bind(&StateMachine::run,owned.get(),status), owned.get());
631 }
632 else {
633 throw std::runtime_error("Token: subprocess '" + scope->id + "' has multiple start nodes");
634 }
635 return;
636 }
637 }
638 else if ( node->represents<BPMN::EventBasedGateway>() ) {
639 // token will automatically be copied and forwarded along each sequence flow
640 auto engine = const_cast<Engine*>(owner->systemState->engine);
641 engine->commands.emplace_back(std::bind(&Token::advanceToDeparting,this), this);
642 }
643 else if ( node->represents<BPMN::TimerCatchEvent>() ) {
644 // determine time
645 auto trigger = node->extensionElements->as<BPMNOS::Model::Timer>()->trigger.get();
646 if (!trigger->expression) {
647 throw std::runtime_error("Token: no trigger given for node '" + node->id + "'");
648 }
649 BPMNOS::number time = trigger->expression->execute(status, *data, globals).value_or(owner->systemState->getTime());
650
651 if ( time > owner->systemState->getTime() ) {
652 awaitTimer(time);
653 }
654 else {
655 auto engine = const_cast<Engine*>(owner->systemState->engine);
656 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
657 }
658 }
659 else if ( node->represents<BPMN::SignalCatchEvent>() ) {
660 // determine signal name
661 assert( node->extensionElements->represents<BPMNOS::Model::Signal>() );
662 awaitSignal( node->extensionElements->as<BPMNOS::Model::Signal>()->name );
663 }
665 // determine conditions
666 assert(node->extensionElements->represents<BPMNOS::Model::Conditions>());
667 auto extensionElements = node->extensionElements->as<BPMNOS::Model::Conditions>();
668 if ( !extensionElements->conditionsSatisfied(status,*data,globals) ) {
669 awaitConditions( owner->root->instance.value() );
670 }
671 else {
672 auto engine = const_cast<Engine*>(owner->systemState->engine);
673 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
674 }
675 }
676 else if ( node->represents<BPMN::MessageCatchEvent>() ) {
677 awaitMessageDelivery();
678 }
679 else if ( node->represents<BPMN::Task>() ) {
681 awaitChoiceEvent();
682 return;
683 }
684
685 if ( auto sendTask = node->represents<BPMN::SendTask>() ) {
687 auto extensionElements = node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
688 // send message(s)
689 if ( sendTask->loopCharacteristics.has_value() ) {
690 // multi-instance send task requires index to access respective message definition
691 if ( !extensionElements->loopIndex.has_value() || !extensionElements->loopIndex->get()->expression ) {
692 throw std::runtime_error("Token: send task '" + sendTask->id + "' requires status attribute holding loop index");
693 }
694 auto attribute = extensionElements->loopIndex->get()->expression->isAttribute();
695 if ( !status[attribute->index].has_value() ) {
696 throw std::runtime_error("Token: cannot find loop index for send task '" + sendTask->id + "'");
697 }
698 assert( status[attribute->index].value() >= 1 );
699 sendMessage( (size_t)(int)status[attribute->index].value()-1 );
700 }
701 else {
702 sendMessage();
703 }
704 // wait for delivery
705 return;
706 }
708 awaitTaskCompletionEvent();
709 return;
710 }
711
712 auto engine = const_cast<Engine*>(owner->systemState->engine);
713 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,this), this);
714 }
715}
716
717/*
718void Token::advanceToCompleted(const Values& statusUpdate) {
719 status = statusUpdate;
720 advanceToCompleted();
721}
722*/
723
724void Token::advanceToCompleted() {
726 if ( node ) {
727 throw std::runtime_error("Token: completion timestamp at node '" + node->id + "' is larger than current time");
728 }
729 else {
730 throw std::runtime_error("Token: completion timestamp for process '" + owner->process->id + "' is larger than current time");
731 }
732 }
733
735
736 if ( node ) {
737 // operators of send task, receive task, and decision task are applied on completion
738 if (
741 ) {
742 if ( auto extensionElements = node->extensionElements->represents<BPMNOS::Model::ExtensionElements>() ) {
743 if ( !extensionElements->isInstantaneous ) {
744 throw std::runtime_error("Token: Operators for task '" + node->id + "' attempt to modify timestamp");
745 }
746 extensionElements->applyOperators(status,*data,globals);
747 // notify about data update
748 if ( extensionElements->dataUpdate.global ) {
749 owner->systemState->engine->notify( DataUpdate( extensionElements->dataUpdate.attributes ) );
750 }
751 else {
752 owner->systemState->engine->notify( DataUpdate( owner->root->instance.value(), extensionElements->dataUpdate.attributes ) );
753 }
754 }
755 }
756 // operators of event subprocesses are applied on completion of typed start event
757 else if ( node->represents<BPMN::TypedStartEvent>() ) {
758 auto eventSubProcess = node->parent->represents<BPMN::EventSubProcess>();
759 if ( !eventSubProcess ) {
760 throw std::runtime_error("Token: typed start event must belong to event subprocess");
761 }
762 if ( auto extensionElements = eventSubProcess->extensionElements->represents<BPMNOS::Model::ExtensionElements>() ) {
763 if ( !extensionElements->isInstantaneous ) {
764 throw std::runtime_error("Token: Operators for event-subprocess '" + eventSubProcess->id + "' attempt to modify timestamp");
765 }
766 extensionElements->applyOperators(status,*data,globals);
767 // notify about data update
768 if ( extensionElements->dataUpdate.global ) {
769 owner->systemState->engine->notify( DataUpdate( extensionElements->dataUpdate.attributes ) );
770 }
771 else {
772 owner->systemState->engine->notify( DataUpdate( owner->root->instance.value(), extensionElements->dataUpdate.attributes ) );
773 }
774 }
775 }
776 }
777
778
779 update(State::COMPLETED);
780
781 auto engine = const_cast<Engine*>(owner->systemState->engine);
782
783 if ( !node ) {
784 // update global objective
787
788//std::cerr << "check restrictions" << std::endl;
789 // check restrictions
790 if ( !completionIsFeasible() ) {
791//std::cerr << "infeasible: " << jsonify().dump() << std::endl;
792 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
793 return;
794 }
795 }
796 else {
797 if ( auto activity = node->represents<BPMN::Activity>() ) {
798 if ( activity->compensatedBy ) {
799 if ( activity->isForCompensation ) {
800 throw std::runtime_error("Token: compensation activity '" + activity->id + "' must not be compensated");
801 }
802 awaitCompensation();
803 }
804
805//std::cerr << activity->id << " is for compensation: " << activity->isForCompensation << std::endl;
806 if ( activity->isForCompensation ) {
807 // final state for compensation activity reached
808 if ( !completionIsFeasible() ) {
809 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
810 return;
811 }
812
813 auto stateMachine = const_cast<StateMachine*>(owner);
814 engine->commands.emplace_back(std::bind(&StateMachine::completeCompensationActivity,stateMachine,this), this);
815 // update global objective
818 }
819 else {
820 // check restrictions
821 if ( !completionIsFeasible() ) {
822 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
823 return;
824 }
825 awaitExitEvent();
826 }
827 return;
828 }
829 else if ( auto compensateBoundaryEvent = node->represents<BPMN::CompensateBoundaryEvent>(); compensateBoundaryEvent ) {
830//assert( owner->parentToken->node->represents<BPMN::Scope>() );
831//std::cerr << "token is compensateBoundaryEvent: " << node->id << "/" << stateName[(int)state] << "/" << ( owner->parentToken->node ? owner->parentToken->node->id : owner->scope->id) << "/" << this << "/" << owner <<std::endl;
832 engine->commands.emplace_back(std::bind(&StateMachine::compensateActivity,const_cast<StateMachine*>(owner),this), this);
833 return;
834 }
835 else if ( auto boundaryEvent = node->represents<BPMN::BoundaryEvent>() ) {
836 auto stateMachine = const_cast<StateMachine*>(owner);
837 auto tokenAtActivity = const_cast<SystemState*>(owner->systemState)->tokenAssociatedToBoundaryEventToken[this];
838 erase_ptr<Token>(const_cast<SystemState*>(owner->systemState)->tokensAwaitingBoundaryEvent[tokenAtActivity],this);
839
840 if ( boundaryEvent->isInterrupting ) {
841 // interrupt activity
842 engine->commands.emplace_back(std::bind(&StateMachine::interruptActivity,stateMachine,tokenAtActivity), tokenAtActivity);
843 }
844 else {
845 // create new token at boundary event
846 engine->commands.emplace_back(std::bind(&StateMachine::initiateBoundaryEvent,stateMachine,tokenAtActivity,node), tokenAtActivity);
847 }
848 }
850 // nothing do
851 }
852 else if ( auto startEvent = node->represents<BPMN::TypedStartEvent>() ) {
853 // event subprocess is triggered
855 throw std::runtime_error("Token: typed start event must belong to event subprocess");
856 }
857 auto context = const_cast<StateMachine*>(owner->parentToken->owned.get());
858
859/*
860std::cerr << "Node: " << this << " at " << node->id << " is owned by " << owner << std::endl;
861std::cerr << "Owner: " << owner << " at " << owner->scope->id << " has " << owner->pendingEventSubProcesses.size() << " pendingEventSubProcess" << std::endl;
862std::cerr << "Context: " << context << " at " << context->scope->id << " has " << context->pendingEventSubProcesses.size() << " pendingEventSubProcess" << std::endl;
863*/
864 // find pending subprocess
865 auto it = std::find_if(context->pendingEventSubProcesses.begin(), context->pendingEventSubProcesses.end(), [this](std::shared_ptr<StateMachine>& eventSubProcess) {
866 auto pendingToken = eventSubProcess->tokens.front();
867 return pendingToken.get() == this;
868 });
869
870 assert( it != context->pendingEventSubProcesses.end() );
871
872 if ( startEvent->isInterrupting ) {
873 // TODO: interrupt activity or process
874
875 // move triggered event subprocess to interruptingEventSubProcess
876 context->interruptingEventSubProcess = std::move(*it);
877 context->pendingEventSubProcesses.erase(it);
878
879 // ensure that no other event subprocess can be triggered
880 for ( auto eventSubProcess : context->pendingEventSubProcesses ) {
881 eventSubProcess->clearObsoleteTokens();
882 }
883 context->pendingEventSubProcesses.clear();
884
885 // terminate all running non-interrupting event subprocesses
886 for ( auto eventSubProcess : context->nonInterruptingEventSubProcesses ) {
887 eventSubProcess->clearObsoleteTokens();
888 }
889 context->nonInterruptingEventSubProcesses.clear();
890
891 // interrupt all running tokens in state machine
892 context->clearObsoleteTokens();
893 }
894 else {
895//std::cerr << "Before pendingEventSubProcesses: " << context->pendingEventSubProcesses.size() << std::endl;
896 // respawn pending event subprocesses
897 std::shared_ptr pendingEventSubProcess = std::make_shared<StateMachine>(it->get());
898
899 // move the triggered event subprocess to nonInterruptingEventSubProcesses
900 context->nonInterruptingEventSubProcesses.push_back(std::move(*it));
901
902 // replace iterator with pending event subprocess
903 *it = pendingEventSubProcess;
904
905//std::cerr << "After pendingEventSubProcesses: " << context->pendingEventSubProcesses.size() << std::endl;
906
907 // prepare the new instance of the pending subprocess
908 auto eventSubProcess = context->pendingEventSubProcesses.back().get();
909//std::cerr << "***" << owner->parentToken << std::endl;
910 eventSubProcess->run(owner->parentToken->status);
911//std::cerr << "***" << std::endl;
912 }
913
914 // check entry scope restrictions of event-subprocess
915//std::cerr << "check entry scope restrictions of event-subprocess" << std::endl;
917 auto eventSubProcess = node->parent;
918 auto extensionElements = eventSubProcess->represents<BPMN::EventSubProcess>()->extensionElements->as<BPMNOS::Model::ExtensionElements>();
919 if ( !extensionElements->feasibleEntry(status,*data,globals) ) {
920 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
921 }
922 else if ( node->outgoing.empty() ) {
923 engine->commands.emplace_back(std::bind(&Token::advanceToDone,this), this);
924 }
925 else {
926 advanceToDeparting();
927 }
928 return;
929 }
930 else if ( auto catchEvent = node->represents<BPMN::CatchEvent>();
931 catchEvent &&
932 node->incoming.size() == 1 &&
933 node->incoming.front()->source->represents<BPMN::EventBasedGateway>()
934 ) {
935 engine->commands.emplace_back(std::bind(&StateMachine::handleEventBasedGatewayActivation,const_cast<StateMachine*>(owner),this), this);
936 }
937 }
938
939
940 if ( !node || node->outgoing.empty() ) {
941//std::cerr << "done: " << jsonify().dump() << std::endl;
942 engine->commands.emplace_back(std::bind(&Token::advanceToDone,this), this);
943 return;
944 }
945//std::cerr << "advanceToDeparting" << std::endl;
946 advanceToDeparting();
947}
948
949void Token::advanceToExiting() {
950//std::cerr << "advanceToExiting: " << jsonify().dump() << std::endl;
951 auto engine = const_cast<Engine*>(owner->systemState->engine);
952
954 throw std::runtime_error("Token: exit timestamp at node '" + node->id + "' is larger than current time");
955 }
956
957 update(State::EXITING);
958
959 // check restrictions
960 if ( !exitIsFeasible() ) {
961 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
962 return;
963 }
964
965 auto extensionElements = node->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
966
967 if ( extensionElements && ( extensionElements->attributes.size() || extensionElements->data.size() ) ) {
968 // update global objective
969 const_cast<SystemState*>(owner->systemState)->contributionsToObjective += extensionElements->getContributionToObjective(status,*data,globals);
970//std::cerr << "objective updated" << std::endl;
971 }
972
973 if ( owned ) {
974//std::cerr << "Use data of scope " << owner->scope->id << std::endl;
975 data = &const_cast<StateMachine*>(owner)->data;
976 }
977
978 auto activity = node->represents<BPMN::Activity>();
979 if ( activity && activity->loopCharacteristics.has_value() && activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::Standard ) {
980 auto& attributeRegistry = getAttributeRegistry();
981
982 auto LOOP = [&]() -> bool {
983 if (extensionElements->loopCondition.has_value() && extensionElements->loopCondition.value()->expression) {
984 auto value = extensionElements->loopCondition.value()->expression->execute(status, *data, globals);
985 assert( value.has_value() );
986 if ( !value.value() ) {
987 // do not loop if loop condition is violated
988 return false;
989 }
990 }
991
992 if ( extensionElements->loopMaximum.has_value() && extensionElements->loopMaximum.value()->expression) {
993 auto maximum = (double)extensionElements->loopMaximum.value()->expression->execute(status, *data, globals).value_or(0);
994 assert( extensionElements->loopIndex.value()->expression );
995 auto indexAttribute = extensionElements->loopIndex.value()->expression->isAttribute();
996 assert( indexAttribute );
997 auto index = attributeRegistry.getValue( indexAttribute, status, *data, globals).value();
998
999 if ( index >= maximum ) {
1000 // do not loop if loop maximum loop count is reached
1001 return false;
1002 }
1003 }
1004 return true;
1005 }();
1006
1007 if ( LOOP ) {
1008 awaitEntryEvent();
1009 return;
1010 }
1011 }
1012
1013 if ( extensionElements && extensionElements->attributes.size() ) {
1014 // remove attributes that are no longer needed
1015 assert( status.size() == extensionElements->attributeRegistry.statusAttributes.size() );
1016 status.resize( status.size() - extensionElements->attributes.size() );
1017 }
1018
1019 if ( activity && activity->loopCharacteristics.has_value() && activity->loopCharacteristics.value() != BPMN::Activity::LoopCharacteristics::Standard ) {
1020 // delegate removal of token copies for multi-instance activity to owner
1021 auto stateMachine = const_cast<StateMachine*>(owner);
1022 engine->commands.emplace_back(std::bind(&StateMachine::deleteMultiInstanceActivityToken,stateMachine,this), this);
1023 return;
1024 }
1025
1026 if ( activity ) {
1027 auto stateMachine = const_cast<StateMachine*>(owner);
1028 if ( !activity->boundaryEvents.empty() ) {
1029 // remove tokens at boundary events
1030 engine->commands.emplace_back( std::bind(&StateMachine::deleteTokensAwaitingBoundaryEvent,stateMachine,this), stateMachine );
1031 }
1032 }
1033
1034 // clear state machine owned by token
1035 if ( owned ) {
1036 owned->tokens.clear();
1040 owned.reset();
1041 }
1042
1043 if ( node->outgoing.empty() ) {
1044 engine->commands.emplace_back(std::bind(&Token::advanceToDone,this), this);
1045 return;
1046 }
1047 advanceToDeparting();
1048}
1049
1050void Token::advanceToDone() {
1051//std::cerr << "advanceToDone: " << jsonify().dump() << std::endl;
1052 update(State::DONE);
1053
1055 auto engine = const_cast<Engine*>(owner->systemState->engine);
1056 auto stateMachine = const_cast<StateMachine*>(owner);
1057 engine->commands.emplace_back(std::bind(&StateMachine::deleteAdHocSubProcessToken,stateMachine,this), this);
1058 return;
1059 }
1060 const_cast<StateMachine*>(owner)->attemptShutdown();
1061}
1062
1063void Token::advanceToDeparting() {
1064//std::cerr << "advanceToDeparting: " /*<< jsonify().dump()*/ << std::endl;
1065
1066 if ( node->outgoing.size() == 1 ) {
1067 auto engine = const_cast<Engine*>(owner->systemState->engine);
1068 engine->commands.emplace_back(std::bind(&Token::advanceToDeparted,this,node->outgoing.front()), this);
1069 return;
1070 }
1071
1072 if ( !node->represents<BPMN::Gateway>() ) {
1073 throw std::runtime_error("Token: implicit split at node '" + node->id + "'");
1074 }
1075
1076 if ( auto exclusiveGateway = node->represents<BPMN::ExclusiveGateway>() ) {
1077 for ( auto sequenceFlow : node->outgoing ) {
1078 if ( sequenceFlow != exclusiveGateway->defaultFlow ) {
1079 // check gatekeeper conditions
1080 if ( auto gatekeeper = sequenceFlow->extensionElements->as<BPMNOS::Model::Gatekeeper>() ) {
1081 if ( gatekeeper->conditionsSatisfied(status,*data,globals) ) {
1082 auto engine = const_cast<Engine*>(owner->systemState->engine);
1083 engine->commands.emplace_back(std::bind(&Token::advanceToDeparted,this,sequenceFlow), this);
1084 return;
1085 }
1086 }
1087 else {
1088 throw std::logic_error("Token: no gatekeeper provided for sequence flow '" + sequenceFlow->id + "'");
1089 }
1090 }
1091 }
1092
1093 // gatekeeper conditions are violated for all sequence flows (except default flow)
1094 if ( exclusiveGateway->defaultFlow ) {
1095 auto engine = const_cast<Engine*>(owner->systemState->engine);
1096 engine->commands.emplace_back(std::bind(&Token::advanceToDeparted,this,exclusiveGateway->defaultFlow), this);
1097 }
1098 else {
1099 auto engine = const_cast<Engine*>(owner->systemState->engine);
1100 engine->commands.emplace_back(std::bind(&Token::advanceToFailed,this), this);
1101 }
1102 }
1103 else if ( node->outgoing.size() > 1 ) {
1104 // non-exclusive diverging gateway
1105 auto engine = const_cast<Engine*>(owner->systemState->engine);
1106 engine->commands.emplace_back(std::bind(&StateMachine::handleDivergingGateway,const_cast<StateMachine*>(owner),this), this);
1107 }
1108}
1109
1110void Token::advanceToDeparted(const BPMN::SequenceFlow* sequenceFlow) {
1111//std::cerr << "advanceToDeparted: " << jsonify().dump() << std::endl;
1112 this->sequenceFlow = sequenceFlow;
1113 auto engine = const_cast<Engine*>(owner->systemState->engine);
1114 update(State::DEPARTED);
1115 engine->commands.emplace_back(std::bind(&Token::advanceToArrived,this), this);
1116}
1117
1118void Token::advanceToArrived() {
1119//std::cerr << "advanceToArrived: " << jsonify().dump() << std::endl;
1121 update(State::ARRIVED);
1122
1123 if ( node->incoming.size() > 1 && !node->represents<BPMN::ExclusiveGateway>() ) {
1124 if ( !node->represents<BPMN::Gateway>() ) {
1125 throw std::runtime_error("Token: implicit join at node '" + node->id + "'");
1126 }
1127 update(State::WAITING);
1128
1129 awaitGatewayActivation();
1130
1131 const_cast<StateMachine*>(owner)->attemptGatewayActivation(node);
1132
1133 return;
1134 }
1135
1136 if ( node->represents<BPMN::Activity>() ) {
1137 awaitReadyEvent();
1138 }
1139 else {
1140 sequenceFlow = nullptr;
1141 auto engine = const_cast<Engine*>(owner->systemState->engine);
1142 engine->commands.emplace_back(std::bind(&Token::advanceToEntered,this), this);
1143 }
1144}
1145
1146void Token::advanceToFailed() {
1147//std::cerr << " advanceToFailed: " << jsonify().dump() << std::endl;
1148 auto engine = const_cast<Engine*>(owner->systemState->engine);
1149
1150 if ( owned ) {
1151//std::cerr << "Use data of scope " << owner->scope->id << std::endl;
1152 data = &const_cast<StateMachine*>(owner)->data;
1153
1154 if ( !owned->scope ) {
1155 owned.reset();
1156 }
1157 else if ( auto activity = owned->scope->represents<BPMN::Activity>();
1158 activity && activity->isForCompensation
1159 ) {
1160 owned.reset();
1161 }
1162 else if ( owned->tokens.size() || owned->interruptingEventSubProcess ) {
1163 // owned state machine may require compensation
1164//std::cerr << "Failing " << owned->scope->id << std::endl;
1165 update(State::FAILING);
1166 engine->commands.emplace_back(std::bind(&Token::terminate,this), this);
1167 return;
1168 }
1169 }
1170 update(State::FAILED);
1171 engine->commands.emplace_back(std::bind(&StateMachine::handleFailure,const_cast<StateMachine*>(owner),this), this);
1172}
1173
1174void Token::terminate() {
1175//std::cerr << "terminate " << (node ? node->id : owner->scope->id ) << std::endl;
1176 auto engine = const_cast<Engine*>(owner->systemState->engine);
1177
1178 assert(owned);
1179
1180 owned->clearObsoleteTokens();
1181
1182 if ( owned->compensationTokens.size() ) {
1183 // don't delete state machine and wait for all compensations to be completed
1184 // before continuing with termination
1185 owned->compensate(owned->compensationTokens, this);
1186 return;
1187 }
1188
1189 // delete state machine
1190 owned.reset();
1191
1192 // all compensations have been completed, now handle failure
1193 engine->commands.emplace_back(std::bind(&Token::update,this,Token::State::FAILED), this);
1194 engine->commands.emplace_back(std::bind(&StateMachine::handleFailure,const_cast<StateMachine*>(owner),this), this);
1195//std::cerr << "terminated" << std::endl;
1196}
1197
1198void Token::awaitCompensation() {
1199 auto activity = node->as<BPMN::Activity>();
1200 if ( auto compensationActivity = activity->compensatedBy->represents<BPMN::Activity>();
1201 compensationActivity &&
1202 activity->loopCharacteristics != compensationActivity->loopCharacteristics
1203 ) {
1204 throw std::runtime_error("Token: compensation activities must have the same loop characteristics as the compensated activity '" + node->id + "'");
1205 }
1206
1207 auto stateMachine = const_cast<StateMachine*>(owner);
1208 auto engine = const_cast<Engine*>(owner->systemState->engine);
1209
1210 if ( auto subProcess = node->represents<BPMN::SubProcess>();
1211 subProcess && subProcess->compensationEventSubProcess
1212 ) {
1213 // create compensation event subprocess with token
1214 engine->commands.emplace_back( std::bind(&StateMachine::createCompensationEventSubProcess,stateMachine,subProcess->compensationEventSubProcess, status), stateMachine );
1215 }
1216 else {
1217 // find compensate boundary event
1218 auto it = std::find_if(activity->boundaryEvents.begin(), activity->boundaryEvents.end(), [](BPMN::FlowNode* boundaryEvent) {
1219 return ( boundaryEvent->represents<BPMN::CompensateBoundaryEvent>() );
1220 });
1221 if ( it != activity->boundaryEvents.end() ) {
1222 // create compensation token
1223 engine->commands.emplace_back( std::bind(&StateMachine::createCompensationTokenForBoundaryEvent,stateMachine,*it, status), stateMachine );
1224 }
1225 }
1226}
1227
1228void Token::awaitReadyEvent() {
1229//std::cerr << "awaitReadyEvent" << std::endl;
1230 auto systemState = const_cast<SystemState*>(owner->systemState);
1231 systemState->tokensAwaitingReadyEvent.emplace_back(weak_from_this());
1232}
1233
1234void Token::awaitEntryEvent() {
1235//std::cerr << "awaitEntryEvent" << std::endl;
1236
1237 auto systemState = const_cast<SystemState*>(owner->systemState);
1239 auto tokenAtSequentialPerformer = getSequentialPerformerToken();
1240//std::cerr << "Token: " << tokenAtSequentialPerformer->jsonify() << " pendingSequentialEntries add " << jsonify() << std::endl;
1241 tokenAtSequentialPerformer->pendingSequentialEntries.emplace_back(weak_from_this());
1242
1243 if ( tokenAtSequentialPerformer->performing ) {
1244//std::cerr << "Token: Waiting for perfomer to become idle" << std::endl;
1245 // defer decision request until performer becomes idle again
1246 return;
1247 }
1248 }
1249
1250 decisionRequest = std::make_shared<DecisionRequest>( this, Observable::Type::EntryRequest );
1251 systemState->pendingEntryDecisions.emplace_back( weak_from_this(), decisionRequest );
1252//std::cerr << "Token: Waiting for entry" << std::endl;
1253 owner->systemState->engine->notify(decisionRequest.get());
1254}
1255
1256void Token::awaitChoiceEvent() {
1257 auto systemState = const_cast<SystemState*>(owner->systemState);
1258 decisionRequest = std::make_shared<DecisionRequest>( this, Observable::Type::ChoiceRequest );
1259 owner->systemState->engine->notify(decisionRequest.get());
1260 systemState->pendingChoiceDecisions.emplace_back( weak_from_this(), decisionRequest );
1261
1262}
1263
1264void Token::awaitTaskCompletionEvent() {
1265 auto systemState = const_cast<SystemState*>(owner->systemState);
1267 systemState->tokensAwaitingCompletionEvent.emplace(time,weak_from_this());
1268}
1269
1270void Token::awaitExitEvent() {
1271//std::cerr << "awaitExitEvent" << std::endl;
1272 auto systemState = const_cast<SystemState*>(owner->systemState);
1273 decisionRequest = std::make_shared<DecisionRequest>( this, Observable::Type::ExitRequest );
1274 owner->systemState->engine->notify(decisionRequest.get());
1275 systemState->pendingExitDecisions.emplace_back( weak_from_this(), decisionRequest );
1276
1277}
1278
1279void Token::awaitMessageDelivery() {
1280//std::cerr << "awaitMessageDelivery" << std::endl;
1281 auto systemState = const_cast<SystemState*>(owner->systemState);
1282 decisionRequest = std::make_shared<DecisionRequest>( this, Observable::Type::MessageDeliveryRequest );
1283 owner->systemState->engine->notify(decisionRequest.get());
1284 systemState->pendingMessageDeliveryDecisions.emplace_back( weak_from_this(), decisionRequest );
1285}
1286
1287void Token::awaitTimer(BPMNOS::number time) {
1288 auto systemState = const_cast<SystemState*>(owner->systemState);
1289 systemState->tokensAwaitingTimer.emplace(time,weak_from_this());
1290}
1291
1292void Token::awaitSignal(BPMNOS::number name) {
1293 auto systemState = const_cast<SystemState*>(owner->systemState);
1294 systemState->tokensAwaitingSignal[name].emplace_back(weak_from_this());
1295}
1296
1297void Token::awaitConditions(BPMNOS::number instanceId) {
1298 auto systemState = const_cast<SystemState*>(owner->systemState);
1299 systemState->tokensAwaitingCondition[instanceId].emplace_back(weak_from_this());
1300}
1301
1302void Token::awaitGatewayActivation() {
1303//std::cerr << "awaitGatewayActivation" << std::endl;
1304
1305 auto systemState = const_cast<SystemState*>(owner->systemState);
1306 auto stateMachine = const_cast<StateMachine*>(owner);
1307 auto gatewayIt = systemState->tokensAwaitingGatewayActivation[stateMachine].find(node);
1308 if (gatewayIt == systemState->tokensAwaitingGatewayActivation[stateMachine].end()) {
1309 // The key is not found, so insert a new entry and get an iterator to it.
1310 gatewayIt = systemState->tokensAwaitingGatewayActivation[stateMachine].insert({node,{}}).first;
1311 }
1312
1313 auto& [key,tokens] = *gatewayIt;
1314 tokens.emplace_back(this);
1315}
1316
1317template<typename DecisionType, typename... Args>
1318std::shared_ptr<DecisionType> Token::createDecisionRequest(Args&&... args) {
1319//std::cerr << "Create pending decision for token in state " << stateName[(int)state] << " at node " << node->id << std::endl;
1320 auto event = std::make_shared<DecisionType>(this, std::forward<Args>(args)...);
1321 owner->systemState->engine->notify(event.get());
1322 return event;
1323}
1324
1325
1326void Token::withdraw() {
1327 if ( state != State::DONE && state != State::FAILED
1328 ) {
1329 update(State::WITHDRAWN);
1330 }
1331}
1332
1333void Token::emitSignal() {
1334 auto systemState = const_cast<SystemState*>(owner->systemState);
1335 assert( node->extensionElements->represents<BPMNOS::Model::Signal>() );
1336 auto signalDefinition = node->extensionElements->as<BPMNOS::Model::Signal>();
1337
1338 auto& waitingTokens = systemState->tokensAwaitingSignal[signalDefinition->name];
1339 if ( !waitingTokens.empty() ) {
1340 // determine signal content
1341 VariedValueMap contentValueMap = getSignalContent(signalDefinition->contentMap);
1342
1343 for ( auto& [token_ptr] : waitingTokens ) {
1344 auto token = token_ptr.lock();
1345 assert( token );
1346 // receive signal content
1347 token->setSignalContent(contentValueMap);
1348
1349 // advance receiving token
1350 auto engine = const_cast<Engine*>(owner->systemState->engine);
1351 engine->commands.emplace_back(std::bind(&Token::advanceToCompleted,token.get()), token.get());
1352 }
1353 waitingTokens.clear();
1354 }
1355}
1356
1357BPMNOS::VariedValueMap Token::getSignalContent(const BPMNOS::Model::ContentMap& contentMap) {
1358 auto& attributeRegistry = getAttributeRegistry();
1359 VariedValueMap contentValueMap;
1360 for (auto& [key,contentDefinition] : contentMap) {
1361 if ( status[contentDefinition->attribute->index].has_value() ) {
1362 contentValueMap.emplace( key, attributeRegistry.getValue(contentDefinition->attribute,status,*data,globals) );
1363 }
1364 else {
1365 contentValueMap.emplace( key, std::nullopt );
1366 }
1367 }
1368 return contentValueMap;
1369}
1370
1371void Token::setSignalContent(BPMNOS::VariedValueMap& sourceMap) {
1372 auto& attributeRegistry = getAttributeRegistry();
1373 assert( node->extensionElements->represents<BPMNOS::Model::Signal>() );
1374 auto signalDefinition = node->extensionElements->as<BPMNOS::Model::Signal>();
1375
1376 size_t counter = 0;
1377 for (auto& [key,contentValue] : sourceMap) {
1378 if ( auto it = signalDefinition->contentMap.find(key); it != signalDefinition->contentMap.end() ) {
1379 auto& [_,definition] = *it;
1380 auto attribute = definition->attribute;
1381//std::cerr << "Attribute: " << attribute.name << "/" << attribute.index << std::endl;
1382 if ( std::holds_alternative< std::optional<number> >(contentValue) && std::get< std::optional<number> >(contentValue).has_value() ) {
1383 // use attribute value of signal
1384 attributeRegistry.setValue(attribute, status, *data, globals, std::get< std::optional<number> >(contentValue).value() );
1385 }
1386 else if (std::holds_alternative<std::string>(contentValue)) {
1387 // use default value of emitter
1388 Value value = std::get< std::string >(contentValue);
1389 attributeRegistry.setValue(attribute, status, *data, globals, BPMNOS::to_number(value,attribute->type) );
1390 }
1391 else {
1392 attributeRegistry.setValue(attribute, status, *data, globals, std::nullopt );
1393 }
1394 }
1395 else {
1396 // key in signal content, but not in recipient content
1397 counter++;
1398 }
1399 }
1400
1401 if ( signalDefinition->contentMap.size() > sourceMap.size() - counter ) {
1402 // recipient has keys in content that are not in signal content
1403 for (auto& [key,definition] : signalDefinition->contentMap) {
1404 if ( !sourceMap.contains(key) ) {
1405 // key in recipient content, but not in message content
1406 attributeRegistry.setValue(definition->attribute, status, *data, globals, std::nullopt );
1407 }
1408 }
1409 }
1410
1411 // notify about data update
1412 owner->systemState->engine->notify( DataUpdate( owner->root->instance.value(), signalDefinition->updatedData ) );
1413}
1414
1415
1416void Token::sendMessage(size_t index) {
1417 auto systemState = const_cast<SystemState*>(owner->systemState);
1418 systemState->messages.emplace_back(std::make_shared<Message>(this,index));
1419 auto& message = systemState->messages.back();
1420
1421 if ( message->recipient.has_value() ) {
1422 // TODO: add to unsent or outbox
1423 auto it = systemState->archive.find((long unsigned int)message->recipient.value());
1424 if ( it == systemState->archive.end() ) {
1425//std::cerr << "Message unsent" << std::endl;
1426 // defer sending of message to when recipient is instantiated
1427 systemState->unsent[(long unsigned int)message->recipient.value()].emplace_back(message->weak_from_this());
1428 }
1429 else if ( auto stateMachine = it->second.lock() ) {
1430//std::cerr << "Message sent from " << node->id << std::endl;
1431 systemState->inbox[stateMachine.get()].emplace_back(message->weak_from_this());
1432 systemState->outbox[node].emplace_back(message->weak_from_this());
1433 }
1434 }
1435 else {
1436//std::cerr << "Message sent from " << node->id << std::endl;
1437 systemState->outbox[node].emplace_back(message->weak_from_this());
1438 }
1439
1440 if ( node->represents<BPMN::SendTask>() ) {
1441 systemState->messageAwaitingDelivery[this] = message->weak_from_this();
1442 }
1443
1444 // message->state = Message::State::CREATED;
1445 owner->systemState->engine->notify(message.get());
1446
1447}
1448
1451 assert( adHocSubProcess );
1452
1453 Token* sequentialPerfomerToken = owner->parentToken;
1454 while (sequentialPerfomerToken->node && sequentialPerfomerToken->node != adHocSubProcess->performer) {
1455 sequentialPerfomerToken = sequentialPerfomerToken->owner->parentToken;
1456 }
1457 return sequentialPerfomerToken;
1458}
1459
1460void Token::occupySequentialPerformer() {
1461 auto tokenAtSequentialPerformer = getSequentialPerformerToken();
1462//std::cerr << "Token::releaseSequentialPerformer " << std::endl;
1463 assert( !tokenAtSequentialPerformer->performing );
1464 tokenAtSequentialPerformer->pendingSequentialEntries.remove(this);
1465
1466 // sequential performer becomes busy
1467 tokenAtSequentialPerformer->performing = this;
1468 owner->systemState->engine->notify(SequentialPerformerUpdate(tokenAtSequentialPerformer));
1469
1470 for ( auto& [ token_ptr ] : tokenAtSequentialPerformer->pendingSequentialEntries ) {
1471 if ( auto activityToken = token_ptr.lock() ) {
1472 // withdraw entry decision requests for each child activity awaiting entry
1473 assert( activityToken->node->represents<BPMN::Activity>() && activityToken->state == State::READY );
1474 assert( activityToken->decisionRequest );
1475//std::cerr << "Token: Withdraw decision " << activityToken->jsonify() << std::endl;
1476 activityToken->decisionRequest.reset();
1477 }
1478 }
1479}
1480
1481void Token::releaseSequentialPerformer() {
1482 auto systemState = const_cast<SystemState*>(owner->systemState);
1483 auto tokenAtSequentialPerformer = getSequentialPerformerToken();
1484//std::cerr << "Token::releaseSequentialPerformer " << std::endl;
1485 assert( tokenAtSequentialPerformer->performing );
1486
1487 // sequential performer becomes idle
1488 tokenAtSequentialPerformer->performing = nullptr;
1489 systemState->engine->notify(SequentialPerformerUpdate(tokenAtSequentialPerformer));
1490
1491 for ( auto& [ token_ptr ] : tokenAtSequentialPerformer->pendingSequentialEntries ) {
1492 if ( auto activityToken = token_ptr.lock() ) {
1493 // create entry decision requests for each child activity awaiting entry
1494 assert( activityToken->node->represents<BPMN::Activity>() && activityToken->state == State::READY );
1495 assert( !activityToken->decisionRequest );
1496//std::cerr << "Token: Renew decision request" << activityToken->jsonify() << std::endl;
1497 activityToken->decisionRequest = std::make_shared<DecisionRequest>( activityToken.get(), Observable::Type::EntryRequest );
1498 systemState->pendingEntryDecisions.emplace_back(activityToken,activityToken->decisionRequest);
1499 owner->systemState->engine->notify(activityToken->decisionRequest.get());
1500 }
1501 }
1502}
1503
1504
1505void Token::update(State newState) {
1506 assert( status.size() >= 1 );
1507 assert( data->size() >= 1 );
1508 assert( (*data)[BPMNOS::Model::ExtensionElements::Index::Instance].get().has_value() );
1510
1511 state = newState;
1512 auto now = owner->systemState->getTime();
1513
1514//std::cerr << "update at time " << now << ": " << jsonify().dump() << std::endl;
1516//std::cerr << "Set timestamp to " << now << std::endl;
1517 // increase timestamp if necessary
1519 }
1520 else if (
1524 ) {
1525//std::cerr << now << "/" << this->jsonify().dump() << now << std::endl;
1526 throw std::runtime_error("Token: timestamp at node '" + node->id + "' is larger than current time");
1527 }
1528 notify();
1529}
1530
1531void Token::notify() const {
1532 owner->systemState->engine->notify(this);
1533}
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.
StateMachines pendingEventSubProcesses
Container with state machines of all inactive event subprocesses that may be triggered.
const SystemState * systemState
const BPMN::Process * process
Pointer to the top-level process.
StateMachines nonInterruptingEventSubProcesses
Container with state machines of all active event subprocesses that are not interrupting.
const StateMachine * root
Pointer to the root state machine.
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.
A class representing the state that the execution or simulation of a given scenario is in.
Definition SystemState.h:21
BPMNOS::number currentTime
Timestamp holding the point in time that the engine is in (this is usually representing now).
Definition SystemState.h:37
std::unordered_map< BPMNOS::number, auto_list< std::weak_ptr< Token > > > tokensAwaitingSignal
Map holding a container of all tokens at a signal event awaiting a signal with a given name.
BPMNOS::number getTime() const
Function returning the current time.
Messages messages
Container holding all messages created by a throwing message event.
std::unordered_map< Token *, std::vector< Token * > > tokensAwaitingBoundaryEvent
Map holding a container of all tokens at a boundary event awaiting to be triggered for each token at ...
Definition SystemState.h:96
auto_list< std::weak_ptr< Token > > tokensAwaitingReadyEvent
Container holding all tokens awaiting a ready event.
Definition SystemState.h:71
auto_set< BPMNOS::number, std::weak_ptr< Token > > tokensAwaitingTimer
Sorted container holding holding all tokens awaiting a timer event.
Definition SystemState.h:99
std::unordered_map< BPMNOS::number, auto_list< std::weak_ptr< Token > > > tokensAwaitingCondition
Map holding a container of all tokens at a conditional event belonginge to a process instance.
Represents a token running through a (sub)process.
Definition Token.h:35
BPMNOS::number getInstanceId() const
Definition Token.cpp:156
const BPMN::FlowNode * node
Definition Token.h:46
Token * performing
Pointer to the activity token currently performed (only applies if node is a performer referenced by ...
Definition Token.h:61
const BPMNOS::Model::AttributeRegistry & getAttributeRegistry() const
Definition Token.cpp:139
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
std::shared_ptr< DecisionRequest > decisionRequest
Definition Token.h:60
const BPMN::SequenceFlow * sequenceFlow
Definition Token.h:47
static std::string stateName[]
Definition Token.h:50
Token * getSequentialPerformerToken() const
Returns token at sequential performer for tokens at activities within sequential adhoc subprocesses.
Definition Token.cpp:1449
Token(const StateMachine *owner, const BPMN::FlowNode *node, const Values &status)
Definition Token.cpp:22
nlohmann::ordered_json jsonify() const
Definition Token.cpp:160
std::vector< Attribute * > statusAttributes
Class holding extension elements representing conditions for sequence flows and conditional events.
Definition Conditions.h:18
Class representing a task in which one or more choices have to be made.
Class holding extension elements representing execution data for nodes.
std::vector< std::unique_ptr< Operator > > operators
std::vector< std::unique_ptr< Attribute > > attributes
Vector containing new status attributes declared for the node.
void computeInitialValues(BPMNOS::number currentTime, BPMNOS::Values &status, DataType &data, BPMNOS::Values &globals) const
struct BPMNOS::Model::ExtensionElements::@0 dataUpdate
Struct containing data attributes that are modified through operators and a flag indicating whether a...
bool isInstantaneous
Boolean indicating whether operators may modify timestamp.
bool feasibleEntry(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
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.
std::optional< std::unique_ptr< Parameter > > loopIndex
Attribute holding the automatically generated loop index.
std::optional< std::unique_ptr< Parameter > > loopCondition
Boolean attribute indicating whether an exit condition holds.
void applyOperators(BPMNOS::Values &status, DataType &data, BPMNOS::Values &globals) const
AttributeRegistry attributeRegistry
Registry allowing to look up all status and data attributes by their names.
std::vector< std::unique_ptr< Attribute > > data
Vector containing data attributes declared for data objects within the node's scope.
std::optional< std::unique_ptr< Parameter > > loopMaximum
Maximum number of iterations of a standard loop (requires loopIndex).
Class holding extension elements representing gatekeeper conditions for sequence flows.
Definition Gatekeeper.h:17
Class representing adhoc subprocesses with sequential ordering.
Class holding extension elements representing the definition of signal events.
Definition Signal.h:17
BPMNOS::number name
Signal name.
Definition Signal.h:22
Class holding extension elements representing the trigger of timer events.
Definition Timer.h:16
std::vector< BoundaryEvent * > boundaryEvents
Definition bpmn++.h:17407
bool isForCompensation
Definition bpmn++.h:17409
std::optional< LoopCharacteristics > loopCharacteristics
Definition bpmn++.h:17411
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
std::vector< std::reference_wrapper< T > > get()
Returns a vector of elements of type T embedded within a container of type T.
Definition bpmn++.h:16367
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
EventSubProcess * compensationEventSubProcess
Pointer to compensation event subprocess of the scope.
Definition bpmn++.h:16534
The SequenceFlow class encapsulates the information and relationships associated with a sequence flow...
Definition bpmn++.h:16657
FlowNode * target
Reference to the target node of the sequence flow.
Definition bpmn++.h:16667
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::unordered_map< std::string, std::unique_ptr< Content > > ContentMap
Definition Content.h:27
std::unordered_map< std::string, std::variant< std::optional< number >, std::string > > VariedValueMap
Definition Number.h:69
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_NUMBER_TYPE number
Definition Number.h:50
@ INTEGER
Definition Value.h:9
@ STRING
Definition Value.h:9
@ BOOLEAN
Definition Value.h:9
@ COLLECTION
Definition Value.h:9
@ DECIMAL
Definition Value.h:9
std::variant< bool, int, double, std::string > Value
Definition Value.h:10