查找

記事
· 2025年9月26日 3m read

HealthCare, prenez soin de votre santé !

Allons-y… "Com saúde não se brinca." (portugais brésilien), "Better safe than sorry" (anglais), "Mieux vaut prévenir que guérir" (français), "Todo tiene solución, menos la muerte." (espagnol), ce dernier terme est lourd de sens, n'est-ce pas ?

Que nous disent ces dictons (et nous pourrions en avoir d'innombrables autres dans encore plus de langues) ? L'importance de prendre soin de sa santé. Et ce n'est pas nouveau ; peut-être que les outils évoluent, ou s'améliorent simplement, au fil des ans. Une chose est sûre : l'idée de prendre soin de soi et ainsi de prévenir certaines maladies existe depuis longtemps, et nous l'avons certainement entendue de la bouche de nos arrière-grands-parents, grands-parents, parents… Et maintenant, nous la transmettons à nos enfants. Certes, dans bien des cas, il ne s'agit que de connaissances empiriques.

Dans les proportions nécessaires, cet apprentissage ou ces formes de soins forment un ensemble d'informations que nous pouvons appeler HealthCare (terme anglais). Le traitement des maladies, les soins préventifs et palliatifs, ainsi que les diagnostics, constituent un puissant système de soutien pour tous ceux qui ont accès à ces informations.

Une fois que nous saurons comment procéder, quelle est la place d'InterSystems dans ce contexte ? Grâce à son expertise et à son infrastructure enviables, InterSystems peut fédérer et diffuser ces données de manière unique, facilitant ainsi l'accès à l'information et la prise de décision (un point crucial pour la réussite).

Avec des utilisateurs dans le monde entier, dont beaucoup sont des leaders d'opinion et des leaders du secteur (de la santé, et même des soins axés sur les technologies de l'information), InterSystems peut fournir des données extrêmement précises et sécurisées. Voici quelques données historiques pour illustrer cela : "À l'échelle mondiale, plus d'un milliard de dossiers médicaux sont gérés par des solutions basées sur la technologie InterSystems." – Source : site web d'InterSystems.

Mais comment InterSystems parvient-il à servir "tout le monde" ? Avec la prise en charge de FHIR (Fast Healthcare Interoperability Resource, utilisée pour l'échange d'informations), HL7 V2 (Health Level Seven, langage standard pour le partage et l'intégration des informations de santé électroniques) et IHE (Integrating the Healthcare Enterprise, initiative internationale visant à améliorer l'interopérabilité des systèmes de santé, grâce à la collaboration entre professionnels de santé et fabricants de technologies pour définir et utiliser des normes de communication, ainsi que d'autres protocoles et formats de messagerie d'information médicale mondiaux, permettant l'intégration et l'interopérabilité de toutes ces applications de santé).

Grâce à ses capacités étendues de gestion de toutes ces données et informations, il s'impose de manière sûre et efficace dans ce monde si important pour le développement humain.

                                                                                                   

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
記事
· 2025年9月26日 19m read

A Configurable Template for Automated Splitting of HL7 Repeating Segments

Introduction

HL7 messages often contain multiple repeating segments such as NTE, AL1, OBX, ZTX, DG1, and others. These segments sometimes require individual processing and routing to different downstream systems. This technical paper introduces a configurable template designed to automate the splitting of these repeating HL7 segments, improving message handling and integration efficiency.

This template offers flexibility by allowing users to either send the message as-is with the required logic applied or split the message based on customizable settings defined within the business process configuration. Importantly, this approach supports reuse across various use cases and HL7 schemas.

Please note that you may need to update or modify a few lines of code depending on your HL7 schema or any custom schema you use. The source message must conform to your message schema.

Key configurable parameters include:

  • Message type selection
  • Identification of repeating segments
  • Segment group definitions
  • Unique identifier settings

Highlights:

  • The source message can contain multiple repeating segments such as NTE, AL1, OBX, etc.
  • You can send the entire message without splitting it, applying only your logic, to downstream systems.
  • Alternatively, you can split the message according to your instructions and send the segments separately to one or multiple downstream systems as needed.

This process is designed to split any repeating HL7 message segment and route it to the chosen downstream systems.  It functions as a reusable template, requiring little to no code modification to adapt to your specific needs.

