Files
ExperionCrawler/fastTable/idea-fastTable.md

188 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# fastTable fastRecord 필요성
- 현재 데이터 저장 간격(1분) 으로는 상세한 필드 데이터의 변동을 캐치 하기 힘듬.
- 정해진 시간동안 초 단위로 데이터를 받아서 평균을 넘어서는 데이터 분류 등에 분석을 위한 데이터 재료로 사용
## Expected Fucnctional Act Sequence
- UI : 에서 최대 8개 까지 태그명을 선정 및 수집기간(시간 , 일) 선정 하고 시작하면,
- 테이블 완성() 테이블은 미리 만들어 놓은 형태여도 좋고, 컬럼의 태그명만 바꾸는 방식도 좋다
- OPC UA 서버로 부터 받는 Realtime 테이블에서 선정된 태그값을 초 단위 (사용자 지정 가능)로 데이터 수집
- 정해진 시간이 만료되면 수집동작 종료
- 수집되고 있는 또는 수집된 데이터를 realtime trend graph로 표시
- UI : 사용자 필요에 따라 전체 테이블 내용 또는 부분 시간 구간을 Excel로 Export 할수있게
- UI : 테이블 삭제 또는 데이터로 보관 가능? 하게
### Claude 가 더 추가하거나 유용한 방식이 있으면 아래에 적어주세요
---
## 추가 아이디어 (Claude 제안, 2026-04-28)
### 핵심 설계 결정
#### 1. 데이터 출처 — 별도 OPC UA Subscription 신설 권장
- 기존 `ExperionRealtimeService`의 Subscription은 SamplingInterval 500ms / PublishingInterval 1000ms로 고정
- fastRecord는 **분해능이 핵심**이므로 별도 Subscription 생성:
- SamplingInterval: 100/250/500/1000ms 중 사용자 선택
- PublishingInterval: SamplingInterval과 동일하게
- 세션 종료 시 Subscription dispose → 평소엔 부하 없음
- 대안(기존 `_pendingUpdates` ConcurrentDictionary 폴링)은 분해능 한계(500ms) 때문에 부적합
#### 2. 스토리지 — 단일 hypertable + session_id 컬럼 (Long 형태)
```
fast_session (메타)
id, name, started_at, ended_at, status, sampling_ms,
duration_sec, tag_list (jsonb), row_count, retention_days, pinned
fast_record (TimescaleDB hypertable)
session_id, recorded_at, tagname, value
→ hypertable on recorded_at, chunk_time_interval = 1 day
→ index (session_id, tagname, recorded_at)
```
- **Long 형태(태그 1행/시점)** 권장 이유: 태그 개수 가변, 태그별 NULL 처리 단순, TimescaleDB 압축 효율
- 조회 시 서버 또는 클라이언트에서 PIVOT → Wide 변환 (그래프/Excel용)
#### 3. 데이터 규모 추정
| 시나리오 | 행수 | 비고 |
|----------|------|------|
| 8태그 × 1s × 1시간 | 28,800 | 즉시 처리 |
| 8태그 × 1s × 24시간 | 691,200 | TimescaleDB 무난 |
| 8태그 × 100ms × 1시간 | 288,000 | TimescaleDB 권장 |
| 8태그 × 100ms × 24시간 | 6,912,000 | retention/압축 필수 |
- 세션당 최대 행수 가드(예: 5,000,000) → 도달 시 자동 종료 + 상태 `RowLimitReached`
#### 4. 세션 상태 머신
```
Pending → Running → Completed
↘ Cancelled (사용자 중지)
↘ Failed (OPC 연결 끊김 등)
↘ RowLimitReached
```
- 동시 Running 세션 최대 N개 제한(권장 3개) — OPC UA Subscription 부하 고려
- 앱 재기동 시 Running 세션은 `Failed` 처리(중간값 보존, 재개 X — 단순화)
### 추가 기능 제안
#### 5. 실시간 트렌드 그래프 — uPlot 권장
- **Chart.js**: 친숙하지만 10만점 초과 시 버벅임
- **uPlot**: 시계열 특화, 100만점도 부드러움. CDN 단일 파일(~50KB)
- 다운샘플링: LTTB 알고리즘으로 화면 픽셀 폭에 맞춰 축소(예: 화면 1200px → 1200점)
- 라이브 갱신: 1~2초 간격 폴링으로 새 데이터만 append
#### 6. 통계 + 이상치 분석 (사용자가 언급한 "평균을 넘어서는 데이터 분류")
- 세션 종료 후 또는 실시간 패널에 표시:
- 태그별 mean / stddev / min / max / median / p95 / p99
- **이상치 강조**: `|value - mean| > k × stddev` (k 사용자 설정, 기본 3)
- **임계값 알람**: 태그별 상/하한 설정 → 초과 구간 그래프에 색상 강조
- **변화율(slope)**: Δvalue/Δt 급변 구간 표시
- DB 부하 없이 클라이언트 JS로 계산 가능 (8태그 × ~30만점 수준)
#### 7. Excel/CSV Export — 클라이언트 사이드
- `xlsx.full.min.js`가 이미 wwwroot에 추가되어 있음 → 즉시 활용
- Wide 포맷: `recorded_at | tag1 | tag2 | ...`
- 옵션: 전체 / 그래프 현재 줌 구간만 / 시간 슬라이더로 지정한 구간
- 행수 50,000 초과 시 CSV 권장 (Excel 시트당 1,048,576행 제한 고려)
- 큰 세션은 서버에서 스트리밍 응답(`text/csv`)으로 제공하는 엔드포인트 추가 권장
#### 8. 보관/정리 정책
- 세션별 `retention_days` 필드 (기본 30, 무한=NULL)
- `pinned` 플래그(불 표시) → 자동 정리 제외
- `ExperionFastCleanupService` BackgroundService — 일 1회 새벽 만료 세션 + 데이터 삭제
- TimescaleDB `drop_chunks` 활용 가능
#### 9. 사용성 개선
- **세션 템플릿**: 자주 쓰는 태그 조합 + 설정 저장 → 원클릭 시작
- **진행률 표시**: `(현재행수 / 예상행수) × 100`, 남은 시간 추정
- **다중 태그 단위 그룹**: 같은 단위 태그를 같은 Y축으로 묶고 다른 단위는 보조 Y축
- **태그별 색상 자동 할당** + 토글로 표시/숨김
- **그래프 위에 마우스 호버** → 모든 태그의 해당 시점 값 툴팁
- **시간 동기화 표시**: 서버 시각(UTC) ↔ 브라우저 KST 변환 (이력 조회와 동일 패턴)
#### 10. Subscription 동시성 / 안전성
- 같은 nodeId를 여러 fast 세션이 동시 구독해도 OPC SDK가 처리 — 단, 각 Subscription 별도 비용
- 세션 시작 시 노드 유효성 사전 검증(`Read` 단발) → bad이면 시작 거부
- OPC 연결 끊김 시 → 세션 자동 `Failed` + 그때까지 데이터 보존
- 메모리 보호: 콜백마다 직접 INSERT가 아니라 기존 패턴(ConcurrentDictionary 버퍼 + 1~2초 배치 INSERT)
#### 11. 라이브 vs 완료 화면 통합
- 동일 화면에서 상태에 따라 컨트롤만 다르게:
- Running: [중지] 버튼, 라이브 갱신 ON, 진행률
- Completed: [Excel] [CSV] [삭제] [고정/해제] 버튼, 통계 패널, 줌/팬
---
## 구현 플랜
### 전체 구조
```
[OPC UA Server]
├──(기존) Subscription 1 → realtime_table → history_table (60s)
└──(신규) Subscription per fastSession
├── 콜백 → ConcurrentDictionary 버퍼
└── 2s 배치 → fast_record (TimescaleDB hypertable)
```
### Task A — DB 스키마 + 엔티티
| 파일 | 작업 |
|------|------|
| `ExperionEntities.cs` | `FastSession`, `FastRecord` 엔티티 추가 |
| `ExperionDbContext.cs` | `DbSet<FastSession>`, `DbSet<FastRecord>`, 테이블 DDL, hypertable 생성(`SELECT create_hypertable('fast_record', 'recorded_at', if_not_exists => TRUE)`) |
| `IExperionServices.cs` | `IExperionFastService` 인터페이스 + `FastSessionStatus`/`FastSessionInfo`/`FastQueryResult` record |
### Task B — FastService (백그라운드 + 컨트롤러)
| 파일 | 작업 |
|------|------|
| `Infrastructure/OpcUa/ExperionFastService.cs` (신규) | `IHostedService` + `IExperionFastService` 구현. 세션별 Subscription 관리, 콜백 → 버퍼, FlushLoop 2s, 자동 종료(만료/행수초과/외부중지) |
| `ExperionDbContext.cs` | `BatchInsertFastRecordsAsync(IEnumerable<FastRecord>)`, `GetFastSessionsAsync()`, `GetFastRecordsAsync(sessionId, from?, to?)`, `DeleteFastSessionAsync(sessionId)` 등 |
| `Web/Controllers/ExperionControllers.cs` | `ExperionFastController` 추가:<br>`POST /api/fast/start` (tags, samplingMs, durationSec, name, retentionDays)<br>`POST /api/fast/{id}/stop`<br>`GET /api/fast/sessions`<br>`GET /api/fast/{id}` (세션 메타)<br>`GET /api/fast/{id}/records?from&to&format=long\|wide`<br>`GET /api/fast/{id}/csv` (스트리밍)<br>`DELETE /api/fast/{id}`<br>`POST /api/fast/{id}/pin` |
| `Web/Program.cs` | `ExperionFastService` Singleton + HostedService 등록 |
| `Web/appsettings.json` | `Fast` 섹션 — `MaxConcurrentSessions:3`, `MaxRowsPerSession:5000000`, `FlushIntervalMs:2000` |
### Task C — UI: 09 fastRecord 탭
| 파일 | 작업 |
|------|------|
| `wwwroot/index.html` | 사이드바 09번 `pane-fast` 섹션 추가:<br>- 좌측: 세션 목록(상태/이름/태그수/시작시각/진행률)<br>- 우측 상단: [신규 세션] 버튼 → 모달(태그 선택 8개, 샘플링 select, 기간 select, 이름, retention)<br>- 우측: 선택 세션의 트렌드 그래프 + 통계 + 이상치 패널 + Export 버튼 |
| `wwwroot/lib/uPlot.iife.min.js` | uPlot 라이브러리 추가 (CDN에서 다운로드한 파일) |
| `wwwroot/lib/uPlot.min.css` | uPlot 스타일 |
| `wwwroot/js/app.js` | `fastSessionsLoad()`, `fastStart()`, `fastStop(id)`, `fastDelete(id)`, `fastPin(id)`, `fastSelect(id)`, `fastRenderChart()`, `fastRenderStats()`, `fastExportXlsx()`, `fastExportCsv()`, `fastLivePollStart/Stop` |
| `wwwroot/css/style.css` | `.fast-session-list`, `.fast-progress`, `.fast-stats-grid`, `.fast-outlier`, 모달 스타일 |
### Task D — 정리/보관 백그라운드
| 파일 | 작업 |
|------|------|
| `Infrastructure/OpcUa/ExperionFastCleanupService.cs` (신규) | `BackgroundService` — 일 1회(03:00) 만료된 세션 + record 삭제. `pinned=true` 제외 |
| `Web/Program.cs` | HostedService 등록 |
### Task E — 안정성 / QA
- 노드 유효성 사전 검증(시작 시 Read 1회) — bad이면 400 반환
- 동시 세션 수 제한 검사 — 초과 시 409
- 세션 시작 시 OPC UA 연결 상태 확인 — 연결 안되어 있으면 400
- 앱 종료 시 Running 세션 graceful 마무리(현재 버퍼 flush 후 status=`Cancelled`)
- 앱 시작 시 Running 상태 잔류 세션 → `Failed` 마킹
- 단위/통합 테스트는 기존 패턴 따름(현 프로젝트엔 테스트 없음 — 수동 QA 시나리오 문서화)
### Task F — 문서화
- `CLAUDE.md`에 작업 이력 항목 추가
- `appsettings.json` 신규 키 설명
---
## 우선순위 추천
1. **MVP**: Task A + B(start/stop/sessions/records 엔드포인트 4개) + C(목록/시작/중지/단순 그래프) — 핵심 가치 검증
2. **분석**: 통계 패널 + 이상치 강조 + 임계값
3. **Export**: xlsx + csv 스트리밍
4. **운영**: Task D 정리, retention/pinned, 동시성 제한, 진행률
5. **고급**: 템플릿, 다중 Y축, LTTB 다운샘플링 최적화