PropertyWindow에 Network 탭 시스템을 추가하고, Twin Agent의 자동 네트워크 설정 기능을 구현했습니다. 주요 기능: - PropertyWindow 탭 시스템 (PropertyTab, PropertyTabView) - Network 탭 추가 (Server Type별 동적 설정) - Button PropertyType 추가 (ButtonProperty, ButtonPropertyUI) - Label PropertyType 추가 (LabelProperty, LabelPropertyUI) - Entity Processor 패턴 (IEntityProcessor, TwinAgentAutoProcessor) - PropertyItem 이벤트 시스템 (ValueChanged, IsVisibleChanged, ValueChangedObject) - 동적 가시성 제어 (Server Type, Connection Type 변경 시) - 진행 상태 애니메이션 (타이핑 효과, 점 애니메이션, 완료 시 초록색) Server Type 구성: - Twin Agent: Auto 버튼 + 자동 진행 (Read Entity → Connection) - Octopus Hub: Connection Type (MQTT/API), Topics, URI, Period - Octopus AI: Agent Type, URI 기술 구현: - PropertyItem 양방향 바인딩 - Entity 프로세서 컨테이너 패턴 - UniTask 비동기 진행 (1~3초 무작위 대기) - 실시간 UI 갱신 (ValueChangedObject 이벤트) Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
15 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
프로젝트 개요
2026년 2월 11일 OCTOPUS DAY 전시회를 위한 디지털 트윈 시연 프로젝트입니다.
- Unity 버전: 6000.2.12f1 (Unity 6.0.2)
- 언어: C# (.NET 6.0+)
- 주요 기술: MQTT IoT 통신, 실시간 센서 데이터 시각화, 3D 디지털 트윈
개발 환경
이 프로젝트는 Unity Editor에서 실행됩니다:
- Unity Hub를 통해 Unity 6000.2.12f1 설치 필요
- IDE: JetBrains Rider 또는 Visual Studio
- Windows 빌드 타겟 (기본값: FullScreen 1920x1080@60fps)
런타임 설정 파일
Assets/StreamingAssets/ 폴더의 JSON 파일들은 런타임에 로드되며 코드 수정 없이 설정을 변경할 수 있습니다:
- MQTTConfig.json: MQTT 브로커 호스트/포트 설정
- FactoryAppConfig.json: 현대위아 시연 설정 (API 엔드포인트, 프레임레이트)
- AppConfig.json: 기본 앱 설정 (언어, 윈도우 크기)
아키텍처
멀티 테넌트 구조
3개의 독립적인 시연 모듈이 있으며, 각각 자체 Scene과 Manager를 가집니다:
-
ChunilENG (
Assets/Scripts/ChunilENG/)- 한전기술 전력 IoT 시연
- 실시간 온도계 데이터 (MQTT 구독:
DVI/HOT/+) - 주요 Manager: DataManager, ViewManager, MachineInfoItemManager
-
KEPCO (
Assets/Scripts/KEPCO/)- 한전 전력 감시 시연
- 센서/시설 3D 시각화
- 주요 Manager: FacilityManager, SensorManager, ColorPalette
-
HyundaiWIA (
Assets/Scripts/HyundaiWIA/)- 현대위아 스마트팩토리 시연
- 설비 이상 감지 시나리오 (AnomalyScenario)
- Dashboard: AMR, COBOT, ASRS, Lifter, Storage
초기화 흐름
OctopusTwinAppMain (App 진입점, Singleton)
↓ Initialized 이벤트
{Scene}Main (예: ChunilENGSceneMain, Singleton)
↓ async Init()
Building/Facility 초기화
↓ await LoadManager<T>()
Manager 로드 (DataManager, UIManager, ViewManager 등)
↓ SetupDataSetting()
MQTT 연결 및 이벤트 바인딩
모든 Manager는 Manager 베이스 클래스를 상속하며 async UniTask Init() 메서드를 구현합니다.
핵심 디자인 패턴
-
Singleton 패턴
SingletonApp<T>: 앱 전역 진입점 (OctopusTwinAppMain)SingletonScene<T>: Scene별 관리자 (ChunilENGSceneMain, KEPCOSceneMain 등)
-
Command 패턴
- 71개의 ICommand 구현체
- CameraCommand, DataCommand, ObjectCommand, UICommand 등
- UI 이벤트와 비즈니스 로직 분리
-
이벤트 기반 통신
// MQTT → DataManager → UI 체인 mqttManager.onThermostatData += dataManager.SetThermostatDataList; dataManager.onSetThermostatData += ui.SetData;
MQTT 통신
구현 위치
- 핵심 클래스:
Assets/Scripts/ChunilENG/Managements/MQTT.cs - 라이브러리: Best MQTT v3.0.4 (TCP 또는 WebSocket)
연결 구조
// 브로커 연결
host: "106.247.236.204"
port: "8901"
topics: "DVI/HOT/+" // 와일드카드 구독
// 이벤트 핸들러
OnConnected → OnMessage (JSON 파싱) → 콜백 실행
데이터 흐름
IoT 센서
↓ MQTT Publish (JSON 페이로드)
MQTT.cs OnMessage()
↓ JsonConvert.DeserializeObject<ThermostatData>()
DataManager.SetThermostatDataList()
↓ onSetThermostatData 이벤트
UI 업데이트 (ThermostatControlPanel)
비동기 처리
프로젝트 전체에서 UniTask (Cysharp)를 사용합니다 (212회 사용):
- Scene 초기화:
await building.Init() - Manager 로딩:
await LoadManager<T>() - 데이터 로드:
await UniTask.CompletedTask
주의: async void는 이벤트 핸들러에서만 사용하고, 일반 메서드는 async UniTask 사용.
3D 객체 계층
Scene
├── Building (GameObject)
│ ├── Facility (컴포넌트)
│ │ └── Sensor (Tag_Machine으로 필터링)
│ └── Floor (층별 관리)
├── Canvas
│ ├── PopupCanvas (동적 팝업)
│ ├── StaticCanvas (고정 UI)
│ └── TopMenuPanel
└── Camera
└── OrbitalController (3D 카메라 제어)
객체 초기화 패턴: Building → Floor → Machine/Thermostat/Facility → Sensor (모두 async Init)
리소스 구조
- JSON 데이터:
Assets/Resources/Data/{ChunilENG|KEPCO|HyundaiWIA}/- MachineData.json, Facility.json, SensorColorData.json 등
- Prefab:
Assets/Resources/(각 Scene Main Prefab) - UI:
Assets/Resources/UI/
JSON은 Resources.Load<TextAsset>(path).text로 로드하고 Newtonsoft.Json으로 역직렬화합니다.
주요 외부 패키지
- com.tivadar.best.http v3.0.16 (HTTP/2 지원)
- com.tivadar.best.mqtt v3.0.4 (MQTT 프로토콜)
- com.tivadar.best.websockets v3.0.7 (WebSocket)
- com.cysharp.unitask (async/await)
- com.unity.nuget.newtonsoft-json v3.2.2 (JSON 처리)
- com.unity.render-pipelines.universal v17.2.0 (URP)
Scene 정보
| Scene | 용도 |
|---|---|
| Demo.unity | 종합 데모 |
| Demo_Home.unity | 프로젝트 선택 화면 |
| Demo_시연.unity | 한전기술 시연 (MQTT 실시간 데이터) |
| HyundaiWIA.unity | 현대위아 스마트팩토리 시연 |
코드 작성 시 주의사항
-
Manager 추가 시:
Manager베이스 클래스 상속async UniTask Init()오버라이드{Scene}Main.cs의LoadManager<T>()호출 추가
-
Command 추가 시:
ICommand인터페이스 구현Execute()메서드에 로직 작성- 적절한 폴더에 배치 (CameraCommand, DataCommand 등)
-
MQTT 토픽 추가 시:
MQTT.cs의subscriptionTopics배열에 추가- 해당 토픽의 콜백 함수 등록 (
thermostatTopicTable등) - 데이터 구조체 정의 (
[Serializable]특성 필요)
-
UI 이벤트 바인딩:
SetupDataSetting()메서드에서 이벤트 체인 구성- Manager → UI 순서로 연결
UI 시스템
Canvas 계층 구조
프로젝트는 3단계 Canvas 아키텍처를 사용합니다:
Level 1: StaticCanvas (항상 표시)
├── LeftSidePanel (도구 모음)
├── TopMenuPanel (메뉴)
└── BottomToolbar (카메라 버튼, ChunilENG만)
Level 2: PopupCanvas (동적 Panel)
├── TotalProgressPanel (종합 현황)
├── MachineDashBoard (기계 대시보드)
├── FloorControlPanel (층 조절)
└── SettingPanel (설정)
Level 3: LocalPopupManager (개별 Popup)
└── PopupBase 상속 (ToastPopup 등)
기본 클래스
-
UICanvas: 모든 Canvas의 베이스
LoadPanel<T>(): Panel 로드GetPanel<T>(): Panel 획득OpenPanel<T>(): Panel 열기- CanvasPanelOpenMode: None (다중), Single (단일)
-
UIPanel: 모든 Panel의 베이스
async UniTask Init(): 비동기 초기화Open(): Panel 활성화Close(): Panel 비활성화GetElement<T>(name): 자식 컴포넌트 검색
-
PopupBase: 모든 Popup의 베이스
- 자동 PopupBlocker 연동
ClosePopup(): 팝업 닫기
-
UIManager: Canvas 관리자
LoadCanvas<T>(): Canvas 로드GetCanvas<T>(): Canvas 획득
프로젝트별 주요 UI
ChunilENG (생산 관리)
- MachineDashBoard: 기계 상세 정보
- TotalProgressPanel: 종합 진행현황 (자동 순환)
- ThermostatControlPanel: MQTT 온도계 제어
- 특징: IColorChangeBehaviour 색상 변경
KEPCO (전력 감시)
- FacilityAndSensorTypeTogglePanel: 설비/센서 목록
- TotalProgressPanel: MTR/GIS 구분 표시
- 특징: ScriptableAnimation (Echo, Scaling 등)
HyundaiWIA (스마트팩토리)
- AnomalyScenario: 이상 시나리오 관리자
- EquipmentTabController: 설비 탭 (AMR, COBOT, Lifter, Storage)
- 특징: 단계별 팝업 시나리오
Panel/Popup 생명주기
// Panel 생명주기
1. LoadPanel<T>() → Panel 검색/생성
2. await Init() → 자식 컴포넌트 캐싱
3. Open() → gameObject.SetActive(true)
4. Close() → gameObject.SetActive(false)
// Popup 생명주기
1. OnEnable() → PopupBlocker.Show()
2. 사용자 상호작용
3. ClosePopup() → gameObject.SetActive(false)
4. OnDisable() → PopupBlocker.Hide()
데이터 바인딩 패턴
// 1. 직접 바인딩
MachineDashBoard.SetDetailDashBoardData(data, machine);
// 2. Content 기반 바인딩
ProgressContent.SetProductionStatusItem(completeInfoList);
// 3. 동적 생성 바인딩
var content = Instantiate(FacilitiesContent, parent);
content.SetProductionStatusItem(group.Panels, group.Type);
UI 추가 가이드
-
Canvas 추가:
UICanvas상속{Project}UIManager.Init()에서LoadCanvas<T>()호출
-
Panel 추가:
UIPanel상속async UniTask Init()구현 (GetElement로 자식 캐싱)- Canvas의 Init()에서
LoadPanel<T>()호출
-
Popup 추가:
PopupBase상속OnEnable/OnDisable에서 PopupBlocker 자동 처리ClosePopup()메서드로 닫기
-
데이터 표시:
SetData()또는Set{DataType}()메서드 구현- 이벤트 구독:
dataManager.onSetData += panel.SetData
-
애니메이션:
- ChunilENG: Panel_Effect, IColorChangeBehaviour
- KEPCO: ScriptableAnimation (Animation_Scaling 등)
- HyundaiWIA: DOTween (RectTransform.DOAnchorPos)
공통 UI 컴포넌트
- PopupBlocker: 전역 팝업 배경 차단 (Singleton)
- ToastPopup: 자동 닫힘 알림 (displayDuration)
- UILoading: 로딩 화면 (Progress Bar, Fade)
- LanguageController: 언어 전환
- BottomToolbar: 카메라 이동 버튼
표준 UI 패턴
Panel 열기 (권장)
var uiManager = ChunilENGSceneMain.Instance.GetManager<ChunilENGUIManager>();
uiManager.GetCanvas<PopupCanvas>().OpenPanel<MachineDashBoard>(CanvasPanelOpenMode.Single);
데이터와 함께 Panel 열기
var uiManager = ChunilENGSceneMain.Instance.GetManager<ChunilENGUIManager>();
var panel = uiManager.GetCanvas<PopupCanvas>().GetPanel<MachineDashBoard>();
panel.SetDetailDashBoardData(data, machine);
uiManager.GetCanvas<PopupCanvas>().OpenPanel<MachineDashBoard>(CanvasPanelOpenMode.Single);
Panel 닫기
var panel = uiManager.GetCanvas<PopupCanvas>().GetPanel<MachineDashBoard>();
panel.Close();
Command 패턴 (UI 열기)
public class OpenMachineDashBoardCommand : ICommand
{
public void Execute(object? parameter = null)
{
var uiManager = ChunilENGSceneMain.Instance.GetManager<ChunilENGUIManager>();
uiManager.GetCanvas<PopupCanvas>().OpenPanel<MachineDashBoard>(CanvasPanelOpenMode.Single);
}
}
이벤트 구독
// SetupDataSetting 패턴
mqttManager.onThermostatData += dataManager.SetThermostatDataList;
dataManager.onSetThermostatData += panel.SetData;
Panel 초기화
public override async UniTask Init()
{
// GetElement로 UI 요소 캐싱
MachineName = GetElement<TextMeshProUGUI>(nameof(MachineName));
Button_Close = GetElement<Button>(nameof(Button_Close));
// 이벤트 리스너 등록
Button_Close.onClick.AddListener(Close);
await UniTask.CompletedTask;
}
3D 오브젝트 상호작용
오브젝트 계층 구조
Building
├── Floor (층별 관리)
│ ├── Facility (설비, KEPCO)
│ │ └── Sensor (센서)
│ ├── Machine (기계, ChunilENG)
│ └── Thermostat (온도계, ChunilENG)
주요 오브젝트 클래스
Building
GetMachines()/GetThermostats(): 오브젝트 리스트 반환SetFloor(index): 층 변경 및 가시성 제어Init(): 모든 자식 오브젝트 비동기 초기화
Facility / Machine
centerPos: 메시 중심 좌표 (카메라 포커싱용)originScale: 원본 스케일Init(): centerPos, 컴포넌트 초기화focusDistance/Azimuth/Elevation: 카메라 설정값 (Machine)
Sensor
SetSensorState(state): 상태 변경 및 색상 업데이트Active()/Inactive(): 센서 활성화/비활성화Hovering(): 호버 상태 동기화, Outline 활성화outline: Outline 컴포넌트 (외곽선 효과)
마우스 상호작용
MachineClickManager (ChunilENG)
- Raycast 기반 3D 오브젝트 클릭 감지
- 이벤트:
onLeftClickMachine,onLeftClickArea
UI 아이콘 클릭 (KEPCO)
- UI_FacilityIcon / UI_SensorIcon이 IPointerClickHandler 구현
OnPointerClick(): SelectedObjectCommand 실행OnPointerEnter/Exit(): 호버 효과 (상태 변경, Outline)
Command 패턴
SelectedObjectCommand
Execute(): ViewManager.SetTargetPosToMachine() 호출- 설비/센서 타입 판별 후 카메라 포커싱
SelectedMachineCommand
- Machine 오브젝트로 카메라 포커싱
OpenFacilityInfoPanel
- 설비 정보 패널 열기
카메라 제어
OrbitalController
- 궤도형 카메라 제어 (Orbital Pattern)
SetTargetPos(Vector3): 타겟 위치 설정AnimateToState(): DoTween 기반 부드러운 이동SetViewMode(ViewMode): PerspectiveView / TopView 전환- 입력: 좌클릭(Pan), 우클릭(Orbit), 휠(Zoom)
오브젝트 검색
Tag 기반 검색
Tag_Machine: Unity VisualScripting 태그로 머신/설비 마킹FindObjectsByType<Tag_Machine>(): 태그된 오브젝트 검색
타입별 필터링
- FacilityManager: 설비타입 필터링 ("GIS", "주변압기")
- SensorManager: SensorType별 그룹화 (PD, CB, DGA 등)
- Dictionary 기반 빠른 검색 (name/code → 오브젝트)
시각적 효과
선택/하이라이트
- Outline: 호버 시 외곽선 표시
- Color 변경: MaterialPropertyBlock으로 상태별 색상
- Animation: ScriptableAnimation (Echo, Scaling)
상태별 색상 (KEPCO)
- ColorPalette에서 SensorState별 색상 관리
- Normal(녹색), Interest(청록), Care(노랑), Anomaly(주황), Danger(빨강)
오브젝트 추가 가이드
-
새 오브젝트 타입 추가:
- Building/Facility/Machine 중 적절한 클래스 상속
async UniTask Init()구현- centerPos, originScale 초기화
-
클릭 이벤트 추가:
- ICommand 구현체 생성 (SelectedXXXCommand)
- MachineClickManager 또는 IPointerClickHandler 사용
-
오브젝트 검색:
- Tag_Machine 태그 추가
- Manager에서 FindObjectsByType으로 검색
- Dictionary에 등록 (이름/코드 → 오브젝트)
-
시각적 효과:
- Outline 컴포넌트 추가
- MaterialPropertyBlock으로 색상 변경
- ScriptableAnimation 활용
성능 최적화
- 타겟 프레임레이트: 60fps (FactoryAppConfig.json)
- LOD 사용: Sensor_LOD.cs (Level of Detail)
- DOTween 애니메이션: ScriptableAnimationManager로 최적화
- URP 렌더링: 경량화된 Universal Render Pipeline
디버깅
- 로그: Unity Console 창 확인
- MQTT 연결 상태:
MQTT.cs의OnStateChange이벤트 로그 - Manager 초기화: 각 Manager의
isInit플래그 확인 - JSON 파싱 에러: Newtonsoft.Json의 예외 메시지 확인
알려진 제약사항
- WebGL 빌드 시 MQTT는 WebSocket 모드로만 작동 (TCP 불가)
- MQTT QoS Level 3 사용 (정확히 1회 배달)
- 현재 중앙 MQTT 수신 시스템(DataRepository)은 주석 처리됨 (각 Scene별로 독립 연결)