---
title: "Einen Stream transformieren (experimentell)"
product: "LumiScan Calibration"
component: "lumiscan-calibration-de"
version: "2.0.0"
display_version: "2.0.0.8"
latest_version: true
language: "de"
url: "https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html"
---

# Einen Stream transformieren (experimentell) – LumiScan Calibration

> [!IMPORTANT]
> Der Stream ist **experimentell** (ab v2.1.0). Seine Knoten und Einstellungen können sich noch ändern. Nutzen Sie ihn zur Evaluierung, und prüfen Sie ihn mit Ihrem Aufbau, bevor Sie sich darauf verlassen.

Die Funktion `transform` rechnet die Punkte um, die Ihr Programm schreibt, ein Request nach dem anderen. Ein Stream funktioniert umgekehrt. Das Modul abonniert einen Data-Layer-Knoten, seine **Quelle**, und transformiert **jedes Update** davon, sobald es eintrifft. Die Quelle wird in zwei Teilen benannt: dem **Ordner**, in dem sie liegt (`address`), und dem **Knoten** in diesem Ordner (das `node` des Schritts). Das Modul veröffentlicht das Ergebnis auf `<module name>/control/subscribe/output/stream/points`, mit der Lineage des Updates - woher seine Punkte stammen - und auf `<module name>/control/subscribe/output/stream/plain-points` als einfaches Array, und Ihr Programm abonniert einen davon.

Ein Stream kann seine Punkte auf zwei Arten lesen. Der Schritt in der Liste `onEntry` des Abschnitts `stream` des Moduls gibt an, welche:

- **plain** (kein Schritt oder ein `plain`-Schritt): Der Knoten enthält die Punkte selbst, als ein Array von Zahlen: `x0,y0,x1,y1,…` oder `x0,y0,z0,x1,y1,z1,…`. Ein SPS-Array ist eine typische Quelle.
- **remap** (ein `remap`-Schritt): Der Knoten enthält das Ergebnis einer anderen Anwendung als FlatBuffer - standardmäßig das Bild-Stream-Ergebnis von LumiScan AI Object Matching. Der Schritt benennt die Liste der Elemente darin und die Felder, die das `x` und `y` jedes Elements enthalten.

