> 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/developer/plugins.md).

# 플러그인 아키텍처 (OEE/RAM/EMS)

## 개요

이 문서에서는 PlantPulse 플러그인 시스템의 아키텍처와 각 플러그인의 상세 동작 방식을 안내합니다. PlantPulse 플러그인 시스템은 설비(Asset)에 대한 고급 분석 기능을 확장하는 프레임워크입니다. 각 플러그인은 `PluginHandler` 인터페이스를 구현하며, CEP 엔진의 이벤트를 수신하여 지표를 계산하고 저장합니다.

처음 플러그인 시스템을 접하시는 분이라면, "센서 데이터 → CEP 이벤트 → 플러그인 핸들러 → 지표 계산 → 저장"의 흐름을 먼저 이해해 주시면 각 플러그인의 동작을 파악하시기 쉽습니다.

현재 3개의 분석 플러그인 패키지가 제공되고 있습니다.

| 플러그인    | 준거 표준                      | 주요 지표                        |
| ------- | -------------------------- | ---------------------------- |
| **OEE** | ISO 22400-2:2014, SEMI E10 | 가용률, 성능, 품질, OEE             |
| **RAM** | ISO 14224                  | MTBF, MTTR, 정비 가용도, 신뢰도, 정비성 |
| **EMS** | ISO 50001                  | 에너지 소비, 효율, CO2 배출           |

***

## 공통 아키텍처 <a href="#common-architecture" id="common-architecture"></a>

### 플러그인 처리 흐름

모든 플러그인은 동일한 이벤트 처리 구조를 따릅니다. 아래 다이어그램에서 데이터가 센서에서 저장소까지 흐르는 과정을 확인하실 수 있습니다.

```mermaid
flowchart TB
  SRC[센서 · PLC · MES]
  CEP[CEP Engine<br/>EQL 룰 매칭]

  subgraph EVENTS["AssetStatement 결과"]
    AC[AssetContext]
    AA[AssetAggregation]
    AE[AssetEvent]
  end

  FACT[PluginHandlerFactory]
  HDLR["{Plugin}Handler"]
  CALC["{Plugin}Calculator.calculate()"]

  subgraph SINK["저장 / 발행"]
    CEP_OUT[CEP Event<br/>실시간]
    CASS[(Cassandra<br/>시계열)]
    PG[(PostgreSQL<br/>관계형)]
  end

  SRC --> CEP
  CEP --> AC
  CEP --> AA
  CEP --> AE
  AC --> FACT
  AA --> FACT
  AE --> FACT
  FACT --> HDLR
  HDLR --> CALC
  CALC --> CEP_OUT
  CALC --> CASS
  CALC --> PG
```

### 공통 컴포넌트

| 컴포넌트                             | 역할                                                                      |
| -------------------------------- | ----------------------------------------------------------------------- |
| `PluginHandler` 인터페이스            | 플러그인 이벤트 라우팅 (AssetContext, AssetAggregation, AssetEvent, AssetCommand) |
| `PluginHandlerFactory`           | 플러그인 핸들러 싱글턴 팩토리, DAO/Calculator 인스턴스 공유                                |
| `{Plugin}Calculator`             | 지표 계산 엔진                                                                |
| `{Plugin}CalculationJob`         | Quartz 스케줄링 Job — 전체 설비 주기 계산                                           |
| `CassandraPlugin{Plugin}Handler` | Cassandra 테이블 DDL/DML                                                   |

### 스케줄링 공통 사항

각 플러그인의 스케줄링 설정은 아래 표와 같습니다.

| 항목     | OEE                  | RAM         | EMS         |
| ------ | -------------------- | ----------- | ----------- |
| 실행 주기  | engine.properties 설정 | 10초         | 10초         |
| 병렬 스레드 | 10                   | 5           | 5           |
| 주문 확인  | 활성 주문 필수             | 활성 주문 필수    | 활성 주문 필수    |
| 집계 윈도우 | 주문 시작 \~ 현재          | 주문 시작 \~ 현재 | 주문 시작 \~ 현재 |

