#Opérations d'entreprise

0 Abonnés · 10 Publications

InterSystems Ensemble fournit des classes d'opérations commerciales spécialisées qui utilisent des adaptateurs sortants spécifiques pour communiquer avec des systèmes et des interfaces utilisateur externes.

Documentation.

Article Guillaume Rongier · Fév 12, 2025 5m read

Cela fait longtemps que je n'ai pas écrit de post de mise à jour sur l'IoP.

image

Quelles sont les nouveautés depuis la publication de l'interface en ligne de commande de l'IoP?

Deux nouvelles fonctionnalités importantes ont été ajoutées à l'IoP:

  • Rebranding: le module grongier.pex a été renommé en iop pour refléter le nouveau nom du projet.
  • Support des opérations asynchrones: L'interface de programmation prend désormais en charge les fonctions asynchrones et les coroutines.

Rebranding

Le module grongier.pex a été renommé en iop pour refléter le nouveau nom du projet.

Le module grongier.pex est encore disponible pour des raisons de rétrocompatibilité, mais il sera supprimé à l'avenir.

Support des opérations asynchrones

L'IoP supporte les appels asynchrones depuis longtemps, mais il n'était pas possible d'utiliser les fonctions asynchrones et les coroutines directement dans l'IoP..

Avant d'aborder cette nouvelle fonctionnalité, je vais expliquer le fonctionnement des appels asynchrones dans InterSystems IRIS et présenter deux exemples d'utilisation des appels asynchrones dans l'IoP.

Appels asynchrones hérités

Examinons le fonctionnement des appels asynchrones hérités du passé:

from iop import BusinessProcess
from msg import MyMessage


class MyBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        self.send_request_async("Python.MyBO", msg_one,completion_key="1")
        self.send_request_async("Python.MyBO", msg_two,completion_key="2")

    def on_response(self, request, response, call_request, call_response, completion_key):
        if completion_key == "1":
            self.response_one = call_response
        elif completion_key == "2":
            self.response_two = call_response

    def on_complete(self, request, response):
        self.log_info(f"Received response one: {self.response_one.message}")
        self.log_info(f"Received response two: {self.response_two.message}")

En fait, ils fonctionnent de la même manière que les appels asynchrones dans IRIS. La méthode send_request_async envoie une requête à une opération métier (Business Operation) et la méthode on_response est appelée lorsque la réponse est reçue.

Vous pouvez distinguer les réponses par le paramètre completion_key.

Envoi de plusieurs requête de synchronisation

Il ne s'agit pas vraiment d'une nouvelle fonctionnalité, mais il convient de mentionner qu'il est possible d'envoyer plusieurs requêtes de synchronisation en parallèle:

from iop import BusinessProcess
from msg import MyMessage


