001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.tool.movescu.test; 040 041import static org.junit.Assert.assertTrue; 042 043import java.io.IOException; 044import java.security.GeneralSecurityException; 045import java.util.ArrayList; 046import java.util.List; 047import java.util.concurrent.ExecutorService; 048import java.util.concurrent.Executors; 049import java.util.concurrent.ScheduledExecutorService; 050 051import org.dcm4che3.data.Attributes; 052import org.dcm4che3.data.ElementDictionary; 053import org.dcm4che3.data.Tag; 054import org.dcm4che3.data.UID; 055import org.dcm4che3.data.VR; 056import org.dcm4che3.net.ApplicationEntity; 057import org.dcm4che3.net.Association; 058import org.dcm4che3.net.Connection; 059import org.dcm4che3.net.Device; 060import org.dcm4che3.net.DimseRSPHandler; 061import org.dcm4che3.net.IncompatibleConnectionException; 062import org.dcm4che3.net.Priority; 063import org.dcm4che3.tool.common.test.TestResult; 064import org.dcm4che3.tool.common.test.TestTool; 065import org.dcm4che3.tool.movescu.MoveSCU; 066import org.dcm4che3.tool.movescu.MoveSCU.InformationModel; 067 068/** 069 * @author Hesham Elbadawi <bsdreko@gmail.com> 070 */ 071public class MoveTool implements TestTool{ 072 073 private final String host; 074 private final int port; 075 private final String aeTitle; 076 private final String destAEtitle; 077 private final String retrieveLevel; 078 private final InformationModel retrieveInformationModel; 079 private final boolean relational; 080 private final Device device; 081 private final Connection conn; 082 private final String sourceAETitle; 083 084 private int numResponses; 085 private int numSuccess; 086 private int numFailed; 087 private int numWarning; 088 private int expectedMatches = Integer.MIN_VALUE; 089 090 private final Attributes moveAttrs = new Attributes(); 091 092 private final List<Attributes> response = new ArrayList<Attributes>(); 093 private TestResult result; 094 095 private static String[] IVR_LE_FIRST = { UID.ImplicitVRLittleEndian, 096 UID.ExplicitVRLittleEndian, UID.ExplicitVRBigEndianRetired }; 097 098 099 public MoveTool(String host, int port, String aeTitle, String destAEtitle, String retrieveLevel, 100 String informationModel, boolean relational, Device device, String sourceAETitle, Connection conn) { 101 super(); 102 this.host = host; 103 this.port = port; 104 this.aeTitle = aeTitle; 105 this.destAEtitle = destAEtitle; 106 this.retrieveLevel = retrieveLevel; 107 this.retrieveInformationModel = "StudyRoot".equalsIgnoreCase(informationModel) ? InformationModel.StudyRoot : InformationModel.PatientRoot; 108 this.relational = relational; 109 this.device = device; 110 this.sourceAETitle = sourceAETitle; 111 this.conn = conn; 112 } 113 114 public void move(String testDescription) throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException { 115 device.setInstalled(true); 116 ApplicationEntity ae = new ApplicationEntity(sourceAETitle); 117 device.addApplicationEntity(ae); 118 ae.addConnection(conn); 119 120 MoveSCU main = new MoveSCU(ae); 121 122 main.getAAssociateRQ().setCalledAET(aeTitle); 123 main.getRemoteConnection().setHostname(host); 124 main.getRemoteConnection().setPort(port); 125 126 // ensure secure connection 127 main.getRemoteConnection().setTlsCipherSuites(conn.getTlsCipherSuites()); 128 main.getRemoteConnection().setTlsProtocols(conn.tlsProtocols()); 129 130 main.getKeys().addAll(moveAttrs); 131 main.setPriority(Priority.NORMAL); 132 main.setDestination(destAEtitle); 133 134 ExecutorService executorService = 135 Executors.newSingleThreadExecutor(); 136 ScheduledExecutorService scheduledExecutorService = 137 Executors.newSingleThreadScheduledExecutor(); 138 main.getDevice().setExecutor(executorService); 139 main.getDevice().setScheduledExecutor(scheduledExecutorService); 140 main.setInformationModel(retrieveInformationModel, IVR_LE_FIRST, relational); 141 main.addLevel(retrieveLevel); 142 143 long timeStart = System.currentTimeMillis(); 144 try { 145 main.open(); 146 main.retrieve(main.getKeys(), getDimseRSPHandler(main.getAssociation().nextMessageID())); 147 } finally { 148 main.close(); 149 executorService.shutdown(); 150 scheduledExecutorService.shutdown(); 151 } 152 153 long timeEnd = System.currentTimeMillis(); 154 155 if (this.expectedMatches >= 0) 156 assertTrue("test[" + testDescription 157 + "] not returned expected result:" + this.expectedMatches 158 + " but:" + numResponses, numResponses == this.expectedMatches); 159 160 // commented since tests could also test failed responses 161 // assertTrue("test[" + testDescription 162 // + "] had failed responses:"+ numFailed, numFailed==0); 163 164 init(new MoveResult(testDescription, expectedMatches, numResponses, 165 numSuccess, numFailed, numWarning, 166 (timeEnd - timeStart), response)); 167 } 168 169 private DimseRSPHandler getDimseRSPHandler(int messageID) { 170 171 DimseRSPHandler rspHandler = new DimseRSPHandler(messageID) { 172 173 @Override 174 public void onDimseRSP(Association as, Attributes cmd, 175 Attributes data) { 176 super.onDimseRSP(as, cmd, data); 177 onCMoveResponse(cmd, data); 178 } 179 }; 180 181 return rspHandler; 182 } 183 184 protected void onCMoveResponse(Attributes cmd, Attributes data) { 185 int status = cmd.getInt(Tag.Status, -1); 186 if(!cmd.contains(Tag. NumberOfRemainingSuboperations)) { 187 numSuccess = cmd.getInt(Tag.NumberOfCompletedSuboperations,0); 188 numFailed = cmd.getInt(Tag.NumberOfFailedSuboperations,0); 189 numWarning = cmd.getInt(Tag.NumberOfWarningSuboperations,0); 190 numResponses = numSuccess + numFailed + numWarning; 191 } 192 response.add(cmd); 193 } 194 195 public void addTag(int tag, String value) throws Exception { 196 VR vr = ElementDictionary.vrOf(tag, null); 197 moveAttrs.setString(tag, vr, value); 198 } 199 200 public void addAll(Attributes keys) { 201 moveAttrs.addAll(keys); 202 } 203 204 public void setExpectedMatches(int expectedResult) { 205 this.expectedMatches = expectedResult; 206 } 207 208 @Override 209 public void init(TestResult resultIn) { 210 this.result = resultIn; 211 } 212 213 @Override 214 public TestResult getResult() { 215 return this.result; 216 } 217 218}