# 일일/월간/연간 생산 레포트 구현 계획

## 현황 파악

| 항목 | 내용 |
|------|------|
| DB | PostgreSQL + TimescaleDB, `history_table` |
| 데이터 주기 | 1분 스냅샷 |
| 현재 태그 수 | 1,807개 |
| 이용 가능 태그 종류 | FI, FICQ, FIC, LI, LICA, TI, TICA, PI, PIC 등 |

---

## ⚠️ 구현 전 반드시 확인해야 할 사항 (사용자 결정 필요)

### 1. 생산량 정의

어떤 태그가 "생산량"을 나타내는지 사용자가 지정해야 한다.

| 질문 | 예시 |
|------|------|
| 주요 생산 제품은? | 예: 에틸렌, LPG, 나프타 등 |
| 각 제품의 생산량 태그는? | 예: `ficq-6101.pv` = 6호기 에틸렌 출하 유량 |
| 유량의 공학 단위는? | 예: ton/hr, Nm³/hr, kg/hr |
| 적산 방법은? | 순간유량(PV) × 1분 → 시간적산, 또는 별도 적산 태그 존재 여부 |
| 조업시간 판정 기준 태그는? | 예: 특정 태그값 > 0 이면 운전 중 |

### 2. 레포트 항목 정의

사용자가 원하는 레포트 항목을 구체화해야 한다. 아래는 일반적인 공정 플랜트 기준 초안.

---

## 레포트 구조 설계 (초안)

### 일일 레포트 (1일치)

```
┌────────────────────────────────────────────────────────┐
│  ○○플랜트 일일 생산 레포트  2026-05-01 (목)            │
├──────────────┬─────────────────────────────────────────┤
│ 1. 생산 실적  │ 제품별 일일 생산량 (ton 또는 Nm³)       │
│              │  - 각 FICQ 태그 적산값                  │
│              │  - 전일 대비 증감                        │
│              │  - 월 누계                               │
├──────────────┼─────────────────────────────────────────┤
│ 2. 조업 현황 │ 운전시간 (분), 가동률 (%)                │
│              │ 트러블/정지 횟수                         │
├──────────────┼─────────────────────────────────────────┤
│ 3. 주요 공정 │ 핵심 온도/압력/레벨 태그 평균·최대·최소  │
│    조건       │ (TI, PI, LI 대표 태그 선정)             │
├──────────────┼─────────────────────────────────────────┤
│ 4. 품질 지표 │ 해당 태그 존재 시 추가                   │
└──────────────┴─────────────────────────────────────────┘
```

### 월간 레포트 (1개월치 — 일별 집계)

```
┌────────────────────────────────────────────────────────┐
│  ○○플랜트 월간 생산 레포트  2026년 5월                  │
├──────────────┬─────────────────────────────────────────┤
│ 1. 월간 생산  │ 제품별 월 총 생산량                     │
│    실적       │ 일별 생산량 추이 (테이블 + 차트)         │
│              │ 최대/최소 생산일                         │
├──────────────┼─────────────────────────────────────────┤
│ 2. 가동률    │ 월간 가동 시간 / 목표 시간               │
│              │ 일별 가동률 테이블                       │
├──────────────┼─────────────────────────────────────────┤
│ 3. 공정 조건 │ 월 평균·최대·최소 (온도, 압력, 레벨)     │
│    통계       │ 일별 평균 추이 테이블                   │
└──────────────┴─────────────────────────────────────────┘
```

### 연간 레포트 (1년치 — 월별 집계)

```
┌────────────────────────────────────────────────────────┐
│  ○○플랜트 연간 생산 레포트  2026년                      │
├──────────────┬─────────────────────────────────────────┤
│ 1. 월별 생산  │ 제품별 월별 생산량 (1~12월 테이블)       │
│    실적       │ 연 총 생산량                             │
├──────────────┼─────────────────────────────────────────┤
│ 2. 월별 가동률│ 월별 가동시간 / 가동률                   │
├──────────────┼─────────────────────────────────────────┤
│ 3. 공정 조건  │ 월별 평균 공정 조건 추이                 │
│    월별 통계  │                                         │
└──────────────┴─────────────────────────────────────────┘
```

---

## 핵심 SQL 설계

### 일일 유량 적산 (1분 데이터 → 일일 합계)

