Zum Inhalt

Plugin-Referenz

Diese Referenz richtet sich an Anwenderinnen und Anwender, die die Routing-Regeln des DICOM-Routers konfigurieren. Sie beschreibt die mitgelieferten Plugins und ihre Konfiguration — jeweils mit Anwendungsfall, den einstellbaren Feldern und einem vollständigen YAML-Beispiel. Die allgemeine Config-Syntax (Filter, Ziele, Reload) steht in configuration.md.

So werden Plugins aktiviert

Ein Plugin wird nicht global eingeschaltet, sondern pro Verbindung über zwei Blöcke in der Konfiguration verdrahtet:

  • Der actions:-Block definiert Instanzen. Jede benannte Action ist eine Liste von Plugin-Einträgen; jeder Eintrag nennt die Plugin-Klasse (plugin:) und ihre Parameter (config:).
  • Eine filter:-Regel referenziert eine oder mehrere Actions über ihren Namen. Nur wenn eine Verbindung auf die Filter-Regel passt, laufen deren Actions.

Dasselbe Plugin lässt sich mehrfach mit unterschiedlicher Config definieren (Multi-Instance): einmal als tapAlles mit weiter Whitelist, einmal als tapSr nur für Befunde. Welches Gerät welche Variante bekommt, entscheidet die Filter-Regel. Die Reihenfolge der Actions in einer Filter-Regel ist die Ausführungsreihenfolge.

Details zu Filter-Feldern (callingAET, calledAET, IP-/Port-Kriterien, regex:-Präfix), Zielen und dem Hot-Reload per dicom-router-ctl reload stehen in configuration.md.

Ein zu grosses Feld kostet nie die Verbindung. Der Router materialisiert Elementwerte erst beim Zugriff und verweigert das oberhalb von router.materialization-threshold bzw. jenseits von router.max-elements (siehe configuration.md). Die Rewrite-Plugins behandeln ein so verweigertes Element wie ein fehlendes: die betroffene Änderung entfällt, die Nachricht wird unverändert weitergeleitet, und die Association läuft weiter. QrFieldRewriteStage geht einen Schritt vorsichtiger vor und überspringt die Operation, die dieses Feld anspricht, statt das Feld als fehlend zu behandeln — sonst würde ein set ein Element überschreiben, von dem gar nicht feststeht, ob es etwa eine Sequenz ist. Die übrigen Operationen greifen normal. Mit den Vorgabewerten (1 MB) ist der Fall praktisch nicht erreichbar — er wird relevant, wenn die Schwellen deutlich kleiner gesetzt sind.

Achtung, zwei Paket-Namen: Die meisten Plugins liegen unter ch.immeditech.imconnect.dicomrouter.plugin.…, die Beispiel-Plugins PduLoggingStagePlugin und ExamplePluginStage dagegen unter ch.immeditech.imconnect.plugins.… — am sichersten die plugin:-Zeilen aus den Beispielen dieser Referenz kopieren.


PduLoggingStage

Was es tut

Schreibt Protokoll-Mitschnitte einer Verbindung — entweder als Zusammenfassung ins Anwendungslog oder als binäre PDU-Dumps in eine Datei. Nutzt man zur Fehlersuche, wenn eine Gegenstelle sich nicht wie erwartet verhält und man den DICOM-Handshake nachvollziehen muss. Payload/Patientendaten landen nie im Anwendungslog.

Plugin-Klasse

ch.immeditech.imconnect.plugins.PduLoggingStagePlugin

Wichtig: Mitschneiden geht auch ganz ohne dieses Plugin

Die Mitschnitt-Funktion ist in jede Verbindung fest eingebaut — es genügt, in der Filter-Regel logLevel zu setzen; eine Action ist dafür nicht nötig. Dieses Plugin fügt lediglich zusätzliche, benannte Logger-Instanzen hinzu (z. B. mit eigenem stageName zur Zuordnung im Log). Auch für die Plugin-Instanzen gilt: Was sie mitschneiden, steuert der logLevel der Filter-Regel — bei logLevel: 0 schneiden weder die eingebauten noch die zusätzlichen Logger etwas mit.

Steuerung über den Filter-logLevel

Was tatsächlich mitgeschnitten wird, steuert das Feld logLevel der Filter-Regel, nicht die Plugin-Config:

logLevel Wirkung
0 Kein Logging
1 Eingehend, als Zusammenfassung ins Anwendungslog
2 Ausgehend, als Zusammenfassung ins Anwendungslog
3 Ein- und ausgehend, als Zusammenfassung ins Anwendungslog
5 Eingehend, als binärer PDU-Dump in router.log-folder
6 Ausgehend, als binärer PDU-Dump in router.log-folder
7 Ein- und ausgehend, als binärer PDU-Dump

Config-Felder

Feld Pflicht/Default Bedeutung
stageName Default PduLoggingStage Frei wählbarer Name der Instanz; erscheint im Log zur Zuordnung.
firstLogger Default true true = Instanz für die eingehende Seite, false = für die ausgehende.
enabled Default true Schaltet diese Instanz aus, ohne sie aus der Config zu nehmen.

Für vollständige In- und Outbound-Mitschnitte definiert man üblicherweise zwei Einträge in derselben Action — einen mit firstLogger: true, einen mit false.

Beispiel

actions:
  protocolLogging:
    - plugin: ch.immeditech.imconnect.plugins.PduLoggingStagePlugin
      config:
        stageName: "InboundProtocolLogger"
        firstLogger: true
        enabled: true
    - plugin: ch.immeditech.imconnect.plugins.PduLoggingStagePlugin
      config:
        stageName: "OutboundProtocolLogger"
        firstLogger: false
        enabled: true

filter:
  - callingAET: MRI1
    calledAET: PACS
    logLevel: 3                 # Zusammenfassung ins Anwendungslog
    actions:
      - "protocolLogging"
    destination:
      - "pacs1"

Hinweise

  • Binäre Dumps (logLevel 5/6/7) landen im Verzeichnis router.log-folder. Sie können Patientendaten enthalten und sind entsprechend zu schützen.
  • Dieses Plugin ersetzt nicht den DICOM-Tap: Es dient dem Protokoll-Debugging, nicht der Ablage fachlich verwertbarer DICOM-Dateien.

AETitleRewriteStage

Was es tut

Schreibt den Calling- und/oder Called-AE-Titel um, mit dem der Router die Verbindung zum Ziel-PACS aufbaut. Typischer Fall: Ein Gerät sendet mit einem AE-Titel, den das PACS nicht kennt oder nicht akzeptiert — der Router ersetzt ihn nach aussen durch einen abgestimmten Wert. Auf dem Rückweg stellt der Router die ursprünglichen AE-Titel für das Gerät wieder her, sodass das Gerät nichts von der Umschreibung merkt.

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.AETitleRewriteStage

Config-Felder