The process provides dynamic configuration settings within the business process setup, allowing users to customize message splitting as needed. The main settings are detailed in Table 1 below:

Table 1: Process Settings – Configurations

SN

Setting Name

Short Description

Example

1.

MessageType 

HL7 message type used for splitting.

ADT^A01, ORU^01, DFT^P03 etc

2.

MultipleSegmentName

Repeating segment(s) in the HL7 message to be split individually. Specify the segment name that needs to be sent individually

NTE, OBX, ZTX, RXP, etc.

3.

IsRepeating

Confirms if the segment in #2 repeats. Only "Y" is currently supported (default is "Y").

Y or N

4.

SegmentRepeatingGrp

Indicates if the segment is part of a group (e.g., RXCgrp()). Nested groups like PIDgrp().NTE() are not supported here.

RXCgrp(), PIDgrp(),

 

5.

MultipleSegGrpPath

If #4 is "Y", define the path to the group. Can be selected from a list

Note: You can edit or update this according to your requirements by changing the code.
Be careful, it depends on your message schema. If you have a custom schema, please update it accordingly.

PIDgrp() → NTE(), PIDgrp() → OBX()
Select DropDownList: PIDgrp, MRGgrp, PRG1grp, IN1grp, ORCgrp(1).OBRuniongrp, PIDgrpgrp(1).ORCgrp(1).OBXgrp

6.

SegmentNameForUniqueID

Segment name to be used for generating a unique message identifier.

MHS or FT1grp

7.

SegmentDetails

Full segment path for inserting the value. Use the segment name if it's the same.

MSH,

FT1grp(1).FT1

8.

SegmentFieldNo

Field number where the unique value should be inserted. Use the instance number.

 

10 = (MSH:10 – Control id)

 2 = ( FT1grp(1).FT1.2 – Financial transaction id) 

9.

SeperationUniqueID 

Separator for the unique value (e.g., ".", "#", "-"). Appends a unique number to the field value.

., #, -, etc

For example, such as XXXXX.1, XXXXX.2, etc.

 

Usage Instructions

  1. Copy the provided code and create a new business process in the HealthConnect Management Portal using the code class.
  2. Navigate to Process → Settings, then select TargetConfigurations.

  1. Choose the operations and target systems where you want to send the message.
  2. Configure all settings as specified in the table above according to your requirements and apply the changes.
  3. Restart your process to activate the new configuration.

TESTING:

Screenshot 1: Management Portal showing sending the entire source HL7 message and sending repeating segments to three different systems.

Select à Process à Setting and complete all the configuration according to your requirements.

Screenshot 2.a: Process settings configuration screen.

If you need more information about any configuration, simply click the title for detailed explanations.

Screenshot 2.b: Supporting information screen.

Examples of Source and Target Messages:

Screenshot 3: Source message containing 5 repeating ZTX segments.

Screenshot 4.a: Target system configuration sending the source message as-is and splitting the ZTX segments individually to operations.

Screenshot 4.b: Target system sending the source message as-is and splitting only the last ZTX segment to operations.

Another example where only split messages are sent, without sending the original full message first:

Screenshot 5: Source message with two ZTX segments split into two separate messages, each containing one ZTX segment.

Screenshot 6: Source Message

Screenshot 7.a: Target message containing the first ZTX  segment.

Screenshot 7.b: Target message containing the second ZTX segment.

Additional Example with Different Configurations

In this example, the source message contains multiple repeating NTE segments, where each NTE segment is part of the group path: PIDgrpgrp().ORCgrp().OBXgrp.

The process is configured to split and send each NTE segment individually to the target system. The resulting target messages are shown below:

  • Screenshot 10.a – First NTE segment
  • Screenshot 10.b – Second NTE segment
  • Screenshot 10.c– Third NTE segment

Screenshot 8: Process Configurations

Screenshot 9: Source message

Screenshot 10.a: Target message – First NTE segment

Screenshot 10.b: Target message – Second NTE segment

Screenshot 10.c: Target message – Third NTE segment

CODE