***

## OEE 엔진 <a href="#oee" id="oee"></a>

OEE(Overall Equipment Effectiveness)는 설비 종합 효율을 실시간으로 계산하는 플러그인입니다. 가용률, 성능, 품질 세 가지 요소를 종합하여 설비의 효율성을 측정합니다.

### 패키지 구조

```
plantpulse.core.service.support.asset.listener.plugin.oee
├── EquipmentStatus.java            ← 설비 상태 상수 (RUN/IDLE/STOP/FAULT/SETUP/PLANNED_STOP/PLANNED_START)
├── EquipmentStatusColor.java       ← 상태별 HEX 색상 (UI 시각화용)
├── PlannedStopType.java            ← 계획정지 유형 코드 (PM/CLEANING/BREAK 등)
├── OEEKeys.java                    ← 플러그인 이벤트 키 상수
├── OEEPluginHandler.java           ← PluginHandler 구현체 — 이벤트 타입별 위임
├── OEEPluginHandlerFactory.java    ← 핸들러 싱글턴 팩토리
├── handler/
│   ├── OEEHandler.java             ← 핸들러 인터페이스 <T>
│   ├── OEEEquipmentStatusHandler   ← 설비 상태 변경 처리 + PLANNED_STOP 락
│   ├── OEECountHandler.java        ← 양품/불량 수량 처리
│   └── OEEOrderHandler.java        ← 작업지시 시작/종료 처리
└── cal/
    └── OEECalculator.java          ← OEE 계산 엔진 (핵심)

plantpulse.core.engine.scheduling.job.type.plugin.oee
└── OEECalculationJob.java          ← Quartz Job — 전체 설비 주기 계산

plantpulse.core.dao
└── OEEDAO.java                     ← PostgreSQL mm_oee 테이블 UPSERT/조회
```

### 이벤트 처리

OEE 플러그인은 아래 세 가지 유형의 이벤트를 처리합니다.

| 이벤트 유형                                                    | 핸들러                         | 트리거 조건                        |
| --------------------------------------------------------- | --------------------------- | ----------------------------- |
| `AssetContext(plugin=OEE, key=EQUIPMENT_STATUS)`          | `OEEEquipmentStatusHandler` | 설비 상태 변경 (RUN, IDLE, FAULT 등) |
| `AssetContext(plugin=OEE, key=START_ORDER\|END_ORDER)`    | `OEEOrderHandler`           | 작업지시 시작/종료                    |
| `AssetAggregation(plugin=OEE, key=GOOD_COUNT\|BAD_COUNT)` | `OEECountHandler`           | 양품/불량 카운트 변경                  |

### 이벤트 키 상수 (OEEKeys)

| 상수                 | 값                    | 용도          |
| ------------------ | -------------------- | ----------- |
| `OEE`              | `"OEE"`              | 플러그인 식별자    |
| `EQUIPMENT_STATUS` | `"EQUIPMENT_STATUS"` | 설비 상태 이벤트 키 |
| `START_ORDER`      | `"START_ORDER"`      | 작업지시 시작     |
| `END_ORDER`        | `"END_ORDER"`        | 작업지시 종료     |
| `GOOD_COUNT`       | `"GOOD_COUNT"`       | 양품 수량       |
| `BAD_COUNT`        | `"BAD_COUNT"`        | 불량 수량       |

### OEE 계산 흐름

아래는 OEE 계산이 수행되는 전체 과정입니다. 9단계를 거쳐 OEE 지표가 산출되고 저장됩니다.

```
calculate(current_timestamp, asset)
│
├── 1. 주문 조회 (OrderDAO.selectCurrentAssetOrder)
├── 2. 기간 클리핑 (effective_end = min(now, order_end))
├── 3. 생산 수량 집계 (양품/불량)
├── 4. 설비 상태 집계 + Overflow 클램프
├── 5. OEE 메트릭 계산 (availability, performance, quality, oee)
├── 6. 생산성 메트릭 계산 (throughput, lead_time, wip)
├── 7. 알람 집계
├── 8. 결과 JSON 조립 (90+ 필드)
└── 9. 저장 + CEP 이벤트 발행
      ├── CEPClient.sendEvent(OEE)
      ├── OrderDAO.insertOrUpdateOEE()
      └── OEEDAO.upsertFromResult()
```

