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