/// This process reads the HL7 messages from the source, splits the messages according 
/// to the given segment, and forwards them to the specified router/system. 
/// For example, if the message has multiple OBX, NTE, or ZTX segments,each message will 
/// include only one of the given segments. Additionally, if required, it will make the 
/// message unique by adding the serial number to the given segment's field.
/// <br>
///  This is a template that can be reused without modification in the code or with minimal modification based on the requirements. 
/// <br/> 
/// <br><b>Author: Sanjib Pandey</b>
/// Version:1.0
/// Date : 25/09/2025
/// <br>
Class SANJIB.Process.SegmentSplitAndSendMessage Extends Ens.BusinessProcess [ ClassType = persistent ]
{

/// The descriptions of the datasets:
/// <ul>
/// <li>Message Type - Type of HL7 Message </li>
/// <li>Multiple Segment Names - Segment Name. </li>
/// <li>Is Repeating - Defines whether the segment given above is repeating or not.</li>
/// <li>Segment Repeating Group - Defines whether the segment is part of a repeating group or not, e.g., RXCgrp(), and not part of multiple or nested groups.</li>
/// <li>Multiple Segment Group Path - define and give the path if segment is multiple group eg PIDgrp->OBX()</li>
/// <li>Segment Name for Unique ID - Specifies the segment for the message's unique ID eg. MSH, FT1.</li>
/// <li>Segment Details - Specifies the segment path details with the field name for the message's unique ID, e.g., MSH, FT1grp(1).FT1.</li>
/// <li>Segment Field No - Specifies the field name where the unique ID should be placed.</li>
/// </ul>
/// Provide the message type, e.g., ADT^A01, DFT^P03.
Property MessageType As %String(MAXLEN = 10);
/// Specify the segment name that needs to be sent individually (e.g., ZTX, RXP, etc.)
Property MultipleSegmentName As %String(MAXLEN = 20);
/// Are the selected multiple segments (MulSegmentName) repeating or not? [Y, N], e.g., OBX(), ZTX(), NTE(). 
/// Note: Currently, only the "Y" part is implemented. By default, it is set to "Y".
Property IsRepeating As %String(VALUELIST = ",Y,N") [ InitialExpression = "Y", Required ];
/// Define if this segment is part of any other group [Y, N]. It should be something like "RXCgrp()",
/// but not part of multiple or nested groups, e.g., PIDgrp()-NTE().
Property SegmentRepeatingGrp As %String(VALUELIST = ",Y,N") [ InitialExpression = "N", Required ];
/// Define and provide the path if a segment has come from nested groups.</li>
/// Define the segment path if a repeating segment consists of another repeating group segment, 
/// e.g., PIDgrp() → NTE(), PIDgrp() → OBX(), PIDgrp() → AL1(). 
/// Currently, this might be suitable for Merge or Move Message e.g. A38, A40, A41, A42, A45 etc and other types of message. 
/// PIDgrpgrp(1)->ORCgrp(1)->OBXgrp(1), ORCgrp(1).OBRuniongrp.NTE() 
/// <br>
/// Note: The following is included but some of them has not been tested yet; it will be included in the new version (working on):
/// <ul>
/// <li>PIDgrpgrp(1)->ORCgrp(1)->OBXgrp(1)->any repeating group segment</li>
/// <li>ORCgrp(1).OBRuniongrp.any repeating group segment </li>
/// <li>RGSgrp(1).AISgrp(1).any repeating group segment?</li>
/// <li>RGSgrp(1).AIGgrp(1).any repeating group segment?</li>
/// <li>RGSgrp(1).AILgrp(1).any repeating group segment?</li>
/// </ul>
/// Multiple Segment Group Path - define and give the path if segment is multiple group eg PIDgrp->OBX()
/// PIDgrp(1).ORCgrp(1).OBXgrp(1).NTE() = PIDgrp(1).ORCgrp(1).OBXgrp (the last segment doesn't need to be mentioned (1)).
Property MultipleSegGrpPath As %String(VALUELIST = ",NA,PIDgrp,MRGgrp,PRG1grp,IN1grp,ORCgrp(1).OBRuniongrp,PIDgrpgrp(1).ORCgrp(1).OBXgrp,The following requires more testing — do not select or use in production!,ORCgrp(1).OBRuniongrp.OBXgrp,RGSgrp(1).AILgrp,RGSgrp(1).AIGgrp,RGSgrp(1).AISgrp") [ InitialExpression = "NA", Required ];
/// To make the message unique, please specify which segment and field you want to use, e.g., MHS or FT1.
Property SegmentNameForUniqueID As %String(MAXLEN = 5);
/// Provide the full segment path to insert the value. If it is the same as the segment name, use the same name here, e.g., MSH, FT1grp(1).FT1.
Property SegmentDetails As %String(MAXLEN = 50);
/// Which field do you want to put the unique number in? For example, MSH:10 (control ID) 
/// or FT1.2 (financial transaction ID). You are required to specify the field 
/// instance number. If you want to use the existing MSH control ID field, enter 10.
Property SegmentFieldNo As %String(MAXLEN = 2);
/// Separation of the unique value: For example, if you want to use MSH:10, 
/// the existing value of MSH:10 will be used, and an additional unique number will be 
/// automatically added by the program, such as XXXXX.1, XXXXX.2, etc.
Property SeperationUniqueID As %String(MAXLEN = 2);
/// Target Configuration Name
Property TargetConfigurations As Ens.DataType.ConfigName(MAXLEN = 100) [ Required ];
Parameter SETTINGS = "MessageType,MultipleSegmentName,IsRepeating,SegmentRepeatingGrp,MultipleSegGrpPath,SegmentNameForUniqueID,SegmentDetails,SegmentFieldNo,SeperationUniqueID,TargetConfigurations:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}";
Method OnRequest(pRequest As EnsLib.HL7.Message, Output pResponse As Ens.Response) As %Status
{
 /* ----------------------------------------------------------------------------------------------
   Note: You can access user-defined data directly from the management portal settings. However, 
   for business requirements, you may need to manipulate or further check the data. In such cases,
   it’s better to assign the data to a local variable. For example, while you can select multiple 
   target configurations in the portal, some business logic may require sending the message to a 
   specific operation, router, or process. All of this can be controlled here.
   ...............................................................................................	*/
   S tSC = $$$OK
   //Let's clear the memory variable and get all user-defined datasets
   S (msgType,mulSeg,isRepeat,segRepGrp,mulSegGrpPath,segNameUID,segFieldNo,sepUID,tgtConfig)=""
   S msgType =..MessageType
   S mulSeg=..MultipleSegmentName
   S isRepeat=..IsRepeating
   S segRepGrp=..SegmentRepeatingGrp
   S mulSegGrpPath=..MultipleSegGrpPath
   S segNameUID=..SegmentNameForUniqueID
   S segDetail=..SegmentDetails
   S segFieldNo=..SegmentFieldNo
   S sepUID=..SeperationUniqueID
   S tgtConfig=..TargetConfigurations
   K messageType S messageType=pRequest.GetValueAt("MSH:9")
   K cpyMsg S cpyMsg = pRequest.%New()
   S cpyMsg =pRequest.%ConstructClone()
   S (segIdx,totSegment)=0
   Try
   { 
      #dim totSegment=pRequest.SegCount
      F i=1:1:totSegment
      {
         K segName S segName = pRequest.GetSegmentAt(i).Name
         I (segName = mulSeg)
          {
            S segIdx=i
            /*-----------------------------------------------------------------------------------
              Note:
               The following line of code is only required if you want to send the original message to another downstream system or perform interface transformation. Otherwise, it is not needed and can be deleted or commented out:
               [S tSC=..ProcessMessage(pRequest,tgtConfig)]
               This line is included here for testing purposes only.. 
            ------------------------------------------------------------------------------------*/
            //S tSC=..ProcessMessage(pRequest,tgtConfig) 
            Q
         }
      }
      //checking necessry datasets
      I ((msgType="" ) || (mulSeg="")||(isRepeat="")|| (segRepGrp=""))
      {
         S tSC=$system.Status.Error(5001,"Empty fields. Please check the MessageType, MultipleSegmentName, IsRepeating, SegmentRepeatingGrp,multipleSegGrpPath in the Process Settings at the Management Portal.")
      }
      elseif messageType=msgType
      {
         I ((isRepeat= "Y") && (segRepGrp="N")&& (mulSegGrpPath="NA") && (segIdx >0))  
         {	
            S tSC=..ProcessMessageFromNoRepeatGroup(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
         }
         elseif ((isRepeat= "Y") && (segRepGrp="Y") && (mulSegGrpPath="NA") && (segIdx >0))
         {
            S tSC=..ProcessMessageFromRepeatingGroup(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
         }
         elseif ((isRepeat = "Y") && (mulSegGrpPath '="") && (segIdx >0))
         {
            S tSC=..ProcessMessageFromRepeatingGroupPath(cpyMsg,msgType,mulSeg,isRepeat,segRepGrp,mulSegGrpPath,segNameUID,segDetail,segFieldNo,sepUID,segIdx,tgtConfig)
         }
         else
         {
            S tSC=..ProcessMessage(pRequest,tgtConfig)	
         }
      }
   }
   catch ErException
   {
      S tSC=ErException.AsStatus()
      $$$TRACE("Exception :"_tSC)	
   }
   Q tSC
}

/// The following method splits, processes, and sends the message only from the given repeating segment.
Method ProcessMessageFromNoRepeatGroup(ByRef mRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
   S tSC = $$$OK
   Try
   {
      K totSeg 
      #dim totSeg = mRequest.SegCount
      //prepare dataset
      S dataSets=..GetDataSets(mRequest,segNameUID,segDetail,segFieldNo)
      K flag S msgUID=$P(dataSets,":",1)
      K flag S flag=$P(dataSets,":",2)
      I sepUID="" S sepUID="-"
      K totRepSeg S totRepSeg=mRequest.GetValueAt(mulSeg_"(*)")
      I (totRepSeg >1)
      {
         F j=1:1:totRepSeg
         {
            K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
            S newMsg.DocType=mRequest.DocType
            I ((segNameUID="MSH") && (flag="G"))
            {
               D newMsg.SetSegmentAt(mRequest.FindSegment("MSH"),1)
               D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
            }
            else
            {
               D newMsg.SetSegmentAt(mRequest.FindSegment("MSH"),1)
            }
            F k=1:1:totSeg
            {
               K segName S segName = mRequest.GetSegmentAt(k).Name
               I (segName '= "MSH") && (segName '= mulSeg) 
               {
                  K getSegment S getSegment=mRequest.FindSegment(segName,,.sc)
                  I $ISOBJECT(getSegment)
                  {
                     I ((segName=segNameUID) && (flag ="G"))
                        {
                           D newMsg.AppendSegment(getSegment)
                           D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo) 
                        }
                        else
                        {
                           D newMsg.AppendSegment(getSegment)
                        }
                  }
                  else
                  {
                     s tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
                  }
               }
            }
            S addSeg=mRequest.GetSegmentAt(mulSeg_"("_j_")")
            D newMsg.InsertSegmentAt(addSeg,segIdx)
            $$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))
            K addSeg
         }
      }
      else
      {
         S tSC=..ProcessMessage(mRequest,tgtConfig)
      }
   }
   catch ErException
   {
      S tSC=ErException.AsStatus()
      $$$TRACE("Exception :"_tSC)	
   }
   Q tSC
}

/// The following method splits, processes, and sends the message only from the given repeating group segment.
Method ProcessMessageFromRepeatingGroup(ByRef pRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
   S tSC = $$$OK
   Try
   {
      K totSeg 
      #dim totSeg = pRequest.SegCount
      S dataSets=..GetDataSets(pRequest,segNameUID,segDetail,segFieldNo)
      K flag S msgUID=$P(dataSets,":",1)
      K flag S flag=$P(dataSets,":",2)
      I sepUID="" S sepUID="-"
      K totRepSeg S totRepSeg=pRequest.GetValueAt(mulSeg_"grp(*)")
      I (totRepSeg >1)
      {
         F j=1:1:totRepSeg
         {
            K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
            S newMsg.DocType=pRequest.DocType
            I ((segNameUID="MSH") && (flag="G"))
            {
               D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
               D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
            }
            else
            {
               D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
            }
            F k=1:1:totSeg
            {
               K segName S segName = pRequest.GetSegmentAt(k).Name
               I (segName '= "MSH") && (segName '= mulSeg) 
               {
                  K getSegment S getSegment=pRequest.FindSegment(segName,,.sc)
                  I $ISOBJECT(getSegment)
                  {
                     I ((segName=segNameUID) && (flag ="G"))
                        {
                           D newMsg.AppendSegment(getSegment)
                           D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo) 
                        }
                        else
                        {
                           D newMsg.AppendSegment(getSegment)
                        }
                  }
                  else
                  {
                     S tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
                  }
               }
            }
            S addSeg=pRequest.GetSegmentAt(mulSeg_"grp("_j_")."_mulSeg)
            D newMsg.InsertSegmentAt(addSeg,segIdx)
            $$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))	
            K addSeg 
         }
      }
      else
      {
         S tSC=..ProcessMessage(pRequest,tgtConfig)	
      }
   }
   catch ErException
   {
      S tSC=ErException.AsStatus()
      $$$TRACE("Exception :"_tSC)	
   }
   Q tSC
}