### OEE 수식

OEE는 가용률, 성능, 품질 세 가지 요소의 곱으로 계산됩니다. 각 요소의 산출 방식은 다음과 같습니다.

```
                   run_time_ms
Availability = ─────────────────────
               PPT(observed)

                ideal_cycle_time_ms × total_production_count
Performance = ──────────────────────────────────────────────
                             run_time_ms

              total_good_count
Quality = ─────────────────────
          total_production_count

OEE = Availability × Performance × Quality
```

* 모든 요소는 `[0.0, 1.0]` 범위로 클램프됩니다.
* PPT(observed) = order\_duration\_time - (setup + planned\_stop + planned\_start)

### Overflow 클램프

설비 상태 시간의 합이 주문 경과 시간을 초과하면 우선순위 기반으로 삭감합니다. 가치 창출 시간(RUN)을 최대한 보존하기 위해 우선도가 낮은 상태부터 먼저 삭감됩니다.

```
삭감 순서 (우선도 낮은 것부터):
  1. PLANNED_STOP    ← 가장 먼저 삭감
  2. PLANNED_START
  3. SETUP
  4. STOP
  5. FAULT
  6. IDLE
  7. RUN             ← 가장 마지막에 삭감 (가치 창출 시간 보존)
```

### PLANNED\_STOP 락 메커니즘

`OEEEquipmentStatusHandler`는 `PLANNED_STOP` 상태에서 락을 적용하여, `PLANNED_START`가 수신될 때까지 다른 상태 변경을 거부합니다. 이를 통해 계획 정지 기간 중의 데이터 무결성을 보장합니다.

```
정상 상태 전이: RUN ──▶ IDLE ──▶ FAULT ──▶ ... (자유)
                          │
                          ▼
                 PLANNED_STOP 수신 ──▶ 락 시작
                          │
                 PLANNED_START만 허용 (그 외 WARN 로그 + 무시)
                          │
                 PLANNED_START 수신 ──▶ 락 해제 → 정상 복귀
```

### 방어 로직

계산 중 발생할 수 있는 예외 상황(0으로 나누기, NaN 등)에 대비하여 아래와 같은 방어 유틸리티를 사용하고 있습니다.

| 유틸                  | 동작                                    |
| ------------------- | ------------------------------------- |
| `safeDiv(num, den)` | `den ≤ 0` 또는 `!isFinite` → `0.0`      |
| `clamp01(v)`        | `!isFinite` → `0.0`, 그 외 `[0, 1]` 클램프 |
| `max0(v)`           | 음수 → `0L`                             |

***

## RAM 모듈 <a href="#ram" id="ram"></a>

RAM(Reliability, Availability, Maintainability)은 설비의 신뢰성, 가용성, 정비성 지표를 산출하는 플러그인입니다. 고장 패턴을 분석하여 MTBF, MTTR 등의 핵심 지표를 계산합니다.

### 패키지 구조

```
plantpulse.core.service.support.asset.listener.plugin.ram
├── RAMKeys.java                    ← RAM 이벤트/지표 키 상수
├── RAMPluginHandler.java           ← 플러그인 핸들러 (FAILURE_START/END 라우팅)
├── handler/
│   └── RAMFailureHandler.java      ← 고장 이벤트 저장 + FAILURE_END 시 RAM 재계산
└── cal/
    └── RAMCalculator.java          ← RAM 지표 계산 엔진

plantpulse.core.engine.scheduling.job.type.plugin.ram
└── RAMCalculationJob.java          ← Quartz Job — 주문 기반 집계

plantpulse.core.engine.deploy
└── RAMCalculateJobDeployer.java    ← Job 배포/등록

plantpulse.core.engine.storage.db.cassandra.plugin
└── CassandraPluginRAMHandler.java  ← Cassandra RAM 테이블 DDL/DML
```