Feld Pflicht/Default Bedeutung
callingAetOverride optional Neuer Calling-AET nach aussen. Leer/weggelassen = ursprünglicher Wert bleibt erhalten.
calledAetOverride optional Neuer Called-AET nach aussen. Leer/weggelassen = ursprünglicher Wert bleibt erhalten.
logRewrites Default true Protokolliert jede Umschreibung.
enabled Default true Schaltet das Plugin aus, ohne es aus der Config zu nehmen.

AE-Titel dürfen höchstens 16 Zeichen lang sein.

Beispiel

actions:
  aetRewrite_ultrasound:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.AETitleRewriteStage
      config:
        callingAetOverride: "US_GATEWAY"
        calledAetOverride: "PACS_PROD"
        logRewrites: true
        enabled: true

filter:
  - callingAET: US1
    calledAET: PACS
    actions:
      - "aetRewrite_ultrasound"
    destination:
      - "pacs1"

Hinweise

  • Nur eines der beiden Override-Felder zu setzen ist zulässig — das jeweils andere bleibt dann unverändert.
  • Wird zusätzlich C-MOVE oder eine Worklist über den Router geführt, sind meist die Plugins CMoveDestinationRewriteStage bzw. WorklistAetRewriteStage nötig, weil die umgeschriebenen AE-Titel sonst in den Nachrichteninhalten nicht mitgezogen werden (siehe unten).

WorklistAetRewriteStage

Was es tut

Schreibt in Worklist-Abfragen (C-FIND MWL) den Stations-AE-Titel ScheduledStationAETitle um. Anwendungsfall: Der Router schreibt den AE-Titel der Modality per AETitleRewriteStage um, sodass das RIS die Modality nur noch unter dem RIS-seitigen AET kennt. Ohne dieses Plugin fragt die Modality mit ihrem eigenen AET als Suchschlüssel und bekommt eine leere Worklist; ausserdem verwirft sie gelieferte Einträge, weil deren Stations-AET nicht zu ihrem eigenen passt. Das Plugin schreibt den AET in der Abfrage vorwärts (Modality → RIS) und in den Antworten zurück (RIS → Modality), sodass die Worklist-Antworten die Original-AETs des Geräts tragen.

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.WorklistAetRewriteStage

Config-Felder

Feld Pflicht/Default Bedeutung
aetMapping Pflicht Zuordnung Modality-AET → RIS-AET für die Abfragerichtung.
responseMapping optional Explizite Rückrichtung RIS-AET → Modality-AET. Hat Vorrang vor der automatischen Umkehrung von aetMapping.
logRewrites Default true Protokolliert jede Umschreibung.
enabled Default true Schaltet das Plugin aus. Bei enabled: true muss aetMapping mindestens einen Eintrag haben.

Beispiel

Das vollständige Setup kombiniert beide Plugins in einer Action: Die AETitleRewriteStage schreibt den Calling-AET der Verbindung um (das RIS sieht US_GATEWAY als Absender), die WorklistAetRewriteStage zieht denselben Wert im Nachrichteninhalt nach (Suchschlüssel ScheduledStationAETitle hin, Antworten zurück). Nur die WorklistAetRewriteStage allein genügt, wenn das RIS den Original-Calling-AET der Modality akzeptiert und lediglich der Stations-AET in den Worklist-Einträgen abweicht.

actions:
  worklistRewrite:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.AETitleRewriteStage
      config:
        callingAetOverride: "US_GATEWAY"   # Verbindungs-Ebene: Absender-AET nach aussen
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.WorklistAetRewriteStage
      config:
        aetMapping:              # Nachrichten-Ebene: Modality-AET -> RIS-AET
          US1: "US_GATEWAY"
        # responseMapping:       # optional; nur nötig bei mehrdeutiger Rückrichtung
        #   US_GATEWAY: "US1"
        logRewrites: true
        enabled: true

filter:
  - callingAET: US1
    calledAET: RIS
    actions:
      - "worklistRewrite"
    destination:
      - "ris1"

Hinweise

  • Die Rückrichtung wird normalerweise automatisch aus aetMapping gebildet.
  • Bilden mehrere Modality-AETs auf denselben RIS-AET ab (Pool), ist die Umkehrung mehrdeutig. Dann trägt man responseMapping explizit nach; andernfalls können betroffene Antwortwerte nicht eindeutig zurückgeschrieben werden und bleiben unverändert (Warnung beim Laden der Konfiguration).
  • Nicht-Worklist-Verkehr und nicht zuordenbare Werte werden unverändert durchgereicht; das Plugin verwirft nie Nachrichten.

QrFieldRewriteStage

Was es tut

Allgemeiner Feld-Umschreiber für Abfrage-Verkehr (C-FIND, C-MOVE, C-GET). Damit lassen sich einzelne Felder in Abfragen und Antworten setzen, mit einem Standardwert vorbelegen oder entfernen — etwa um eine Abfrage auf bestimmte Modalitäten einzuschränken, eine feste Retrieve-AE in Antworten zu ergänzen oder ein Feld herauszunehmen. Ohne weitere Angabe wirkt das Plugin auf die Query/Retrieve-Modelle (Patient Root und Study Root); über sopClasses lässt es sich auf andere Abfrage-Klassen ausdehnen, z. B. Worklist-Abfragen (MWL).

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.QrFieldRewriteStage

Config-Felder

Feld Pflicht/Default Bedeutung
applyTo Default [C-FIND, C-MOVE, C-GET] Auf welche Operationen das Plugin wirkt.
sopClasses Default: die sechs Q/R-Modelle (Patient/Study Root) Whitelist von SOP-Class-UIDs, gegen die die Abfrage-Klasse geprüft wird. Eine eigene Liste ersetzt den Default — wer die Q/R-Modelle behalten und z. B. MWL (1.2.840.10008.5.1.4.31) ergänzen will, listet beide auf.
request optional Regeln für eingehende Abfrage-Identifier. Enthält set, default, remove.
response optional Regeln für ausgehende (Pending-)Antwort-Identifier. Enthält set, default, remove.
fail-open Default false true = ein Fehler beim Verarbeiten überspringt nur diese Regel, statt die Verbindung abzubrechen.
logRewrites Default true Protokolliert eine Zusammenfassung pro Datensatz (ohne Werte).
enabled Default true Bei enabled: true muss mindestens eine Regel unter request: oder response: stehen.

Innerhalb von request: und response: gibt es drei Operationen:

  • set — Feld überschreiben oder neu anlegen.
  • default — Feld nur setzen, wenn es noch nicht vorhanden ist.
  • remove — Feld entfernen.

Feld-/Pfad-Schreibweise: Tags werden als "GGGG,EEEE" (oder 8 Hex-Ziffern) angegeben.

Manche DICOM-Felder sind Sequenzen: Statt eines einfachen Wertes (Text, Zahl, UID) enthalten sie eine Liste von Items, und jedes Item ist ein eigener kleiner Datensatz mit wiederum eigenen Feldern. Beispiel: Die „Referenced Study Sequence" (0008,1110) enthält pro referenzierter Studie ein Item, und darin stehen dann die eigentlichen Felder (etwa die Studien-UID). In solche Strukturen steigt man mit /-getrennten Pfaden ab: "<sequenz-tag>/<item-index oder *>/<tag>", beliebig tief; * steht für alle vorhandenen Items. Pfade steigen nur in bereits vorhandene Sequenzen und Items ab — set/default können einfache Felder neu anlegen, aber keine Sequenz-Struktur und keine Items erzeugen; ein Pfad in eine nicht vorhandene Sequenz bewirkt schlicht nichts.