/// The following method splits, processes, and sends the message only from the specified group segments, e.g., PIDgrp().NTE(), ORCgrp(1).OBRuniongrp.NTE(), etc.
Method ProcessMessageFromRepeatingGroupPath(ByRef pRequest As EnsLib.HL7.Message, msgType As %String, mulSeg As %String, isRepeat As %String, segRepGrp As %String, mulSegGrpPath As %String, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, sepUID As %String, segIdx As %Integer, tgtConfig As %String) As %Status
{
   // This method is not fully completed yet, it's still a work in progress. 
   // But whatever existing features are here are working.
   S tSC = $$$OK
   Try
   {   
      K totSeg 
      #dim totSeg = pRequest.SegCount
      S dataSets=..GetDataSets(pRequest,segNameUID,segDetail,segFieldNo)
      K flag S msgUID=$P(dataSets,":",1)
      K flag S flag=$P(dataSets,":",2)
      I sepUID="" S sepUID="-"
      S mulSegPathType=0
      I mulSegGrpPath="ORCgrp(1).OBRuniongrp"
      {
         S totRepSeg=pRequest.GetValueAt(mulSegGrpPath_"."_mulSeg_"(*)")
         S mulSegPathType=1
      }
      else
      {
         S totRepSeg=pRequest.GetValueAt(mulSegGrpPath_"(1)."_mulSeg_"(*)")
         S mulSegPathType=2
      }
      I (totRepSeg >1)
      {
         F j=1:1:totRepSeg
         {
            K newMsg S newMsg=##class(EnsLib.HL7.Message).%New()
            S newMsg.DocType=pRequest.DocType
            I ((segNameUID="MSH") && (flag="G"))
            {
               D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
               D newMsg.SetValueAt(msgUID_sepUID_j,"MSH:"_segFieldNo)
            }
            else
            {
               D newMsg.SetSegmentAt(pRequest.FindSegment("MSH"),1)
            }
            F k=1:1:totSeg
            {
               K segName S segName = pRequest.GetSegmentAt(k).Name
               I (segName '= "MSH") && (segName '= mulSeg) 
               {
                  K getSegment S getSegment=pRequest.FindSegment(segName,,.sc)
                  I $ISOBJECT(getSegment)
                  {
                     I ((segName=segNameUID) && (flag ="G"))
                     {
                        D newMsg.AppendSegment(getSegment)
                        D newMsg.SetValueAt(msgUID_sepUID_j,segDetail_":"_segFieldNo) 
                     }
                     else
                     {
                        D newMsg.AppendSegment(getSegment)
                     }
                  }
                  else
                  {
                     s tSC=$system.Status.Error(5001,"Not found "_segName_"Segment")
                  }
               }
            }
            I mulSegPathType=1
            {
               S addSeg=pRequest.GetSegmentAt(mulSegGrpPath_"."_mulSeg_"("_j_")")
            }
            I mulSegPathType=2
            {
               S addSeg=pRequest.GetSegmentAt(mulSegGrpPath_"(1)."_mulSeg_"("_j_")")
            }
            D newMsg.InsertSegmentAt(addSeg,segIdx)
            $$$QuitOnError(..ProcessMessage(newMsg,tgtConfig))	
            K addSeg
         }
      }
      else
      {
         S tSC=..ProcessMessage(pRequest,tgtConfig)		
      }
   }
   catch ErException
   {
      S tSC=ErException.AsStatus()
      $$$TRACE("Exception :"_tSC)	
   }
   Q tSC
}

