Wednesday, 15 June 2011

Walkthrough using BizTalk HL7 Accelerator, Schemas and MLLP Adapter

This post will show you how to Setup BizTalk HL7 Projects, Create and use HL7 Schemas and basic configuration of MLLP Adapter.

Introduction:

  1. By this time you should be aware what HL7 Message is. If not, just Google it.


In BizTalk Server, there is a schema supporting each type of HL7 Message. These schemas can be found once you install BizTalk HL7 Accelerator. They will be available in the below path.

<drive>:\Program Files\Microsoft BizTalk 2010 Accelerator for HL7\Templates\Schemas\V2.X\2.5 or any 2.x



Each folder in the above has various HL7 Schemas required for your EAI Solution.

2. To test the HL7 Solution (from the developer aspect) a HL7 Simulator is required. For eg: 7Edit. Download a trail version of this software for testing the sample application in this post.

This Resultant HL7 Message from our EAI Solution will be sent to 7Edit via MLLP Adapter (MLLP is the protocol that is used to Transport HL7 Messages. It uses TCP/IP Internally).
7Edit in turn gives us an HL7 (not BizTalk) Acknowledgement (ACK) / Negative Acknowledgement (NACK).

3. The ACK / NACK given by this 7edit will be captured by the TwoWayAckReceivePort which is created automatically when you install BizTalk HL7 Accelerator.



Please note that this TwoWayAckReceivePort is must to receive that ACK/NACK. If this port is not present in your Admin Console, it means that you didn’t install HL7 Accelerator properly.
This TwoWayAckReceivePort has one receive loacation TwoWayAckReceiveLocation that is configured to use BTAHL72XReceivePipeline.

Sample Scenario:

1. In this Post we receive a HL7 ORU (Observation Result) Message of Version 2.5.

2. Convert that message to an ORU 2.4 Version message.

3. Add the necessary MSH Segment (Every HL7 Message should have a MSH Segment. This is like Header).

4. Send the resultant 2.4 version Message to 7Edit using MLLP Adapter.

5. 7Edit will give us ACK/NACK.

6. Capture the ACK Message.

Example:

1. The HL7 Schema that we are going to use here is ORU_R01_25_GLO_DEF.xsd & ORU_R01_24_GLO_DEF.xsd. Each HL7 Schema has 100s of nodes and each of these nodes is of a particular complex Type. These complex types are in turn available in three separate schemas datatypes_25.xsd, segments_25.xsd and tablevalues_25.xsd.

Normal Convention is to Create a separate project for these common schemas and reference the dll of these common schemas project in the actual project that uses the main schema ORU_R01_25_GLO_DEF.xsd

i) Create an Empty Solution.

ii) Add a Folder to it named Common (or anything else). Right click the Common and Select Add new Project.

iii) Select BTAHL7V25Common Project and Click Ok.



iv) Repeat and add BTAHL7V24Common Project.

Alternatively, you can create an Empty BizTalk Project and add the common schemas datatypes_25.xsd, segments_25.xsd and tablevalues_25.xsd by yourself. These schemas will be in the location  <drive>:\Program Files\Microsoft BizTalk 2010 Accelerator for HL7\Templates\Schemas\V2.X\2.5 or any 2.x

2. To the 2.5 Version of the common project, add the MSH schema MSH_25_GLO_DEF.xsd. You can find this one in <drive>:\Program Files\Microsoft BizTalk 2010 Accelerator for HL7\Templates\Schemas\V2.X\2.5 location.

This schema is required for HL7 Pipeline.

3. Add an Empty BizTalk Project to the main solution and add the ORU_R01_24_GLO_DEF.xsd and ORU_R01_25_GLO_DEF.xsd (<drive>:\Program Files\Microsoft BizTalk 2010 Accelerator for HL7\Templates\Schemas\V2.X\2.5\Observation Reporting\ORU) schemas. Resultant solution should be like below.

(Here the schemas come with Microsoft Namespace. Namespaces should be changed as required).



4. Add references of the two common Projects to the main project and try to build the solution. The solution should work fine.

5. Create a map between the V25 and V24 versions of the schemas.



6. Create an Empty Orchestration. Add a receive shape and a receive port to receive the messages of the type ORU_R01_25_GLO_DEF.xsd schema. Set the Activate to True.



