DICOM Routing using Capacitor

DICOM Capacitor is a store-and-forward DICOM router, and conditional routing is among the simplest of its many features.

Here, we show an example of a configuration that makes Capacitor:

  • Receive studies from any modality,
  • Compress images on the way to a primary PACS, and
  • Re-route DICOM SR instances to a secondary PACS.

Getting Started

To begin, collect information about our PACS’.

For this example, we have gathered the following:

  AE Title IP Address Port Preferred Transfer Syntax  
Primary PACS IMAGEPACS 192.168.1.200 104 JPEG 2000 Lossless  
Secondary PACS REPORTPACS 192.168.1.201 104 Any  

We’re ready to install.

Install Capacitor

  1. Click this link to download the latest evaluation of DICOM Capacitor:
    https://store.fluxinc.ca/files/DCP/latest
  2. Run the installer and complete the installation process.
  3. Once finished, open DICOM Capacitor Control using your start menu, and, if needed, follow the prompts to obtain an evaluation license.
  4. Start, and then stop, the “DICOM Capacitor” service using either the Control applet, the Windows Services snap-in, or by running the following commands:
$ net start DICOMCapacitorService
$ net stop DICOMCapacitorService

Close DICOM Capacitor Control if it’s still open because all further steps will be performed outside the user interface.

Configure Capacitor

All Capacitor configuration files live in the %ProgramData%\Flux Inc\DICOM Capacitor folder.

Here are the three files we will be editing:

  • config.yml, which defines Capacitor’s operational settings, and
  • nodes.yml, which defines all destination (and source) nodes
  • routings.yml, which defines the routing rules

Set Capacitor Defaults

  1. Open config.yml in your YAML text editor — we recommend Atom, or Visual Studio Code.
  2. Confirm the value of scpPort:

    scpPort: 1040

    This is the port on which Capacitor will accept C-STORE (and C-ECHO) associations.
  3. Turn on the routing module by adding “route” the filters setting:

    filters: route

3. Finally, save and close config.yml.

Define the Two Nodes

Next, we define our destination nodes.

  1. First, open nodes.yml in your YAML text editor.
  2. Replace the contents of nodes.yml with:
# Primary PACS
- NodeRole: Storage
  AeTitle: IMAGEPACS
  HostName: 192.168.1.200
  Impersonate: true
  Port: 104
  TransferSyntax: JPEG2000Lossless
  MinimumLineSpeed: 500

# Secondary PACS
- NodeRole: Storage
  AeTitle: REPORTPACS
  HostName: 192.168.1.201
  Impersonate: true
  Port: 104
  MinimumLineSpeed: 500

According to this node definition file, Capacitor will:

  • Listen for C-ECHO and C-STORE associations on port 1040.
  • Respond and automatically route to AE titles IMAGEPACS and REPORTPACS1.
  • Compress all instances bound for IMAGEPACS to JPEG 2000 Lossless.
  • Impersonate, i.e., use the sending modality’s own AE title, when forwarding to these destinations.

1 Capacitor will also respond to DCP_IMAGEPACS, and DCP_REPORTPACS in the same way.

All that remains is for us to configure our routing rules.

Define DICOM Routing Rules

All route definitions live in a file called routings.yml.

  1. Create routings.yml if it does not already exist, and open it in your YAML editor.
  2. Paste in the following content and save the file:
---
- AeTitles:
    - IMAGEPACS
  Conditions:
    - Tag: 0008,0016
      MatchExpression: ^1\.2\.840\.10008\.5\.1\.4\.1\.1\.88\.[\d]{1,2}$
  Actions:
    - Description: Re-route to REPORTPACS
      Type: add_destination
      Target: REPORTPACS
      RemoveOriginal: true

This definition tells Capacitor’s routing module to:

  • Consider only instances bound for IMAGEPACS
  • Determine if the SOP Class UID (0008,0016) matches the regular expression ^1.2.840.10008.5.1.4.1.1.88.[\d]{1,2}$, which matches all DICOM SR types
  • If so, then add the destination REPORTPACS, which is defined in our nodes.yml file, and
  • Remove the original route for this instance

We’re ready to test.

Start Capacitor and Inspect the Logs

  1. Start the DICOM Capacitor service using the Windows Services snap-in, or:

    $ net start DICOMCapacitorService
  2. Next, open and inspect
    %ProgramData%\Flux Inc\DICOM Capacitor\logs\capacitor_service.log

The last few lines in this log should confirm that Capacitor is running and accepting connections on behalf of our two nodes.

<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)”/> </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> <Store name=“StoreToPrimary”>
<Compression type=“RLE” /> <ConnectionParameters>
<PeerAeTitle>CONQUESTSRV1 </PeerAeTitle>
<Host>192.168.0.41</Host> <Port>5678</Port>
</ConnectionParameters>
</Store> <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>