6. Verwendung von JavaScript

6.1. JavaScript-Unterstützung in CETONI-Elementen

Achtung

Das CETONI Elements Script System Add-on ermöglicht die Steuerung und Automatisierung von Prozessen mit Hilfe der Scriptsprache JavaScript. Überprüfen Sie die erstellten Skripte/Programme sowie die Parametereingaben, bevor Sie diese zum ersten Mal ausführen! CETONI übernimmt keine Haftung für direkte und/oder indirekte Schäden an Ihrem System oder externen Hard- und Softwarekomponenten, die durch die von Ihnen erstellten Skripte/Programme oder durch die Verwendung von Parametern, die für Ihre spezifische Anwendung nicht geeignet oder ungünstig sind, entstehen.

Die CETONI Elements Software verfügt über eine integrierte JavaScript-Engine. Diese Engine bietet eine Umgebung für die Ausführung von JavaScript-Code. Sie ermöglicht die Ausführung von JavaScript-Code in CETONI Elements-Skripten über eine entsprechende Skriptfunktion und bietet eine einfache Möglichkeit, die Skriptsprache JavaScript in Ihre CETONI Elements-Skripte einzubinden.

Dies gibt Ihnen eine zusätzliche Möglichkeit, Logik im Script-System zu implementieren und kann die Implementierung komplexer Berechnungen vereinfachen.

6.2. JavaScript-Konsole

6.2.1. Überblick über die JavaScript-Konsole

Das Skript-System bietet eine JavaScript-Konsole, mit der Sie ähnlich wie mit der Python-Konsole interaktiv JavaScript-Code eingeben und ausführen können. Sie können die JavaScript-Konsole über das Hauptmenü aufrufen (Window → Show View → Scripting → JavaScript Console):

../_images/javascript_console.png

In der JavaScript-Konsole können Sie Befehle ausprobieren, den Zugriff auf Objekte testen und Fehler debuggen. Wenn Funktionsaufrufe in der Konsole funktionieren, können Sie diese in Ihrem Skript verwenden.

Das Kontextmenü der JavaScript-Konsole enthält die üblichen Befehle zur Textbearbeitung:

../_images/javascript_console_context_menu.png

Sie haben drei Möglichkeiten, den Inhalt der JavaScript-Konsole zu löschen:

  • wählen Sie Clear im Kontextmenü

  • klicken Sie auf das Mülleimer-Symbol in der Titelleiste

  • geben Sie in der Konsole den Befehl clear() ein

Achtung

Gefahr von Fehlfunktionen oder Datenverlust !

Verwenden Sie die JavaScript-Konsole nicht in einem laufenden Experiment, Prozess oder während des normalen Betriebs bzw. produktiven Einsatzes. Falsche Eingaben oder Zugriffe auf Ressourcen können unter Umständen zu einem Absturz der Software führen.

6.2.2. Code-Vervollständigung

Die JavaScript-Konsole unterstützt Sie bei der Eingabe mit einer einfachen Code-Vervollständigung.

../_images/js_console_code_completion.png

Im obigen Beispiel zeigt die Code-Vervollständigung eine Auswahl von Geräten an, die mit Nemes beginnen.

6.2.3. Fehlermeldungen

Fehler, die beim Ausführen von Code in der JavaScript-Konsole auftreten, werden Ihnen als roter Text angezeigt:

../_images/js_console_error.png

6.2.4. Hilfe bekommen - getting help

Sie können den globalen Befehl help() verwenden, um mehr über die verfügbaren Objekte und Funktionen herauszufinden. Wenn Sie den Befehl help() ohne Parameter verwenden, erhalten Sie eine Liste der verfügbaren Funktionen und Objekte:

../_images/js_console_help_no_param.png

Wenn Sie das globale Objekt über help(this) übergeben, dann erhalten Sie eine Liste aller Standardmäßig vorhandene Objekte im globalen Bereich:

../_images/js_console_help_built_in.png

Wenn Sie ein Objekt oder einen Funktionsnamen als Parameter an die Hilfefunktion übergeben, z.B. help(ScriptEnv), erhalten Sie detaillierte Informationen über das angegebene Objekt, wie z.B. Typ , Eigenschaften , Methoden und Signale :

../_images/js_console_help.png

Tipp

Verwenden Sie die Funktion help(object), um einen Überblick über die Methoden und Eigenschaften eines bestimmten Objekts zu erhalten.

6.3. Zugriff auf Anwendungsobjekte

Das ScriptEnv-Objekt ist das zentrale Objekt für den Zugriff auf verfügbare Geräte und Anwendungsobjekte. Verwenden Sie den Befehl help(ScriptEnv), um eine Liste der verfügbaren Methoden und Eigenschaften dieses Objekts zu erhalten.

6.3.1. Verwendung von Geräteobjekten

Sie können auf Geräteobjekte mit der Funktion ScriptEnv.getDevice() zugreifen. Um eine Übersicht der verfügbaren Gerätenamen zu erhalten, können Sie die Funktion ScriptEnv.listDevices() aufrufen.

