Multiple PACS and Print Destinations using DICOM Printer 2

This post describes how to configure DP2 for multiple PACS and Print destinations. At the completion of these steps you will be able to:

  • Print a document or image to DICOM Printer 2,
  • Choose from among three destinations (2 PACS systems, and 1 Dry Imager), and
  • Have DICOM Printer deliver based on your choices.

Note:
DICOM Printer is a print-to-PACS product. If you’re looking for a store-forward multi-destination PACS router, check out Capacitor.

Getting Started

Before we continue, you might want to read our previous post about automating storage to PACS; it will familiarize you with basic DP2 configuration, <ActionList> components, as well as the <Workflow> block.

We’ll begin this document with the configuration from the other post:

<DicomPrinterConfig>
  ...
  <ActionsList>
    <ParseJobTextFile name="ParseContents">
      <DcmTag tag="(8,50)">^(.*)\-.*$</DcmTag>
      <DcmTag tag="(8,60)">^.*\-(.*)$</DcmTag>
    </ParseJobFileName>
    <Query name="Query" type="Worklist">
      <DcmTag tag="(0008,0050)" />
      <DcmTag tag="(0008,0060)" />
      <DcmTag tag="(0010,0020)" />
      <DcmTag tag="(0020,000D)" />
      <ConnectionParameters>
        <MyAeTitle>DICOMPRINTER2</MyAeTitle>
        <PeerAeTitle>DVT</PeerAeTitle>
        <Host>localhost</Host>
        <Port>104</Port>
      </ConnectionParameters>
    </Query>
    <Store name="Store">
      <Compression type="RLE" />
      <ConnectionParameters>
        <PeerAeTitle>CONQUESTSRV1</PeerAeTitle>
        <Host>192.168.0.41</Host>
        <Port>5678</Port>
      </ConnectionParameters>
    </Store>
  </ActionsList>
  <Workflow>
    <Perform action="ParseContents" onError="Hold" />
    <Perform action="Query" onError="Suspend" />
    <If field="QUERY_FOUND" value="1">
      <Statements>
        <Perform action="Store" />
      </Statements>
      <Else>
        <Suspend resumeAction="Query" />
      </Else>
    </If>
  </Workflow>
</DicomPrinterConfig>

This configuration contains three main <Action> elements and a <Workflow> block containing one <If> condition. The actions are:

  • ParseContents: An instance of <ParseJobTextFile> , which retrieves the accession number and modality from the document filename.
  • Query: An instance of <Query>, which Queries and Retrieves additional attributes from a DICOM Worklist, and
  • Store: An instance of the <Store> action, which stores the resulting document and meta-data to PACS.

Making Changes

We will be adding:

  • An additional <Store> action for storage to a second PACS;
  • A <Print> action, for printing to film;
  • A <Run> action that will present the user with destination choices; and
  • A series of additional <If>conditions that enact the user’s selections.

Adding New Destinations

To add a new PACS destination we will need to drop a new <Store> action into the <ActionsList> block:

    <!-- First PACS -->
    <Store name="StoreToPrimary">
      <Compression type="RLE" />
      <ConnectionParameters>
        <PeerAeTitle>CONQUESTSRV1</PeerAeTitle>
        <Host>192.168.0.41</Host>
        <Port>5678</Port>
      </ConnectionParameters>
    </Store>

    <!-- Second PACS -->
    <Store name="StoreToSecondary">
      <Compression type="RLE" />
      <ConnectionParameters>
        <MyAeTitle>DICOM_PRINTER</MyAeTitle>
        <PeerAeTitle>CONQUESTSRV2</PeerAeTitle>
        <Host>192.168.0.42</Host>
        <Port>104</Port>
      </ConnectionParameters>
    </Store>
  </ActionsList>

Note that in order to help differentiate these elements in the config file, we’ve also renamed the first <Store> action from just “Store” to “StoreToPrimary”. We’ll have to update the <Perform>call in the <Workflow> block to reflect this change.

You may have also noticed that we added a <MyAeTitle> element. This tells DP2 to use a specific AE title when calling out to the second PACS.

Next, we add our new <Print> action:

...
  </Store>
  <Print name="Print">
    <PrintMode>Grayscale12</PrintMode>
    <Resolution>254 x 254</Resolution>
    <ConnectionParameters>
      <MyAeTitle>DICOM_PRINTER</MyAeTitle>
      <PeerAeTitle>DRIPIX</PeerAeTitle>
      <Host>192.168.0.43</Host>
      <Port>5040</Port>
    </ConnectionParameters>
  </Print>
</ActionsList>

This action references a printer located at 192.168.0.43, listening on port 5040, responding to the AE title DRYPIX. DryPix printers tend to use a resolution of 254/508 DPI. Specifying the resolution here helps with true-size image reproduction.

Saving Data

Next, we need to create a place to save our user responses. DP2 doesn’t have variables, but it does let us save things in private DICOM tags:

<ActionsList>
  <SetTag tag="(1001,1000)" tagName="Destination"
          name="InitDestination" vr="LO"/>
...

Using a private tag ensures that whatever we put there won’t interfere with actual DICOM meta-data.

GeneralSelectPlugin.exe

To get the user to choose a destination we’ll use DICOM Printer 2’s <Run> action, which allows you to bring up any external executable; in this case, GeneralSelectPlugin.exe, a tool included with DP2 for exactly this purpose.

<Run type="Interactive" name="ChooseDestination">
    <Command>plugins/GeneralSelectPlugin.exe</Command>
    <Timeout>60000</Timeout>
    <Arguments>Select Report Destination|Primary PACS|Secondary PACS|Dry Imager</Arguments>
    <Output tag="(1001,1000)"/>
    <!-- Destination -->
</Run>

We’ve given the user 60 seconds to select from among three options: “Primary PACS”, “Secondary PACS”, and “Dry Imager”. The selection will be stored to the (1001,1000)private attribute. If the user fails to make their selection within 60 seconds, then the run action will return in error, which can later be used to interrupt the workflow.


The Workflow

Next, we’ll need to assemble our Actions into the <Workflow> block.

Here is the order of events in our workflow:

  • Initialize our destination private tag;
  • Present the user with our destination choices, and store their response to our private tag;
  • Check if the user wishes to print to film, and do it.
  • If the user has chosen to send to either PACS, then query worklist, and then store to either one or both.

Choose Destination

<Perform action="InitDestination"/>
<Perform action="ChooseDestination"/ onError="Discard">

The line calls the InitDestination action, which initializes the (1001,1000)attribute with a blank value. The second calls the GeneralSelectPlugin.exe executable and stores the response. If the user closes the window without making a selection, or fails to make a choice within our timeout of 60 seconds, then the job will be “Discarded”.

Print to Film

Next, we check if the user wishes to print, and print if necessary.

<If field="TAG_VALUE( 1001, 1000 )" value="\*Dry\*">
  <Statements>
    <Perform action="Print" onError="Ignore"/>
  </Statements>
</If>

The <If> element checks if the value of our private tag contains the word “Dry”, which would indicate that this was one of the user selections. “\*Dry\*” is a wildcard match.

Parse, Query, and Store

If the user chose either of the destination PACS, then our private attribute will contain the word “PACS”.

<If field="TAG_VALUE( 1001, 1000 )" value="\*PACS\*">
  <Statements>
    ...
  </Statements>
</If>

If that’s the case, then we need to get a couple of attributes from the filename and Query the Worklist.

<Perform action="ParseContents" onError="Hold" />
<Perform action="Query" onError="Ignore" />

Then check if the query returned a result, and either proceed to storage, or retry the Query later.

<If field="QUERY_FOUND" value="1">
  <Statements>
    ...
  </Statements>
  <Else>
    <Suspend resumeAction="Query">
  </Else>
</If>

Finally, we check for the words “Primary” and “Secondary” in the user’s selection, and then store correspondingly:

<If field="TAG_VALUE( 1001, 1000 )" value="\*Primary\*">
  <Statements>
    <Perform action="StoreOnPrimaryPacs" onError="Ignore"/>
  </Statements>
</If>
<If field="TAG_VALUE( 1001, 1000 )" value="\*Secondary\*">
  <Statements>
    <Perform action="StoreOnSecondaryPacs" onError="Ignore"/>
  </Statements>
</If>

Plug-In Launcher

DICOM Printer 2 is a system service. Because if this it cannot communicate directly with the user space — or present user interface elements directly.