/// The following method retrieves the existing source message ID from the given location to create a new unique message ID for the new message (repeating segment).
Method GetDataSets(ByRef pRequest As EnsLib.HL7.Message, segNameUID As %String, segDetail As %String, segFieldNo As %Integer, pOutput As %Integer = 0, flag As %String = "R") As %Status
{
   S tSC=$$$OK
   Try
   {
      I $ISOBJECT(pRequest)
      {
         K msgUID
         I (segNameUID '="" && segDetail '= "" && segFieldNo '="")
         {
            S msgUID = pRequest.GetValueAt(segDetail_":"_segFieldNo)
            I (msgUID =""){S msgUID=0}
            S flag="G"
            S pOutput=msgUID
         }
      }
      else
      {
         S tSC=$system.Status.Error(5001,"Not a valid HL7 message!")
      }
   }
   catch Exception
   {
      S tSC=Exception.AsStatus()
   }
   Q pOutput_":"_flag
}

/// The following sends or processes the message to the selected target names in the management portal.
Method ProcessMessage(pMessage As EnsLib.HL7.Message, TargetList As %String) As %Status
{
   S tSC=$$$OK
   Try
   {
      I $ISOBJECT(pMessage)
      {
         For i=1:1:$L(TargetList,",") 
         {
            S tSC=..SendRequestAsync($P(TargetList,",",i),pMessage,0)	
         }
      }
   }
   catch ErException
   {
      S tSC=ErException.AsStatus()
   }
   Q tSC
}

Method OnResponse(request As Ens.Request, ByRef response As Ens.Response, callrequest As Ens.Response, callresponse As Ens.Response, pCompletionKey As %String) As %Status
{
   Quit $$$OK
}

Storage Default
{
<Data name="SegmentSplitAndSendMessageDefaultData">
<Subscript>"SegmentSplitAndSendMessage"</Subscript>
<Value name="1">
<Value>MessageType</Value>
</Value>
<Value name="2">
<Value>MultipleSegmentName</Value>
</Value>
<Value name="3">
<Value>IsRepeating</Value>
</Value>
<Value name="4">
<Value>SegmentRepeatingGrp</Value>
</Value>
<Value name="5">
<Value>MultipleSegGrpPath</Value>
</Value>
<Value name="6">
<Value>SegmentNameForUniqueID</Value>
</Value>
<Value name="7">
<Value>SegmentDetails</Value>
</Value>
<Value name="8">
<Value>SegmentFieldNo</Value>
</Value>
<Value name="9">
<Value>SeperationUniqueID</Value>
</Value>
<Value name="10">
<Value>TargetConfigurations</Value>
</Value>
</Data>
<DefaultData>SegmentSplitAndSendMessageDefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}

}

 

