pdx Konfiguration | Index | pdx Interaktiver Betrieb

## pdx Referenz

### Erzeugung von Berichten und Diagrammen

Berichte entstehen aus Textvorlagen. pdx sucht innerhalb einer solchen Vorlage nach einem gekennzeichneten Abschnitt mit Funktionsaufrufen, meist einer format-Funktion. Dieser Abschnitt wird dann aus der Vorlage herausgeschnitten, ausgewertet und durch das Ergebnis ersetzt. In einer Vorlage können beliebig viele Abschnitte mit Funktionsaufrufen angeordnet werden.
Textvorlagen sind entweder einfacher ASCII-Text oder Text in einer Formatierungssprache wie HTML oder XML bzw. einer Programmiersprache wie C oder SQL, im Sinne dieser Abhandlung eine Wirtssprache. Worum es sich dabei konkret handelt, soll absichtlich in keiner Weise eingeschränkt werden. pdx muß aber wissen, woran die Abschnitte mit den Funktionsaufrufen zu erkennen sind. Deshalb werden solche Abschnitte generell in Kommentaren der jeweiligen Wirtssprache angeordnet, in HTML oder XML also zwischen <!-- und -->, in C hingegen zwischen /* und */. Dabei bleibt die Vorlage eine unvollständige, aber korrekte Datei ihres Typs, was den Vorteil hat, daß man sie nach wie vor mit Werkzeugen (z.B. HTML-Editoren) bearbeiten kann. Es ist klug, diese pdx-Kommentarblöcke noch weiter zu kennzeichnen, um sie von bereits existierenden, echten Kommentaren zu unterscheiden. Man verwende also klugerweise z.B. <!--- und ---> oder /** und **/. Eine vollständige, kleine Vorlage für eine HTML-Datei mit pdx-Anweisungen würde also beispielsweise wie folgt aussehen:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html lang="de-ch">
<head>
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=iso-8859-1">
<title>MyTitle</title>
</head>
<body style="direction: ltr;" lang="de-DE">
<!--- (now) --->, <b>pdx</b> <!--- (version) ---> (<!--- (build) --->) *
</body>
</html>
In der mit * markierten Zeile stehen drei pdx-Abschnitte, in denen je eine der Funktionen now, version und build aufgerufen wird. Die Ausgabe, die diese Zeile erzeugt, sieht ungefähr so aus:
2009-12-27 15:14:51, pdx 0.3.0 (2009-12-27 10:28:14 on castor, GNU/Linux 2.6.31-ARCH x86_64)
Man sieht, daß alle Bestandteile der Zeile, die nicht innerhalb pdx-Abschnitten stehen, (sowie auch der gesamte umgebende Text) unverändert in die Ausgabe übernommen werden. Diese Zeile hätte übrigens unter Benutzung der format-Funktion auch anders formuliert werden können:
<!--- (format (now) ", <b>pdx</b> " (version) " (" (build) ")") --->
Um nun in HTML eine komplette Tabelle anzuordnen, würde man beispielsweise wie folgt vorgehen:
[...]
<table style="page-break-before: always; page-break-inside: avoid;
width: 800px;" border="1" cellpadding="1" cellspacing="1">
<tbody>
<tr valign="top">
<td>Datum/Zeit</td>
<td>*</td>
<td>n</td>
<td>l</td>
<td>m</td>
<td>x</td>
<td>Kommentar</td>
</tr>
<!---
(format
(empty "<br>")
"<tr valign=top>"
"<td>" datetime "</td>"
"<td>" (select "*" (days 7)) <1.1> "</td>"
"<td>" (select "n" (days 7)) <1> "</td>"
"<td>" (select "l" (days 7)) <1> "</td>"
"<td>" (select "m" (days 7)) <1.0> "</td>"
"<td>" (select "x" (days 7)) <1.1> "</td>"
"<td>" (select "#" (days 7)) "</td>"
"</tr>" newline
)
--->
</tbody>
</table>
[...]
Die Tabelle beginnt außerhalb des pdx-Abschnitts und endet wiederum außerhalb. Die Zeilen der Tabelle mit Ausnahme der Kopfzeile werden aber vollständig von pdx generiert. Diese Zeilen besitzen alle das gleiche Aussehen.
Diagramme entstehen aus Diagrammdefinitionsdateien. Diese enthalten immer genau eine Diagrammdefinition, d.h. einen Aufruf der diagram-Funktion. In Diagrammdefinitionsdateien sind keine Kommentar-Abschnitte notwendig, da es keine Wirtssprache gibt. Eine vollständige Diagrammdefinition sieht beispielsweise so aus:
(diagram 400 300 #FFFDFD
(axes (month 1) 3.0 9.0 1.0 #0)
(hline 5.0 #C0C0C0)
(hline 6.0 #C0C0C0)
(hline 7.0 #C0C0C0)
(curve (sum (select "*" (month 1)) day 3:30 9:30) #FF0000)
(curve (sum (select "*" (month 1)) day 11:00 14:30) #00FF00)
(curve (sum (select "*" (month 1)) day 17:30 20:30) #0000FF)
(curve (sum (select "*" (month 1)) day 21:00 2:00) #FFFF00)
(curve (avg (select "*" (month 1)) day 2:00) #0 1.0)
)
Bei der Entwicklung neuer Berichtsvorlagen bzw. Diagrammdefinitionen ist es klug, sich existierende Daten herzunehmen und leicht zu modifizieren.

### Syntax

Es gelten folgende syntaktische Vereinbarungen:

- jede Funktionsdefinition wird mit einer öffnenden runden Klammer begonnen und mit einer schließenden runden Klammer beendet, dieser Abschnitt zwischen den runden Klammern ist das, was zum Aurfuf vollständig angegeben werden muß
- der Name der Funktion ist das erste, was hinter der öffnenden runden Klammer folgt
- alle weiteren Angaben bis zur schließenden Klammer sind Typen von möglichen Parametern, jeder Typ wird in geschweiften Klammern geschrieben
- die Menge möglicher Typen ist begrenzt, folgende Typen sind möglich:

{int}, {double} | vorzeichenbehaftete Zahlen | 5, 3.14
{string} | Zeichenketten beliebiger Länge | "Hugo"
{time} | meist eine Zeitdauer, selten tatsächlich eine Uhrzeit | 09:13
{timestamp} | ein konkreter Zeitpunkt mit Datum und Uhrzeit | 2009-12-31-7:30:01
{selection} | eine Menge von Zeitstempel-Wert-Paaren |
{color} | eine RGB-Farbe in hexadezimaler Schreibweise | #00FF00
{nothing} | nur für Funktionsrückgabetypen: das Ergebnis der Funktion ist leer und kann nicht weiter ausgewertet werden |

- hinter der schließenden runden Klammer folgt ein Pfeil und ein weiterer Typ, dies ist der Typ des Funktionsergebnisses

Hinweis: In einigen Fällen werden Zeitstempel als Parameter verwendet. Zeitstempel besitzen eigentlich die Syntax CCYY-MM-DD hh:mm[:ss], haben also eigentlich ein Leerzeichen in der Mitte. Damit hier beim Funktionsaufruf klar ist, daß es sich bei diesen Zeitstempeln um einen und nicht um zwei Parameter handelt, ist hier ein - (Minuszeichen) als Bindeglied zwischen Datum und Uhrzeit erforderlich.

### Funktionen

Zeitspannen
Die folgenden Funktionen berechnen jeweils eine konkrete Zeitspanne. Ein mögliches {int}-Argument ist schlicht ein Faktor. Ist kein {int}-Argument angegeben, so gilt implizit der Faktor 1. Die Pluralform hat keinerlei inhaltliche Bedeutung, es handelt sich lediglich um eine intuitivere Schreibweise.
(second) → {time} (second {int}) → {time} (seconds {int}) → {time} (minute) → {time} (minute {int}) → {time} (minutes {int}) → {time} (hour) → {time} (hour {int}) → {time} (hours {int}) → {time} (day) → {time} (day {int}) → {time} (days {int}) → {time} (week) → {time} (week {int}) → {time} (weeks {int}) → {time} | Die Namen dieser Funktionen sind selbsterklärend.
(month) → {time} (month {int}) → {time} (months {int}) → {time} | Achtung, ein Monat im hier relevanten Sinne entspricht genau 30 Tagen. Die Angaben (month) und (days 30) sind synonym.
(year) → {time} (year {int}) → {time} (years {int}) → {time} | Achtung, ein Jahr im hier relevanten Sinne entspricht genau 365 Tagen. Die Angaben (year) und (days 365) sind synonym.

now
(now) → {timestamp} | Die Funktion now berechnet den aktuellen Zeitstempel.
Hinweis 1: Der Wert entspricht bei Verwendung des Kommandozeilenparameters -f dem Zeitpunkt des Programmstarts, auch wenn seitdem aufgrund längerer Berechnungen bereits ein paar Sekunden vergangen sein mögen. D.h. alle now-Aufrufe liefern bei Verwendung von -f denselben Zeitstempel. Das ist notwendig, um Zwischenergebnisse aufheben zu können. Hinweis 2: Der von der Funktion now zurückgegebene Wert kann mit dem Kommandozeilenparameter -n explizit auf einen konkrete Zeitstempel gesetzt werden. Damit lassen sich bei geschickt gestalteten Berichtsvorlagen und Diagrammdefinitionen Berichte und Diagramme für beliebige Zeitpunkte in der Vergangenheit erstellen.

Selektion
Die fünf Implementationen der select-Funktion holen Werte aus einer collection und legen diese in einer selection ab. Eine selection ist ein zeitlich begrenzter, nicht unbedingt lückenloser Ausschnitt aus einer collection. Der obligatorische {string}-Parameter benennt jeweils die collection. Ist die collection leer oder wird entsprechend der zeitlichen Einschränkungen kein einziger Wert in der collection gefunden, so ist das Ergebnis leer.
(select {string}) → {selection} | Die Funktion holt alle Datenwerte der collection.
(select {string} {timestamp}) → {selection} | Die Funktion holt den Datenwert aus der collection, der zu dem angegebenen {timestamp} paßt.
(select {string} {timestamp} {timestamp}) → {selection} | Die Funktion holt alle Datenwerte aus der collection, die in der Zeitspanne zwischen den beiden {timestamp}-Parametern liegen.
(select {string} {time}) → {selection} | Die Funktion holt alle Datenwerte aus der collection, die in der Zeitspanne (now)-{time} bis (now) liegen.
(select {string} {time} {timestamp}) → {selection} | Die Funktion holt alle Datenwerte aus der collection, die in der Zeitspanne {timestamp}-{time} bis {timestamp} liegen.
Beispiele: (select "*") alle Werte der default collection holen (select "*" 2009-12-01-12:34) Werte der default collection seit dem 01.12.2009 12:34 holen (select "n" 2009-01-01-0:00 2010-01-01-0:00) alle Werte der collection n des Jahres 2009 holen (select "l" (weeks 2)) alle Werte der collection l der letzten zwei Wochen holen (select "l" (months 3) 2009-06-01-0:00) alle Werte der collection l aus den drei Monaten vor dem 01.06.2009 holen

merge
Die Funktion merge erlaubt das Mischen von selections, d.h. von Werten, die nicht aus derselben collection stammen. Das ist nützlich, wenn man Werte kategorisiert in verschiedenen collections ablegt, die aber doch zusammengehören.
(merge keyword ...) → {selection} | keyword benennt die Funktion, die benutzt wird, wenn zwei Werte verschiedener collections den gleichen Zeitstempel aufweisen. Folgende Angaben sind für keyword erlaubt: avg (Durchschnitt bilden) min (den kleineren Wert nehmen) max (den größeren Wert nehmen) sum (beide Werte summieren) Die offene Parameterliste hinter keyword erlaubt ausschließlich selections, und zwar mindestens zwei.
Beispiel: (merge avg (select "*" (days 7)) (select "x" (days 5)))

fold
Die Funktion fold erlaubt das "Falten" der Zeitachse einer selection. Man stelle sich die collection auf einem Streifen Papier vor und falte diesen in Gedanken ein paar Mal, so daß Zeitabschnitte übereinanderliegen. Auf diese Weise wird es möglich, beispielsweise Tage oder Monate miteinander zu vergleichen. Der Sinn dieser Funktion liegt darin, beispielsweise den Verlauf einer durchschnittlichen Tageskurve zu ermitteln, jedoch auf Basis mehrerer Tage mit dann sehr vielen Meßwerten.
(fold keyword1 keyword2 {selection}) → {selection} | keyword1 steuert das Interval, anhand dessen die Faltung erfolgen soll. Anstelle von keyword1 sind folgende Angaben denkbar: year, month, day, hour oder minute. Wird beispielsweise mit der Angabe day gefaltet, so werden aus den Zeitstempeln der selection alle Bestandteile bis hin zum Tag entfernt, d.h. es bleibt nur noch die Uhrzeit übrig. Alle Werte liegen dann auf einer Zeitachse, die nur noch 24 Stunden umfaßt. keyword2 hat dieselbe Funktion wie keyword in der merge-Funktion. Auch hier sind folgende Angaben denkbar: avg, min, max, sum, first, last.
Hinweis: auch die selection im Ergebnis einer fold-Operation muß Zeitstempel enthalten. Es kann jedoch bei der Faltung eines Zeitbereichs kein absoluter Zeitstempel mehr ausgemacht werden, mehrere liegen ja übereinander. Aus diesem Grund tragen die Zeitstempel das Jahr 9999. Entsprechend dem benutzten Interval sind zudem auch weitere Bestandteile dieser Zeitstempel (Monat, Tag usw.) zwar syntaktisch gültig, aber inhaltlich nicht sinnvoll.
Beispiel: selection a (fold day avg (select "a")) (fold day first (select "a")) (fold day last (select "a")) -------------------- --------------------------- ----------------------------- ---------------------------- 2009-12-01 13:01 5.2 9999-01-01 13:01 5.45 <- avg! 9999-01-01 13:01 5.2 <- first! 9999-12-05 13:01 5.7 <- last! 2009-12-02 13:02 5.7 9999-01-01 13:02 5.7 9999-01-01 13:02 5.7 9999-01-01 13:02 5.7 2009-12-03 13:03 3.2 9999-01-01 13:03 3.2 9999-01-01 13:03 3.2 9999-01-01 13:03 3.2 2009-12-04 13:04 4.8 9999-01-01 13:04 4.8 9999-01-01 13:04 4.8 9999-01-01 13:04 4.8 2009-12-05 13:01 5.7 2009-12-06 13:06 5.3 9999-01-01 13:06 5.3 9999-01-01 13:06 5.3 9999-01-01 13:06 5.3

Statistik
Die Statistikfunktionen dienen zur Ausführung typischer, statistischer Berechnungen auf einer selection. Die Funktionen berechnen im Einzelnen: avg: den arithmetischen Durchschnitt count: die Anzahl der Werte first: den zeitlich ersten Wert last: den zeitlich letzten Wert max: den größten Wert min: den kleinsten Wert sdv: die Standardabweichung sum: die Summe aller Werte Diese Funktionen liegen zunächst in jeweils fünf Implementierungen vor (func ist ein Platzhalter für den Funktionsnamen).
(func {selection}) → {selection} | die Berechnung auf die gesamte selection anwenden, das Ergebnis enthält eine einzelne Zeile
(func {selection} {time} {time}) → {selection} | die Berechnung auf die gesamte selection anwenden, dabei aber nur die Werte zwischen den beiden angegebenen Tageszeiten berücksichtigen, das Ergebnis enthält eine einzelne Zeile
Die folgenden Implementierungen benutzen ein keyword, das das Aggregationsintervall benennt. Dieses keyword kann lauten: year, month, day, hour, minute, second. Das Ergebnis enthält dann eine Zeile pro Interval. Benutzt man beispielsweise day als Aggregationsinterval, so liefert der Aufruf im Ergebnis eine Zeile pro in der ursprünglichen selection enthaltenem Tag.
(func {selection} keyword) → {selection} | die Berechnung auf die gesamte selection anwenden, dabei entsprechend keyword aggregieren
(func {selection} day {time}) → {selection} | die Berechnung auf die gesamte selection anwenden, dabei entsprechend keyword aggregieren, die angegebene Uhrzeit wird als Mitternacht gewertet, dies erlaubt es, auch Werte, die nach 0 Uhr liegen noch dem Vortag zuzurechnen
(func {selection} keyword {time} {time}) → {selection} | die Berechnung auf die gesamte selection anwenden, dabei entsprechend keyword aggregieren und nur Werte zwischen den beiden angegebenen Uhrzeiten benutzen
Die Funktionen avg und sdv haben noch eine sechste, gleitende Implementierung, die zur Berechnung jeweils vorhergehende bzw. nachfolgende Werte heranzieht und sehr schöne Kurven liefert.
(func {selection} {int} {int}) → {selection} | die Berechnung auf die gesamte selection anwenden, dabei pro Wert {int} zurückliegende und {int} nachfolgende Werte ermitteln und nur in diesem gleitenden Fenster rechnen, dies ergibt im Ergebnis so viele Werte, wie in der ursprünglichen selection enthalten sind
Beispiele: (avg (select "*")) den Durchschnitt über alle Werte der default collection bilden (max (select "*") day 2:00) das tägliche Maximum der default collection ermitteln, annehmen, daß der Tageswechsel um 2:00 erfolgt (sum (select "n" 3:30 9:00) day) die tägliche Summe über Werte der collection n bilden, aber nur Werte zwischen 3:30 und 9:00 berücksichtigen (avg (select "l" (month)) 5 5) den gleitenden Durchschnitt über je 11 Werte der collection l des letzten Monats bilden (first (select "*" (month)) day 2:00) die jeweils erste Zeile eines jeden Tages des letzten Monats aus der default collection holen (last (select "*" (day)) hour) die jeweils letzte Zeile einer Stunde des letzten Tages aus der default collection holen

Vergleich
(== {selection} {double}) → {selection} (!= {selection} {double}) → {selection} (< {selection} {double}) → {selection} (> {selection} {double}) → {selection} (<= {selection} {double}) → {selection} (>= {selection} {double}) → {selection} | All diese Funktionen vergleichen jeden Wert einer selection mit der angegebenen Konstante und übernehmen nur diejenigen Werte in das Ergebnis, die dem Vergleich genügen. Die selection im Funktionsergebis ist somit höchstens gleich groß wie die als Parameter übergebene selection.
Beispiele: (< (select "*") 5.0) liefert alle Werte aus der default collection, die kleiner als 5.0 sind (>= (select "*") 7.0) liefert alle Werte aus der default collection, die größer oder gleich 7.0 sind

Arithmetik
(+ {double} {double}) → {selection} (- {double} {double}) → {selection} (* {double} {double}) → {selection} (/ {double} {double}) → {selection} | Diese vier Funktionen verknüpfen jeweils zwei Zahlen miteinander. Achtung: das Ergebnis ist dennoch eine selection, um es einfacher weiterverarbeiten zu können. Diese selection hat nur eine Zeile und auf dieser keinen Zeitstempel.
(+ {selection} {double}) → {selection} (- {selection} {double}) → {selection} (* {selection} {double}) → {selection} (/ {selection} {double}) → {selection} | Diese Gruppe von Funktionen verknüpft jeweils jeden Wert der übergebenen selection mit einer Zahl. Das Ergebnis hat genauso viele Zeilen wie die übergebene selection.
(+ {selection} {selection}) → {selection} (- {selection} {selection}) → {selection} (* {selection} {selection}) → {selection} (/ {selection} {selection}) → {selection} | Diese vier Funktionen verknüpfen jeweils zwei selections. Dabei werden Zeile für Zeile ihre Zeitstempel verglichen und als Schlüssel benutzt. Die Anzahlen der Zeilen in beiden selections müssen allerdings nicht gleich sein. Wenn in der zweiten selection keine gleich alte Zeile wie in der ersten selection vorhanden ist, wird die nächst ältere genommen. Das Ergebnis hat so viele Zeilen wie die erste selection. Die Zeitstempel des Ergebnisses entsprechen ebenfalls der ersten selection. Wesentlich ist, daß auch für die erste Zeile in der ersten selection eine brauchbare, d.h. gleich alte oder ältere Zeile in der zweiten selection erforderlich ist. pdx gibt einen Fehler aus, wenn diese Bedingung nicht erfüllt ist. Nützlich sind diese vier Implementierungen insbesondere dann, wenn man zwei selections hat, die Zähler und Nenner eines Quotienten darstellen, wenn man also Werte hat, die auf irgendeine Basis bezogen sind, d.h. spezifische Werte, z.B. ein Benzinverbrauch pro 100km.
(+ {timestamp} {time}) → {timestamp} (- {timestamp} {time}) → {timestamp} | Diese beiden Funktionen rechnen zu einem Zeitstempel {timestamp} eine Zeitdauer {time} hinzu oder hinweg. Das Ergebnis is folgerichtig wieder ein Zeitstempel.
Beispiel 1: (+ 2010-17-12-00:00:00 (days 3)) ergibt den 20.12.2010, 0 Uhr Beispiel 2: selection a selection b (* (select "a") (select "b")) -------------------- -------------------- ----------------------------- 2009-11-17 12:38 9.3 2009-12-01 13:00 5.2 -> 5.2 * 9.3 = 2009-12-01 13:00 48.36 2009-12-02 13:00 5.7 -> 5.7 * 9.3 = 2009-12-02 13:00 53.01 2009-12-03 13:00 3.2 -> 3.2 * 9.3 = 2009-12-03 13:00 18.24 2009-12-03 19:17 8.4 2009-12-04 13:00 4.8 -> 4.8 * 8.4 = 2009-12-04 13:00 40.32 2009-12-05 13:00 5.7 2009-12-05 13:00 4.7 -> 5.7 * 4.7 = 2009-12-05 13:00 26.79 2009-12-06 13:00 5.3 -> 5.3 * 4.7 = 2009-12-06 13:00 30.21

HbA1c
Diese Funktionen sind spezifisch für Diabetiker. Sie berechnen den Wert HbA1c anhand der Blutzuckerwerte der letzten 90 Tage. Diese Datenmenge ist in der collection erforderlich. Die Funktion HbA1c gewichtet alle Werte in der collection gleich, während die Funktion HbA1c2 jüngeren Werten ein höheres Gewicht gibt. Sie schwankt deshalb etwas mehr als erstere. Der {string}-Parameter bei allen HbA1c-Funktionen benennt die collection, aus der die Werte entnommen werden.
(HbA1c {string}) → {selection} | berechnet HbA1c von heute, das Ergebnis enthält nur eine Zeile
(HbA1c {string} {timestamp}) → {selection} | berechnet HbA1c vom angegebenen {timestamp}, das Ergebnis enthält nur eine Zeile
(HbA1c {string} {timestamp} {timestamp}) → {selection} | berechnet HbA1c in der Zeitspanne vom ersten {timestamp} bis zum zweiten {timestamp}, das Ergebnis enthält so viele Zeilen, wie in dieser Zeitspanne Werte gefunden werden
(HbA1c {string} {time}) → {selection} | berechnet HbA1c in der Zeitspanne von (now)-{time} bis (now), das Ergebnis enthält so viele Zeilen, wie in dieser Zeitspanne Werte gefunden werden
(HbA1c {string} {time} {timestamp}) → {selection} | berechnet HbA1c in der Zeitspanne von {timestamp} - {time} bis {timestamp}, das Ergebnis enthält so viele Zeilen, wie in dieser Zeitspanne Werte gefunden werden
(HbA1c2 {string}) → {selection} | (diese fünf Implementationen verhalten sich hinsichtlich der Parameter genau wie die fünf vorhergehenden)
(HbA1c2 {string} {timestamp}) → {selection}
(HbA1c2 {string} {timestamp} {timestamp}) → {selection}
(HbA1c2 {string} {time}) → {selection}
(HbA1c2 {string} {time} {timestamp}) → {selection}

Berichte
Die Funktionen dieses Abschnitts sind notwendig für die Erstellung von Berichten. Sie geben jeweils eine Zeichenkette zurück, oft einen umfangreichen Block Text. pdx liest die Textvorlage des Berichts, trifft darin auf eine format-Anweisung, wertet diese augenblicklich aus und ersetzt sie an derselben Position im Text mit deren Ergebnis. Diese Funktionen können interaktiv getestet werden.
(format ...) → {string} | Sie erwartet eine beliebig lange Liste von Parametern, die aus Text, Funktionsergebnissen, Formatierungsanweisungen und Schlüsselworten bestehen können: (format "<tr>" "<td>" datetime "</td>" "<td>" (select "*" (days 7)) <1.1> "</td>" "<td>" (select "n" (days 7)) <1> "</td>" "</tr>" newline ) All diese Teilausdrücke ergeben lauter kurze Textstücke, die verkettet werden. Das Ergebnis ist ein ein- oder mehrzeiliges Stück Text beliebiger Länge. Die Anzahl der Zeilen darin richtet sich nach der Anzahl der Werte in den Funktionsergebnissen. Passen Werte entsprechend ihrer Zeitstempel zusammen, so erscheinen sie auf derselben Zeile. Das Schlüsselwort datetime ist ein Platzhalter für den Zeitstempel der Zeile. Das Schlüsselwort newline fügt einen physischen Zeilenwechsel ein. Die Formatieranweisungen erkennt man an ihrer Schreibweise in spitzen Klammern. Sie beziehen sich stets auf den Wert unmittelbar zuvor. Es gibt drei verschiedene Formate: <n>: eine ganze Zahl (ohne Komma) mit mindestens n Stellen ausgeben <n.0>: eine Zahl mit n Vorkommastellen und nur wenn nötig Nachkommastellen ausgeben <n.m>: immer n Vorkomma- und m Nachkommastellen ausgeben Das Ergebnis obigen Beispiels ist echtes HTML: [...] <tr><td>2009-01-17 21:42:49</td><td>5.6</td><td>6</td></tr> <tr><td>2009-01-18 05:54:41</td><td>6.8</td><td>7</td></tr> <tr><td>2009-01-18 12:17:22</td><td>5.4</td><td>6</td></tr> [...]
(empty {string}) → {string} | Innerhalb einer format-Funktion besteht also gelegentlich das Problem, daß man in der Ausgabe leere Werte kennzeichnen, d.h. sichtbar machen will oder muß. Leere Werte entstehen durch die Verknüpfung mehrerer Selektionen zu einer mehrspaltigen Tabelle (outer join). Mit Hilfe der empty-Funktion kann man der umgebenden format-Funktion mitteilen, welche Zeichenkette {string} anstelle eines leeren Werts in die Ausgabe eingefügt werden soll.

Diagramme
Die folgenden Funktionen zeichnen etwas Sichtbares in ein Diagramm. Sie können daher nicht in der interaktiven Betriebsart von pdx erprobt werden. Das Ergebnis all dieser Funktionen ist außerdem immer vom Typ {nothing}, man kann es nicht für weitere Berechnungen nutzen.
(diagram {int} {int} {color} ...) → {nothing} | Die diagram-Funktion ist ein wrapper. Sie umschließt die gesamte Definition eines konkreten Diagramms. Der erste {int}-Parameter benennt die Größe de Diagramms in x-Richtung, der zweite {int}-Parameter die Größe in y-Richtung. Der {color}-Parameter gibt die Hintergrundfarbe in RGB-Notation an. Die dann folgende offene Parameterliste enthält Aufrufe anderer Diagrammfunktionen, insbesondere mindestens eine axes- und eine curve-Funktion.
Die vier Implementierungen der axes-Funktion zeichnen jeweils ein komplettes und beschriftetes Koordinatensystem, um dessen Einzelheiten sich der Benutzer nicht mehr kümmern muß. Dabei bedeuten die drei gemeinsam benutzten {double}-Parameter 1. den unteren Wert der y-Achse, 2. den oberen Wert der y-Achse, 3. die Strichdicke der Achse. Der {color}-Parameter benennt die Farbe beider Achsen und ihrer Beschriftung.
(axes {timestamp} {timestamp} {double} {double} {double} {color}) → {nothing} | x-Achse im Zeitabschnitt zwischen den beiden {timestamp}-Parametern
(axes {time} {timestamp} {double} {double} {double} {color}) → {nothing} | x-Achse im Zeitabschnitt zwischen {timestamp}-{time} und {timestamp}
(axes {time} {double} {double} {double} {color}) → {nothing} | x-Achse im Zeitabschnitt zwischen (now)-{time} und (now)
(axes keyword {double} {double} {double} {color}) → {nothing} | mit keyword = year, month, day, hour oder minute, speziell für das Zeichnen von Daten, die aus einem Aufruf der Funktion fold stammen. Dabei ist dasselbe Interval anzugeben.
(curve {selection} {color} ...) → {nothing} | Die Funktion zeichnet eine Kurve im Diagramm. Ohne weitere Parameter wird eine Zickzacklinie in der angegebenen Farbe gezeichnet, einfach durch Verbinden der Datenwerte, die in der selection übergeben werden. Durch zusätzliche Parameter kann das Verhalten erheblich beeinflußt werden: Das Schlüsselwort bars erzeugt vertikale Balken anstatt einer Zickzacklinie. Eine Zeichenkette der Form "+", "|", "-", "x" oder "°" erzeugt keine Linie, sondern einzelne, nicht verbundene Punkte, die durch das jeweilige Symbol gekennzeichnet werden. Eine {double}-Zahl ermöglicht die Angabe einer Strichdicke (nur bei Zickzacklinie sinnvoll). Bei Balkendarstellung können mit Hilfe von zwei {int}-Zahlen mehrere Balken pro Aggregationsintervall dargestellt werden. Dies klingt schwierig, ist aber anhand eines Beispiels leicht zu verstehen. Angenommen, wir haben Werte an vier verschiedenen Tageszeiten, sagen wir morgens, mittags, abends und spät. Und wir wollen jeden Tag durch vier nebeneinanderstehende Balken darstellen, die für diese Tageszeiten stehen. Dann wäre es erforderlich, die Balken so zu platzieren, daß sie sich nicht überdecken: (curve (sum (select "n" (week)) day 3:30 9:30) #FF1000 bars 1 4) (curve (sum (select "n" (week)) day 11:00 14:30) #FF5000 bars 2 4) (curve (sum (select "n" (week)) day 17:30 20:30) #FF9000 bars 3 4) (curve (sum (select "n" (week)) day 21:00 2:00) #FFB000 bars 4 4) Diese vier Zeilen unterscheiden sich in den Selektionen, den Balkenfarben und dem ersten {int}-Parameter. Dieser benennt die Nummer des aktuellen Balkens, der zweite sagt, wieviele Balken es überhaupt gibt. Die erste Zeile zeichnet also den ersten von vier Balken. pdx berechnet dann, wie breit die Balken gezeichnet werden müssen, damit sie sauber nebeneinander kommen. Im Beispiel hat also ein Balken jeweils die Breite eines Viertels der Breite eines Tages auf der x-Achse. Man kann damit etwas experimentieren. Man muß auch nicht jeden Balken zeichnen, d.h. man könnte damit auch Lücken erzeugen, Balken gruppieren usw.
Die beiden Implementierungen der hline-Funktion zeichnen eine horizontale Linie in der angegeben Farbe.
(hline {double} {color}) → {nothing} | {double} bezeichnet die Position der Linie auf der y-Achse
(hline {double} {double} {color}) → {nothing} | der erste {double}-Parameter bezeichnet die Position der Linie auf der y-Achse, der zweite gibt die Strichdicke der Linie an
Die beiden Implementierungen der vline-Funktion zeichnen eine vertikale Linie in der angegeben Farbe.
(vline {timestamp} {color}) → {nothing} | {timestamp} bezeichnet die Position der Linie auf der x-Achse
(vline {timestamp} {double} {color}) → {nothing} | {timestamp} bezeichnet die Position der Linie auf der x-Achse, {double} bezeichnet die Strichdicke der Linie
(vline {time} {color}) → {nothing} | {time} bezeichnet die Position der Linie auf der x-Achse, speziell für das Zeichnen von Daten, die aus einem Aufruf der Funktion fold stammen
(vline {time} {double} {color}) → {nothing} | {time} bezeichnet die Position der Linie auf der x-Achse, {double} bezeichnet die Strichdicke der Linie, speziell für das Zeichnen von Daten, die aus einem Aufruf der Funktion fold stammen
Beispiel: (diagram 500 375 #FFFDFD (axes day 3.0 9.0 1.0 #0) (hline 4.5 #C0C0C0) (hline 5.0 #C0C0C0) (hline 5.5 #C0C0C0) (hline 6.0 #C0C0C0) (hline 6.5 #C0C0C0) (hline 7.0 #C0C0C0) (hline 7.5 #C0C0C0) (vline 5:45 #C0C0C0) (vline 12:30 #C0C0C0) (vline 18:30 #C0C0C0) (vline 21:30 #C0C0C0) (curve (fold day first (merge avg (select "*" (week)) (select "x" (week)))) #FF0000 "+") (curve (avg (fold day first (merge avg (select "*" (week)) (select "x" (week)))) 3 3) #000000 2.0) (curve (avg (fold day first (merge avg (select "*" (year)) (select "x" (year)))) 30 30) #000000) )

sonstiges
(build) → {string} | Die Funktion liefert den build-string von pdx. Dieser enthält Angaben darüber, wann und wie pdx compiliert wurde, d.h. auch welche Optionen benutzt wurden und welche optionalen Fähigkeiten das Programm besitzt.
(database) → {string} | Die Funktion liefert Name und Version des verwendeten Datenbanksystems.
(version) → {string} | Die Funktion liefert die Version von pdx.
Beispiele: (build) Dec 15 2010, 17:25:30, USE_SQLITE, USE_MYSQL, USE_READLINE, USE_BOARD, USE_CAIRO, USE_ETPAN (database) MySQL 5.1.51 (version) 1.2.0

pdx Konfiguration | Index | pdx Interaktiver Betrieb