User Spezifischer Java Script Code
Um eine einfache Möglichkeit zu bieten, AvNav an seine Bedürfnisse anzupassen, kann man mit ein wenig Java Script Code AvNav relativ einfach erweitern.
Dabei ist es zunächst einmal möglich, neue Anzeigen zu definieren, die dann im Layout Editor ausgewählt werden können. Prinzipiell kann man dort natürlich beliebigen Java Script Code ausführen - muss dabei aber natürlich zusehen, die Funktionen von AvNav nicht zu stören.
Der Java Script Code liegt in der Datei user.js im Verzeichnis
BASEDIR/user/viewer.
BASEDIR ist z.B. auf dem pi /home/pi/avnav/data.
Bearbeitung
Um die Bearbeitung zu erleichtern, kann man über die Files/Download Seite und die Unterseite auf die Dateien in diesem Verzeichnis zugreifen.
Wie im Bild zu sehen, existiert dort bereits eine Datei user.js. Diese wird beim erstmaligen Start aus einem Template erzeugt.
Durch einen Klick auf die Datei und die Auswahl von "Edit" kann man die Datei direkt bearbeiten.
In der Datei sind bereits Beispiele vorhanden für die Möglichkeiten der Anpassung. Nach dem Bearbeiten die Datei speichern und AvNav neu laden.
Es empfiehlt sich, die Datei in regelmäßigen Abständen nach dem Bearbeiten herunterzuladen und noch einmal irgenwo zu speichern - es gibt keine Versionsverwaltung in AvNav.
Das aktuelle Template kann man auch auf github finden.
Anzeigen (Widgets)
Man kann im Wesentlichen die folgenden Arten von Anzeigen hinzufügen:
- Widgets mit eigenem Formatter (und ggf. festen Werten) basierend auf dem Default Widget (Beispiel 1 - user.js: rpmWidget, testPlugin: testPluing_simpleWidget )
- Anpassung/Erweiterung der Grafik Widgets mit canvas
gauges (Beispiel 2 - user.js:
rpmGauge)
Hiermit ist es auch möglich, Parameter zugänglich zu machen, die in den bisher vorhandenen Widgets nicht enthalten sind - Widgets mit eigenem HTML code (Beispiel 3 - user.js: userSpecialRpm, TestPlugin: testPlugin_courseWidget)
- Widgets mit Grafik in einem Canvas Element (Beispiel im TestPlugin: testPlugin_courseWidget)
- Widgets mit eigenem HTML, die mit dem Server Teil eines Plugins interagieren (TestPlugin: testPlugin_serverWidget)
- Widgets, die Grafiken auf der Karte darstellen (type: map) - ab 20220819 z.B. SailInstrument
Das Interface, über das mit AvNav kommuniziert wird, findet sich auf
github bzw. im Template Code.
Für map Widgets kann über das Api auf die zugrunde
liegenden Bibliotheken für geografische Berechnungen zugegriffen
werden (Funktion LatLon und Dms).
Canvas Gauges
Für Canvas Gauge Widgets können bestimmte Parameter (siehe Canvas Gauges Beschreibung) entweder auf feste Werte gesetzt werden (dann müssen sie in der Widget Definition vorhanden sein - siehe die Werte im Beispiel ab Zeile 134) oder sie können für den Nutzer im Layout Editor einstellbar gemacht werden (dann müssen sie als editable WidgetParameter gesetzt werden - im Beispiel ab Zeile 156).
Ausserdem kann eventuell ein eigener Formatter definiert werden und als default für das Widget gesetzt werden.
Wenn man für bestimmte vordefinierte Parameter vermeiden möchte, das sie im Layout Editor für den Nutzer sichtbar werden, müssen sie in den editable Parameters mit false angegeben werden.
Für jedes gauge widget muss der Parameter "type" angegeben werden -
entweder "radialGauge" oder "linearGauge".
Ausserdem haben sie den zusätzlichen Parameter
Dieser Parameter steuert, ob auch eine numerische Anzeige des Wertes erfolgen soll. Der originale "valueBox" Parameter der canvas gauges wird ignoriert!
Neben den Parametern kann auch noch eine translateFunction definiert werden. Diese erhält als Parameter ein Objekt mit den aktuellen Werten aller Parameter und kann dieses modifizieren, bevor sie an canvas gauges übergeben wird (Beispiel ab Zeile 104). Diese Funktion muss "zustandsfrei" sein, d.h. die Werte der Rückgabe dürfen nur von den übergebenen Werten (und anderen unveränderlichen Werten) abhängig sein. Andernfalls werden potentiell Änderungen nicht in der Anzeige sichtbar.
Eigene Widgets
Für ein selbst geschriebenes Widget können die folgenden Funktionen/Eigenschaften implementiert werden:
Name | Typ | Nutzbar bei Typ | Beschreibung |
name | String | alle | der Name des Widgets |
type | String (optional) |
alle | Bestimmt, welches Widget erzeugt werden soll. Werte: radialGauge, linearGauge, map Wenn der Typ nicht gesetzt ist, wird entweder das default widget genutzt (keine Funktion renderHtml und keine Funktion renderCanvas angegeben) - oder ein nutzer definiertes Widget (userWidget) |
renderHtml | Funktion (optional) |
userWidget | Diese Methode muss einen String zurückgeben, der dann als HTML
code in das Widget eingebaut wird. Falls eventHandler für Elemente genutzt werden sollen, müssen diese vorher registriert werden (siehe initFunction) und werden im HTML code einfach mit <button onclick="myHandler">Click!</button>
angegeben (das ist keine exakte HTML Syntax, da nur der Name des
event handlers angegeben wird, kein java script code).Die "this" variable innerhalb von renderHtml zeigt auf ein Objekt, das spezifisch für das Widget ist (Kontext). Wenn der EventHandler aufgerufen wird, zeigt this ebenfalls auf diesen Kontext. Das als Parameter an renderHtml übergebene Objekt enthält die unter storeKeys definierten Werte. Die Funktion wird jedesmal erneut aufgerufen, wenn sich die Werte geändert haben. |
renderCanvas |
Funktion (optional) |
userWidget, map |
Mit dieser Funktion kann in das übergebene Canvas Objekt
gezeichnet werden. Das als zweiter Parameter an renderCanvas übergebene Objekt enthält die unter storeKeys definierten Werte. Die Funktion wird jedesmal erneut aufgerufen, wenn sich die Werte geändert haben. Die "this" variable innerhalb von renderCanvas zeigt auf ein Objekt, das spezifisch für das Widget ist (Kontext). Für map widgets ist dieser Canvas ein Overlay, das über die Karte gelegt wird. Am Widget Kontext stehen Funktionen zur Umrechnung von Koordinaten in Canvas Pixel bereit. Es ist wichtig den Canvas korrekt mit save/restore zu beschreiben, da sich alle map widgets den gleichen Canvas teilen. |
storeKeys |
Object | alle | Hier müssen die Daten angegeben werden, die aus dem zentralen
Speicher gelesen und als Parameter den renderXXX Funktionen
mitgegeben werden sollen |
caption | String (optional) |
alle | Eine default Beschriftung |
unit | String (optional) |
alle | Eine default Einheit |
formatter | Funktion (optional) |
defaultWidget, radialGauge, linearGauge |
Ein Formatierer für den Wert. Für das defaultWidget muss diese Funktion angegeben werden. |
translateFunction | Funktion (optional) |
alle ausser map | Diese Funktion wird mit den aktuellen Werten als Parameter
aufgerufen (so wie bei storeKeys angegeben) und muss die daraus
berechneten Werte zurückgeben. Falls keine eigene renderXXX Funktion genutzt werden soll, kann hier vor dem Rendern eine Umrechnung von Werten erfolgen - siehe Beispiel. |
initFunction | Funktion (optional) |
userWidget, map |
Falls vorhanden, wird diese Funktion einmalig aufgerufen, wenn das
Widget erzeugt wird. Als Parameter (und als this) ist der Widget
Context vorhanden. Dieses Objekt hat eine eventHandler Eigenschaft - hier müssen die im renderHTML genutzten eventHandler eingetragen werden. Mit der Funktion triggerRedraw am Widget Kontext kann ein erneuter Aufruf der renderXXX Funktionen erzwungen werden, Ab Version 20210422 erhält die initFunction einen 2. Parameter, der die Eigenschaften des Widgets enthält. Das sind insbesondere auch alle editierbaren Widget Parameter, die definiert wurden. |
finalizeFunktion | Funktion (optional) |
userWidget, map |
Falls vorhanden, wird diese Funktion aufgerufen, bevor das Widget
nicht mehr genutzt wird. Die "this" Variable zeigt wieder auf den
Widget Kontext. Ausserdem ist der Kontext auch als erster Parameter vorhanden - wie bei der initFunction. |
Der java script code erhält folgende globale Variablen:
Name | plugin.js/user.js | Beschreibung |
AVNAV_BASE_URL | beide | die URL zum Verzeichnis, aus dem die Java script Datei geladen
wurde. Diese kann z.B. verwendet werden, um weitere Elemente von
dort zu laden. Für die user.js können Dateien aus dem images
Verzeichnis über AVNAV_BASE_URL+"../images" erreicht werden. Für plugins kann über AVNAV_BASE_URL+"/api" die Kommunikation mit dem Python Anteil erreicht werden. |
AVNAV_PLUGIN_NAME | plugin.js | Der Name des Plugins. |
Nach der Definition muss das Widget bei AvNav bekannt gemacht werden (avnav.registerWidget).
Widget Context
User Widgets und Map Widgets bekommen einen WidgetContext. Dieser wird für jedes Widget erzeugt und den Funktionen:
- initFunction (this und erster Parameter)
- finalizeFunction (this und erster Parameter)
- renderHtml (this)
- renderCanvas (this)
Damit der Kontext als this Parameter genutzt werden kann, müssen die Funktionen "klassisch" mittels function definiert werden und nicht als "arrow function".
Richtig:
renderHtml: function(context,props){
return "<p>Hello</p>";
}
}
Im WidgetContext können Nutzerdaten gespeichert werden, die in
aufeinanderfolgenden Aufrufen benötigt werden.
Ausserdem enthält er einige Funktionen, die vom Widget Code aufgerufen
werden können.
Name | Widget | Parameter | Beschreibung |
eventHandler | userWidget | --- | eventHandler ist keine Funktion sondern ein array. Falls im
renderHtml event Handler angegeben werden (z.B. <button
onclick="clickHandler"/>), dann muss in der initFunction
eine Funktion clickHandler hier registriert werden: this.eventHandler.clickHandler=function(ev){...} Siehe TestPlugin. |
triggerRedraw | userWidget | --- | Diese Funktion muss gerufen werden, wenn das Widget (z.B. nach
einer Kommunikation mit dem Server) möchte, das es neu gezeichnet
wird. Siehe TestPlugin. |
lonLatToPixel | map | lon,lat | Konvertiert die Koordinaten in pixel Koordinaten für das Zeichnen
in renderCanvas. Gibt ein array mit x,y Koordinate zurück. |
pixelToLonLat | map | x,y | Berechnet aus den Canvas-Koordinaten x,y longitude und latitude. Gibt ein array mit lon,lat zurück. |
getScale | map | --- | Gibt den Scaling Faktor für das Display zurück. Hochauflösende Display haben einen scaling Factor > 1. Gezeichnete Objekte (besonders Text) sollten in ihren Dimensionen angepasst werden. |
getRotation | map | --- | Gibt die Drehung der Karte (in radian!) zurück |
getContext | map | --- | Gibt den renderingContext2D des Canvas zurück (nur aktiv innerhalb der renderCanvas Funktion) |
getDimensions | map | --- | gibt die Größe des Canvas zurück [Breite,Höhe] |
triggerRender | map | --- | gleiche Funktion wie triggerRedraw beim user Widget |
Widget Parameter
Neben der Widget Definition können hier noch Parameter angegeben werden, die dann im Layout Editor für das Widget angezeigt werden.
Beispiele sind im user.js
Template zu finden. Die Werte, die im Layout Editor für diese
Parameter angegeben werden, stehen später in den renderHtml und
renderCanvas Funktionen zur Verfügung (Ausnahme: Typ KEY, hier wird
der aus dem Speicher gelesene Wert zur Verfügung gestellt).
Für jeden Parameter kann man die folgenden Werte angeben:
Name | Type | Beschreibung |
key | Der Name des Parameters so wie er im Layout Editor angezeigt werden soll, und wie er den renderXXX Funktionen zur Verfügung stehen soll. | |
type | String | STRING, NUMBER, KEY, SELECT, ARRAY, BOOLEAN, COLOR Der Typ für den Parameter. Je nach Typ wird er dem Nutzer unterschiedlich angezeigt. Für COLOR eine Farb-Auswahl, für SELECT eine AuswahlListe und für KEY die Liste der momentan verfügbaren Werte im Store. Für ein Array kann eine durch Komma getrennte Liste angegeben werden. |
default | je nach type | Der default Wert. Für COLOR eine color css Property - also z.B. "rgba(200, 50, 50, .75)" |
list | Array (nur für type SELECT) |
Ein Array von Strings oder von Objekten {name:'xxx',value:'yyy'} - diese Werte werden zur Auswahl angezeigt. |
Es gibt eine Reihe von vordefinierten Parametern für den Layout Editor. Bei diesen wird zur Beschreibung kein Objekt mit Eigenschaften angegeben, sonder nur true oder false (das zeigt, ob sie zum Ändern angeboten werden sollen oder nicht).
Das sind:
- caption (STRING)
- unit (STRING)
- formatter (SELECT)
- formatterParameters (ARRAY)
- value (KEY)
- className (STRING)
Ein Beispiel für eine Definition:
Formatierer (Formatter)
Neben den eigentlichen Anzeigen können auch eigene Formatierer
geschrieben werden, die die Werte für die Anzeige aufbereiten.
Im System sind bereits eine Reihe von Formatierern vorhanden - siehe Layout
Editor.
Ab Version 20210106 können eigene Formatierer bei AvNav registriert
werden und stehen dann allen Widgets zur Verfügung. Ein Formatierer ist
eine Funktion, die als ersten Parameter den zu formatierenden Wert
übergeben bekommt und als Ergebnis einen String zurück liefern muss.
Der String sollte dabei unabhänging vom momentanen Wert immer die gleiche
Länge haben (ggf. Leerzeichen voranstellen) um die Größenanpassung auf den
Dashboard-Seiten nicht zu stören.
Eine Formatierer-Funktion kann zusätzliche Parameter akzeptieren, um die Umwandlung zu steuern. Diese werden über die Widget Eigenschaft formatterParameters typischerweise im Layout Editor gesetzt.
Beispiel:
Falls ein Formatierer mit dem gleichen Namen schon existiert, wirft registerFormatter eine Exception.
Jede Formatter Funktion sollte eine Property "parameters" bekommen. Diese beschreibt die im Layout-Editor sichtbaren Parameter für die Funktion. Die Werte in dieser Definition haben die gleiche Syntax wie die editierbaren Widget-Parameter.
Bibliotheken und Bilder
Falls der eigene Java Script code auf libraries oder images zugreifen soll, können diese in das gleiche Verzeichnis hochgeladen werden - Images auch in das Images Verzeichnis.
Das Einbinden von Bibliotheken kann z.B. so erfolgen
Es empfiehlt sich, für alle Widgets css Klassen zu vergeben, damit man diese dann mit nutzerspezifischem CSS anpassen kann. IDs sollten nicht verwendet werden, da die Elemente potentiell mehrfach auf der Seite auftauchen können.
Falls Daten vom Server geladen werden sollen, empfiehlt sich die Verwendung von fetch. Alle Dateien im user Verzeichnis (oder im plugin Verzeichnis für plugin.js) sind nach dem Schema AVNAV_BASE_URL+"/"+name abrufbar.
Falls im User-Verzeichnis z.B. eine weitere Text-, Html- oder andere Datei angelegt werden soll (ohne eine hochzuladen), kann man das auch direkt mit dem "+" Button unten rechts erledigen - die Datei kann dann natürlich ebenfalls direkt bearbeitet werden.
Feature Formatierer (featureFormatter)
Ab Version 20210114 gibt es die Möglichkeit, eigene Funktionen zu
registrieren, die die Anzeige von Daten aus Overlays aufbereiten.
Solche Funktionen können in der user.js oder in Plugins implementiert
werden.
Mit
werden sie registriert. Für Details siehe Overlays.