7. Add a transform shape to the Orchestration and Use the map create in step 4 and convert the v25 Message to v24 Message (of type ORU_R01_24_GLO_DEF.xsd)



8. Now you have the 2.4 Version of ORU_R01_24_GLO_DEF Message.
This message as such is not sufficient to convert it into an HL7 Message. Every HL7 Message should have an MSH Segment.
Now we should convert the ORU_R01_24_GLO_DEF Message into a real HL7 Message.

9. Create a new Multipart Message with 3 Body Parts as below.

i) BodySegments -> MLLPAdapter.ORUSchemas.ORU_R01_24_GLO_DEF (Should be the Primary Body Part. Message Body Part Property should be set to True)
ii) MSHSegment -> BTAHL7Schemas.MSH_25_GLO_DEF (added in step 2)
iii) ZSegments -> System.String



Note that the names should be given in the same way as mentioned. Hl7 Pipeline will be using the same names.

10. Create a new message of the Multipart Type created in step 9 and add a Message Assignment shape to the Orchestration to Construct the MultiPart Message.



11. Add the following code in the Message Assignment Shape.

v24MultiPart.BodySegments = v24;
str = @"<MSH_25_GLO_DEF xmlns='http://microsoft.com/HealthCare/HL7/2X'>
    <MSH xmlns=''>
      <MSH.2_EncodingCharacters>^~\&amp;</MSH.2_EncodingCharacters>
      <MSH.3_SendingApplication>
        <HD.0_NamespaceId>MyHealthCareCompany</HD.0_NamespaceId>
      </MSH.3_SendingApplication>
      <MSH.4_SendingFacility>
        <HD.0_NamespaceId>MyHealthCareCompany</HD.0_NamespaceId>
        <HD.1_UniversalId>12</HD.1_UniversalId>
        <HD.2_UniversalIdType>L,M,N</HD.2_UniversalIdType>
      </MSH.4_SendingFacility>
      <MSH.5_ReceivingApplication>
        <HD.0_NamespaceId>MyReceivingCompany</HD.0_NamespaceId>
      </MSH.5_ReceivingApplication>
      <MSH.6_ReceivingFacility>
        <HD.0_NamespaceId>MyReceivingCompany</HD.0_NamespaceId>
      </MSH.6_ReceivingFacility>
      <MSH.7_DateTimeOfMessage>
        <TS.1>20110217203656+0530</TS.1>
      </MSH.7_DateTimeOfMessage>
      <MSH.9_MessageType>
        <CM_MSG.0_MessageType>ORU</CM_MSG.0_MessageType>
        <CM_MSG.1_TriggerEvent>R01</CM_MSG.1_TriggerEvent>
        <CM_MSG.2_MessageStructure>ORU_R01</CM_MSG.2_MessageStructure>
      </MSH.9_MessageType>
      <MSH.10_MessageControlId>19316405-a86e-4dfe-afff-542128d80bd6</MSH.10_MessageControlId>
      <MSH.11_ProcessingId>
        <PT.0_ProcessingId>P</PT.0_ProcessingId>
        <PT.1_ProcessingMode>A</PT.1_ProcessingMode>
      </MSH.11_ProcessingId>
      <MSH.12_VersionId>
        <VID_0_VersionId>2.4</VID_0_VersionId>
      </MSH.12_VersionId>
      <MSH.13_SequenceNumber>1</MSH.13_SequenceNumber>
      <MSH.15_AcceptAcknowledgmentType>AL</MSH.15_AcceptAcknowledgmentType>
      <MSH.21_ConformanceStatementId>
        <EI_0_EntityIdentifier>AA</EI_0_EntityIdentifier>
      </MSH.21_ConformanceStatementId>
    </MSH>
  </MSH_25_GLO_DEF>";

xmldoc.LoadXml(str);
v24MultiPart.MSHSegment = xmldoc;
v24MultiPart.ZSegments="";

v24MultiPart(BTAHL7Schemas.ParseError) = false;
v24MultiPart(BTAHL7Schemas.SegmentDelimiter2Char) = true;
v24MultiPart(BTAHL7Schemas.ZPartPresent) = false;
v24MultiPart(BTAHL7Schemas.MessageClass) = "MessageClass2X";
v24MultiPart(BTAHL7Schemas.MessageEncoding) = 65001;

