Fünf Units für HL7 Version 2 — den im Gesundheitswesen meistverbreiteten Nachrichtenstandard. std.hl7 deckt den gesamten Stack ab: MLLP-TCP-Framing, MSH/ACK-Engine, Patientenverwaltung (ADT), Auftragswesen (ORM/OML/RAS), Befundübermittlung (ORU/OUL) und Terminplanung (SIU).
→ Übersicht · Standard Library · Welche Unit?
Alle Beispiele dieser Seite sind mit lyxc 1.0.21A übersetzt und ausgeführt; die gezeigten Ausgaben sind die echten Programmausgaben.
| Unit | WP | Beschreibung |
|---|---|---|
| std.hl7.core | WP-HL7-00 | MLLP-Framing (VT+FS+CR), MSH-Parser, ACK-Generator, Duplikatserkennung (FNV1a-Ring), Versions- und Testmodus-Prüfung |
| std.hl7.adt | WP-HL7-01 | Patient Administration: ADT^Axx parsen/schreiben; PID, PV1, MRG, NK1; automatische Merge-Erkennung (A34–A45) |
| std.hl7.orders | WP-HL7-02 | Order Management: ORM^O01, OML^O21, RAS^O17 parsen/schreiben; ORC, OBR, RXA; STAT-Erkennung; ORR^O02-Quittung |
| std.hl7.results | WP-HL7-03 | Result Reporting: ORU^R01, OUL^R22 parsen/schreiben; OBX (Panikwert- und Korrektur-Flags), NTE-Kommentare |
| std.hl7.scheduling | WP-HL7-04 | Terminplanung: SIU^S12–S26 parsen/schreiben; SCH, AIS, AIG, AIL; Flags für Absage (S15), Löschung (S17), Nicht-Erscheinen (S26) |
Alle vier Fachunits importieren std.hl7.core — import std.hl7.adt; zieht den Kern also mit.
^in DokuWiki-Tabellen. Der Nachrichtentyp wird in HL7 mit^geschrieben (ADT^A01). In einer Wiki-Tabelle trennt genau dieses Zeichen die Zellen — deshalb steht es hier überall in…. Wer diese Seite bearbeitet: bitte beibehalten, sonst zerfällt die Tabelle in Spalten.
HL7 (Health Level 7) Version 2 ist der faktische Standard für Datenintegration in Krankenhäusern. Jede Nachricht besteht aus zeilenweise (CR) geschriebenen Segmenten (3 Großbuchstaben + Feldtrenner), die |-getrennte Felder und ^-getrennte Komponenten enthalten:
MSH|^~\&|LIS|HOSPITAL|HIS||20260616083000||ORU^R01|MSG001|P|2.5.1
PID|||PAT123^^^MPI||Muster^Max||19850315|M
OBR|1|ORD042|LAB042|1743-4^Glucose^LN|||20260616082000
OBX|1|NM|1743-4^Glucose^LN||195|mg/dL|70-99||||HH||F
Die Standard-Trennzeichen stehen in MSH-1 und MSH-2 und werden von Hl7MshParse aus der Nachricht gelesen, nicht angenommen:
| Ebene | Zeichen | Beispiel |
|---|---|---|
| Feld | | | MSH|^~\&|LIS |
| Komponente | ^ | Muster^Max |
| Wiederholung | ~ | ID1~ID2 |
| Escape | \ | \F\ für ein literales | |
| Subkomponente | & | A&B |
HL7 v2-Nachrichten werden über TCP immer im MLLP-Frame übertragen (Minimal Lower Layer Protocol):
[VT=0x0B] + HL7-Nachrichtenbytes + [FS=0x1C] + [CR=0x0D]
MllpFrameWrite legt den Rahmen um eine fertige HL7-Nachricht, MllpFrameRead schält die Nachricht aus dem TCP-Empfangspuffer. MllpFrameRead sucht das VT auch mitten im Puffer — Datenmüll davor wird übersprungen.
HL7_*_SIZE-Konstanten.Hl7OruParse) — die einzelnen OBX-Segmente werden getrennt mit Hl7ObxParse in einer Schleife geparst. Genauso bei NTE, AIS, AIG und AIL.HL7_ORD_ISSTAT, HL7_OBX_ISCRIT/HL7_OBX_ISCORR sowie HL7_APPT_ISCANCEL/ISDELETE/ISNOSHOW setzt der Parser selbst — keine manuellen String-Vergleiche nötig.Zeiger aus einer Struct sind nicht nullterminiert. Jedes Feld liegt als Paar vor: OffsetXhält den Zeiger, OffsetX_LENdie Länge. Wer den Wert anPrintLnoderStrConcatübergibt, druckt den Rest der Nachricht mit. Für die Ausgabe eine terminierte Kopie anlegen — siehe die FunktionFeld()im Quickstart unten.
HIS/ADT-System HL7-Gateway / Lyx
| |
|-- ADT^A01 (Aufnahme) ->| Hl7AdtParse()
|<-- ACK^AA (Quittung) --| Hl7AckWrite() + MllpFrameWrite()
| |
|-- ADT^A08 (Update) --->| Hl7AdtParse()
|<-- ACK^AA -------------|
| |
|-- ADT^A03 (Entlassung)>| Hl7AdtParse()
|<-- ACK^AA -------------|
KIS / Station LIS (Labor)
| |
|-- ORM^O01 (Auftrag) -->| Hl7OrmParse()
|<-- ORR^O02 (OK/ER) ----| Hl7OrrWrite()
| | [Analyse laeuft ...]
|<-- ORU^R01 (Befund) ---| Hl7OruParse() + Hl7ObxParse() je OBX
|-- ACK^AA ------------->|
Medikamentensystem Stationssystem
| |
|-- RAS^O17 ------------>| Hl7RasParse()
|<-- ACK^AA -------------|
Terminplaner Fachabteilung
| |
|-- SIU^S12 (neu) ------>| Hl7SiuParse()
|-- SIU^S15 (Absage) --->| HL7_APPT_ISCANCEL = 1
|-- SIU^S26 (No-Show) -->| HL7_APPT_ISNOSHOW = 1
|<-- ACK^AA -------------|
| Funktion | Rückgabe | Zweck |
|---|---|---|
Hl7StateInit(state) | – | Dedup-State nullen; einmalig pro Instanz |
MllpFrameWrite(buf, bufMax, msg, msgLen) | Bytes oder -1 | VT+Nachricht+FS+CR schreiben |
MllpFrameRead(buf, bufLen, out, outMax) | Länge, -1 (kein Frame), -2 (out zu klein) | Nachricht aus dem Frame schälen |
Hl7MshParse(msg, len, msh) | HL7_OK / HL7_ERR_SYNTAX | MSH-Segment in die Struct parsen |
Hl7AckWrite(msh, code, text, out, outMax) | Bytes oder -1 | ACK/NACK erzeugen; code ist "AA"c, "AE"c oder "AR"c als pchar |
Hl7DupCheck(state, partnerKey, ctrlId) | 1 = schon gesehen, 0 = neu | Duplikate je Partner über FNV1a-Ring (64 Einträge) |
Hl7VersionCheck(msh) | HL7_OK / HL7_ERR_VERSION | Unterstützt 2.3.1, 2.4, 2.5, 2.5.1, 2.6, 2.7 |
Hl7IsTestMsg(msh) | 1 = Testnachricht | MSH-11 beginnt mit T — nicht in die Produktivdatenbank schreiben |
Hl7IsUtf8(msh) | 1 = UTF-8 | MSH-18 = UNICODE…; ohne MSH-18 gilt ISO-8859-1, nicht UTF-8 |
Hl7DupCheck registriert die Nachricht beim Rückgabewert 0 gleich mit — ein zweiter Aufruf mit derselben Control-ID liefert 1.
| Funktion | Zweck |
|---|---|
Hl7AdtParse(msg, len, adt) | Ganze Nachricht: MSH-Kopf, PID, PV1, MRG, NK1 in eine Hl7Adt-Struct |
Hl7AdtWrite(adt, event, out, outMax) | Nachricht aus der Struct schreiben; event z. B. "A01"c |
Hl7PidParse · Hl7Pv1Parse · Hl7MrgParse · Hl7Nk1Parse | Einzelsegment parsen (Zeiger auf das Segment, nicht auf die Nachricht) |
Hl7AdtIsMerge(eventPtr, eventLen) | 1 bei A34–A45 (Merge-/Move-Ereignisse) |
| Funktion | Zweck |
|---|---|
Hl7OrmParse · Hl7OmlParse | Auftragsnachricht in eine Hl7Order-Struct |
Hl7RasParse(msg, len, admin) | Medikamentengabe in eine Hl7RxAdmin-Struct |
Hl7OrcParse · Hl7ObrParse · Hl7RxaParse | Einzelsegmente |
Hl7OrmWrite · Hl7OmlWrite | Auftrag schreiben |
Hl7OrrWrite(order, status, out, outMax) | Auftragsquittung ORR^O02 |
HL7_ORD_MSGTYPE unterscheidet die Herkunft (1 = ORM, 2 = OML), HL7_ORD_ISSTAT meldet einen STAT-Auftrag (OBR-5 = S).
| Funktion | Zweck |
|---|---|
Hl7OruParse · Hl7OulParse | Befundkopf (OBR + PID) in eine Hl7Report-Struct |
Hl7ObxParse(seg, segLen, obx) | Ein OBX-Segment; je Beobachtung einmal aufrufen |
Hl7NteParse(seg, segLen, note) | Kommentarsegment |
Hl7OruWrite(report, obx, out, outMax) | Befund schreiben |
HL7_RPT_MSGTYPE ist HL7_RPT_TYPE_ORU (1) oder HL7_RPT_TYPE_OUL (2). HL7_OBX_ISCRIT markiert Panikwerte (OBX-8 HH/LL), HL7_OBX_ISCORR korrigierte Ergebnisse (OBX-11 C).
| Funktion | Zweck |
|---|---|
Hl7SiuParse(msg, len, appt) | Terminnachricht in eine Hl7Appointment-Struct |
Hl7SiuWrite(appt, out, outMax) | Termin schreiben |
Hl7SchParse · Hl7AisParse · Hl7AigParse · Hl7AilParse | Einzelsegmente: Termin, Leistung, Ressource, Ort |
| Konstante | Wert | Bedeutung |
|---|---|---|
HL7_OK | 0 | erfolgreich |
HL7_ERR_TRUNC | 1 | Nachricht abgeschnitten |
HL7_ERR_SYNTAX | 2 | Segment nicht lesbar (z. B. kein MSH am Anfang) |
HL7_ERR_DUP | 3 | Duplikat |
HL7_ERR_OVERFLOW | 4 | Zielpuffer zu klein |
HL7_ERR_VERSION | 5 | HL7-Version nicht unterstützt |
Die Schreib-Funktionen (…Write) melden Überlauf dagegen mit -1 als Rückgabewert, nicht mit HL7_ERR_OVERFLOW — der Rückgabewert ist dort die geschriebene Bytezahl.
HL7_ACK_AA / HL7_ACK_AE / HL7_ACK_AR (1/2/3) sind Kennzahlen für eigene Zustandslogik. Hl7AckWrite erwartet nicht sie, sondern den zweibuchstabigen Code als pchar — seit #1617 ohne Cast: "AA"c.
| Struct | Konstante | Bytes | Unit | Zweck |
|---|---|---|---|---|
| Hl7Msh | HL7_MSH_SIZE | 192 | core | MSH-Header einer beliebigen Nachricht |
| Hl7Slot | HL7_SLOT_SIZE | 528 | core | Ein Partner-Slot des Dedup-Rings (16 + 64×8) |
| Hl7State | HL7_STATE_SIZE | 8448 | core | Dedup-State (16 Partner × 528 Bytes) |
| Hl7Pid | HL7_PID_SIZE | 176 | adt | Patient Identification |
| Hl7Pv1 | HL7_PV1_SIZE | 160 | adt | Patient Visit / Stationsbelegung |
| Hl7Mrg | HL7_MRG_SIZE | 48 | adt | Patient Merge |
| Hl7Nk1 | HL7_NK1_SIZE | 48 | adt | Next of Kin / Angehörige |
| Hl7Adt | HL7_ADT_SIZE | 544 | adt | Vollständige ADT-Nachricht (Kopf + PID + PV1 + MRG + NK1) |
| Hl7Order | HL7_ORD_SIZE | 192 | orders | Auftragsstruktur (ORC + OBR + PID + PV1) |
| Hl7RxAdmin | HL7_RXA_SIZE | 112 | orders | Medikamentengabe (RXA) |
| Hl7ObxResult | HL7_OBX_SIZE | 144 | results | Einzelbeobachtung mit Panikwert-Flag |
| Hl7NteNote | HL7_NTE_SIZE | 16 | results | Kommentar (NTE) |
| Hl7Report | HL7_RPT_SIZE | 104 | results | Befundkopf (OBR + PID, ohne OBX) |
| Hl7Sch | HL7_SCH_SIZE | 128 | scheduling | Terminsegment (SCH) |
| Hl7Ais | HL7_AIS_SIZE | 48 | scheduling | Terminleistung (AIS) |
| Hl7Aig | HL7_AIG_SIZE | 48 | scheduling | Terminressource (AIG) |
| Hl7Ail | HL7_AIL_SIZE | 32 | scheduling | Terminort (AIL) |
| Hl7Appointment | HL7_APPT_SIZE | 152 | scheduling | Vollständige SIU-Nachricht |
Die eingebetteten Structs der ADT-Nachricht liegen an festen Offsets: HL7_ADT_PID = 112, HL7_ADT_PV1 = 288, HL7_ADT_MRG = 448, HL7_ADT_NK1 = 496. Ein Feld daraus wird also mit peek64(adt + HL7_ADT_PID + HL7_PID_FAMILY) gelesen.
Empfangen, prüfen, parsen, quittieren — der vollständige Weg einer ADT-Nachricht:
import std.hl7.core;
import std.hl7.adt;
import std.alloc;
import std.string;
// Zero-Copy: die Struct-Felder zeigen in den Nachrichtenpuffer und sind
// NICHT nullterminiert. Fuer die Ausgabe eine Kopie mit \0 anlegen.
fn Feld(ptr: int64, len: int64): pchar {
var buf: int64 := alloc(len + 1);
var i: int64 := 0;
while (i < len) { poke8(buf + i, peek8(ptr + i)); i := i + 1; }
poke8(buf + len, 0);
return buf as pchar;
}
fn main(): int64 {
// Dedup-State: einmalig pro Server-Instanz
var state: int64 := alloc(HL7_STATE_SIZE);
Hl7StateInit(state);
// Eine Nachricht, wie sie aus dem Socket kommt (hier selbst gerahmt)
var msg: pchar := "MSH|^~\\&|HIS|KH|LIS|LAB|20260813081500||ADT^A01|MSG001|P|2.5.1\rPID|||PAT123^^^MPI||Muster^Max||19850315|M\rPV1||I|ITS^201^A^KH|||||||||||||||V4711\r"c;
var tcpBuf: int64 := alloc(65536);
var tcpLen: int64 := MllpFrameWrite(tcpBuf, 65536, msg as int64, StrLen(msg));
// MLLP-Frame auspacken
var msgBuf: int64 := alloc(65536);
var msgLen: int64 := MllpFrameRead(tcpBuf, tcpLen, msgBuf, 65536);
if (msgLen < 0) { return 1; }
// MSH parsen und pruefen
var msh: int64 := alloc(HL7_MSH_SIZE);
if (Hl7MshParse(msgBuf, msgLen, msh) != HL7_OK) { return 1; }
if (Hl7VersionCheck(msh) != HL7_OK) { return 1; }
if (Hl7IsTestMsg(msh) == 1) { PrintLn("Testnachricht - nicht speichern"); }
// Doppelte Nachricht? (Partnerschluessel: sendende Anwendung)
var sendApp: int64 := peek64(msh + HL7_MSH_SEND_APP);
var ctrlId: int64 := peek64(msh + HL7_MSH_CTRL_ID);
if (Hl7DupCheck(state, sendApp, ctrlId) == 1) { PrintLn("Duplikat"); return 0; }
// ADT vollstaendig parsen
var adt: int64 := alloc(HL7_ADT_SIZE);
Hl7AdtParse(msgBuf, msgLen, adt);
var famPtr: int64 := peek64(adt + HL7_ADT_PID + HL7_PID_FAMILY);
var famLen: int64 := peek64(adt + HL7_ADT_PID + HL7_PID_FAMILY_LEN);
var bedPtr: int64 := peek64(adt + HL7_ADT_PV1 + HL7_PV1_LBED);
var bedLen: int64 := peek64(adt + HL7_ADT_PV1 + HL7_PV1_LBED_LEN);
PrintLn(StrConcat("Nachname: ", Feld(famPtr, famLen)));
PrintLn(StrConcat("Bett: ", Feld(bedPtr, bedLen)));
// ACK bauen und rahmen
var ackBuf: int64 := alloc(512);
var ackLen: int64 := Hl7AckWrite(msh, "AA"c, "OK"c, ackBuf, 512);
var outBuf: int64 := alloc(600);
var outLen: int64 := MllpFrameWrite(outBuf, 600, ackBuf, ackLen);
PrintLn(StrConcat("ACK-Bytes: ", IntToStr(outLen)));
free(outBuf, 600); free(ackBuf, 512); free(adt, HL7_ADT_SIZE);
free(msh, HL7_MSH_SIZE); free(msgBuf, 65536); free(tcpBuf, 65536);
free(state, HL7_STATE_SIZE);
return 0;
}
Nachname: Muster
Bett: A
ACK-Bytes: 66
Das Bett kommt aus PV1-3.3: die Ortsangabe ITS^201^A^KH zerlegt der Parser in Station (ITS), Zimmer (201), Bett (A) und Einrichtung (KH).
\r, nicht \n. Eine mit Zeilenumbrüchen aufgebaute Nachricht wird nicht geparst; der Parser sucht CR (0x0D).msgBuf freigegeben oder wiederbenutzt, zeigen alle Felder der Structs ins Leere. Werte, die länger gebraucht werden, vorher kopieren.Hl7IsUtf8 fragen, bevor der Text weitergereicht wird.Hl7IsTestMsg vor jedem Schreibzugriff auf die Produktivdatenbank aufrufen; MSH-11 T kommt in Kopplungstests regelmäßig vor.Hl7AckWrite erwartet den Code als Text ("AA"c), nicht die Konstante HL7_ACK_AA.Weiterführend: std.hl7.core · std.hl7.adt · std.hl7.orders · std.hl7.results · std.hl7.scheduling · std.net · Rohspeicher
Letzte Aktualisierung: 2026-08-13 — Tabellen repariert (^ in Zellen maskiert), „Vier Units„ → fünf, Funktionsübersicht je Unit, Rückgabewerte, Größentabelle um die scheduling-Structs und HL7_SLOT_SIZE ergänzt, Quickstart lauffähig gemacht (er benutzte eine nicht deklarierte tcpBufLen). Alles gegen std/hl7/*.lyx und lyxc 1.0.21A geprüft.
Codebeispiele geprüft: gegen lyxc 1.2.5C übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die pub fn-Signaturen in aurum/std gehalten, 0 Abweichungen).