BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
FlattenedGraph.cpp
Go to the documentation of this file.
1#include "FlattenedGraph.h"
8#include <ranges>
9#include <iostream>
10
11using namespace BPMNOS::Execution;
12
13/**********************
14** Vertex
15**********************/
16
17FlattenedGraph::Vertex::Vertex(FlattenedGraph* graph, BPMNOS::number rootId, BPMNOS::number instanceId, std::vector< size_t > loopIndices, const BPMN::Node* node, Type type, std::optional< std::pair<Vertex*, Vertex*> > parent)
18 : graph(graph)
19 , index(graph->vertices.size())
20 , rootId(rootId)
21 , instanceId(instanceId)
22 , loopIndices(std::move(loopIndices))
23 , node(node)
24 , type(type)
25 , parent(parent)
26{
27assert(node);
28 if ( parent.has_value() ) {
29 dataOwners = parent.value().first->dataOwners;
30 }
31}
32
34 assert( node->represents<BPMN::Activity>() );
36/*
37 auto performer = node->as<BPMN::Activity>()->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>()->performer;
38 const Vertex* entry = (type == Type::ENTRY ? this : this - 1);
39 const Vertex* exit = (type == Type::EXIT ? this : this + 1);
40 do {
41 assert( entry->parent.has_value() );
42 auto parentVertices = entry->parent.value();
43 entry = &parentVertices.first;
44 exit = &parentVertices.second;
45 } while ( entry->node != performer );
46
47 return entry;
48*/
49 auto adHocSubProcess = node->as<BPMN::Activity>()->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>();
50 assert( adHocSubProcess );
51 assert( parent.has_value() );
52
53 auto vertex = parent.value().first;
54 while (vertex->node && vertex->node != adHocSubProcess->performer) {
55 assert( vertex->parent.has_value() );
56 vertex = vertex->parent.value().first;
57 }
58 return vertex;
59}
60
63 for ( size_t index = 0; index < dataOwners.size(); index++) {
64 auto extensionElements = dataOwners[index]->node->extensionElements->as<BPMNOS::Model::ExtensionElements>();
65 if (
66 attribute->index < extensionElements->attributeRegistry.dataAttributes.size() &&
67 attribute->index >= extensionElements->attributeRegistry.dataAttributes.size() - extensionElements->data.size()
68 ) {
69 return index;
70 }
71 }
72 throw std::logic_error("FlattenedGraph: unable to determine data owner index for '" + attribute->id + "'");
73}
74
75std::pair< const FlattenedGraph::Vertex*, const FlattenedGraph::Vertex*> FlattenedGraph::Vertex::dataOwner( const BPMNOS::Model::Attribute* attribute ) const {
76 auto index = dataOwnerIndex(attribute);
77 return { dataOwners[index], (dataOwners[index] + 1) };
78}
79
81 return shortReference() + "," + ( type == Type::ENTRY ? "entry" : "exit" );
82}
83
85 std::string result = BPMNOS::to_string(instanceId, STRING) + "," + node->id;
86 for ( auto index : loopIndices ) {
87 result += "°" + std::to_string(index);
88 }
89 return result;
90}
91
92nlohmann::ordered_json FlattenedGraph::Vertex::jsonify() const {
93 nlohmann::ordered_json jsonObject;
94 jsonObject["vertex"] = reference();
95 jsonObject["inflows"] = nlohmann::json::array();
96 for ( auto& [_,vertex] : inflows ) {
97 jsonObject["inflows"].push_back(vertex->reference());
98 }
99 jsonObject["outflows"] = nlohmann::json::array();
100 for ( auto& [_,vertex] : outflows ) {
101 jsonObject["outflows"].push_back(vertex->reference());
102 }
103 jsonObject["predecessors"] = nlohmann::json::array();
104 for ( auto vertex : predecessors ) {
105 jsonObject["predecessors"].push_back(vertex->reference());
106 }
107 jsonObject["successors"] = nlohmann::json::array();
108 for ( auto vertex : successors ) {
109 jsonObject["successors"].push_back(vertex->reference());
110 }
111
112 return jsonObject;
113}
114
115/**********************
116** FlattenedGraph
117**********************/
118
120 // get all known instances
122//std::cerr << "Instances: " << instances.size() << std::endl;
123 for ( auto& instance : instances ) {
124 addInstance( instance );
125 }
126}
127
128nlohmann::ordered_json FlattenedGraph::jsonify() const {
129 nlohmann::ordered_json jsonObject = nlohmann::json::array();
130 for ( auto& vertex : vertices ) {
131 jsonObject.push_back(vertex->jsonify());
132 }
133 return jsonObject;
134}
135
137//std::cerr << "Add instance: " << instance->id << std::endl;
138 // create process vertices
139 auto [ entry, exit ] = createVertexPair(instance->id, instance->id, {}, instance->process, std::nullopt);
140 initialVertices.push_back(entry);
141 flatten( instance->id, instance->process, entry, exit );
142}
143
144void FlattenedGraph::addNonInterruptingEventSubProcess( const BPMN::EventSubProcess* eventSubProcess, Vertex* parentEntry, Vertex* parentExit ) {
145 nonInterruptingEventSubProcesses.emplace_back(eventSubProcess, parentEntry, parentExit, 0, nullptr);
146 // iterate through all known trigger and flatten event-subprocess instantiations
147 auto& counter = std::get<3>( nonInterruptingEventSubProcesses.back() );
148 auto& lastStart = std::get<4>( nonInterruptingEventSubProcesses.back() );
149 assert( eventSubProcess->startEvent->represents<BPMN::MessageStartEvent>() );
150 assert( eventSubProcess->startEvent->extensionElements );
151 assert( eventSubProcess->startEvent->extensionElements->represents<BPMNOS::Model::ExtensionElements>() );
152 auto& candidates = eventSubProcess->startEvent->extensionElements->as<BPMNOS::Model::ExtensionElements>()->messageCandidates;
153 for ( auto candidate : candidates ) {
154 if( sendingVertices.contains(candidate) ) {
155 for ( [[maybe_unused]] auto& _ : sendingVertices.at(candidate) ) {
156 // create and flatten next event-subprocess
157 counter++;
158 BPMNOS::number id =
160 BPMNOS::to_string(parentEntry->instanceId,STRING) +
162 eventSubProcess->id +
164 std::to_string(counter)
165 , STRING)
166 ;
167 flatten( id, eventSubProcess, parentEntry, parentExit );
168 // newly created vertices at start event must succeed previous vertices at start event
169 auto& [startEntry,startExit] = vertexMap.at({ id, parentEntry->loopIndices, eventSubProcess->startEvent });
170 if ( lastStart ) {
171 lastStart->successors.push_back(startEntry);
172 startEntry->predecessors.push_back(lastStart);
173 }
174 lastStart = startExit;
175 }
176 }
177 }
178}
179
180void FlattenedGraph::addSender( const BPMN::MessageThrowEvent* messageThrowEvent, Vertex* senderEntry, Vertex* senderExit ) {
181//std::cerr << "Add sender: " << senderEntry.reference() << std::endl;
182 // flatten event subprocesses if applicable
183 assert( messageThrowEvent->extensionElements );
184 assert( messageThrowEvent->extensionElements->represents<BPMNOS::Model::ExtensionElements>() );
185 auto& candidates = messageThrowEvent->extensionElements->as<BPMNOS::Model::ExtensionElements>()->messageCandidates;
186 for ( auto& [eventSubProcess, parentEntry, parentExit, counter, lastStart] : nonInterruptingEventSubProcesses ) {
187 if (std::find(candidates.begin(), candidates.end(), eventSubProcess->startEvent) != candidates.end()) {
188 // TODO: check header
189 // eventSubProcess may be triggered by message throw event, create and flatten next event-subprocess
190 counter++;
191//std::cerr << "Add non-interrupting event-subprocess: " << eventSubProcess->id << "#" << counter << std::endl;
192 BPMNOS::number id = BPMNOS::to_number( BPMNOS::to_string(parentEntry->instanceId,STRING) + BPMNOS::Model::Scenario::delimiters[0] + eventSubProcess->id + BPMNOS::Model::Scenario::delimiters[1] + std::to_string(counter), STRING);
193 flatten( id, eventSubProcess, parentEntry, parentExit );
194 // newly created vertices at start event must succeed previous vertices at start event
195 auto& [startEntry,startExit] = vertexMap.at({ id, parentEntry->loopIndices, eventSubProcess->startEvent });
196 if ( lastStart ) {
197 lastStart->successors.push_back(startEntry);
198 startEntry->predecessors.push_back(lastStart);
199 }
200 lastStart = startExit;
201 }
202 }
203
204 // set precedences for all recipients
205 for ( auto candidate : candidates ) {
206 for ( auto& [ recipientEntry, recipientExit] : receivingVertices[candidate] ) {
207 // TODO: check header
208 senderEntry->recipients.push_back(recipientExit);
209 recipientExit->senders.push_back(senderEntry);
210 if ( messageThrowEvent->represents<BPMN::SendTask>() ) {
211 recipientExit->recipients.push_back(senderExit);
212 senderExit->senders.push_back(recipientExit);
213 }
214 }
215 }
216
217 sendingVertices[messageThrowEvent].emplace_back(senderEntry, senderExit);
218}
219
220void FlattenedGraph::addRecipient( const BPMN::MessageCatchEvent* messageCatchEvent, Vertex* recipientEntry, Vertex* recipientExit ) {
221 // set precedences for all senders
222 assert( messageCatchEvent->extensionElements );
223 assert( messageCatchEvent->extensionElements->represents<BPMNOS::Model::ExtensionElements>() );
224 auto& candidates = messageCatchEvent->extensionElements->as<BPMNOS::Model::ExtensionElements>()->messageCandidates;
225 for ( auto candidate : candidates ) {
226 for ( auto& [ senderEntry, senderExit] : sendingVertices[candidate] ) {
227 // TODO: check header
228 senderEntry->recipients.push_back(recipientExit);
229 recipientExit->senders.push_back(senderEntry);
230 if ( candidate->represents<BPMN::SendTask>() ) {
231 recipientExit->recipients.push_back(senderExit);
232 senderExit->senders.push_back(recipientExit);
233 }
234 }
235 }
236
237 receivingVertices[messageCatchEvent].emplace_back(recipientEntry, recipientExit);
238}
239
241 auto extensionElements = activity->extensionElements->as<BPMNOS::Model::ExtensionElements>();
242 assert(extensionElements);
243 const BPMNOS::Model::Attribute* attribute =
244 extensionElements->loopIndex.has_value() ?
245 extensionElements->loopIndex.value()->expression->isAttribute() :
246 nullptr
247 ;
248 if ( !attribute ) {
249 throw std::runtime_error("FlattenedGraph: unable to determine loop index for activity '" + activity->id + "'");
250 }
252 return attribute;
253}
254
255std::pair<FlattenedGraph::Vertex*, FlattenedGraph::Vertex*> FlattenedGraph::createVertexPair(BPMNOS::number rootId, BPMNOS::number instanceId, std::vector< size_t > loopIndices, const BPMN::Node* node, std::optional< std::pair<Vertex*, Vertex*> > parent) {
256 vertices.emplace_back(std::make_unique<Vertex>(this, rootId, instanceId, loopIndices, node, Vertex::Type::ENTRY, parent));
257 auto entry = vertices.back().get();
258 vertices.emplace_back(std::make_unique<Vertex>(this, rootId, instanceId, loopIndices, node, Vertex::Type::EXIT, parent));
259 auto exit = vertices.back().get();
260//std::cerr << "Add vertex pair: " << entry.reference() << std::endl;
261 if ( node->represents<BPMN::Scope>() ) {
262 entry->successors.push_back(exit);
263 exit->predecessors.push_back(entry);
264 }
265 else {
266 entry->outflows.emplace_back(nullptr,exit);
267 exit->inflows.emplace_back(nullptr,entry);
268 }
269
270 if ( parent.has_value() ) {
271 entry->predecessors.push_back( parent.value().first );
272 exit->successors.push_back( parent.value().second );
273 parent.value().first->successors.push_back( entry );
274 parent.value().second->predecessors.push_back( exit );
275 }
276
277 vertexMap.emplace( {instanceId,loopIndices,node}, {entry,exit} );
278
279 if ( !loopIndexAttributes.contains(node) ) {
280 std::vector< const BPMNOS::Model::Attribute* > attributes = (
281 parent.has_value() ?
282 loopIndexAttributes.at(parent.value().first->node) :
283 std::vector< const BPMNOS::Model::Attribute* >()
284 );
285
286 if ( auto activity = node->represents<BPMN::Activity>();
287 activity &&
288 activity->loopCharacteristics.has_value() &&
289 activity->loopCharacteristics.value() == BPMN::Activity::LoopCharacteristics::Standard
290 ) {
291 attributes.push_back( getLoopIndexAttribute(activity) );
292 }
293//std::cerr << node->id << " has " << attributes.size() << " loop index attributes" << std::endl;
294 loopIndexAttributes[node] = std::move(attributes);
295 }
296
297 assert( node->extensionElements );
298 auto extensionElements = node->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
299
300 if ( !extensionElements ) {
301 return { entry, exit };
302 }
303
304 if ( auto messageThrowEvent = node->represents<BPMN::MessageThrowEvent>() ) {
305 addSender( messageThrowEvent, entry, exit );
306 }
307 else if ( auto messageCatchEvent = node->represents<BPMN::MessageCatchEvent>() ) {
308 addRecipient( messageCatchEvent, entry, exit );
309 }
310
311 if ( extensionElements->data.size() ) {
312 entry->dataOwners.push_back( entry );
313 exit->dataOwners.push_back( entry );
314 }
315
316 // populate lookup maps of sequential activities for each performer
317 if ( extensionElements->hasSequentialPerformer ) {
318 sequentialActivities.emplace(entry,std::vector< std::pair<const Vertex*, const Vertex*> >());
319 }
320 else if ( auto sequentialAdHocSubProcess = node->represents<BPMNOS::Model::SequentialAdHocSubProcess>();
321 sequentialAdHocSubProcess && !sequentialAdHocSubProcess->performer
322 ) {
323 sequentialActivities.emplace(entry,std::vector< std::pair<const Vertex*, const Vertex*> >());
324 }
325
326 if ( auto activity = node->represents<BPMN::Activity>();
327 activity &&
329 ) {
330 sequentialActivities.at( entry->performer() ).push_back( { entry, exit } );
331 }
332
333 // populate lookup maps of data modifiers for data owner
334 if ( extensionElements->data.size() ) {
335 dataModifiers.emplace(entry,std::vector< std::pair<const Vertex*, const Vertex*> >());
336 }
337
338 if ( node->represents<BPMN::Task>() ) {
339 std::unordered_set< const Vertex* > dataOwners;
340 bool modifiesGlobals = false;
341 for ( auto& operator_ : extensionElements->operators ) {
342 if ( operator_->attribute->category == BPMNOS::Model::Attribute::Category::DATA ) {
343 dataOwners.emplace( entry->dataOwner(operator_->attribute).first );
344 }
345 else if ( operator_->attribute->category == BPMNOS::Model::Attribute::Category::GLOBAL ) {
346 modifiesGlobals = true;
347 }
348 }
349 for ( auto dataOwner : dataOwners ) {
350 assert( dataModifiers.contains( dataOwner ) );
351 dataModifiers.at( dataOwner ).push_back( { entry, exit } );
352 }
353 if ( modifiesGlobals ) {
354 globalModifiers.push_back( { entry, exit } );
355 }
356 }
357
358 if ( auto typedStartEvent = node->represents<BPMN::TypedStartEvent>() ) {
359 extensionElements = typedStartEvent->parent->extensionElements->as<BPMNOS::Model::ExtensionElements>();
360 std::unordered_set< const Vertex* > dataOwners;
361 bool modifiesGlobals = false;
362 for ( auto& operator_ : extensionElements->operators ) {
363 if ( operator_->attribute->category == BPMNOS::Model::Attribute::Category::DATA ) {
364 dataOwners.emplace( entry->dataOwner(operator_->attribute).first );
365 }
366 else if ( operator_->attribute->category == BPMNOS::Model::Attribute::Category::GLOBAL ) {
367 modifiesGlobals = true;
368 }
369 }
370 for ( auto dataOwner : dataOwners ) {
371 assert( dataModifiers.contains( dataOwner ) );
372 dataModifiers.at( dataOwner ).push_back( { entry, exit } );
373 }
374 if ( modifiesGlobals ) {
375 globalModifiers.push_back( { entry, exit } );
376 }
377 }
378
379 return { entry, exit };
380}
381
382void FlattenedGraph::createLoopVertices(BPMNOS::number rootId, BPMNOS::number instanceId, std::vector< size_t > loopIndices, const BPMN::Activity* activity, std::optional< std::pair<Vertex*, Vertex*> > parent) {
383 // loop & multi-instance activties
384
385 // create main vertices
386 vertices.emplace_back(std::make_unique<Vertex>(this, rootId, instanceId, loopIndices, activity, Vertex::Type::ENTRY, parent));
387 auto entry = vertices.back().get();
388 vertices.emplace_back(std::make_unique<Vertex>(this, rootId, instanceId, loopIndices, activity, Vertex::Type::EXIT, parent));
389 auto exit = vertices.back().get();
390 entry->successors.push_back(exit);
391 exit->predecessors.push_back(entry);
392
393 vertexMap.emplace( {instanceId,loopIndices,activity}, {entry,exit} );
394 dummies.insert( entry );
395 dummies.insert( exit );
396 assert( parent.has_value() );
397
398 entry->predecessors.push_back( parent.value().first );
399 exit->successors.push_back( parent.value().second );
400 parent.value().first->successors.push_back( entry );
401 parent.value().second->predecessors.push_back( exit );
402
403 // create loopIndexAttributes
404 if ( !loopIndexAttributes.contains(activity) ) {
405 assert( loopIndexAttributes.contains(parent.value().first->node) );
406 auto attributes = loopIndexAttributes.at(parent.value().first->node);
408 attributes.push_back( getLoopIndexAttribute(activity) );
409 }
410//std::cerr << activity->id << " has " << attributes.size() << " loop index attributes" << std::endl;
411 loopIndexAttributes[activity] = std::move(attributes);
412 }
413
414 // lambda returning parameter value known at time zero
415 auto getValue = [&](BPMNOS::Model::Parameter* parameter) -> std::optional<BPMNOS::number> {
416 if ( parameter->expression ) {
417//std::cerr << parameter->expression->expression << std::endl;
418 // collect variable values
419 std::vector< double > variableValues;
420 for ( auto attribute : parameter->expression->variables ) {
421 if ( !attribute->isImmutable ) {
422 throw std::runtime_error("FlattenedGraph: Loop parameter '" + parameter->name + "' for activity '" + activity->id +"' must be immutable" );
423 }
424 auto value = scenario->getKnownValue(rootId, attribute, scenario->getEarliestInstantiationTime() );
425 if ( !value.has_value() ) {
426//std::cerr << "unknown value of '" << attribute->id << "'" << std::endl;
427 // return nullopt because required attribute value is not given
428 return std::nullopt;
429 }
430 variableValues.push_back( (double)value.value() );
431 }
432
433 // collect values of all variables in collection
434 std::vector< std::vector< double > > collectionValues;
435 for ( auto attribute : parameter->expression->collections ) {
436 if ( !attribute->isImmutable ) {
437 throw std::runtime_error("FlattenedGraph: Loop parameter '" + parameter->name + "' for activity '" + activity->id +"' must be immutable" );
438 }
439 collectionValues.push_back( {} );
440 auto collection = scenario->getKnownValue(rootId, attribute, scenario->getEarliestInstantiationTime() );
441 if ( !collection.has_value() ) {
442//std::cerr << "unknown collection value of '" << attribute->id << "'" << std::endl;
443 // return nullopt because required collection is not given
444 return std::nullopt;
445 }
446 for ( auto value : collectionRegistry[(size_t)collection.value()] ) {
447 collectionValues.back().push_back( value );
448 }
449 }
450
451//std::cerr << parameter->expression->expression << " = " << number(parameter->expression->compiled.evaluate(variableValues,collectionValues)) << std::endl;
452 return number(parameter->expression->compiled.evaluate(variableValues,collectionValues));
453 }
454
455 return std::nullopt;
456 };
457
458 // determine number of vertices to be created
459 int n = 0;
460 auto extensionElements = activity->extensionElements->represents<BPMNOS::Model::ExtensionElements>();
461 assert(extensionElements);
462
464/*
465 if( activity->represents<BPMN::SubProcess>() ) {
466 throw std::runtime_error("FlattenedGraph: loop subprocesses are not yet supported" );
467 }
468*/
469 if ( extensionElements->loopMaximum.has_value() ) {
470 auto value = getValue( extensionElements->loopMaximum.value().get() );
471 n = value.has_value() ? (int)value.value() : 0;
472 }
473 }
474 else {
475 if ( extensionElements->loopCardinality.has_value() ) {
476 auto value = getValue( extensionElements->loopCardinality.value().get() );
477 n = value.has_value() ? (int)value.value() : 0;
478 }
479 }
480
481 if ( n <= 0 ) {
482 throw std::runtime_error("FlattenedGraph: cannot determine loop maximum/cardinality for activity '" + activity->id +"'" );
483 }
484
485
486
487// auto& container = vertexMap[activity][instanceId]; // get or create container
488// container.emplace_back( entry );
489
491//std::cerr << "Standard" << std::endl;
492 // create vertices for loop activity
493 Vertex* previous = entry;
494 loopIndices.push_back(0);
495 assert( loopIndexAttributes.contains(previous->node) );
496//std::cerr << entry.reference() << ": " << loopIndices.size() << "/" << loopIndexAttributes.at(entry.node).size() << std::endl;
497 assert( loopIndices.size() == loopIndexAttributes.at(previous->node).size() );
498 for ( size_t i = 1; i <= (size_t)n; i++ ) {
499 loopIndices.back() = i;
500 auto [ loopingEntry, loopingExit ] = createVertexPair(rootId, instanceId, loopIndices, activity, parent);
501 // every looping entry vertex has an inflow from the previous
502 loopingEntry->inflows.emplace_back(nullptr,previous);
503 previous->outflows.emplace_back(nullptr,loopingEntry);
504
505 // every looping exit can be the last
506 exit->inflows.emplace_back(nullptr,loopingExit );
507 loopingExit->outflows.emplace_back(nullptr,exit );
508
509 previous = loopingExit;
510
511 if( auto subprocess = activity->represents<BPMN::SubProcess>() ) {
512 flatten(instanceId, subprocess, loopingEntry, loopingExit );
513 }
514 }
515 }
516 else {
517//std::cerr << "MultiInstance" << std::endl;
518 std::string baseName = BPMNOS::to_string(instanceId,STRING) + BPMNOS::Model::Scenario::delimiters[0] + activity->id + BPMNOS::Model::Scenario::delimiters[1] ;
519 // create vertices for multi-instance activity
520 Vertex* previous = nullptr;
521 for ( int i = 1; i <= n; i++ ) {
522 auto multiInstanceId = BPMNOS::to_number(baseName + std::to_string(i),STRING);
523 auto [ multiInstanceEntry, multiInstanceExit ] = createVertexPair(rootId, multiInstanceId, loopIndices, activity, parent);
524 // every multi-instance entry vertex has an inflow from the main entry
525 multiInstanceEntry->inflows.emplace_back(nullptr,entry);
526 entry->outflows.emplace_back(nullptr,multiInstanceEntry);
527 // every multi-instance exit vertex has an outflow to the main exit
528 multiInstanceExit->outflows.emplace_back(nullptr,exit);
529 exit->inflows.emplace_back(nullptr,multiInstanceExit);
530
531 if (
532 previous &&
534 ) {
535 previous->successors.push_back(multiInstanceEntry);
536 multiInstanceEntry->predecessors.push_back(previous);
537 }
538 previous = multiInstanceExit;
539
540 if( auto subprocess = activity->represents<BPMN::SubProcess>() ) {
541 flatten(multiInstanceId, subprocess, multiInstanceEntry, multiInstanceExit );
542 }
543 }
544 }
545
546//std::cerr << "done" << std::endl;
547
548}
549
550void FlattenedGraph::flatten(BPMNOS::number instanceId, const BPMN::Scope* scope, Vertex* scopeEntry, Vertex* scopeExit) {
551//std::cerr << "flatten: " << BPMNOS::to_string(instanceId,STRING) << "/" << scopeEntry.shortReference() << std::endl;
552 std::pair<Vertex*, Vertex*> parent = {scopeEntry,scopeExit};
553
554 if ( scope->flowNodes.empty() ) {
555 scopeEntry->outflows.emplace_back(nullptr,scopeExit);
556 scopeExit->inflows.emplace_back(nullptr,scopeEntry);
557 return;
558// throw std::runtime_error("FlattenedGraph: unable to flatten empty scope '" + scope->id + "'");
559 }
560 for ( auto& flowNode : scope->flowNodes ) {
561
562 // create vertices for flow node
563 if ( auto activity = flowNode->represents<BPMN::Activity>();
564 activity &&
565 activity->loopCharacteristics.has_value()
566 ) {
567 // create copies for loop and multi-instance activities
568 createLoopVertices(scopeEntry->rootId, instanceId, scopeEntry->loopIndices, activity, parent);
569 }
570 else {
571 createVertexPair(scopeEntry->rootId, instanceId, scopeEntry->loopIndices, flowNode, parent);
572 }
573
574 auto& [flowNodeEntry,flowNodeExit] = vertexMap.at({instanceId,scopeEntry->loopIndices,flowNode});
575
576 if ( flowNode->represents<BPMN::UntypedStartEvent>() || flowNode->represents<BPMN::TypedStartEvent>() ) {
577 flowNodeEntry->inflows.emplace_back(nullptr,scopeEntry);
578 scopeEntry->outflows.emplace_back(nullptr,flowNodeEntry);
579 }
580
581 if ( flowNode->represents<BPMN::Activity>() && flowNode->incoming.empty() ) {
582 if( !flowNode->parent->represents<BPMNOS::Model::SequentialAdHocSubProcess>() ) {
583 throw std::runtime_error("FlattenedGraph: only activities within adhoc subprocesses maybe without incoming sequence flow");
584 }
585 flowNodeEntry->inflows.emplace_back(nullptr,scopeEntry);
586 scopeEntry->outflows.emplace_back(nullptr,flowNodeEntry);
587 }
588
589 if ( flowNode->outgoing.empty() ) {
590 // flow node without outgoing sequence flow
591 scopeExit->inflows.emplace_back(nullptr,flowNodeExit);
592 flowNodeExit->outflows.emplace_back(nullptr,scopeExit);
593 }
594
595 // flatten child scopes
596 if ( auto subprocess = flowNode->represents<BPMN::SubProcess>();
597 subprocess &&
598 subprocess->loopCharacteristics.has_value()
599 ) {
600 // nothing to be done because loops of each subprocess are already flattened
601 }
602 else if ( auto childScope = flowNode->represents<BPMN::Scope>() ) {
603 flatten( flowNodeEntry->instanceId, childScope, flowNodeEntry, flowNodeExit );
604 }
605
606 }
607
608//std::cerr << "sequence flows: " << scope->sequenceFlows.size() << std::endl;
609 // sequence flows
610 for ( auto& sequenceFlow : scope->sequenceFlows ) {
611 // create unique flow from origin to destination
612 Vertex* origin = vertexMap.at({instanceId,scopeEntry->loopIndices,sequenceFlow->source}).second;
613 Vertex* destination = vertexMap.at({instanceId,scopeEntry->loopIndices,sequenceFlow->target}).first;
614 origin->outflows.emplace_back(sequenceFlow.get(),destination);
615 destination->inflows.emplace_back(sequenceFlow.get(),origin);
616 }
617
618 // boundary events
619 for ( auto& flowNode : scope->flowNodes ) {
620 if ( auto boundaryEvent = flowNode->represents<BPMN::BoundaryEvent>() ) {
621 throw std::runtime_error("FlattenedGraph: Boundary event '" + boundaryEvent->id + "' is not supported");
622 }
623 }
624
625 // event-subprocesses
626 for ( auto& eventSubProcess : scope->eventSubProcesses ) {
627 if ( eventSubProcess->startEvent->isInterrupting ) {
628 // interrupting event-subprocesses
629 throw std::runtime_error("FlattenedGraph: Interrupting event-subprocess '" + eventSubProcess->id + "' is not supported");
630// flatten( instanceId, eventSubProcess, scopeEntry, scopeExit );
631 }
632 else {
633 // non-interrupting event-subprocesses
634 if ( eventSubProcess->startEvent->represents<BPMN::MessageStartEvent>() ) {
635 addNonInterruptingEventSubProcess(eventSubProcess, scopeEntry, scopeExit);
636 }
637 else {
638 throw std::runtime_error("FlattenedGraph: Type of non-interrupting event-subprocess '" + eventSubProcess->id + "' is not supported");
639 }
640 }
641 }
642
643 // compensation nodes
644 for ( auto& flowNode : scope->flowNodes ) {
645 auto activity = flowNode->represents<BPMN::Activity>();
646 if ( activity && activity->isForCompensation ) {
647 throw std::runtime_error("FlattenedGraph: Compensation activity '" + activity->id + "' is not supported");
648 }
649 }
650}
651
652bool FlattenedGraph::modifiesData(const Vertex* vertex, const Vertex* dataOwner) const {
653 for ( auto& [entry,exit] : dataModifiers.at( dataOwner ) ) {
654 if ( vertex == entry || vertex == exit ) return true;
655 }
656 return false;
657}
658
659bool FlattenedGraph::modifiesGlobals(const Vertex* vertex) const {
660 for ( auto& [entry,exit] : globalModifiers ) {
661 if ( vertex == entry || vertex == exit ) return true;
662 }
663 return false;
664}
665
666std::vector< const FlattenedGraph::Vertex* > FlattenedGraph::sortVertices() const {
667 std::vector< const Vertex* > sortedVertices;
668 sortedVertices.reserve( vertices.size() );
669 for ( auto initialVertex : initialVertices ) {
670 std::unordered_map<const Vertex*, size_t> inDegree;
671
672 std::deque<const Vertex*> queue;
673 queue.push_back(initialVertex);
674
675 // determine vertices in topological order
676 while ( !queue.empty() ) {
677//std::cerr << "Queue " << queue.size() << std::endl;
678 const Vertex* current = queue.front();
679 queue.pop_front();
680 sortedVertices.push_back(current);
681 inDegree.erase(current);
682
683 for ( auto& [_,vertex] : current->outflows ) {
684 if ( !inDegree.contains(vertex) ) {
685 // initialize in degree
686 inDegree[vertex] = vertex->inflows.size() + vertex->predecessors.size();
687 }
688 // decrement in degree and add vertex to queue if zero
689 if ( --inDegree.at(vertex) == 0 ) {
690 queue.push_back(vertex);
691 }
692 }
693 for ( auto vertex : current->successors ) {
694 if ( !inDegree.contains(vertex) ) {
695 // initialize in degree
696 inDegree[vertex] = vertex->inflows.size() + vertex->predecessors.size();
697 }
698//std::cerr << "Vertex " << vertex.reference() << " has inDegree " << inDegree.at(&vertex) << "/" << vertex.inflows.size() << "/" << vertex.predecessors.size() << std::endl;
699 // decrement in degree and add vertex to queue if zero
700 if ( --inDegree.at(vertex) == 0 ) {
701 queue.push_back(vertex);
702 }
703 }
704 }
705
706 if ( inDegree.size() ) {
707 throw std::runtime_error("FlattenedGraph: cycle detected in '" + BPMNOS::to_string(initialVertex->rootId, STRING) + "'");
708 }
709 }
710//std::cerr << "sortedVertices:" << sortedVertices.size() << std::endl;
711 return sortedVertices;
712}
713
715//std::cerr << "getVertex(" << token->jsonify() << ")" << std::endl;
716 auto node = token->node ? token->node->as<BPMN::Node>() : token->owner->process->as<BPMN::Node>();
717 if( !loopIndexAttributes.contains(node) ) {
718 // unreachable typed start event
719 assert( node->represents<BPMN::TypedStartEvent>() );
720 return nullptr;
721 }
722
723 // determine loop indices from token
724 std::vector< size_t > loopIndices;
725 for ( auto attribute : loopIndexAttributes.at(node) ) {
726//std::cerr << attribute->id << std::endl;
727 if ( !token->status.at(attribute->index).has_value() ) {
728 // loop index has not yet been set
729 break;
730 }
731 loopIndices.push_back( (size_t)token->status.at(attribute->index).value() );
732 }
733
734 auto instanceId = token->data->at(BPMNOS::Model::ExtensionElements::Index::Instance).get().value();
735//std::cerr << stringRegistry[(size_t)instanceId] << "/" << node->id << "/" << token->jsonify() << std::endl;
736 if( !vertexMap.contains({instanceId,loopIndices,node}) ) {
737 return nullptr;
738 }
739 assert( vertexMap.contains({instanceId,loopIndices,node}) );
740 auto& [entry,exit] = vertexMap.at({instanceId,loopIndices,node});
741 return (token->state == Token::State::ENTERED) ? entry : exit;
742}
743
745 return vertices[ vertex->index - ( vertex->type == Vertex::Type::EXIT ) ].get();
746}
747
749 return vertices[ vertex->index + ( vertex->type == Vertex::Type::ENTRY ) ].get();
750}
751
CollectionRegistry collectionRegistry
std::pair< const Vertex *, const Vertex * > dataOwner(const BPMNOS::Model::Attribute *attribute) const
Returns the index of the data owner of an attribute.
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex * > > outflows
Container holding vertices connecting by an incoming sequence flow, for loop activities the sequence ...
std::vector< Vertex * > successors
Container holding predecessors according to the execution logic (excl. sequence flows)
nlohmann::ordered_json jsonify() const
Returns a reference of the vertex excluding the type.
std::vector< std::pair< const BPMN::SequenceFlow *, Vertex * > > inflows
Parent vertices.
size_t dataOwnerIndex(const BPMNOS::Model::Attribute *attribute) const
Returns the entry vertex of the performer of a sequential activity vertex.
std::string shortReference() const
Returns a unique reference of the vertex.
std::vector< Vertex * > predecessors
Container holding vertices connecting by an outgoing sequence flow, for loop activities the sequence ...
const Vertex * performer() const
Container holding all entry vertices of nodes owning at least one data attribute.
std::optional< std::pair< Vertex *, Vertex * > > parent
std::vector< Vertex * > dataOwners
Container holding all possible vertices receiving a message (or the message delivery notification for...
std::string reference() const
Returns the vertices of the owner of a data attribute.
Represents a graph containing all BPMN nodes that may receive a token during execution of a scenario.
void addInstance(const BPMNOS::Model::Scenario::InstanceData *instance)
Map holding entry and exit vertices of each possible instantiation of a node.
nlohmann::ordered_json jsonify() const
std::unordered_set< const Vertex * > dummies
Container holding entry and exit vertices of each possible instantiation of a node.
const Vertex * getVertex(const Token *token) const
Method returning true if the vertex modifies a global attribute.
const BPMNOS::Model::Attribute * getLoopIndexAttribute(const BPMN::Activity *activity) const
Container holding dummy vertices for loop & multi-instance activities.
const BPMNOS::Model::Scenario * scenario
const Vertex * exit(const Vertex *vertex) const
std::vector< const Vertex * > sortVertices() const
Container holding entry vertices of all process instances.
const Vertex * entry(const Vertex *vertex) const
std::vector< std::pair< const Vertex *, const Vertex * > > globalModifiers
Container allowing to look up vertices of tasks modifying data attributes given a pointer to the entr...
std::vector< const Vertex * > initialVertices
std::unordered_map< const Vertex *, std::vector< std::pair< const Vertex *, const Vertex * > > > sequentialActivities
tuple_map< std::tuple< BPMNOS::number, std::vector< size_t >, const BPMN::Node * >, std::pair< Vertex *, Vertex * > > vertexMap
bool modifiesGlobals(const Vertex *vertex) const
Method returning true if the vertex modifies a data attribute of the given owner.
std::unordered_map< const Vertex *, std::vector< std::pair< const Vertex *, const Vertex * > > > dataModifiers
Container allowing to look up vertices of sequential activities given a pointer to the entry vertex o...
std::unordered_map< const BPMN::Node *, std::vector< const BPMNOS::Model::Attribute * > > loopIndexAttributes
bool modifiesData(const Vertex *vertex, const Vertex *dataOwner) const
Container holding vertices of tasks modifying global attributes.
std::vector< std::unique_ptr< Vertex > > vertices
Returns a topologically sorted vector of all vertices reachable from the initial vertices.
FlattenedGraph(const BPMNOS::Model::Scenario *scenario)
const BPMN::Process * process
Pointer to the top-level process.
Represents a token running through a (sub)process.
Definition Token.h:35
const BPMN::FlowNode * node
Definition Token.h:46
const StateMachine * owner
State machine owning the token.
Definition Token.h:44
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
Definition Token.h:58
std::unique_ptr< const Expression > expression
Definition Attribute.h:31
size_t index
Index of attribute (is automatically set by attribute registry).
Definition Attribute.h:28
Class holding extension elements representing execution data for nodes.
Abstract base class for scenarios holding data for all BPMN instances.
Definition Scenario.h:21
virtual BPMNOS::number getEarliestInstantiationTime() const =0
Method returning the time of the earliest instantiation.
static constexpr char delimiters[]
Delimiters used for disambiguation of identifiers of non-interrupting event subprocesses and multi-in...
Definition Scenario.h:32
virtual std::vector< const InstanceData * > getKnownInstances(const BPMNOS::number currentTime) const =0
Method returning a vector of all instances that have been created or are known for sure until the giv...
virtual std::optional< BPMNOS::number > getKnownValue(const Scenario::InstanceData *instance, const BPMNOS::Model::Attribute *attribute, const BPMNOS::number currentTime) const =0
Method returning a known value of an attribute.
Class representing adhoc subprocesses with sequential ordering.
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< SequenceFlow * > incoming
Vector containing all incoming sequence flows of the node.
Definition bpmn++.h:16703
Base class for all nodes in a BPMN model.
Definition bpmn++.h:16444
Base class for BPMN elements that may contain a ChildNode elements.
Definition bpmn++.h:16510
std::vector< EventSubProcess * > eventSubProcesses
Vector containing pointers to all event subprocesses within the scope of the nodes.
Definition bpmn++.h:16522
std::vector< FlowNode * > flowNodes
Vector containing pointers to all flow nodes within the scope of the nodes.
Definition bpmn++.h:16519
std::vector< std::unique_ptr< SequenceFlow > > sequenceFlows
Vector containing all sequence flows within the scope.
Definition bpmn++.h:16528
Base class for all start events with an event definition.
Definition bpmn++.h:16880
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
@ STRING
Definition Value.h:9
STL namespace.
const BPMN::Process * process
Definition Scenario.h:24
size_t id
Instance identifier.
Definition Scenario.h:25