../_images/js_console_device_names.png

Die Code-Vervollständigung hilft Ihnen bei der Eingabe eines Gerätenamens, indem sie Ihnen eine Liste der passenden Namen anzeigt (siehe Abbildung oben). Wenn Sie die Funktion getDevice() ohne eine Zuweisung an eine Variable aufrufen, können Sie in der Konsole sehen, ob der Aufruf erfolgreich war:

js> ScriptEnv.getDevice("Nemesys_S_1")
QtLabb::CNemesys4Pump(0x1e2136485a0, "Nemesys_S_1")

Um auf ein Gerät zuzugreifen, weisen Sie das Ergebnis des Aufrufs von getDevice() einer Variablen zu. Im folgenden Beispiel weisen wir das Geräteobjekt für die erste Nemesys S-Pumpe der Variablen pump zu:

js> pump = ScriptEnv.getDevice("Nemesys_S_1")
QtLabb::CNemesys4Pump(0x1e2136485a0, "Nemesys_S_1")

Jetzt können Sie die Funktion help(pump) verwenden, um einen Überblick über die verfügbaren Methoden und Funktionen des Pumpenobjekts zu erhalten.

Tipp

Verwenden Sie die Funktion help(object), um einen Überblick über die Methoden und Eigenschaften von Geräteobjekten zu erhalten.

Wichtig

Bei vielen Geräten ist der Zugriff auf Geräteeigenschaften und -methoden nur möglich, wenn die Anwendung mit den Geräten verbunden ist.

Achtung

Risiko von Fehlfunktionen / Datenverlust !

Über Gerätefunktionen können Sie auf Funktionen zugreifen, die in der grafischen Benutzeroberfläche nicht verfügbar sind. Testen Sie Funktionen immer außerhalb laufender Prozesse und nicht während des produktiven Einsatzes. Falsche Eingaben oder Zugriffe auf Ressourcen, Methoden oder Eigenschaften können zu Fehlfunktionen oder einem Absturz der Software führen.

Wenn Sie mit dem Gerät verbunden sind, können Sie nun über die Funktionen des Geräteobjekts auf die Gerätefunktionalität zugreifen. So können Sie beispielsweise einen Nachfüllvorgang für die Pumpe auslösen:

js> pump.refillSyringe()

oder den Pumpvorgang stoppen:

js> pump.stopPumping()

Der folgende Code zeigt, wie man das Geräteobjekt für die Nemesys S-Pumpe über das ScriptEnv-Objekt erhält und dann einen Entleerungsvorgang der Spritze startet:

js> pump = ScriptEnv.getDevice("Nemesys_S_1")
QtLabb::CNemesys4Pump(0x1e2136485a0, "Nemesys_S_1")

js> pump.emptySyringe()

6.3.2. Verwendung von Anwendungsobjekten

Ähnlich wie bei Geräteobjekten können Sie auch auf Anwendungsobjekte zugreifen, die keine Geräte sind. Verwenden Sie dazu die beiden Funktionen ScriptEnv.getObject() und ScriptEnv.listObjects()

Der folgende Code zeigt, wie man das Anwendungsobjekt des grafischen Loggers über das ScriptEnv-Objekt abruft und dann die Protokollierung startet:

js> plot = ScriptEnv.getObject("ProcessDataGraph")
QtLabb::CQCustomPlotDataLogger(0x1e2112dc280, "ProcessDataGraph")

js> plot.startLogging()

Tipp

Verwenden Sie die Funktion help(object), um einen Überblick über die Methoden und Eigenschaften von Anwendungsobjekten zu erhalten.

Achtung

Risiko von Fehlfunktionen / Datenverlust !

Über die Funktionen der Anwendungsobjekte können Sie auf Funktionen zugreifen, die in der grafischen Benutzeroberfläche nicht verfügbar sind. Testen Sie Funktionen immer außerhalb laufender Prozesse und nicht während des produktiven Einsatzes. Falsche Eingaben oder Zugriffe auf Ressourcen, Methoden oder Eigenschaften können zu Fehlfunktionen oder einem Absturz der Software führen.

6.4. Standardmäßig vorhandene Objekte

Die JavaScript-Engine verfügt über eine Reihe von Standardobjekten, die in den globalen Bereich integriert sind. Eines dieser Objekte ist das global object, auf das mit dem Operator this zugegriffen werden kann. Um alle eingebauten Objekte aufzulisten, müssen Sie nur die Funktion help mit dem globalen Objekt wie folgt aufrufen: help(this).

../_images/js_console_help_built_in.png

Wenn Sie die Eigenschaften und Funktionen eines bestimmten eingebauten Objekts, wie z.B. Math, sehen möchten, müssen Sie nur die Funktion help aufrufen und dieses Objekt übergeben: help(Math). Eine detaillierte Liste der eingebauten Objekte, die von der integrierten JavaScript-Engine unterstützt werden, finden Sie in der Qt-Dokumentation:

https://doc.qt.io/qt-5/qtqml-javascript-functionlist.html

