# 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="/files/EvGTWGyLWW3jXO4QqlBv" 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](/integrations/grafana/advanced-api/02-api-queries.md) und [Variablen](/integrations/grafana/advanced-api/03-variables.md) 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="/files/EvGTWGyLWW3jXO4QqlBv" 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="/files/SbwATNCmliaTb13zqjSH" 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="/files/7A8702Z0JB56RYVTS6u6" 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="/files/JAhFeWQNCc6P001NwpzZ" 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="/files/byok5D3CjD9ryA1QiD08" 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](/integrations/grafana/advanced-api/02-api-queries.md)) 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](/integrations/grafana/production-dashboards/04-production-status.md).

***

## Nächste Schritte

* [**Stillstandsauswertung**](/integrations/grafana/production-dashboards/06-downtime-reporting.md) — Maschinenstillstände analysieren und visualisieren


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.enlyze.com/integrations/grafana/production-dashboards/05-oee-dashboard.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