VR-Angabe: Jedes DICOM-Feld hat einen Datentyp (VR, z.B. AE für AE-Titel, LO für kurzen Text, UI für UIDs). Für alle Standard-DICOM-Felder (das komplette Standard-Dictionary, PS3.6) kennt der Router den Typ selbst — dann genügt die Kurzform ("0008,0061": "CT\\MR" oder "0008,0054": "ROUTER").

Nur für private und exotische Felder (die nicht im Standard-Dictionary stehen oder dort keinen eindeutigen Typ haben) ist die erweiterte Form mit ausdrücklicher VR Pflicht ({ value: "XYZ", vr: LO }) — fehlt sie, lehnt der Router die Konfiguration beim Start bzw. beim reload mit einer klaren Fehlermeldung ab; zur Laufzeit kann dadurch nichts schiefgehen. Eine erweiterte Form, deren VR dem Standard-Dictionary widerspricht, wird ebenfalls abgelehnt. Felder mit binärem Datentyp (z.B. US, OW) lassen sich mit set/default grundsätzlich nicht schreiben — die Rewrite-Werte sind Text; solche Einträge werden beim Laden abgelehnt. Mehrere Werte werden mit Backslash getrennt.

Beispiel

actions:
  qrFieldRewrite:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.QrFieldRewriteStage
      config:
        applyTo: ["C-FIND"]
        request:
          set:
            "0008,0061": "CT\\MR"          # ModalitiesInStudy (Kurzform)
          remove:
            - "0010,1000"                  # OtherPatientIDs
        response:
          set:
            "0008,0054": "ROUTER"          # RetrieveAETitle (Kurzform)
          default:
            "0008,0080": "KLINIK_A"        # InstitutionName (Kurzform)
            "0009,1001":                   # privates Feld - erweiterte Form mit VR
              value: "ZUWEISER-A"
              vr: LO
          remove:
            - "0008,1120/*/0010,0020"      # in allen Items der Sequenz
        fail-open: false
        logRewrites: true
        enabled: true

filter:
  - callingAET: WS1
    calledAET: PACS
    actions:
      - "qrFieldRewrite"
    destination:
      - "pacs1"

Hinweise

  • Die Konfiguration wird streng beim Laden geprüft: ungültige Pfade oder VR-Codes, fehlende VR bei unbekannten Feldern, leere Werte und sich überschneidende Regeln (etwa set und default auf dasselbe Feld) werden schon beim Start bzw. Reload abgelehnt — nicht erst zur Laufzeit.
  • Verkehr ausserhalb der sopClasses-Whitelist, unlesbare Nachrichten und fehlende Felder sind erwartbare Zustände und führen zu unverändertem Durchreichen, nie zu einem Abbruch.
  • ISO-2022-Zeichensätze: Deklariert ein Gerät einen nicht unterstützten Mehrwert-Zeichensatz (ISO-2022-Code-Extensions, z.B. ISO 2022 IR 6\ISO 2022 IR 87), kann der Router Textfelder nicht sicher dekodieren — die Stage wird für dieses Objekt übersprungen (WARN im Log, Original wird byte-exakt weitergeleitet). Das betrifft alle Standard-Textfelder, auch bei Implicit-VR-Übertragung.
  • Setzt set denselben Wert, der ohnehin schon vorhanden ist, entsteht keine Änderung.
  • Wirken auf derselben Verbindung sowohl QrFieldRewriteStage (mit MWL in sopClasses) als auch WorklistAetRewriteStage auf Worklist-Abfragen, laufen sie in der Reihenfolge, in der die Actions in der Filterregel stehen.

CMoveDestinationRewriteStage

Was es tut

Schreibt in eingehenden C-MOVE-Anfragen das Ziel-Feld MoveDestination um. Anwendungsfall: Wird der AE-Titel per AETitleRewriteStage umgeschrieben, trägt die C-MOVE-Anfrage noch den ursprünglichen Ziel-AET, den das PACS nach der Umschreibung nicht mehr kennt. Dieses Plugin ersetzt den Ziel-AET durch einen Wert, den das PACS erreichen kann (typischerweise den Pool- bzw. Router-AET).

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.CMoveDestinationRewriteStage

Config-Felder

Feld Pflicht/Default Bedeutung
destinationMapping optional Genaue Zuordnung ursprünglicher Ziel-AET → neuer Ziel-AET. Hat Vorrang vor destinationOverride.
destinationOverride optional Fester Ersatz-AET für jeden Ziel-AET ohne Mapping-Eintrag.
logRewrites Default true Protokolliert jede Umschreibung.
enabled Default true Bei enabled: true muss mindestens eines von destinationMapping/destinationOverride gesetzt sein.

Beispiel

actions:
  cmoveRewrite_us:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.CMoveDestinationRewriteStage
      config:
        destinationOverride: "US_POOL"
        # destinationMapping:
        #   US1: "RTR_US1"
        #   US2: "RTR_US2"
        logRewrites: true
        enabled: true

filter:
  - callingAET: US1
    calledAET: PACS
    actions:
      - "cmoveRewrite_us"
    destination:
      - "pacs1"

Hinweise

  • Ist zu einem Ziel-AET weder ein Mapping-Eintrag noch ein Override vorhanden, bleibt die Anfrage unverändert (mit Warnung im Log).
  • C-MOVE-Antworten tragen kein Ziel-Feld und werden nicht angefasst; andere Nachrichtentypen werden unverändert durchgereicht.

CMoveTrackingStage

Was es tut

Bereitet den Rückweg eines C-MOVE für die Q/R-Bridge vor. Das Plugin macht in einem Schritt dreierlei: Es schreibt MoveDestination genauso um wie die CMoveDestinationRewriteStage (die es in diesem Szenario ersetzt), tauscht zusätzlich die MessageID der C-MOVE-Anfrage gegen eine router-eigene, cluster-weit eindeutige Nummer aus und merkt sich zu dieser Nummer, an welches Ziel die zurückkommenden Bilder gehören. Ein normkonformes PACS trägt die Nummer in jede zurückgesendete Bild-Nachricht ein, wodurch der Router die zweite, unabhängige Verbindung wieder der ursprünglichen Anfrage zuordnen kann. Füllt das PACS das Feld nicht oder mit einem eigenen Wert, ordnet der Router das Bild ersatzweise über die Studien-UID dem jüngsten passenden C-MOVE zu. C-MOVE-Antworten und ein allfälliges C-CANCEL des Geräts werden automatisch zurück- bzw. mitgeschrieben; das Gerät sieht durchgehend seine eigene Nachrichtennummer.

