/* ***** 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-2014
* 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.dcm4chee.archive.ian.scu.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.ListIterator;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Sequence;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.data.VR;
import org.dcm4chee.storage.conf.Availability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Gunter Zeilinger <gunterze@gmail.com>
*
*/
public class IANBuilder {
private static final Logger LOG = LoggerFactory.getLogger(IANBuilder.class);
private final Attributes ian;
private final Sequence ianRefSeriesSeq;
private final Sequence ianRefPPSSeq;
private final HashSet<String> ianRefInstanceUIDs = new HashSet<String>();
private String ppsiuid;
private String studyiuid;
private HashMap<String,Attributes> ppsRefSOPs;
public IANBuilder() {
this.ian = new Attributes(3);
this.ianRefPPSSeq = ian.newSequence(Tag.ReferencedPerformedProcedureStepSequence, 1);
this.ianRefSeriesSeq =
ian.newSequence(Tag.ReferencedSeriesSequence, 1);
}
public String getMPPSInstanceUID() {
return ppsiuid;
}
public void setReferencedMPPS(String ppsiuid, Attributes attrs) {
if (!ianRefInstanceUIDs.isEmpty())
throw new IllegalStateException("SOP Reference already added");
if (ppsiuid == null)
throw new NullPointerException("ppsiuid");
Attributes ssa = attrs.getNestedDataset(
Tag.ScheduledStepAttributesSequence);
if (ssa == null)
throw new IllegalArgumentException("Missing Scheduled Step Attributes");
String studyiuid = ssa.getString(Tag.StudyInstanceUID);
if (studyiuid == null)
throw new IllegalArgumentException(
"Missing Study Instance UID");
Sequence perfSeriesSeq = attrs.getSequence(Tag.PerformedSeriesSequence);
if (perfSeriesSeq == null)
throw new IllegalArgumentException(
"Missing Performed Series Sequence");
HashMap<String, Attributes> map = new HashMap<String,Attributes>();
for (Attributes series : perfSeriesSeq) {
if (!series.containsValue(Tag.SeriesInstanceUID))
throw new IllegalArgumentException(
"Missing Series Instance UID");
addRefSOPSeq(ppsiuid,
series.getSequence(Tag.ReferencedImageSequence), map);
addRefSOPSeq(ppsiuid,
series.getSequence(Tag.ReferencedNonImageCompositeSOPInstanceSequence), map);
}
this.ppsRefSOPs = map;
this.ppsiuid = ppsiuid;
this.studyiuid = studyiuid;
ian.setString(Tag.StudyInstanceUID, VR.UI, studyiuid);
Attributes refPPS = new Attributes(3);
refPPS.setString(Tag.ReferencedSOPClassUID, VR.UI,
UID.ModalityPerformedProcedureStepSOPClass);
refPPS.setString(Tag.ReferencedSOPInstanceUID, VR.UI, ppsiuid);
refPPS.setNull(Tag.PerformedWorkitemCodeSequence, VR.SQ);
ianRefPPSSeq.add(refPPS);
}
private void addRefSOPSeq(String ppsiuid, Sequence refSOPSeq,
HashMap<String,Attributes> map) {
if (refSOPSeq != null)
for (Attributes refSOP : refSOPSeq) {
String iuid = refSOP.getString(Tag.ReferencedSOPInstanceUID);
if (iuid == null)
throw new IllegalArgumentException("Missing Referenced SOP Instance UID");
String cuid = refSOP.getString(Tag.ReferencedSOPClassUID);
if (cuid == null)
throw new IllegalArgumentException("Missing Referenced SOP Class UID");
Attributes prev = map.put(iuid, refSOP);
if (prev != null) {
LOG.warn(
"MPPS[iuid={}] contains multiple references of Instance[iuid={}, cuid={}]",
ppsiuid, iuid, cuid);
}
};
}
public int numberOfOutstandingInstances() {
return ppsRefSOPs != null ? ppsRefSOPs.size() : 0;
}
public Attributes getIAN() {
return ian;
}
public boolean addReferencedInstance(String studyiuid, String seriesiuid,
String iuid, String cuid, Availability availability,
String... retrieveAETs) {
if (this.studyiuid == null) {
this.studyiuid = studyiuid;
ian.setString(Tag.StudyInstanceUID, VR.UI, studyiuid);
} else if (!this.studyiuid.equals(studyiuid)) {
throw new IllegalStateException(
"Study[iuid=" + studyiuid
+ "] of received Instance[iuid=" + iuid + ", cuid=" + cuid
+ "] of Series[iuid=" + seriesiuid
+ "] does not match Study[iuid=" + this.studyiuid
+ (ppsiuid != null
? ("] referenced by MPPS[iuid=" + ppsiuid + "]")
: "] of previous added referenced Instance"));
}
if (!ianRefInstanceUIDs.add(iuid)) {
return false;
}
if (ppsRefSOPs != null) {
Attributes refSOP = ppsRefSOPs.remove(iuid);
if (refSOP == null)
return false;
String seriesiuidInPPS = refSOP.getParent().getString(Tag.SeriesInstanceUID);
String cuidInPPS = refSOP.getString(Tag.ReferencedSOPClassUID);
if (!seriesiuid.equals(seriesiuidInPPS)) {
LOG.warn("Series of received Instance[iuid={}, cuid={}] "
+ "of Series[iuid={}] of Study[iuid={}] differs from"
+ "Series[iuid={}] referenced by MPPS[iuid={}] - "
+ "no IAN will be emitted",
iuid, cuid, seriesiuid, studyiuid, seriesiuidInPPS, ppsiuid);
ppsRefSOPs.put(iuid, refSOP);
return false;
}
if (!cuid.equals(cuidInPPS)) {
LOG.warn("SOP Class of received Instance[iuid={}, cuid={}] "
+ "of Series[iuid={}] of Study[iuid={}] differs from"
+ "SOP Class[cuid={}] referenced by MPPS[iuid={}] - "
+ "emitted IAN will reference SOP Class of received Instance",
iuid, cuid, seriesiuid, studyiuid, cuidInPPS, ppsiuid);
}
}
getIANRefSeries(seriesiuid).getSequence(Tag.ReferencedSOPSequence)
.add(mkRefSOP(iuid, cuid, availability, retrieveAETs));
return true;
}
private Attributes getIANRefSeries(String seriesiuid) {
for (ListIterator<Attributes> iter =
ianRefSeriesSeq.listIterator(ianRefSeriesSeq.size());
iter.hasPrevious();) {
Attributes refSeries = iter.previous();
if (refSeries.getString(Tag.SeriesInstanceUID).equals(seriesiuid)) {
return refSeries;
}
}
Attributes refSeries = new Attributes(2);
refSeries.newSequence(Tag.ReferencedSOPSequence, 10);
refSeries.setString(Tag.SeriesInstanceUID, VR.UI, seriesiuid);
ianRefSeriesSeq.add(refSeries);
return refSeries;
}
private Attributes mkRefSOP(String iuid, String cuid,
Availability availability, String... retrieveAETs) {
Attributes refSOP = new Attributes(4);
refSOP.setString(Tag.RetrieveAETitle, VR.AE, retrieveAETs);
refSOP.setString(Tag.InstanceAvailability, VR.CS, availability.name());
refSOP.setString(Tag.ReferencedSOPClassUID, VR.UI, cuid);
refSOP.setString(Tag.ReferencedSOPInstanceUID, VR.UI, iuid);
return refSOP;
}
}