Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ add_library(safecrowd_domain STATIC
src/domain/SafeCrowdDomain.cpp
src/domain/Geometry2D.h
src/domain/PopulationSpec.h
src/domain/ScenarioAuthoring.h
src/domain/ProjectRepository.h
src/domain/ScenarioTemplateCatalog.h
src/domain/RawImportModel.h
src/domain/CanonicalGeometry.h
src/domain/DemoFixtureService.h
Expand Down
21 changes: 16 additions & 5 deletions docs/architecture/프로젝트 구조.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ Project/
- 공간 구조
- 시나리오 데이터
- `FacilityLayout`, `PopulationSpec`, `EnvironmentState`, `ControlPlan`, `ExecutionConfig` 입력 계약
- `ScenarioDraft` 같은 application authoring 계약과 baseline/alternative/recommended 구분
- `FacilityLayout` 안의 `Room`, `Door`, `Connector`, `ControlZone`, `MeasurementRegionSpec` 같은 실행 계약
- baseline과 대안 비교를 위한 `ScenarioVariation`
- `ScenarioTemplateCatalog` 같은 authoring helper 계약
- `ProjectRepository`, `ResultRepository` 같은 저장 경계 계약
- 시나리오 보정(`ScenarioCalibrationService`)
- 단일 실행 세션(`SimulationSession`)과 배치 실행(`ScenarioBatchRunner`)
- 시뮬레이션 규칙
Expand All @@ -120,10 +123,12 @@ Project/
포함할 것:
- `main()`
- 메인 윈도우
- 시나리오 편집 UI
- 실행/정지 버튼
- 결과 표시 화면
- 추천안 검토 및 적용 화면
- `Project`, `Authoring`, `Run`, `Analysis` workspace shell
- import review, layout correction canvas, scenario library, template picker, scenario editor, readiness / diff UI
- run queue, 실행/정지 버튼, batch progress, live viewport, heatmap overlay
- 결과 표시 화면, variation summary, comparison, export, recommendation 검토 및 적용 화면
- UI state gating과 패널 활성 조건
- `ProjectRepository`와 `ResultRepository`를 구분해 domain 호출과 화면을 연결하는 코드
- domain 호출과 화면 연결

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

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

---

Expand Down
266 changes: 202 additions & 64 deletions src/application/MainWindow.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/application/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class MainWindow : public QMainWindow {
QPushButton* startButton_{nullptr};
QPushButton* pauseButton_{nullptr};
QPushButton* stopButton_{nullptr};
QLabel* workspaceStageValue_{nullptr};
QLabel* runtimeStateValue_{nullptr};
QLabel* frameValue_{nullptr};
QLabel* fixedStepValue_{nullptr};
Expand Down
32 changes: 32 additions & 0 deletions src/domain/ProjectRepository.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <optional>
#include <string>
#include <vector>

#include "domain/ScenarioAuthoring.h"

namespace safecrowd::domain {

struct ProjectArtifactReference {
std::string artifactKind{};
std::string artifactId{};
std::string storageKey{};
};

struct ProjectWorkspaceRecord {
ProjectWorkspaceSnapshot workspace{};
std::vector<ProjectArtifactReference> artifactIndex{};
std::vector<std::string> runIds{};
std::vector<std::string> variationKeys{};
};

class ProjectRepository {
public:
virtual ~ProjectRepository() = default;

virtual std::optional<ProjectWorkspaceRecord> loadProject(const std::string& projectId) const = 0;
virtual void saveProject(const ProjectWorkspaceRecord& record) = 0;
};

} // namespace safecrowd::domain
63 changes: 63 additions & 0 deletions src/domain/ScenarioAuthoring.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include <cstdint>
#include <string>
#include <vector>

#include "domain/FacilityLayout2D.h"
#include "domain/PopulationSpec.h"

namespace safecrowd::domain {

enum class ScenarioRole {
Baseline,
Alternative,
Recommended,
};

struct EnvironmentState {
bool reducedVisibility{false};
std::string familiarityProfile{};
std::string guidanceProfile{};
};

struct OperationalEventDraft {
std::string id{};
std::string name{};
std::string triggerSummary{};
std::string targetSummary{};
};

struct ControlPlan {
std::vector<OperationalEventDraft> events{};
};

struct ExecutionConfig {
double timeLimitSeconds{0.0};
double sampleIntervalSeconds{0.0};
std::uint32_t repeatCount{1};
std::uint32_t baseSeed{0};
bool recordOccupantHistory{false};
};

struct ScenarioDraft {
std::string scenarioId{};
std::string name{};
ScenarioRole role{ScenarioRole::Alternative};
PopulationSpec population{};
EnvironmentState environment{};
ControlPlan control{};
ExecutionConfig execution{};
std::string sourceTemplateId{};
std::vector<std::string> variationDiffKeys{};
std::vector<std::string> blockingIssues{};
};

struct ProjectWorkspaceSnapshot {
std::string projectId{};
std::string displayName{};
FacilityLayout2D layout{};
std::vector<ScenarioDraft> scenarios{};
};

} // namespace safecrowd::domain
35 changes: 35 additions & 0 deletions src/domain/ScenarioTemplateCatalog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <optional>
#include <string>
#include <vector>

#include "domain/ScenarioAuthoring.h"

namespace safecrowd::domain {

struct ScenarioTemplateDescriptor {
std::string templateId{};
std::string name{};
std::string intendedUse{};
std::vector<std::string> requiredLayoutFeatures{};
std::vector<std::string> focusRiskAxes{};
};

struct ScenarioTemplateDraft {
ScenarioDraft scenario{};
std::vector<std::string> highlightedFields{};
std::vector<std::string> unmetRequirements{};
};

class ScenarioTemplateCatalog {
public:
virtual ~ScenarioTemplateCatalog() = default;

virtual std::vector<ScenarioTemplateDescriptor> listTemplates() const = 0;
virtual std::optional<ScenarioTemplateDraft> instantiate(
const std::string& templateId,
const FacilityLayout2D& layout) const = 0;
};

} // namespace safecrowd::domain
19 changes: 18 additions & 1 deletion uml/PlantUML 읽는 법.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
## 목적
이 문서는 `uml/` 폴더의 PlantUML 다이어그램을 렌더링해서 볼 때, 각 요소와 선이 무엇을 뜻하는지 빠르게 이해할 수 있도록 정리한 문서이다.

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

---

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

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

---

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

### 상태도에서의 `-->`
- 의미: 상태 전이
- 읽는 법: "`A` 상태에서 어떤 이벤트나 조건이 만족되면 `B` 상태로 이동한다"
- 예시:
- `ScenarioReady --> BatchRunning : Run clicked`
- `ComparisonReady --> RecommendationReady : ScenarioComparison + CumulativeArtifact ready`

### `..>`
- 의미가 같은 표기가 여러 문맥에서 쓰일 수 있음
- 이 프로젝트에서는 주로 "의존" 또는 "간접 연결" 정도로 보면 된다.
Expand Down Expand Up @@ -203,6 +216,10 @@ PlantUML에서 선은 크게 두 가지를 같이 본다.
- 이 프로젝트 UML에서는 노트에 실제 구현 규칙이 많이 들어 있다.
- 예를 들어 deferred mutation, reusable engine, query cache 후순위 같은 정보는 노트를 봐야 정확히 이해된다.

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

---

## 7. 한 줄 요약
Expand Down
82 changes: 82 additions & 0 deletions uml/application-analysis-workspace.puml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@startuml application-analysis-workspace
title SafeCrowd Application Analysis Workspace

left to right direction
skinparam componentStyle rectangle
skinparam packageStyle rectangle
skinparam shadowing false
skinparam linetype ortho

package "application" {
[Run Control\n(run / pause / stop)] as RunControl
[Batch Progress\n(run / variation progress)] as BatchProgress
[Live Viewport\n(runtime playback)] as LiveViewport
[Heatmap Selector\n(live / persisted layers)] as HeatmapSelector
[Run Results Panel\n(single-run summary)] as RunResultsPanel
[Variation Summary\n(repeated-run aggregate)] as VariationSummary
[Comparison View\n(baseline vs alternatives)] as ComparisonView
[Recommendation Drawer\n(evidence / scenarioize)] as RecommendationDrawer
[Export Dialog\n(canonical artifact bundle)] as ExportDialog
}

package "domain" {
[ScenarioBatchRunner\n(batch orchestration)] as BatchRunner
[SimulationSession\n(single run lifecycle)] as SimulationSession
[ResultRepository\n(run / variation /\ncomparison / cumulative)] as ResultRepository
[ResultAggregator\n(update persisted summaries)] as ResultAggregator
[AlternativeRecommendationService\n(candidate generation)] as RecommendationService
}

package "engine" {
[EngineRuntime\n(playback control)] as EngineRuntime
[IRenderBridge\n(frame sync)] as RenderBridge
}

RunControl --> BatchRunner : start / pause / stop batch
BatchRunner --> BatchProgress : publish current run / variation
BatchRunner --> SimulationSession : spawn run
SimulationSession --> EngineRuntime : initialize / play / stop
EngineRuntime --> RenderBridge : live frame snapshots
RenderBridge --> LiveViewport : present runtime state

HeatmapSelector --> LiveViewport : toggle live overlay
HeatmapSelector --> ResultRepository : load persisted heatmap layer

BatchRunner --> ResultRepository : persist run artifacts
BatchRunner --> ResultAggregator : compute higher-level artifacts
ResultAggregator --> ResultRepository : persist variation / comparison / cumulative artifacts

RunResultsPanel --> ResultRepository : load RunResult summaries
VariationSummary --> ResultRepository : load VariationSummary
RunResultsPanel --> VariationSummary : inspect repeated-run aggregate
VariationSummary --> ComparisonView : open baseline comparison
ComparisonView --> ResultRepository : load ScenarioComparison / CumulativeArtifact

ComparisonView --> RecommendationDrawer : inspect recommendation evidence
RecommendationDrawer --> RecommendationService : request candidates / scenarioize
RecommendationService --> ResultRepository : read ScenarioComparison / CumulativeArtifact

ComparisonView --> ExportDialog : export selected bundle
ExportDialog --> ResultRepository : resolve canonical artifact set

note bottom of LiveViewport
Live viewport consumes runtime snapshots only.
It must not calculate comparison deltas or
infer recommendation evidence from transient state.
end note

note bottom of ComparisonView
Comparison, recommendation, and export
are persisted-analysis workflows.
They stay disabled until the required
stored artifacts exist.
end note

note bottom of HeatmapSelector
The same selector can switch between
live overlays during playback and
persisted layers after aggregation,
but the data source is explicit.
end note

@enduml
56 changes: 56 additions & 0 deletions uml/application-analysis-workspace.puml 해설.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# SafeCrowd UML 설계 해설 - application-analysis-workspace.puml

대상 파일: `uml/application-analysis-workspace.puml`

## 문서 목적
이 문서는 SafeCrowd의 실행 후반과 결과 분석 UI를 설명한다. 핵심은 `live playback`과 `persisted artifact 기반 분석`을 한 화면 군 안에서 연결하되, 데이터 출처를 섞지 않는 것이다.

## `Run Control`
- 개요: batch 실행의 시작, 일시정지, 정지를 요청하는 패널이다.
- 목적: application이 엔진 세부 제어 대신 `ScenarioBatchRunner`를 통해 실행을 조정하게 한다.
- 유의사항: comparison이나 export 조건 판단을 여기서 맡지 않는다.

## `Batch Progress`
- 개요: 현재 run 번호, variation 식별자, 반복 실행 진행률을 보여 주는 패널이다.
- 목적: 사용자가 반복 실행과 batch 상태를 실시간으로 추적하게 한다.
- 유의사항: 결과 집계가 끝나기 전과 후의 상태를 구분해서 보여 준다.

## `Live Viewport`
- 개요: 실행 중 runtime snapshot을 재생하는 시각화 영역이다.
- 목적: 현재 움직임과 즉시적인 overlay를 확인하게 한다.
- 유의사항: persisted comparison이나 recommendation evidence를 직접 계산하지 않는다.

## `Heatmap Selector`
- 개요: heatmap과 위험 레이어의 데이터 출처와 표시 종류를 고르는 컨트롤이다.
- 목적: live overlay와 post-run heatmap을 같은 UI affordance로 연결한다.
- 유의사항: 데이터 출처가 `IRenderBridge` 기반인지 `ResultRepository` 기반인지 명확해야 한다.

## `Run Results Panel`
- 개요: 단일 실행 요약을 읽는 첫 결과 패널이다.
- 목적: run 종료 직후의 핵심 지표를 persisted 형태로 먼저 보여 준다.
- 유의사항: live 상태를 그대로 붙이는 패널이 아니라 저장된 `RunResult` 소비자다.

## `Variation Summary`
- 개요: 같은 variation에 속한 반복 실행 집계를 보여 주는 패널이다.
- 목적: deterministic single run과 repeated-run aggregate를 구분한다.
- 유의사항: `Comparison View`로 넘어가기 전에 repeated-run 맥락을 먼저 고정한다.

## `Comparison View`
- 개요: baseline과 alternative variation을 나란히 비교하는 핵심 분석 패널이다.
- 목적: `ScenarioComparison`과 `CumulativeArtifact`를 기준으로 변화량과 근거를 제시한다.
- 유의사항: ad hoc domain delta 계산기로 쓰지 않는다.

## `Recommendation Drawer`
- 개요: 추천 후보, 근거, 시나리오화 액션을 보여 주는 패널이다.
- 목적: 결과 분석 뒤에 이어지는 설명 가능한 운영 대안 검토 흐름을 만든다.
- 유의사항: 추천 입력은 persisted comparison/cumulative artifact에 한정한다.

## `Export Dialog`
- 개요: canonical 결과 번들을 외부 공유용으로 내보내는 대화상자다.
- 목적: comparison과 recommendation이 보는 같은 결과 계약을 export에도 재사용한다.
- 유의사항: 결과가 충분하지 않으면 비활성 상태를 유지한다.

## `ResultRepository` / `ResultAggregator`
- 개요: `ResultRepository`는 run/variation/comparison/cumulative artifact 저장소이고, `ResultAggregator`는 상위 결과를 생성하는 서비스다.
- 목적: 분석 패널들이 같은 persisted 결과 계약을 읽게 한다.
- 유의사항: comparison, export, recommendation의 선행 조건은 이 저장소에 결과가 존재하는지로 판단한다.
Loading
Loading