Eine ausführliche Dokumentation der eingebauten Objekte finden Sie in der JavaScript-Referenzdokumentation:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects

6.5. JavaScript-Skript-Funktion

6.5.1. Übersicht über JavaScript-Skriptfunktionen

../_images/javascript_logo.svg

Die Skriptfunktion JExecute JavaScript Code ist in der Kategorie Core Functions des Script Pool verfügbar:

../_images/core_functions.png

Mit dieser Funktion können Sie JavaScript-Code im Skript-System der Anwendung ausführen. Wenn Sie die Funktion in Ihr Skript einfügen, sehen Sie den ursprünglichen JavaScript-Code im Konfigurationsbereich.

// Implement your script logic in this function
// Avoid blocking function calls
function main() {
   return ScriptEnv.FINISH;
}

Wenn das Skript ausgeführt wird, wird es von der JavaScript-Engine geladen, und dann wird die Funktion main() aufgerufen. D.h. diese Funktion ist die Hauptfunktion des Skripts und die Logik sollte dort implementiert werden.

Tipp

Alle Optionen, die Sie in der JavaScript-Konsole für den Zugriff auf Geräteobjekte und Anwendungsobjekte haben, sind auch in der Skriptfunktion verfügbar.

Jede Skriptfunktion verwendet ihre eigene JavaScript-Engine-Instanz. Dies ermöglicht die Verwendung von JavaScript-Funktionen in parallelen Sequenzen.

6.5.2. JavaScript Editor

Die JavaScript Skript Funktion verfügt über einen JavaScript Code Editor, der Sie beim Schreiben von JavaScript Code unterstützt.

../_images/javascript_editor.png

Der Editor verfügt über die folgenden Funktionen:

  • Syntax-Hervorhebung für JavaScript-Code

  • eine einfache Code-Vervollständigung

  • Code-Faltung

  • Zeilennummern

  • Undo / Redo-Funktionalität

Einige Funktionen des Editors sind über das Kontextmenü zugänglich, andere Funktionen sind über Tastaturkürzel abrufbar. Hier sind einige der Funktionen:

Aktion

Tastaturkürzel

Schriftgröße erhöhen

Strg + +

Schriftgröße verkleinern

Strg + -

Schriftgröße auf Standard zurücksetzen

Strg + 0

Ausgewählten Code-Block einrücken

Tab

Ausgewählten Code-Block wieder einrücken

Umschalt + Tab

Rückgängig machen

Strg + Z oder Kontextmenü

Wiederholen

Strg + Y oder Kontextmenü

Wichtig

Die Bearbeitung des JavaScript-Quellcodes ist nur möglich, wenn das Skript nicht läuft. Sobald das Skript gestartet wurde, ist die Bearbeitung des Quellcodes deaktiviert. Im Falle eines Fehlers müssen Sie das Skript über die Schaltfläche Terminate Script beenden, bevor Sie den JavaScript-Code bearbeiten können.

6.5.3. Implementierung der Funktionslogik in main()

Bei der Implementierung des Skripts in main() sollten Sie darauf achten, keine blockierenden Funktionen oder blockierende Wartezeiten zu verwenden. Normalerweise führt die JavaScript-Engine den JavaScript-Code im Haupt-Thread der Benutzeroberfläche aus, und die Verwendung blockierender Funktionsaufrufe kann alle Aktualisierungen der Benutzeroberfläche und die Hauptereignisschleife blockieren.

Wichtig

Verwenden Sie keine blockierenden Funktionsaufrufe, um ein Blockieren des Haupt-UI-Threads zu vermeiden.

Wenn Sie komplexe Logik oder Zustandsautomaten in JavaScript implementieren wollen, sollten Sie die gleichzeitige Ausführung in einem Worker-Thread in Betracht ziehen. Um die gleichzeitige Ausführung zu aktivieren, können Sie den Kippschalter Current Execution einschalten. Lesen Sie mehr über diese Funktion im Abschnitt Nebenläufige Ausführung.

../_images/concurrent_execution.png

Anders als bei den Scriptfunktionen Set Variable oder Create Variable ist es hier nicht erlaubt, Variablenbezeichner (wie $Flow) oder Geräteeigenschaften (wie $$Nemesys_S_1.ActualFlow) direkt im JavaScript-Quellcode zu verwenden. Das heißt, der folgende Code ist falsch und ungültig:

function calculateFlow()
{
   // Wrong - $Flow and $$Nemesys_S_1.ActualFlow are not defined
   return $Flow + $$Nemesys_S_1.ActualFlow
}

Für den Zugriff auf Variablen müssen die Funktionen ScriptEnv.setVar() und ScriptEnv.getVar() verwendet werden. Der Zugriff auf Gerätefunktionen ist über ScriptEnv.getDevice() möglich. Der korrekte Weg, um die obige Funktion zu implementieren, ist dieser:

function calculateFlow()
{
   Flow = ScriptEnv.getVar("$Flow");
   pump = ScriptEnv.getDevice("Nemesys_S_1");
   return Flow + pump.ActualFlow;
}

Achtung