To help with this communication, DP2 comes accompanied by a tool called the “Plug-In Launcher”. In order for this all to function as designed you will have to make sure that Plug-In Launcher is started on user login. You can verify that it’s running by the presence of its icon in the task tray.

A shortcut to Plug-In Launcher can be found in your Start Menu.


The Full Configuration

Reproduced here for copy/paste purposes.

<DicomPrinterConfig>
  <General>
    <CheckingInterval>1</CheckingInterval>
    <SuspensionTime>1</SuspensionTime>
    <Verbosity>20</Verbosity>
  </General>
  <ActionsList>
    <SetTag tag="(1001,1000)" tagName="Destination"
          name="InitDestination" vr="LO"/>

    <Run type="Interactive" name="ChooseDestination">
        <Command>plugins/GeneralSelectPlugin.exe</Command>
        <Timeout>60000</Timeout>
        <Arguments>Select Report Destination|Primary PACS|Secondary PACS|Dry Imager</Arguments>
        <Output tag="(1001,1000)"/>
        <!-- Destination -->
    </Run>

    <ParseJobTextFile name="ParseContents">
      <DcmTag tag="(8,50)">^(.*)\-.*$</DcmTag>
      <DcmTag tag="(8,60)">^.*\-(.*)$</DcmTag>
    </ParseJobFileName>

    <Query name="Query" type="Worklist">
      <DcmTag tag="(0008,0050)" />
      <DcmTag tag="(0008,0060)" />
      <DcmTag tag="(0010,0020)" />
      <DcmTag tag="(0020,000D)" />
      <ConnectionParameters>
        <MyAeTitle>DICOMPRINTER2</MyAeTitle>
        <PeerAeTitle>DVT</PeerAeTitle>
        <Host>localhost</Host>
        <Port>104</Port>
      </ConnectionParameters>
    </Query>

    <!-- First PACS -->
    <Store name="StoreToPrimary">
      <Compression type="RLE" />
      <ConnectionParameters>
        <PeerAeTitle>CONQUESTSRV1</PeerAeTitle>
        <Host>192.168.0.41</Host>
        <Port>5678</Port>
      </ConnectionParameters>
    </Store>

    <!-- Second PACS -->
    <Store name="StoreToSecondary">
      <Compression type="RLE" />
      <ConnectionParameters>
        <MyAeTitle>DICOM_PRINTER</MyAeTitle>
        <PeerAeTitle>CONQUESTSRV2</PeerAeTitle>
        <Host>192.168.0.42</Host>
        <Port>104</Port>
      </ConnectionParameters>
    </Store>

    <Print name="Print">
      <PrintMode>Grayscale12</PrintMode>
      <Resolution>254 x 254</Resolution>
      <ConnectionParameters>
        <MyAeTitle>DICOM_PRINTER</MyAeTitle>
        <PeerAeTitle>DRIPIX</PeerAeTitle>
        <Host>192.168.0.43</Host>
        <Port>5040</Port>
      </ConnectionParameters>
    </Print>
  </ActionsList>
  </ActionsList>
  <Workflow>
    <Perform action="InitDestination"/>
    <Perform action="ChooseDestination"/ onError="Discard">
    
    <If field="TAG_VALUE( 1001, 1000 )" value="\*Dry\*">
      <Statements>
        <Perform action="Print" onError="Ignore"/>
      </Statements>
    </If>

    <If field="TAG_VALUE( 1001, 1000 )" value="\*PACS\*">
      <Statements>
        <Perform action="ParseContents" onError="Hold" />
        <Perform action="Query" onError="Ignore" />
        <If field="QUERY_FOUND" value="1">
          <Statements>
            <If field="TAG_VALUE( 1001, 1000 )" value="\*Primary\*">
              <Statements>
                <Perform action="StoreToPrimary" onError="Ignore"/>
              </Statements>
            </If>
            <If field="TAG_VALUE( 1001, 1000 )" value="\*Secondary\*">
              <Statements>
                <Perform action="StoreToSecondary" onError="Ignore"/>
              </Statements>
            </If>
          </Statements>
          <Else>
            <Suspend resumeAction="Query">
          </Else>
        </If>
      </Statements>
    </If>
    
  </Workflow>
</DicomPrinterConfig>

Leave a Reply

Your email address will not be published. Required fields are marked *