# 02 OEE-Dashboard

In diesem Tutorial erstellst du ein Abteilungs-Dashboard mit aggregierten OEE-Kennzahlen, Produktionsstatus aller Maschinen und einer Abteilungsübersicht:

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-e2dab229c2e58701729e0a8013f946928130c633%2Fgrafana-oee-hero-01.png?alt=media" alt=""><figcaption></figcaption></figure>

## Was du lernst

* POST-Requests mit dem `productivity-metrics`-Endpoint konfigurieren
* Versteckte Zeitvariablen mit 10-Minuten-Ausrichtung erstellen
* OEE-Metriken als Tabelle mit Farbskala anzeigen
* Maschinenaktivität als State Timeline visualisieren
* Gewichtete OEE-Aggregation für Abteilungsübersichten berechnen

## Voraussetzungen

* [ENLYZE API abfragen](https://docs.enlyze.com/integrations/grafana/advanced-api/02-api-queries) und [Variablen](https://docs.enlyze.com/integrations/grafana/advanced-api/03-variables) abgeschlossen
* Verständnis von Transformationen ([Tutorial 3.2](https://github.com/enlyze/enlyze-docs/blob/main/de/grafana/intermediate-features/03-transformations.md))

***

## 10-Minuten-Ausrichtung

Der `productivity-metrics`-Endpoint erfordert Zeitbereiche, die auf 10-Minuten-Intervalle ausgerichtet sind (Minuten: 0, 10, 20, 30, 40, 50; Sekunden und Millisekunden: 0).

Wenn Nutzende in Grafana z.B. "Last 7 days" auswählen, enthalten die Zeitwerte beliebige Sekunden. Erstelle zwei versteckte Query-Variablen, die die Zeitwerte automatisch runden.

### Zeitvariablen erstellen

Erstelle ein neues Dashboard und lege unter **Settings** > **Variables** zwei Variablen an:

| Einstellung | start                 | end                  |
| ----------- | --------------------- | -------------------- |
| Name        | `start`               | `end`                |
| Type        | Query                 | Query                |
| Data source | ENLYZE API            | ENLYZE API           |
| Hide        | Variable              | Variable             |
| Refresh     | On time range change  | On time range change |
| Source      | Inline                | Inline               |
| Data        | `{"time": ${__from}}` | `{"time": ${__to}}`  |

**Root Selector (JSONata, für beide Variablen identisch):**

```
(
  $time_millis := $number($.time);
  $time_rounded := $time_millis - ($time_millis % 600000);
  $fromMillis($time_rounded)
)
```

Der Ausdruck rechnet den Zeitstempel in Millisekunden um, rundet auf das nächste 10-Minuten-Intervall ab und wandelt zurück in ein ISO-Datum.

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-e2dab229c2e58701729e0a8013f946928130c633%2Fgrafana-oee-hero-01.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Für einfachere Dashboards reicht auch die Rundung auf volle Stunden: `${__from:date:YYYY-MM-DDTHH:00:00}+00:00`. Die 10-Minuten-Rundung ist aber genauer und funktioniert mit allen Endpunkten der ENLYZE API.
{% endhint %}

***

## OEE nach Maschine (Tabelle)

Die Tabelle zeigt OEE, Availability, Performance und Quality für jede Maschine der Abteilung.

### POST-Request konfigurieren

Erstelle ein **Table**-Panel und konfiguriere die erste Query:

| Einstellung       | Wert                                                                 |
| ----------------- | -------------------------------------------------------------------- |
| Type              | JSON                                                                 |
| Parser            | Backend                                                              |
| Method            | POST                                                                 |
| URL               | `machines/141e0927-62b3-4e76-8398-ad82d20f397f/productivity-metrics` |
| Body type         | Raw                                                                  |
| Body content type | application/json                                                     |

**Body:**

```json
{
  "datetime_ranges": [
    {
      "start": "${start}",
      "end": "${end}"
    }
  ]
}
```

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-e9edc3c098dacbc693ba4187b14ed323ff32e841%2Fgrafana-oee-post-request-01.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
Achte darauf, dass der **Body content type** auf `application/json` steht (nicht `text/plain`). Andernfalls antwortet die API mit HTTP 422.
{% endhint %}

### Columns und Computed Columns

Setze den **Root Selector** auf `$.data` und füge unter **Parsing options & Result fields** folgende **Columns** hinzu:

| Selector             | Title        | Type   |
| -------------------- | ------------ | ------ |
| `productivity.score` | productivity | Number |
| `availability.score` | availability | Number |
| `performance.score`  | performance  | Number |
| `quality.score`      | quality      | Number |

Füge zwei **Computed Columns** hinzu, um die Maschine zu identifizieren:

| Selector                                 | Title         | Type   |
| ---------------------------------------- | ------------- | ------ |
| `"141e0927-62b3-4e76-8398-ad82d20f397f"` | machine\_uuid | String |
| `"Kiefel"`                               | Machine       | String |

### Weitere Maschinen

Wiederhole die Query für jede Maschine der Abteilung (Macchi, Alpine, Reifenhäuser, W\&H Varex). Ändere jeweils die UUID in der URL und in den Computed Columns.

### Transformationen

1. **Merge** bringt alle Queries in eine gemeinsame Tabelle
2. **Organize fields** blendet `machine_uuid` aus und benennt die Spalten um (productivity → OEE, availability → Availability usw.)

### Visuelle Gestaltung

Konfiguriere die Tabelle unter **Standard options**:

* **Unit**: Percent (0.0-1.0)
* **Color scheme**: Continuous - RdYlGr
* **Min**: 0, **Max**: 1

Erstelle **Field Overrides** für die Spalten OEE, Availability, Performance und Quality:

* **Cell type**: Gauge (basic, color display)

Aktiviere den **Table Footer** mit Calculation **Mean** für die Abteilungsdurchschnitte.

Füge unter **Value mappings** einen Eintrag hinzu:

* **Type**: Special > Null
* **Display text**: NO DATA
* **Color**: Text

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-fcc7e2b577f1a8850cc8495494d7fa345ed449c7%2Fgrafana-oee-table-01.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Nicht alle Maschinen liefern Qualitätsdaten. Im Demo-Account ist Quality für alle Maschinen 1 (100 %). Das `null`-Mapping sorgt dafür, dass Maschinen ohne Daten im gewählten Zeitraum "NO DATA" statt eines Fehlers zeigen.
{% endhint %}

***

## Maschinenaktivität (State Timeline)

Die State Timeline zeigt auf einen Blick, wann welche Maschine in Produktion war und wann sie stillstand.

### ENLYZE-Datenquelle verwenden

Erstelle ein **State Timeline**-Panel mit der **ENLYZE**-Datenquelle (nicht ENLYZE API). Erstelle für jede Maschine eine Query und wähle jeweils die Variable **Durchsatz** aus.

Konfiguriere **Field Overrides** für jede Query (nach **Frame refID**), um die Maschine als Display Name anzuzeigen (z.B. "Kiefel", "Macchi" usw.).

### Value Mappings

Erstelle zwei Range-Mappings, um den Durchsatzwert in einen Betriebsstatus umzuwandeln:

| Von   | Bis   | Text          | Farbe |
| ----- | ----- | ------------- | ----- |
| -1000 | 1     | STILLSTAND    | Rot   |
| 1     | 10000 | IN PRODUKTION | Grün  |

Die Logik: Liegt der Durchsatz bei 0 oder nahe 0, steht die Maschine. Liegt er über 1, produziert sie.

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-05c8cb683cb24a7c10cc45aec79acc559d880c74%2Fgrafana-oee-state-timeline-01.png?alt=media" alt=""><figcaption></figcaption></figure>

***

## Abteilungsübersicht

Vier Stat-Panels zeigen die aggregierten OEE-Kennzahlen der gesamten Abteilung: OEE, Availability, Performance und Quality.

### OEE und Availability (Mittelwert)

Erstelle ein **Stat**-Panel für den Department OEE. Konfiguriere 5 Queries (eine pro Maschine) mit denselben POST-Requests wie in der Tabelle, aber mit anderem Root Selector und Columns:

* **Root Selector**: `$.data`
* **Column**: `productivity.score` als "OEE" (Number)

Füge eine **Merge**-Transformation hinzu. Unter **Value options** > **Calculation** wähle **Mean**.

Konfiguriere:

* **Color mode**: Background
* **Unit**: Percent (0.0-1.0)
* **Thresholds**: Rot (Basis), Gelb (0.6), Grün (0.8)
* **Value Mapping**: `null` → "NO DATA"

Erstelle ein zweites Panel für **Availability** nach dem gleichen Muster, mit Column `availability.score`.

### Performance und Quality (gewichtet)

Für Performance und Quality reicht ein einfacher Mittelwert nicht aus. Wenn Maschinen unterschiedlich lange laufen, müssen die Werte nach Availability gewichtet werden.

**Formel:**

$$
\text{Dept. Performance} = \frac{\sum\_{i} \text{Availability}\_i \times \text{Performance}*i}{\sum*{i} \text{Availability}\_i}
$$

Erstelle ein **Stat**-Panel für Performance. Die 5 Queries verwenden einen JSONata Root Selector:

```
$map($.data, function($v) {
  {
    "avail": $v.availability.score,
    "weighted_perf": $v.availability.score * $v.performance.score
  }
})
```

Jede Query liefert die Availability und das gewichtete Produkt (Availability x Performance).

**Transformationen:**

1. **Merge** fasst alle Queries zusammen
2. **Reduce** mit Mode **Reduce fields** und Calculation **Sum** summiert beide Spalten
3. **Add field from calculation** mit Mode **Binary operation**: `weighted_perf / avail`, Alias "Performance", **Replace all fields** aktiviert

Für **Quality** folge dem gleichen Muster mit diesem Root Selector:

```
$map($.data, function($v) {
  {
    "avail": $v.availability.score,
    "weighted_quality": $v.availability.score *
      ($exists($v.quality) ? $v.quality.score : 1)
  }
})
```

Der `$exists`-Fallback stellt sicher, dass Maschinen ohne Qualitätsdaten als Quality = 1 (100 %) gewertet werden.

<figure><img src="https://3556205377-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fn6Jn6Re8NNPSKD1jGZyL%2Fuploads%2Fgit-blob-0162b03e04e816e1ca810233d35a2d6a70137177%2Fgrafana-oee-dept-summary-01.png?alt=media" alt=""><figcaption></figcaption></figure>

{% hint style="info" %}
Die gewichtete Aggregation stellt sicher, dass Maschinen mit hoher Verfügbarkeit stärker in die Abteilungskennzahlen einfließen als Maschinen, die kaum liefen. Mehr zur OEE-Berechnung findest du in der [ENLYZE-Dokumentation](https://docs.enlyze.com).
{% endhint %}

***

## Tipps

* **10-Minuten-Variablen**: Die versteckten `start`- und `end`-Variablen funktionieren in jedem Dashboard, das den `productivity-metrics`-Endpoint nutzt. Kopiere sie bei Bedarf in andere Dashboards.
* **production-runs vs. productivity-metrics**: Verwende `production-runs` (GET, [Tutorial 4.2](https://docs.enlyze.com/integrations/grafana/advanced-api/02-api-queries)) für OEE pro Auftrag. Verwende `productivity-metrics` (POST) für aggregierte Schicht- oder Tagesberichte.
* **422 Unprocessable Entity**: Die Zeitwerte sind nicht korrekt ausgerichtet. Prüfe die versteckten Variablen und stelle sicher, dass der Body content type auf `application/json` steht.
* **Einzelne Maschine**: Für ein Dashboard mit Live-Status einer einzelnen Maschine siehe [Produktionsstatus-Dashboard](https://docs.enlyze.com/integrations/grafana/production-dashboards/04-production-status).

***

## Nächste Schritte

* [**Stillstandsauswertung**](https://docs.enlyze.com/integrations/grafana/production-dashboards/06-downtime-reporting) — Maschinenstillstände analysieren und visualisieren
