> For the complete documentation index, see [llms.txt](https://kopens.gitbook.io/plantpulse-platform/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kopens.gitbook.io/plantpulse-platform/user/cep.md).

# CEP (복합 이벤트 처리)

## 목차

* [개요](#overview)
* [화면 구성 — Automation 그룹의 EQL 화면](#layout)
* [쿼리 화면 (`/query/index`)](#query)
  * [상단 — 쿼리 저장·이력](#query-top)
  * [EQL 입력 패널](#query-input)
  * [실행 결과 패널](#query-result)
* [스테이트먼트 화면 (`/statement/index`)](#statement)
  * [상단 검색·필터](#statement-search)
  * [목록 테이블](#statement-list)
* [트리거 화면 참고](#trigger)
* [활용 시나리오](#use-cases)
* [자주 묻는 질문](#faq)
* [관련 화면](#related)

***

## 개요 <a href="#overview" id="overview"></a>

CEP(Complex Event Processing) 기능은 실시간 이벤트 스트림에서 의미 있는 패턴을 찾아내어 즉시 액션(저장·발행·알람)을 수행하는 룰 엔진입니다. 현재 좌측 메뉴에서는 **Automation** 그룹의 **EQL 쿼리**와 **스테이트먼트**로 노출됩니다.

| 하위 메뉴      | 내부 URL             | 용도                                                     |
| ---------- | ------------------ | ------------------------------------------------------ |
| **EQL 쿼리** | `/query/index`     | EQL(Event Query Language) 임시 쿼리를 실시간 스트림에 던져 결과를 즉시 보기 |
| **스테이트먼트** | `/statement/index` | 자산 단위 도메인 룰(상태/집계/이벤트/명령) 조회                           |

CEP 룰의 결과는 [플로우](/plantpulse-platform/user/flow.md) 의 자산 트리거(`flow_on_asset_event` 등)로도 자동 연결됩니다.

***

## 화면 구성 — Automation 그룹의 EQL 화면 <a href="#layout" id="layout"></a>

| 화면         | 주된 사용자 | 한줄 요약                           |
| ---------- | ------ | ------------------------------- |
| **쿼리**     | 운영 분석가 | EQL 한 줄을 즉시 실행해 실시간 스트림에서 결과 보기 |
| **스테이트먼트** | 운영자    | 자산에 등록된 도메인 룰의 상태와 실행 통계 조회     |

***

## 쿼리 화면 (`/query/index`) <a href="#query" id="query"></a>

EQL 임시 쿼리를 작성하고 ▶ 버튼으로 즉시 실행해 실시간 스트림에서 결과를 보는 화면입니다.

### 상단 — 쿼리 저장·이력 <a href="#query-top" id="query-top"></a>

```
🔍 쿼리              [💾 이 쿼리를 저장] [↩ 마지막 쿼리] [▼ 이력 ▼]
```

| 버튼           | 동작                                                        |
| ------------ | --------------------------------------------------------- |
| **이 쿼리를 저장** | `saveQueryHistory()` — 현재 EQL 을 즐겨찾기 저장                   |
| **마지막 쿼리**   | `loadLastQuery()` — 가장 최근 실행 쿼리 불러오기                      |
| **이력 드롭다운**  | `loadQueryHistory()` — 저장한 쿼리 이력을 드롭다운으로 표시(최대 400px 스크롤) |

상단 또는 좌측 메뉴 EQL 입력창에서 작성한 쿼리는 이 화면으로 자동 전달되어 실행할 수 있습니다.

### EQL 입력 패널 <a href="#query-input" id="query-input"></a>

검은 보더의 큰 입력 박스(`textarea[name=epl]`, 높이 8행) + 우상단 두 개 아이콘.

| 아이콘        | 동작                                                                                     |
| ---------- | -------------------------------------------------------------------------------------- |
| ▶ (재생, 흰색) | `runQuery()` — EQL 실행                                                                  |
| ⚙ (톱니, 회색) | `resultConfig()` — 결과 표시 옵션 다이얼로그 (display\_count·expire\_time·result\_format·차트 옵션 등) |

### 실행 결과 패널 <a href="#query-result" id="query-result"></a>

결과 패널의 헤더에는 다음이 표시됩니다.

| 항목 | 표시                                                |
| -- | ------------------------------------------------- |
| 좌측 | ⚪ "쿼리 실행 결과" + (저장된 쿼리면) `[배치시간=..., EQL ID=...]` |
| 우측 | ▶ **시작** / ⏸ **중지** 버튼 (실시간 결과 수신 일시정지)           |

#### 결과 차트 (`query_result_chart`, 160px)

* 우상단에 입력 카운트(`input_count_cur`) 와 분당 입력 수(`input_count_min`) 표시
* 결과 데이터가 들어오기 시작하면 차트가 채워짐. 실시간 라인 차트 형태
* 데이터 도착 전에는 "차트 데이터 수신 대기중..." (회전 아이콘)

#### 결과 테이블 (`query_result_table`)

| 컬럼        | 설명                  |
| --------- | ------------------- |
| **#**     | 행 번호                |
| **출력 시간** | EQL이 결과를 emit 한 시각  |
| **행수**    | 한 emit 의 행 수        |
| **데이터**   | JSON 또는 표 형태의 결과 본문 |

> 실행을 중지하지 않으면 결과 테이블이 계속 누적됩니다. **중지** 버튼을 누르거나 새 쿼리를 실행하면 누적이 멈춥니다.

***

## 트리거 화면 참고 <a href="#trigger" id="trigger"></a>

별도 **트리거** 메뉴는 현재 통합 사이드바에서 비활성화되어 있습니다. 기존 `/trigger/index` 화면이나 내부 API를 운영 중인 설치 환경에서는 아래 내용을 레거시 참고로만 사용하세요. 신규 자동화는 [플로우](/plantpulse-platform/user/flow.md) 의 트리거 노드와 **Automation > EQL 쿼리 / 스테이트먼트** 흐름을 우선 사용합니다.

EQL 매칭 결과를 외부 메시지 채널로 발행하거나 스토리지에 저장하는 사용자 정의 트리거를 관리합니다.

### 상단 도구

```
⚡ 트리거                              [➕ 트리거 추가] [↻]
```

### 목록 테이블 <a href="#trigger-list" id="trigger-list"></a>

목록 패널 헤더 우측에는 **선택된 트리거 배치** / **해제** / **모두 재배치** 3개 버튼이 있습니다.

| 컬럼              | 폭     | 설명                       |
| --------------- | ----- | ------------------------ |
| **선택**          | 50px  | 일괄 배치/해제용 체크박스           |
| **상태**          | 80px  | 배치/해제 뱃지                 |
| **트리거 ID**      | 110px | 시스템 자동 부여 ID             |
| **트리거 명 \| 설명** | 자동    | 운영자 지정 메타                |
| **MQ 스트리밍**     | 70px  | MQ 발행 사용 여부 (Y/N)        |
| **스토리지 저장**     | 70px  | 결과를 스토리지에 적재 사용 여부 (Y/N) |
| **전체 실행 건수**    | 70px  | 누적 매칭 건수                 |
| **마지막 실행 일자**   | 110px | 가장 최근 매칭 시각              |
| **에러**          | 70px  | 누적 에러 카운트                |
| **등록일**         | 110px | 등록 시각                    |
| **마지막 수정일**     | 110px | 마지막 수정 시각                |
| **액션**          | 80px  | 수정·삭제 버튼                 |

페이지 하단에는 안내 박스가 표시됩니다.

> **사용자 정의 트리거의 사용** 특정 조건에 해당할 시 액션을 수행하는 사용자 정의 트리거를 추가할 수 있습니다. 추가로 이를 MQ를 통해 스트리밍하거나 스토리지에 저장할 수 있습니다.

### 트리거 추가/수정 양식 <a href="#trigger-form" id="trigger-form"></a>

상단 **트리거 추가** 버튼으로 양식 화면(`/trigger/form`)으로 이동합니다.

#### 1) 기본 정보 fieldset

| 입력         | path           | 폭                      | 비고                    |
| ---------- | -------------- | ---------------------- | --------------------- |
| **트리거 ID** | `trigger_id`   | 200px                  | **읽기 전용** — 시스템 자동 부여 |
| **트리거 명**  | `trigger_name` | 450px                  | 사람이 식별하기 쉬운 이름        |
| **설명**     | `trigger_desc` | 600 × 100px (textarea) | 트리거 용도                |

#### 2) EQL fieldset

| 입력          | path  | 설명                                                                               |
| ----------- | ----- | -------------------------------------------------------------------------------- |
| **EQL**     | `epl` | 8행 textarea — EQL 쿼리 작성 (`SELECT ... FROM ... WHERE ...`)                        |
| **▶ 시험 실행** | (아이콘) | `runQuery()` — 작성한 EQL 을 즉시 시험 실행. 결과는 아래의 **쿼리 결과** 표(`graph_query_result`)에 표시 |

쿼리 결과 표(시험 실행 결과 미리보기):

| 컬럼        | 설명                 |
| --------- | ------------------ |
| **출력 시간** | EQL이 결과를 emit 한 시각 |
| **데이터**   | 결과 본문              |

#### 3) 스트리밍 fieldset

| 입력           | path                     | 설명                                                                                              |
| ------------ | ------------------------ | ----------------------------------------------------------------------------------------------- |
| **MQ 출력 여부** | `use_mq` (체크박스)          | 결과 데이터를 외부 메시지 채널로 발행할지                                                                         |
| **프로토콜**     | `mq_protocol` (셀렉터)      | `MQTT` 또는 `KAFKA`                                                                               |
| **데스티네이션**   | `mq_destination` (450px) | 발행 대상 토픽/채널 — 구분자: KAFKA `-`, MQTT `/`. 예: `device-machine-topic-1` 또는 `device/machine/topic_1` |

#### 4) 스토리지 저장 fieldset

| 입력             | path                 | 설명                 |
| -------------- | -------------------- | ------------------ |
| **스토리지 저장 여부** | `use_storage` (체크박스) | 결과 데이터를 스토리지에 적재할지 |

> 스토리지 테이블명·컬럼 정의 영역은 트리거 결과 저장 테이블이 고정되어 현재 비활성화 상태입니다.

#### 폼 제출

| 버튼          | 동작                                                                    |
| ----------- | --------------------------------------------------------------------- |
| **목록(☰)**   | 트리거 목록 화면으로 돌아가기                                                      |
| **저장** (파랑) | 입력 검증 후 저장. 저장만으로는 아직 배치되지 않으며, 목록 화면에서 **선택된 트리거 배치** 를 눌러야 실제 동작 시작 |

### 모두 재배치 <a href="#trigger-redeploy" id="trigger-redeploy"></a>

| 버튼         | 위치               | 동작                                            |
| ---------- | ---------------- | --------------------------------------------- |
| **모두 재배치** | 목록 패널 헤더 우측 (🔁) | 등록된 모든 트리거를 일괄 재배치. 룰 엔진 재시작 후 또는 시스템 점검 후 사용 |

### 운영 절차

1. **트리거 추가** → EQL·스트리밍·스토리지 옵션 입력 → **저장**
2. 목록에서 해당 트리거 체크 → **선택된 트리거 배치** 클릭 → 상태가 "배치" 로 변경
3. 운영 중 잠시 멈추려면 **해제** 클릭 → 상태가 "해제" 로 변경
4. 점검 후 **모두 재배치** 로 일괄 재가동

***

## 스테이트먼트 화면 (`/statement/index`) <a href="#statement" id="statement"></a>

자산 단위로 등록된 도메인 룰(상태/집계/이벤트/명령)의 상태와 실행 통계를 조회하는 화면입니다.

### 상단 검색·필터 <a href="#statement-search" id="statement-search"></a>

```
√ 스테이트먼트                 [유형 ▼] [검색 ...] [조회] [↻]
```

| 컨트롤                        | 옵션                                                              | 설명                                          |
| -------------------------- | --------------------------------------------------------------- | ------------------------------------------- |
| **유형 셀렉터** (`search_type`) | (전체)·상태(`CONTEXT`)·집계(`AGGREGATION`)·이벤트(`EVENT`)·명령(`COMMAND`) | 4가지 룰 타입 필터                                 |
| **검색 입력** (`txt`)          | (250px)                                                         | placeholder: "검색 ..." — 룰 명/적용 자산 ID/설명 키워드 |
| **조회**                     | (빨강 + 🔍)                                                       | `search()` — 조건으로 목록 갱신                     |
| **새로고침**                   | (빨강 + ↻)                                                        | `refresh()` — 화면 새로고침                       |

### 4가지 룰 타입

| 코드            | 한국어 | 의미                                        |
| ------------- | --- | ----------------------------------------- |
| `CONTEXT`     | 상태  | 자산의 현재 상태(가동/정지/이상)를 EQL 로 정의             |
| `AGGREGATION` | 집계  | 일정 윈도우 동안 평균/최댓값 등 집계                     |
| `EVENT`       | 이벤트 | 특정 패턴(임계 초과 등) 발생 시 도메인 이벤트 발행            |
| `COMMAND`     | 명령  | 룰 매칭 시 자산에 명령(`flow_on_asset_command`) 발행 |

### 목록 테이블 <a href="#statement-list" id="statement-list"></a>

| 컬럼            | 폭     | 설명                                 |
| ------------- | ----- | ---------------------------------- |
| **상태**        | 80px  | 활성/비활성 뱃지                          |
| **적용된 에셋 ID** | 150px | 룰이 부여된 자산 식별자                      |
| **타입**        | 80px  | 4가지 타입 중 하나 (위 표 참고)               |
| **스테이트먼트명**   | 자동    | 룰 이름                               |
| **설명**        | 300px | 룰 메모                               |
| **전체 실행 건수**  | 80px  | 룰이 매칭된 누적 횟수                       |
| **마지막 실행 일자** | 120px | 가장 최근 매칭 시각                        |
| **에러**        | 60px  | 누적 에러 카운트                          |
| **등록일**       | 120px | 룰 등록 시각                            |
| **상세**        | 50px  | 클릭 시 룰 상세 보기 화면(`/statement/view`) |

### 상세 보기

목록의 **상세** 버튼을 클릭하면 별도 화면(`/statement/view/{asset_id}/{statement_name}`) 으로 이동해 다음을 볼 수 있습니다(아래 [스테이트먼트 상세 화면](#statement-view) 참고).

* 룰의 EQL 본문
* 실행 결과 이력
* 실행 트렌드/집계
* 분석 성능 트렌드
* 상태 이력

### 스테이트먼트 상세 화면 <a href="#statement-view" id="statement-view"></a>

상세 버튼으로 진입하는 분석 뷰(`statement/view.jsp`, 715줄). 한 룰의 모든 운영 정보를 한 화면에 모았습니다.

#### 상단 — 식별 헤더

```
√ 스테이트먼트 | {statement_name}   [ {asset_id} ]    [← 목록] [↻]
```

| 버튼        | 동작                        |
| --------- | ------------------------- |
| **목록(←)** | `/statement/index` 로 돌아가기 |
| **새로고침**  | 화면 다시 불러오기                |

#### 패널 1 — 스테이트먼트 정보 (5 컬럼 표)

| 컬럼          | 폭     | 설명                                                                       |
| ----------- | ----- | ------------------------------------------------------------------------ |
| **상태**      | 250px | 활성/비활성 + 마지막 실행 시각                                                       |
| **스테이트먼트명** | 자동    | 룰 이름                                                                     |
| **적용된 에셋**  | 200px | 자산 도메인 뱃지 — 클릭 시 [자산 트리](/plantpulse-platform/user/factory.md#asset)로 이동 |
| **타입**      | 200px | 상태(`CONTEXT`)·집계(`AGGREGATION`)·이벤트(`EVENT`)·명령(`COMMAND`)               |
| **등록일**     | 160px | 룰 등록 시각                                                                  |

#### EQL 본문 fieldset

룰의 EQL 쿼리 본문이 그대로 표시됩니다. 텍스트 영역에서 복사해 [쿼리 화면](#query) 으로 붙여 시험 실행할 수 있습니다.

#### 입력·출력 fieldset

| 영역             | 표시                                                             |
| -------------- | -------------------------------------------------------------- |
| **입력 시리즈**     | 룰의 입력으로 사용되는 태그·이벤트 타입 목록                                      |
| **출력 시리즈**     | 룰이 발행하는 도메인 이벤트 타입                                             |
| **에러 카운트 초기화** | 누적 에러 카운트만 0으로 리셋(`resetErrorCount(asset_id, statement_name)`) |

#### 패널 2 — 실행 결과 이력 (4 컬럼 표)

| 컬럼                | 폭     | 설명                         |
| ----------------- | ----- | -------------------------- |
| **실행 시간**         | 160px | 룰 실행 시각                    |
| **결과**            | 60px  | 성공(녹색) / 에러(빨강)            |
| **결과 값 / 에러 메시지** | 자동    | 성공이면 결과 본문, 에러면 에러 메시지     |
| **복사**            | 50px  | 📋 — 결과 값/에러 메시지를 클립보드에 복사 |

#### 패널 3 — 실행 트렌드 차트

| 항목     | 설명                                                            |
| ------ | ------------------------------------------------------------- |
| **헤더** | 📊 "실행 트렌드"                                                   |
| **버튼** | **오늘**(`switchTrendDay(0)`) / **어제**(`switchTrendDay(-1)`) 토글 |
| **차트** | 24시간 시간대별 성공/에러 누적 막대 (시리즈: 성공·에러)                            |

#### 패널 4 — 실행 집계

| 항목             | 설명                                                        |
| -------------- | --------------------------------------------------------- |
| **헤더**         | 📊 "실행 집계"                                                |
| **버튼**         | **오늘**(`switchAggDay(0)`) / **어제**(`switchAggDay(-1)`) 토글 |
| **fieldset 1** | 분 단위 집계(시간대별 카운트·평균 처리량 등)                                |
| **fieldset 2** | 우선순위 또는 결과 분포                                             |

#### 패널 5 — 분석 성능 트렌드

| 항목     | 설명                                                        |
| ------ | --------------------------------------------------------- |
| **헤더** | 🖥 "분석 성능 트렌드"                                            |
| **버튼** | **오늘**(`switchCpuDay(0)`) / **어제**(`switchCpuDay(-1)`) 토글 |
| **차트** | 룰 처리에 사용된 CPU/처리 시간 시계열 — 무거운 룰 식별                        |

#### 패널 6 — 상태 이력 표

| 컬럼        | 설명                       |
| --------- | ------------------------ |
| **변경 시간** | 룰 상태 변경 시각 (활성·해제·재배포 등) |
| **상태**    | 변경된 상태                   |

룰 라이프사이클(언제 활성화·해제·수정되었는지)을 추적할 때 사용합니다.

### 활용 패턴

| 분석 흐름          | 화면 흐름                                                         |
| -------------- | ------------------------------------------------------------- |
| **에러 룰 진단**    | 실행 결과 이력 → 에러 메시지 → 복사 → EQL 본문 검토 → [쿼리 화면](#query) 에서 시험 실행 |
| **무거운 룰 식별**   | 분석 성능 트렌드 → CPU 사용량이 큰 시간대 → EQL 본문 단순화                       |
| **룰 매칭 분포 분석** | 실행 트렌드 + 집계 → 시간대별 매칭 패턴                                      |
| **운영 룰 정비**    | 상태 이력 → 활성화/해제 시점 추적                                          |

***

## 활용 시나리오 <a href="#use-cases" id="use-cases"></a>

| 시나리오                     | 화면                                       | 절차                                                                             |
| ------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------ |
| **현재 라인 가동 상태 즉시 확인**    | 쿼리                                       | EQL 입력창에 `SELECT * FROM AssetData.win:time(1 sec) WHERE asset_id='LINE-A'` → ▶ |
| **새 알람 룰 시험·배포**         | 트리거                                      | 추가 → EQL 작성 → ▶ 시험 실행 → 결과 확인 → 저장 → 배치                                        |
| **외부 SCADA 로 자산 이벤트 중계** | 트리거                                      | EQL 매칭 메시지를 MQTT 토픽 `scada/line-a/events` 로 발행                                 |
| **자산 단위 룰 점검**           | 스테이트먼트                                   | 유형=`EVENT` + 자산 ID 검색 → 활성 룰 목록 + 실행 건수                                        |
| **룰 폭주 의심**              | 스테이트먼트                                   | 전체 실행 건수 큰 룰부터 확인 → 상세에서 EQL/입력 검토                                             |
| **자동화로 룰 결과 받기**         | [플로우](/plantpulse-platform/user/flow.md) | `flow_on_asset_event` 트리거로 도메인 룰의 결과를 직접 수신                                    |

***

## 자주 묻는 질문 <a href="#faq" id="faq"></a>

**Q. EQL 은 SQL 과 같은가요?** A. 비슷한 문법을 쓰지만 EQL 은 **시간 윈도우** 와 **스트림** 개념이 추가된 이벤트 처리 언어입니다. 예: `Point.win:time(5 sec)` 는 최근 5초의 데이터만, `Point.win:length(100)` 은 최근 100건만 처리합니다.

**Q. 쿼리 화면에서 실행한 쿼리는 저장되나요?** A. ▶ 실행만으로는 임시이며, **이 쿼리를 저장** 버튼을 눌러야 즐겨찾기에 저장됩니다. 일회성 분석용이면 저장하지 않아도 됩니다.

**Q. 트리거를 저장만 했는데 동작하지 않습니다.** A. 저장은 정의만 등록한 상태입니다. 목록에서 그 트리거를 체크 후 **선택된 트리거 배치** 를 클릭해야 실제 룰 엔진에 등록되어 매칭이 시작됩니다.

**Q. 트리거의 데스티네이션을 어떻게 정해야 하나요?** A. **프로토콜** 에 따라 구분자가 다릅니다.

* KAFKA: 하이픈(`-`) 구분 — 예: `device-machine-topic-1`
* MQTT: 슬래시(`/`) 구분 — 예: `device/machine/topic_1`

**Q. 스테이트먼트 화면에서 검색이 결과가 안 나옵니다.** A. 유형 셀렉터에서 "유형" (전체)을 선택하셨는지 확인하세요. 또한 자산 ID 의 일부분만 입력해도 부분 일치 검색됩니다.

**Q. 전체 실행 건수가 비정상적으로 큽니다.** A. 룰의 입력 윈도우가 너무 좁거나 매칭 조건이 너무 광범위할 가능성이 큽니다. **상세** 버튼으로 EQL 을 검토하시거나 [플로우](/plantpulse-platform/user/flow.md) 의 트리거 패턴(`*_pattern`)으로 메시지를 사전 필터링하세요.

**Q. 트리거의 EQL 시험 실행 결과가 비어 있습니다.** A. 시험 시점에 데이터 인입이 없거나 EQL 의 윈도우가 너무 길어 첫 매칭이 늦을 수 있습니다. 시험을 1분 정도 둔 뒤 다시 보시거나, 윈도우를 짧게 (`win:time(1 sec)`) 조정해 시험하세요.

**Q. 트리거를 일시적으로 멈추려면?** A. 목록에서 그 트리거를 체크하고 **해제** 클릭. 다시 동작시키려면 **선택된 트리거 배치**.

**Q. 자산에 룰이 자동으로 부여되는가요?** A. 자산 등록 시 운영 환경 설정에 따라 기본 도메인 룰이 자동 부여되는 경우가 있습니다. 현재 적용된 룰은 스테이트먼트 화면에서 확인하시고, 추가/제거가 필요하면 시스템 관리자 또는 자산별 설정 화면에서 진행합니다.

**Q. EQL 결과를 자동으로 처리하고 싶어요.** A. [플로우](/plantpulse-platform/user/flow.md) 의 자산 트리거(`flow_on_asset_event`/`flow_on_asset_alarm` 등)로 도메인 룰의 결과를 자동 수신해 후속 액션(작업지시 발행·이메일·외부 API 호출)으로 연결할 수 있습니다.

***

## 관련 화면 <a href="#related" id="related"></a>

* [데이터 포인트](/plantpulse-platform/user/data-point.md) — EQL 의 입력이 되는 시계열 태그 데이터
* [알람](/plantpulse-platform/user/alarm.md) — 트리거가 발생시키는 알람의 운영 화면
* [플로우](/plantpulse-platform/user/flow.md) — 자산 트리거로 룰 결과를 자동 처리
* [팩토리 관리](/plantpulse-platform/user/factory.md) — 자산 트리에서 룰 부여
* [상태 코드 정의](/plantpulse-platform/user/status.md) — 룰이 평가하는 상태 코드(NORMAL/WARN/ERROR)
* [진단](/plantpulse-platform/user/diagnostic.md) — 룰 처리 중 오류 로그