Note:

i) v24MultiPart is the Multipart Message Constructed.
ii) Str and xmldoc are sting and XmlDocument Variables.
iii) V24 is message created out of the Map.
iv) A reference to the dll Microsoft.Solutions.BTAHL7.HL7Schemas (:\Program Files\Microsoft BizTalk 2010 Accelerator for HL7\Bin\) should be added.

12. Add a send shape to send the MultiPart message out.



13. Sign and Deploy the Application.

14. Create a new Receive Port and File Receive Location as shown below. Bind it to the Logical Receive Port of the Orchestration.



15. Create a new send port and set the Adapter to MLLP. Configure the Adapter as shown below. Bind this Send port to the Send port of the Orchestration.

Incase if you don’t find the Pipeline shown below in your project, it might have got installed in Default Application. You have to move it to your Application. This Pipeline is responsible for converting our Multipart message into a HL7 Message.



16. Configure 7Edit to receive the HL7 Message sent by the above step.
Open 7Edit, Click on Profiles -> Add Profiles and create a new profile as shown below.





17. Finally create a send port to receive the Acknowledgement Sent by the 7 Edit. This ACK / NACK will be received by the TwoWayAckReceivePort that is created automatically when you install BizTalk HL7 Accelerator.

So, create a send port with File Adapter and set the Filter Condition pointing to TwoWayAckReceivePort.





18. Start the Application. Drop an input file of ORU 25 type in the input folder. Sample File is below.