### 이벤트 처리

| 이벤트 유형                                        | 핸들러                 | 처리                             |
| --------------------------------------------- | ------------------- | ------------------------------ |
| `AssetContext(plugin=RAM, key=FAILURE_START)` | `RAMFailureHandler` | 고장 이벤트를 저장합니다                  |
| `AssetContext(plugin=RAM, key=FAILURE_END)`   | `RAMFailureHandler` | 고장 이벤트를 저장하고 RAM 지표를 즉시 재계산합니다 |

### RAM 계산 흐름

RAM 지표는 고장 통계를 기반으로 8단계에 걸쳐 계산됩니다.

```
calculate(asset_id, run_time_sec, from_ts, to_ts)
│
├── 1. 고장 통계 조회 (fetchRAMWindowStats → tm_asset_ram_failure_event)
│      → FAILURE_START/END 쌍 매칭
│      → failure_count, repair_count, total_repair_time_ms
│
├── 2. MTBF 계산
│      → total_run_time_sec / failure_count
│
├── 3. MTTR 계산
│      → total_repair_time_sec / repair_count
│
├── 4. MA 계산
│      → MTBF / (MTBF + MTTR)
│
├── 5. 고장률 (λ)
│      → 1 / MTBF
│
├── 6. 신뢰도 R(t=1h)
│      → e^(-3600 / MTBF)
│
├── 7. 정비성 M(t=1h)
│      → 1 - e^(-3600 / MTTR)
│
└── 8. 결과 JSON 조립 + 저장
```

### 스케줄 기반 계산 (RAMCalculationJob)

* 실행 주기: 10초 (`RAM_CALCULATE_JOB_TERM = "10 SECONDS"`)
* 병렬 처리: 5 스레드 (`engine.job.thread.ram`)
* 주문 확인 후 OEE의 `run_time_ms`를 운전 시간으로 참조합니다.
* 결과: Cassandra 듀얼 라이트 (tm\_asset\_ram + tm\_asset\_ram\_history + tm\_asset\_ram\_history\_by\_timestamp)

### EQL 스테이트먼트

`MM_ASSET_STATEMENT_PLUGIN` 테이블에 아래와 같이 등록되어 있습니다.

| 키               | 타입      | EQL                                                 |
| --------------- | ------- | --------------------------------------------------- |
| `FAILURE_START` | CONTEXT | `SELECT ... FROM Point(tag_id='...').win:length(1)` |
| `FAILURE_END`   | CONTEXT | `SELECT ... FROM Point(tag_id='...').win:length(1)` |

***

## EMS 모듈 <a href="#ems" id="ems"></a>

EMS(Energy Management System)는 설비의 에너지 소비, 효율, 탄소 배출 지표를 산출하는 플러그인입니다. ISO 50001 표준에 기반하여 에너지 관리 지표를 실시간으로 계산합니다.

### 패키지 구조

```
plantpulse.core.service.support.asset.listener.plugin.ems
├── EMSKeys.java                    ← EMS 이벤트/지표 키 상수
├── EMSPluginHandler.java           ← 플러그인 핸들러 (POWER_USAGE 라우팅)
├── handler/
│   ├── EMSHandler.java             ← EMS 핸들러 인터페이스
│   └── EMSPowerUsageHandler.java   ← 적산 전력(kWh) 이벤트 핸들러
└── cal/
    └── EMSCalculator.java          ← EMS 지표 계산 엔진

plantpulse.core.engine.scheduling.job.type.plugin.ems
└── EMSCalculationJob.java          ← Quartz Job — 주문 기반 집계

plantpulse.core.engine.storage.db.cassandra.plugin
└── CassandraPluginEMSHandler.java  ← Cassandra EMS 테이블 DDL/DML
```

### 이벤트 처리

| 이벤트 유형                                          | 핸들러                    | 처리                              |
| ----------------------------------------------- | ---------------------- | ------------------------------- |
| `AssetAggregation(plugin=EMS, key=POWER_USAGE)` | `EMSPowerUsageHandler` | 적산 전력(kWh)을 저장하고 EMS 지표를 재계산합니다 |