You can also download the code from the GitHub  

I hope this template will help streamline your HL7 message processing. If you have any questions or need further assistance, please feel free to ask.

Thank you.

3 Comments
ディスカッション (3)2
続けるにはログインするか新規登録を行ってください
記事
· 2025年9月26日 3m read

SentinelIRIS

 

Building a Sensor Data Demo with Spring Boot and InterSystems IRIS

In the era of IoT and connected devices, sensor data is everywhere—tracking temperature in logistics, monitoring equipment performance, or recording environmental conditions. But capturing, storing, and analyzing this data in real time requires more than just hardware sensors.

It’s a practical example that combines Spring Boot, InterSystems IRIS, and a simple sensor simulation script to show how sensor data can flow from generation to storage and alerting.

Basically, the idea of ​​this project is to show all the integration capacity of IRIS with JAVA, where we can create a lightweight, high-performance and high-impact application.


What the Project Does

At its core, the repository demonstrates an end-to-end workflow:

  1. Sensor Simulation
    A Python script (simulate_sensor.py) generates realistic temperature, humidity, and location data. This removes the need for physical devices while testing the pipeline. (Not working very well yet!)
  2. Backend with Spring Boot
    A Java application built with Spring Boot receives sensor data through REST APIs, processes it, and persists it into the database.
  3. Database with InterSystems IRIS
    IRIS acts as the high-performance data store, with an initialization script (init.sql) to create tables and structure for historical analysis and alerting.