In beiden Fällen veröffentlicht das Modul ein Tripel pro Punkt, `x,y,z`, in der Reihenfolge der Quelle (siehe [Die Punkte](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#points)). Ein Plain-Stream kann die Punkte eines anderen Moduls lesen, sodass sich Module verketten lassen (siehe [Module verketten](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#chaining)).

Ein Modul hat eine Quelle, die mit der einen Lookup-Tabelle des Moduls transformiert wird. Um zwei Quellen zu transformieren, verwenden Sie zwei Module - auch für zwei Quellen im Bild derselben Kamera, die sich dann dieselbe Lookup-Tabellendatei teilen.

> [!NOTE]
> Ein Remap-Stream transformiert jedes Ergebnis seiner Quelle, unabhängig davon, von welcher Kamera es stammt: Ein Filter liest Felder der Elemente, nicht die Lineage des Ergebnisses. Um die Ergebnisse mehrerer Kameras mit der eigenen Tabelle jeder Kamera zu transformieren, geben Sie jeder Kamera ein eigenes AI-Object-Matching-Modul und jedem davon ein Calibration-Modul.

## Einen Stream konfigurieren {#configuring}

Jedes Modul hat die Stream-Knoten, und jedes Modul kann streamen. Der Abschnitt `stream` seiner Modul-Konfigurationsdatei benennt die Quelle und enthält den Stream-Modus. Seine Liste `onEntry` enthält die Schritte, die jedes Update der Quelle lesen. Ein Modul, dessen Datei keinen Abschnitt `stream` hat, streamt plain, und `control/subscribe/execute` legt den Abschnitt an, wenn es die Quelle speichert.

Der Abschnitt `stream` hat in jeder LumiScan-Anwendung, die einen Stream abonniert, dieselben allgemeinen Schlüssel. Nur die Schritte unterscheiden sich:

```json
"stream": {
    "enable": true,
    "address": "hdvisionsystems/lumiscan-ai-object-matching/example/control/subscribe/output/stream",
    "queueSize": 64,
    "keepLatestOutput": false,
    "onEntry": [
        { "kind": "remap", "node": "result", "array": "instances",
          "x": "oriented_bounding_box.center_x", "y": "oriented_bounding_box.center_y" }
    ]
}
```

| Schlüssel | Erforderlich | Bedeutung |
| --- | --- | --- |
| `enable` | Nein | Stream-Modus: `true` lässt den Stream ab dem Start der Anwendung laufen, ohne dass jemand `control/subscribe/execute` aufruft. Benötigt eine `address`. Weggelassen oder `false`: Der Stream ist aus, bis `control/subscribe/execute` ihn einschaltet. |
| `address` | Mit `enable` | Der Ordner des Knotens, der abonniert werden soll; das `node` des Schritts benennt den Knoten darin. Sie wird genau so verwendet, wie sie geschrieben ist: Die Groß-/Kleinschreibung bleibt erhalten, was ein SPS-Symbol benötigt. Für einen Plain-Stream muss der Knoten ein float32- oder float64-Array enthalten, für einen Remap-Stream einen FlatBuffer des `schema` des Schritts. Weggelassen oder leer, um zur Laufzeit zu abonnieren. Eine Adresse, die mit dem Knoten des Schritts endet - der Knoten selbst, nicht sein Ordner - wird mit dem noch einmal angehängten Knoten abonniert, was fehlschlägt: Das Protokoll warnt und nennt den Ordner, der zu schreiben ist. |
| `tag` | Nein | Das Tag auf den Punkten, die das Modul veröffentlicht: Der Schritt des Moduls in ihrer Lineage trägt es, sodass ein nachfolgender Client sieht, welches Modul sie transformiert hat. Weggelassen oder leer: der Modulname. Siehe [Die Lineage der Punkte](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#lineage). |
| `queueSize` | Nein | Wie viele Updates auf die Transformation warten dürfen: 1 bis 4096, standardmäßig 64. Siehe [Die Warteschlange](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#queue). |
| `keepLatestOutput` | Nein | `true` schreibt die Punkte jedes Updates zusätzlich in die normale Ausgabe des Moduls. Siehe [Die neueste Ausgabe behalten](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#keep-latest-output). Standardmäßig `false`. |
| `onEntry` | Nein | Die Schritte, die auf jedes Update bei seinem Eintreffen angewendet werden, der Reihe nach, jeder ein Objekt mit einem `kind`: ein [`plain`](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#plain)- oder ein [`remap`](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#remap)-Schritt. Derzeit wird ein Schritt verarbeitet; eine Liste mit mehreren ist fehlerhaft. Weggelassen oder leer: ein Plain-Stream mit 2 Zahlen pro Punkt. |
| `onExit` | Nein | Reserviert für Schritte, die auf die Punkte angewendet werden, bevor sie veröffentlicht werden. Wird noch nicht gelesen: Das Modul protokolliert eine Warnung, und die Schritte werden nicht angewendet. |

- Das Hinzufügen, Ändern oder Entfernen des Abschnitts `stream` wird mit einem `reset` des Moduls wirksam. Ein Neustart der Anwendung ist nicht nötig.
- Ein Reset, der ändert, wie die Quelle gelesen wird - ein Schritt von `onEntry` -, startet den Stream im nächsten Zyklus des Moduls neu, etwa 100 ms später. Ein Reset, der nur `enable`, `address`, `tag`, `queueSize` oder `keepLatestOutput` ändert, startet nichts neu: Der laufende Stream folgt.
- Der Abschnitt enthält auch den Stream-Modus und die Quelle, wie sie zur Laufzeit gesetzt wurden: `control/subscribe/execute` und `control/unsubscribe/execute` speichern sie darin. Siehe [In der Modul-Konfigurationsdatei gespeichert](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#kept).
- Ein fehlerhafter Abschnitt `stream` ist eine fehlerhafte Moduldatei: Das Modul meldet Status `-2` (`invalidSetting`) und transformiert nichts, bis er korrigiert und das Modul zurückgesetzt ist. Fehlerhaft sind zum Beispiel Stream-Modus ohne Quelle (`enable` auf `true` gesetzt und keine `address`), ein Schritt mit einem anderen `kind` und ein Schlüssel eines Schritts - `kind`, `node`, `components`, `schema`, `rootType`, `array`, `x`, `y` oder `filters` -, der oben im Abschnitt statt in `onEntry` steht. Das Protokoll nennt den Schlüssel.

Ein älterer Schlüssel `subscriptions`, der mehrere Quellen nach Tag benannte, wird nicht mehr gelesen: Das Modul protokolliert eine entsprechende Warnung. Übertragen Sie seine eine Quelle nach `address` - ihren Ordner - und in das `node` des Schritts.

Um zu sehen, was eine Quelle enthalten darf, lesen Sie `hdvisionsystems/lumiscan-calibration/system/meta/subscription-schema`: siehe [Was eine Quelle enthalten darf](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#subscription-schema).

### Plain: Arrays von Punkten {#plain}

```json
"stream": {
    "enable": true,
    "address": "plc/app/Application/sym/GVL_Vision",
    "onEntry": [
        { "kind": "plain", "node": "aPixelPoints", "components": 3 }
    ]
}
```

Ein Stream ohne Schritt - kein `onEntry` oder ein leeres - ist plain, mit 2 Zahlen pro Punkt, und liest den Knoten `points`: die Punkte eines anderen Calibration-Moduls (siehe [Module verketten](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#chaining)). Ein `plain`-Schritt ist für einen anderen Knoten nötig, etwa ein SPS-Array, oder für 3 Zahlen pro Punkt.

| Schlüssel des Schritts | Erforderlich | Bedeutung |
| --- | --- | --- |
| `kind` | Ja | `"plain"` |
| `node` | Nein | Der Knoten des Ordners, der das Array enthält, zum Beispiel ein SPS-Symbol, `aPixelPoints`. Standardmäßig `points`. |
| `components` | Nein | Die Zahlen pro Punkt: `2` für `x,y` (der Standard), `3` für `x,y,z`. Die Lookup-Tabelle bildet ein Pixel ab, ein `z` wird daher ignoriert. Ein Array, dessen Länge keine ganze Anzahl von Punkten ergibt, wird nicht transformiert und in `statistics/failed-updates` gezählt. |

### Remap: Punkte aus dem Ergebnis einer anderen Anwendung {#remap}

```json
"stream": {
    "address": "hdvisionsystems/lumiscan-ai-object-matching/<module name>/control/subscribe/output/stream",
    "onEntry": [
        {
            "kind": "remap",
            "node": "result",
            "array": "instances",
            "x": "oriented_bounding_box.center_x",
            "y": "oriented_bounding_box.center_y",
            "filters": [
                { "field": "class_index", "anyOf": [0, 2] },
                { "field": "score", "min": 0.5 }
            ]
        }
    ]
}
```

Dieses Beispiel transformiert den Mittelpunkt jeder Box, die AI Object Matching in seinem Bild-Stream findet, für die Klassen `0` und `2` mit einem Score von mindestens `0.5`. Es liest den Knoten `result` des Stream-Ordners des AI-Moduls, einen FlatBuffer des Standardschemas `stream_detections`.

Ein Stream-Ergebnis wie dieses enthält zwei Dinge: seine `lineage` und das Ergebnis selbst - dasselbe Ergebnis, das die Anwendung in ihre normale Ausgabe schreibt. Der Schritt liest innerhalb des Ergebnisses, `array` ist also der Pfad wie in der normalen Ausgabe: `instances`, die Liste der Funde von AI Object Matching. Die Punkte, die das Modul veröffentlicht, tragen die Lineage des AI-Ergebnisses - das Kamerabild, auf dem es inferiert wurde - mit dem eigenen Schritt des Moduls ergänzt.

Das Beispielmodul, das die Anwendung mitbringt, `example`, hat einen solchen Schritt ohne Filter. Es liest das Beispielmodul von AI Object Matching, ebenfalls `example`, von seinem Start an. Siehe [Modul-Konfigurationsdatei](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/module-configuration-file.html).

| Schlüssel des Schritts | Erforderlich | Bedeutung |
| --- | --- | --- |
| `kind` | Ja | `"remap"` |
| `node` | Nein | Der Knoten des Ordners, der den FlatBuffer enthält. Standardmäßig `result`: das Stream-Ergebnis einer LumiScan-Anwendung in ihrem Ordner `control/subscribe/output/stream`. |
| `schema` | Nein | Welchen FlatBuffer der Knoten enthält; standardmäßig `stream_detections`, das Stream-Ergebnis von AI Object Matching. Entweder der Name eines Schemas, das die Anwendung mitbringt, oder der Pfad einer kompilierten Schemadatei (`.bfbs`), relativ zur Modul-Konfigurationsdatei. Die mitgelieferten Schemata, jeweils mit ihrem `array`:<br><br>\* die Stream-Ergebnisse (Knoten `result`): `stream_detections` (AI Object Matching, `instances`), `stream_template_matches` (Template Finder, `matches`), `stream_object_matches` (Object Finder, `matches`), `stream_areas` (Area Detector, `blobs`), `stream_codes` (die Code Reader, `results`), `stream_texts` (Text Reader, `words`); \* die normalen Ausgaben: `object_detection` (`output/result` von AI Object Matching, `instances`), `template_match_result`, `object_find_result`, `area_detect_result`, `code_detect_result` und `text_read_result`, mit denselben Listen; \* die Punkte eines anderen Calibration-Moduls: `stream_points` (Knoten `points`, `array` `points`, `x` und `y` mit den Namen `x` und `y`). |
| `rootType` | Nein | Die Tabelle an der Wurzel des Buffers, mit ihrem Namespace, zum Beispiel `hdv.object_detection2d.Result`. Weglassen, um den eigenen Root-Typ des Schemas zu verwenden. |
| `array` | Nein | Der Pfad zur Liste, deren Elemente die Punkte sind, zum Beispiel `instances`. In einem Stream-Ergebnis beginnt er innerhalb des Ergebnisses, wie in der normalen Ausgabe. Weggelassen: keine Liste - das Ergebnis selbst ist das eine Element, und jedes Update liefert einen Punkt, oder keinen, wenn ein Filter es ablehnt. Siehe [Ein Punkt pro Update](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#one-point). |
| `x`, `y` | Ja | Der Pfad innerhalb eines Elements zu seinem `x` und seinem `y`, zum Beispiel `oriented_bounding_box.center_x`. |
| `filters` | Nein | Nur die Elemente, die jeden Filter passieren, werden zu Punkten. Jeder Filter hat ein `field` (ein Pfad innerhalb eines Elements zu einer Zahl) und mindestens eines von `anyOf` (eine Liste von Werten, von denen das Feld einem gleichen muss), `min` und `max` (einschließende Grenzen). Ein Integer-Feld wird als Integer verglichen. |

Ein Pfad ist eine Liste von Feldnamen, verbunden mit `.`, so wie das Schema sie benennt. Jeder Schritt außer dem letzten muss zu einer Struktur führen, der letzte zu einer Zahl. Das Modul prüft jeden Pfad beim Start des Streams gegen das Schema. Passt einer nicht, startet der Stream nicht, und das Protokoll sagt, welcher Schritt falsch ist. Zwei Fehler werden mit der Korrektur genannt:

- ein `array`, das mit dem eigenen Feld des Stream-Ergebnisses beginnt, `result.instances`: Das Ergebnis wird bereits gelesen, schreiben Sie `"array": "instances"`;
- kein `array`, während `x` oder `y` nur in den Elementen einer Liste des Ergebnisses zu finden ist: Benennen Sie die Liste, zum Beispiel `"array": "instances"`.

#### Ein Punkt pro Update {#one-point}

Ein Ergebnis, das keine Liste ist - eine Pose, eine Position -, liefert einen Punkt pro Update. Lassen Sie `array` weg, und benennen Sie die Felder innerhalb des Ergebnisses:

```json
{ "kind": "remap", "schema": "my_result.bfbs", "node": "result", "x": "pose.x", "y": "pose.y" }
```

Die Filter gelten für dieses eine Element: Ein Update, das sie nicht passiert, wird ohne Punkte veröffentlicht.

Gut zu wissen:

- Ein Feld, das das Element nicht setzt, wird als sein Standardwert im Schema gelesen, wie bei jedem FlatBuffer-Reader.
- Fehlt eine Struktur auf einem Pfad in einem Element, wird der Punkt als `NaN,NaN,NaN` veröffentlicht, und ein Filter auf diesem Pfad lehnt das Element ab.
- Ein Update, das kein gültiger Buffer des angegebenen Typs ist, wird nicht transformiert. Es wird in `statistics/failed-updates` gezählt, und das erste wird in das Protokoll geschrieben.

## Adressen {#addresses}

Alle folgenden Adressen beginnen mit `hdvisionsystems/lumiscan-calibration/<module name>/`. Jedes Modul hat sie. Der Stream folgt dem Aufbau einer Execute-Methode: `control/subscribe` besitzt seine Eingaben und Ausgaben.

**Tabelle 1. Methoden**

| Adresse | Bedeutung |
| --- | --- |
| `control/subscribe/execute` | Prüft alle `control/subscribe/input/…` gemeinsam, speichert sie in der Modul-Konfigurationsdatei und wendet sie dann an: Der Stream folgt im nächsten Zyklus des Moduls, ohne Neustart. Siehe [Zur Laufzeit abonnieren](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#subscribing). |
| `control/unsubscribe/execute` | Entfernt die Quelle, die `control/unsubscribe/input/address` nennt, was auch den Stream-Modus beendet, und speichert das. |

**Tabelle 2. Eingaben**

| Adresse | Typ | Beim Start | Bedeutung |
| --- | --- | --- | --- |
| `control/subscribe/input/enable` | bool | `stream.enable` oder `false` | `true` lässt den Stream laufen; `false` stoppt ihn. |
| `control/subscribe/input/address` | string | `stream.address` oder leer | Der Ordner des Knotens, der abonniert werden soll, genau wie geschrieben. Das `node` des Schritts benennt den Knoten darin. |
| `control/subscribe/input/tag` | string | `stream.tag` oder leer | Das Tag auf den Punkten, die das Modul veröffentlicht, in ihrer Lineage. Leer: der Modulname. |
| `control/subscribe/input/queue-size` | uint32 | `stream.queueSize` oder 64 | Wie viele Updates auf die Transformation warten dürfen, 1 bis 4096. Siehe [Die Warteschlange](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#queue). |
| `control/subscribe/input/keep-latest-output` | bool | `stream.keepLatestOutput` oder `false` | `true` schreibt die Punkte jedes Updates zusätzlich in die normale Ausgabe. Siehe [Die neueste Ausgabe behalten](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#keep-latest-output). |
| `control/unsubscribe/input/address` | string | leer | Die Quelle, die `control/unsubscribe/execute` entfernt: die Adresse auf `setting/stream/address`. |

Das Schreiben einer Eingabe ändert für sich allein nichts: Nur `control/subscribe/execute` wendet die Eingaben an. Sie beginnen mit dem, was das Modul anwendet, und ein Reset setzt sie wieder auf die Werte der Datei, sodass ein Client eine Eingabe ändern und die Methode aufrufen kann.

**Tabelle 3. Ausgaben**

| Adresse | Typ | Bedeutung |
| --- | --- | --- |
| `control/subscribe/output/stream/active` | bool | `true`, solange der Stream läuft. |
| `control/subscribe/output/stream/points` | flatbuffers (`stream_points`) | Die Punkte jedes Updates, transformiert, mit ihrer Lineage. Abonnieren Sie ihn, um jedes zu erhalten. Ein Lesen liefert das neueste. Siehe [Die Punkte](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#points). |
| `control/subscribe/output/stream/plain-points` | float32-Array | Dieselben Punkte als einfaches Array, `x0,y0,z0,x1,y1,z1,…`, ohne die Lineage: für einen Client, der keinen FlatBuffer lesen kann, etwa eine SPS. |
| `control/subscribe/output/stream/statistics/…` | uint64 | Zähler des aktuellen oder letzten Streams, siehe [Die Statistik lesen](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#statistics). |

**Tabelle 4. Einstellung**

| Adresse | Typ | Bedeutung |
| --- | --- | --- |
| `setting/stream/enable`, `setting/stream/address`, `setting/stream/tag`, `setting/stream/queue-size`, `setting/stream/keep-latest-output` | wie die Eingaben | Was das Modul jetzt anwendet, wie im Abschnitt `stream` gespeichert. |
| `setting/settings-hash` | string | Der Hash dessen, womit die Punkte transformiert werden, als 16 Hexadezimalziffern. Siehe [Der Einstellungs-Hash](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#settings-hash). |

Sofern `keep-latest-output` nicht gesetzt ist, werden die Punkte des Streams nur auf `control/subscribe/output/stream/points` und `control/subscribe/output/stream/plain-points` veröffentlicht, und die normalen Ausgaben behalten das Ergebnis des letzten Transforms auf Anfrage.

### Was eine Quelle enthalten darf {#subscription-schema}

`hdvisionsystems/lumiscan-calibration/system/meta/subscription-schema` (string, schreibgeschützt, experimentell) beschreibt, was der Knoten, den ein Modul abonniert, enthalten darf, als JSON Schema (draft-04) der JSON-Form des Knotens. Es führt drei Arten auf, von denen jede passt (`anyOf`):

- ein einfaches Array von Zahlen - ein `plain`-Schritt oder keiner;
- ein `stream_points` - die Punkte eines anderen Calibration-Moduls, plain gelesen;
- ein `stream_detections` - das Stream-Ergebnis von AI Object Matching, gelesen von einem `remap`-Schritt mit dem Standardschema.

Es ist für jedes Modul gleich und sagt daher nicht, welche Art ein Modul liest: Das sagt sein `onEntry`-Schritt. Die Beschreibungen darin stammen aus den FlatBuffer-Schemata. Ein Feld eines FlatBuffers wird so gezeigt, wie das JSON von `flatc --strict-json` es darstellt: ein Enum über seinen Namen, eine Struktur als Objekt.

## Einen Stream ausführen {#running}

1. Abonnieren Sie `control/subscribe/output/stream/points` oder `control/subscribe/output/stream/plain-points` für das einfache Array.
2. Schreiben Sie `true` auf `control/subscribe/input/enable` - und die Adresse der Quelle, falls das Modul noch keine hat - und `true` auf `control/subscribe/execute`. Warten Sie, bis `control/subscribe/output/stream/active` `true` ist. Ab dann werden Updates der Quelle transformiert.
3. Zum Stoppen schreiben Sie `false` auf `control/subscribe/input/enable` und `true` auf `control/subscribe/execute`.

Gut zu wissen:

- Der Stream-Modus wird im Abschnitt `stream` gespeichert, bevor er wirkt: Nach einem Neustart der Anwendung streamt das Modul wieder, oder nicht, wie zuvor. Niemand muss die Methode erneut aufrufen.
- Der Stream läuft nur, solange die Lizenz vorhanden ist.
- Kann der Stream nicht starten, weil ein Remap-Pfad nicht zum Schema passt, bleibt `control/subscribe/output/stream/active` `false`, und der Grund wird in das Protokoll geschrieben. Es wird nicht erneut versucht, bis das Modul zurückgesetzt oder der Stream-Modus mit `control/subscribe/execute` aus- und wieder eingeschaltet wird.
- Eine Quelle, die nicht abonniert werden kann, zum Beispiel ein Knoten, der noch nicht existiert, hindert den Stream nicht am Laufen: Es wird alle 5 Sekunden erneut versucht, und der erste Fehlschlag wird in das Protokoll geschrieben.
- Das Transformieren auf Anfrage funktioniert weiter, während der Stream läuft: Der `transform`-Handshake und `control/transform/execute` werden wegen des Streams nie abgelehnt.
- Ein `reset` ersetzt die Lookup-Tabelle sofort, auch für den laufenden Stream: Das nächste Update wird mit der neuen Tabelle transformiert. Schlägt der Reset fehl, hat das Modul keine Tabelle, und der Stream veröffentlicht `NaN` für jeden Punkt, bis ein Reset gelingt.

## Die Warteschlange {#queue}

Die Updates der Quelle warten in einer Warteschlange, in der Reihenfolge ihres Eintreffens, und werden nacheinander transformiert. Ein Update wird nur verworfen, wenn die Warteschlange voll ist - `queueSize` Updates, standardmäßig 64, warten bereits: Dann geht das älteste verloren und wird in `statistics/skipped-updates` gezählt.

Eine größere `queueSize` übersteht längere Lastspitzen, ohne ein Update zu verwerfen; aktualisiert die Quelle aber über längere Zeit schneller, als das Modul transformiert, sind die veröffentlichten Punkte älter: bis zu `queueSize` Updates im Rückstand. Eine kleinere hält die Punkte aktuell und verwirft mehr.

Eine geänderte Warteschlangengröße gilt sofort, ohne Neustart des Streams. Schrumpft sie unter die Zahl der bereits wartenden Updates, werden die ältesten davon verworfen und als übersprungen gezählt.

## Zur Laufzeit abonnieren {#subscribing}

Eine SPS oder ein anderer Client ändert die Quelle und den Stream-Modus, während die Anwendung läuft, mit oder ohne laufenden Stream:

1. Schreiben Sie die Eingaben, die sich ändern, auf `control/subscribe/input/…` - für eine andere Quelle `control/subscribe/input/address`, ihren Ordner (der Knoten darin bleibt der des Schritts); zum Starten oder Stoppen `control/subscribe/input/enable`; für ein anderes Tag auf den Punkten `control/subscribe/input/tag`.
2. Schreiben Sie `true` auf `control/subscribe/execute`.

Die Methode prüft alle Eingaben gemeinsam, speichert sie im Abschnitt `stream` und wendet sie dann an: `setting/stream/…` zeigt sie sofort, und ein laufender Stream folgt im nächsten Zyklus des Moduls. Eine andere Adresse ersetzt die Quelle - ein vorheriges Unsubscribe ist nicht nötig -, und die Updates, die die alte Quelle noch wartend hatte, werden verworfen und als übersprungen gezählt. Die Warteschlangengröße und `keep-latest-output` gelten, ohne erneut zu abonnieren, ebenso das Tag: Die ab dann veröffentlichten Punkte tragen es.

Um die Quelle zu entfernen, schreiben Sie ihre Adresse auf `control/unsubscribe/input/address` und `true` auf `control/unsubscribe/execute`. Das beendet auch den Stream-Modus und speichert `enable` als `false` ohne Adresse. Das Tag bleibt: Es markiert die Punkte des Moduls, nicht die Quelle. `control/subscribe/input/…` werden ebenfalls darauf gesetzt, sodass ein späteres `control/subscribe/execute`, das eine andere Eingabe ändert, die Quelle nicht zurückbringt. Ein `/` an einem der Enden einer Adresse spielt keine Rolle.

Die Methoden antworten wie folgt. Wo ein Ergebnis nur einen Namen hat, ist es der Name auf dem ctrlX Data Layer; LumiScan Keryx meldet dasselbe Ergebnis unter eigenem Namen, siehe [Ergebnisse eines Methodenaufrufs](https://docs.hdvisionsystems.com/de/lumiscan-keryx-de/1.0.0/status-codes.html#method-results).

| Ergebnis | Wann |
| --- | --- |
| `DL_OK` | Erledigt. |
| `DL_INVALID_VALUE` (ctrlX Data Layer), `Rejected` (LumiScan Keryx) | `control/subscribe`: `enable` ist `true` und die Adresse ist leer, oder die Warteschlangengröße liegt nicht zwischen 1 und 4096. `control/unsubscribe`: Die Adresse ist nicht die Quelle, oder es gibt keine Quelle. Nichts hat sich geändert. |
| `DL_CREATION_FAILED` | Die Änderung konnte nicht in der Modul-Konfigurationsdatei gespeichert werden: Die Datei konnte nicht gelesen oder geschrieben werden. Nichts hat sich geändert: Der Stream bleibt, wie er war. Die Antwort und das Protokoll nennen den Grund. |
| `DL_RESOURCE_UNAVAILABLE` | Die Einstellungen wurden auf `system/settings` bearbeitet, und die Anwendung wartet auf einen Neustart. |

## In der Modul-Konfigurationsdatei gespeichert {#kept}

Stream-Modus und Quelle sind Einstellungen des Moduls, im Abschnitt `stream` seiner Konfigurationsdatei. `control/subscribe/execute` und `control/unsubscribe/execute` speichern `enable`, `address`, `tag`, `queueSize` und `keepLatestOutput` dort, bevor sie sie anwenden: Können sie nicht gespeichert werden, antwortet die Methode mit `DL_CREATION_FAILED`, und nichts ändert sich. Nach einem Neustart abonniert das Modul dieselbe Quelle und startet, wenn `enable` `true` ist, seinen Stream sofort.

Das Speichern ändert nur diese Werte: Der Rest der Datei bleibt, wie er war, einschließlich Kommentaren und der Schritte von `onEntry`. Eine Datei ohne Abschnitt `stream` erhält einen, mit diesen Werten und ohne Schritt: ein Plain-Stream.

Ein Reset liest die Datei erneut ein und setzt die Eingaben darauf: Eine Änderung, die Sie von Hand in der Datei vornehmen, wird mit dem nächsten Reset wirksam.

## Die neueste Ausgabe behalten {#keep-latest-output}

Mit gesetztem `keep-latest-output` werden die Punkte jedes Updates zusätzlich in die normale Ausgabe des Moduls geschrieben, so wie ein Transform auf Anfrage sie schreibt: `output/x-coordinate-array`, `output/y-coordinate-array` und `output/z-coordinate-array`, `output/timestamp` und `output/package-identifier`, weitergezählt von den Transforms auf Anfrage. Ein Client, der die normale Ausgabe liest - zum Beispiel ein SPS-Programm, das für die Funktion `transform` geschrieben wurde -, sieht dort dann die neuesten Punkte des Streams. Ein Punkt, der nicht transformiert werden kann, ist auch dort `NaN`.

Es kostet eine Kopie der Punkte jedes Updates und ist deshalb standardmäßig aus. Die Punkte auf `control/subscribe/output/stream/points` werden zuerst veröffentlicht.

## Die Punkte {#points}

`control/subscribe/output/stream/points` enthält eine FlatBuffers-Tabelle vom Typ `stream_points`, auf dem Data Layer registriert als `types/hdvisionsystems/lumiscan-calibration/stream_points`. Ihr kompiliertes Schema ist als `bfbs/stream_points.bfbs` installiert.

| Feld | Bedeutung |
| --- | --- |
| `points` | Je ein `x`, `y`, `z` (float32) pro Punkt des Updates, in dessen Reihenfolge. |
| `invalid` | Wie viele der Punkte nicht transformiert werden konnten. |
| `lineage` | Woher das Update stammt, siehe [Die Lineage der Punkte](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#lineage). |

`control/subscribe/output/stream/plain-points` veröffentlicht dieselben Punkte als `x0,y0,z0,x1,y1,z1,…`, direkt nach `control/subscribe/output/stream/points`.

- Der i-te Punkt ist der i-te Punkt des Updates. Ein Punkt, der nicht transformiert werden kann, wird an seiner Stelle als `NaN` veröffentlicht, sodass die Reihenfolge erhalten bleibt. Ein Punkt kann nicht transformiert werden, wenn er außerhalb der Lookup-Tabelle liegt oder wenn das Modul keine Tabelle hat.
- Ein Update ohne Punkte wird ohne Punkte veröffentlicht.
- `z` ist der Wert der Lookup-Tabelle. Ein `z` in einer Plain-Quelle mit `components` `3` wird ignoriert.

> [!NOTE]
> Ein Punkt außerhalb der Lookup-Tabelle wird nur vom Stream als `NaN` veröffentlicht. Auf Anfrage liefert die Funktion `transform` für einen solchen Punkt einen Wert vom Rand der Tabelle, ohne Fehler, wie schon immer. Halten Sie die Punkte, die Sie anfragen, innerhalb des Bildes, für das die Tabelle erstellt wurde.

### Module verketten {#chaining}

`control/subscribe/output/stream/points` eines Moduls kann die Quelle eines Plain-Streams in einem anderen Modul sein. Das zweite Modul liest ihn als `x,y,z`-Punkte, unabhängig von seinem `components`, transformiert das `x` und `y` jedes Punkts und ignoriert das `z`. Seine Punkte tragen die Lineage des ersten Moduls mit seinem eigenen Schritt ergänzt. `control/subscribe/output/stream/plain-points` funktioniert ebenfalls als Quelle, mit `components` `3`, aber seine Punkte beginnen eine eigene Lineage.

### Die Lineage der Punkte {#lineage}

`lineage.steps` listet auf, woher die Punkte eines Updates stammen, Ursprung zuerst:

- Eine Quelle, die eine Lineage trägt - `control/subscribe/output/stream/result` von AI Object Matching, `control/subscribe/output/stream/points` eines anderen Moduls -, gibt ihre Schritte weiter: zuerst der Schritt der Kamera (`producer` die Adresse der Kamera, `tag` das Tag des Cam-Driver-Moduls, `run` und `sequence` die Stelle des Bildes im Stream der Kamera), dann der des AI-Moduls, mit dem Tag des AI-Moduls.
- Eine Quelle, die keine trägt - ein SPS-Array -, erhält einen für sie erzeugten Schritt, markiert als `synthesized`: Sein `producer` ist die Adresse des Knotens, Ordner und Knoten; er hat kein `tag`; seine `sequence` ist der eigene Zähler des Moduls seit dem Start des Streams. Auf LumiScan Keryx ist die `sequence` die Sequenznummer des Data Layers für das Update, wo es eine gibt, sonst der eigene Zähler des Moduls.
- Der eigene Schritt des Moduls kommt zuletzt: `producer` ist die Adresse des Moduls, `hdvisionsystems/lumiscan-calibration/<module name>`; `tag` das Tag des Moduls (`stream.tag`) oder der Modulname, wenn es keines hat; `run`, wann der Stream des Moduls gestartet wurde; `sequence` die Nummer des Updates im Lauf; `settings_hash`, siehe [Der Einstellungs-Hash](https://docs.hdvisionsystems.com/de/lumiscan-calibration-de/2.0.0/lumiscan-calibration/streams.html#settings-hash); `elapsed_us` die Mikrosekunden vom Eintreffen des Updates bis zur Veröffentlichung der Punkte.

Das `tag` jedes Schritts sagt, welches Modul das Update dort verarbeitet hat - der eigene Name des Moduls oder das Tag, das es erhalten hat. Geben Sie zwei gleichnamigen Modulen in verschiedenen Anwendungen, oder einem Modul, dessen Name wenig aussagt, ein Tag, das sagt, was es in Ihrer Linie tut.

Eine Quelle, die geändert wird, während der Stream läuft, behält den Lauf des Moduls: Die Updates der neuen Quelle werden weitergezählt, und der `producer` ihres ersten Schritts benennt sie.

### Der Einstellungs-Hash {#settings-hash}

`setting/settings-hash` und der `settings_hash` des Modulschritts identifizieren, womit die Punkte transformiert werden. Der Hash ändert sich, wenn sich eines davon ändert:

- die Einstellungen der Modul-Konfigurationsdatei - außer dem `name` des Transforms; `enable`, `address`, dem `node` seines Schritts, `tag`, `queueSize` und `keepLatestOutput` des Streams, die die Updates schalten, auswählen, takten und markieren, statt ihre Punkte zu ändern; und seinem `onExit`, das noch nicht gelesen wird;
- die Lookup-Tabelle (`relativePathToTiff`) nach ihrem Inhalt - eine neue Tabelle unter dem alten Dateinamen ist ein neuer Hash; und das `schema` eines Remap-Schritts nach seinem Inhalt - dasselbe Schema unter anderem Namen ist derselbe Hash;
- die Version der Anwendung.

Ein Reset liest sie erneut ein. Wenn ein Hash zum ersten Mal auftritt, schreibt das Modul die Einstellungen, für die er steht, nach `settings-history/<module name>-<hash>.json` im Konfigurationsordner der Anwendung, sodass ein Hash, der in der Lineage der Punkte gefunden wird, später nachgeschlagen werden kann.

> [!NOTE]
> Die Punkte tragen nur den Hash. Ein anderer Hash sagt, dass sich etwas geändert hat, womit sie transformiert werden, nicht was. Um es herauszufinden, vergleichen Sie die beiden Dateien in `settings-history`, die die Hashes benennen, von Hand.

## Die Statistik lesen {#statistics}

Die Zähler beginnen bei jedem Start des Streams bei null und werden etwa viermal pro Sekunde aktualisiert, solange er läuft. Alle Adressen beginnen mit `control/subscribe/output/stream/statistics/`.

| Zähler | Bedeutung |
| --- | --- |
| `elapsed-ms` | Millisekunden seit dem Start des Streams. Teilen Sie die anderen Zähler dadurch, um Raten zu erhalten. |
| `received-updates` | Updates der Quelle, die eingetroffen sind. |
| `transformed-updates` | Updates, die transformiert und auf `control/subscribe/output/stream/points` und `control/subscribe/output/stream/plain-points` veröffentlicht wurden. |
| `skipped-updates` | Updates, die unverarbeitet verworfen wurden, weil die Warteschlange voll war (`queueSize`, standardmäßig 64): Das Modul konnte mit der Quelle nicht Schritt halten. Das älteste wartende Update wird zuerst verworfen. Außerdem die Updates, die warteten, als die Quelle ersetzt wurde oder als die Warteschlangengröße unter ihre Zahl schrumpfte. |
| `failed-updates` | Updates, die nicht transformiert werden konnten: nicht der erwartete Typ oder die erwartete Länge, oder kein gültiger Buffer. Das erste wird in das Protokoll geschrieben. |
| `lost-updates` | Updates, die verworfen wurden, bevor sie eintrafen. Für eine Quelle, die eine Lineage trägt, ermittelt aus Lücken in der `sequence` des Schritts ihres Produzenten innerhalb eines Laufs. Auf LumiScan Keryx, für ein einfaches Array, aus Lücken in den Sequenznummern des Data Layers. Eine neue Quelle beginnt die Zählung neu. |
| `points` | Transformierte Punkte, alle Updates zusammen. |
| `invalid-points` | Davon die Punkte, die als `NaN` veröffentlicht wurden. |
| `transform-us` | Mikrosekunden, die alle Transformationen gedauert haben, summiert. Teilen Sie durch `transformed-updates` für den Mittelwert. |

## Was ein Stream empfängt {#data-layers}

`control/subscribe/output/stream/points` veröffentlicht jedes Update an seine Abonnenten. Was der Stream selbst empfängt, hängt vom Quellknoten ab:

| Data Layer | Quelle, die jedes Update veröffentlicht (zum Beispiel `control/subscribe/output/stream/result` von AI Object Matching oder `control/subscribe/output/stream/points` eines anderen Moduls) | Gewöhnlicher Knoten (zum Beispiel ein SPS-Array) |
| --- | --- | --- |
| ctrlX Data Layer (ctrlX OS) | Jedes Update, in Reihenfolge. | Der Wert etwa alle 100 ms. Änderungen dazwischen werden nicht gesehen. |
| LumiScan Keryx (PC) | Jedes Update, in Reihenfolge. | Jede **Änderung**: Dieselben Werte erneut zu schreiben ist kein Update. |

Auf dem ctrlX Data Layer wurde der Stream mit einer SPS-Quelle auf einem Gerät noch nicht gemessen.

Das Stream-Konzept aller LumiScan-Anwendungen ist unter [Streams (experimentell)](https://docs.hdvisionsystems.com/de/lumiscan-keryx-de/1.0.0/streams.html) beschrieben.