<ORU_R01_25_GLO_DEF xmlns="http://microsoft.com/HealthCare/HL7/2X">
    <PID_PatientIdentification xmlns="">
      <PID_1_SetIdPid>1</PID_1_SetIdPid>
      <PID_2_PatientId />
      <PID_3_PatientIdentifierList>
        <CX_0_IdNumber>7060106805</CX_0_IdNumber>
        <CX_4_IdentifierTypeCode>AM</CX_4_IdentifierTypeCode>
      </PID_3_PatientIdentifierList>
      <PID_5_PatientName>
        <XPN_0_FamilyName>
          <XPN_0_0_Surname>DOUGLAS</XPN_0_0_Surname>
        </XPN_0_FamilyName>
        <XPN_1_GivenName>KENNETH</XPN_1_GivenName>
        <XPN_6_NameTypeCode>B</XPN_6_NameTypeCode>
      </PID_5_PatientName>
      <PID_7_DateTimeOfBirth>
        <TS_0_Time>19600222</TS_0_Time>
      </PID_7_DateTimeOfBirth>
      <PID_8_AdministrativeSex>A</PID_8_AdministrativeSex>
    </PID_PatientIdentification>
    <PV1_PatientVisit xmlns="">
      <PV1_1_SetIdPv1>1</PV1_1_SetIdPv1>
      <PV1_2_PatientClass>U</PV1_2_PatientClass>
      <PV1_7_AttendingDoctor>
        <XCN_0_IdNumber>A123</XCN_0_IdNumber>
        <XCN_1_FamilyName>
          <XCN_1_0_Surname>Test</XCN_1_0_Surname>
        </XCN_1_FamilyName>
        <XCN_2_GivenName>Test</XCN_2_GivenName>
        <XCN_3_SecondAndFurtherGivenNamesOrInitialsThereof>T</XCN_3_SecondAndFurtherGivenNamesOrInitialsThereof>
      </PV1_7_AttendingDoctor>
      <PV1_17_AdmittingDoctor>
        <XCN_0_IdNumber>G21549</XCN_0_IdNumber>
        <XCN_1_FamilyName>
          <XCN_1_0_Surname>Rosenfeld</XCN_1_0_Surname>
        </XCN_1_FamilyName>
        <XCN_2_GivenName>David</XCN_2_GivenName>
        <XCN_7_SourceTable>MD</XCN_7_SourceTable>
      </PV1_17_AdmittingDoctor>
    </PV1_PatientVisit>
    <Observation xmlns="">
      <ORC_CommonOrder>
        <ORC_1_OrderControl>AF</ORC_1_OrderControl>
        <ORC_2_PlacerOrderNumber>
          <EI_0_EntityIdentifier>123</EI_0_EntityIdentifier>
        </ORC_2_PlacerOrderNumber>
        <ORC_3_FillerOrderNumber>
          <EI_2_UniversalId>P20005</EI_2_UniversalId>
        </ORC_3_FillerOrderNumber>
        <ORC_12_OrderingProvider>
          <XCN_0_IdNumber>A123</XCN_0_IdNumber>
          <XCN_1_FamilyName>
            <XCN_1_0_Surname>Test</XCN_1_0_Surname>
          </XCN_1_FamilyName>
          <XCN_2_GivenName>Test1</XCN_2_GivenName>
          <XCN_3_SecondAndFurtherGivenNamesOrInitialsThereof>T</XCN_3_SecondAndFurtherGivenNamesOrInitialsThereof>
        </ORC_12_OrderingProvider>
      </ORC_CommonOrder>
      <OBR_ObservationRequest>
        <OBR_1_SetIdObr>1</OBR_1_SetIdObr>
        <OBR_2_PlacerOrderNumber>
          <EI_0_EntityIdentifier>7060106805</EI_0_EntityIdentifier>
        </OBR_2_PlacerOrderNumber>
        <OBR_3_FillerOrderNumber>
          <EI_0_EntityIdentifier>1</EI_0_EntityIdentifier>
          <EI_1_NamespaceId>1</EI_1_NamespaceId>
          <EI_2_UniversalId>P145773</EI_2_UniversalId>
          <EI_3_UniversalIdType>DNS</EI_3_UniversalIdType>
        </OBR_3_FillerOrderNumber>
        <OBR_4_UniversalServiceIdentifier>
          <CE_0_Identifier>13130</CE_0_Identifier>
          <CE_1_Text>Test Report</CE_1_Text>
        </OBR_4_UniversalServiceIdentifier>
        <OBR_7_ObservationDateTime>
          <TS_0_Time>20060124120000+0530</TS_0_Time>
        </OBR_7_ObservationDateTime>
        <OBR_11_SpecimenActionCode>A</OBR_11_SpecimenActionCode>
        <OBR_15_SpecimenSource>
          <SPS_0_SpecimenSourceNameOrCode>
            <SPS_0_0_Identifier>ALL</SPS_0_0_Identifier>
            <SPS_0_1_Text>Urine</SPS_0_1_Text>
          </SPS_0_SpecimenSourceNameOrCode>
        </OBR_15_SpecimenSource>
        <OBR_16_OrderingProvider>
          <XCN_0_IdNumber>G21549</XCN_0_IdNumber>
          <XCN_1_FamilyName>
            <XCN_1_0_Surname>Rosenfeld</XCN_1_0_Surname>
          </XCN_1_FamilyName>
          <XCN_2_GivenName>David</XCN_2_GivenName>
          <XCN_7_SourceTable>MD</XCN_7_SourceTable>
          <XCN_12_IdentifierTypeCode>AN</XCN_12_IdentifierTypeCode>
        </OBR_16_OrderingProvider>
        <OBR_21_FillerField2>Hello</OBR_21_FillerField2>
        <OBR_22_ResultsRptStatusChngDateTime>
          <TS_0_Time>20060221102000+0530</TS_0_Time>
        </OBR_22_ResultsRptStatusChngDateTime>
        <OBR_25_ResultStatus>Y</OBR_25_ResultStatus>
        <OBR_27_QuantityTiming>
          <TQ_0_Quantity>
            <TQ_0_0_Quantity>1</TQ_0_0_Quantity>
          </TQ_0_Quantity>
        </OBR_27_QuantityTiming>
      </OBR_ObservationRequest>
      <ObservationResults>
        <OBX_ObservationResult>
          <OBX_1_SetIdObx>1</OBX_1_SetIdObx>
          <OBX_2_ValueType>AD</OBX_2_ValueType>
          <OBX_3_ObservationIdentifier>
            <CE_0_Identifier>123</CE_0_Identifier>
            <CE_1_Text>RxGuardian</CE_1_Text>
          </OBX_3_ObservationIdentifier>
          <OBX_5_ObservationValue>Hello</OBX_5_ObservationValue>
          <OBX_7_ReferencesRange>1-2</OBX_7_ReferencesRange>
          <OBX_8_AbnormalFlags>LL</OBX_8_AbnormalFlags>
          <OBX_11_ObservationResultStatus>C</OBX_11_ObservationResultStatus>
          <OBX_15_ProducerSId>
            <CE_2_NameOfCodingSystem>C4</CE_2_NameOfCodingSystem>
          </OBX_15_ProducerSId>
        </OBX_ObservationResult>
      </ObservationResults>
    </Observation>
  </ORU_R01_25_GLO_DEF>