### EMS 계산 흐름

EMS 지표는 에너지 통계와 OEE 데이터를 결합하여 7단계에 걸쳐 계산됩니다.

```
calculate(asset_id, oee, from_ts, to_ts)
│
├── 1. 에너지 통계 조회 (selectAssetEnergyStats → tm_asset_ems_power_event)
│      → event_count, total_energy_kwh, peak_power_kw, avg_power_kw
│      → event_count = 0 이면 전체 0 반환
│
├── 2. OEE 데이터 참조
│      → total_good_count (양품 수량)
│      → run_time_ms (가동 시간)
│
├── 3. 단위 에너지 계산
│      → total_energy_kwh / total_good_count
│
├── 4. 에너지 효율 계산
│      → avg_power_kw / peak_power_kw
│
├── 5. CO2 배출량 계산
│      → total_energy_kwh × 0.4594 (한국 배출계수)
│
├── 6. 단위 CO2 계산
│      → co2_emission_kg / total_good_count
│
└── 7. 결과 JSON 조립 + 저장
```

### 스케줄 기반 계산 (EMSCalculationJob)

* 실행 주기: 10초 (`EMS_CALCULATE_JOB_TERM = "10 SECONDS"`)
* 병렬 처리: 5 스레드 (`engine.job.thread.ems`)
* 주문 확인 후 OEE의 양품 수량/가동 시간을 참조합니다.
* 결과: Cassandra 듀얼 라이트 (tm\_asset\_ems + tm\_asset\_ems\_history + tm\_asset\_ems\_history\_by\_timestamp)

### EQL 스테이트먼트

| 키             | 타입          | 설명                                    |
| ------------- | ----------- | ------------------------------------- |
| `POWER_USAGE` | AGGREGATION | 적산 전력(kWh) — AssetAggregation으로 전달됩니다 |

***

## 데이터베이스 스키마 <a href="#database" id="database"></a>

각 플러그인은 PostgreSQL과 Cassandra에 아래와 같은 테이블을 사용합니다.

### PostgreSQL

| 테이블      | 플러그인 | PK         | 설명                                                        |
| -------- | ---- | ---------- | --------------------------------------------------------- |
| `mm_oee` | OEE  | `order_id` | 주문별 최신 OEE 스냅샷 (UPSERT), `payload` 컬럼에 전체 JSON `jsonb` 저장 |

### Cassandra — OEE

| 테이블                                 | PK                                                     | 용도                   |
| ----------------------------------- | ------------------------------------------------------ | -------------------- |
| `tm_asset_oee`                      | `(site_id), area_id, line_id, asset_id, order_id`      | 설비별 OEE 현재값 (최신 스냅샷) |
| `tm_asset_oee_history`              | `(asset_id), order_id, year, month, day, hour, minute` | OEE 히스토리             |
| `tm_asset_oee_history_by_timestamp` | `(asset_id), timestamp`                                | OEE 히스토리 타임스탬프 기반 조회 |

### Cassandra — RAM

| 테이블                                 | PK                                                     | 용도                                   |
| ----------------------------------- | ------------------------------------------------------ | ------------------------------------ |
| `tm_asset_ram`                      | `(site_id), area_id, line_id, asset_id, order_id`      | 설비별 RAM 최신 지표                        |
| `tm_asset_ram_history`              | `(asset_id), order_id, year, month, day, hour, minute` | RAM 이력                               |
| `tm_asset_ram_history_by_timestamp` | `(asset_id), timestamp`                                | RAM 이력 타임스탬프 기반 조회                   |
| `tm_asset_ram_failure_event`        | `(asset_id), timestamp`                                | 고장 이벤트 원본 (event\_type, fault\_code) |

### Cassandra — EMS

