Connecter un EAI
Chargez les plugins @posos/hl7 (HL7 v2 et CDA R2) et @posos/pn13 (SIPhII) sur le proxy et le moteur. Le proxy utilise DEFINITION_STORE_URL pour conserver les connexions et les messages reçus dans PostgreSQL.
Créer une connexion
Ouvrez Connexions → Connexions entrantes dans la console. Créez une connexion, choisissez le format et le transport, puis activez-la après avoir renseigné ses paramètres. Les formats sont ceux des plugins chargés.
Un transport est un type de connexion entrant : l’hôte fournit HTTP et Dossier, offerts à tout format ; un plugin en contribue d’autres, réservés à ses formats (@posos/hl7 contribue Écoute MLLP, pour HL7 v2). Le formulaire affiche les paramètres que le type déclare, avec leurs valeurs par défaut et leur description.
| Transport | Contribué par | Format | Paramètres |
|---|---|---|---|
Écoute MLLP (mllp) |
@posos/hl7 |
HL7 v2 ER7 | adresse d’écoute (défaut 127.0.0.1), port (défaut 2575, entre 1024 et 65535), encodage |
HTTP (http) |
hôte | tous | encodage ; credential Basic ou header ; URL /api/ingress/<id> |
Dossier (directory) |
hôte | tous | dossier (chemin absolu), encodage |
Une connexion peut aussi être déclarée dans le manifeste d’instance, avec format (voir Instance manifest) : elle apparaît en lecture seule dans la console, et son auth HTTP lit son secret dans l’environnement (env:NOM).
Les paramètres sont rangés dans params de la connexion (GET /api/connections). Un client antérieur qui envoie host, port, encoding ou directory au premier niveau reste accepté : ces champs sont rangés dans params. Une connexion sans valeur pour un paramètre prend sa valeur par défaut.
Pour HTTP, créez d’abord le credential dans la console et sélectionnez-le dans la connexion. Envoyez le texte ou les octets XML directement dans le corps HTTP, sans enveloppe JSON ni base64. Une connexion HTTP active exige un credential. La limite de réception est de 5 MiB.
GET /api/openapi.json documente chaque connexion HTTP non archivée sous son URL réelle. L’opération indique le format, l’encodage, l’état, le credential attendu, le profil de validation et les réponses de réception. Les types de média du corps et le message d’exemple sont ceux que déclare le plugin du format : application/hl7-v2 et text/plain avec un message d’exemple pour HL7 v2, application/xml et text/xml pour CDA R2, application/xml et text/xml avec un message d’exemple pour PN13. Le type de média n’est pas contrôlé à la réception : l’encodage de la connexion décode le corps. MLLP et dossier restent décrits dans la console car ils ne sont pas des opérations HTTP.
Pour MLLP, un acquittement positif est envoyé après persistance. Avec MSH-15 et MSH-16 vides, le mode original utilise AA, AE et AR. En mode amélioré, MSH-15 accepte AL (toujours), NE (jamais), ER (échec seulement) et SU (succès seulement) ; les codes de réception sont CA, CE et CR. AE et CE signalent une erreur liée au contenu : renvoyer les mêmes octets donne la même réponse. AR et CR signalent une réception momentanément impossible : l’EAI renvoie le message plus tard et la boîte d’entrée dédoublonne la retransmission.
| Situation | Mode original | Mode amélioré | ERR-3 |
|---|---|---|---|
| Message persisté, ou retransmission identique | AA |
CA |
— |
| Octets invalides dans l’encodage de la connexion | AE |
CE |
102 |
| Structure ER7 invalide, MSH-9, MSH-10 ou MSH-12 absent | AE |
CE |
100 |
| MSH-10 déjà reçu avec un autre contenu | AE |
CE |
205 |
MSH-16 ni vide ni NE |
— | CE |
103 |
| Connexion désactivée, échec de persistance, délai de 30 secondes dépassé | AR |
CR |
207 |
Un acquittement négatif porte un segment ERR : ERR-3 contient le code de la table HL7 0357 au format code^libellé^HL70357, ERR-4 la sévérité E, et ERR-2 l’emplacement MSH^1^16 pour un refus de MSH-16. MSA-3 n’est pas renseigné. Un en-tête illisible dans l’encodage de la connexion reçoit une réponse en mode original. Une demande d’acquittement applicatif différé est refusée avant persistance, puis la connexion est fermée. L’acquittement porte son propre MSH-10 de 20 caractères ; MSA-2 reprend le MSH-10 reçu et les identités de routage sont inversées dans la réponse. L’acquittement confirme la réception dans la boîte d’entrée ; il ne confirme pas la réussite du traitement clinique.
Le mode NE ne donne aucune confirmation de réception au système émetteur. Pour un échange avec reprise sur erreur, configurez AL en MSH-15 et NE en MSH-16. Les règles suivent le chapitre Control HL7.
Pour un dossier, configurez un chemin absolu accessible en lecture et écriture par le proxy. Si la variable INBOUND_DIRECTORY_ROOT est définie sur le proxy, le dossier doit se trouver sous cette racine. L’EAI écrit un fichier temporaire puis le renomme atomiquement en un nom unique terminé par .ready. Après persistance, Meridian le renomme en .ready.processed. Un échec conserve le fichier pour une nouvelle tentative. Le serveur SFTP, si nécessaire, reste externe et dépose dans ce dossier.
L’encodage choisi s’applique aux octets reçus : UTF-8, Windows-1252 ou ISO-8859-1. MSH-18 et la déclaration XML ne le remplacent pas.
Relier un workflow
Ajoutez le déclencheur Message HL7 v2 reçu, Document CDA reçu ou Message PN13 reçu, choisissez la connexion dans ses paramètres et publiez le workflow. Plusieurs workflows peuvent utiliser une même connexion. Seuls les workflows publiés liés à cette connexion reçoivent ses messages. Les messages reçus avant publication restent en attente.
Un sélecteur de déclencheur (trigger.selector) restreint les messages remis à un workflow. Il s’écrit sur event, comme pour le routage des autres événements : event.content contient le message source et event.connectionId la connexion, par exemple "ADT^A01" in event.content. Un workflow dont le sélecteur est faux ne reçoit pas le message ; les autres workflows liés le reçoivent. Un sélecteur en erreur a le même effet et le proxy journalise un avertissement « Filtre en erreur ». Voir Choose which events start a workflow.
Le déclencheur expose le texte source et la structure analysée. Reliez cette structure au nœud d’extraction correspondant pour sélectionner les champs utiles. Les identifiants restent des chaînes, y compris leurs zéros initiaux. La résolution du patient, la conversion des codes et les contrôles métier doivent être configurés explicitement.
Émettre des événements canoniques
Une connexion peut activer des bindings fournis par le plugin de son format. Chaque binding activé reconnaît un type de message et en extrait un événement de domaine canonique. Les workflows qui reçoivent ces événements traitent les messages hospitaliers sans manipuler l’objet HL7 ou PN13 ; le déclencheur brut reste disponible pour les autres profils.
Ces événements sont déclarés par @posos/clinical : leur contrat (sortes de contexte, champs typés, champs requis) se lit dans la console, Plugins → Événements → Événements déclarés. Le require d’un binding de message est comparé au contrat au chargement des plugins (avertissement « binding hors contrat » dans le journal, le binding reste actif). À la réception, le require du binding est appliqué ; en EVENT_CONTRACTS=enforce, les champs et identifiants requis par le contrat s’y ajoutent. L’événement émis est ensuite contrôlé contre son contrat selon le mode de l’instance (voir Event ingestion API).
| Binding | Message | Événement |
|---|---|---|
hl7v2-adt-admission |
ADT A01 et A06 | PatientAdmitted |
hl7v2-adt-discharge |
ADT A03 | PatientDischarged |
hl7v2-adt-transfer |
ADT A02 | PatientTransferred |
hl7v2-oru-bio-result |
ORU^R01, un événement par OBX numérique | BioResultReceived |
hl7v2-oru-critical-result |
ORU^R01, OBX critiques | CriticalResultReceived |
hl7v2-omp-medication |
OMP^O09, lignes nouvelles | MedicationPrescribed |
hl7v2-omp-discontinuation |
OMP^O09, lignes arrêtées, annulées ou remplacées | MedicationDiscontinued |
hl7v2-rde-medication |
RDE^O11, lignes validées | MedicationPrescribed |
hl7v2-ras-administration |
RAS^O17, administrations tracées | MedicationAdministered |
pn13-prescription-medication |
Prescription SIPhII | MedicationPrescribed |
Les bindings se cochent dans les paramètres de la connexion. Une option laissée vide prend la valeur par défaut affichée. Un message qui ne correspond à aucun binding activé n’émet rien. Un message reconnu mais incomplet (champ requis absent) n’émet pas d’événement et reste visible dans le journal du proxy ; il n’est pas rejoué.
Dates
Les dates des événements émis (admittedAt, dischargedAt, transferredAt, deceasedAt, occurredAt, issuedAt, authoredAt, startAt, discontinuedAt, administeredAt) sont converties en ISO 8601. Un horodatage reçu sans décalage, par exemple 20260905093000, est lu dans le fuseau de l’émetteur : l’option Fuseau horaire de l’émetteur du binding (identifiant IANA, par exemple Europe/Paris), sinon le fuseau de l’instance (timezone du manifeste d’instance, Europe/Paris par défaut), changements d’heure compris. Il devient 2026-09-05T09:30:00+02:00. La précision reçue est conservée : un jour seul donne 2026-09-05. Une valeur illisible ou hors calendrier n’est pas transmise : les bindings HL7 v2 passent à la source suivante (pour une admission : PV1-44, ZBE-2, EVN-6, EVN-2 puis MSH-7), le binding PN13 omet la date. Un fuseau inconnu rend le message inexploitable pour ce binding.
Identifiant patient
Tous les bindings HL7 lisent l’identifiant patient dans PID-3. Autorité d’affectation PID-3.4 retient la répétition dont l’autorité complète est égale à la valeur saisie. Sinon, Type d’identifiant PID-3.5 (défaut PI, l’IPP) retient la première répétition de ce type ; sans répétition de ce type, la première répétition est retenue. Une INS (type INS) n’est retenue que si PID-32 vaut VALI ; sinon l’identifiant est vide et l’événement n’est pas émis. L’identifiant émis doit être celui que le dossier connaît ; vérifiez ce réglage avant d’activer un binding.
Résultats de biologie
Un OBX est émis si son type OBX-2 est NM ou SN et si son statut figure dans Statuts de résultat émis (défaut F,C). Le statut est lu en OBX-11, sinon en OBR-25, sinon il vaut final. Ajoutez P pour recevoir aussi les résultats préliminaires. Les statuts X, D et W ne sont jamais émis, pas plus que les demandes annulées (ORC-5 CA) ou non réalisées (OBR-25 X), les OBX sans valeur, les valeurs SN non scalaires (rapport, intervalle), les réponses aux questions de prescription (OBX-29 QST ou SCI) et les OBX d’échantillon qui suivent un SPM. Pour une demande arrêtée en cours (ORC-5 DC), chaque OBX suit son propre statut. Un comparateur (<5, <^5 en SN) est transmis dans comparator ; une valeur SN réduite à un nombre (5.2) est acceptée.
L’analyte vient de la table code → analyte (paires code=analyte, cherchées en OBX-3.1 puis OBX-3.4), puis de la table LOINC intégrée, sinon du libellé OBX-3.2. La table intégrée couvre les codes LOINC principaux de ces analytes, convertis dans leur unité de référence :
| Analyte | Unité de référence | Unités acceptées |
|---|---|---|
creatinine |
µmol/L | µmol/L, mg/dL, mg/L, mmol/L |
potassium, sodium |
mmol/L | mmol/L, mEq/L |
hemoglobin |
g/dL | g/dL, g/L |
leukocytes, platelets |
10*9/L | 109/L, G/L, 103/µL, /nL |
crp |
mg/L | mg/L, mg/dL |
troponin-t, troponin-i |
ng/L | ng/L, pg/mL, ng/mL, µg/L |
lactate |
mmol/L | mmol/L, mg/dL |
glucose |
mmol/L | mmol/L, mg/dL, g/L |
urea |
mmol/L | mmol/L, g/L ; mg/dL pour l’azote uréique (LOINC 3094-0) |
nt-probnp |
ng/L | ng/L, pg/mL |
inr |
1 | sans unité |
L’unité est cherchée en OBX-6.1, puis OBX-6.2, puis dans le triplet alternatif OBX-6.4 et OBX-6.5. Pour ces analytes, un OBX sans unité acceptée n’est pas émis. Les autres analytes gardent la valeur et l’unité reçues (OBX-6.1, sinon OBX-6.2). Analytes connus uniquement à true n’émet que les OBX de la table code → analyte ; à loinc, ceux de la table code → analyte et de la table LOINC intégrée.
L’événement porte aussi le code LOINC (loincCode), le statut (final, amended pour un correctif, preliminary), l’interprétation OBX-8, l’intervalle de référence OBX-7 converti (referenceLow, referenceHigh, referenceRange), la date de prélèvement (occurredAt) et la date de validation (issuedAt).
hl7v2-oru-critical-result lit les mêmes OBX et émet un CriticalResultReceived quand l’interprétation figure dans Interprétations critiques (défaut LL,HH,AA) ou quand la valeur franchit un seuil critique. Un seuil s’écrit analyte:bas:haut dans l’unité de référence, par exemple potassium:2.8:6.2;creatinine::350, bornes incluses. Le même OBX émet aussi son BioResultReceived : les deux événements démarrent chacun les workflows publiés qui reçoivent leur type. Avec P dans les statuts de ce binding, un résultat préliminaire puis validé démarre deux fois les workflows qui reçoivent CriticalResultReceived.
Mouvements ADT
L’admission accepte par défaut A01 et A06 : un patient hospitalisé depuis les urgences reçoit A04 puis A06, sans A01. Saisissez A01 dans Événements ADT acceptés pour ne garder que les admissions directes. La date émise est la date clinique du mouvement : PV1-44 pour une admission, PV1-45 pour une sortie, ZBE-2 pour une mutation ou un A06. La sortie porte l’indicateur de décès PID-30 (deceased) ; une date de décès PID-29 avec PID-30 vide vaut décès. La mutation (A02) porte l’UF d’hébergement PV1-3, l’UF précédente PV1-6 et l’UF médicale ZBE-7 ; les natures listées dans Natures de mouvement ignorées (défaut L,D,LD : changement de lit seul, changement médico-administratif) n’émettent rien.
Un mouvement annulé ou mis à jour (ZBE-4 CANCEL ou UPDATE) n’émet rien : les annulations A11, A12, A13 et un A06 d’annulation ne lancent aucun workflow et ne rétractent pas les exécutions déjà lancées.
Pharmacie
Le produit est lu dans le triplet CE dont le système figure le plus tôt dans Systèmes de codes médicament (défaut 99CIS=urn:posos:cis;99POSOS=urn:posos:medicabase;99CIP=urn:posos:cip13;99UCD=urn:posos:ucd13). Sans triplet reconnu, le triplet principal est émis avec son système.
hl7v2-omp-medicationémet la prescription telle qu’écrite, avec la posologie structurée lorsqu’elle est transmise : dose RXO-2, unité RXO-4, prises par jour TQ1-3, durée TQ1-7 à TQ1-8, voie RXR-1, indication RXO-20 et texte RXO-7. Aucune posologie n’est déduite du texte libre. La dose TQ1-2 n’est reprise qu’avec son unité. Avec plusieurs TQ1 de doses différentes (dose variable selon l’heure), ni la dose ni les prises par jour ne sont émises ; avec une même dose, les prises de chaque TQ1 sont additionnées s’ils couvrent la même période.hl7v2-rde-medicationn’émet que les lignes validées : ORC-1NWouRO(ligne de remplacement), ouSCavec ORC-5IPet une validation finale (ORC-25V3, ou ORC-25 sans état HMW). Les refus, annulations et validations intermédiaires n’émettent rien. La posologie suit les mêmes règles, lue en RXE et dans les TQ1 du groupe encodé.hl7v2-omp-discontinuationémet l’arrêt, l’annulation ou le remplacement d’une ligne (ORC-1DC,CA,RP) ; le code d’origine est danscontrol.hl7v2-ras-administrationémet chaque administration dont le statut RXA-20 figure dans Statuts d’administration émis (défautCP), hors refus (RXA-18), suppression (RXA-21D) et annulation (ORC-1OC).
Activez hl7v2-omp-medication quand le flux du DPI vers la pharmacie est visible, et hl7v2-rde-medication seulement sinon. Si les deux sont actifs sur la même connexion, une ligne reçue en OMP puis en RDE n’émet qu’un MedicationPrescribed ; une substitution par le pharmacien n’est alors pas analysée à nouveau. Si l’OMP et le RDE arrivent par deux connexions distinctes, la même ligne émet un événement sur chacune.
Identifiants stables
Chaque événement HL7 porte l’identité métier du fait projeté : demande et analyte pour un résultat, identifiant de mouvement ZBE-1, ligne de prescription ORC-2, administration. L’identifiant de l’événement en est dérivé, par connexion et par type d’événement. Sur une même connexion, une retransmission, un correctif (C) de même valeur qu’un résultat validé ou une ligne reçue en OMP puis en RDE ne lancent donc qu’une exécution. Un correctif qui change la valeur est un nouveau fait et relance les workflows. Deux connexions ne partagent pas leurs identifiants : un même fait reçu par deux connexions lance une exécution par connexion. Un résultat préliminaire et son résultat validé restent deux faits distincts. Sans identité métier (pas d’OBR-3 ni d’OBR-2, pas de ZBE, pas d’ORC-2), l’identifiant est dérivé du message et du binding. Chaque workflow qui reçoit le type de l’événement démarre, et chaque workflow reçoit un événement une fois : une retransmission ne démarre que les workflows qui ne l’ont pas encore reçu. Un workflow publié après la première réception de l’événement ne le reçoit pas à la retransmission. Si la soumission d’une exécution échoue, le message passe en delivery-unavailable et l’EAI le relivre plus tard : seules les exécutions manquantes démarrent. Les événements apparaissent dans l’historique du trafic comme des événements ordinaires, une entrée par workflow démarré.
Valider les messages PN13
Dans les paramètres de la connexion PN13, sélectionnez Profil de validation → PN13-IS 4.0.34, puis enregistrez. Sans validation conserve la réception existante. Le profil est propre à la connexion et s’applique aux transports HTTP et dossier, avant toute livraison aux workflows ou aux bindings.
Le profil utilise le fichier PN13-IS.4.0.34.xsd, version 4.0.34, avec l’empreinte SHA-256 3a33815ef5dc9c9d42ad306b5fdd4ecd1ea054b875311b7df5906d036c006fb4. Le namespace SIPhII est requis par ce schéma ; les messages anciens sans namespace ne sont pas conformes à ce profil.
Un document conforme entre dans la file de livraison. Un document non conforme reste dans la boîte de réception avec le statut Refusé et le motif « Validation refusée. », et ne déclenche aucun workflow. HTTP renvoie 422 avec le message « Validation refusée (profil « PN13-IS 4.0.34 »). », l’identifiant de réception et le rapport ; un fichier est conservé avec le suffixe .ready.rejected. Une indisponibilité du validateur renvoie 503 en HTTP et laisse le fichier prêt pour une nouvelle tentative.
Dans Routage → Historique du trafic, ouvrez le rapport du message : il contient le profil, l’empreinte du schéma, la version du validateur, les lignes et éléments concernés. Les diagnostics ne contiennent pas les valeurs XML. Les doublons conservent leur décision initiale, même après modification du profil. Le bouton de reprise ne relance pas les refus : corrigez le document et envoyez-le avec un nouvel identifiant.
Le serveur API requiert xmllint (libxml2). L’image Docker et la CI installent libxml2-utils ; une installation locale doit fournir xmllint dans le PATH. Le validateur traite au plus deux documents simultanément, avec un délai de cinq secondes par document. Les DTD sont refusées avant validation ; les ressources réseau sont désactivées. L’encodage de la connexion décode les octets ; le texte est ensuite transmis au validateur en UTF-8, sans modifier la source conservée.
La validation s’applique à la réception par connexion. Les nœuds de parsing appelés directement et les simulations n’effectuent pas cette validation XSD. Le profil ne vérifie pas les règles cliniques ou les nomenclatures et ne remplace pas les profils CDA/HL7.
Vérifier la réception
Envoyez un message de test synthétique depuis l’EAI. Dans la connexion, consultez son identifiant, sa date, son état et les tentatives. HTTP renvoie 202 après persistance ; un doublon identique renvoie le même identifiant. Un identifiant réutilisé avec un contenu différent est refusé (409 en HTTP, AE ou CE en MLLP).
La déduplication est propre à la connexion : application émettrice + établissement + MSH-10 pour HL7, Phast-id_message pour PN13, ou empreinte du contenu PN13 si cet attribut manque. Utilisez des identifiants stables lors des retransmissions.
Les messages en attente survivent au redémarrage du proxy. La livraison fige les workflows cibles puis conserve la progression par cible avec un identifiant Restate stable. Les exécutions des cibles d’un même message partagent leur événement source (inbound:<message>) : le lien « Même événement » d’une exécution les regroupe. L’état livré signifie que la remise au workflow a été effectuée, ou que son sélecteur l’a exclu. Consultez ensuite l’exécution pour connaître le résultat du workflow. Le bouton de reprise avance la prochaine tentative des messages en attente ; il ne rejoue pas les messages livrés. Une connexion désactivée suspend réception et livraison.
Retirer une connexion
Désactivez une connexion pour suspendre sa réception et sa livraison. L’archivage arrête définitivement son endpoint et la retire des sélecteurs tout en conservant sa configuration et son historique. Il est bloqué tant qu’un workflow publié utilise la connexion ; réaffectez ou dépubliez ces workflows au préalable.
La suppression définitive est proposée uniquement si aucun workflow, quel que soit son état, ne référence la connexion et si elle n’a reçu aucun message. Les connexions ayant un historique restent archivables mais ne sont jamais supprimées.
Déployer les récepteurs
Le proxy possède les récepteurs. En conteneur, liez MLLP à 0.0.0.0, publiez explicitement le port choisi sur le service proxy, et montez le dossier si vous utilisez les fichiers. Le Compose fourni ne publie pas de port MLLP par défaut.
Un verrou PostgreSQL élit un proxy pour les listeners et la livraison. Les répliques doivent partager les mêmes chemins et disposer d’un routage réseau vers le récepteur actif. L’état affiché dans la console reflète le proxy interrogé. Vérifiez cette topologie avant un déploiement répliqué.
Placez la console et les routes d’administration derrière le contrôle d’accès du déploiement. Utilisez HTTPS pour HTTP et le réseau d’intégration protégé de l’établissement pour MLLP ; ce récepteur ne fournit pas de TLS natif. La boîte d’entrée conserve le contenu source : incluez ses tables dans les règles de sauvegarde et de rétention du déploiement. Aucune purge automatique n’est configurée.
Les parseurs contrôlent la syntaxe prise en charge. La validation XSD PN13 est disponible par connexion ; les profils HL7/CDA, les règles de prescription et les conversions FHIR restent distincts.
Voir aussi Mapper un message HL7 ou PN13 et Traiter un document CDA R2.