19. You can observer that 7Edit receives the file and sends an Acknowledgement.



20. The ACK will then be received by the send port you created in step 17.

Now try testing this by yourself by modifying some of the steps and see what kind of errors you get. For eg - in step 9, try giving different names to Multipart message and see. This way you will be able to learn more.

Hope this is helpful. Post if you need the source code.

- Shiv
Web Counters

Group Max Occurs vs. Max Occurs / Group Min Occurs vs. Min Occurs

Min vs. Max

For a given schema, the Min occurs value should always be less than Max Occurs value. The default values for these 2 properties are “1”, which means if we won’t specify the values, then the field is mandatoryJ. So please don’t ignore these properties and always set the Min Occurs value to “0”, if the field is not mandatory.  If the maximum no of occurrences of the field is not known, then set the Max Occurs property to “unbounded” or “*” (Same is the case for Group Max Occurs and Group Min Occurs also.)

Group Max Occurs vs. Max Occurs

As the name implies the ‘Max Occurs' informs us the maximum occurrences of a particular field and the 'Group Max Occurs' informs us the maximum occurrences of a group of fields irrespective of the Group Order  Type property value. Here the Group can be of any type. It can be either “Sequence “or “Choice”. (Same is the case for Min occurs also). It means if the Group Max property is 5 for a group, then the group can appear for a max of 5 times but not more than that.

Ex:

The node structure will be like this.

<Parent>

    <Child>First Instance</ Child >

    < Child >Second Instance </ Child >

    < Child >Third Instance </ Child >

    < Child >Fourth Instance </ Child >

    < Child> Fifth Instance </ Child >

</Parent>

Note: It’s the Group Max property of “Parent” element but not “Child”. Because I have seen people thinking it’s for “Child” node as “Child” is getting repeated but not the “Parent”.

The Group properties will be disabled for the Records which don’t have any sub records. A point to note down here is if we add a Sequence/Choice group directly (instead of setting the Group Order property to “Sequence”/”Choice”), the Group properties will be disabled.

Here the Record111 does not have any sub fields. So automatically the Group properties are disabled.



 

 

Here as the Record11 has sub elements, the Group properties are enabled.



 

Here you can see that the Group Max/ Min Properties are not applicable for Sequence. Similar is the case for Choice.



 

Hope you are clear on these properties now. :)

Monday, 13 June 2011

Using Parties and RoleLinks to dynamically route a message to a destination (BizTalk 2010)

Configuring and Using Parties, RoleLinks in BizTalk a huge concept.
This post will show you simple steps in Using Parties and RoleLinks to dynamically route a message to a different destination.


Example Scenario:


Consider you receive a message from some sources. This message has a node called Name. Based on the value of Name node, you have to route the message to that party. Each party saves the message to a different folder using FILE Adapter.


Sample:


1. Create an empty BizTalk Solution. Add a schema with two nodes ID and Name. For simplicity make both nodes as Distinguished Fields.



2. Add an orchestration to the solution and a receive Shape to it, to receive a message of the Type Schema1 created in step1.



3. Right Click the Port Surface and add a new RoleLink to it.



4. Follow the Wizard. Give the RoleLink a suitable name and Create a new Role Link Type. In the Last Step, select the Consumer Role (I will be sending the First Message).



5. From the resultant RoleLink, delete the Provider Role. Right Click the Consumer Role and select Add Port Type.



6. In the Port Type Wizard, select the communication Pattern as One-Way and Access Restriction to Public. Complete the Wizard.


7. Now we should add a line of code, to indicate how to determine the Destination Party to which the message should be routed.


Add an expression shape to the orchestration and put the following code in that.



Keep note of “SomeId”. This is the identifier that the part should have to route the message to it. We will use this back in Admin Console.


8. Add a Send Shape to the Orchestration and Send the Input Message (received in step2) to the Role Link. Resulting Orchestration will be as follows.



9. Sign the solution and deploy to Admin Console.

10. Create two Send Ports with File Adapter. These two Ports will then be used by two parties to route the message to the corresponding Folder.



11. In Admin Console, right click the Parties Node and Select new Party.



12. In the General Tab you can add any Name Value Pairs (I m yet to find where these will be used).



13. In the Send Ports Tab, Add Party1 Send Port which is created in step 10 and Click ok to complete the step.



14. Expand the Party1 you just created and open the Party1_Profile. Go to Identity Tab and set the Identity as shown below.



Here the Qualifier is the same value that we gave in step 7.


What this means is, whenever the Name node of the input message has a value of Party1, it will be routed to the party we just created.


15. Repeat the same Steps and Create another Party named Party2.


16. Once the party creation is done, Refresh the Applications. This is important, else parties might not reflect in Configuration.


17. Right Click the Parties Application in Admin Console and Click Configure. Create a new Receive port (File Adapter) for the Logical Port of the Orchestration.



18. Select the Role_1 Tab. Click on Enlist and select the two parties you created in previous steps.


19. Now select Party1 and Click on Bind. Select the Party1 Send Port available for Party1.


20. Repeat the Same step and Bind the Party2. Complete the Configuration and Start the Applicaion.


Testing the Parties:


1. Drop the below file in the Receive Location Folder of the Application.


<ns0:Root xmlns:ns0="http://Parties.Schema1">
  <ID>ID_0</ID>
  <Name>Party2</Name>
</ns0:Root> 

2. Here Name node has the value Party2. It will mach this value with SomeId Key for the two Parties. Since the Party Party2 has SomeId value set to Party2, this file will be send to the send port linked with Party2.


3. If the Name node has a Value Party1, the file will be sent to the sendport linked with Party1.


Hope it helps..
- Shiv


Web Counters

Monday, 6 June 2011

Using PartContextPropertyBase in BizTalk Server

Every element in the Property schema should be of type as below.



Message Data Property Base and Message Context Property Base would be clear to everyone (incase if you are not clear with the difference, you can refer this link).

Where is this PartContextPropertyBase used ? This post will give you a simple demo on using PartContextPropertyBase.

PartContextPropertyBase is used for setting context properties for individual Message Parts of a Multipart Message. A multipart message can have more than one part.

A MessageContextPropertyBase property can be set at the message level, irrespective of how many parts it has. Also, MessageContextPropertyBase should be accessed at Message Level (eg: Message_1(your property) rather than Message_1.Part1(your property). Whereas PartContextPropertyBase can be set for individual parts with in a Message.

Consider the following sample:

1. Create a new BizTalk Project and a sample schema, just to initiate the Orchestration Instance. (It has no other purpose in this sample).



2. Create a Property Schema. Add two elements PartProperty , NormalContextProperty to it as shown below.



3. Set the Property Schema Base of the PartProperty to PartContextPropertyBase.



4. Similarly set the Property Schema Base of the NormalContextProperty to MessageContextPropertyBase.


 

5. Save all the stuff and create a new Orchestration. Add a receive shape and a receive port to receive a message of Schema Created in step1. (This is just to initiate an orchestration. This message is of no use for us in this sample.)



6. Now expand Multi-Part Message and create a new MultiPartMessageType.



7. Set the MessagePart_1 to the schema you created in step1. (This is again for sample. It is not related to out topic)



8. Create two more MessageParts and assign them to any data type like String, Datetime etc. (Just for demo)



Here comes the real application of PartContextPropertyBase.

9. Create a new Message called Message_2 and set its type to the Multipart Message created in above step.



10. Add a Message Assignment shape to the Orchestration and Set the Messages Constructed Property to Message_2. Add the following code to construct the Message.



11. Assign the Values to the properties created in Step 2. You can observe that only NormalContextProperty is available at the Message_2. Where as PartProperty is not available here.



12. In Order to use the PartProperty, you should apply that the Individual part of the message as shown below.



13. Complete the assignment of values as shown below.



14. Add an expression shape to the Orchestration to print the values you assigned just now to the Eventlog.



15. Deploy the Solution. Bind the Logical Port to physical file port. Drop a file of Schema1 in the physical folder.

16. Very soon you will see that all your values are printed to EventLog.



Hope this gives an idea on using PartContextPropertyBase.

- Shiv


Web Counters