class MyMultiBP(BusinessProcess):

    def on_message(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        tuple_responses = self.send_multi_request_sync([("Python.MyMultiBO", msg_one),
                                                        ("Python.MyMultiBO", msg_two)])

        self.log_info("All requests have been processed")
        for target,request,response,status in tuple_responses:
            self.log_info(f"Received response: {response.message}")

Ici, nous envoyons deux requêtes à la même opération métier (Business Operation) en parallèle.

La réponse est un tuple contenant la cible, la requête, la réponse et le statut de chaque appel.

C'est très utile lorsque vous devez envoyer plusieurs requêtes et que vous ne vous souciez pas de l'ordre des réponses.

Fonctions asynchrones et coroutines

Voyons maintenant comment utiliser les fonctions asynchrones et les coroutines dans l'IoP:

import asyncio

from iop import BusinessProcess
from msg import MyMessage


class MyAsyncNGBP(BusinessProcess):

    def on_message(self, request):

        results = asyncio.run(self.await_response(request))

        for result in results:
            print(f"Received response: {result.message}")

    async def await_response(self, request):
        msg_one = MyMessage(message="Message1")
        msg_two = MyMessage(message="Message2")

        # utilisation d'asyncio.gather pour envoyer plusieurs requêtes de manière asynchrone
        # utilisation de la méthode send_request_async_ng
        tasks = [self.send_request_async_ng("Python.MyAsyncNGBO", msg_one),
                 self.send_request_async_ng("Python.MyAsyncNGBO", msg_two)]

        return await asyncio.gather(*tasks)

Dans cet exemple, nous envoyons plusieurs demandes à la même opération métier (Business Operation) en parallèle à l'aide de la méthode send_request_async_ng.

Si vous avez lu attentivement ce post jusqu'à ce point, veuillez ajouter le commentaire "Boomerang". C'est peut-être peu de choses pour vous, mais pour moi c'est très important. Je vous remercie!

La méthode await_response est une coroutine qui envoie plusieurs requêtes et attend que toutes les réponses soient reçues. Grâce à la fonction asyncio.gather, nous pouvons attendre que toutes les réponses soient reçues en parallèle.

Les avantages de l'utilisation des fonctions asynchrones et des coroutines sont les suivants :

  • Meilleures performances: vous pouvez envoyer plusieurs requêtes en parallèle.
  • Simplicité de lecture et de maintenance: vous pouvez utiliser le mot-clé await pour attendre les réponses.
  • Plus de flexibilité: vous pouvez utiliser le module asyncio pour créer des flux de travail complexes.
  • Plus de contrôle: vous pouvez utiliser le module asyncio pour gérer les exceptions et les timeouts.

Conclusion

Quelle est la différence entre send_request_async, send_multi_request_sync et send_request_async_ng?

  • send_request_async: envoie une requête à une opération métier (Business Operation) et attend la réponse si la méthode on_response est implémentée et le paramètre completion_key est utilisé.
    • avantage: vous pouvez utiliser les appels asynchrones comme vous en avez l'habitude.
    • inconvénient: il peut être difficile à maintenir si vous avez besoin d'envoyer plusieurs requêtes en parallèle.
  • send_multi_request_sync : envoie plusieurs requêtes à la même opération métier (Business Operation) en parallèle et attend que toutes les réponses soient reçues.
    • avantage: facile à utiliser.
    • inconvénient: vous ne pouvez pas contrôler l'ordre des réponses (c'est-à-dire que la liste des réponses n'est pas ordonnée).
  • send_request_async_ng: envoie plusieurs requêtes à la même opération métier (Business Operation) en parallèle et attend que toutes les réponses soient reçues.
    • avantage: vous pouvez contrôler l'ordre des réponse.
    • inconvénient : vous devez utiliser des fonctions asynchrones et des coroutines.

Joyeux multitraitement!

0
0 46
Article Iryna Mykhailova · Jan 27, 2025 1m read

Dans votre production d'interopérabilité, vous pouvez toujours avoir une Business Operation qui est un client HTTP, qui utilise OAuth 2.0 pour l'authentification, mais vous avez du personnaliser l'opération pour cette méthodologie d'authentification. Depuis la version v2024.3, qui a été récemment publiée, il existe une nouvelle fonctionnalité, fournissant de nouveaux paramètres, pour gérer cela plus facilement.

Dans votre Business Operation qui utilise l'outbound adaptateur HTTP, vous trouverez de nouveaux paramètres, sous le groupe OAuth.

0
0 42
Article Iryna Mykhailova · Oct 9, 2024 4m read

L'accès à un stockage cloud Azure pour charger/télécharger des blobs est assez simple à l'aide des méthodes API de classe %Net.Cloud.Storage.Client désignées ou des adaptateurs entrants/sortants EnsLib.CloudStorage.*.

Notez que vous devez avoir le serveur de %JavaServer External Language opérationnel pour utiliser l'API ou les adaptateurs de stockage cloud, car ils utilisent tous deux le framework PEX à l'aide du serveur Java.

Voici un bref résumé :

L'accès à Azure Blob Storage s'effectue à l'aide d'une chaîne de connexion qui ressemble à celle-ci :

0
0 91
Article Pierre LaFay · Juin 26, 2024 1m read

Ajouter un identifiant pour se connecter à l'interface FHIR REST - dans ce cas, ne considérer qu'une authentification de base

 

Ajouter un registre de service - dans ce cas, ne considérer qu'une authentification de base

- configurer un service HTTP

- saisir le chemin d'accès au serveur FHIR

- saisir l'URL du service FHIR

- utiliser l'identifiant profilé


 

 

Ajouter un "HS.FHIRServer.Interop.HTTPOperation"

Choisissez le nom du service

Tester le client FHIR

Tracer le résultat du test

0
0 66
Article Pierre LaFay · Avr 9, 2024 4m read

Introduction

Il existe une option d'assistant de procédure de liaison dans le portail de gestion (Système > SQL > Assistants > Procédure de liaison) avec laquelle j'ai eu des problèmes de fiabilité, j'ai donc décidé d'utiliser cette solution à la place.

Problème

0
0 77
Question Cécile Heuillet · Jan 17, 2024

Bonjour,

je dois faire une requête LDAP pour récupérer des infos sur une équipe spécifique d'employés. J'ai un service custom qui instancie un message de type EnsLib.LDAP.Message.Search avec un filtre correspondant à la recherche et qui le redirige vers une opération de type EnsLib.LDAP.Operation.Standard. Je ne récupérais pas d'erreur mais pas de résultat non plus. J'ai donc créé une opération qui hérite de EnsLib.LDAP.Operation.Standard et j'ai surchargé la méthode pour récupérer qqch. Je récupère ceci :

3
1 132
Article Pierre LaFay · Jan 7, 2024 8m read

Introduction

Cet article vise à explorer le fonctionnement du système FHIR-PEX et a été développé, en tirant parti des capacités d'InterSystems IRIS.

En rationalisant l'identification et le traitement des examens médicaux dans les centres de diagnostic clinique, notre système vise à améliorer l'efficacité et la précision des flux de travail de soins de santé. En intégrant les normes FHIR à la base de données InterSystems IRIS Java-PEX, le système aide les professionnels de santé avec des capacités de validation et de routage, contribuant ainsi à améliorer la prise de décision et les soins aux patients.

how it works

  • Interopérabilité IRIS : Reçoit les messages au standard FHIR, garantissant l'intégration et la compatibilité avec les données de santé.

  • Traitement de l'information avec 'PEX Java' : Traite les messages au format FHIR et les dirige vers des sujets Kafka en fonction de règles configurées globalement dans la base de données, facilitant ainsi le traitement et le routage efficaces des données, en particulier pour les examens dirigés vers la quarantaine.

  • Gestion des retours Kafka via un backend Java externe : Interprète uniquement les examens dirigés vers la quarantaine, permettant au système de gérer les retours de Kafka via un backend Java externe. Il facilite la génération d'informations pronostiques pour les professionnels de la santé grâce à l'IA générative, en s'appuyant sur les consultations des résultats d'examens précédents pour les patients respectifs.

Development

Grâce au PEX (Production EXtension) d'InterSystems, un outil d'extensibilité permettant d'améliorer et de personnaliser le comportement du système, nous avons élaboré une Opération Métier. Ce composant est chargé de traiter les messages entrants au format FHIR au sein du système. Comme exemple suivant :

import com.intersystems.enslib.pex.*;
import com.intersystems.jdbc.IRISObject;
import com.intersystems.jdbc.IRIS;
import com.intersystems.jdbc.IRISList;
import com.intersystems.gateway.GatewayContext;

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class KafkaOperation extends BusinessOperation {
// Connection to InterSystems IRIS
private IRIS iris;

// Connection to Kafka
private Producer<Long, String> producer;

// Kafka server address (comma separated if several)
public String SERVERS;

// Name of our Producer
public String CLIENTID;

/// Path to Config File
public String CONFIG;

public void OnInit() throws Exception {
[...]
}

public void OnTearDown() throws Exception {
[...]
}

public Object OnMessage(Object request) throws Exception {
    IRISObject req = (IRISObject) request;
    LOGINFO("Received object: " + req.invokeString("%ClassName", 1));

    // Create record
    String value = req.getString("Text");
    String topic = getTopicPush(req);
    final ProducerRecord<Long, String> record = new ProducerRecord<>(topic, value);

    // Send new record
    RecordMetadata metadata = producer.send(record).get();

    // Return record info
    IRISObject response = (IRISObject)(iris.classMethodObject("Ens.StringContainer","%New",topic+"|"+metadata.offset()));
    return response;
}

private Producer<Long, String> createProducer() throws IOException {
[...]
}

private String getTopicPush(IRISObject req) {
[...]
}

[...]
}

`

Au sein de l'application, la méthode getTopicPush se charge d'identifier le sujet auquel le message sera envoyé.

La détermination du sujet auquel le message sera envoyé dépend de l'existence d'une règle dans la globale "quarantineRule", telle que lue dans IRIS.

String code = FHIRcoding.path("code").asText();
String system = FHIRcoding.path("system").asText();

IRISList quarantineRule = iris.getIRISList("quarantineRule",code,system);

 String reference = quarantineRule.getString(1);
 String value = quarantineRule.getString(2);

 String observationValue = fhir.path("valueQuantity").path("value").asText()

When the global ^quarantineRule exists, validation of the FHIR object can be validated.

private boolean quarantineValueQuantity(String reference, String value, String observationValue) {
    LOGINFO("quarantine rule reference/value: " + reference + "/" + value);
    double numericValue = Double.parseDouble(value);
    double numericObservationValue = Double.parseDouble(observationValue);

    if ("<".equals(reference)) {
        return numericObservationValue < numericValue;
    }
    else if (">".equals(reference)) {
        return numericObservationValue > numericValue;
    }
    else if ("<=".equals(reference)) {
        return numericObservationValue <= numericValue;
    }
    else if (">=".equals(reference)) {
        return numericObservationValue >= numericValue;
    }
    
    return false;
}

Exemple pratique :

Lors de la définition d'une globale, telle que :

Set ^quarantineRule("59462-2","http://loinc.org") = $LB(">","500") 

Ceci établit une règle pour le code "59462-2" et le système ""http://loinc.org"" dans la globale ^quarantineRule , spécifiant une condition dans laquelle la valeur supérieure à 500 est définie comme quarantaine. Dans l'application, la méthode getTopicPush peut ensuite utiliser cette règle pour déterminer le sujet approprié pour envoyer le message en fonction du résultat de la validation.

Compte tenu de l'affectation, le JSON ci-dessous serait envoyé en quarantaine car il correspond à la condition spécifiée en ayant :

 {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
}

"valueQuantity": { "value": 550, "unit": "ng/dL", "system": "http://unitsofmeasure.org", "code": "ng/dL" }

FHIR Observation:

{
    "resourceType": "Observation",
    "id": "3a8c7d54-1a2b-4c8f-b54a-3d2a7efc98c9",
    "status": "final",
    "category": [
      {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/observation-category",
            "code": "laboratory",
            "display": "laboratory"
          }
        ]
      }
    ],
    "code": {
      "coding": [
        {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
        }
      ],
      "text": "Testosterone"
    },
    "subject": {
      "reference": "urn:uuid:274f5452-2a39-44c4-a7cb-f36de467762e"
    },
    "encounter": {
      "reference": "urn:uuid:100b4a8f-5c14-4192-a78f-7276abdc4bc3"
    },
    "effectiveDateTime": "2022-05-15T08:45:00+00:00",
    "issued": "2022-05-15T08:45:00.123+00:00",
    "valueQuantity": {
      "value": 550,
      "unit": "ng/dL",
      "system": "http://unitsofmeasure.org",
      "code": "ng/dL"
    }
}

L'application Java Quarkus

Après envoi sur le sujet souhaité, une application Quarkus Java a été construite pour recevoir les examens en quarantaine. @ApplicationScoped public class QuarentineObservationEventListener {

@Inject
PatientService patientService;

@Inject
EventBus eventBus;

@Transactional
@Incoming("observation_quarantine")
public CompletionStage<Void> onIncomingMessage(Message<QuarentineObservation> quarentineObservationMessage) {
	var quarentineObservation = quarentineObservationMessage.getPayload();
	var patientId = quarentineObservation.getSubject()
			.getReference();
	var patient = patientService.addObservation(patientId, quarentineObservation);
	publishSockJsEvent(patient.getId(), quarentineObservation.getCode()
			.getText());
	return quarentineObservationMessage.ack();
}

private void publishSockJsEvent(Long patientId, String text) {
	eventBus.publish("monitor", MonitorEventDto.builder()
			.id(patientId)
			.message(" is on quarentine list by observation ." + text)
			.build());
}
 }

Ce segment du système est chargé de conserver les informations reçues de Kafka, de les stocker dans les observations du patient dans la base de données et de notifier l'événement au moniteur.

The monitor

Enfin, le moniteur du système est chargé de fournir une visualisation frontale simple. Cela permet aux professionnels de la santé d’examiner les données des patients/examens et de prendre les mesures nécessaires.

Implementation of langchainPT

Grâce au moniteur, le système permet aux professionnels de la santé de demander des recommandations à l'IA générative.

@Unremovable
@Slf4j
@ApplicationScoped
public class PatientRepository {
	@Tool("Get anamnesis information for a given patient id")
	public Patient getAnamenisis(Long patientId) {
		log.info("getAnamenisis called with id " + patientId);
		Patient patient = Patient.findById(patientId);
		return patient;
	}

	@Tool("Get the last clinical results for a given patient id")
	public List<Observation> getObservations(Long patientId) {
		log.info("getObservations called with id " + patientId);
		Patient patient = Patient.findById(patientId);
		return patient.getObservationList();
	}

}

suivre la mise en œuvre de Langchain4j

@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.BeanChatMemoryProviderSupplier.class, tools = {PatientRepository.class})
public interface PatientAI {

	@SystemMessage("""
			You are a health care assistant AI. You have to recommend exams for patients based on history information.
			""")
	@UserMessage("""
			 Your task is to recommend clinical exams for the patient id {patientId}.

			 To complete this task, perform the following actions:
			 1 - Retrieve anamnesis information for patient id {patientId}.
			 2 - Retrieve the last clinical results for patient id {patientId}, using the property 'name' as the name of exam and 'value' as the value.
			 3 - Analyse results against well known conditions of health care.

			 Answer with a **single** JSON document containing:
			 - the patient id in the 'patientId' key
			 - the patient weight in the 'weight' key
			 - the exam recommendation list in the 'recommendations' key, with properties exam, reason and condition.
			 - the 'explanation' key containing an explanation of your answer, especially about well known diseases.

			Your response must be just the raw JSON document, without ```json, ``` or anything else.
			 """)
	String recommendExams(Long patientId);
}

Le système peut ainsi aider les professionnels de santé à prendre des décisions et à mener des actions.

Video demo

VIDEO

Authors

NOTE:

L'application https://openexchange.intersystems.com/package/fhir-pex participe actuellement au concours InterSystems Java 2023. N'hésitez pas à explorer davantage la solution et n'hésitez pas à nous contacter si vous avez des questions ou avez besoin d'informations supplémentaires. Nous vous recommandons d'exécuter l'application dans votre environnement local pour une expérience pratique. Merci pour l'opportunité 😀!

0
0 124
Article Guillaume Rongier · Juil 12, 2023 4m read

L' adaptateur Telegram pour InterSystems IRIS sert de pont entre la populaire plateforme de messagerie Telegram et InterSystems IRIS, facilitant une communication et un échange de données transparents. En exploitant les capacités de l'API Telegram, l'adaptateur permet aux développeurs de créer des chatbots robustes, d'automatiser des tâches et d'intégrer Telegram aux applications d'InterSystems IRIS.

Les scénarios les plus courants dans lesquels l'adaptateur Telegram peut être utilisé sont les suivants :

0
0 114
Article Guillaume Rongier · Jan 4, 2023 11m read

Les systèmes EHR (Electronic Health Record) sont modélisés dans un format/structure propriétaire et ne sont pas basés sur des modèles du marché tels que FHIR ou HL7. Certains de ces systèmes peuvent interopérer des données dans un format propriétaire pour FHIR et d'autres modèles de marché, mais pas tous. InterSystems dispose de deux plateformes capables d'interopérer des formats propriétaires pour ceux du marché : InterSystems HealthShare Connect et InterSystems IRIS for Health. La fonctionnalité de transformation (DTL - Data Transformation Language) de ces plateformes peut recevoir des données dans n'importe quel format, structure ou canal de communication (CSV, JSON, XML, et autres via FTP, File, HTTP, etc.) et les transformer directement en formats du marché (FHIR, CDA, HL7, etc.). Cependant, InterSystems dispose d'un format intermédiaire appelé SDA (Summary Document Architecture) qui est utilisé par ces plateformes pour générer sans effort des FHIR STU, R3, R4, HL7v2, HL7v3, etc. En outre, lorsqu'elles sont au format SDA, les données de santé peuvent être conservées dans le RCU HealthShare. Ainsi, le format propriétaire/personnel est d'abord transformé en SDA, puis les données peuvent être automatiquement converties dans n'importe quel format du marché, ainsi que sauvegardées dans HealthShare. Dans cet article, nous allons vous montrer comment transformer des données propriétaires/personnalisées en SDA à l'aide d'IRIS for Health. L'exemple de données que nous avons utilisé a été généré par le projet de génération de données en masse SYNTHEA (https://synthea.mitre.org/downloads). Nous allons convertir 1000 patients d'un fichier CSV en SDA, en utilisant les fonctions d'interopérabilité d'IRIS for Health.

Application de soutien aux articles – custom2sda

Installation de l'application d'exemple qui sera utilisée avec cet article en suivant les instructions :

Si vous voulez installer en utilisant ZPM :

1. Ouvrez le Namespace IRIS avec l'interopérabilité activée.
2. Ouvrez le Terminal et appelez : USER>zpm "install custom2sda"

Si vous voulez installer en utilisant Docker :

1. Clonez Git et tirez le repo dans n'importe quel répertoire local :

$ git clone https://github.com/yurimarx/custom2sda.git

2. Ouvrez le terminal dans ce répertoire et exécutez :

$ docker-compose build

3. Lancez le conteneur IRIS avec votre projet :

$ docker-compose up -d

4. Ouvrez la production (http://localhost:52775/csp/healthshare/user/EnsPortal.ProductionConfig.zen?PRODUCTION=customsda.CustomToSDAProduction) et la lancez. (utilizateur _SYSTEM et le mot de passe SYS). Elle lira le fichier patients.csv et le convertira en SDA.

Créer une carte d'enregistrement CSV pour obtenir les données personnalisées/propriétaires

Dans l'étape précédente, vous avez exécuté la production qui a lu le fichier patients.csv et l'a transformé en SDA. Maintenant nous allons faire la même chose avec patients2.csv. Nous pourrions profiter de la production actuelle, mais je voudrais montrer comment créer tout à partir de zéro. Donc, arrêtez cette production, et faisons ce qui suit.

1.    Allez au portail de gestion (http://localhost:52775/csp/sys/%25CSP.Portal.Home.zen?$NAMESPACE=USER, assurez-vous que vous êtes dans l'espace de noms de l'utilisateur).
2.    Créer un CSV Mapper pour le fichier patients2.csv. Interopérabilité > Build > CSV Record Wizard (Assistant d'enregistrement CSV) :

Remarque : sélectionnez CRLF pour Record Terminator (terminateur d'enregistrement)

3.    Cliquez sur "Create RecordMap" pour ouvrir l'interface utilisateur de Record Mapper et modifiez le nom de la classe cible en customsda.Patients2RecordMap.Record :

4.    Sélectionnez BIRTHPLACE et définissez MAXLEN=200 dans le champ Paramètres du type de données. Par défaut, tous les champs %String contiennent 50 caractères, mais BIRTHPLACE et ADDRESS ont besoin de plus d'espace. Faites de même pour le champ ADDRESS :

5.    Cliquez sur le bouton "Save" (Enregistrer) et sur le bouton "Generate" (Générer). Acceptez les options par défaut et cliquez sur Ok pour générer les classes RecordMap.

6.    Cliquez sur "Interoperability" (Interopérabilité) pour passer aux tâches suivantes:

Créer la transformation de données de Custom à SDA

Il est temps d'utiliser le DTL pour construire visuellement la carte de transformation de Custom à SDA.

1.    Cliquez sur Interoperability" > Build > Data Transformations (transformation de données) > Go :

2.    Vous pouvez voir le Data Transformation Builder ici. Cliquez sur le bouton "New" (Nouveau) :

3.    Dans Data Transformation Wizard (Assistant de transformation des données), changez le Package en customsda, et le Name en PatientDTL2 :

4.    À ce stade, nous allons définir la classe de la source. Cliquez sur l'icône de la loupe près du champ "Source Class" :

5.    Cliquez sur Message Classes > customsda > Patients2RecordMap > Record :

6.    À ce stade, la classe "Source Class" devrait ressembler à ceci :

7.    Dans la section Target Type, sélectionnez XML et acceptez Target Class avec la valeur EnsLib.EDI.XML.Document :

8.    Sélectionnez l'icône de la loupe près du Type de document cible et cliquez sur Document XML > SDA3_schema > Container :

Remarque 1 : Le conteneur est l'élément racine de tous les éléments SDA, comme Patient.

Remarque 2 : pour que SDA3_schema soit disponible, les actions suivantes sont nécessaires :
Copier SDA3_schema.xsd dans votre système de fichiers local :

Importez le schéma XSD de SDA3 :

9.    Maintenant, lorsque la source et la cible sont configurées, cliquez sur le bouton OK :

10.    Les champs source et cible sont maintenant disponibles pour le mappage visuel :

11.    Pour créer une transformation, vous devez faire glisser le cercle du champ source et le déposer dans la flèche du champ cible, ligne par ligne.

12.    Dans la section Actions, vous pouvez voir les résultats obtenus :

13.    Après avoir mappé tous les champs, votre liste d'actions ressemblera à ceci :

Remarque : pour les propriétés avec (), vous devez définir l'index car ces propriétés peuvent avoir plus d'un élément. Dans cet exemple, nous n'avons qu'une seule adresse, donc target.{Patient.Addresses(1)….} est configuré avec 1.

14.    Cliquez sur le bouton "Save" (enregistrer) et sur le bouton "Compile" (compiler).

15.    Enfin, cliquez sur le raccourci "Interoperability" pour accéder au menu d'Interoperability :

Créer la production de l'interopérabilité (Interoperability Production), le dernier artefact pour compléter notre travail !

Les productions sont les mécanismes utilisés pour automatiser efficacement les flux d'intégration. Il est temps de créer notre production pour transformer les données des patients du fichier patients2.csv en SDA.

1.    Cliquez sur Interoperability > List > Productions :

2.    Cliquez sur le bouton "New" :

3.    Définissez Customsda sur Package, Patients2Production sur Production Name et Production Type sur Generic. Cliquez sur Ok :

4.    Nous avons maintenant la configuration de production :

5.    Cliquez sur le bouton "Plus" près de "Services" :

Remarque : Les services sont les composants utilisés par les productions pour obtenir des données sources.

6.    Configurez la classe "Service Class" avec la valeur EnsLib.RecordMap.Service.FileService, et le nom du service "Service Name" comme PatientCSVService, et cochez "Enable Now" :

7.    Sélectionnez PatientCSVService et configurez l'onglet Paramètres avec les valeurs

  et cliquez sur "Apply" (appliquer):
●    Chemin du fichier : /opt/user/data/
●    Spécification du fichier : patients2.csv
●    Carte d'enregistrement : Patients2RecordMap
●    Noms des configurations cibles : PatientProcess

8.    Cliquez sur le bouton "Plus" situé près des Opérations :

Remarque : Les opérations sont des composants de production utilisés pour écrire/persister des données sur une cible (bases de données, systèmes, API, services Web, FTP, fichier, etc.)

9.    Configurez l'Opération avec les valeurs suivantes :
●    Classe d'Opération : EnsLib.EDI.XML.Operation.FileOperation
●    Nom d'Opération : PatientSDAOperation
●    Cochez Enable Now

10.    Sélectionnez PatientSDAOperation et définissez la valeur /opt/user/data/ comme chemin de fichier, puis cliquez sur le bouton "Apply" :

11.    Cliquez sur le bouton "Plus" à côté de "Processes" (Processus) :

Remarque : les processus sont le composant de production permettant de coordonner le flux de données.

12.    Configurez le processus métier avec les valeurs suivantes :
●    Classe du processus métier: EnsLib.MsgRouter.RoutingEngine
●    Nom de règle de routage : customsda.PatientRouterRule2
●    Nom du processus métier: PatientProcess
●    Cochez Enable Now

13.    Jusqu'à présent, nous avons créé tous les composants :

14.    Sélectionnez PatientProcess et allez à l'onglet "Settings" > icône de loupe près de "Business Rule Name" (Nom de règle de routage) :

15.    Maintenant, nous allons configurer la règle de routage "Routing Rule" dans l'éditeur de règles "Rule Editor" :

16.    Faites un double-clic sur le composant de contrainte et configurez la Source avec PatientCSVService et la Classe de message avec customsda.Patients2RecordMap.Record :

17.    Actuellement, nous avons configuré la source et la cible :

18.    Sélectionnez le composant de règle et cliquez sur le bouton "Green Plus" :

19.    Sélectionnez Send (Envoyer) pour créer le composant When (Quand) :

20.    Sélectionnez le composant "When", cliquez sur le bouton "Green Plus", puis sélectionnez le composant "Send" :

21.    Vous êtes censé voir l'image affichée ci-dessous sur votre écran :

22.    Faites un double-clic sur le composant cible et définissez les éléments de configuration sur PatientSDAOperation, puis cliquez sur le bouton OK :

23.    Faites un double-clic sur le composant de transformation et définissez "Transforms" sur customsda.PatientDTL2, puis cliquez sur OK :

24.    Maintenant, vous avez vos définitions de règles prêtes :

25.    Cliquez sur le bouton "Save" et allez au menu "Interoperability" :

26.    Allez à Interoperability > List > Productions :

27.    Sélectionnez Patients2Production et cliquez sur le bouton "Open" :

28.    Nous allons lancer notre nouvelle production ! Cliquez sur le bouton "Start" :

29.    Sélectionnez PatentProcess et allez à l'onglet "Messages" pour voir les résultats (messages) :

30.    Cliquez sur un message pour voir un diagramme de séquence "Sequence Diagram" avec les résultats de la transformation :

Comme vous pouvez le constater, il s'agit d'un processus de glisser-déposer facile et entièrement visuel, qui transforme les messages personnalisés en SDA ou en d'autres formats. Pour en savoir plus, consultez les liens ci-dessous :

1.    Création d'intégrations FHIR de base avec InterSystems IRIS for Health : https://learning.intersystems.com/course/view.php?id=1959&ssoPass=1
2.    Découvrez HealthShare pour les développeurs et les intégrateurs de systèmes : https://learning.intersystems.com/course/view.php?id=26&ssoPass=1
3.    Création d'intégrations métier avec InterSystems IRIS https://learning.intersystems.com/course/view.php?id=1437&ssoPass=1
4.    Création d'intégrations de base HL7 avec InterSystems: https://learning.intersystems.com/course/view.php?id=1350&ssoPass=1

0
0 138
Article Sylvain Guilbaud · Mars 18, 2022 6m read

Dans cet article, je vais vous montrer comment vous pouvez facilement conteneuriser les passerelles .Net/Java.

Pour notre exemple, nous allons développer une intégration avec Apache Kafka.

Et pour interopérer avec le code Java/.Net, nous utiliserons PEX.

Architecture

Notre solution fonctionnera entièrement dans docker et ressemblera à ceci :

Passerelle Java

Tout d'abord, nous allons développer l'opération Java pour envoyer des messages dans Kafka. Le code peut être écrit dans l'IDE de votre choix et il peut ressembler à ceci.

En bref :

  • Pour développer une nouvelle opération commerciale PEX, nous devons implémenter la classe abstraite com.intersystems.enslib.pex.BusinessOperation
  • Les propriétés publiques sont les paramètres de l'hôte de l'entreprise
  • La méthode OnInit est utilisée pour initier la connexion à Kafka et obtenir un pointeur vers InterSystems IRIS
  • OnTearDown est utilisé pour se déconnecter de Kafka (à l'arrêt du processus)
  • OnMessage reçoit le message dc.KafkaRequest et l'envoie à Kafka

Maintenant, plaçons-le dans Docker !

Voici notre dockerfile :

FROM openjdk:8 AS builder

ARG APP_HOME=/tmp/app

COPY src $APP_HOME/src

COPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/

WORKDIR $APP_HOME/jar/
ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .
ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .

WORKDIR $APP_HOME/src

RUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java && \
    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.class

FROM intersystemscommunity/jgw:latest

COPY --from=builder /tmp/app/jar/*.jar $GWDIR/

Allons-y ligne par ligne et voyons ce qui se passe ici (je suppose que vous connaissez les constructions docker à plusieurs niveaux) :

FROM openjdk:8 AS builder

Notre image de départ est JDK 8.

ARG APP_HOME=/tmp/app
COPY src $APP_HOME/src

Nous copions nos sources du dossier /src dans le dossier /tmp/app.

COPY --from=intersystemscommunity/jgw:latest /jgw/*.jar $APP_HOME/jgw/

Nous copions les sources de la passerelle Java dans le dossier /tmp/app/jgw.

WORKDIR $APP_HOME/jar/
ADD https://repo1.maven.org/maven2/org/apache/kafka/kafka-clients/2.5.0/kafka-clients-2.5.0.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar .
ADD https://repo1.maven.org/maven2/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar .
ADD https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar .

WORKDIR $APP_HOME/src

RUN javac -classpath $APP_HOME/jar/*:$APP_HOME/jgw/* dc/rmq/KafkaOperation.java && \
    jar -cvf $APP_HOME/jar/KafkaOperation.jar dc/rmq/KafkaOperation.class

Maintenant toutes les dépendances sont ajoutées et javac/jar est appelé pour compiler le fichier jar. Pour un projet concret, il est préférable d'utiliser maven ou gradle.

FROM intersystemscommunity/jgw:latest

COPY --from=builder /tmp/app/jar/*.jar $GWDIR/

Et enfin, les jars sont copiés dans l'image de base jgw (l'image de base se charge également du démarrage de la passerelle et des tâches connexes).

Passerelle .Net

Vient ensuite le service .Net qui recevra les messages de Kafka. Le code peut être écrit dans l'IDE de votre choix et il peut ressembler à ceci.

En bref :

  • Pour développer un nouveau service d'entreprise PEX nous devons implémenter la classe abstraite InterSystems.EnsLib.PEX.BusinessService
  • Les propriétés publiques sont les paramètres de l'hôte de l'entreprise
  • La méthode OnInit est utilisée pour établir une connexion avec Kafka, s'abonner à des sujets et obtenir un pointeur vers InterSystems IRIS
  • OnTearDown est utilisé pour se déconnecter de Kafka (à l'arrêt du processus)
  • OnMessage consomme les messages de Kafka et envoie des messages Ens.StringContainer aux autres hôtes d'Interoperability

Maintenant, plaçons-le dans Docker !

Voici notre dockerfile :

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build

ENV ISC_PACKAGE_INSTALLDIR /usr/irissys
ENV GWLIBDIR lib
ENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21

WORKDIR /source
COPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/

# copier csproj et restaurer en tant que couches distinctes
COPY *.csproj ./
RUN dotnet restore

# copier et publier l'application et les bibliothèques
COPY . .
RUN dotnet publish -c release -o /app

# étape/image finale
FROM mcr.microsoft.com/dotnet/core/runtime:2.1
WORKDIR /app
COPY --from=build /app ./

# Configurations pour démarrer le serveur passerelle
RUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json && \
    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.json

ENV PORT 55556

CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Allons-y ligne par ligne :

FROM mcr.microsoft.com/dotnet/core/sdk:2.1 AS build

Nous utilisons le SDK complet .Net Core 2.1 pour construire notre application.

ENV ISC_PACKAGE_INSTALLDIR /usr/irissys
ENV GWLIBDIR lib
ENV ISC_LIBDIR ${ISC_PACKAGE_INSTALLDIR}/dev/dotnet/bin/Core21

WORKDIR /source
COPY --from=store/intersystems/iris-community:2020.2.0.211.0 $ISC_LIBDIR/*.nupkg $GWLIBDIR/

Copiez .Net Gateway NuGets de l'image Docker officielle d'InterSystems dans notre image de constructeur

# copier csproj et restaurer comme couches distinctes
COPY *.csproj ./
RUN dotnet restore

# copier et publier l'application et les bibliothèques
COPY . .
RUN dotnet publish -c release -o /app

Construisez notre bibliothèque.

# étape/image finale
FROM mcr.microsoft.com/dotnet/core/runtime:2.1
WORKDIR /app
COPY --from=build /app ./

Copiez les dll de la bibliothèque dans le conteneur final que nous exécuterons réellement.

# Configurats pour démarrer le serveur Gateway
RUN cp KafkaConsumer.runtimeconfig.json IRISGatewayCore21.runtimeconfig.json && \
    cp KafkaConsumer.deps.json IRISGatewayCore21.deps.json

Actuellement, la passerelle .Net doit charger toutes les dépendances au démarrage, nous lui faisons donc connaître toutes les dépendances possibles.

ENV PORT 55556

CMD dotnet IRISGatewayCore21.dll $PORT 0.0.0.0

Démarrez la passerelle sur le port 55556 en écoutant sur toutes les interfaces.

Et voilà, c'est tout !

Voici un docker-compose complet pour que tout fonctionne (y compris Kafka et Kafka UI pour voir les messages).

Pour exécuter la démo, vous dever :

  1. Installer :
  2. Exécuter :
git clone https://github.com/intersystems-community/pex-demo.git cd pex-demo docker-compose pull docker-compose up -d

 

Avis important : Les bibliothèques Java Gateway et .Net Gateway DOIVENT provenir de la même version que le client InterSystems IRIS.

0
0 229