BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
ExtensionElements.cpp
Go to the documentation of this file.
1#include "ExtensionElements.h"
17#include <algorithm>
18#include <iostream>
19
20using namespace BPMNOS::Model;
21
22ExtensionElements::ExtensionElements(XML::bpmn::tBaseElement* baseElement, const AttributeRegistry attributeRegistry_, BPMN::Scope* parent, std::vector<std::reference_wrapper<XML::bpmnos::tAttribute>> dataAttributes)
23 : BPMN::ExtensionElements( baseElement )
24 , attributeRegistry(attributeRegistry_)
25 , parent(parent)
26 , hasSequentialPerformer(false)
27 , isInstantaneous(true)
28{
29 // @attention: this->baseElement is only set after constructed extension elements have been bound to the BPMN base element.
30//std::cerr << "Base: " << baseElement->id.value().get().value.value << std::endl;
31
32 if ( element ) {
33 if ( auto status = element->getOptionalChild<XML::bpmnos::tStatus>(); status.has_value() ) {
34 // add all attributes
35 if ( status->get().attributes.has_value() ) {
36 for ( XML::bpmnos::tAttribute& attributeElement : status->get().attributes.value().get().attribute ) {
37 auto attribute = std::make_unique<Attribute>(&attributeElement, Attribute::Category::STATUS, attributeRegistry);
38 if ( attribute->id == Keyword::Instance ) {
39 // always insert instance attribute at first position
40 attributes.insert(attributes.begin(), std::move(attribute));
41 // fix indices
42 for (size_t index = 0; index < attributes.size(); index ++) {
43 attributes[index]->index = index;
44 }
45 }
46 else if ( attribute->id == Keyword::Timestamp ) {
47 if ( attributes.size() && attributes[0]->id == Keyword::Instance ) {
48 // insert timestamp attribute at the second position
49 attributes.insert(++attributes.begin(), std::move(attribute));
50 // fix indices
51 for (size_t index = 0; index < attributes.size(); index ++) {
52 attributes[index]->index = index;
53 }
54 }
55 else {
56 // insert timestamp attribute at first position, expecting that
57 // instance attribute will be inserted before
58 attributes.insert(attributes.begin(), std::move(attribute));
59 // fix indices
60 for (size_t index = 0; index < attributes.size(); index ++) {
61 attributes[index]->index = index;
62 }
63 }
64 }
65 else {
66 // TODO: add entry data dependencies
67 if ( attribute->expression ) {
68 for ( auto input : attribute->expression->inputs ) {
69 entryDependencies.insert(input);
70 }
71 }
72 attributes.push_back(std::move(attribute));
73 }
74 }
75 }
76 }
77 }
78
79 // add all data attributes
80 for ( XML::bpmnos::tAttribute& attributeElement : dataAttributes ) {
81 data.push_back( std::make_unique<Attribute>(&attributeElement, Attribute::Category::DATA, attributeRegistry) );
82 // TODO: add entry data dependencies
83 if ( data.back()->expression ) {
84 for ( auto input : data.back()->expression->inputs ) {
85 entryDependencies.insert(input);
86 }
87 }
88 }
89
90 if ( !element ) return;
91
92 if ( auto status = element->getOptionalChild<XML::bpmnos::tStatus>(); status.has_value() ) {
93 // add all restrictions
94 if ( status->get().restrictions.has_value() ) {
95 for ( XML::bpmnos::tRestriction& restriction : status->get().restrictions.value().get().restriction ) {
96 try {
97 restrictions.push_back(std::make_unique<Restriction>(&restriction,attributeRegistry));
98 }
99 catch ( const std::exception& error ) {
100 throw std::runtime_error("ExtensionElements: illegal restriction '" + (std::string)restriction.id.value + "'.\n" + error.what());
101 }
102 // add entry and exit dependencies
103 for ( auto input : restrictions.back()->expression.inputs ) {
104 if ( restrictions.back()->scope != Restriction::Scope::EXIT ) {
105 entryDependencies.insert(input);
106 }
107 if ( restrictions.back()->scope != Restriction::Scope::ENTRY ) {
108 exitDependencies.insert(input);
109 }
110 }
111 }
112 }
113
114 // recursively determine entry and exit dependencies
115 auto ancestor = parent;
116 while ( ancestor ) {
117 if ( auto extensionElements = ancestor->extensionElements->represents<BPMNOS::Model::ExtensionElements>() ) {
118 for ( auto& restriction : extensionElements->restrictions ) {
119 for ( auto input : restriction->expression.inputs ) {
120 if ( restriction->scope == Restriction::Scope::FULL ) {
121 entryDependencies.insert(input);
122 exitDependencies.insert(input);
123 }
124 }
125 }
126 }
127 if ( auto child = ancestor->represents<BPMN::ChildNode>() ) {
128 ancestor = child->parent;
129 }
130 else {
131 break;
132 }
133 }
134
135 // add all operators
136 if ( status->get().operators.has_value() ) {
137 assert( !baseElement->is<XML::bpmn::tProcess>() );
138 assert( (
139 !baseElement->is<XML::bpmn::tSubProcess>() ||
140 !baseElement->is<XML::bpmn::tSubProcess>()->triggeredByEvent.has_value() ||
141 (bool)baseElement->is<XML::bpmn::tSubProcess>()->triggeredByEvent.value().get().value
142 ) );
143
144 for ( XML::bpmnos::tOperator& operator_ : status->get().operators.value().get().operator_ ) {
145 try {
146 operators.push_back( std::make_unique<Operator>(&operator_,attributeRegistry) );
147 }
148 catch ( const std::exception& error ) {
149 throw std::runtime_error("ExtensionElements: illegal operator '" + (std::string)operator_.id.value + "'.\n" + error.what() );
150 }
151 auto attribute = operators.back()->attribute;
152 if ( attribute->category == Attribute::Category::STATUS && attribute->index == BPMNOS::Model::ExtensionElements::Index::Timestamp ) {
153 isInstantaneous = false;
154 }
155 if ( attribute->category == Attribute::Category::DATA && attribute->index == BPMNOS::Model::ExtensionElements::Index::Instance ) {
156 throw std::runtime_error("ExtensionElements: operator '" + (std::string)operator_.id.value + "' modifies instance attribute.\n" );
157 }
158
159 for ( auto input : operators.back()->expression.inputs ) {
160 operatorDependencies.insert(input);
161 }
162 }
163 }
164
165 // add all choices to be made
166 if ( status->get().decisions.has_value() ) {
167 for ( XML::bpmnos::tDecision& decision : status->get().decisions.value().get().decision ) {
168 try {
169 choices.push_back(std::make_unique<Choice>(&decision,attributeRegistry));
170 }
171 catch ( const std::exception& error ) {
172 throw std::runtime_error("ExtensionElements: illegal attributes for choice '" + (std::string)decision.id.value + "'.\n" + error.what());
173 }
174 for ( auto input : choices.back()->dependencies ) {
175 choiceDependencies.insert(input);
176 }
177 }
178 }
179 }
180
181 // add data attributes and global values modified by operator to dataUpdate
182 dataUpdate.global = false; // data update does not affect all instances unless a global value is updated
183
184 // operators are applied upon entry
185 for ( auto& operator_ : operators ) {
186 if ( operator_->attribute->category != Attribute::Category::STATUS ) {
187 dataUpdate.attributes.push_back(operator_->attribute);
188 }
189 if ( operator_->attribute->category == Attribute::Category::GLOBAL ) {
190 dataUpdate.global = true;
191 }
192 }
193
194 // add data attributes and global values modified by choice to dataUpdateOnCompletion
195 for ( auto& choice : choices ) {
196 if ( choice->attribute->category != Attribute::Category::STATUS ) {
197 dataUpdate.attributes.push_back(choice->attribute);
198 }
199 if ( choice->attribute->category == Attribute::Category::GLOBAL ) {
200 dataUpdate.global = true;
201 }
202 }
203
204 // add all message definitions
205 if ( element->getOptionalChild<XML::bpmnos::tMessages>().has_value() ) {
206 for ( XML::bpmnos::tMessage& message : element->getOptionalChild<XML::bpmnos::tMessages>()->get().message ) {
207 messageDefinitions.push_back(std::make_unique<MessageDefinition>(&message,attributeRegistry));
208 }
209 }
210 else if ( auto message = element->getOptionalChild<XML::bpmnos::tMessage>(); message.has_value() ) {
211 messageDefinitions.push_back(std::make_unique<MessageDefinition>(&message->get(),attributeRegistry));
212 }
213
214 if ( baseElement->is<XML::bpmn::tReceiveTask>() || baseElement->is<XML::bpmn::tCatchEvent>() ) {
215 // add data attributes modified by message to dataUpdateOnCompletion (global values must not be updated by messages)
216 for ( auto& messageDefinition : messageDefinitions ) {
217 for ( auto& [key,content] : messageDefinition->contentMap ) {
218 Attribute* attribute = content->attribute;
219 if ( attribute->category == Attribute::Category::DATA ) {
220 dataUpdate.attributes.push_back(attribute);
221 }
222 }
223 }
224 }
225
226 // add loop characteristics
227 if ( element->getOptionalChild<XML::bpmnos::tLoopCharacteristics>().has_value() ) {
228 for ( XML::bpmnos::tParameter& parameter : element->getOptionalChild<XML::bpmnos::tLoopCharacteristics>()->get().parameter ) {
229 if ( parameter.name.value.value == "cardinality" ) {
230 loopCardinality = std::make_unique<Parameter>(&parameter,attributeRegistry);
231 }
232 else if ( parameter.name.value.value == "index" ) {
233 loopIndex = std::make_unique<Parameter>(&parameter,attributeRegistry);
234 }
235 else if ( parameter.name.value.value == "condition" ) {
236 loopCondition = std::make_unique<Parameter>(&parameter,attributeRegistry);
237 }
238 else if ( parameter.name.value.value == "maximum" ) {
239 loopMaximum = std::make_unique<Parameter>(&parameter,attributeRegistry);
240 }
241 }
242 }
243
244 if ( auto process = baseElement->is<XML::bpmn::tProcess>() ) {
245 hasSequentialPerformer = BPMNOS::Model::Model::hasSequentialPerformer( process->resourceRole );
246 }
247 else if ( auto activity = baseElement->is<XML::bpmn::tActivity>() ) {
248 hasSequentialPerformer = BPMNOS::Model::Model::hasSequentialPerformer( activity->resourceRole );
249 }
250
251 // add all guidances
252 for ( XML::bpmnos::tGuidance& item : element->getChildren<XML::bpmnos::tGuidance>() ) {
253 auto guidance = std::make_unique<Guidance>(&item,attributeRegistry);
254 if ( guidance->type == Guidance::Type::Entry ) {
255 entryGuidance = std::move(guidance);
256 }
257 else if ( guidance->type == Guidance::Type::Exit ) {
258 exitGuidance = std::move(guidance);
259 }
260 else if ( guidance->type == Guidance::Type::Choice ) {
261 choiceGuidance = std::move(guidance);
262 }
263 else if ( guidance->type == Guidance::Type::MessageDelivery ) {
264 messageDeliveryGuidance = std::move(guidance);
265 }
266 }
267}
268
270 if ( index > messageDefinitions.size() ) {
271 throw std::runtime_error("ExtensionElements: no message definition with index " + std::to_string(index) + " provided for '" + baseElement->id + "'" );
272 }
273 return messageDefinitions[index].get();
274}
275
277 size_t index = 0;
278
279 if ( auto receiveTask = baseElement->represents<BPMN::ReceiveTask>();
280 receiveTask &&
281 receiveTask->loopCharacteristics.has_value()
282 ) {
283 // multi-instance receive task
284 if ( !loopIndex->get()->expression || !loopIndex->get()->expression->isAttribute() || loopIndex->get()->expression->isAttribute()->category != Attribute::Category::STATUS ) {
285 throw std::runtime_error("ExtensionElements: receive task '" + receiveTask->id + "' requires status attribute holding loop index");
286 }
287 size_t attributeIndex = loopIndex->get()->expression->isAttribute()->index;
288 assert( status[attributeIndex].has_value() );
289
290 assert( status[attributeIndex].value() >= 1 );
291 index = (size_t)(int)status[attributeIndex].value() - 1;
292 }
293
294 if ( index >= messageDefinitions.size() ) {
295 throw std::runtime_error("ExtensionElements: no message definition with index " + std::to_string(index) + " provided for '" + baseElement->id + "'" );
296 }
297
298 return messageDefinitions[index].get();
299}
300
301template <typename DataType>
302bool ExtensionElements::feasibleEntry(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
303 for ( auto& restriction : restrictions ) {
304 if ( restriction->scope != Restriction::Scope::EXIT && !restriction->isSatisfied(status,data,globals) ) {
305 return false;
306 }
307 }
308 return satisfiesInheritedRestrictions(status,data,globals);
309}
310
311template bool ExtensionElements::feasibleEntry<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
312template bool ExtensionElements::feasibleEntry<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
313
314template <typename DataType>
315bool ExtensionElements::feasibleExit(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
316 for ( auto& restriction : restrictions ) {
317 if ( restriction->scope != Restriction::Scope::ENTRY && !restriction->isSatisfied(status,data,globals) ) {
318 return false;
319 }
320 }
321
322 return satisfiesInheritedRestrictions(status,data,globals);
323}
324
325template bool ExtensionElements::feasibleExit<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
326template bool ExtensionElements::feasibleExit<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
327
328
329template <typename DataType>
330bool ExtensionElements::satisfiesInheritedRestrictions(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
331 auto base = baseElement->represents<BPMN::ChildNode>();
332
333 if ( !base ) return true;
334
335 // check restrictions within ancestor scopes
336 const BPMN::Node* ancestor = base->parent;
337 while ( ancestor ) {
338 assert( ancestor->extensionElements->represents<BPMNOS::Model::ExtensionElements>() );
340 return false;
341 }
342 if ( auto eventSubProcess = ancestor->represents<BPMN::EventSubProcess>();
343 eventSubProcess &&
344 ( eventSubProcess->startEvent->represents<BPMN::ErrorStartEvent>() || eventSubProcess->startEvent->represents<BPMN::CompensateStartEvent>() )
345 ) {
346 // error and compensate event subprocesses do not inherit restrictions
347 break;
348 }
349 else if ( auto activity = ancestor->represents<BPMN::Activity>();
350 activity && activity->isForCompensation
351 ) {
352 // compensation activities do not inherit restrictions
353 break;
354 }
355 else if ( auto child = ancestor->represents<BPMN::ChildNode>() ) {
356 ancestor = child->parent;
357 }
358 else {
359 break;
360 }
361 }
362 return true;
363}
364
365template bool ExtensionElements::satisfiesInheritedRestrictions<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
366template bool ExtensionElements::satisfiesInheritedRestrictions<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
367
368template <typename DataType>
369bool ExtensionElements::fullScopeRestrictionsSatisfied(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
370 for ( auto& restriction : restrictions ) {
371 if ( restriction->scope == Restriction::Scope::FULL && !restriction->isSatisfied(status,data,globals) ) {
372 return false;
373 }
374 }
375 return true;
376}
377
378template bool ExtensionElements::fullScopeRestrictionsSatisfied<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
379template bool ExtensionElements::fullScopeRestrictionsSatisfied<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
380
381template <typename DataType>
382void ExtensionElements::computeInitialValues(BPMNOS::number currentTime, BPMNOS::Values& status, DataType& data, BPMNOS::Values& globals) const {
384 for ( auto& attribute : attributes ) {
385 if ( attribute->expression ) {
386 attributeRegistry.setValue( attribute.get(), status, data, globals, attribute->expression->execute(status,data,globals) );
387 }
388 }
389 for ( auto& attribute : this->data ) {
390 if ( attribute->expression ) {
391 attributeRegistry.setValue( attribute.get(), status, data, globals, attribute->expression->execute(status,data,globals) );
392 }
393 }
394}
395
396template void ExtensionElements::computeInitialValues<BPMNOS::Values>(BPMNOS::number currentTime, Values& status, BPMNOS::Values& data, BPMNOS::Values& globals) const;
397template void ExtensionElements::computeInitialValues<BPMNOS::SharedValues>(BPMNOS::number currentTime, Values& status, BPMNOS::SharedValues& data, BPMNOS::Values& globals) const;
398
399
400template <typename DataType>
401void ExtensionElements::applyOperators(BPMNOS::Values& status, DataType& data, BPMNOS::Values& globals) const {
402 for ( auto& operator_ : operators ) {
403 operator_->apply(status,data,globals);
404 }
405}
406
407template void ExtensionElements::applyOperators<BPMNOS::Values>(Values& status, BPMNOS::Values& data, BPMNOS::Values& globals) const;
408template void ExtensionElements::applyOperators<BPMNOS::SharedValues>(Values& status, BPMNOS::SharedValues& data, BPMNOS::Values& globals) const;
409
410template <typename DataType>
411BPMNOS::number ExtensionElements::getObjective(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
412 BPMNOS::number objective = 0;
413 for ( auto& [name, attribute] : attributeRegistry.statusAttributes ) {
414 auto value = attributeRegistry.getValue(attribute,status,data,globals);
415 if ( value.has_value() ) {
416//std::cerr << attribute->name << " contributes " << attribute->weight * value.value() << std::endl;
417 objective += attribute->weight * value.value();
418 }
419 }
420 for ( auto& [name, attribute] : attributeRegistry.dataAttributes ) {
421 auto value = attributeRegistry.getValue(attribute,status,data,globals);
422 if ( value.has_value() ) {
423//std::cerr << attribute->name << " contributes " << attribute->weight * value.value() << std::endl;
424 objective += attribute->weight * value.value();
425 }
426 }
427 for ( auto& [name, attribute] : attributeRegistry.globalAttributes ) {
428 auto value = attributeRegistry.getValue(attribute,status,data,globals);
429 if ( value.has_value() ) {
430//std::cerr << attribute->name << " contributes " << attribute->weight * value.value() << std::endl;
431 objective += attribute->weight * value.value();
432 }
433 }
434 return objective;
435}
436
437template BPMNOS::number ExtensionElements::getObjective<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
438template BPMNOS::number ExtensionElements::getObjective<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
439
440
441template <typename DataType>
442BPMNOS::number ExtensionElements::getContributionToObjective(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
443 BPMNOS::number contribution = 0;
444 for ( auto& attribute : attributes ) {
445 auto value = attributeRegistry.getValue(attribute.get(),status,data,globals);
446 if ( value.has_value() ) {
447 contribution += attribute->weight * value.value();
448 }
449 }
450 for ( auto& attribute : this->data ) {
451 auto value = attributeRegistry.getValue(attribute.get(),status,data,globals);
452 if ( value.has_value() ) {
453 contribution += attribute->weight * value.value();
454 }
455 }
456 return contribution;
457}
458
459template BPMNOS::number ExtensionElements::getContributionToObjective<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
460template BPMNOS::number ExtensionElements::getContributionToObjective<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
461
void setValue(const Attribute *attribute, Values &status, Values &data, Values &globals, std::optional< BPMNOS::number > value) const
std::map< std::string, Attribute * > dataAttributes
std::map< std::string, Attribute * > globalAttributes
std::map< std::string, Attribute * > statusAttributes
std::optional< BPMNOS::number > getValue(const Attribute *attribute, const Values &status, const Values &data, const Values &globals) const
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.
bool feasibleExit(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
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
BPMNOS::number getObjective(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
Returns the total objective of all attributes provided.
std::set< const Attribute * > exitDependencies
Set containing all input attributes influencing the exit feasibility.
bool satisfiesInheritedRestrictions(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
ExtensionElements(XML::bpmn::tBaseElement *baseElement, const AttributeRegistry attributeRegistry_, BPMN::Scope *parent=nullptr, std::vector< std::reference_wrapper< XML::bpmnos::tAttribute > >={})
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::set< const Attribute * > entryDependencies
Set containing all input attributes influencing the entry feasibility.
void applyOperators(BPMNOS::Values &status, DataType &data, BPMNOS::Values &globals) const
const MessageDefinition * getMessageDefinition(size_t index) const
std::vector< std::unique_ptr< Restriction > > restrictions
Vector containing new restrictions provided for the node.
AttributeRegistry attributeRegistry
Registry allowing to look up all status and data attributes by their names.
std::vector< std::unique_ptr< MessageDefinition > > messageDefinitions
Vector containing message definition(s) provided for the node.
std::vector< std::unique_ptr< Attribute > > data
Vector containing data attributes declared for data objects within the node's scope.
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 BPMN elements within a Scope.
Definition bpmn++.h:16562
T * represents()
Attempts to cast the element to the specified type T.
Definition bpmn++.h:16236
BaseElement * baseElement
Reference to the base element the extension elements are bound to.
Definition bpmn++.h:16391
XML::bpmn::tExtensionElements * element
Definition bpmn++.h:16362
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::optional< std::reference_wrapper< T > > getOptionalChild()
Get an optional child of type T.
Definition bpmn++.h:288
T * is()
Returns a pointer of type T of the object.
Definition bpmn++.h:159
std::vector< std::reference_wrapper< T > > getChildren()
Get all children of type T.
Definition bpmn++.h:302
T * get()
Attempt to cast the current instance to the specified type T.
Definition bpmn++.h:172
const std::string Instance
Definition Keywords.h:13
const std::string Timestamp
Definition Keywords.h:12
BPMNOS_NUMBER_TYPE number
Definition Number.h:42
The BPMN namespace contains linked classes representing a BPMN model.
Definition bpmn++.h:16221