| 테이블                                 | PK                                                     | 용도                                   |
| ----------------------------------- | ------------------------------------------------------ | ------------------------------------ |
| `tm_asset_ems`                      | `(site_id), area_id, line_id, asset_id, order_id`      | 설비별 EMS 최신 지표                        |
| `tm_asset_ems_history`              | `(asset_id), order_id, year, month, day, hour, minute` | EMS 이력                               |
| `tm_asset_ems_history_by_timestamp` | `(asset_id), timestamp`                                | EMS 이력 타임스탬프 기반 조회                   |
| `tm_asset_ems_power_event`          | `(asset_id), timestamp`                                | 전력 이벤트 원본 (power\_kw, consumed\_kwh) |

***

## REST API <a href="#api" id="api"></a>

플러그인 데이터를 조회할 수 있는 REST API입니다.

### RAM API

```
GET /as-plugin/ram/{asset_id}?from_ts={시작타임스탬프}&to_ts={종료타임스탬프}
```

**응답 예시**:

```json
{
  "ram": {
    "mtbf_sec": 36000.0,
    "mtbf_hours": 10.0,
    "mttr_sec": 1800.0,
    "mttr_hours": 0.5,
    "maintenance_availability": 0.9524,
    "failure_rate_lambda_per_sec": 0.00002778,
    "reliability_R_1h": 0.904837,
    "maintainability_M_1h": 0.864665,
    "failure_count": 5,
    "repair_count": 5,
    "total_run_time_hours": 50.0
  }
}
```

### EMS API

```
GET /as-plugin/ems/{asset_id}?from_ts={시작타임스탬프}&to_ts={종료타임스탬프}
```

**응답 예시**:

```json
{
  "ems": {
    "total_energy_kwh": 1500.5,
    "energy_per_unit_kwh": 0.15,
    "energy_efficiency": 0.82,
    "co2_emission_kg": 689.33,
    "co2_per_unit_kg": 0.0689,
    "peak_power_kw": 210.5,
    "avg_power_kw": 172.6,
    "run_time_hours": 8.5
  }
}
```

***

## ProductionChangeEventBus <a href="#eventbus" id="eventbus"></a>

플러그인 핸들러가 계산한 생산 이벤트를 Kafka와 Cassandra로 발행하는 이벤트 버스입니다. 모든 OEE/RAM/EMS 이벤트는 이 버스를 통해 외부 시스템으로 전파됩니다.

### 아키텍처

```
센서/PLC → CEP Engine → PluginHandlerFactory → OEE/RAM/EMS Handler
                                                       │
                                                       ▼
                                      ProductionChangeManager.getBus().publish(event)
                                                       │
                                                       ▼
                                         DefaultProductionChangeEventBus
                                              │              │
                                              ▼              ▼
                                         Cassandra        Kafka
                                       (3 히스토리 테이블)  (3 토픽)
```

### 이벤트 유형 (13종)

| 분류            | 이벤트                              | 설명                                |
| ------------- | -------------------------------- | --------------------------------- |
| **OEE** (10종) | `EQUIPMENT_STATUS_RUN`           | 설비 RUN 상태 전환                      |
|               | `EQUIPMENT_STATUS_IDLE`          | 설비 IDLE 상태 전환                     |
|               | `EQUIPMENT_STATUS_STOP`          | 설비 STOP 전환 (`stop_cause_code` 포함) |
|               | `EQUIPMENT_STATUS_FAULT`         | 설비 FAULT 전환                       |
|               | `EQUIPMENT_STATUS_SETUP`         | 설비 SETUP 전환                       |
|               | `EQUIPMENT_STATUS_PLANNED_STOP`  | 계획 정지 시작                          |
|               | `EQUIPMENT_STATUS_PLANNED_START` | 계획 정지 해제                          |
|               | `GOOD_COUNT`                     | 양품 수량 변경                          |
|               | `BAD_COUNT`                      | 불량 수량 변경 (`defect_cause_code` 포함) |
|               | `ORDER_START` / `ORDER_END`      | 워크 오더 시작/종료                       |
| **RAM** (2종)  | `FAILURE_START`                  | 고장 시작 (`fault_code` 포함)           |
|               | `FAILURE_END`                    | 고장 종료                             |
| **EMS** (1종)  | `POWER_USAGE`                    | 에너지 소비 (`consumed_kwh` 포함)        |

