# 포인트빌더 개선 방안

## 1. 현재 문제점

### 1.1 구조적 문제

`node_map_master` 테이블의 level 3 레코드 구조:

| name | node_id | data_type | level |
|------|---------|-----------|-------|
| `pv` | `ns=1;s=sinamserver:ficq-2113.pv` | `Double` | 3 |
| `op` | `ns=1;s=sinamserver:ficq-2113.op` | `Double` | 3 |
| `sp` | `ns=1;s=sinamserver:ficq-2113.sp` | `Double` | 3 |
| `md` | `ns=1;s=sinamserver:ficq-2113.md` | `i=7594` | 3 |
| `pv` | `ns=1;s=sinamserver:p-602.pv` | `i=7594` | 3 |
| `op` | `ns=1;s=sinamserver:xv-402.op` | `i=7594` | 3 |

- `name`은 속성명만 (`pv`, `op`, `sp`, `md` 등) — 태그명 없음
- 태그명은 `node_id` 안에 포함됨
- **521,958개**의 level 3 레코드가 존재
- 현재 드롭다운이 `name` 전체를 로드 → `pv` 선택 시 **모든** 컨트롤러의 pv全选됨

### 1.2 속성별 데이터 타입 분포

| 속성 | data_type | 설명 |
|------|-----------|------|
| `pv` | Double / i=7594 | 아날로그/디지털 |
| `op` | Double / i=7594 | 아날로그/디지털 |
| `sp` | Double / Boolean | 아날로그/디지털 |
| `md` | i=7594 | 모드 표시 (StatusCode) |
| `mode` | i=7594 | 모드 (md와 중복 → 제외) |
| `a1~a4` | Boolean / Double | Aux parameter (Alarm 아님 → 제외) |
| `qv.value` | Double | 품질 값 |

### 1.3 태그명 규칙의 다양성

- 회사마다, 프로젝트마다 태그명 규칙이 다름
- 정형화된 드롭다운/조건으로는 모든 케이스 커버 불가
- 사용자 자유도가 필수

---

## 2. 개선 방안

### 2.1 UI 구성

```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 조건으로 테이블 작성                                                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│ ┌─ 컨트롤러 포인트 #1 ────────────────────────────────────────────────────────┐ │
│ │ 태그명 패턴 (쉼표 구분, OR)                                                  │ │
│ │ [ 예) %fic%, %lic%, %tic%, %pic%                                ]           │ │
│ │ ☑ pv  ☑ op  ☑ sp  ☑ md    사용자 속성: [        ] [        ]               │ │
│ │ 데이터 타입: [Double ▼]                                                   │ │
│ └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│ ┌─ 아날로그 모니터링 포인트 #2 ───────────────────────────────────────────────┐ │
│ │ 태그명 패턴 (쉼표 구분, OR)                                                  │ │
│ │ [ 예) %fi%, %ti%, %li%, %pia%                                 ]           │ │
│ │ ☑ pv  ☐ op  ☐ sp  ☐ md    사용자 속성: [        ] [        ]               │ │
│ │ 데이터 타입: [Double ▼]                                                   │ │
│ └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│ ┌─ 디지털 포인트 #1 ──────────────────────────────────────────────────────────┐ │
│ │ 태그명 패턴 (쉼표 구분, OR)                                                  │ │
│ │ [ 예) %p-%, %xv-%, %vp-%                                  ]           │ │
│ │ ☑ pv  ☑ op  ☑ sp  ☑ md    사용자 속성: [        ] [        ]               │ │
│ │ 데이터 타입: [i=7594 ▼]                                                  │ │
│ └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│ ┌─ 디지털 포인트 #2 ──────────────────────────────────────────────────────────┐ │
│ │ 태그명 패턴 (쉼표 구분, OR)                                                  │ │
│ │ [                                                        ]           │ │
│ │ ☑ pv  ☑ op  ☑ sp  ☑ md    사용자 속성: [        ] [        ]               │ │
│ │ 데이터 타입: [i=7594 ▼]                                                  │ │
│ └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│ ┌─ 사용자 정의 ───────────────────────────────────────────────────────────────┐ │
│ │ 태그명 패턴 (쉼표 구분, OR)                                                  │ │
│ │ [ 예) %ficq-6113%, %ti-6101%                                 ]           │ │
│ │ ☑ pv  ☑ op  ☑ sp  ☑ md    사용자 속성: [        ] [        ]               │ │
│ │ 데이터 타입: [전체 ▼]                                                    │ │
│ └────────────────────────────────────────────────────────────────────────────────┘ │
│                                                                                 │
│              [🔨 테이블 작성하기]  [📋 테이블 조회]                                │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
```

### 2.2 각 그룹의 동작

**태그명 패턴 입력:**
- 쉼표(`,`)로 구분하여 여러 패턴 입력
- 각 패턴은 `node_id`의 태그명 부분에 LIKE 매칭
- `%` 자동 추가 (선택): 사용자가 `%fic%` 입력 시 `%fic%`로, `fic` 입력 시 `%fic%`로 변환
- 예: `fic, tic, lica` → `%fic%` OR `%tic%` OR `%lica%`

**속성 체크박스:**
- `pv`, `op`, `sp`, `md` 기본 체크박스
- 오른쪽 작은 입력창 2개: 사용자 정의 속성 (예: `qv.value`, `tagname`)
- 체크된 속성 + 입력된 사용자 속성이 AND 조건으로 필터

