69 if (table.size() < 2) {
70 throw std::runtime_error(
"StochasticDataProvider: table must have at least a header and one data row");
75 throw std::runtime_error(
"StochasticDataProvider: expected 3, 4, or 6 columns, got " + std::to_string(
columnCount));
79 enum { INSTANCE_ID, NODE_ID, INITIALIZATION, DISCLOSURE, ARRIVAL, COMPLETION };
81 for (
auto& row : table | std::views::drop(1)) {
86 throw std::runtime_error(
"StochasticDataProvider: inconsistent number of cells");
90 if (!std::holds_alternative<std::string>(row.at(INSTANCE_ID))) {
91 throw std::runtime_error(
"StochasticDataProvider: illegal instance id");
93 std::string instanceIdentifier = std::get<std::string>(row.at(INSTANCE_ID));
96 if (!std::holds_alternative<std::string>(row.at(NODE_ID))) {
97 throw std::runtime_error(
"StochasticDataProvider: illegal node id");
99 std::string nodeId = std::get<std::string>(row.at(NODE_ID));
102 if (!std::holds_alternative<std::string>(row.at(INITIALIZATION))) {
103 throw std::runtime_error(
"StochasticDataProvider: illegal initialization");
105 std::string initialization = std::get<std::string>(row.at(INITIALIZATION));
108 std::string disclosureExpression;
110 if (!std::holds_alternative<std::string>(row.at(DISCLOSURE))) {
111 throw std::runtime_error(
"StochasticDataProvider: illegal disclosure");
113 disclosureExpression = std::get<std::string>(row.at(DISCLOSURE));
117 std::string arrivalExpression;
119 if (!std::holds_alternative<std::string>(row.at(ARRIVAL))) {
120 throw std::runtime_error(
"StochasticDataProvider: illegal arrival");
122 arrivalExpression = std::get<std::string>(row.at(ARRIVAL));
126 std::string completionExpression;
128 if (!std::holds_alternative<std::string>(row.at(COMPLETION))) {
129 throw std::runtime_error(
"StochasticDataProvider: illegal completion");
131 completionExpression = std::get<std::string>(row.at(COMPLETION));
135 if (instanceIdentifier.empty() && nodeId.empty()) {
136 if (!disclosureExpression.empty()) {
137 throw std::runtime_error(
"StochasticDataProvider: global attributes must not have disclosure");
139 if (!arrivalExpression.empty()) {
140 throw std::runtime_error(
"StochasticDataProvider: global attributes must not have arrival");
142 if (!completionExpression.empty()) {
143 throw std::runtime_error(
"StochasticDataProvider: global attributes must not have completion");
145 if (initialization.empty()) {
151 for (
auto& [
id, globalAttribute] :
attributes[
nullptr]) {
152 if (globalAttribute->name == attributeName) {
153 attribute = globalAttribute;
158 throw std::runtime_error(
"StochasticDataProvider: unknown global attribute '" + attributeName +
"'");
163 globals[globalAttribute->index] = globalValue;
168 if (!value.has_value()) {
169 throw std::runtime_error(
"StochasticDataProvider: failed to evaluate global '" + attributeName +
"'");
173 else if (instanceIdentifier.empty()) {
174 throw std::runtime_error(
"StochasticDataProvider: instance id required when node id is provided");
183 throw std::runtime_error(
"StochasticDataProvider: first row for instance '" + instanceIdentifier +
184 "' must reference a process node, got '" + nodeId +
"'");
188 std::numeric_limits<BPMNOS::number>::max(), {}};
195 if (!completionExpression.empty()) {
200 throw std::runtime_error(
"StochasticDataProvider: COMPLETION only valid for Task nodes, not '" +
206 if (!extensionElements->attributeRegistry.contains(attributeName)) {
207 throw std::runtime_error(
"StochasticDataProvider: node '" + nodeId +
208 "' has no attribute '" + attributeName +
"'");
212 auto expression = std::make_unique<Expression>(
stochasticHandle, expressionString,
213 extensionElements->attributeRegistry);
218 if (!arrivalExpression.empty()) {
220 throw std::runtime_error(
"StochasticDataProvider: ARRIVAL only valid for Activity nodes, not '" +
226 if (!extensionElements->attributeRegistry.contains(attributeName)) {
227 throw std::runtime_error(
"StochasticDataProvider: node '" + nodeId +
228 "' has no attribute '" + attributeName +
"'");
232 if (attribute->expression) {
233 throw std::runtime_error(
"StochasticDataProvider: attribute '" + attributeName +
234 "' has model expression and cannot use ARRIVAL");
238 if (instance.data.contains(attribute)) {
239 throw std::runtime_error(
"StochasticDataProvider: attribute '" + attributeName +
240 "' cannot use both ARRIVAL and INITIALIZATION");
244 if (pending.attribute == attribute) {
245 throw std::runtime_error(
"StochasticDataProvider: attribute '" + attributeName +
246 "' cannot use both ARRIVAL and INITIALIZATION");
251 auto expression = std::make_unique<Expression>(
stochasticHandle, expressionString,
252 extensionElements->attributeRegistry);
257 if (initialization.empty()) {
263 if (!extensionElements->attributeRegistry.contains(attributeName)) {
264 throw std::runtime_error(
"StochasticDataProvider: node '" + nodeId +
265 "' has no attribute '" + attributeName +
"'");
269 if (attribute->expression) {
270 throw std::runtime_error(
"StochasticDataProvider: value of attribute '" + attributeName +
271 "' is initialized by expression and must not be provided explicitly");
277 if (arrival.attribute == attribute) {
278 throw std::runtime_error(
"StochasticDataProvider: attribute '" + attributeName +
279 "' cannot use both INITIALIZATION and ARRIVAL");
286 if (!disclosureExpression.empty()) {
295 if (disclosureTime == 0) {
297 instance.data[attribute] = value;
314 effectiveInstantiation = std::max(effectiveInstantiation,
disclosure.at(
id).at(instance.process));
327 const std::string& initialization)
const {
328 auto pos = initialization.find(
":=");
329 if (pos == std::string::npos) {
330 throw std::runtime_error(
"StochasticDataProvider: initialization must be 'attribute := expression', got '" +
331 initialization +
"'");
334 std::string attributeName = initialization.substr(0, pos);
335 std::string expression = initialization.substr(pos + 2);
337 auto trimStart = attributeName.find_first_not_of(
" \t");
338 auto trimEnd = attributeName.find_last_not_of(
" \t");
339 if (trimStart == std::string::npos) {
340 throw std::runtime_error(
"StochasticDataProvider: empty attribute name in '" + initialization +
"'");
342 attributeName = attributeName.substr(trimStart, trimEnd - trimStart + 1);
344 trimStart = expression.find_first_not_of(
" \t");
345 trimEnd = expression.find_last_not_of(
" \t");
346 if (trimStart == std::string::npos) {
347 throw std::runtime_error(
"StochasticDataProvider: empty expression in '" + initialization +
"'");
349 expression = expression.substr(trimStart, trimEnd - trimStart + 1);
351 return {attributeName, expression};
435 auto instantiationTime = instance.data[timestampAttribute];
436 scenario->addInstance(instance.process,
id, instantiationTime);
437 for (
auto& [attribute, value] : instance.data) {
438 scenario->setValue(
id, attribute, value);
443 for (
auto& [instanceId, nodes] :
disclosure) {
444 for (
auto& [node, disclosureTime] : nodes) {
445 scenario->setDisclosure(instanceId, node, disclosureTime);
451 for (
auto& pending : pendings) {
452 scenario->addPendingDisclosure(instanceId, {pending.attribute, pending.disclosureTime, pending.value});
458 for (
auto& [task, expressions] : tasks) {
459 for (
auto& sourceExpression : expressions) {
461 sourceExpression.expression->expression,
462 sourceExpression.expression->attributeRegistry);
463 scenario->addCompletionExpression(instanceId, task, {sourceExpression.attribute, std::move(expression)});
470 for (
auto& [node, expressions] : nodes) {
471 for (
auto& sourceExpression : expressions) {
473 sourceExpression.expression->expression,
474 sourceExpression.expression->attributeRegistry);
475 scenario->addArrivalExpression(instanceId, node, {sourceExpression.attribute, std::move(expression)});
481 scenario->revealData(0);