```sql
-- FICQ 태그 일일 적산값 (단위: 공학단위/분 × 1440분)
-- 순간유량이 ton/hr 단위일 경우 × (1/60) = ton/분
SELECT
    recorded_at::date                   AS day,
    tagname,
    SUM(value::double precision / 60.0) AS daily_total,   -- ton/hr → ton
    COUNT(*)                            AS data_points,
    ROUND(COUNT(*) / 1440.0 * 100, 1)  AS data_rate_pct  -- 데이터 충족률
FROM history_table
WHERE tagname IN ('ficq-6101.pv', 'ficq-6113.pv', ...)  -- 생산 태그 목록
  AND recorded_at >= '2026-05-01+09'
  AND recorded_at <  '2026-06-01+09'
GROUP BY day, tagname
ORDER BY day, tagname;
```

### 공정 조건 일별 통계

```sql
SELECT
    recorded_at::date               AS day,
    tagname,
    AVG(value::double precision)    AS avg_val,
    MAX(value::double precision)    AS max_val,
    MIN(value::double precision)    AS min_val,
    STDDEV(value::double precision) AS stddev_val
FROM history_table
WHERE tagname IN ('ti-6111a.pv', 'ti-6211a.pv', ...)   -- 대표 온도/압력 태그
  AND recorded_at >= '2026-05-01+09'
  AND recorded_at <  '2026-06-01+09'
GROUP BY day, tagname
ORDER BY day, tagname;
```

### 조업시간 계산 (일별 가동분 집계)

```sql
-- 특정 태그(기준 태그)의 값이 0 초과인 분의 수 = 가동 분
SELECT
    recorded_at::date            AS day,
    COUNT(*) FILTER (
        WHERE value::double precision > 0
    )                            AS running_minutes,
    COUNT(*)                     AS total_minutes,
    ROUND(
        COUNT(*) FILTER (WHERE value::double precision > 0)
        / COUNT(*)::numeric * 100, 1
    )                            AS availability_pct
FROM history_table
WHERE tagname = 'ficq-6101.pv'   -- 가동 판정 기준 태그
  AND recorded_at >= '2026-05-01+09'
  AND recorded_at <  '2026-06-01+09'
GROUP BY day
ORDER BY day;
```

### 월별 집계 (연간 레포트용)

```sql
SELECT
    DATE_TRUNC('month', recorded_at AT TIME ZONE 'Asia/Seoul') AS month,
    tagname,
    SUM(value::double precision / 60.0)  AS monthly_total,
    AVG(value::double precision)         AS avg_val
FROM history_table
WHERE tagname IN ('ficq-6101.pv', ...)
  AND recorded_at >= '2026-01-01+09'
  AND recorded_at <  '2027-01-01+09'
GROUP BY month, tagname
ORDER BY month, tagname;
```

---

## 구현 구성요소

### Backend (C# ASP.NET Core)

#### 새 인터페이스 추가 (`IExperionServices.cs`)

```csharp
// 레포트 서비스 인터페이스
public interface IProductionReportService
{
    Task<DailyReportResult>   GetDailyReportAsync(DateOnly date, ReportConfig config);
    Task<MonthlyReportResult> GetMonthlyReportAsync(int year, int month, ReportConfig config);
    Task<AnnualReportResult>  GetAnnualReportAsync(int year, ReportConfig config);
}

// 레포트 설정 (사용자 정의 태그 목록)
public record ReportConfig(
    List<string> ProductionTags,    // 생산량 적산 태그
    List<string> ConditionTags,     // 공정 조건 태그 (온도/압력/레벨)
    string       AvailabilityTag,   // 가동 판정 기준 태그
    string       FlowUnit,          // 유량 단위 (ton/hr, Nm³/hr 등)
    double       FlowConvFactor     // 단위 환산 계수 (예: 1/60 for ton/hr → ton/min)
);

// 결과 레코드
public record DailyProductionRow(string TagName, double DailyTotal, int DataPoints, double DataRatePct);
public record ConditionStatRow(string TagName, double Avg, double Max, double Min, double Stddev);
public record DailyReportResult(
    DateOnly Date,
    List<DailyProductionRow> Production,
    List<ConditionStatRow>   Conditions,
    int RunningMinutes, int TotalMinutes, double AvailabilityPct
);
// Monthly/Annual 유사 구조
```

#### 새 컨트롤러 (`ExperionControllers.cs`)

```
GET /api/report/daily?date=2026-05-01
GET /api/report/monthly?year=2026&month=5
GET /api/report/annual?year=2026
GET /api/report/config            ← 현재 레포트 설정 조회
POST /api/report/config           ← 레포트 설정 저장 (태그 목록 등)
```

#### 레포트 설정 저장

`report_config.json` 파일로 서버 디렉토리에 저장 (appsettings 또는 별도 JSON).

---

### Frontend (HTML/JS)

#### 새 탭 추가 (`index.html`)