### Kafka 토픽

| 토픽                  | 파티션 키      | 메시지 형식                                                                     |
| ------------------- | ---------- | -------------------------------------------------------------------------- |
| `pp-production-oee` | `asset_id` | JSON: `{event_type, plugin, statement_type, asset_id, timestamp, payload}` |
| `pp-production-ram` | `asset_id` | JSON: 동일 구조                                                                |
| `pp-production-ems` | `asset_id` | JSON: 동일 구조                                                                |

> **파티션 키:** `asset_id` 기준으로 파티셔닝되어, 같은 설비의 이벤트는 항상 같은 파티션에 순서대로 기록됩니다.

### Cassandra 히스토리 테이블

| 테이블                                              | 파티션 키                   | 용도                 |
| ------------------------------------------------ | ----------------------- | ------------------ |
| `tm_asset_production_event_history`              | `(asset_id)`            | 설비 기준 이벤트 이력 조회    |
| `tm_asset_production_event_history_by_order`     | `(order_id)`            | 워크 오더 기준 이벤트 이력 조회 |
| `tm_asset_production_event_history_by_timestamp` | `(asset_id), timestamp` | 날짜 기준 이벤트 이력 조회    |

### 활용 사례

* **실시간 대시보드**: WebSocket을 통해 생산 이벤트를 브라우저로 푸시
* **MES/ERP 연동**: Kafka 컨슈머로 SAP, Oracle 등 외부 시스템에 이벤트 전달
* **데이터 레이크**: Spark/Flink 컨슈머로 이벤트 스트림 수집
* **지능형 알림**: FAULT 발생, 품질 저하, 장시간 정지 등 임계값 기반 알림
* **워크 오더 이벤트 타임라인**: 오더별 전체 생산 이벤트 이력 추적

***

## EquipmentStateMachine <a href="#state-machine" id="state-machine"></a>

설비 상태 전이를 검증하고 관리하는 상태 머신입니다.

### 주요 기능

* **상태 전이 검증**: 유효하지 않은 상태 전이(예: STOP → RUN 직접 전환)를 거부하고 에러 로깅 테이블에 기록합니다.
* **디바운싱**: `engine.oee.debounce.ms` (기본 100ms) 설정에 따라, 동일 설비에서 빠르게 연속되는 상태 변경을 하나로 합쳐 OEE 재계산 횟수를 1회로 줄입니다.
* **에셋 단위 잠금**: `ConcurrentHashMap` 기반 에셋별 잠금으로 동시 이벤트 처리 시 데이터 정합성을 보장합니다.
* **PLANNED\_STOP 락**: 계획 정지 상태에서는 PLANNED\_START만 수락하고 다른 상태 변경을 거부합니다.

***

## 외부 의존성 <a href="#dependencies" id="dependencies"></a>

각 플러그인이 사용하는 외부 컴포넌트를 아래 표에서 확인하실 수 있습니다.

| 컴포넌트                        | 사용 플러그인       | 역할                                    |
| --------------------------- | ------------- | ------------------------------------- |
| `StorageClient.forPlugin()` | OEE, RAM, EMS | Cassandra 플러그인 데이터 R/W                |
| `StorageClient.forSelect()` | OEE           | Cassandra 알람 조회                       |
| `OrderDAO`                  | OEE, RAM, EMS | 현재 주문 조회 + OEE 결과 저장                  |
| `OEEDAO`                    | OEE           | mm\_oee UPSERT/조회                     |
| `PathDAO`                   | OEE           | 설비 경로 조회 (site → area → line → asset) |
| `AssetDAO`                  | OEE, RAM, EMS | 전체 설비 목록 조회 (스케줄링 Job)                |
| `CEPClient`                 | OEE           | OEE 결과 실시간 이벤트 발행                     |
| `ManagedExecutorFactory`    | OEE, RAM, EMS | 병렬 실행 스레드풀 관리                         |
| `Quartz Scheduler`          | OEE, RAM, EMS | CalculationJob 주기 실행                  |
