/* ***** 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) 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.conf.defaults;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import org.dcm4che3.conf.api.AttributeCoercion;
import org.dcm4che3.data.Code;
import org.dcm4che3.data.Issuer;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.imageio.codec.CompressionRule;
import org.dcm4che3.imageio.codec.ImageReaderFactory;
import org.dcm4che3.imageio.codec.ImageWriterFactory;
import org.dcm4che3.net.ApplicationEntity;
import org.dcm4che3.net.Connection;
import org.dcm4che3.net.DefaultTransferCapabilities;
import org.dcm4che3.net.Device;
import org.dcm4che3.net.DeviceType;
import org.dcm4che3.net.Dimse;
import org.dcm4che3.net.ExternalArchiveAEExtension;
import org.dcm4che3.net.QueryOption;
import org.dcm4che3.net.TCGroupConfigAEExtension;
import org.dcm4che3.net.TransferCapability;
import org.dcm4che3.net.audit.AuditLogger;
import org.dcm4che3.net.audit.AuditRecordRepository;
import org.dcm4che3.net.hl7.HL7Application;
import org.dcm4che3.net.hl7.HL7DeviceExtension;
import org.dcm4che3.net.imageio.ImageReaderExtension;
import org.dcm4che3.net.imageio.ImageWriterExtension;
import org.dcm4che3.net.web.WebServiceAEExtension;
import org.dcm4chee.archive.conf.ArchiveAEExtension;
import org.dcm4chee.archive.conf.ArchiveDeviceExtension;
import org.dcm4chee.archive.conf.ArchiveHL7ApplicationExtension;
import org.dcm4chee.archive.conf.ArchivingRule;
import org.dcm4chee.archive.conf.AttributeFilter;
import org.dcm4chee.archive.conf.DeletionRule;
import org.dcm4chee.archive.conf.Entity;
import org.dcm4chee.archive.conf.MPPSCreationRule;
import org.dcm4chee.archive.conf.MPPSEmulationAndStudyUpdateRule;
import org.dcm4chee.archive.conf.MetadataUpdateStrategy;
import org.dcm4chee.archive.conf.NoneIOCMChangeRequestorExtension;
import org.dcm4chee.archive.conf.PatientSelectorConfig;
import org.dcm4chee.archive.conf.PrivateDerivedFields;
import org.dcm4chee.archive.conf.PrivateTag;
import org.dcm4chee.archive.conf.QueryRetrieveView;
import org.dcm4chee.archive.conf.RejectionParam;
import org.dcm4chee.archive.conf.StoreAction;
import org.dcm4chee.storage.conf.Archiver;
import org.dcm4chee.storage.conf.Availability;
import org.dcm4chee.storage.conf.Container;
import org.dcm4chee.storage.conf.FileCache;
import org.dcm4chee.storage.conf.StorageDeviceExtension;
import org.dcm4chee.storage.conf.StorageSystem;
import org.dcm4chee.storage.conf.StorageSystemGroup;
import org.dcm4chee.storage.conf.SyncPolicy;
import static org.dcm4che3.net.TransferCapability.Role.SCP;
import static org.dcm4che3.net.TransferCapability.Role.SCU;
public class DefaultArchiveConfigurationFactory {
private static final String AET_FALLBACK_WEB_CLIENT = "FALLBACK-WEBC";
private static final String MPPSSCP = "MPPSSCP";
private static final String DCMEXT = "DCMEXT";
public static final String PIX_MANAGER = "HL7RCV^DCM4CHEE";
public static final String[] OTHER_DEVICES = {
"dcmqrscp",
"stgcmtscu",
"storescp",
"mppsscp",
"ianscp",
"storescu",
"mppsscu",
"findscu",
"getscu",
"fallbackWebClient",
"movescu",
"hl7snd",
"dcmext"
};
protected static final String[] OTHER_AES = {
"DCMQRSCP",
"STGCMTSCU",
"STORESCP",
MPPSSCP,
"IANSCP",
"STORESCU",
"MPPSSCU",
"FINDSCU",
"GETSCU",
AET_FALLBACK_WEB_CLIENT,
"MOVESCU",
"HL7SND",
DCMEXT
};
protected static final Issuer SITE_A =
new Issuer("Site A", "1.2.40.0.13.1.1.999.111.1111", "ISO");
protected static final Code INST_A =
new Code("111.1111", "99DCM4CHEE", null, "Site A");
protected static final int[] OTHER_PORTS = {
11113, 2763, // DCMQRSCP
11114, 2765, // STGCMTSCU
11115, 2766, // STORESCP
11116, 2767, // MPPSSCP
11117, 2768, // IANSCP
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // STORESCU
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // MPPSSCU
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // FINDSCU
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // GETSCU
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // AET_FALLBACK_WEB_CLIENT
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // MOVESCU
Connection.NOT_LISTENING, Connection.NOT_LISTENING, // HL7SND
11122, Connection.NOT_LISTENING // DCMEXT
};
private static final Issuer SITE_B =
new Issuer("Site B", "1.2.40.0.13.1.1.999.222.2222", "ISO");
protected static final Issuer[] OTHER_ISSUER = {
SITE_B, // DCMQRSCP
null, // STGCMTSCU
SITE_A, // STORESCP
SITE_A, // MPPSSCP
null, // IANSCP
SITE_A, // STORESCU
SITE_A, // MPPSSCU
SITE_A, // FINDSCU
SITE_A, // GETSCU
SITE_A, // AET_FALLBACK_WEB_CLIENT
null, // MOVESCU
SITE_A, // HL7SND
null, // DCMEXT
};
private static final Code INST_B =
new Code("222.2222", "99DCM4CHEE", null, "Site B");
protected static final Code[] OTHER_INST_CODES = {
INST_B, // DCMQRSCP
null, // STGCMTSCU
null, // STORESCP
null, // MPPSSCP
null, // IANSCP
INST_A, // STORESCU
null, // MPPSSCU
null, // FINDSCU
null, // GETSCU
null, // AET_FALLBACK_WEB_CLIENT
null, // MOVESCU
null, // HL7SND
null, // DCMEXT
};
private static final int PENDING_CMOVE_INTERVAL = 5000;
private static final int CONFIGURATION_STALE_TIMEOUT = 60;
private static final int WADO_ATTRIBUTES_STALE_TIMEOUT = 60;
private static final int QIDO_MAX_NUMBER_OF_RESULTS = 1000;
private static final int[] PATIENT_ATTRS = {
Tag.SpecificCharacterSet,
Tag.PatientName,
Tag.PatientID,
Tag.IssuerOfPatientID,
Tag.IssuerOfPatientIDQualifiersSequence,
Tag.PatientBirthDate,
Tag.PatientBirthTime,
Tag.PatientSex,
Tag.PatientInsurancePlanCodeSequence,
Tag.PatientPrimaryLanguageCodeSequence,
Tag.OtherPatientNames,
Tag.OtherPatientIDsSequence,
Tag.PatientBirthName,
Tag.PatientAge,
Tag.PatientSize,
Tag.PatientSizeCodeSequence,
Tag.PatientWeight,
Tag.PatientAddress,
Tag.PatientMotherBirthName,
Tag.MilitaryRank,
Tag.BranchOfService,
Tag.MedicalRecordLocator,
Tag.MedicalAlerts,
Tag.Allergies,
Tag.CountryOfResidence,
Tag.RegionOfResidence,
Tag.PatientTelephoneNumbers,
Tag.EthnicGroup,
Tag.Occupation,
Tag.SmokingStatus,
Tag.AdditionalPatientHistory,
Tag.PregnancyStatus,
Tag.LastMenstrualDate,
Tag.PatientReligiousPreference,
Tag.PatientSpeciesDescription,
Tag.PatientSpeciesCodeSequence,
Tag.PatientSexNeutered,
Tag.PatientBreedDescription,
Tag.PatientBreedCodeSequence,
Tag.BreedRegistrationSequence,
Tag.ResponsiblePerson,
Tag.ResponsiblePersonRole,
Tag.ResponsibleOrganization,
Tag.PatientComments,
Tag.ClinicalTrialSponsorName,
Tag.ClinicalTrialProtocolID,
Tag.ClinicalTrialProtocolName,
Tag.ClinicalTrialSiteID,
Tag.ClinicalTrialSiteName,
Tag.ClinicalTrialSubjectID,
Tag.ClinicalTrialSubjectReadingID,
Tag.PatientIdentityRemoved,
Tag.DeidentificationMethod,
Tag.DeidentificationMethodCodeSequence,
Tag.ClinicalTrialProtocolEthicsCommitteeName,
Tag.ClinicalTrialProtocolEthicsCommitteeApprovalNumber,
Tag.SpecialNeeds,
Tag.PertinentDocumentsSequence,
Tag.PatientState,
Tag.PatientClinicalTrialParticipationSequence,
Tag.ConfidentialityConstraintOnPatientDataDescription
};
private static final int[] STUDY_ATTRS = {
Tag.SpecificCharacterSet,
Tag.StudyDate,
Tag.StudyTime,
Tag.AccessionNumber,
Tag.IssuerOfAccessionNumberSequence,
Tag.ReferringPhysicianName,
Tag.StudyDescription,
Tag.ProcedureCodeSequence,
Tag.PatientAge,
Tag.PatientSize,
Tag.PatientSizeCodeSequence,
Tag.PatientWeight,
Tag.Occupation,
Tag.AdditionalPatientHistory,
Tag.PatientSexNeutered,
Tag.StudyInstanceUID,
Tag.StudyID
};
private static final Map<Integer, String> STUDY_PRIVATE_ATTRS = new TreeMap<>();
private static final int[] SERIES_ATTRS = {
Tag.SpecificCharacterSet,
Tag.Modality,
Tag.Manufacturer,
Tag.InstitutionName,
Tag.InstitutionCodeSequence,
Tag.StationName,
Tag.SeriesDescription,
Tag.InstitutionalDepartmentName,
Tag.PerformingPhysicianName,
Tag.ManufacturerModelName,
Tag.ReferencedPerformedProcedureStepSequence,
Tag.BodyPartExamined,
Tag.SeriesInstanceUID,
Tag.SeriesNumber,
Tag.Laterality,
Tag.PerformedProcedureStepStartDate,
Tag.PerformedProcedureStepStartTime,
Tag.RequestAttributesSequence,
Tag.ProtocolName
};
private static final int[] INSTANCE_ATTRS = {
Tag.SpecificCharacterSet,
Tag.ImageType,
Tag.SOPClassUID,
Tag.SOPInstanceUID,
Tag.ContentDate,
Tag.ContentTime,
Tag.ReferencedSeriesSequence,
Tag.InstanceNumber,
Tag.NumberOfFrames,
Tag.Rows,
Tag.Columns,
Tag.BitsAllocated,
Tag.ObservationDateTime,
Tag.ConceptNameCodeSequence,
Tag.VerifyingObserverSequence,
Tag.ReferencedRequestSequence,
Tag.CompletionFlag,
Tag.VerificationFlag,
Tag.DocumentTitle,
Tag.MIMETypeOfEncapsulatedDocument,
Tag.ContentLabel,
Tag.ContentDescription,
Tag.PresentationCreationDate,
Tag.PresentationCreationTime,
Tag.ContentCreatorName,
Tag.OriginalAttributesSequence,
Tag.IdenticalDocumentsSequence,
Tag.CurrentRequestedProcedureEvidenceSequence
};
private static final String ATTRIBUTE_COERCION_ENSURE_PID_XSL =
"${jboss.server.config.url}/dcm4chee-arc/ensure-pid.xsl";
private static final String ATTRIBUTE_COERCION_NULLIFY_PN_XSL =
"${jboss.server.config.url}/dcm4chee-arc/nullify-pn.xsl";
private static final String ATTRIBUTE_COERCION_ENMSURE_AET_XSL =
"${jboss.server.config.url}/dcm4chee-arc/ensure-retrieve-ae-title.xsl";
private static final String WADO_SR_TEMPLATE_URI =
"${jboss.server.config.url}/dcm4chee-arc/sr-report-html-dicom-native.xsl";
private static final String DCM4CHEE_ARC_KEY_JKS =
"${jboss.server.config.url}/dcm4chee-arc/key.jks";
private static final String HL7_ADT2DCM_XSL =
"${jboss.server.config.url}/dcm4chee-arc/hl7-adt2dcm.xsl";
private static final Code INCORRECT_WORKLIST_ENTRY_SELECTED =
new Code("110514", "DCM", null, "Incorrect worklist entry selected");
private static final Code REJECTED_FOR_QUALITY_REASONS =
new Code("113001", "DCM", null, "Rejected for Quality Reasons");
private static final Code REJECT_FOR_PATIENT_SAFETY_REASONS =
new Code("113037", "DCM", null, "Rejected for Patient Safety Reasons");
private static final Code INCORRECT_MODALITY_WORKLIST_ENTRY =
new Code("113038", "DCM", null, "Incorrect Modality Worklist Entry");
private static final Code DATA_RETENTION_POLICY_EXPIRED =
new Code("113039", "DCM", null, "Data Retention Policy Expired");
private static final Code REVOKE_REJECTION =
new Code("REVOKE_REJECTION", "99DCM4CHEE", null, "Restore rejected Instances");
public static final Code[] REJECTION_CODES = {
INCORRECT_WORKLIST_ENTRY_SELECTED,
REJECTED_FOR_QUALITY_REASONS,
REJECT_FOR_PATIENT_SAFETY_REASONS,
INCORRECT_MODALITY_WORKLIST_ENTRY,
DATA_RETENTION_POLICY_EXPIRED
};
public static final QueryRetrieveView REGULAR_USE_VIEW =
createQueryRetrieveView("regularUse",
new Code[]{REJECTED_FOR_QUALITY_REASONS},
new Code[]{DATA_RETENTION_POLICY_EXPIRED},
false);
public static final QueryRetrieveView HIDE_REJECTED_VIEW =
createQueryRetrieveView("hideRejected",
new Code[0],
new Code[]{DATA_RETENTION_POLICY_EXPIRED},
false);
public static final QueryRetrieveView TRASH_VIEW =
createQueryRetrieveView("trashView",
REJECTION_CODES,
new Code[0],
true);
private static final String[] HL7_MESSAGE_TYPES = {
"ADT^A02",
"ADT^A03",
"ADT^A06",
"ADT^A07",
"ADT^A08",
"ADT^A40",
"ORM^O01"
};
private static final int MPPS_EMULATOR_POLL_INTERVAL = 60;
private static final int ARCHIVING_POLL_INTERVAL = 60;
private static final int SYNC_LOCATION_STATUS_POLL_INTERVAL = 3600;
private static final String[] WADO_SUPPORTED_SR_SOP_CLASSES = {
UID.BasicTextSRStorage,
UID.EnhancedSRStorage,
UID.ComprehensiveSRStorage,
UID.Comprehensive3DSRStorage,
UID.ProcedureLogStorage,
UID.MammographyCADSRStorage,
UID.KeyObjectSelectionDocumentStorage,
UID.ChestCADSRStorage,
UID.XRayRadiationDoseSRStorage,
UID.RadiopharmaceuticalRadiationDoseSRStorage,
UID.ColonCADSRStorage,
UID.ImplantationPlanSRStorage
};
private static final int TIMEOUT = 5000;
public static QueryRetrieveView[] QUERY_RETRIEVE_VIEWS = {
HIDE_REJECTED_VIEW,
REGULAR_USE_VIEW,
TRASH_VIEW};
private static final String RETRIEVER_CACHE_STORAGE_SYSTEM_GROUP_TYPE = "RETRIEVER_CACHE";
//ASK BEA: SHE REALLY WANTS TO KICK THIS 2 TAGS IN THEIR PRIVATE ASS :-)
// static {
// STUDY_PRIVATE_ATTRS.put(ExtendedStudyDictionary.StudyLastUpdateDateTime, "EXTENDED STUDY");
// STUDY_PRIVATE_ATTRS.put(ExtendedStudyDictionary.StudyStatus, "EXTENDED STUDY");
// }
public DefaultArchiveConfigurationFactory() {
factoryParams = new FactoryParams();
}
public DefaultArchiveConfigurationFactory(FactoryParams params) {
factoryParams = params;
}
public int getTimeout() {
return getFactoryParams().socketTimeout == null ? TIMEOUT : getFactoryParams().socketTimeout;
}
public static class FactoryParams {
public String baseStoragePath = "/var/local/dcm4chee-arc/";
public boolean useGroupBasedTCConfig;
/**
* Useful for testing, not recommended for production
*/
public boolean generateUUIDsBasedOnName;
/**
* If not null, used to assign all timeouts to all connections.
* If null, default (5sec) is used.
* If set to 0, there is no timeout.
*/
public Integer socketTimeout;
}
FactoryParams factoryParams = new FactoryParams();
public FactoryParams getFactoryParams() {
return factoryParams;
}
private static QueryRetrieveView createQueryRetrieveView(String viewID,
Code[] showInstancesRejectedByCodes, Code[] hideRejectionNoteCodes,
boolean hideNotRejectedInstances) {
QueryRetrieveView view = new QueryRetrieveView();
view.setViewID(viewID);
view.setShowInstancesRejectedByCodes(showInstancesRejectedByCodes);
view.setHideRejectionNotesWithCodes(hideRejectionNoteCodes);
view.setHideNotRejectedInstances(hideNotRejectedInstances);
return view;
}
public Device createARRDevice(String name, Connection.Protocol protocol, int port) {
Device arrDevice = new Device(name);
AuditRecordRepository arr = new AuditRecordRepository();
arrDevice.addDeviceExtension(arr);
Connection auditUDP = new Connection("audit-udp", "localhost", port, getTimeout());
auditUDP.setProtocol(protocol);
arrDevice.addConnection(auditUDP);
arr.addConnection(auditUDP);
if (factoryParams.generateUUIDsBasedOnName) {
auditUDP.setUuid(makeUuidFromName(name + "auditUDP"));
}
return arrDevice;
}
protected Device createDevice(String name) {
return init(new Device(name), null, null);
}
private static Device init(Device device, Issuer issuer, Code institutionCode) {
String name = device.getDeviceName();
//TODO: implement
// device.setThisNodeCertificates(config.deviceRef(name),
// (X509Certificate) keystore.getCertificate(name));
device.setIssuerOfPatientID(issuer);
device.setIssuerOfAccessionNumber(issuer);
if (institutionCode != null) {
device.setInstitutionNames(institutionCode.getCodeMeaning());
device.setInstitutionCodes(institutionCode);
}
return device;
}
protected Device createDevice(String name,
Issuer issuer, Code institutionCode, String aet,
String host, int port, int tlsPort) {
Device device = init(new Device(name), issuer, institutionCode);
if (name.equalsIgnoreCase(OTHER_DEVICES[0])
|| name.equalsIgnoreCase("dcm4chee-arc"))
device.setPrimaryDeviceTypes(new String[]{DeviceType.ARCHIVE.toString()});
ApplicationEntity ae = new ApplicationEntity(aet);
// For MPPSSCP add a universal transfer capability
if (MPPSSCP.equals(aet)) {
TransferCapability tc = new TransferCapability();
tc.setCommonName("All");
tc.setSopClass("*");
tc.setRole(SCP);
tc.setTransferSyntaxes("*");
ae.addTransferCapability(tc);
}
if (DCMEXT.equals(aet)) {
for (TCGroupConfigAEExtension.DefaultGroup defaultGroup : TCGroupConfigAEExtension.DefaultGroup.values()) {
DefaultArchiveConfigurationFactory.addTCsForDefaultGroup(ae, SCP, defaultGroup);
DefaultArchiveConfigurationFactory.addTCsForDefaultGroup(ae, SCU, defaultGroup);
}
}
ExternalArchiveAEExtension externalArchiveExt =
new ExternalArchiveAEExtension();
if (containsArchiveType(device.getPrimaryDeviceTypes()))
ae.addAEExtension(externalArchiveExt);
externalArchiveExt.setAeFetchPriority(0);
externalArchiveExt.setPrefersForwarding(false);
ae.setAssociationAcceptor(true);
device.addApplicationEntity(ae);
Connection dicom = new Connection("dicom", host, port, getTimeout());
device.addConnection(dicom);
ae.addConnection(dicom);
Connection dicomTLS = new Connection("dicom-tls", host, tlsPort, getTimeout());
dicomTLS.setTlsCipherSuites(
Connection.TLS_RSA_WITH_AES_128_CBC_SHA,
Connection.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
device.addConnection(dicomTLS);
ae.addConnection(dicomTLS);
if (factoryParams.generateUUIDsBasedOnName) {
ae.setUuid(makeUuidFromName(aet));
dicom.setUuid(makeUuidFromName(aet + "dicom"));
dicomTLS.setUuid(makeUuidFromName(aet + "dicomtls"));
}
return device;
}
private static boolean containsArchiveType(String[] primaryDeviceTypes) {
for (String str : primaryDeviceTypes)
if (str == DeviceType.ARCHIVE.toString())
return true;
return false;
}
protected Device createHL7Device(String name,
Issuer issuer, Code institutionCode, String appName,
String host, int port, int tlsPort) {
Device device = new Device(name);
HL7DeviceExtension hl7Device = new HL7DeviceExtension();
device.addDeviceExtension(hl7Device);
init(device, issuer, institutionCode);
HL7Application hl7app = new HL7Application(appName);
hl7Device.addHL7Application(hl7app);
Connection hl7 = new Connection("hl7", host, port, getTimeout());
hl7.setProtocol(Connection.Protocol.HL7);
device.addConnection(hl7);
hl7app.addConnection(hl7);
Connection hl7TLS = new Connection("hl7-tls", host, tlsPort, getTimeout());
hl7TLS.setProtocol(Connection.Protocol.HL7);
hl7TLS.setTlsCipherSuites(
Connection.TLS_RSA_WITH_AES_128_CBC_SHA,
Connection.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
device.addConnection(hl7TLS);
hl7app.addConnection(hl7TLS);
if (factoryParams.generateUUIDsBasedOnName) {
hl7.setUuid(makeUuidFromName(name + "hl7"));
hl7TLS.setUuid(makeUuidFromName(name + "hl7tls"));
}
return device;
}
public Device createArchiveDevice(String name, Device arrDevice) {
//KeyStore keyStore = SSLManagerFactory.loadKeyStore("JKS", ResourceLocator.resourceURL("cacerts.jks"), "secret");
// TODO: configure keystore
Device device = new Device(name);
device.setPrimaryDeviceTypes(new String[]{DeviceType.ARCHIVE.toString()});
Connection dicom = new Connection("dicom", "localhost", 11112, getTimeout());
dicom.setBindAddress("0.0.0.0");
dicom.setClientBindAddress("0.0.0.0");
dicom.setMaxOpsInvoked(0);
dicom.setMaxOpsPerformed(0);
device.addConnection(dicom);
Connection dicomTLS = new Connection("dicom-tls", "localhost", 2762, getTimeout());
dicomTLS.setBindAddress("0.0.0.0");
dicomTLS.setClientBindAddress("0.0.0.0");
dicomTLS.setMaxOpsInvoked(0);
dicomTLS.setMaxOpsPerformed(0);
dicomTLS.setTlsCipherSuites(
Connection.TLS_RSA_WITH_AES_128_CBC_SHA,
Connection.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
device.addConnection(dicomTLS);
if (factoryParams.generateUUIDsBasedOnName) {
dicom.setUuid(makeUuidFromName(name + "dicom"));
dicomTLS.setUuid(makeUuidFromName(name + "dicomTls"));
}
addArchiveDeviceExtension(device);
addHL7DeviceExtension(device);
addAuditLogger(device, arrDevice);
addStorageDeviceExtension(device, factoryParams.baseStoragePath);
addNoneIOCMDeviceExtension(device);
device.addDeviceExtension(new ImageReaderExtension(ImageReaderFactory.getDefault()));
device.addDeviceExtension(new ImageWriterExtension(ImageWriterFactory.getDefault()));
device.setManufacturer("dcm4che.org");
device.setManufacturerModelName("dcm4chee-arc");
device.setSoftwareVersions("4.2.0.Alpha3");
device.setKeyStoreURL(DCM4CHEE_ARC_KEY_JKS);
device.setTrustStoreURL(DCM4CHEE_ARC_KEY_JKS);
device.setKeyStoreType("JKS");
device.setTrustStoreType("JKS");
device.setTrustStorePin("secret");
device.setKeyStorePin("secret");
// device.setThisNodeCertificates(config.deviceRef(name),
// (X509Certificate) keystore.getCertificate(name));
// for (String other : OTHER_DEVICES)
// device.setAuthorizedNodeCertificates(config.deviceRef(other),
// (X509Certificate) keystore.getCertificate(other));
ApplicationEntity DCM4CHEE_AE =
createAE("DCM4CHEE", dicom, dicomTLS, HIDE_REJECTED_VIEW, null, PIX_MANAGER, null, "DEFAULT");
device.addApplicationEntity(DCM4CHEE_AE);
device.setDefaultAE(DCM4CHEE_AE);
device.addApplicationEntity(
createQRAE("DCM4CHEE_ADMIN", dicom, dicomTLS, REGULAR_USE_VIEW, null, PIX_MANAGER));
device.addApplicationEntity(
createAE("DCM4CHEE_FETCH", dicom, dicomTLS, HIDE_REJECTED_VIEW, null, PIX_MANAGER, RETRIEVER_CACHE_STORAGE_SYSTEM_GROUP_TYPE, null));
device.addApplicationEntity(
createQRAE("DCM4CHEE_TRASH", dicom, dicomTLS, TRASH_VIEW, null, PIX_MANAGER));
return device;
}
private void addNoneIOCMDeviceExtension(Device device) {
NoneIOCMChangeRequestorExtension ext = new NoneIOCMChangeRequestorExtension();
ext.setGracePeriod(3600);
ext.setNoneIOCMChangeRequestorDevices(new ArrayList<Device>());
ext.setNoneIOCMModalityDevices(new ArrayList<Device>());
device.addDeviceExtension(ext);
}
private static void addStorageDeviceExtension(Device device, String baseStoragePath) {
StorageSystem fs1 = new StorageSystem();
fs1.setStorageSystemID("fs1");
fs1.setProviderName("org.dcm4chee.storage.filesystem");
fs1.setStorageSystemPath(baseStoragePath +
"fs1");
fs1.setAvailability(Availability.ONLINE);
fs1.setSyncPolicy(SyncPolicy.ON_ASSOCIATION_CLOSE);
StorageSystem arc = new StorageSystem();
arc.setStorageSystemID("nearline");
arc.setProviderName("org.dcm4chee.storage.filesystem");
arc.setStorageSystemPath(baseStoragePath +
"nearline");
arc.setAvailability(Availability.NEARLINE);
Map<String, String> exts = new LinkedHashMap<String, String>();
exts.put(".archived", "ARCHIVED");
arc.setStatusFileExtensions(exts);
StorageSystem metadata = new StorageSystem();
metadata.setStorageSystemID("metadata");
metadata.setProviderName("org.dcm4chee.storage.filesystem");
metadata.setStorageSystemPath(baseStoragePath +
"metadata");
metadata.setAvailability(Availability.ONLINE);
Container container = new Container();
container.setProviderName("org.dcm4chee.storage.zip");
FileCache fileCache = new FileCache();
fileCache.setProviderName("org.dcm4chee.storage.filecache");
fileCache.setFileCacheRootDirectory(
baseStoragePath +
"nearline-cache/data");
fileCache.setJournalRootDirectory(
baseStoragePath +
"nearline-cache");
fileCache.setMinFreeSpace("100MiB");
fileCache.setCacheAlgorithm(FileCache.Algorithm.LRU);
StorageSystemGroup online = new StorageSystemGroup();
online.setGroupID("DEFAULT");
online.setRetrieveAETs(new String[]{"DCM4CHEE"});
online.setDigestAlgorithm("MD5");
online.addStorageSystem(fs1);
online.setBaseStorageAccessTime(1000);
online.setStorageFilePathFormat("{now,date,yyyy/MM/dd}/{0020000D,hash}/{0020000E,hash}/{00080018,hash}");
online.setActiveStorageSystemIDs(fs1.getStorageSystemID());
StorageSystemGroup nearline = new StorageSystemGroup();
nearline.setRetrieveAETs(new String[]{"DCM4CHEE"});
nearline.setGroupID("ARCHIVE");
nearline.addStorageSystem(arc);
nearline.setBaseStorageAccessTime(2000);
nearline.setStorageFilePathFormat("{now,date,yyyy/MM/dd}/{0020000D,hash}/{0020000E,hash}/{now,date,HHmmssSSS}.zip");
nearline.setActiveStorageSystemIDs(arc.getStorageSystemID());
nearline.setContainer(container);
nearline.setFileCache(fileCache);
StorageSystemGroup metadataG = new StorageSystemGroup();
metadataG.setGroupID("METADATA");
metadataG.addStorageSystem(metadata);
metadataG.setBaseStorageAccessTime(0);
metadataG.setStorageFilePathFormat("{now,date,yyyy/MM/dd}/{0020000D,hash}/{0020000E,hash}/{00080018,hash}");
metadataG.setActiveStorageSystemIDs(metadata.getStorageSystemID());
StorageSystem retrieveFs1 = new StorageSystem();
retrieveFs1.setStorageSystemID("retrieve_fs1");
retrieveFs1.setProviderName("org.dcm4chee.storage.filesystem");
retrieveFs1.setStorageSystemPath(baseStoragePath + "retrieve_fs1");
retrieveFs1.setAvailability(Availability.ONLINE);
StorageSystemGroup retrieverCache = new StorageSystemGroup();
retrieverCache.setGroupID("RETRIEVER_CACHE");
retrieverCache.setStorageSystemGroupType(RETRIEVER_CACHE_STORAGE_SYSTEM_GROUP_TYPE);
retrieverCache.setRetrieveAETs(new String[]{"DCM4CHEE_RETRIEVE"});
retrieverCache.setDigestAlgorithm("MD5");
retrieverCache.addStorageSystem(retrieveFs1);
retrieverCache.setBaseStorageAccessTime(1000);
retrieverCache.setStorageFilePathFormat("{now,date,yyyy/MM/dd}/{0020000D,hash}/{0020000E,hash}/{00080018,hash}");
retrieverCache.setActiveStorageSystemIDs(retrieveFs1.getStorageSystemID());
Archiver archiver = new Archiver();
archiver.setObjectStatus("TO_ARCHIVE");
StorageDeviceExtension ext = new StorageDeviceExtension();
ext.setArchiver(archiver);
ext.addStorageSystemGroup(online);
ext.addStorageSystemGroup(nearline);
ext.addStorageSystemGroup(metadataG);
ext.addStorageSystemGroup(retrieverCache);
device.addDeviceExtension(ext);
}
private void addAuditLogger(Device device, Device arrDevice) {
Connection auditUDP = new Connection("audit-udp-initiating", "localhost");
auditUDP.setProtocol(Connection.Protocol.SYSLOG_UDP);
device.addConnection(auditUDP);
AuditLogger auditLogger = new AuditLogger();
device.addDeviceExtension(auditLogger);
auditLogger.addConnection(auditUDP);
auditLogger.setAuditSourceTypeCodes("4");
auditLogger.addAuditRecordRepositoryDevice(arrDevice);
if (factoryParams.generateUUIDsBasedOnName) {
auditUDP.setUuid(makeUuidFromName(device.getDeviceName() + "audipudp"));
}
}
private void addHL7DeviceExtension(Device device) {
HL7DeviceExtension ext = new HL7DeviceExtension();
device.addDeviceExtension(ext);
Connection hl7 = new Connection("hl7", "localhost", 2575, getTimeout());
hl7.setBindAddress("0.0.0.0");
hl7.setProtocol(Connection.Protocol.HL7);
device.addConnection(hl7);
Connection hl7TLS = new Connection("hl7-tls", "localhost", 12575, getTimeout());
hl7TLS.setBindAddress("0.0.0.0");
hl7TLS.setProtocol(Connection.Protocol.HL7);
hl7TLS.setTlsCipherSuites(
Connection.TLS_RSA_WITH_AES_128_CBC_SHA,
Connection.TLS_RSA_WITH_3DES_EDE_CBC_SHA);
device.addConnection(hl7TLS);
if (factoryParams.generateUUIDsBasedOnName) {
hl7.setUuid(makeUuidFromName(device.getDeviceName() + "hl7"));
hl7TLS.setUuid(makeUuidFromName(device.getDeviceName() + "hl7Tls"));
}
HL7Application hl7App = new HL7Application("*");
ArchiveHL7ApplicationExtension hl7AppExt = new ArchiveHL7ApplicationExtension();
hl7App.addHL7ApplicationExtension(hl7AppExt);
hl7App.setAcceptedMessageTypes(HL7_MESSAGE_TYPES);
hl7App.setHL7DefaultCharacterSet("8859/1");
hl7AppExt.addTemplatesURI("adt2dcm", HL7_ADT2DCM_XSL);
ext.addHL7Application(hl7App);
hl7App.addConnection(hl7);
hl7App.addConnection(hl7TLS);
}
private static void addArchiveDeviceExtension(Device device) {
ArchiveDeviceExtension ext = new ArchiveDeviceExtension();
device.addDeviceExtension(ext);
ext.setIncorrectWorklistEntrySelectedCode(INCORRECT_WORKLIST_ENTRY_SELECTED);
ext.setFuzzyAlgorithmClass("org.dcm4che3.soundex.ESoundex");
ext.setConfigurationStaleTimeout(CONFIGURATION_STALE_TIMEOUT);
ext.setWadoAttributesStaleTimeout(WADO_ATTRIBUTES_STALE_TIMEOUT);
ext.setRejectionParams(createRejectionNotes());
ext.setQueryRetrieveViews(QUERY_RETRIEVE_VIEWS);
ext.setMppsEmulationPollInterval(MPPS_EMULATOR_POLL_INTERVAL);
ext.setArchivingSchedulerPollInterval(ARCHIVING_POLL_INTERVAL);
ext.setSyncLocationStatusPollInterval(SYNC_LOCATION_STATUS_POLL_INTERVAL);
ext.setDeletionServicePollInterval(60);
ext.setDeleteServiceAllowedInterval("0-23");
ext.setSyncLocationStatusStorageSystemGroupIDs("ARCHIVE");
ext.setAttributeFilter(Entity.Patient, new AttributeFilter(PATIENT_ATTRS, new TreeMap(), MetadataUpdateStrategy.COERCE_MERGE));
ext.setAttributeFilter(Entity.Study, new AttributeFilter(STUDY_ATTRS, STUDY_PRIVATE_ATTRS, MetadataUpdateStrategy.COERCE_MERGE));
ext.setAttributeFilter(Entity.Series, new AttributeFilter(SERIES_ATTRS, new TreeMap(), MetadataUpdateStrategy.COERCE_MERGE));
ext.setAttributeFilter(Entity.Instance, new AttributeFilter(INSTANCE_ATTRS, new TreeMap(), MetadataUpdateStrategy.OVERWRITE));
ext.setFetchAETitle("DCM4CHEE_FETCH");
ext.addPrivateDerivedField(new PrivateTag(PrivateDerivedFields.NAMES
.StudyUpdateTimeDerivedField.name(), "7FD91060",
"studyUpdate"));
ext.setVisibleImageSRClasses(DefaultTransferCapabilities.IMAGE_CUIDS);
ext.setUseWhitelistOfVisibleImageSRClasses(false);
ext.setUseNullForEmptyQueryFields(true);
ext.addMppsEmulationRule(new MPPSEmulationAndStudyUpdateRule("Default EmulateAll Rule", new LinkedHashSet<ApplicationEntity>(), 10, MPPSCreationRule.ALWAYS));
DeletionRule deletionRule = new DeletionRule();
deletionRule.setCommonName("Deletion Rule");
deletionRule.setStorageSystemGroupID("DEFAULT");
deletionRule.setArchivedOnGroups(new String[]{"ARCHIVE"});
deletionRule.setDeleteAsMuchAsPossible(false);
deletionRule.setMinTimeStudyNotAccessed(30);
deletionRule.setMinTimeStudyNotAccessedUnit("DAYS");
deletionRule.setDeletionThreshold("7:1h;19:24h");
deletionRule.setNumberOfArchivedCopies("1");
deletionRule.setSafeArchivingType("*");
ext.addDeletionRule(deletionRule);
// default fallback for web client source AETs
Map<String, String> hostNameToAETitleMap = new HashMap<>();
hostNameToAETitleMap.put("*", AET_FALLBACK_WEB_CLIENT);
ext.setHostNameToAETitleMap(hostNameToAETitleMap);
}
private static RejectionParam[] createRejectionNotes() {
return new RejectionParam[]{
createRejectionParam(REJECTED_FOR_QUALITY_REASONS, false,
StoreAction.IGNORE),
createRejectionParam(REJECT_FOR_PATIENT_SAFETY_REASONS, false,
null, REJECTED_FOR_QUALITY_REASONS),
createRejectionParam(INCORRECT_WORKLIST_ENTRY_SELECTED, false,
null, REJECTED_FOR_QUALITY_REASONS),
createRejectionParam(INCORRECT_MODALITY_WORKLIST_ENTRY, false,
null, REJECTED_FOR_QUALITY_REASONS),
createRejectionParam(DATA_RETENTION_POLICY_EXPIRED, false,
StoreAction.REPLACE, REJECTED_FOR_QUALITY_REASONS),
createRejectionParam(REVOKE_REJECTION, true, null,
REJECTED_FOR_QUALITY_REASONS,
REJECT_FOR_PATIENT_SAFETY_REASONS,
INCORRECT_WORKLIST_ENTRY_SELECTED,
INCORRECT_MODALITY_WORKLIST_ENTRY,
DATA_RETENTION_POLICY_EXPIRED)
};
}
private static RejectionParam createRejectionParam(Code title,
boolean revokeRejection, StoreAction storeAction,
Code... overwritePreviousRejection) {
RejectionParam param = new RejectionParam();
param.setRejectionNoteTitle(title);
param.setRevokeRejection(revokeRejection);
param.setAcceptPreviousRejectedInstance(storeAction);
param.setOverwritePreviousRejection(overwritePreviousRejection);
param.setRetentionTime(-1);
param.setRetentionTimeUnit(TimeUnit.DAYS);
return param;
}
private ApplicationEntity createAE(String aet,
Connection dicom, Connection dicomTLS,
QueryRetrieveView queryRetrieveView,
String pixConsumer, String pixManager,
String storageSystemGroupType,
String storageSystemGroupID) {
ApplicationEntity ae = new ApplicationEntity(aet);
if (factoryParams.generateUUIDsBasedOnName)
ae.setUuid(makeUuidFromName(aet));
ae.addConnection(dicom);
ae.addConnection(dicomTLS);
ArchiveAEExtension aeExt = new ArchiveAEExtension();
ae.addAEExtension(aeExt);
ae.setAssociationAcceptor(true);
ae.setAssociationInitiator(true);
if (storageSystemGroupID != null) {
aeExt.setStorageSystemGroupID(storageSystemGroupID);
}
if (storageSystemGroupType != null) {
aeExt.setStorageSystemGroupType(storageSystemGroupType);
}
aeExt.setMetaDataStorageSystemGroupID("METADATA");
aeExt.setSpoolDirectoryPath("spool");
aeExt.setPreserveSpoolFileOnFailure(true);
aeExt.setSuppressWarningCoercionOfDataElements(false);
aeExt.setCheckNonDBAttributesOnStorage(false);
aeExt.setIgnoreDuplicatesOnStorage(true);
aeExt.setMatchUnknown(true);
aeExt.setSendPendingCGet(true);
aeExt.setSendPendingCMoveInterval(PENDING_CMOVE_INTERVAL);
aeExt.setQueryRetrieveViewID(queryRetrieveView.getViewID());
aeExt.setWadoSRTemplateURI(WADO_SR_TEMPLATE_URI);
aeExt.setWadoSupportedSRClasses(WADO_SUPPORTED_SR_SOP_CLASSES);
aeExt.setQIDOMaxNumberOfResults(QIDO_MAX_NUMBER_OF_RESULTS);
aeExt.setQidoClientAcceptType("application/json");
aeExt.setDefaultExternalRetrieveAETAvailability(Availability.ONLINE);
aeExt.addAttributeCoercion(new AttributeCoercion(
"Supplement missing PID",
null,
Dimse.C_STORE_RQ,
SCP,
new String[]{"ENSURE_PID"},
null, // Source Device Names
ATTRIBUTE_COERCION_ENSURE_PID_XSL));
aeExt.addAttributeCoercion(new AttributeCoercion(
"Remove person names",
null,
Dimse.C_STORE_RQ,
SCU,
new String[]{"WITHOUT_PN"},
null, // Source Device Names
ATTRIBUTE_COERCION_NULLIFY_PN_XSL));
aeExt.addAttributeCoercion(new AttributeCoercion(
"Ensure Retrieve AETitle",
null,
Dimse.C_FIND_RSP,
SCP,
new String[]{"WITHOUT_PN"},
null, // Source Device Names
ATTRIBUTE_COERCION_ENMSURE_AET_XSL));
aeExt.addCompressionRule(new CompressionRule(
"JPEG 8-bit Lossy",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"RGB"},
new int[]{8}, // Bits Stored
0, // Pixel Representation
new String[]{"JPEG_LOSSY"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGBaseline1,
"compressionQuality=0.8",
"maxPixelValueError=10",
"avgPixelValueBlockSize=8"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG 12-bit Lossy",
new String[]{
"MONOCHROME1",
"MONOCHROME2",},
new int[]{9, 10, 11, 12}, // Bits Stored
0, // Pixel Representation
new String[]{"JPEG_LOSSY"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGExtended24,
"compressionQuality=0.8",
"maxPixelValueError=20",
"avgPixelValueBlockSize=8"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_LOSSLESS"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGLossless,
"maxPixelValueError=0"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG LS Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_LS"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGLSLossless,
"maxPixelValueError=0"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG 2000 Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_2000"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEG2000LosslessOnly,
"maxPixelValueError=0",
"compressionType=JPEG2000"
));
ArchivingRule archivingRule = new ArchivingRule();
archivingRule.setCommonName("Archiving Rule");
archivingRule.setStorageSystemGroupIDs("ARCHIVE");
archivingRule.setDelayAfterInstanceStored(60);
archivingRule.setAeTitles(new String[]{"ARCHIVE"});
aeExt.addArchivingRule(archivingRule);
addTCs(ae,
EnumSet.allOf(TCGroupConfigAEExtension.DefaultGroup.class),
EnumSet.of(TCGroupConfigAEExtension.DefaultGroup.STORAGE,
TCGroupConfigAEExtension.DefaultGroup.PPS,
TCGroupConfigAEExtension.DefaultGroup.STORAGE_COMMITMENT));
aeExt.setReturnOtherPatientIDs(true);
aeExt.setReturnOtherPatientNames(true);
aeExt.setLocalPIXConsumerApplication(pixConsumer);
aeExt.setRemotePIXManagerApplication(pixManager);
// patient selector
PatientSelectorConfig ps = new PatientSelectorConfig();
ps.setPatientSelectorClassName("org.dcm4chee.archive.patient.DemographicsPatientSelector");
Map<String, String> sels = new TreeMap<>();
sels.put("familyName", "BROAD");
sels.put("givenName", "BROAD");
ps.setPatientSelectorProperties(sels);
aeExt.setPatientSelectorConfig(ps);
ExternalArchiveAEExtension extArcAEExt = new ExternalArchiveAEExtension();
ae.addAEExtension(extArcAEExt);
extArcAEExt.setAeFetchPriority(0);
WebServiceAEExtension wsAEExt = new WebServiceAEExtension();
ae.addAEExtension(wsAEExt);
wsAEExt.setQidoRSBaseURL("http://localhost:8080/dicom-web/" + aet);
wsAEExt.setWadoRSBaseURL("http://localhost:8080/dicom-web/" + aet);
wsAEExt.setStowRSBaseURL("http://localhost:8080/dicom-web/" + aet);
wsAEExt.setWadoURIBaseURL("http://localhost:8080/dicom-web/" + aet);
return ae;
}
private String makeUuidFromName(String aet) {
try {
return javax.xml.bind.DatatypeConverter.printHexBinary(MessageDigest.getInstance("SHA-1").digest(aet.getBytes()));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
private ApplicationEntity createQRAE(String aet,
Connection dicom, Connection dicomTLS,
QueryRetrieveView queryRetrieveView,
String pixConsumer, String pixManager) {
ApplicationEntity ae = new ApplicationEntity(aet);
if (factoryParams.generateUUIDsBasedOnName)
ae.setUuid(makeUuidFromName(aet));
ae.addConnection(dicom);
ae.addConnection(dicomTLS);
ArchiveAEExtension aeExt = new ArchiveAEExtension();
ae.addAEExtension(aeExt);
ae.setAssociationAcceptor(true);
ae.setAssociationInitiator(true);
aeExt.setMatchUnknown(true);
aeExt.setSendPendingCGet(true);
aeExt.setSendPendingCMoveInterval(PENDING_CMOVE_INTERVAL);
aeExt.setQueryRetrieveViewID(queryRetrieveView.getViewID());
aeExt.setWadoSRTemplateURI(WADO_SR_TEMPLATE_URI);
aeExt.setWadoSupportedSRClasses(WADO_SUPPORTED_SR_SOP_CLASSES);
aeExt.setQIDOMaxNumberOfResults(QIDO_MAX_NUMBER_OF_RESULTS);
addTCs(ae,
EnumSet.of(
TCGroupConfigAEExtension.DefaultGroup.QUERY,
TCGroupConfigAEExtension.DefaultGroup.RETRIEVE,
TCGroupConfigAEExtension.DefaultGroup.MWL),
EnumSet.of(TCGroupConfigAEExtension.DefaultGroup.STORAGE));
aeExt.setReturnOtherPatientIDs(true);
aeExt.setReturnOtherPatientNames(true);
aeExt.setLocalPIXConsumerApplication(pixConsumer);
aeExt.setRemotePIXManagerApplication(pixManager);
return ae;
}
public ApplicationEntity createAnotherAE(String aet,
String pixConsumer, String pixManager) {
ApplicationEntity ae = new ApplicationEntity(aet);
if (factoryParams.generateUUIDsBasedOnName)
ae.setUuid(makeUuidFromName(aet));
ArchiveAEExtension aeExt = new ArchiveAEExtension();
ae.addAEExtension(aeExt);
ae.setAssociationAcceptor(true);
ae.setAssociationInitiator(true);
aeExt.setStorageSystemGroupID("notDEFAULT");
aeExt.setSpoolDirectoryPath("archive/anotherspool");
aeExt.setPreserveSpoolFileOnFailure(true);
aeExt.setSuppressWarningCoercionOfDataElements(false);
aeExt.setMatchUnknown(true);
aeExt.setSendPendingCGet(true);
aeExt.setSendPendingCMoveInterval(4000);
aeExt.setQIDOMaxNumberOfResults(500);
aeExt.addAttributeCoercion(new AttributeCoercion(
"Supplement missing PID",
null,
Dimse.C_ECHO_RQ,
SCU,
new String[]{"ENSURE_PID"},
null, // Source Device Names
ATTRIBUTE_COERCION_ENSURE_PID_XSL));
aeExt.addAttributeCoercion(new AttributeCoercion(
"Remove person names",
null,
Dimse.C_STORE_RQ,
SCP,
new String[]{"WITHOUT_PN"},
null, // Source Device Names
ATTRIBUTE_COERCION_NULLIFY_PN_XSL));
aeExt.addCompressionRule(new CompressionRule(
"JPEG Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_LOSSLESS"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGLossless,
"maxPixelValueError=0"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG LS Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_LS"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEGLSLossless,
"maxPixelValueError=0"
));
aeExt.addCompressionRule(new CompressionRule(
"JPEG 2000 Lossless",
new String[]{
"MONOCHROME1",
"MONOCHROME2",
"PALETTE COLOR",
"RGB",
"YBR_FULL"},
new int[]{8, 9, 10, 11, 12, 13, 14, 15, 16}, // Bits Stored
-1, // Pixel Representation
new String[]{"JPEG_2000"}, // Source AETs
null, // SOP Classes
null, // Image Types
null, // Body Parts
UID.JPEG2000LosslessOnly,
"maxPixelValueError=0",
"compressionType=JPEG2000"
));
addTCs(ae,
EnumSet.allOf(TCGroupConfigAEExtension.DefaultGroup.class),
EnumSet.of(TCGroupConfigAEExtension.DefaultGroup.STORAGE, TCGroupConfigAEExtension.DefaultGroup.PPS));
aeExt.setReturnOtherPatientIDs(false);
aeExt.setReturnOtherPatientNames(true);
aeExt.setLocalPIXConsumerApplication(pixConsumer);
aeExt.setRemotePIXManagerApplication(pixManager);
return ae;
}
private void addTCs(ApplicationEntity ae,
Set<TCGroupConfigAEExtension.DefaultGroup> scpGroups,
Set<TCGroupConfigAEExtension.DefaultGroup> scuGroups) {
if (factoryParams.useGroupBasedTCConfig) {
TCGroupConfigAEExtension tcGroupConfig = ae.getAEExtension(TCGroupConfigAEExtension.class);
if (tcGroupConfig == null) {
tcGroupConfig = new TCGroupConfigAEExtension();
ae.addAEExtension(tcGroupConfig);
}
for (TCGroupConfigAEExtension.DefaultGroup scpGroup : scpGroups)
tcGroupConfig.getScpTCs().put(scpGroup.name(), new TCGroupConfigAEExtension.TCGroupDetails());
for (TCGroupConfigAEExtension.DefaultGroup scuGroup : scuGroups)
tcGroupConfig.getScuTCs().put(scuGroup.name(), new TCGroupConfigAEExtension.TCGroupDetails());
} else {
for (TCGroupConfigAEExtension.DefaultGroup group : scpGroups)
addTCsForDefaultGroup(ae, SCP, group);
for (TCGroupConfigAEExtension.DefaultGroup group : scuGroups)
addTCsForDefaultGroup(ae, SCU, group);
}
}
private static void addTCsForDefaultGroup(ApplicationEntity ae, TransferCapability.Role role, TCGroupConfigAEExtension.DefaultGroup group) {
switch (group) {
case STORAGE:
DefaultTransferCapabilities.addTCs(ae, null, role, DefaultTransferCapabilities.IMAGE_CUIDS, DefaultTransferCapabilities.IMAGE_TSUIDS);
DefaultTransferCapabilities.addTCs(ae, null, role, DefaultTransferCapabilities.VIDEO_CUIDS, DefaultTransferCapabilities.VIDEO_TSUIDS);
DefaultTransferCapabilities.addTCs(ae, null, role, DefaultTransferCapabilities.OTHER_CUIDS, DefaultTransferCapabilities.OTHER_TSUIDS);
DefaultTransferCapabilities.addTC(ae, null, role, UID.InstanceAvailabilityNotificationSOPClass, UID.ImplicitVRLittleEndian);
DefaultTransferCapabilities.addTC(ae, null, role, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
case PPS:
DefaultTransferCapabilities.addTC(ae, null, role, UID.ModalityPerformedProcedureStepSOPClass, UID.ImplicitVRLittleEndian);
DefaultTransferCapabilities.addTC(ae, null, SCU, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
case QUERY:
DefaultTransferCapabilities.addTCs(ae, EnumSet.allOf(QueryOption.class), role, DefaultTransferCapabilities.QUERY_CUIDS, DefaultTransferCapabilities.OTHER_TSUIDS);
DefaultTransferCapabilities.addTC(ae, null, role, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
case RETRIEVE:
DefaultTransferCapabilities.addTCs(ae, EnumSet.of(QueryOption.RELATIONAL), role, DefaultTransferCapabilities.RETRIEVE_CUIDS, DefaultTransferCapabilities.OTHER_TSUIDS);
DefaultTransferCapabilities.addTC(ae, null, role, UID.CompositeInstanceRetrieveWithoutBulkDataGET, UID.ImplicitVRLittleEndian);
DefaultTransferCapabilities.addTC(ae, null, role, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
case MWL:
DefaultTransferCapabilities.addTC(ae, EnumSet.allOf(QueryOption.class), role, UID.ModalityWorklistInformationModelFIND, UID.ImplicitVRLittleEndian);
DefaultTransferCapabilities.addTC(ae, null, role, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
case STORAGE_COMMITMENT:
DefaultTransferCapabilities.addTC(ae, null, role, UID.StorageCommitmentPushModelSOPClass, UID.ImplicitVRLittleEndian);
DefaultTransferCapabilities.addTC(ae, null, role, UID.VerificationSOPClass, UID.ImplicitVRLittleEndian);
break;
}
}
}