19 , index(graph->vertices.size())
21 , instanceId(instanceId)
22 , loopIndices(
std::move(loopIndices))
28 if (
parent.has_value() ) {
50 assert( adHocSubProcess );
51 assert( parent.has_value() );
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;
63 for (
size_t index = 0; index < dataOwners.size(); index++) {
66 attribute->
index < extensionElements->attributeRegistry.dataAttributes.size() &&
67 attribute->
index >= extensionElements->attributeRegistry.dataAttributes.size() - extensionElements->data.size()
72 throw std::logic_error(
"FlattenedGraph: unable to determine data owner index for '" + attribute->
id +
"'");
76 auto index = dataOwnerIndex(attribute);
77 return { dataOwners[index], (dataOwners[index] + 1) };
81 return shortReference() +
"," + ( type == Type::ENTRY ?
"entry" :
"exit" );
86 for (
auto index : loopIndices ) {
87 result +=
"°" + std::to_string(index);
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());
99 jsonObject[
"outflows"] = nlohmann::json::array();
100 for (
auto& [_,vertex] : outflows ) {
101 jsonObject[
"outflows"].push_back(vertex->reference());
103 jsonObject[
"predecessors"] = nlohmann::json::array();
104 for (
auto vertex : predecessors ) {
105 jsonObject[
"predecessors"].push_back(vertex->reference());
107 jsonObject[
"successors"] = nlohmann::json::array();
108 for (
auto vertex : successors ) {
109 jsonObject[
"successors"].push_back(vertex->reference());
123 for (
auto& instance : instances ) {
129 nlohmann::ordered_json jsonObject = nlohmann::json::array();
131 jsonObject.push_back(vertex->jsonify());
139 auto [
entry,
exit ] = createVertexPair(instance->
id, instance->
id, {}, instance->
process, std::nullopt);
144void FlattenedGraph::addNonInterruptingEventSubProcess(
const BPMN::EventSubProcess* eventSubProcess, Vertex* parentEntry, Vertex* parentExit ) {
145 nonInterruptingEventSubProcesses.emplace_back(eventSubProcess, parentEntry, parentExit, 0,
nullptr);
147 auto& counter = std::get<3>( nonInterruptingEventSubProcesses.back() );
148 auto& lastStart = std::get<4>( nonInterruptingEventSubProcesses.back() );
153 for (
auto candidate : candidates ) {
154 if( sendingVertices.contains(candidate) ) {
155 for ( [[maybe_unused]]
auto& _ : sendingVertices.at(candidate) ) {
162 eventSubProcess->
id +
164 std::to_string(counter)
167 flatten(
id, eventSubProcess, parentEntry, parentExit );
169 auto& [startEntry,startExit] =
vertexMap.at({ id, parentEntry->loopIndices, eventSubProcess->
startEvent });
171 lastStart->successors.push_back(startEntry);
172 startEntry->predecessors.push_back(lastStart);
174 lastStart = startExit;
180void FlattenedGraph::addSender(
const BPMN::MessageThrowEvent* messageThrowEvent, Vertex* senderEntry, Vertex* senderExit ) {
186 for (
auto& [eventSubProcess, parentEntry, parentExit, counter, lastStart] : nonInterruptingEventSubProcesses ) {
187 if (std::find(candidates.begin(), candidates.end(), eventSubProcess->
startEvent) != candidates.end()) {
193 flatten(
id, eventSubProcess, parentEntry, parentExit );
195 auto& [startEntry,startExit] =
vertexMap.at({ id, parentEntry->loopIndices, eventSubProcess->
startEvent });
197 lastStart->successors.push_back(startEntry);
198 startEntry->predecessors.push_back(lastStart);
200 lastStart = startExit;
205 for (
auto candidate : candidates ) {
206 for (
auto& [ recipientEntry, recipientExit] : receivingVertices[candidate] ) {
208 senderEntry->recipients.push_back(recipientExit);
209 recipientExit->senders.push_back(senderEntry);
211 recipientExit->recipients.push_back(senderExit);
212 senderExit->senders.push_back(recipientExit);
217 sendingVertices[messageThrowEvent].emplace_back(senderEntry, senderExit);
220void FlattenedGraph::addRecipient(
const BPMN::MessageCatchEvent* messageCatchEvent, Vertex* recipientEntry, Vertex* recipientExit ) {
225 for (
auto candidate : candidates ) {
226 for (
auto& [ senderEntry, senderExit] : sendingVertices[candidate] ) {
228 senderEntry->recipients.push_back(recipientExit);
229 recipientExit->senders.push_back(senderEntry);
231 recipientExit->recipients.push_back(senderExit);
232 senderExit->senders.push_back(recipientExit);
237 receivingVertices[messageCatchEvent].emplace_back(recipientEntry, recipientExit);
242 assert(extensionElements);
244 extensionElements->loopIndex.has_value() ?
245 extensionElements->loopIndex.value()->
expression->isAttribute() :
249 throw std::runtime_error(
"FlattenedGraph: unable to determine loop index for activity '" + activity->
id +
"'");
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) {
270 if ( parent.has_value() ) {
273 parent.value().first->successors.push_back(
entry );
274 parent.value().second->predecessors.push_back(
exit );
280 std::vector< const BPMNOS::Model::Attribute* > attributes = (
283 std::vector< const BPMNOS::Model::Attribute* >()
300 if ( !extensionElements ) {
305 addSender( messageThrowEvent,
entry,
exit );
308 addRecipient( messageCatchEvent,
entry,
exit );
311 if ( extensionElements->data.size() ) {
317 if ( extensionElements->hasSequentialPerformer ) {
321 sequentialAdHocSubProcess && !sequentialAdHocSubProcess->
performer
334 if ( extensionElements->data.size() ) {
335 dataModifiers.emplace(
entry,std::vector< std::pair<const Vertex*, const Vertex*> >());
339 std::unordered_set< const Vertex* > dataOwners;
341 for (
auto& operator_ : extensionElements->operators ) {
343 dataOwners.emplace(
entry->
dataOwner(operator_->attribute).first );
349 for (
auto dataOwner : dataOwners ) {
360 std::unordered_set< const Vertex* > dataOwners;
362 for (
auto& operator_ : extensionElements->operators ) {
364 dataOwners.emplace(
entry->
dataOwner(operator_->attribute).first );
370 for (
auto dataOwner : dataOwners ) {
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) {
396 assert( parent.has_value() );
400 parent.value().first->successors.push_back(
entry );
401 parent.value().second->predecessors.push_back(
exit );
416 if ( parameter->expression ) {
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" );
425 if ( !value.has_value() ) {
430 variableValues.push_back( (
double)value.value() );
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" );
439 collectionValues.push_back( {} );
441 if ( !collection.has_value() ) {
447 collectionValues.back().push_back( value );
452 return number(parameter->expression->compiled.evaluate(variableValues,collectionValues));
461 assert(extensionElements);
469 if ( extensionElements->loopMaximum.has_value() ) {
470 auto value = getValue( extensionElements->loopMaximum.value().get() );
471 n = value.has_value() ? (int)value.value() : 0;
475 if ( extensionElements->loopCardinality.has_value() ) {
476 auto value = getValue( extensionElements->loopCardinality.value().get() );
477 n = value.has_value() ? (int)value.value() : 0;
482 throw std::runtime_error(
"FlattenedGraph: cannot determine loop maximum/cardinality for activity '" + activity->
id +
"'" );
493 Vertex* previous =
entry;
494 loopIndices.push_back(0);
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);
502 loopingEntry->inflows.emplace_back(
nullptr,previous);
503 previous->outflows.emplace_back(
nullptr,loopingEntry);
507 loopingExit->outflows.emplace_back(
nullptr,
exit );
509 previous = loopingExit;
512 flatten(instanceId, subprocess, loopingEntry, loopingExit );
520 Vertex* previous =
nullptr;
521 for (
int i = 1; i <= n; i++ ) {
523 auto [ multiInstanceEntry, multiInstanceExit ] = createVertexPair(rootId, multiInstanceId, loopIndices, activity, parent);
525 multiInstanceEntry->inflows.emplace_back(
nullptr,
entry);
528 multiInstanceExit->outflows.emplace_back(
nullptr,
exit);
529 exit->
inflows.emplace_back(
nullptr,multiInstanceExit);
535 previous->successors.push_back(multiInstanceEntry);
536 multiInstanceEntry->predecessors.push_back(previous);
538 previous = multiInstanceExit;
541 flatten(multiInstanceId, subprocess, multiInstanceEntry, multiInstanceExit );
550void FlattenedGraph::flatten(
BPMNOS::number instanceId,
const BPMN::Scope* scope, Vertex* scopeEntry, Vertex* scopeExit) {
552 std::pair<Vertex*, Vertex*> parent = {scopeEntry,scopeExit};
555 scopeEntry->outflows.emplace_back(
nullptr,scopeExit);
556 scopeExit->inflows.emplace_back(
nullptr,scopeEntry);
560 for (
auto& flowNode : scope->
flowNodes ) {
568 createLoopVertices(scopeEntry->rootId, instanceId, scopeEntry->loopIndices, activity, parent);
571 createVertexPair(scopeEntry->rootId, instanceId, scopeEntry->loopIndices, flowNode, parent);
574 auto& [flowNodeEntry,flowNodeExit] =
vertexMap.at({instanceId,scopeEntry->loopIndices,flowNode});
577 flowNodeEntry->inflows.emplace_back(
nullptr,scopeEntry);
578 scopeEntry->outflows.emplace_back(
nullptr,flowNodeEntry);
583 throw std::runtime_error(
"FlattenedGraph: only activities within adhoc subprocesses maybe without incoming sequence flow");
585 flowNodeEntry->inflows.emplace_back(
nullptr,scopeEntry);
586 scopeEntry->outflows.emplace_back(
nullptr,flowNodeEntry);
589 if ( flowNode->outgoing.empty() ) {
591 scopeExit->inflows.emplace_back(
nullptr,flowNodeExit);
592 flowNodeExit->outflows.emplace_back(
nullptr,scopeExit);
602 else if (
auto childScope = flowNode->represents<
BPMN::Scope>() ) {
603 flatten( flowNodeEntry->instanceId, childScope, flowNodeEntry, flowNodeExit );
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);
619 for (
auto& flowNode : scope->
flowNodes ) {
621 throw std::runtime_error(
"FlattenedGraph: Boundary event '" + boundaryEvent->id +
"' is not supported");
629 throw std::runtime_error(
"FlattenedGraph: Interrupting event-subprocess '" + eventSubProcess->
id +
"' is not supported");
635 addNonInterruptingEventSubProcess(eventSubProcess, scopeEntry, scopeExit);
638 throw std::runtime_error(
"FlattenedGraph: Type of non-interrupting event-subprocess '" + eventSubProcess->
id +
"' is not supported");
644 for (
auto& flowNode : scope->
flowNodes ) {
647 throw std::runtime_error(
"FlattenedGraph: Compensation activity '" + activity->
id +
"' is not supported");
654 if ( vertex ==
entry || vertex ==
exit )
return true;
661 if ( vertex ==
entry || vertex ==
exit )
return true;
667 std::vector< const Vertex* > sortedVertices;
668 sortedVertices.reserve(
vertices.size() );
670 std::unordered_map<const Vertex*, size_t> inDegree;
672 std::deque<const Vertex*> queue;
673 queue.push_back(initialVertex);
676 while ( !queue.empty() ) {
678 const Vertex* current = queue.front();
680 sortedVertices.push_back(current);
681 inDegree.erase(current);
683 for (
auto& [_,vertex] : current->
outflows ) {
684 if ( !inDegree.contains(vertex) ) {
686 inDegree[vertex] = vertex->inflows.size() + vertex->predecessors.size();
689 if ( --inDegree.at(vertex) == 0 ) {
690 queue.push_back(vertex);
694 if ( !inDegree.contains(vertex) ) {
696 inDegree[vertex] = vertex->inflows.size() + vertex->predecessors.size();
700 if ( --inDegree.at(vertex) == 0 ) {
701 queue.push_back(vertex);
706 if ( inDegree.size() ) {
707 throw std::runtime_error(
"FlattenedGraph: cycle detected in '" +
BPMNOS::to_string(initialVertex->rootId,
STRING) +
"'");
711 return sortedVertices;
724 std::vector< size_t > loopIndices;
727 if ( !token->
status.at(attribute->index).has_value() ) {
731 loopIndices.push_back( (
size_t)token->
status.at(attribute->index).value() );
736 if( !
vertexMap.contains({instanceId,loopIndices,node}) ) {
739 assert(
vertexMap.contains({instanceId,loopIndices,node}) );
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.
const BPMN::FlowNode * node
const StateMachine * owner
State machine owning the token.
SharedValues * data
Pointer to the data of the owner or owned state machine subprocesses)
std::unique_ptr< const Expression > expression
size_t index
Index of attribute (is automatically set by attribute registry).
Class holding extension elements representing execution data for nodes.
Abstract base class for scenarios holding data for all BPMN instances.
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...
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.
std::optional< LoopCharacteristics > loopCharacteristics
@ MultiInstanceSequential
std::unique_ptr< ExtensionElements > extensionElements
std::string id
Id of element.
Base class for all boundary events attached to an Activity.
Scope * parent
Reference to the parent node.
T * as()
Casts the element to the specified type T.
T * represents()
Attempts to cast the element to the specified type T.
TypedStartEvent * startEvent
std::vector< SequenceFlow * > incoming
Vector containing all incoming sequence flows of the node.
Base class for all nodes in a BPMN model.
Base class for BPMN elements that may contain a ChildNode elements.
std::vector< EventSubProcess * > eventSubProcesses
Vector containing pointers to all event subprocesses within the scope of the nodes.
std::vector< FlowNode * > flowNodes
Vector containing pointers to all flow nodes within the scope of the nodes.
std::vector< std::unique_ptr< SequenceFlow > > sequenceFlows
Vector containing all sequence flows within the scope.
Base class for all start events with an event definition.
std::string to_string(number numericValue, const ValueType &type)
Converts a number to a string.
number to_number(const std::string &valueString, const ValueType &type)
Converts a string to a number.
BPMNOS_NUMBER_TYPE number
static constexpr size_t Instance
const BPMN::Process * process
size_t id
Instance identifier.