Es ist nicht erlaubt, Variablenbezeichner wie $Flow oder Geräteeigenschaften wie $$Nemesys_S_1.ActualFlow direkt im JavaScript-Quellcode zu verwenden.

6.5.4. Globale Werte mit Namen registrieren

Das ScriptEnv Objekt hat die Funktion setNamedValue() Sie können diese Funktion verwenden, um einen bestimmten Wert mit einem definierten Namen global für die aktuelle Skript-Engine-Instanz zu registrieren. Sie können dann später die Funktion namedValue() verwenden, um einfach auf den registrierten benannten Wert zuzugreifen. Dies ist z.B. dann praktisch, wenn Sie auf bestimmte Objekte, wie z.B. Settings Dateien oder globale Einstellungen, aus unterschiedlichen JavaScript Funktionen heraus zugreifen möchten. So können Sie dann in einer JavaScript Funktion den Wert registrieren und dann später aus anderen JavaScript Funktionen darauf zugreifen.

Tipp

Weitere Details finden Sie in der API Dokumentation der beiden Funktionen ScriptEnv.setNamedValue() und ScriptEnv.namedValue()

6.5.5. JavaScript Module importieren

Die JavaScript Engine erlaubt den Import eigener JavaScript Module aus dem aktuellen Projektverzeichnis. Wenn Sie eigene Module verwenden, die Sie zusammen mit Ihrem Projekt weitergeben oder ausliefern wollen, dann können Sie diese in den Unterordner Scripts/JavaScript ihres aktuellen Projekts speichern. Wenn Sie z.B. im Projekt JavaScript_Tutorial arbeiten, dann wäre der absolute Pfad zu diesem Verzeichnis:

C:/Users/Public/Documents/QmixElements/Projects/JavaScript_Tutorial/Scripts/JavaScript

Module, die in diesem Ordner enthalten sind, können Sie dann über die import Funktion des ScriptEnv Objektes importieren. Im folgenden Beispiel wird im Scripts/JavaScript Ordner ein JavaScript Modul in der Datei test.js mit folgendem Inhalt erstellt:

// module "test.js"

function cube(x) {
   return x * x * x;
}

const foo = Math.PI + Math.SQRT2;

const graph = {
   options: {
      color: "white",
      thickness: "2px",
   },
   draw() {
      console.log("From graph draw function");
   },
};

class Person {
   constructor(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
   }

   getFullName() {
      return `${this.firstName} ${this.lastName}`;
   }
}

export { cube, foo, graph, Person };

In der JavaScript Script-Funktion, wird das Modul nun als MyModule importiert und verwendet:

function main() {
   ScriptEnv.import("test.js", "MyModule");

   print(MyModule.foo);
   print(MyModule.cube(3));
   print(MyModule.graph.options.color);
   const person1 = new MyModule.Person("John", "Doe");
   console.log("Person: ", person1.getFullName());

   return ScriptEnv.ScriptFinish;
}

Die JavaScript-Konsole sollte nach der Ausführung der Scriptfunktion die folgenden Ausgaben enthalten:

js>
4.555806215962888
27
white
Person:  John Doe

6.5.6. Fehler bei der Skriptausführung

Wenn während der Ausführung eines Skripts Fehler auftreten, werden diese im Event Log angezeigt. Wenn Sie den Mauszeiger über die Fehlermeldung im Ereignisprotokoll bewegen, wird ein Hinweisfenster mit Details angezeigt:

../_images/script_execution_errors.png

In der Fehlermeldung erhalten Sie auch die Information, in welcher Zeile des Skripts ein Fehler aufgetreten ist. Dies hilft Ihnen, den Fehler im Skript-Editor zu finden und zu beheben.

Wichtig

Die Bearbeitung des JavaScript-Quellcodes ist nur möglich, wenn das Skript nicht läuft. Sobald das Skript gestartet wurde, ist die Bearbeitung des Quellcodes deaktiviert. Im Falle eines Fehlers müssen Sie das Skript über die Schaltfläche Terminate Script beenden, bevor Sie den JavaScript-Quellcode bearbeiten können.

6.6. JavaScript-Code debuggen

Wenn Sie eine Skriptfunktion mit JavaScript entwickeln, gibt es verschiedene Möglichkeiten, Fehler zu suchen und zu debuggen. In den folgenden Abschnitten wird beschrieben, wie Sie die JavaScript-Konsolen-API verwenden können, um Ihren JavaScript-Code zu debuggen.

Funktion

Beschreibung

print

Verwenden Sie print, um auf der Konsole und im Ereignisprotokoll zu drucken

Zum Beispiel:

function f(a, b) {
   print("a is ", a, "b is ", b);
}

Log

Verwenden Sie console.log, console.debug, console.info, console.warn, oder console.error, um Debugging-Informationen auf der Konsole auszugeben.

Zum Beispiel:

function f(a, b) {
   console.log("a is ", a, "b is ", b);
}

Assert

console.assert testet, ob ein Ausdruck wahr ist. Ist dies nicht der Fall, wird eine optionale Meldung auf die Konsole geschrieben und der Stack-Trace ausgegeben.