사이드바에 `09 생산레포트` 탭 추가, `#pane-report` 섹션.

#### UI 구성

```
┌─────────────────────────────────────────────┐
│ [일일] [월간] [연간]  ← 레포트 유형 선택     │
│ 날짜/월/년 선택기                            │
│ [조회]  [Excel 다운로드]  [PDF 인쇄]         │
├─────────────────────────────────────────────┤
│                                             │
│  레포트 본문 렌더링 영역                     │
│  (HTML 테이블 기반, 인쇄 스타일 적용)        │
│                                             │
└─────────────────────────────────────────────┘
```

#### JS 함수 목록

```javascript
rptLoadDaily(date)       // 일일 레포트 로드
rptLoadMonthly(y, m)     // 월간 레포트 로드
rptLoadAnnual(y)         // 연간 레포트 로드
rptRender(data, type)    // 레포트 HTML 렌더링
rptExportExcel()         // SheetJS로 Excel 출력 (기존 t2sExportExcel 패턴 재사용)
rptPrint()               // window.print() — 인쇄/PDF 저장
rptSaveConfig(config)    // 태그 설정 저장
```

---

### 레포트 설정 화면 (태그 관리)

레포트 생성 전에 **어떤 태그가 무슨 의미인지** 등록해야 한다.

```
생산량 태그 등록:
  태그명          설명          단위     환산계수
  ficq-6101.pv   6호기 원료 유량  ton/hr   0.01667
  ficq-6113.pv   6호기 제품 유량  ton/hr   0.01667
  ...

공정 조건 태그 등록:
  태그명          설명          단위
  ti-6111a.pv    6호기 반응온도   ℃
  pi-6101.pv     6호기 반응압력   kPa
  li-6111.pv     6호기 레벨      %
  ...

가동 판정 기준 태그: ficq-6101.pv  (값 > [0] 이면 운전)
```

이 설정을 `report_config.json`으로 저장하고 API에서 읽어 SQL을 동적 생성.

---

## 수정/추가 파일 목록

| 파일 | 작업 |
|------|------|
| `src/Core/Application/Interfaces/IExperionServices.cs` | `IProductionReportService`, `ReportConfig`, 결과 record 추가 |
| `src/Infrastructure/Database/ExperionDbContext.cs` | `ProductionReportService` 구현 (일별/월별/연간 SQL) |
| `src/Web/Controllers/ExperionControllers.cs` | `ProductionReportController` 추가 (5개 엔드포인트) |
| `src/Web/Program.cs` | `ProductionReportService` DI 등록 |
| `src/Web/wwwroot/index.html` | `09 생산레포트` 탭 + `#pane-report` 섹션 추가 |
| `src/Web/wwwroot/js/app.js` | `rptLoad*`, `rptRender`, `rptExportExcel`, `rptPrint` 구현 |
| `src/Web/wwwroot/css/style.css` | 레포트 전용 스타일 + `@media print` 인쇄 스타일 |
| `src/Web/report_config.json` | 생산 태그 설정 파일 (신규) |

서버 코드(Python MCP) 변경 없음.

---

## 구현 순서 권장

```
1단계 (필수 선행): 사용자가 생산 태그 목록 및 단위 확정
    ↓
2단계: report_config.json 스키마 설계 + ReportConfig record
    ↓
3단계: DB 쿼리 레이어 (ProductionReportService) — 일별 집계부터
    ↓
4단계: API 엔드포인트 (컨트롤러)
    ↓
5단계: 프론트엔드 UI + 테이블 렌더링
    ↓
6단계: Excel export + 인쇄 스타일
    ↓
7단계: 월간 → 연간으로 확장
```

---

## 주의 사항

- **KST 기준 집계**: `recorded_at`은 UTC 저장이므로 일별 집계 시 `AT TIME ZONE 'Asia/Seoul'` 또는 `+ INTERVAL '9 hours'` 적용 필수. 그렇지 않으면 UTC 기준으로 집계되어 날짜가 달라짐.
- **유량 적산 단위**: `ficq-*.pv` 태그가 `ton/hr` 단위이면 1분 적산 시 `× (1/60)`. 단위 확인 필수.
- **결측 데이터 처리**: 데이터 충족률(`data_rate_pct`)이 낮은 날은 레포트에 `*` 표시 권장.
- **과거 데이터 부재**: 현재 history_table은 2026-04-22부터만 존재. 5월 데이터는 5월 이후 수집 시 자동 반영됨.
- **TimescaleDB 활용**: `time_bucket()` 또는 `DATE_TRUNC()`로 일별/월별 집계 — `time_bucket_gapfill()`로 결측 구간 0 채움 가능.
