/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is part of dcm4che, an implementation of DICOM(TM) in
* Java(TM), hosted at https://github.com/gunterze/dcm4che.
*
* The Initial Developer of the Original Code is
* Agfa Healthcare.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* See @authors listed below
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.dcm4che3.tool.stgcmtscu.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.io.DicomOutputStream;
import org.dcm4che3.net.ApplicationEntity;
import org.dcm4che3.net.Association;
import org.dcm4che3.net.AssociationStateException;
import org.dcm4che3.net.Commands;
import org.dcm4che3.net.Connection;
import org.dcm4che3.net.Device;
import org.dcm4che3.net.Dimse;
import org.dcm4che3.net.IncompatibleConnectionException;
import org.dcm4che3.net.Status;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.net.service.AbstractDicomService;
import org.dcm4che3.net.service.DicomService;
import org.dcm4che3.net.service.DicomServiceException;
import org.dcm4che3.tool.common.DicomFiles;
import org.dcm4che3.tool.common.test.TestResult;
import org.dcm4che3.tool.common.test.TestTool;
import org.dcm4che3.tool.stgcmtscu.StgCmtSCU;
import org.dcm4che3.util.SafeClose;
import org.junit.Assert;
/**
* @author Hesham Elbadawi <bsdreko@gmail.com>
*/
public class StgCmtTool implements TestTool {
private final String host;
private final int port;
private final File baseDirectory;
private final File storageDirectory;
String aeTitle;
Device device;
StgCmtSCU stgCmtSCU ;
private final String sourceAETitle;
private Attributes nEventReqData = new Attributes();
private int success;
private int fails;
private TestResult result;
Connection bound;
public StgCmtTool(String host, int port, String aeTitle,
File baseDir,File storageDirectory, Device device,
String sourceAETitle, Connection conn) {
this.host = host;
this.port = port;
this.aeTitle = aeTitle;
this.baseDirectory = baseDir;
this.device = device;
this.sourceAETitle = sourceAETitle;
this.storageDirectory = storageDirectory;
this.bound = conn;
}
private final DicomService stgcmtResultHandler =
new AbstractDicomService(UID.StorageCommitmentPushModelSOPClass) {
@Override
public void onDimseRQ(Association as, PresentationContext pc,
Dimse dimse, Attributes cmd, Attributes data)
throws IOException {
if (dimse != Dimse.N_EVENT_REPORT_RQ)
throw new DicomServiceException(Status.UnrecognizedOperation);
int eventTypeID = cmd.getInt(Tag.EventTypeID, 0);
if (eventTypeID != 1 && eventTypeID != 2)
throw new DicomServiceException(Status.NoSuchEventType)
.setEventTypeID(eventTypeID);
String tuid = data.getString(Tag.TransactionUID);
try {
Attributes rsp = Commands.mkNEventReportRSP(cmd, 0);
Attributes rspAttrs = writeResponse(as, cmd, data);
nEventReqData = rspAttrs;
as.writeDimseRSP(pc, rsp, null);
} catch (AssociationStateException e) {
System.out.println(as.toString() + " << N-EVENT-RECORD-RSP failed: " + e.getMessage());
} finally {
stgCmtSCU.removeOutstandingResult(tuid);
}
}
};
private Attributes writeResponse(Association as, Attributes cmd, Attributes data) throws DicomServiceException {
setFailsandSuccess(data);
if(storageDirectory == null) {
return data;
}
else {
String cuid = cmd.getString(Tag.AffectedSOPClassUID);
String iuid = cmd.getString(Tag.AffectedSOPInstanceUID);
String tuid = data.getString(Tag.TransactionUID);
File file = new File(storageDirectory, tuid);
DicomOutputStream out = null;
// System.out.println(as + "{}: M-WRITE {}" + file);
try {
out = new DicomOutputStream(file);
out.writeDataset(
Attributes.createFileMetaInformation(iuid, cuid,
UID.ExplicitVRLittleEndian),
data);
} catch (IOException e) {
//System.out.println(as + ": Failed to store Storage Commitment Result:" + e);
if(!(e instanceof FileNotFoundException))
throw new DicomServiceException(Status.ProcessingFailure, e);
} finally {
SafeClose.close(out);
}
}
return data;
}
private void setFailsandSuccess(Attributes data) {
success = data.getSequence(Tag.ReferencedSOPSequence).size();
if(data.contains(Tag.FailedSOPSequence))
fails = data.getSequence(Tag.FailedSOPSequence).size();
}
public void stgcmt(String description, String fileName) throws InterruptedException, IOException, GeneralSecurityException, IncompatibleConnectionException {
long t1, t2;
File file = new File(baseDirectory, fileName);
Assert.assertTrue(
"file or directory does not exists: " + file.getAbsolutePath(),
file.exists());
ApplicationEntity ae = new ApplicationEntity(sourceAETitle);
device.addApplicationEntity(ae);
ae.addConnection(bound);
for(Iterator<Connection> iterator=device.getConnections().iterator(); iterator.hasNext();) {
Connection next = iterator.next();
if(!next.getCommonName().equalsIgnoreCase(bound.getCommonName()))
iterator.remove();
}
this.stgCmtSCU = new StgCmtSCU(ae, stgcmtResultHandler);
// configure
bound.setMaxOpsInvoked(0);
bound.setMaxOpsPerformed(0);
stgCmtSCU.getAAssociateRQ().setCalledAET(aeTitle);
stgCmtSCU.getRemoteConnection().setHostname(host);
stgCmtSCU.getRemoteConnection().setPort(port);
//ensure secure connection
stgCmtSCU.getRemoteConnection().setTlsCipherSuites(bound.getTlsCipherSuites());
stgCmtSCU.getRemoteConnection().setTlsProtocols(bound.tlsProtocols());
stgCmtSCU.setTransferSyntaxes(new String[]{UID.ImplicitVRLittleEndian, UID.ExplicitVRLittleEndian, UID.ExplicitVRBigEndianRetired});
stgCmtSCU.setAttributes(new Attributes());
stgCmtSCU.setStorageDirectory(storageDirectory);
// scan
t1 = System.currentTimeMillis();
DicomFiles.scan(Arrays.asList(file.getAbsolutePath()), new DicomFiles.Callback() {
@Override
public boolean dicomFile(File f, Attributes fmi, long dsPos,
Attributes ds) {
return stgCmtSCU.addInstance(ds);
}
});
t2 = System.currentTimeMillis();
// create executor
ExecutorService executorService =
Executors.newCachedThreadPool();
ScheduledExecutorService scheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
device.setExecutor(executorService);
device.setScheduledExecutor(scheduledExecutorService);
device.bindConnections();
// open, send and wait for response
try {
stgCmtSCU.open();
stgCmtSCU.sendRequests();
} finally {
stgCmtSCU.close();
if (bound.isListening()) {
device.waitForNoOpenConnections();
device.unbindConnections();
}
executorService.shutdown();
scheduledExecutorService.shutdown();
}
init(new StgCmtResult(description,t2-t1,success, fails, nEventReqData));
}
@Override
public void init(TestResult result) {
this.result = result;
}
@Override
public TestResult getResult() {
return this.result;
}
public File getStorageDirectory() {
return storageDirectory;
}
}