Zum Beispiel:

function f() {
   var x = 12
   console.assert(x == 12, "This will pass");
   console.assert(x > 12, "This will fail");
}

Timer

console.time und console.timeEnd protokollieren die Zeit (in Millisekunden), die zwischen den Aufrufen vergangen ist. Beide benötigen ein String-Argument, das die Messung identifiziert.

Zum Beispiel:

function f() {
   console.time("wholeFunction");
   console.time("firstPart");
   // first part
   console.timeEnd("firstPart");
   // second part
   console.timeEnd("wholeFunction");
}

Trace

console.trace gibt den Stack-Trace der JavaScript-Ausführung an der Stelle aus, an der sie aufgerufen wurde. Diese Stack-Trace-Informationen enthalten den Funktionsnamen, den Dateinamen, die Zeilennummer und die Spaltennummer. Der Stack-Trace ist auf die letzten 10 Stack-Frames beschränkt.

Count

console.count gibt die aktuelle Anzahl der Durchläufe eines bestimmten Codes aus, zusammen mit einer Meldung.

Zum Beispiel:

function f() {
   console.count("f called");
}

Exception

console.exception gibt eine Fehlermeldung zusammen mit dem Stack-Trace der JavaScript-Ausführung an der Stelle aus, an der sie aufgerufen wird.

Zusätzlich haben Sie in der Software die Möglichkeit die ScriptEnv.log() Funtion zur Ausgabe von Nachrichten im Event-Log zu verwenden. Der Vorteil dieser Funktion ist, dass Sie als Parameter zusätzlich einen Log-Level und eine Quelle Source angeben können, die dann im Event-Log ausgegebn wird.

Im folgenden Beispiel, werden in der JavaScript Funktion vier Log-Nachrichten mit unterschiedlichem Level ausgegeben:

function main() {
   ScriptEnv.log(ScriptEnv.LogInfo, "Test Info Message", "JavaScript Function");
   ScriptEnv.log(ScriptEnv.LogWarning, "Test Warning Message", "JavaScript Function");
   ScriptEnv.log(ScriptEnv.LogError, "Test Error Message", "JavaScript Function");
   ScriptEnv.log(ScriptEnv.LogDebug, "Test Debug Message", "JavaScript Function");
   return ScriptEnv.ScriptFinish;
}

Dieser Code führt im Event-Log zu folgenden Log-Ausgaben:

../_images/js_scriptenv_log_eventlog.png

Wichtig

Nachrichten mit dem Log-Level ScriptEnv.LogDebug werden nur ausgegeben, wenn der Debug-Modus aktiv ist.

6.7. Nebenläufige Ausführung

Normalerweise wird der JavaScript-Code im Haupt-Thread der Benutzeroberfläche ausgeführt. Wenn Sie lang laufenden JavaScript-Code mit blockierenden Funktionsaufrufen oder längeren Verzögerungen implementieren möchten, sollten Sie die gleichzeitige Ausführung in einem eigenen Worker-Thread in Betracht ziehen, um eine Blockierung des Haupt-UI-Threads zu vermeiden. Um die gleichzeitige Ausführung zu aktivieren, können Sie die Option Concurrent Execution aktivieren.

../_images/concurrent_execution.png

Wenn der JavaScript-Code nebenläufig ausgeführt wird, ist es möglich, die JavaScript-Skriptfunktion zu unterbrechen, wenn die Skriptausführung angehalten wird. Der Nachteil der gleichzeitigen Ausführung ist, dass es nicht sicher ist, auf Methoden und Eigenschaften von UI-Objekten zuzugreifen, die Sie über ScriptEnv.getObject() erhalten haben.

Achtung

Es ist nicht sicher, Eigenschaften und Methoden von UI-Anwendungsobjekten zu verwenden, wenn der JavaScript-Code nebenläufig außerhalb des Haupt-UI-Threads ausgeführt wird. In diesem Fall kann der Zugriff auf UI-Objekte die Anwendung zum Absturz bringen.

Um Methoden des UI-Objekts aufzurufen, müssen Sie die Funktion ScriptEnv.invoke() verwenden. Dadurch wird sichergestellt, dass die aufgerufene Methode im Haupt-UI-Thread aufgerufen wird. Das folgende Beispiel zeigt, wie man eine Funktion des grafischen Loggers und einer Pumpe mit ScriptEnv.invoke() aufruft:

function main()
{
   pump = ScriptEnv.getDevice("Nemesys_S_1");
   ScriptEnv.invoke(pump.generateFlow, [-0.01]);
   graph = ScriptEnv.getObject("ProcessDataGraph");
   ScriptEnv.invoke(graph.startLogging, []);
}

Die folgende Tabelle zeigt die Vor- und Nachteile der beiden Ausführungsarten, um Ihnen die Entscheidung zu erleichtern, welche Ausführungsart Sie verwenden sollten:

Ausführung im UI-Thread

Gleichzeitige Ausführung

Zugriff auf UI-Objekte

ja

nur über ScriptEnv.invoke()