This setup mimics real-world use cases like cold-chain logistics, smart city monitoring, or industrial IoT systems.


Tech Stack Overview

Component Role
Spring Boot (Java 17) Core backend service for receiving and processing sensor data
Python Script Sensor simulator that pushes test data to the backend
InterSystems IRIS Database for storage and historical queries
Gradle + Docker Build and deployment tooling

Together, these pieces form a complete demo environment—simple to spin up locally, yet close enough to a production architecture to be a solid learning tool.


Why It’s Interesting

  • End-to-end clarity
    Many tutorials focus only on one layer. This repo shows the full pipeline, from data generation to storage.
  • Realistic scenario
    The simulated data includes environmental metrics often used in logistics and monitoring.
  • IRIS integration
    Instead of using a traditional database, the project leverages InterSystems IRIS, known for its performance and ability to handle time-series data.

Room for Improvement

While the project works well as a demo, there are opportunities to make it more robust:

  • API Documentation: Adding Swagger/OpenAPI would help developers understand and test endpoints easily.
  • Testing: Unit and integration tests could validate edge cases, such as data outside safe thresholds.
  • Security: Credentials are currently hardcoded. Introducing environment variables or a secrets manager would improve best practices.
  • Deployment: A Docker Compose setup would allow one-command startup of both the backend and IRIS.
  • Visualization: A simple dashboard could make the data and alerts more tangible for end users.

