Skip to content

Commit 415be06

Browse files
authored
[Application] align v1.1 workspace UI design (#116)
1 parent 9424e5f commit 415be06

18 files changed

Lines changed: 965 additions & 161 deletions

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ add_library(safecrowd_domain STATIC
7575
src/domain/SafeCrowdDomain.cpp
7676
src/domain/Geometry2D.h
7777
src/domain/PopulationSpec.h
78+
src/domain/ScenarioAuthoring.h
79+
src/domain/ProjectRepository.h
80+
src/domain/ScenarioTemplateCatalog.h
7881
src/domain/RawImportModel.h
7982
src/domain/CanonicalGeometry.h
8083
src/domain/DemoFixtureService.h

docs/architecture/프로젝트 구조.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ Project/
100100
- 공간 구조
101101
- 시나리오 데이터
102102
- `FacilityLayout`, `PopulationSpec`, `EnvironmentState`, `ControlPlan`, `ExecutionConfig` 입력 계약
103+
- `ScenarioDraft` 같은 application authoring 계약과 baseline/alternative/recommended 구분
103104
- `FacilityLayout` 안의 `Room`, `Door`, `Connector`, `ControlZone`, `MeasurementRegionSpec` 같은 실행 계약
104105
- baseline과 대안 비교를 위한 `ScenarioVariation`
106+
- `ScenarioTemplateCatalog` 같은 authoring helper 계약
107+
- `ProjectRepository`, `ResultRepository` 같은 저장 경계 계약
105108
- 시나리오 보정(`ScenarioCalibrationService`)
106109
- 단일 실행 세션(`SimulationSession`)과 배치 실행(`ScenarioBatchRunner`)
107110
- 시뮬레이션 규칙
@@ -120,10 +123,12 @@ Project/
120123
포함할 것:
121124
- `main()`
122125
- 메인 윈도우
123-
- 시나리오 편집 UI
124-
- 실행/정지 버튼
125-
- 결과 표시 화면
126-
- 추천안 검토 및 적용 화면
126+
- `Project`, `Authoring`, `Run`, `Analysis` workspace shell
127+
- import review, layout correction canvas, scenario library, template picker, scenario editor, readiness / diff UI
128+
- run queue, 실행/정지 버튼, batch progress, live viewport, heatmap overlay
129+
- 결과 표시 화면, variation summary, comparison, export, recommendation 검토 및 적용 화면
130+
- UI state gating과 패널 활성 조건
131+
- `ProjectRepository``ResultRepository`를 구분해 domain 호출과 화면을 연결하는 코드
127132
- domain 호출과 화면 연결
128133

129134
넣지 말아야 할 것:
@@ -172,7 +177,10 @@ Qt viewport가 엔진 렌더러와 직접 연결되어야 하면 `application ->
172177
- `uml/domain-control-model.puml` : 행동, 트리거, occupant tag, route choice policy, `ControlZone` 대상 제어 구조
173178
- `uml/engine-routing-and-connectors.puml` : topology snapshot, connector, path cost, `ControlZone` access override, measurement 경계 구조
174179
- `uml/domain-result-artifacts.puml` : run, variation, comparison, cumulative 결과 아티팩트 구조
175-
- `uml/application-run-results-workflow.puml` : persisted artifact 기준의 작성, 실행, 비교, 내보내기, 추천 흐름
180+
- `uml/application-run-results-workflow.puml` : project persistence와 persisted artifact 기준의 전체 작성, 실행, 비교, 내보내기, 추천 흐름
181+
- `uml/application-authoring-workspace.puml` : import 검토/보정, 템플릿, 시나리오 작성, readiness / diff UI 구조
182+
- `uml/application-analysis-workspace.puml` : 실행, live viewport, heatmap, 결과 요약, 비교, 추천, 내보내기 UI 구조
183+
- `uml/application-workspace-state-model.puml` : workspace 상태 전이와 패널 활성 규칙
176184

177185
각 UML 해설 문서는 다음 파일에 둔다.
178186
- `uml/project-structure.puml 해설.md`
@@ -185,6 +193,9 @@ Qt viewport가 엔진 렌더러와 직접 연결되어야 하면 `application ->
185193
- `uml/engine-routing-and-connectors.puml 해설.md`
186194
- `uml/domain-result-artifacts.puml 해설.md`
187195
- `uml/application-run-results-workflow.puml 해설.md`
196+
- `uml/application-authoring-workspace.puml 해설.md`
197+
- `uml/application-analysis-workspace.puml 해설.md`
198+
- `uml/application-workspace-state-model.puml 해설.md`
188199

189200
---
190201

src/application/MainWindow.cpp

Lines changed: 202 additions & 64 deletions
Large diffs are not rendered by default.

src/application/MainWindow.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class MainWindow : public QMainWindow {
2727
QPushButton* startButton_{nullptr};
2828
QPushButton* pauseButton_{nullptr};
2929
QPushButton* stopButton_{nullptr};
30+
QLabel* workspaceStageValue_{nullptr};
3031
QLabel* runtimeStateValue_{nullptr};
3132
QLabel* frameValue_{nullptr};
3233
QLabel* fixedStepValue_{nullptr};

src/domain/ProjectRepository.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <optional>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "domain/ScenarioAuthoring.h"
8+
9+
namespace safecrowd::domain {
10+
11+
struct ProjectArtifactReference {
12+
std::string artifactKind{};
13+
std::string artifactId{};
14+
std::string storageKey{};
15+
};
16+
17+
struct ProjectWorkspaceRecord {
18+
ProjectWorkspaceSnapshot workspace{};
19+
std::vector<ProjectArtifactReference> artifactIndex{};
20+
std::vector<std::string> runIds{};
21+
std::vector<std::string> variationKeys{};
22+
};
23+
24+
class ProjectRepository {
25+
public:
26+
virtual ~ProjectRepository() = default;
27+
28+
virtual std::optional<ProjectWorkspaceRecord> loadProject(const std::string& projectId) const = 0;
29+
virtual void saveProject(const ProjectWorkspaceRecord& record) = 0;
30+
};
31+
32+
} // namespace safecrowd::domain

src/domain/ScenarioAuthoring.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "domain/FacilityLayout2D.h"
8+
#include "domain/PopulationSpec.h"
9+
10+
namespace safecrowd::domain {
11+
12+
enum class ScenarioRole {
13+
Baseline,
14+
Alternative,
15+
Recommended,
16+
};
17+
18+
struct EnvironmentState {
19+
bool reducedVisibility{false};
20+
std::string familiarityProfile{};
21+
std::string guidanceProfile{};
22+
};
23+
24+
struct OperationalEventDraft {
25+
std::string id{};
26+
std::string name{};
27+
std::string triggerSummary{};
28+
std::string targetSummary{};
29+
};
30+
31+
struct ControlPlan {
32+
std::vector<OperationalEventDraft> events{};
33+
};
34+
35+
struct ExecutionConfig {
36+
double timeLimitSeconds{0.0};
37+
double sampleIntervalSeconds{0.0};
38+
std::uint32_t repeatCount{1};
39+
std::uint32_t baseSeed{0};
40+
bool recordOccupantHistory{false};
41+
};
42+
43+
struct ScenarioDraft {
44+
std::string scenarioId{};
45+
std::string name{};
46+
ScenarioRole role{ScenarioRole::Alternative};
47+
PopulationSpec population{};
48+
EnvironmentState environment{};
49+
ControlPlan control{};
50+
ExecutionConfig execution{};
51+
std::string sourceTemplateId{};
52+
std::vector<std::string> variationDiffKeys{};
53+
std::vector<std::string> blockingIssues{};
54+
};
55+
56+
struct ProjectWorkspaceSnapshot {
57+
std::string projectId{};
58+
std::string displayName{};
59+
FacilityLayout2D layout{};
60+
std::vector<ScenarioDraft> scenarios{};
61+
};
62+
63+
} // namespace safecrowd::domain
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#pragma once
2+
3+
#include <optional>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "domain/ScenarioAuthoring.h"
8+
9+
namespace safecrowd::domain {
10+
11+
struct ScenarioTemplateDescriptor {
12+
std::string templateId{};
13+
std::string name{};
14+
std::string intendedUse{};
15+
std::vector<std::string> requiredLayoutFeatures{};
16+
std::vector<std::string> focusRiskAxes{};
17+
};
18+
19+
struct ScenarioTemplateDraft {
20+
ScenarioDraft scenario{};
21+
std::vector<std::string> highlightedFields{};
22+
std::vector<std::string> unmetRequirements{};
23+
};
24+
25+
class ScenarioTemplateCatalog {
26+
public:
27+
virtual ~ScenarioTemplateCatalog() = default;
28+
29+
virtual std::vector<ScenarioTemplateDescriptor> listTemplates() const = 0;
30+
virtual std::optional<ScenarioTemplateDraft> instantiate(
31+
const std::string& templateId,
32+
const FacilityLayout2D& layout) const = 0;
33+
};
34+
35+
} // namespace safecrowd::domain

uml/PlantUML 읽는 법.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
## 목적
44
이 문서는 `uml/` 폴더의 PlantUML 다이어그램을 렌더링해서 볼 때, 각 요소와 선이 무엇을 뜻하는지 빠르게 이해할 수 있도록 정리한 문서이다.
55

6-
이 프로젝트에서는 크게 종류를 본다.
6+
이 프로젝트에서는 크게 종류를 본다.
77
- 상위 구조를 보여주는 **component/package 중심 그림**
88
- 엔진 내부 타입 관계를 보여주는 **class diagram**
9+
- UI 게이팅과 흐름을 보여주는 **state diagram**
910

1011
---
1112

@@ -47,6 +48,11 @@
4748
- 이 프로젝트 예시: `WorldCommands`가 즉시 mutation하지 않는다는 설명, `EcsCore`가 재사용 가능한 엔진 코어라는 설명
4849
- 읽는 법: 선과 박스만 보면 놓치기 쉬운 설계 규칙을 보충하는 텍스트다.
4950

51+
### 상태 원과 상태 박스
52+
- 의미: 화면이나 워크플로가 어떤 상태를 거치며 전이되는지 표현한다.
53+
- 이 프로젝트 예시: `NoProject`, `ScenarioReady`, `ComparisonReady`
54+
- 읽는 법: "지금 UI가 어느 조건을 만족한 상태인가"와 "무슨 이벤트로 다음 상태로 가는가"를 본다.
55+
5056
---
5157

5258
## 2. 선과 화살표 읽는 법
@@ -98,6 +104,13 @@ PlantUML에서 선은 크게 두 가지를 같이 본다.
98104
- 예시:
99105
- `PackedComponentStorage ..|> IComponentStorage`
100106

107+
### 상태도에서의 `-->`
108+
- 의미: 상태 전이
109+
- 읽는 법: "`A` 상태에서 어떤 이벤트나 조건이 만족되면 `B` 상태로 이동한다"
110+
- 예시:
111+
- `ScenarioReady --> BatchRunning : Run clicked`
112+
- `ComparisonReady --> RecommendationReady : ScenarioComparison + CumulativeArtifact ready`
113+
101114
### `..>`
102115
- 의미가 같은 표기가 여러 문맥에서 쓰일 수 있음
103116
- 이 프로젝트에서는 주로 "의존" 또는 "간접 연결" 정도로 보면 된다.
@@ -203,6 +216,10 @@ PlantUML에서 선은 크게 두 가지를 같이 본다.
203216
- 이 프로젝트 UML에서는 노트에 실제 구현 규칙이 많이 들어 있다.
204217
- 예를 들어 deferred mutation, reusable engine, query cache 후순위 같은 정보는 노트를 봐야 정확히 이해된다.
205218

219+
### 상태도는 어떤 용도로 보나?
220+
- application state diagram은 화면 구성을 보여주기보다 "버튼과 패널이 언제 활성화되는가"를 읽는 용도로 본다.
221+
- 따라서 상태명과 전이 라벨은 UI 문구이면서 동시에 요구사항 계약이기도 하다.
222+
206223
---
207224

208225
## 7. 한 줄 요약
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
@startuml application-analysis-workspace
2+
title SafeCrowd Application Analysis Workspace
3+
4+
left to right direction
5+
skinparam componentStyle rectangle
6+
skinparam packageStyle rectangle
7+
skinparam shadowing false
8+
skinparam linetype ortho
9+
10+
package "application" {
11+
[Run Control\n(run / pause / stop)] as RunControl
12+
[Batch Progress\n(run / variation progress)] as BatchProgress
13+
[Live Viewport\n(runtime playback)] as LiveViewport
14+
[Heatmap Selector\n(live / persisted layers)] as HeatmapSelector
15+
[Run Results Panel\n(single-run summary)] as RunResultsPanel
16+
[Variation Summary\n(repeated-run aggregate)] as VariationSummary
17+
[Comparison View\n(baseline vs alternatives)] as ComparisonView
18+
[Recommendation Drawer\n(evidence / scenarioize)] as RecommendationDrawer
19+
[Export Dialog\n(canonical artifact bundle)] as ExportDialog
20+
}
21+
22+
package "domain" {
23+
[ScenarioBatchRunner\n(batch orchestration)] as BatchRunner
24+
[SimulationSession\n(single run lifecycle)] as SimulationSession
25+
[ResultRepository\n(run / variation /\ncomparison / cumulative)] as ResultRepository
26+
[ResultAggregator\n(update persisted summaries)] as ResultAggregator
27+
[AlternativeRecommendationService\n(candidate generation)] as RecommendationService
28+
}
29+
30+
package "engine" {
31+
[EngineRuntime\n(playback control)] as EngineRuntime
32+
[IRenderBridge\n(frame sync)] as RenderBridge
33+
}
34+
35+
RunControl --> BatchRunner : start / pause / stop batch
36+
BatchRunner --> BatchProgress : publish current run / variation
37+
BatchRunner --> SimulationSession : spawn run
38+
SimulationSession --> EngineRuntime : initialize / play / stop
39+
EngineRuntime --> RenderBridge : live frame snapshots
40+
RenderBridge --> LiveViewport : present runtime state
41+
42+
HeatmapSelector --> LiveViewport : toggle live overlay
43+
HeatmapSelector --> ResultRepository : load persisted heatmap layer
44+
45+
BatchRunner --> ResultRepository : persist run artifacts
46+
BatchRunner --> ResultAggregator : compute higher-level artifacts
47+
ResultAggregator --> ResultRepository : persist variation / comparison / cumulative artifacts
48+
49+
RunResultsPanel --> ResultRepository : load RunResult summaries
50+
VariationSummary --> ResultRepository : load VariationSummary
51+
RunResultsPanel --> VariationSummary : inspect repeated-run aggregate
52+
VariationSummary --> ComparisonView : open baseline comparison
53+
ComparisonView --> ResultRepository : load ScenarioComparison / CumulativeArtifact
54+
55+
ComparisonView --> RecommendationDrawer : inspect recommendation evidence
56+
RecommendationDrawer --> RecommendationService : request candidates / scenarioize
57+
RecommendationService --> ResultRepository : read ScenarioComparison / CumulativeArtifact
58+
59+
ComparisonView --> ExportDialog : export selected bundle
60+
ExportDialog --> ResultRepository : resolve canonical artifact set
61+
62+
note bottom of LiveViewport
63+
Live viewport consumes runtime snapshots only.
64+
It must not calculate comparison deltas or
65+
infer recommendation evidence from transient state.
66+
end note
67+
68+
note bottom of ComparisonView
69+
Comparison, recommendation, and export
70+
are persisted-analysis workflows.
71+
They stay disabled until the required
72+
stored artifacts exist.
73+
end note
74+
75+
note bottom of HeatmapSelector
76+
The same selector can switch between
77+
live overlays during playback and
78+
persisted layers after aggregation,
79+
but the data source is explicit.
80+
end note
81+
82+
@enduml
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# SafeCrowd UML 설계 해설 - application-analysis-workspace.puml
2+
3+
대상 파일: `uml/application-analysis-workspace.puml`
4+
5+
## 문서 목적
6+
이 문서는 SafeCrowd의 실행 후반과 결과 분석 UI를 설명한다. 핵심은 `live playback``persisted artifact 기반 분석`을 한 화면 군 안에서 연결하되, 데이터 출처를 섞지 않는 것이다.
7+
8+
## `Run Control`
9+
- 개요: batch 실행의 시작, 일시정지, 정지를 요청하는 패널이다.
10+
- 목적: application이 엔진 세부 제어 대신 `ScenarioBatchRunner`를 통해 실행을 조정하게 한다.
11+
- 유의사항: comparison이나 export 조건 판단을 여기서 맡지 않는다.
12+
13+
## `Batch Progress`
14+
- 개요: 현재 run 번호, variation 식별자, 반복 실행 진행률을 보여 주는 패널이다.
15+
- 목적: 사용자가 반복 실행과 batch 상태를 실시간으로 추적하게 한다.
16+
- 유의사항: 결과 집계가 끝나기 전과 후의 상태를 구분해서 보여 준다.
17+
18+
## `Live Viewport`
19+
- 개요: 실행 중 runtime snapshot을 재생하는 시각화 영역이다.
20+
- 목적: 현재 움직임과 즉시적인 overlay를 확인하게 한다.
21+
- 유의사항: persisted comparison이나 recommendation evidence를 직접 계산하지 않는다.
22+
23+
## `Heatmap Selector`
24+
- 개요: heatmap과 위험 레이어의 데이터 출처와 표시 종류를 고르는 컨트롤이다.
25+
- 목적: live overlay와 post-run heatmap을 같은 UI affordance로 연결한다.
26+
- 유의사항: 데이터 출처가 `IRenderBridge` 기반인지 `ResultRepository` 기반인지 명확해야 한다.
27+
28+
## `Run Results Panel`
29+
- 개요: 단일 실행 요약을 읽는 첫 결과 패널이다.
30+
- 목적: run 종료 직후의 핵심 지표를 persisted 형태로 먼저 보여 준다.
31+
- 유의사항: live 상태를 그대로 붙이는 패널이 아니라 저장된 `RunResult` 소비자다.
32+
33+
## `Variation Summary`
34+
- 개요: 같은 variation에 속한 반복 실행 집계를 보여 주는 패널이다.
35+
- 목적: deterministic single run과 repeated-run aggregate를 구분한다.
36+
- 유의사항: `Comparison View`로 넘어가기 전에 repeated-run 맥락을 먼저 고정한다.
37+
38+
## `Comparison View`
39+
- 개요: baseline과 alternative variation을 나란히 비교하는 핵심 분석 패널이다.
40+
- 목적: `ScenarioComparison``CumulativeArtifact`를 기준으로 변화량과 근거를 제시한다.
41+
- 유의사항: ad hoc domain delta 계산기로 쓰지 않는다.
42+
43+
## `Recommendation Drawer`
44+
- 개요: 추천 후보, 근거, 시나리오화 액션을 보여 주는 패널이다.
45+
- 목적: 결과 분석 뒤에 이어지는 설명 가능한 운영 대안 검토 흐름을 만든다.
46+
- 유의사항: 추천 입력은 persisted comparison/cumulative artifact에 한정한다.
47+
48+
## `Export Dialog`
49+
- 개요: canonical 결과 번들을 외부 공유용으로 내보내는 대화상자다.
50+
- 목적: comparison과 recommendation이 보는 같은 결과 계약을 export에도 재사용한다.
51+
- 유의사항: 결과가 충분하지 않으면 비활성 상태를 유지한다.
52+
53+
## `ResultRepository` / `ResultAggregator`
54+
- 개요: `ResultRepository`는 run/variation/comparison/cumulative artifact 저장소이고, `ResultAggregator`는 상위 결과를 생성하는 서비스다.
55+
- 목적: 분석 패널들이 같은 persisted 결과 계약을 읽게 한다.
56+
- 유의사항: comparison, export, recommendation의 선행 조건은 이 저장소에 결과가 존재하는지로 판단한다.

0 commit comments

Comments
 (0)