**데이터 타입 풀다운:**
- `전체` (필터 없음)
- `Double` (아날로그)
- `i=7594` (디지털, StatusCode)
- `Boolean`
- `String`
- `Int16`, `Int32`, `UInt16`, `UInt32`
- `Float`, `DateTime`

### 2.3 백엔드 쿼리 로직

각 그룹별로 독립적인 쿼리를 생성하고 UNION DISTINCT:

```sql
-- 그룹 1: 컨트롤러 포인트
SELECT * FROM node_map_master WHERE level = 3
  AND (node_id LIKE '%fic%' OR node_id LIKE '%tic%' OR node_id LIKE '%lica%')
  AND name IN ('pv', 'op', 'sp', 'md')
  AND data_type = 'Double'

UNION

-- 그룹 2: 아날로그 모니터링
SELECT * FROM node_map_master WHERE level = 3
  AND (node_id LIKE '%fi%' OR node_id LIKE '%ti%' OR node_id LIKE '%li%')
  AND name IN ('pv')
  AND data_type = 'Double'

UNION

-- 그룹 3: 디지털 포인트 #1
SELECT * FROM node_map_master WHERE level = 3
  AND (node_id LIKE '%p-%' OR node_id LIKE '%xv-%' OR node_id LIKE '%vp-%')
  AND name IN ('pv', 'op', 'sp', 'md')
  AND data_type = 'i=7594'

UNION

-- 그룹 4: 디지털 포인트 #2
-- (그룹 3과 동일 구조, 다른 패턴)

UNION

-- 그룹 5: 사용자 정의
SELECT * FROM node_map_master WHERE level = 3
  AND (node_id LIKE '%custom1%' OR node_id LIKE '%custom2%')
  AND name IN ('pv', 'op', 'sp', 'md', 'customAttr1', 'customAttr2')
  -- data_type 필터 없음 (전체)
```

### 2.4 DTO 구조

```csharp
public class PointBuilderGroupDto
{
    public List<string> TagPatterns { get; set; } = new();  // 쉼표 구분 → 분할
    public List<string> Attributes  { get; set; } = new();  // 체크박스 + 사용자 속성
    public string?      DataType    { get; set; }           // null = 전체
}

public class PointBuilderBuildDto
{
    public PointBuilderGroupDto Controller1   { get; set; } = new();
    public PointBuilderGroupDto AnalogMonitor1 { get; set; } = new();
    public PointBuilderGroupDto Digital1      { get; set; } = new();
    public PointBuilderGroupDto Digital2      { get; set; } = new();
    public PointBuilderGroupDto Custom        { get; set; } = new();
}
```

---

## 3. 추가 제안

### 3.1 미리보기 기능

테이블 작성 전에 조건에 맞는 레코드 수와 샘플을 표시:

```
[🔍 미리보기] → "조건에 맞는 레코드: 1,247개 (샘플: ficq-2113.pv, ficq-2113.op, ...)"
```

### 3.2 그룹 활성화 토글

각 그룹에 체크박스 추가하여 비활성화 가능한 그룹 표시:

```
☑ 컨트롤러 포인트 #1
☐ 아날로그 모니터링 포인트 #2  ← 비활성화 (쿼리에서 제외)
```

### 3.3 최근 조건 저장/로드

사용자가 자주 사용하는 조건 조합을 로컬 스토리지에 저장:

```
[💾 조건 저장] [📂 조건 로드] [삭제]
```

### 3.4 태그명 패턴 자동완성

level 2 name (`ficq-2113`, `ficq-2114` 등)을 기반으로 자동완성 제공:

```
사용자 입력: "fic" → 제안: fic, ficq-111, ficq-113, ficq-122, ficq-124, ...
```

### 3.5 중복 제거

UNION으로 결합 시 중복 레코드 자동 제거 (UNION DISTINCT).

---

## 4. 변경 범위

| 파일 | 변경 내용 |
|------|----------|
| `wwwroot/index.html` | 포인트빌더 pane 전면 재구성 |
| `wwwroot/js/app.js` | `pbLoad()` 제거, `pbBuild()` 그룹 기반 전송 |
| `wwwroot/css/style.css` | 그룹 카드, 체크박스, 입력창 스타일 |
| `Core/DTOs/ExperionDtos.cs` | `PointBuilderGroupDto`, `PointBuilderBuildDto` 재정의 |
| `Core/Interfaces/IExperionServices.cs` | `BuildRealtimeTableAsync` 시그니처 변경 |
| `Infrastructure/Database/ExperionDbContext.cs` | UNION 기반 쿼리 로직 |
| `Web/Controllers/ExperionControllers.cs` | DTO 매핑 |

---

## 5. 데이터 타입 참고

| data_type | 설명 | 포인트 유형 |
|-----------|------|------------|
| `Double` | 부동소수점 | 아날로그 (pv/op/sp) |
| `i=7594` | StatusCode | 디지털 (pv/op/sp/md) |
| `Boolean` | 참/거짓 | sp (디지털), a1~a4 |
| `String` | 문자열 | desc, name, area |
| `Int16` | 정수 16bit | eulo, euhi, pvperiod |
| `Int32` | 정수 32bit | numberofparents |
| `DateTime` | 일시 | lastscannedtime |