Real-World Applications

This kind of architecture can be applied across industries:

  • Logistics & Cold Chain – monitor goods in transit and alert if temperature deviates.
  • Smart Cities – track air quality, noise, or traffic patterns.
  • Industrial IoT – analyze machinery telemetry for predictive maintenance.
  • Healthcare – monitor environmental conditions in labs or hospitals.

The repo provides a blueprint: swap out the simulator for real sensors, enhance the backend logic, and you’re on the way to a production system.


Conclusion

The demo repository was coded by @Cecilia Valim and is more than just a toy project—it’s a practical showcase of how to handle sensor data with modern tools. By combining Spring Boot, Python, and InterSystems IRIS, it provides a clear, reproducible example of IoT data pipelines in action.

Whether you’re learning Spring Boot, exploring IRIS, or prototyping an IoT project, this repo is a great place to start.


👉 You can explore the full project here: github.com/cissavalim/demo

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
お知らせ
· 2025年9月26日

Édition d'automne : découvrez de nouvelles récompenses passionnantes sur Global Masters

Bonjour la communauté !

🍂 Les récompenses d'automne sont arrivées sur Global Masters !

Découvrez la saison avec nos nouvelles récompenses : c'est le moment idéal pour explorer la section Récompenses et obtenir vos récompenses préférées !

N'oubliez pas que la disponibilité des récompenses varie selon les régions. Vérifiez donc les offres disponibles dans votre région.

Ne manquez pas ces gourmandises de saison !

Vous n'êtes pas encore membre de Global Masters ? Inscrivez-vous ici avec vos identifiants SSO InterSystems.

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
お知らせ
· 2025年9月25日

★賞品決定★第3回 InterSystems Japan 技術文書ライティングコンテスト 開催!

開発者の皆さんこんにちは!

技術文書ライティングコンテストの開始(10月1日)まであと少しとなりました!💨

このお知らせでは、今年の賞品を発表いたします!

👀

🎁賞品情報🎁

審査員投票とコミュニティメンバーからの「いいね」の数の合計で順位を決定します。

1位~3位を受賞された方は、各順位に記載された賞品の中からお好きな1点をお選びいただけます。(1位の方は1~3位の賞品を、2位の方は2~3位の賞品をお選びいただけます)

🥇 1位

  • リカバリーウェア TENTIAL BAKUNEシリーズ
  • スマートリング Re・De Ring
  • アウトドアの体験ギフト EXCITING Gift Premium -エキサイティング プレミアム

🥈 2位

  • MYTREX DR. HEAT NECK
  • ソーダーストリーム TERRA スターターキット
  • アウトドアの体験ギフト EXCITING Gift -エキサイティング

🥉 3位

  • Bluetoothスピーカー
  • モレスキン クラシックダイアリー Large
  • スタバギフトカード(5000円)

 

🎁参加賞:投稿いただいた方全員に「モバイルバッテリー」をプレゼント!

 

1回目、2回目とは異なる賞品を!ということで賞品決めチームでいろいろ探してみました👀

高得点を狙う場合は、早めの投稿がおすすめです!(コミュニティメンバーからの「いいね」ポイントをより多くゲットできます!)

また、今年もボーナスポイントを設定しておりますので、対象の項目を含めていただくとポイント追加となります。

 

今年も沢山のご応募お待ちしております!💨

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください