Skip to content

Commit 678497a

Browse files
[Application] Integrate staged batch run with latest main
1 parent 4051497 commit 678497a

14 files changed

Lines changed: 1169 additions & 150 deletions

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ add_library(safecrowd_domain STATIC
8282
src/domain/PopulationSpec.h
8383
src/domain/ScenarioAuthoring.h
8484
src/domain/ScenarioAuthoring.cpp
85+
src/domain/ScenarioBatchRunner.h
86+
src/domain/ScenarioBatchRunner.cpp
8587
src/domain/ScenarioRiskMetrics.h
8688
src/domain/ScenarioRiskMetrics.cpp
8789
src/domain/ScenarioRiskMetricsSystem.cpp
@@ -156,6 +158,7 @@ if (BUILD_TESTING)
156158
tests/DeterministicRngTests.cpp
157159
tests/ScenarioSimulationRunnerTests.cpp
158160
tests/ScenarioAuthoringTests.cpp
161+
tests/ScenarioBatchRunnerTests.cpp
159162
)
160163

161164
target_include_directories(safecrowd_tests
@@ -191,6 +194,7 @@ if (SAFECROWD_BUILD_APP)
191194
src/application/ProjectNavigatorWidget.h
192195
src/application/ProjectPersistence.h
193196
src/application/ScenarioAuthoringWidget.h
197+
src/application/ScenarioBatchResultWidget.h
194198
src/application/ScenarioCanvasWidget.h
195199
src/application/ScenarioResultWidget.h
196200
src/application/ScenarioRunWidget.h
@@ -213,6 +217,7 @@ if (SAFECROWD_BUILD_APP)
213217
src/application/ProjectNavigatorWidget.cpp
214218
src/application/ProjectPersistence.cpp
215219
src/application/ScenarioAuthoringWidget.cpp
220+
src/application/ScenarioBatchResultWidget.cpp
216221
src/application/ScenarioCanvasWidget.cpp
217222
src/application/ScenarioResultWidget.cpp
218223
src/application/ScenarioRunWidget.cpp

src/application/MainWindow.cpp

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "application/ProjectPersistence.h"
1313
#include "application/ProjectNavigatorWidget.h"
1414
#include "application/ScenarioAuthoringWidget.h"
15+
#include "application/ScenarioBatchResultWidget.h"
1516
#include "application/ScenarioResultWidget.h"
1617
#include "application/ScenarioRunWidget.h"
1718
#include "domain/DemoFixtureService.h"
@@ -73,12 +74,17 @@ ProjectWorkspaceState makeEvacuationScenarioDemoWorkspace() {
7374
workspace.activeView = ProjectWorkspaceView::ScenarioResult;
7475
workspace.authoring = std::move(authoring);
7576
workspace.runningScenario = fixture.alternativeScenario;
77+
workspace.runningScenarios = {fixture.baselineScenario, fixture.alternativeScenario};
7678
workspace.result = SavedScenarioResultState{
77-
.scenario = std::move(fixture.alternativeScenario),
79+
.scenario = fixture.alternativeScenario,
7880
.frame = std::move(fixture.frame),
7981
.risk = std::move(fixture.risk),
8082
.artifacts = std::move(fixture.artifacts),
8183
};
84+
workspace.batchResult = SavedScenarioBatchResultState{
85+
.results = {*workspace.result},
86+
.currentResultIndex = 0,
87+
};
8288
return workspace;
8389
}
8490

@@ -410,6 +416,15 @@ void MainWindow::openProject(const ProjectMetadata& metadata) {
410416
}
411417
break;
412418
case ProjectWorkspaceView::ScenarioRun:
419+
if (!workspace.runningScenarios.empty()) {
420+
showScenarioRun(
421+
*importResult.layout,
422+
std::move(workspace.runningScenarios),
423+
workspace.authoring.has_value()
424+
? std::make_optional(initialStateFromSaved(*workspace.authoring, *importResult.layout))
425+
: std::nullopt);
426+
return;
427+
}
413428
if (workspace.runningScenario.has_value()) {
414429
showScenarioRun(
415430
*importResult.layout,
@@ -421,6 +436,16 @@ void MainWindow::openProject(const ProjectMetadata& metadata) {
421436
}
422437
break;
423438
case ProjectWorkspaceView::ScenarioResult:
439+
if (workspace.batchResult.has_value() && workspace.batchResult->results.size() > 1) {
440+
showScenarioBatchResult(
441+
*importResult.layout,
442+
workspace.batchResult->results,
443+
workspace.batchResult->currentResultIndex,
444+
workspace.authoring.has_value()
445+
? std::make_optional(initialStateFromSaved(*workspace.authoring, *importResult.layout))
446+
: std::nullopt);
447+
return;
448+
}
424449
if (workspace.result.has_value()) {
425450
showScenarioResult(
426451
*importResult.layout,
@@ -477,6 +502,22 @@ void MainWindow::saveCurrentProject() {
477502
if (auto* authoringWidget = visibleChild<ScenarioAuthoringWidget>(centralWidget())) {
478503
workspace.activeView = ProjectWorkspaceView::ScenarioAuthoring;
479504
workspace.authoring = authoringWidget->currentSavedState();
505+
} else if (auto* batchResultWidget = visibleChild<ScenarioBatchResultWidget>(centralWidget())) {
506+
workspace.activeView = ProjectWorkspaceView::ScenarioResult;
507+
if (auto authoring = batchResultWidget->returnAuthoringState(); authoring.has_value()) {
508+
workspace.authoring = savedStateFromInitial(*authoring);
509+
}
510+
workspace.batchResult = SavedScenarioBatchResultState{
511+
.results = batchResultWidget->results(),
512+
.currentResultIndex = batchResultWidget->currentResultIndex(),
513+
};
514+
if (!workspace.batchResult->results.empty()) {
515+
const auto index = std::clamp(
516+
workspace.batchResult->currentResultIndex,
517+
0,
518+
static_cast<int>(workspace.batchResult->results.size()) - 1);
519+
workspace.result = workspace.batchResult->results[static_cast<std::size_t>(index)];
520+
}
480521
} else if (auto* resultWidget = visibleChild<ScenarioResultWidget>(centralWidget())) {
481522
workspace.activeView = ProjectWorkspaceView::ScenarioResult;
482523
if (resultWidget->returnAuthoringState().has_value()) {
@@ -494,6 +535,7 @@ void MainWindow::saveCurrentProject() {
494535
workspace.authoring = savedStateFromInitial(*runWidget->returnAuthoringState());
495536
}
496537
workspace.runningScenario = runWidget->scenario();
538+
workspace.runningScenarios = runWidget->scenarios();
497539
}
498540

499541
if (!ProjectPersistence::saveProjectWorkspace(currentProject_, workspace, &errorMessage)) {
@@ -603,10 +645,20 @@ void MainWindow::showScenarioRun(
603645
const safecrowd::domain::FacilityLayout2D& layout,
604646
const safecrowd::domain::ScenarioDraft& scenario,
605647
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState) {
648+
showScenarioRun(
649+
layout,
650+
std::vector<safecrowd::domain::ScenarioDraft>{scenario},
651+
std::move(returnAuthoringState));
652+
}
653+
654+
void MainWindow::showScenarioRun(
655+
const safecrowd::domain::FacilityLayout2D& layout,
656+
std::vector<safecrowd::domain::ScenarioDraft> scenarios,
657+
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState) {
606658
setCentralWidget(new ScenarioRunWidget(
607659
currentProject_.name,
608660
layout,
609-
scenario,
661+
std::move(scenarios),
610662
[this]() {
611663
saveCurrentProject();
612664
},
@@ -622,8 +674,37 @@ void MainWindow::showScenarioRun(
622674
showLayoutReview(currentProject_);
623675
}
624676
},
625-
this,
626-
std::move(returnAuthoringState)));
677+
std::move(returnAuthoringState),
678+
this));
679+
}
680+
681+
void MainWindow::showScenarioBatchResult(
682+
const safecrowd::domain::FacilityLayout2D& layout,
683+
std::vector<SavedScenarioResultState> results,
684+
int currentResultIndex,
685+
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState) {
686+
setCentralWidget(new ScenarioBatchResultWidget(
687+
currentProject_.name,
688+
layout,
689+
std::move(results),
690+
[this]() {
691+
saveCurrentProject();
692+
},
693+
[this]() {
694+
hasCurrentProject_ = false;
695+
currentProject_ = {};
696+
showProjectNavigator();
697+
},
698+
[this]() {
699+
if (lastApprovedImportResult_.has_value()) {
700+
showLayoutReview(currentProject_, *lastApprovedImportResult_);
701+
} else {
702+
showLayoutReview(currentProject_);
703+
}
704+
},
705+
std::move(returnAuthoringState),
706+
currentResultIndex,
707+
this));
627708
}
628709

629710
void MainWindow::showScenarioResult(

src/application/MainWindow.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#pragma once
22

33
#include <optional>
4+
#include <vector>
45

56
#include <QMainWindow>
67

78
#include "application/ProjectMetadata.h"
9+
#include "application/ProjectWorkspaceState.h"
810
#include "application/ScenarioAuthoringWidget.h"
911
#include "domain/ImportResult.h"
1012
#include "domain/ScenarioResultArtifacts.h"
@@ -41,6 +43,15 @@ class MainWindow : public QMainWindow {
4143
const safecrowd::domain::FacilityLayout2D& layout,
4244
const safecrowd::domain::ScenarioDraft& scenario,
4345
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState = std::nullopt);
46+
void showScenarioRun(
47+
const safecrowd::domain::FacilityLayout2D& layout,
48+
std::vector<safecrowd::domain::ScenarioDraft> scenarios,
49+
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState = std::nullopt);
50+
void showScenarioBatchResult(
51+
const safecrowd::domain::FacilityLayout2D& layout,
52+
std::vector<SavedScenarioResultState> results,
53+
int currentResultIndex,
54+
std::optional<ScenarioAuthoringWidget::InitialState> returnAuthoringState = std::nullopt);
4455
void showScenarioResult(
4556
const safecrowd::domain::FacilityLayout2D& layout,
4657
const safecrowd::domain::ScenarioDraft& scenario,

src/application/ProjectPersistence.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include "application/ProjectPersistence.h"
22

33
#include <algorithm>
4+
#include <cstddef>
5+
#include <vector>
46

57
#include <QDateTime>
68
#include <QDebug>
@@ -1137,6 +1139,46 @@ SavedScenarioResultState resultStateFromJson(const QJsonObject& object) {
11371139
};
11381140
}
11391141

1142+
QJsonArray scenarioDraftsToJson(const std::vector<safecrowd::domain::ScenarioDraft>& scenarios) {
1143+
QJsonArray array;
1144+
for (const auto& scenario : scenarios) {
1145+
array.append(scenarioDraftToJson(scenario));
1146+
}
1147+
return array;
1148+
}
1149+
1150+
std::vector<safecrowd::domain::ScenarioDraft> scenarioDraftsFromJson(const QJsonArray& array) {
1151+
std::vector<safecrowd::domain::ScenarioDraft> scenarios;
1152+
scenarios.reserve(static_cast<std::size_t>(array.size()));
1153+
for (const auto& value : array) {
1154+
scenarios.push_back(scenarioDraftFromJson(value.toObject()));
1155+
}
1156+
return scenarios;
1157+
}
1158+
1159+
QJsonObject batchResultStateToJson(const SavedScenarioBatchResultState& batch) {
1160+
QJsonObject object;
1161+
QJsonArray results;
1162+
for (const auto& result : batch.results) {
1163+
results.append(resultStateToJson(result));
1164+
}
1165+
object["results"] = results;
1166+
object["currentResultIndex"] = batch.currentResultIndex;
1167+
return object;
1168+
}
1169+
1170+
SavedScenarioBatchResultState batchResultStateFromJson(const QJsonObject& object) {
1171+
SavedScenarioBatchResultState batch;
1172+
for (const auto& value : object.value("results").toArray()) {
1173+
batch.results.push_back(resultStateFromJson(value.toObject()));
1174+
}
1175+
batch.currentResultIndex = object.value("currentResultIndex").toInt(0);
1176+
if (batch.currentResultIndex < 0 || batch.currentResultIndex >= static_cast<int>(batch.results.size())) {
1177+
batch.currentResultIndex = 0;
1178+
}
1179+
return batch;
1180+
}
1181+
11401182
QJsonObject workspaceStateToJson(const ProjectWorkspaceState& state) {
11411183
QJsonObject object;
11421184
object["version"] = 1;
@@ -1147,9 +1189,15 @@ QJsonObject workspaceStateToJson(const ProjectWorkspaceState& state) {
11471189
if (state.runningScenario.has_value()) {
11481190
object["runningScenario"] = scenarioDraftToJson(*state.runningScenario);
11491191
}
1192+
if (!state.runningScenarios.empty()) {
1193+
object["runningScenarios"] = scenarioDraftsToJson(state.runningScenarios);
1194+
}
11501195
if (state.result.has_value()) {
11511196
object["result"] = resultStateToJson(*state.result);
11521197
}
1198+
if (state.batchResult.has_value()) {
1199+
object["batchResult"] = batchResultStateToJson(*state.batchResult);
1200+
}
11531201
return object;
11541202
}
11551203

@@ -1162,9 +1210,22 @@ ProjectWorkspaceState workspaceStateFromJson(const QJsonObject& object) {
11621210
if (object.value("runningScenario").isObject()) {
11631211
state.runningScenario = scenarioDraftFromJson(object.value("runningScenario").toObject());
11641212
}
1213+
if (object.value("runningScenarios").isArray()) {
1214+
state.runningScenarios = scenarioDraftsFromJson(object.value("runningScenarios").toArray());
1215+
} else if (state.runningScenario.has_value()) {
1216+
state.runningScenarios.push_back(*state.runningScenario);
1217+
}
11651218
if (object.value("result").isObject()) {
11661219
state.result = resultStateFromJson(object.value("result").toObject());
11671220
}
1221+
if (object.value("batchResult").isObject()) {
1222+
state.batchResult = batchResultStateFromJson(object.value("batchResult").toObject());
1223+
} else if (state.result.has_value()) {
1224+
state.batchResult = SavedScenarioBatchResultState{
1225+
.results = {*state.result},
1226+
.currentResultIndex = 0,
1227+
};
1228+
}
11681229
return state;
11691230
}
11701231

src/application/ProjectWorkspaceState.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,18 @@ struct SavedScenarioResultState {
5050
safecrowd::domain::ScenarioResultArtifacts artifacts{};
5151
};
5252

53+
struct SavedScenarioBatchResultState {
54+
std::vector<SavedScenarioResultState> results{};
55+
int currentResultIndex{0};
56+
};
57+
5358
struct ProjectWorkspaceState {
5459
ProjectWorkspaceView activeView{ProjectWorkspaceView::LayoutReview};
5560
std::optional<SavedScenarioAuthoringState> authoring{};
5661
std::optional<safecrowd::domain::ScenarioDraft> runningScenario{};
62+
std::vector<safecrowd::domain::ScenarioDraft> runningScenarios{};
5763
std::optional<SavedScenarioResultState> result{};
64+
std::optional<SavedScenarioBatchResultState> batchResult{};
5865
};
5966

6067
} // namespace safecrowd::application

src/application/ScenarioAuthoringWidget.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,12 +1060,12 @@ void ScenarioAuthoringWidget::refreshScenarioSwitcher() {
10601060
scenarioSwitcher_->blockSignals(false);
10611061
}
10621062

1063-
void ScenarioAuthoringWidget::runFirstStagedBaselineScenario() {
1064-
const auto* scenario = firstStagedBaselineScenario();
1065-
if (scenario == nullptr) {
1063+
void ScenarioAuthoringWidget::runStagedScenarios() {
1064+
auto scenarios = stagedRunnableScenarios();
1065+
if (scenarios.empty()) {
10661066
if (stagedScenariosLabel_ != nullptr) {
10671067
stagedScenariosLabel_->setText(stagedScenariosLabel_->text()
1068-
+ "\n\nNo staged baseline scenario is ready to run.");
1068+
+ "\n\nNo staged scenario is ready to run.");
10691069
}
10701070
return;
10711071
}
@@ -1078,12 +1078,12 @@ void ScenarioAuthoringWidget::runFirstStagedBaselineScenario() {
10781078
auto* runWidget = new ScenarioRunWidget(
10791079
projectName_,
10801080
layout_,
1081-
scenario->draft,
1081+
std::move(scenarios),
10821082
saveProjectHandler_,
10831083
openProjectHandler_,
10841084
backToLayoutReviewHandler_,
1085-
this,
1086-
currentInitialState());
1085+
currentInitialState(),
1086+
this);
10871087
rootLayout->replaceWidget(shell_, runWidget);
10881088
shell_->hide();
10891089
shell_->deleteLater();
@@ -1307,7 +1307,7 @@ QWidget* ScenarioAuthoringWidget::createScenarioPanel() {
13071307
stageCurrentScenario();
13081308
});
13091309
connect(executeRunButton_, &QPushButton::clicked, this, [this]() {
1310-
runFirstStagedBaselineScenario();
1310+
runStagedScenarios();
13111311
});
13121312

13131313
return inspector;
@@ -1327,13 +1327,16 @@ const ScenarioAuthoringWidget::ScenarioState* ScenarioAuthoringWidget::currentSc
13271327
return &scenarios_[currentScenarioIndex_];
13281328
}
13291329

1330-
const ScenarioAuthoringWidget::ScenarioState* ScenarioAuthoringWidget::firstStagedBaselineScenario() const {
1331-
const auto it = std::find_if(scenarios_.begin(), scenarios_.end(), [](const auto& scenario) {
1332-
return scenario.stagedForRun
1333-
&& scenarioHasOccupants(scenario)
1334-
&& scenario.draft.role == safecrowd::domain::ScenarioRole::Baseline;
1335-
});
1336-
return it == scenarios_.end() ? nullptr : &(*it);
1330+
std::vector<safecrowd::domain::ScenarioDraft> ScenarioAuthoringWidget::stagedRunnableScenarios() const {
1331+
std::vector<safecrowd::domain::ScenarioDraft> staged;
1332+
for (const auto& scenario : scenarios_) {
1333+
if (scenario.stagedForRun && scenarioHasOccupants(scenario)) {
1334+
auto draft = scenario.draft;
1335+
draft.control.events = scenario.events;
1336+
staged.push_back(std::move(draft));
1337+
}
1338+
}
1339+
return staged;
13371340
}
13381341

13391342
} // namespace safecrowd::application

0 commit comments

Comments
 (0)