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}