Messreihen und Tabellen
In diesem Teil soll beschrieben werden, wie Messwerte aufgenommen werden können und ins Protokoll eingefügt werden. Dazu wollen wir das vorhergehende Skript um eine Messroutine erweitern, welche einen Parameter vom Benutzer abfragt. Als Ausgangspunkt soll folgendes Skript dienen:
import { Injectable } from 'injection-js';
import { Script, StepsService } from '@script';
@Injectable()
export class SkriptName extends Script {
constructor(private steps: StepsService) {}
public Run(): void {
this.steps.Add("Parameter 1", async () => {
// TODO Benutzerinteraktion anlegen
}, {});
}
}
Benutzerinteraktion
Zuerst wollen wir uns anschauen, wie von einem Kalibrierskript aus mit dem Kalibrierer kommuniziert werden kann.
Dazu dient die Klasse ScriptUI (UI = User-Interface, Benutzeroberfläche).
ScriptUI kapselt alle Funktionalitäten, die zur Kommunikation mit dem Nutzer notwendig sind und bietet eine einfache Programmierschnittstelle für Skriptentwickler an.
Zu den Funktionen der Benutzerinteraktion zählen:
PushStatusMessage()- Einseitige Statusinformation zum Benutzer senden, welche links im Hauptfenster dargestellt werden.PushCalibrationMessage()- Einseitige Benutzerinformation, welche rechts im FensterKalibrieranweisungdargestellt werden.FileUpload()- Dateiupload für z.B. Fremdprotokolle oder Bilder, welche im Folgenden durch das Skript benutzt werden können.ConfirmationBox()- Bestätigungsabfrage zum Benutzer. Kombiniert die KalibriScript Funktionen @bild, Text auf den Bildschirm Schreiben und @waitInputBox()- Fragt eine Eingabe vom Benutzer ab, z.B. Istwert. Während in KalibriScript diese InputBox nur innerhalb von Tabellen benutzt werden konnte, kann hier jederzeit Benutzereingabe angefordert werden.TextInputBox()- Fragt eine Eingabe vom Benutzer ab, allerdings ohne Umrechnung zu Zahlen in Textform. Nützlich z.B. für Seriennummern-Abgleich etc.YesNoBox()- Bestätigung ja/nein vom Benutzer anfordern - Analog zur alten Ja/Nein Box in KalibriScript
Alle Funktionen der Klasse ScriptUI erhalten als Parameter eine Liste von UIContent (Inhaltselementen, z.B. Text/Bild/Tabellen). Diese werden in der Kalibrierung alle untereinander in der gegebenen Reihenfolge angezeigt.
Hinweis: Alle Funktionen der Benutzerinteraktion außer PushStatusMessage sind asynchron, d.h. bei der Benutzung mit
awaitdas Ergebnis abwarten!
Ein einfaches Skript, welches den Benutzer einen Messaufbau vornehmen lässt, würde z.B. wie folgt aussehen:
import { Injectable } from 'injection-js';
// NEU: Zusätzliche Funktionsimporte von `delay` und `ScriptUI`
import { Script, StepsService, delay, ScriptUI } from '@script';
@Injectable()
export class SkriptName extends Script {
// NEU: wir speichern zusätzlich den Dienst zur Benutzerinteraktion in der Variable `ui`.
constructor(private steps: StepsService, private ui: ScriptUI) {
}
public Run(): void {
this.steps.Add("Parameter 1", async () => {
// Zuerst wollen wir dem Benutzer den Messaufbau vornehmen lassen:
// Dazu benutzen wir die Methode `this.ui.ConfirmationBox()`, welche eine Bestätigung vom Benutzer einfordert.
// Da diese Aktion im Hintergrund passiert, müssen wir ebenfalls mit `await` das Ergebnis der Aktion abwarten.
// ConfirmationBox gibt dem Benutzer die Auswahl zwischen "Weiter" und "Abbrechen", für den Fall, dass Abbrechen geklickt wird,
// wird die Ausführung des gesamten Makroschritts abgebrochen und man kann ihn wiederholen oder die Kalibrierung beenden.
await this.ui.ConfirmationBox(
"Messaufbau vornehmen", // Erster Parameter: Titel der Box
[ // Zweiter Parameter: Liste an Inhalt, Inhalt wird im UI untereinander in der gegebenen Reihenfolge angezeigt
{
kind: "text", message: `Bitte folgenden Messaufbau vornehmen und foldende Einstellungen an Gerät 2 vornehmen:
Einstellung Wellenlänge: 1310 nm
Einstellung Pegel: 10 dBm` // Textinhalt der Box. Ein String kann entweder einzeilig mit " oder ', alternativ mehrzeilig mit `. Zeilenumbrüche werden beibehalten.
},
{
kind: "image",
data: "MESSAUFBAU.PNG", // Zeige das Bild "MESSAUFBAU.PNG" im gleichen Ordner wie die Skriptdatei in der Benutzeroberfläche an. Achtung: Dateinahmen berücksichtigen Groß/Kleinschreibung.
}
],
);
// Wir wollen eine Benutzerinfo an den Benutzer geben, damit er sich nicht wundert, was gerade passiert, während wir z.B. eine langlaufende Messung im Hintergrund haben:
// Dazu nutzen wir `this.ui.PushStatusMessage`, was lediglich eine Information zum Anzeigen an die Benutzeroberfläche sendet.
await this.ui.PushStatusMessage([{ kind: 'text', message: `Achtung: Warte auf Standard und Messgerät (10s)` }]);
// Warte 10s ab.
await delay(10000);
const userInput = await this.ui.InputBox(
"Messwert erfassen", // Erster Parameter: Titel
[{ kind: "text", message: "Bitte lesen Sie den Messwert vom Prüfling ab und tragen ihn ein" }], // Zweiter Parameter: Inhaltselemente
"dBm", // 3. Parameter: Einheit des Messwerts, hier dBm
-21, // 4. Parameter: Minimum
-19, // 5. Parameter: Maximum
);
// in userInput steht jetzt der Zahlenwert, welchen der Nutzer ins Textfeld eingegeben hat.
// Im nächsten Schritt wollen wir diesen Zahlenwert jetzt ins Protokoll einfügen.
}, { });
}
}
Tabellen
Genau wie in KalibriScript enthält auch KalibriWare viele mächtige Funktionen um Messreihen teil-/vollautomatisiert aufzunehmen, Abweichungsberechnungen vorzunehmen etc.
Dabei wurde beim Entwurf der Programmierschnittstelle darauf geachtet, möglichst wenig Luft für Mehrdeutigkeiten zu lassen und eine Klasse immer nur mit einer Funktion zu versehen.
Dies folgt dem Unix-Designprinzip do one thing and do it well. (Mache eine Sache und mache sie gut.)
In KalibriScript wurde zwischen Sichtbaren und Unsichtbaren Tabellen unterschieden, zusätzlich konnten Tabellen auch als Datenquelle für Grafiken dienen. Im Gegensatz dazu wurden in KalibriWare diese Funktionen in mehrere Klassen aufgeteilt, um die Entwicklung zu erleichtern.
MeasurementSeries
Zuerst wollen wir uns die Klasse MeasurementSeries (Messreihe) anschauen. Die MeasurementSeries bildet die Grundlage jeder Messreihe. Sie enthält die Messdaten, sowie eine Definition der Spalten.
Um eine Messreihe zu erstellen, wird die Methode StepsService.createSeries() benutzt. Dabei gibt der Parameter die Spaltendefinition an.
Hinweis: MeasurementSeries werden nicht automatisch ins Protokoll eingefügt!
Eine beispielhafte Tabelle welche das Tabellenformat STIA? aus KalibriScript wiederspiegelt, kann z.B. folgendermaßen angelegt werden:
import { Injectable } from 'injection-js';
// NEU: Zusätzlicher Import von `ProtocolManager`, Interaktion mit dem Kalibrierprotokoll
import { Script, StepsService, delay, ScriptUI, ProtocolManager } from '@script';
// NEU: Zusätzlicher Import von `SeriesTemplates`, enthält vordefinierte Tabellenformate
import { SeriesTemplates } from '@script/table';
@Injectable()
export class SkriptName extends Script {
constructor(private steps: StepsService, private ui: ScriptUI, private proto: ProtocolManager) {
}
public Run(): void {
this.steps.Add("Parameter 1", async () => {
await this.ui.ConfirmationBox(
"Messaufbau vornehmen",
`Bitte folgenden Messaufbau vornehmen und foldende Einstellungen an Gerät 2 vornehmen:
Einstellung Wellenlänge: 1310 nm
Einstellung Pegel: 10 dBm`,
"MESSAUFBAU.PNG",
);
await this.ui.PushStatusMessage(`Achtung: Warte auf Standard und Messgerät (10s)`);
await delay(10000);
const userInput = await this.ui.InputBox(
"Messwert erfassen",
"Bitte lesen Sie den Messwert vom Prüfling ab und tragen ihn ein",
undefined,
"dBm",
-21,
-19,
);
// NEU: Messreihe erzeugen, dabei wird hier ein STIA? (? = Q in KalibriWare) Format benutzt.
const series = this.steps.createSeries(SeriesTemplates.STIAQ());
// NEU: Daten in Messreihe einfügen, manuell:
// s: Sollwert
// t: Toleranz, hier +- 1dBm
// i: Istwert, hier kopiert aus der vorherigen InputBox
series.addRow({ s: -20, t: Tolerance.Absolute(1), i: userInput });
// Wichtig: Nachdem alle Messungen hinzugefügt wurden, muss die Tabelle explizit ins Kalibrierprotokoll eingefügt werden:
// Dazu verwenden wir `ProtocolManager.addMeasurementSeries()`
this.proto.addMeasurementSeries(series);
}, { });
}
}
Damit haben wir jetzt eine Messreihe mit STIA?-Format, wobei die Abweichung und das entsprechende Statusfeld aus der Toleranz und dem Sollwert berechnet wird.
Einheiten in MeasurementSeries
Die Klasse MeasurementSeries kann (optional) die Einheiten der Zahlenwerte mit übergeben bekommen. In diesem Fall werden die Werte für die entsprechenden Spalten jeweils passend skaliert. Eine Tabellendefinition könnte z.B. so aussehen:
this.steps.createSeries({
s: Columns.TargetValue(100, Unit.mega("Ohm")), // Sollwertspalte in MOhm, Standardwert 100MOhm
t: Columns.Tolerance(100, Unit.one("Ohm")), // Toleranzspalte, hier standardmäßig symmetrische Toleranz von +- 100 Ohm
i: Columns.ActualValue(Unit.kilo("Ohm")), // Istwert in kOhm, z.b. weil das Messgerät den Istwert so anzeigt.
a: Columns.Deviation(Unit.kilo("Ohm")), // Abweichungsfeld in kOhm
q: Columns.Status(), // Statusfeld, dort kommt ok oder fehler rein
});
Damit weiß die Skriptumgebung, dass Toleranz und Sollwert in verschiedenen Einheiten sind und wird die Zahlenwerte vor dem Rechnen passend skalieren.
Tabellenformate
Aktuell existieren 7 verschiedene Spaltentypen:
| Name | Zugriff | Beschreibung |
|---|---|---|
| NOP | Columns.NOP() |
Datenspalte mit Rohdaten. Der Datentyp kann bei Erzeugung der Spalte angegeben werden und fließt nicht weiter in die Berechnung ein |
| QuestionBox | Columns.QuestionBox() |
Datenspalte, bei der automatisch der User über einen interaktiven Dialog einen Wert oder eine Bestätigung eingibt. Der Text der Fragebox wird aus einer NOP-Spalte mit dem Datentyp String dynamisch geladen (analog zum alten XQ-Format) |
| Sollwert | Columns.TargetValue() |
Sollwertspalte, kann über die Parameter mit einem Standardwert und einer Einheit versehen werden. |
| Istwert | Columns.ActualValue() |
Istwertspalte, kann über die Parameter mit einer Einheit versehen werden. |
| Toleranz | Columns.Tolerance() |
Toleranz, also wie viel Abweichung vom Sollwert erlaubt ist. Kalibriware erlaubt mehrere Toleranztypen, welche weiter unten beschrieben sind. |
| Abweichung | Columns.Deviation() |
Abweichung, Sollwert - Istwert, umgerechnet auf die angegebene Einheit |
| Status | Columns.Status() |
Statusspalte - wird automatisch aus Sollwert, Istwert und Toleranz berechnet. |
Innerhalb einer Messwert-Tabelle kann jeweils nur eine Sollwert, Istwert sowie Toleranzspalte definiert werden, alle anderen Spaltentypen können beliebig oft vorkommen.
Die Klasse SeriesTemplates stellt häufig genutzte und aus dem alten KalibriScript bekannte Formate bereit.
DisplayTable
Durch das reine Hinzufügen einer Messreihe zum Kalibrierprotokoll wird allerdings noch nichts angezeigt, da uns die Beschreibung der anzuzeigenden Spalten noch fehlt.
Um eine echte, sichtbare Tabelle ins Kalibrierprotokoll einzutragen, müssen wir zusätzlich noch eine DisplayTable erzeugen.
Dies kann über die Funktion DisplayTable.create() erfolgen. Die DisplayTable enthält alle Informationen, welche zur Anzeige der Daten im Protokoll relevant sind.
Dabei kann eine DisplayTable aus mehreren MeasurementSeries zusammengesetzt sein.
Um die Messreihe von oben ins Protokoll aufzunehmen, erweitern wir das Skript wie folgt:
import { Injectable } from 'injection-js';
import { Script, StepsService, delay, ScriptUI, ProtocolManager } from '@script';
// NEU: Zusätzlicher Import von DisplayTable und TableColumn.
import { SeriesTemplates, DisplayTable, TableColumn, Fields, Tolerance } from '@script/table';
@Injectable()
export class SkriptName extends Script {
constructor(private steps: StepsService, private ui: ScriptUI, private proto: ProtocolManager) {
}
public Run(): void {
this.steps.Add("Parameter 1", async () => {
await this.ui.ConfirmationBox(
"Messaufbau vornehmen",
`Bitte folgenden Messaufbau vornehmen und foldende Einstellungen an Gerät 2 vornehmen:
Einstellung Wellenlänge: 1310 nm
Einstellung Pegel: 10 dBm`,
"MESSAUFBAU.PNG",
);
await this.ui.PushStatusMessage(`Achtung: Warte auf Standard und Messgerät (10s)`);
await delay(10000);
const userInput = await this.ui.InputBox(
"Messwert erfassen",
"Bitte lesen Sie den Messwert vom Prüfling ab und tragen ihn ein",
undefined,
"dBm",
-21,
-19,
);
const series = this.steps.createSeries(SeriesTemplates.STIAQ());
series.addRow({ s: -20, t: Tolerance.Absolute(1), i: userInput });
this.proto.addMeasurementSeries(series);
// NEU: Anzeigetabelle anlegen. Dabei enthält dieser Aufruf die Kopfzeile(n), sowie die Anzahl der Spalten.
const dt = DisplayTable.create("Parameter 1", "Untertitel", 5, [
"Sollwert",
"Toleranz",
"Istwert",
"Abweichung",
"Status",
]);
// NEU: Messreihe in die Anzeigetabelle einfügen, dazu benutzen wir `addSeries` und fügen die vorher angelegte Messreihe ein.
// Für jede Verbindung von DisplayTable und MeasurementSeries muss festgelegt sein, welche Spalte der Messreihe in die Spalte der DisplayTable gehört.
// Dazu dient `TableColumn.Manual()`, `TableColumn.Number()`, und `TableColumn.Status()`.
dt.addSeries(series, [
TableColumn.Number("s"), // Extrahiere die erste Spalte (Sollwert) der DisplayTable aus dem Feld namens `s` der Messreihe `series` und erwarte einen Zahlenwert.
TableColumn.Number("t"),
TableColumn.Number("i"),
TableColumn.Number("a"),
TableColumn.Status("q"), // Extrahiere die letzte Spalte (Status) der DisplayTable aus dem Feld namens `q` der Messreihe Series und erwarte ein Statusfeld.
]);
// Nach dem Erzeugen der DisplayTable, muss diese wieder ins Protokoll eingefügt werden:
this.proto.addDisplayTable(dt);
}, { });
}
}
Damit haben wir jetzt eine vollständige manuelle Messreihe ausgeführt.