Blockierende Funktionsaufrufe

nein - blockiert UI-Thread

ja

Unterbrechbar

nein

ja

Geeignet für

kurze Skripte oder Berechnungen

komplexe Skripte mit blockierenden Funktionsaufrufen und Verzögerungen

6.8. Zugriff auf Signale und Slots in Skripten

Die eingebettete JavaScript-Engine bietet die Möglichkeit, Signale und Slots zu verwenden. Signale werden von einem Objekt ausgesendet, wenn sich sein interner Zustand in irgendeiner Weise geändert hat. Ein Slot wird aufgerufen, wenn ein mit ihm verbundenes Signal ausgegeben wird. Slots sind normale Funktionen und können normal aufgerufen werden; ihre einzige Besonderheit ist, dass Signale mit ihnen verbunden werden können. Wenn Sie eine Verbindung zu einem Signal herstellen, kann der Empfänger ein regulärer Slot eines anderen Objekts oder eine JavaScript-Funktion sein. Der häufigste Fall ist, dass Sie das Signal mit einer anonymen Funktion verbinden:

pump.dosageFinished.connect(function() {
    console.log('dosage finished!');
});

Wenn Sie die Verbindung rückgängig machen wollen, müssen Sie die Funktion in einer Variablen speichern:

function dosageFinished() {
   console.log('dosage finished!')
}

pump.dosageFinished.connect(dosageFinished);
//...
pump.dosageFinished.disconnect(dosageFinished);

Sie können das Signal auch mit einem Signal oder Slot eines anderen sichtbaren Objekts verbinden. Im folgenden Beispiel verbinden wir das Signal dosageFinished von pump2 mit dem Slot stopPumping von pump1. Dadurch wird pump1 automatisch gestoppt, wenn pump2 anhält:

pump1 = ScriptEnv.getDevice("peRISYS_S_1");
pump2 = ScriptEnv.getDevice("peRISYS_S_2");
pump2.dosageFinished.connect(pump1.stopPumping)

Das folgende Beispiel ist etwas komplexer und zeigt, wie die gleichzeitige Ausführung zusammen mit einer Signalverbindung verwendet werden kann.

 1function onDosageFinished() {
 2   print("onDosageFinished");
 3   ScriptEnv.leave();
 4}
 5
 6// Implement your script logic in this function
 7// Avoid blocking function calls
 8function main() {
 9   pump = ScriptEnv.getDevice("Nemesys_S_1");
10   if (typeof ScriptEnv.Initialized == "undefined") {
11      ScriptEnv.Initialized = true;
12      print("Connecting signal")
13      pump.dosageFinished.connect(onDosageFinished);
14   }
15   pump.aspirate(0.01, 0.01);
16   return ScriptEnv.KEEP_RUNNING;
17}

Der Code in den Zeilen 10 bis 14 verbindet das Signal dosageFinished der Pumpe mit der JavaScript-Funktion onDosageFinished.

if (typeof ScriptEnv.Initialized == "undefined") {
   ScriptEnv.Initialized = true;
   print("Connecting signal")
   pump.dosageFinished.connect(onDosageFinished);
}

Die umgebende Prüfung if (typeof ScriptEnv.Initialized == "undefined") stellt sicher, dass es nur eine Verbindung gibt, wenn die Funktion mehrmals aufgerufen wird. Wenn wir diese Prüfung nicht verwenden, wird bei jedem Aufruf der Funktion eine neue Verbindung erstellt.

In den nächsten beiden Zeilen wird die Pumpenansaugung gestartet und die Skriptfunktion main() gibt ScriptEnv.KEEP_RUNNING zurück, um anzuzeigen, dass die Skriptausführung nicht beendet werden soll, wenn die Hauptfunktion beendet ist.

pump.aspirate(0.01, 0.01);
return ScriptEnv.KEEP_RUNNING;

Das Skripting-System bleibt in dieser Skriptfunktion, bis die Pumpe die Dosierung beendet hat. In diesem Fall wird die JavaScript-Funktion onDosageFinished() aufgerufen.

function onDosageFinished() {
   print("onDosageFinished");
   ScriptEnv.leave();
}

Die Funktion gibt eine Meldung auf der Konsole aus und ruft ScriptEnv.leave() auf, um der Skriptfunktion zu signalisieren, dass die Funktion beendet ist und die nächste Skriptfunktion ausgeführt werden kann. Dieses Beispiel zeigt, wie man eine länger laufende Aufgabe ausführt und die Skriptfunktion beendet, wenn die Aufgabe beendet ist.

6.9. API-Referenz

6.9.1. ScriptEnv

Das ScriptEnv-Objekt ist das zentrale Objekt für den Zugriff auf verfügbare Geräte und Anwendungsobjekte.

enum eMainFunctionReturnCode

The main function return code.

Normally the script execution finishes, if the JavaScript main() function is left. To indicate, that script execution should not finish, i.e. if you would like to wait for a certain event or signal, then your main function can return KEEP_RUNNING, to indicate, that you would like to stay in the current script function.

Example:

function main() {
    return ScriptEnv.ScriptFinish;
}

Values:

enumerator ScriptFinish

finish current script function

enumerator ScriptKeepRunning

keep the JavaScript engine running and stay in current script

enum eProjectLocation

This enum describes the different locations that can be queried using projectPath() function.

The following example shows, how to get the data path of the current project:

Example:

ScriptEnv.projectPath(ScriptEnv.LocationData);

Values:

enumerator LocationProject

location of the current project directory

enumerator LocationConfigurations

location of the device configurations in the current project

enumerator LocationLog

location of the log files

enumerator LocationScripts

location of the current project script files

enumerator LocationPictures

location of the current project image files

enumerator LocationVideos

location of the current project video files

enumerator LocationData

location of data in the current project

enumerator LocationSettings

location where application can store settings and configuration files

enum eLogLevel

Log levels - keep this in sync with Core::CLogEvent::eType.

Values:

enumerator LogInfo

LogInfo.

enumerator LogWarning

LogWarning.

enumerator LogError

LogError.

enumerator LogDebug

LogDebug.

QStringList listDevices() const

Returns an array with all device names that can be accessed from JavaScript.

Use the device name when calling the getDevice() function to get the corresponding device object.

QtLabb::Core::CDevice *getDevice(const QString &Name) const

Returns the device object for the given device name.

The device object provides access to device-specific functions and properties of this device. The device names are the names that are also used in the CETONI Elements script system to access devices or device properties.

Example:

pump = ScriptEnv.getDevice("Nemesys_S_1")

QStringList listObjects() const

Returns an array of all registered application objects that are not devices and that can be accessed from JavaScript, such as the graphical logger.

QObject *getObject(const QString &Name) const

Returns the object with the given object name Name.

Example:

plot = ScriptEnv.getObject("ProcessDataGraph")

QVariant getVar(const QString &VarName) const

Returns the value of a certain script variable.

The variable name needs to start with a dollar sign.

Example:

flow = ScriptEnv.getVar("$FlowRate")

void setVar(const QString &Name, const QVariant &Value)

Sets the value of the script variable to the given value.

Example:

ScriptEnv.setVar("$TargetPos", 25)

void setVars(const QVariantList Values)

Sets multiple script variables using a JavaScript array.

Example:

ScriptEnv.setVars(["$Value1", 0.5, "$Value2", 1.5])

void setNamedValue(const QString &Name, const QVariant &Value, bool ReplaceIfExists = true)

Registers a named value.

You can use this function to register a certain value with a given name globally to the current script engine instance. You can then use the namedValue() function later, to easily access the registered named value. If the value is already registered, it is overwritten with the new value given in Value If the value already exists and ReplaceIfExists is false, then the value is not replaced

Siehe auch

namedValue()

QVariant namedValue(const QString &Name)

Returns a named value or an invalid variant, if no value with the given name is registered.

Siehe auch

setNamedValue()

void sleep(float Seconds)

Sleep for the given number of seconds without blocking the application main event loop.

Use this function for small delays only.

void leave()

Leave the current script function.

If your JavaScript main() function returned ScriptEnv.KEEP_RUNNING then you can use this function if you finished your JavaScript logic to signal the scripting system that the script system should leave the current script function and process the next one.

QJSValue invoke(const QJSValue &fun, const QJSValue &args)

Invoke the given function with the given parameters in the main UI thread.

This allows calling of UI objects methods from a JavaScript function running concurrently in its own thread. The first parameter fun is the function that should get invoked (like pump.generateFlow) and the second parameter args is an JavaScript array with the function arguments (like [-0.01, 25]).

Example:

pump = ScriptEnv.getDevice("Nemesys_S_1");
ScriptEnv.invoke(pump.generateFlow, [-0.01]);
graph = ScriptEnv.getObject("ProcessDataGraph");
ScriptEnv.invoke(graph.startLogging, []);

QString projectPath(int Location) const

Returns the absolute path of a certain data location in the current project.

The following example shows, how to get the data path of the current project:

Example:

path = ScriptEnv.projectPath(ScriptEnv.LocationData);
path += "/TestFile1.txt";
f = new QFile(path);
f.open(QFile.ModeReadWrite);
f.write("This is just a test test");
f.close();

Siehe auch

eProjectLocation

Parameter:

Location – Location identifier (see eProjectLocation enum)

void import(const QString &RelativeFilePath, const QString &JsModuleIdentifier)

Import a JavaScript module from the Scripts/JavaScript subfolder of the current project.

The following example shows, how to register and use the JavaScript module Scripts/JavaScript/TestModules/test.js

ScriptEnv.import("TestModules/test.js", "MyModule");
MyModule.add(1, 2);

Parameter:
  • RelativeFilePath – The module file path relative to Scripts/JavaScript

  • JsModuleIdentifier – The name of the module in JavaScript

void log(int Level, const QString &Message, const QString &Source)

Log a message to the application event log.

This function is similar to the console.log, console.warn or console.error functions. But this function allows to provide an additional source parameter. If you use the console.log functions, then the event source in the event log is always the JavaScript script function that contains the log statement. If you would like to evaluate and print errors for a certain object or device, then it is better for the user if the source if the name of the device or object that caused the error. For example, if you have a device „Pure Water Station“ that signals the warning „Filter Cleaning Required“ then you can use this function to signal the warning:

ScriptEnv.log(ScriptEnv.LogWarning, "Filter Cleaning Required", "Pure Water Station");

Parameter:
  • Level – The log level (ScriptEnv.LogInfo, ScriptEnv.LogWarning, ScriptEnv.LogError)

  • Message – The log message printed in the Event column

  • Source – The name of the error source printed in the Event source column

bool isDebugModeOn() const

Returns true, if the debug mode of the script engine is on.

6.9.2. QFile

Die Klasse QFile bietet eine Schnittstelle zum Lesen von und Schreiben in Dateien. Sie ist ein Wrapper für die Klasse QFile aus dem Qt-Framework.

class CScriptQFile : public QObject

Wrapper for QFile object.

The QFile class provides an interface for reading from and writing to files. The file name is usually passed in the constructor, but it can be set at any time using setFileName(). QFile expects the file separator to be ‚/‘ regardless of operating system. The use of other separators (e.g., ‚') is not supported.

Public Types

enum OpenModeFlag

This enum is used with open() to describe the mode in which a device is opened.

Values:

enumerator ModeNotOpen
enumerator ModeReadOnly
enumerator ModeWriteOnly
enumerator ModeReadWrite
enumerator ModeAppend
enumerator ModeTruncate
enumerator ModeText
enumerator ModeUnbuffered
enumerator ModeNewOnly
enumerator ModeExistingOnly

Public Functions

void close()

Closes the file.

Calls flush() and closes the file. Errors from flush are ignored.

bool copy(const QString &newName)

Copies the file named fileName() to newName.

This file is closed before it is copied. If the copied file is a symbolic link (symlink), the file it refers to is copied, not the link itself. With the exception of permissions, which are copied, no other file metadata is copied. Returns true if successful; otherwise returns false. Note that if a file with the name newName already exists, copy() returns false. This means QFile will not overwrite it.

CScriptQFile(const QString &name = QString())

Constructs a new file object to represent the file with the specified name.

Example:

f = new QFile("C:/temp/test1.txt");
f.open(QFile.ModeReadWrite);

QString errorString() const

Returns a human-readable description of the last device error that occurred.

bool exists() const

Returns true if the file specified by fileName() exists; otherwise returns false.

QString fileName() const

Returns the name of the file.

bool isOpen() const

Returns true if the device is open; otherwise returns false.

A file is open if it can be read from and/or written to.

bool moveToTrash()

Moves the file specified by fileName() to the trash.

Returns true if successful, and sets the fileName() to the path at which the file can be found within the trash; otherwise returns false.

bool open(int mode)

Opens the file with the given open mode.

Returns true if successful; otherwise returns false.

Example:

f = new QFile("C:/temp/test1.txt");
f.open(QFile.ModeReadWrite);

QByteArray read(qint64 maxSize)

Reads at most maxSize bytes from the device, and returns the data read as a QByteArray.

This function has no way of reporting errors; returning an empty QByteArray can mean either that no data was currently available for reading, or that an error occurred.

QByteArray readAll()

Reads all remaining data from the device, and returns it as a byte array.

This function has no way of reporting errors; returning an empty QByteArray can mean either that no data was currently available for reading, or that an error occurred.

QByteArray readLine(qint64 maxSize = 0)

Reads a line from the device, but no more than maxSize characters, and returns the result as a byte array.

This function has no way of reporting errors; returning an empty QByteArray can mean either that no data was currently available for reading, or that an error occurred.

bool remove()

Removes the file specified by the fileName given.

Returns true if successful; otherwise returns false.

bool rename(const QString &newName)

Renames the file currently specified by fileName() to newName.

Returns true if successful; otherwise returns false.

If a file with the name newName already exists, rename() returns false (i.e., QFile will not overwrite it).

The file is closed before it is renamed.

If the rename operation fails, Qt will attempt to copy this file’s contents to newName, and then remove this file, keeping only newName. If that copy operation fails or this file can’t be removed, the destination file newName is removed to restore the old state.

bool seek(qint64 pos)

For random-access devices, this function sets the current position to pos, returning true on success, or false if an error occurred.

For sequential devices, the default behavior is to do nothing and return false.

Seeking beyond the end of a file: If the position is beyond the end of a file, then seek() will not immediately extend the file. If a write is performed at this position, then the file will be extended. The content of the file between the previous end of file and the newly written data is UNDEFINED and varies between platforms and file systems.

void setFileName(const QString &FileName)

Sets the name of the file.

The name can have no path, a relative path, or an absolute path. Do not call this function if the file has already been opened. If the file name has no path or a relative path, the path used will be the application’s current directory path at the time of the open() call.

qint64 write(const QByteArray &data)

Writes the content of byteArray to the device.

Returns the number of bytes that were actually written, or -1 if an error occurred.