Stand dieser Version: Der Weg ist durchgehend: Das Plugin legt den Tracking-Eintrag an, der Router nimmt die Rückverbindung des PACS selbst an, löst pro Bild die Zuordnung auf und sendet das Bild an das Gerät weiter (Destination-Typ qr-bridge, siehe configuration.md). Das Gerät erhält dabei seine eigene Nachrichtennummer und seinen eigenen AE-Title zurück, und der Status, mit dem das Gerät ein Bild quittiert, wird unverändert ans PACS weitergereicht — Warnungen bleiben Warnungen. Die Aktivierung sollte wegen der Presentation-Context-Konfiguration weiterhin mit dem Support abgestimmt werden.

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.CMoveTrackingStage

Config-Felder

Feld Pflicht/Default Bedeutung
destinationMapping optional Genaue Zuordnung ursprünglicher Ziel-AET → neuer Ziel-AET. Hat Vorrang vor destinationOverride.
destinationOverride optional Fester Ersatz-AET für jeden Ziel-AET ohne Mapping-Eintrag (typischerweise der Router-AET).
returnDestination Pflicht Name eines Eintrags aus destinations: vom Typ forward — dorthin gehören die zurückkommenden Bilder. Ein Tippfehler wie auch ein Verweis auf eine qr-bridge- oder reject-Destination lässt die Konfiguration bewusst scheitern; sonst fiele der Fehler erst Stunden später auf dem Rückweg auf.
store Default memory memory für einen einzelnen Router, database im Cluster (gemeinsame Datenbank; erfordert eine aktive Datenbank). Muss mit dem store: der qr-bridge-Destination übereinstimmen — jeder benutzte Store muss von einer Bridge gelesen werden.
ttl Default 24h Gültigkeitsdauer eines Tracking-Eintrags (ms/s/m/h/d, maximal 30 Tage).
logRewrites Default true Protokolliert jede Umschreibung.
enabled Default true Bei enabled: true müssen returnDestination und mindestens eines von destinationMapping/destinationOverride gesetzt sein.

Beispiel

actions:
  cmoveTracking_us1:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.CMoveTrackingStage
      config:
        destinationOverride: "ROUTER"
        returnDestination: "us1"
        store: memory
        ttl: 24h

filter:
  - callingAET: US1
    calledAET: PACS
    actions:
      - "aetRewrite_us"
      - "cmoveTracking_us1"
    destination:
      - "pacs1"

Hinweise

  • Ist zu einem Ziel-AET weder ein Mapping-Eintrag noch ein Override vorhanden, bleibt die Anfrage vollständig unverändert (mit Warnung im Log) — es wird dann auch nichts nachverfolgt.
  • Lässt sich keine Nummer vergeben (Datenbank nicht erreichbar), wird die Anfrage ebenfalls unverändert weitergeleitet und ein Fehler protokolliert. Die Verbindung bleibt in jedem Fall bestehen.
  • Ein Wechsel von store: memory auf database (oder umgekehrt) erfordert einen Neustart. Ebenso neustartpflichtig ist das Entfernen oder Umbenennen eines solchen Eintrags, wenn im selben reload kein Eintrag mit demselben Store an seine Stelle tritt — die Speicher-Instanzen sind an den Start gebunden, und Umbenennen ist von Entfernen nicht unterscheidbar. Alle übrigen Felder lassen sich per reload ändern.
  • Der store-Wert muss mit dem der qr-bridge-Destination übereinstimmen, die den Rückweg annimmt — sonst schreibt die eine Seite in einen anderen Speicher als die andere liest. Die Konfigurationsprüfung lehnt eine solche Kombination ab. Umgekehrt ist eine qr-bridge-Destination ohne dieses Plugin erlaubt: im Cluster kann ein Knoten die Rückverbindungen annehmen, während eine andere Instanz die Einträge schreibt.
  • Die Tracking-Einträge überleben die Verbindung bewusst — das PACS meldet sich oft erst Stunden später zurück. Aufgeräumt wird über die Gültigkeitsdauer und die betreiberseitige Datenbank-Bereinigung.
  • Wie lange der Router auf ein langsames Gerät wartet und ab wie vielen unzustellbaren Bytes er eine Rückverbindung abbricht, wird nicht hier, sondern an der qr-bridge-Destination eingestellt (timeouts: und max-drain-bytes, siehe configuration.md).

DicomTapStage

Was es tut

Zweigt Daten der getappten Verbindung als byte-exakte DICOM-Dateien (Part-10) in eine konfigurierbare Ablage ab oder versendet sie zusätzlich an ein Fremdsystem — ohne die eigentliche Weiterleitung zu beeinflussen. Anwendungsfall: Daten für ein Fremdsystem, Archiv oder eine Auswertung abzweigen, ohne den produktiven Fluss zum PACS zu verändern. Standardmässig werden die gespeicherten Bilder (C-STORE) abgezweigt; über commands: lassen sich auch andere Nachrichten (etwa Abfrage-Inhalte) mitschneiden. Die Wire-Transfer-Syntax wird 1:1 übernommen, es findet keine Umkodierung statt.

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.DicomTapStage

Config-Felder

Feld Pflicht/Default Bedeutung
pathTemplate Pflicht, falls kein forward Absolutes Pfad-Template mit Platzhaltern für die Ablage. Mindestens eines von pathTemplate/forward muss gesetzt sein.
sopClasses optional Whitelist von SOP-Class-UIDs (exakte UIDs, keine Wildcards). Fehlt sie, wird jede ausgewählte Nachricht abgezweigt.
dataSetFilters optional Filter über Data-Set-Felder: Liste von {tag, values}-Einträgen. Nur Objekte, deren Data-Set alle Einträge erfüllt, werden abgezweigt (Semantik siehe unten).
commands Default [C-STORE-RQ] Welche DIMSE-Nachrichten abgezweigt werden (z. B. C-FIND-RQ, C-FIND-RSP, C-MOVE-RQ).
finalize Default on-success on-success = erst nach erfolgreicher Speicher-Bestätigung der weitergeleiteten Verbindung ablegen und versenden; always = sofort.
quotaMb optional Obergrenze (in MB) für den Plattenverbrauch der Ablage. Bei Überschreitung setzt der Tap aus, bis der Verbrauch sinkt.
forward optional Block zum Versand der Captures als C-STORE an ein Ziel (siehe unten).
enabled Default true Schaltet den Tap aus.

Platzhalter im pathTemplate: benannte Aliase <callingAet>, <calledAet> (die Original-AETs des Geräts vor jeder Umschreibung), <sopClassUid>, <sopInstanceUid>, <studyInstanceUid>, <seriesInstanceUid>, <modality>, <date>/<time> (Empfangszeitpunkt), <command> (DIMSE-Name wie C-FIND-RSP), <messageId> (Kennung des Austauschs; Anfrage und Antwort gruppieren sich dadurch), <seq> (4-stelliger Zähler pro Austausch), <associationId> (Verbindungs-Kennung) sowie generisch <tag:GGGG,EEEE> für beliebige Header-Tags. Fehlt ein Wert, wird UNKNOWN eingesetzt.

„Alle Structured Reports" auswählen: sopClasses vergleicht exakte UIDs — eine Kategorie-Auswahl („alle SR") gibt es nicht. Als Referenz die gängigen SR-SOP-Klassen aus dem DICOM-Standard (für klassische Befund-SRs genügen meist die ersten drei; welche Klassen ein Gerät tatsächlich erzeugt, steht in dessen DICOM Conformance Statement):

UID Bedeutung
1.2.840.10008.5.1.4.1.1.88.11 Basic Text SR
1.2.840.10008.5.1.4.1.1.88.22 Enhanced SR
1.2.840.10008.5.1.4.1.1.88.33 Comprehensive SR
1.2.840.10008.5.1.4.1.1.88.34 Comprehensive 3D SR
1.2.840.10008.5.1.4.1.1.88.35 Extensible SR
1.2.840.10008.5.1.4.1.1.88.40 Procedure Log
1.2.840.10008.5.1.4.1.1.88.50 Mammography CAD SR
1.2.840.10008.5.1.4.1.1.88.59 Key Object Selection Document
1.2.840.10008.5.1.4.1.1.88.65 Chest CAD SR
1.2.840.10008.5.1.4.1.1.88.67 X-Ray Radiation Dose SR
1.2.840.10008.5.1.4.1.1.88.68 Radiopharmaceutical Radiation Dose SR
1.2.840.10008.5.1.4.1.1.88.69 Colon CAD SR
1.2.840.10008.5.1.4.1.1.88.73 Patient Radiation Dose SR

dataSetFilters: — Auswahl nach Data-Set-Feldern: Zusätzlich zur SOP-Klassen-Whitelist lässt sich nach Inhalten des Data-Sets filtern — der Haupt-Anwendungsfall ist die Modalität (0008,0060):

        dataSetFilters:
          - tag: "0008,0060"        # Modality
            values: [CT, MR]
          - tag: "0008,0016"        # SOP Class UID per Regex (MR Image Storage + Enhanced)
            values: ["regex:1\\.2\\.840\\.10008\\.5\\.1\\.4\\.1\\.1\\.4(\\..*)?"]

Semantik:

  • Jeder Eintrag besteht aus tag (Top-Level-Tag in Hex-Schreibweise "GGGG,EEEE" oder als 8 Hex-Ziffern "GGGGEEEE", auch private Tags erlaubt; kein Abstieg in Sequenzen) und values (mindestens ein Wert).
  • Mehrere Einträge müssen alle zutreffen (UND); mehrere Werte eines Eintrags sind Alternativen (ODER). Derselbe Tag darf in mehreren Einträgen stehen (auch die zählen als UND — erfüllbar über mehrwertige Attribute).
  • Verglichen wird die Text-Repräsentation des Werts, exakt (nach Entfernen umgebender Leerzeichen). Ein Wert mit dem Präfix regex: ist ein regulärer Ausdruck und muss den gesamten Wert matchen — gleiche Präfix-Konvention wie bei den AE-Titeln der Filter-Regeln; anders als dort ist * hier aber kein Wildcard, sondern ein gewöhnliches Zeichen (nur regex: ist speziell).
  • Tags mit binärer VR (z. B. Rows US, PixelData OW) können nie als Text matchen und werden beim Config-Load abgelehnt; numerische String-VRs wie IS/DS (z. B. SeriesNumber) funktionieren normal.
  • Bei mehrwertigen Attributen (Backslash-getrennt, z. B. Modality CT\MR) genügt ein passendes Element.
  • Fehlt das Attribut im Data-Set oder ist es leer (auch: besteht nur aus Füll-Leerzeichen), wird das Objekt nicht abgezweigt (Vermerk im Anwendungslog auf DEBUG-Stufe). Auch ein Lesefehler beim Filter-Check überspringt nur dieses Capture (erster Vorfall als Warnung im Log) — die Weiterleitung ist wie immer nie betroffen.
  • Ohne dataSetFilters bleibt das Verhalten unverändert (jede ausgewählte Nachricht wird abgezweigt). Der Filter gilt auch für commands:-Captures — dort wird gegen die Felder des mitgeschnittenen Data-Sets geprüft (z. B. die Schlüssel eines Abfrage-Identifiers). Achtung bei C-FIND-Anfragen (C-FIND-RQ): Return-Keys wie die Modalität sind in der Anfrage üblicherweise leer (Universal Matching) — ein Filter darauf erfasst bei RQ-Captures daher nie etwas; sinnvoll ist er dort nur für tatsächlich gefüllte Matching-Keys. Antworten (C-FIND-RSP) tragen die Werte gefüllt.

Grenzen: Bei privaten oder dem Router unbekannten Tags wird der Wert als roher Latin-1-Text verglichen — Umlaute/Sonderzeichen matchen dort unter Umständen nicht; ohne Zeichensatz-Deklaration (0008,0005) wird ASCII angenommen. Extrem grosse Attributwerte werden nicht gelesen (zählt als Lesefehler → nie erfasst), und einzelne Werte über 1024 Zeichen matchen nie.

forward:-Block:

Feld Pflicht/Default Bedeutung
destination Pflicht Name eines Ziels aus destinations:; muss vom Typ forward sein.
spoolPath Pflicht Absolutes Verzeichnis für die Warteschlange; ein eigenes Verzeichnis pro Action.
onSuccess Default delete delete = versendete Dateien löschen; done = nach <spool>/done/ verschieben.
spoolMaxMb optional Obergrenze der Warteschlange; darüber setzt das Spooling aus (Warnung). Zählt nur die aktive Warteschlange — done/ und failed/ zählen nicht mit.
callingAet Default AET des Router-Listeners Calling-AET beim Versand.
calledAet Default AET des Ziels Called-AET beim Versand.

Beispiel

actions:
  tapSr:                                  # nur Befunde (SR) ablegen
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.DicomTapStage
      config:
        pathTemplate: "/var/imconnect/tap/sr/<callingAet>/<studyInstanceUid>/<sopInstanceUid>.dcm"
        sopClasses:
          - "1.2.840.10008.5.1.4.1.1.88.11"   # Basic Text SR
          - "1.2.840.10008.5.1.4.1.1.88.22"   # Enhanced SR
          - "1.2.840.10008.5.1.4.1.1.88.33"   # Comprehensive SR
        finalize: on-success
        quotaMb: 10240

  tapForward:                             # Captures an ein Fremdsystem versenden
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.DicomTapStage
      config:
        forward:
          destination: pacs2
          spoolPath: /var/imconnect/tap-spool/sr
          onSuccess: delete
          spoolMaxMb: 4096

  tapQueries:                             # C-FIND-Austausch beidseitig ablegen
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.DicomTapStage
      config:
        pathTemplate: "/var/imconnect/tap/queries/<callingAet>/<date>/<messageId>/<command>-<seq>.dcm"
        commands: ["C-FIND-RQ", "C-FIND-RSP"]

filter:
  - callingAET: US1
    calledAET: PACS
    actions:
      - "tapSr"
    destination:
      - "pacs1"

Hinweise

  • Atomare Dateien: Eine abgelegte Datei erscheint erst vollständig am Zielpfad. Nachgelagerte Skripte oder inotify-Watcher sehen nie eine partielle Datei.
  • finalize: Bei on-success (Default) wird ein Capture erst wirksam, wenn die erfolgreiche Speicher-Bestätigung (C-STORE-Antwort, Success oder Warning) des Ziels der weitergeleiteten Verbindung eintrifft — also vom Primärziel der Filter-Regel, nicht vom forward:-Ziel. Das Gating gilt für beide Ausgänge gleichermassen: Weder wird die Datei abgelegt noch etwas in den Versand-Spool gestellt, solange die Bestätigung aussteht; ein Fehlschlag oder Verbindungsabbruch verwirft das Capture. Bei always wird sofort abgelegt, unabhängig vom Ergebnis.
  • Ablage und Versand kombinieren: pathTemplate und forward dürfen in derselben Instanz stehen (mindestens eines von beiden muss konfiguriert sein) — dann wird jedes Capture abgelegt und versendet. Getrennte Action-Einträge braucht es nur, wenn Ablage und Versand unterschiedliche Selektionen (sopClasses, commands) haben sollen.
  • Der Tap beobachtet nur: Die Verbindung braucht immer ein normales Weiterleitungs-Ziel in der Filter-Regel — „nur abzweigen, ohne weiterzuleiten" gibt es nicht (ein reject-Ziel würde die Verbindung ablehnen, bevor Daten fliessen).
  • Zielverzeichnisse und Schreibrechte: Der Router legt Unterverzeichnisse unterhalb des Template-Stammverzeichnisses selbst an; das Stammverzeichnis muss für den Dienst-Benutzer beschreibbar sein. Achtung mit der mitgelieferten systemd-Unit: Sie erlaubt Schreibzugriffe nur unter /var/imconnect und /var/log/imconnect (ProtectSystem=strict). Soll die Ablage woanders liegen (z. B. /data/...), muss der Pfad in der Unit unter ReadWritePaths= ergänzt werden — sonst setzt der Tap mit einer Warnung im Anwendungslog aus, während die Weiterleitung normal weiterläuft. Captures landen nur für den Dienst-Benutzer lesbar (Dateien 0600, vom Router selbst angelegte Verzeichnisse 0700) — die Dateien enthalten Patientendaten. Holt ein Konsument die Dateien unter einem anderen Konto ab, braucht er ausdrücklich eingeräumten Zugriff, etwa über die Gruppe oder eine ACL (setfacl) auf dem Ablageverzeichnis, oder er läuft unter dem Konto des Routers; bereits vorhandene, betreiberseitig angelegte Verzeichnisse behalten ihre Rechte.
  • Nie störend: Jeder Tap-Fehler (volle Platte, unbeschreibbares Verzeichnis) wird protokolliert, setzt den Tap für die Verbindung aus und lässt die Weiterleitung unverändert weiterlaufen. Die produktive Route ist nie betroffen.
  • Fehlgeschlagene Sendungen (failed/): Sendungen, die das Ziel dauerhaft ablehnt (z. B. nicht unterstützte Transfer-Syntax oder wiederholt fehlgeschlagene Speicherversuche), verschiebt der Versand nach <spool>/failed/ und protokolliert eine Warnung — sie blockieren die Warteschlange nicht und werden nicht automatisch erneut versucht. Dasselbe gilt, wenn das Ziel auf einen Speicherversuch protokollwidrig antwortet: das Bild ist zu diesem Zeitpunkt bereits vollständig übertragen, nur die Quittung ist unbrauchbar. Ein erneuter Versuch würde deshalb nur eine weitere Kopie im Ziel erzeugen — der Eintrag wandert direkt nach failed/. Bricht das Ziel die Verbindung dagegen mitten in der Übertragung ab, zählt das als ein fehlgeschlagener Zustellversuch wie eine reguläre Fehlerantwort: ein Abbruch kann vorübergehend sein (ein Gerät, das gerade neu startet), deshalb wird wiederholt und der Eintrag erst nach fünf Versuchen abgelegt.
  • Quota/Aufräumen: Das regelmässige Aufräumen der Ablage sowie der done/- und failed/-Verzeichnisse ist Aufgabe des Konsumenten bzw. Betreibers — done/ und failed/ zählen nicht gegen spoolMaxMb und wachsen sonst unbegrenzt. quotaMb und spoolMaxMb gelten je Action-Eintrag (jeder Eintrag hat sein eigenes Stamm- bzw. Spool-Verzeichnis); sie sind ein Schutz gegen Volllaufen, kein Retention-Mechanismus.
  • PHI-Hinweis: Werden PHI-tragende Tags (z. B. Patientenname) ins pathTemplate aufgenommen, landen Patientendaten in Dateinamen. Ob das zulässig ist und wie das Ablageverzeichnis abgesichert wird, ist eine bewusste Betreiber-Entscheidung.
  • Query-Capture (commands:): Beim Mitschneiden von Abfragen gelten für das pathTemplate Pflichtplatzhalter, damit sich Captures nicht überschreiben: Beim Mitschneiden von Antworten sind <seq> und <messageId> erforderlich (eine Abfrage liefert viele Antworten, und <seq> beginnt pro Austausch neu); beim Mitschneiden mehrerer Kommandos ist <command> erforderlich. Sollen sich Captures verschiedener Verbindungen nie überschreiben, gehört zusätzlich <associationId> ins Template.
  • Versand über Loopback: Soll der Versand durch weitere Umschreibe-Plugins laufen, zeigt die Forward-Destination auf einen Router-Listener; eine passende Filter-Regel hängt dort die gewünschten Actions an. Diese Loopback-Regel darf selbst keine Tap-Forward-Action referenzieren, sonst entsteht eine Endlosschleife (der Config-Load warnt bei dieser Konstellation).

CharsetTranscodePlugin

Was es tut

Kodiert die Textwerte ausgewählter DICOM-Nachrichten von einem Zeichensatz in einen anderen um und schreibt die Zeichensatz-Deklaration (0008,0005) passend nach. Anwendungsfall: Ein Gerät kann UTF-8 (ISO_IR 192) nicht darstellen — z. B. zeigt eine Modality Worklist-Antworten mit Umlauten falsch an, weil ihr Anzeigeteil nur Latin-1 (ISO_IR 100) beherrscht. Das Plugin kodiert die Worklist-Antworten auf dem Weg zum Gerät um; das Quellsystem (RIS/PACS) bleibt unangetastet.

Umkodiert werden alle Text-Felder mit erweitertem Zeichenvorrat (Personennamen, Beschreibungen, Kommentare — die VRs PN/LO/SH/ST/LT/UT/UC), auch innerhalb von Sequenzen. Datensätze ohne Zeichensatz-Deklaration oder mit einer Deklaration, die nicht in from steht, werden byte-identisch durchgereicht. Ist der Ziel-Zeichensatz ISO_IR 6 (Standard-Repertoire ASCII), wird (0008,0005) entfernt — so verlangt es der DICOM-Standard.

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetTranscodePlugin

Config-Felder

Feld Pflicht/Default Bedeutung
from Pflicht Quell-Zeichensätze (DICOM-Terme, z. B. ISO_IR 192). Nur Datensätze, deren (0008,0005) einen dieser Terme deklariert, werden umkodiert.
to Pflicht Ziel-Zeichensatz (DICOM-Term). ISO_IR 6 entfernt die Deklaration. Darf nicht gleichzeitig in from stehen.
commands Default ["C-FIND-RSP"] Welche DIMSE-Nachrichten umkodiert werden (z. B. C-FIND-RSP, C-FIND-RQ, C-STORE-RQ). Die Richtung ergibt sich aus RQ/RSP.
sopClasses optional (leer = alle) Einschränkung auf bestimmte SOP-Klassen (Affected SOP Class UID des Kommandos), z. B. 1.2.840.10008.5.1.4.31 für die Modality Worklist.
replacement Default ? Ersatzzeichen für Zeichen, die der Ziel-Zeichensatz nicht darstellen kann (genau ein Zeichen; kein DICOM-Trennzeichen \, ^, = und kein Steuerzeichen). Ersetzungen werden einmal pro Verbindung als Warnung protokolliert (nur Tag-Nummern, nie Werte).
transliterate Default false Transliteriert nicht darstellbare Zeichen, bevor replacement greift: zuerst die eingebaute Tabelle (ä→ae, ö→oe, ü→ue, Ä→Ae, Ö→Oe, Ü→Ue, ß→ss, ẞ→SS), dann generisches Entfernen von Akzenten (é→e, à→a, ç→c, ñ→n, …). Beispiel: Mueller^Joerg statt M?ller^J?rg für ein Nur-ASCII-Gerät. Ohne das Flag verhält sich das Plugin exakt wie bisher.
transliterations optional (leer) Eigene Einträge, die die eingebaute Tabelle ergänzen oder überschreiben (z. B. "ø": "oe"). Schlüssel: genau ein Zeichen; Wert: Text, der im Ziel-Zeichensatz darstellbar ist und keine DICOM-Trennzeichen (\, ^, =) oder Steuerzeichen enthält. Nur zusammen mit transliterate: true erlaubt.
logTranscodes Default true Protokolliert jede umkodierte Nachricht (ohne Inhalte).
enabled Default true Schaltet das Plugin aus.

Unterstützte Zeichensatz-Terme: ISO_IR 6 (ASCII), die Einzelbyte-Familie ISO_IR 100/101/109/110/144/127/126/138/148/166/203 sowie ISO_IR 192 (UTF-8), GB18030 und GBK. Unbekannte Terme lehnt der Config-Load ab.

Beispiel

Worklist-Antworten für ein Latin-1-Gerät umkodieren:

actions:
  worklistLatin1:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetTranscodePlugin
      config:
        enabled: true
        commands: ["C-FIND-RSP"]                  # Default
        sopClasses: ["1.2.840.10008.5.1.4.31"]    # Modality Worklist
        from: ["ISO_IR 192"]
        to: "ISO_IR 100"
        replacement: "?"

filter:
  - callingAET: US1
    actions:
      - "worklistLatin1"
    destination:
      - "ris1"

Welches Gerät die Umkodierung bekommt, steuert wie immer die Filter-Regel — es braucht keine eigene AET-Logik im Plugin.

Für ein Nur-ASCII-Gerät liefert transliterate: true lesbare Namen statt Ersatzzeichen — Mueller^Joerg statt M?ller^J?rg:

actions:
  worklistAscii:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetTranscodePlugin
      config:
        commands: ["C-FIND-RSP"]
        sopClasses: ["1.2.840.10008.5.1.4.31"]
        from: ["ISO_IR 192", "ISO_IR 100"]
        to: "ISO_IR 6"
        transliterate: true
        # transliterations:        # optional: eingebaute Tabelle ergänzen/überschreiben
        #   "ø": "oe"

Hinweise

  • Achtung, verlustbehaftete Ziele: Ein Ziel wie ISO_IR 100 kann nicht jedes Quell-Zeichen darstellen — nicht darstellbare Zeichen werden unwiderruflich durch das Ersatzzeichen ersetzt. Verlustbehaftete Umkodierungen deshalb nur in Richtung Anzeigegeräte einsetzen (Worklist, Query-Antworten). In Richtung Archiv/PACS ist nur das verlustfreie Ziel ISO_IR 192 (UTF-8) vertretbar — archivierte Objekte mit ersetzten Zeichen sind dauerhaft beschädigt.
  • Transliteration greift nur bei nicht darstellbaren Zeichen: Ein Ziel wie ISO_IR 100 kann ü darstellen — dort bleibt ü erhalten, auch mit transliterate: true. ue entsteht nur, wenn das Ziel das Zeichen nicht kennt (z. B. ISO_IR 6/ASCII). Bei Grossbuchstaben passt sich die Ersetzung dem Umfeld an: MÜLLER wird MUELLER, Müller wird Mueller — das gilt auch für ß in Grossbuchstaben-Umgebung (STRAßE wird STRASSE). Eigene transliterations:-Einträge werden dagegen immer exakt wie konfiguriert eingesetzt — ohne Gross-/Kleinschreibungs-Anpassung.
  • Kürzung überlanger Werte (nur mit transliterate: true): Transliteration kann Werte verlängern (aus einem Zeichen werden zwei). Wächst ein Wert dadurch über die vom DICOM-Standard erlaubte Maximallänge seines Feldtyps (Personenname und lange Beschreibung 64 Bytes, kurze Beschreibung 16 Bytes, Kurztext 1024 Bytes, Langtext 10240 Bytes; unbegrenzte Textfelder werden nie gekürzt), wird er am Ende abgeschnitten — bei mehrwertigen Feldern jeder Wert einzeln, die Trenner bleiben erhalten. Kürzungen werden einmal pro Verbindung als Warnung protokolliert (nur Tag-Nummern, nie Werte). Ohne transliterate: true wird nie gekürzt.
  • Abgleich mit RIS/PACS: Das Gerät zeigt nach der Transliteration Mueller, während RIS und PACS weiterhin Müller führen. Schickt das Gerät den Patientennamen zurück (z. B. in MPPS oder in den Bildern), weicht er vom Auftragssystem ab — der Abgleich läuft in der Praxis über Patient-ID und Accession Number, nicht über den Namen. Gegenüber M?ller ist die Transliteration deterministisch und lesbar.
  • ISO 2022 ist nicht unterstützt: Mehrwertige (0008,0005)-Deklarationen (Code-Extensions, z. B. \ISO 2022 IR 100 oder japanische/koreanische Mehrbyte-Sätze) werden nicht umkodiert — solche Datensätze passieren unverändert, mit einer Warnung im Log (einmal pro Verbindung).
  • Grenze bei Implicit VR: Bei Implicit-VR-Übertragung steht der Feldtyp nicht im Datenstrom. Standard-Tags erkennt das Plugin über eine eingebaute Tabelle; private und unbekannte Tags bleiben bei Implicit VR unangetastet (bei Explicit VR werden sie normal umkodiert). Nicht auflösbare Sequenzen unbekannter Tags bleiben ebenfalls unangetastet.
  • Alles oder nichts: Ein Datensatz wird entweder vollständig umkodiert oder vollständig unverändert durchgereicht. Bei jedem unerwarteten Problem (z. B. ein pathologisch grosser Textwert) passiert der Datensatz unverändert — das Plugin verwirft nie Nachrichten und bricht nie Verbindungen ab.
  • Sequenz-Items mit eigener (0008,0005)-Deklaration werden mit dieser umkodiert, sofern sie in from steht; deklariert ein Item einen Zeichensatz ausserhalb von from, bleibt das ganze Item unangetastet (Warnung im Log).
  • Es findet keine Transfer-Syntax-Konvertierung statt — nur die Zeichenkodierung der Textwerte ändert sich.

CharsetDeclarePlugin

Was es tut

Setzt oder überschreibt ausschliesslich die Zeichensatz-Deklaration (0008,0005) ausgewählter DICOM-Nachrichten — die Textwerte selbst werden nie umkodiert (das ist die Aufgabe des CharsetTranscodePlugin). Zwei Anwendungsfälle:

  • mode: if-absent (Default): Eine Abfrage (z. B. ein Worklist-C-FIND-RQ) ohne Zeichensatz-Deklaration bekommt eine — viele RIS/PACS liefern ihre Antworten dann gleich im gewünschten Zeichensatz, transkodieren also selbst an der Quelle. Damit erübrigt sich oft das Umkodieren der Antworten im Router.
  • mode: always: Korrigiert lügende Deklarationen — ein Gerät deklariert z. B. ISO_IR 100, sendet aber tatsächlich UTF-8-Bytes. Das Plugin schreibt die Deklaration auf den tatsächlichen Zeichensatz um, ohne die Bytes anzufassen.

Ist der konfigurierte Zeichensatz ISO_IR 6 (Standard-Repertoire ASCII), wird (0008,0005) entfernt statt gesetzt — so verlangt es der DICOM-Standard (das Standard-Repertoire wird durch Abwesenheit der Deklaration ausgedrückt).

Plugin-Klasse

ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetDeclarePlugin

Config-Felder

Feld Pflicht/Default Bedeutung
charset Pflicht Der zu deklarierende Zeichensatz (DICOM-Term, z. B. ISO_IR 100). ISO_IR 6 entfernt die Deklaration (nur mit mode: always).
mode Default if-absent if-absent: nur setzen, wenn (0008,0005) fehlt oder leer ist (mit ASCII-Prüfung, siehe Hinweise). always: vorhandene Deklaration immer überschreiben.
commands Default ["C-FIND-RQ"] Welche DIMSE-Nachrichten die Deklaration bekommen (z. B. C-FIND-RQ, C-FIND-RSP, C-STORE-RQ). Die Richtung ergibt sich aus RQ/RSP.
sopClasses optional (leer = alle) Einschränkung auf bestimmte SOP-Klassen (Affected SOP Class UID des Kommandos), z. B. 1.2.840.10008.5.1.4.31 für die Modality Worklist.
logDeclares Default true Protokolliert jede gesetzte/entfernte Deklaration (ohne Inhalte).
enabled Default true Schaltet das Plugin aus.

Unterstützte Zeichensatz-Terme: dieselben wie beim CharsetTranscodePlugin (ISO_IR 6, die Einzelbyte-Familie ISO_IR 100203, ISO_IR 192, GB18030, GBK). Unbekannte Terme lehnt der Config-Load ab.

Beispiel

Worklist-Abfragen eines Latin-1-Geräts bekommen eine Zeichensatz-Deklaration, damit das RIS seine Antworten selbst in Latin-1 liefert; als Fallback für RIS-Systeme, die den Abfrage-Zeichensatz ignorieren, kodiert das CharsetTranscodePlugin die Antworten zusätzlich um:

actions:
  worklistCharset:
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetDeclarePlugin
      config:
        commands: ["C-FIND-RQ"]                   # Default
        sopClasses: ["1.2.840.10008.5.1.4.31"]    # Modality Worklist
        charset: "ISO_IR 100"
        mode: if-absent
    - plugin: ch.immeditech.imconnect.dicomrouter.plugin.charset.CharsetTranscodePlugin
      config:
        commands: ["C-FIND-RSP"]                  # Fallback: Antworten umkodieren
        sopClasses: ["1.2.840.10008.5.1.4.31"]
        from: ["ISO_IR 192"]
        to: "ISO_IR 100"

filter:
  - callingAET: US1
    actions:
      - "worklistCharset"
    destination:
      - "ris1"

Welches Gerät die Deklaration bekommt, steuert wie immer die Filter-Regel.

Hinweise

  • Es werden nie Bytes umkodiert. Das Plugin ändert nur die Deklaration (0008,0005); alle Textwerte passieren byte-identisch. Sollen die Werte selbst umkodiert werden, ist das CharsetTranscodePlugin zuständig.
  • ASCII-Prüfung bei if-absent: Bevor eine Deklaration gesetzt wird, prüft das Plugin alle erkannten Textwerte (die VRs PN/LO/SH/ST/LT/UT/UC, auch in Sequenzen). Enthält irgendein Wert ein Byte ausserhalb von ASCII, wird nichts gesetzt und die Nachricht passiert unverändert (Warnung im Log, einmal pro Verbindung) — Bytes unbekannter Kodierung werden nicht umgedeutet. Bei mode: always entfällt diese Prüfung bewusst: das Umdeklarieren vorhandener Nicht-ASCII-Bytes ist dort gerade der Zweck.
  • Sequenz-Items mit eigener (0008,0005)-Deklaration bleiben in beiden Modi unangetastet (Warnung im Log) — nur die Deklaration auf oberster Ebene wird geschrieben; die eigene Deklaration des Items gilt für dessen Werte weiter.
  • Grenze bei Implicit VR: Bei Implicit-VR-Übertragung steht der Feldtyp nicht im Datenstrom; Textfelder erkennt die ASCII-Prüfung über eine eingebaute Tabelle der Standard-Tags. Private und unbekannte Tags werden nicht geprüft — die Prüfung ist konservativ auf die erkannten Textwerte beschränkt.
  • ISO_IR 6 entfernt: Der Standard-Zeichensatz wird auf dem Draht durch Abwesenheit von (0008,0005) ausgedrückt; charset: "ISO_IR 6" entfernt deshalb eine vorhandene Deklaration, statt eine zu setzen. Das wirkt nur mit mode: always — die Kombination mit if-absent wäre garantiert wirkungslos (bei fehlender Deklaration gibt es nichts zu entfernen) und wird beim Config-Load abgelehnt.
  • Erst prüfen, ob die Modalität es selbst kann: Viele Geräte können den gewünschten Abfrage-Zeichensatz selbst konfigurieren — das ist die sauberste Lösung. Das Plugin ist für Geräte gedacht, bei denen das nicht möglich ist.
  • Bei jedem unerwarteten Problem passiert die Nachricht unverändert — das Plugin verwirft nie Nachrichten und bricht nie Verbindungen ab.