From ed620ec374698b08afd37b964be6a1d38815e336 Mon Sep 17 00:00:00 2001 From: gunterze Date: Mon, 16 Dec 2024 18:11:50 +0100 Subject: [PATCH] Optionally feed MWL C-FIND SCP by the content of the UPS DB table, intiated by dicomWeb Worklist Service (UPS-RS) fix #4620 --- .../ldap/apacheds/dcm4chee-archive.ldif | 35 +++++++- .../ldap/opendj/12-dcm4chee-archive.ldif | 13 +++ .../ldap/schema/dcm4chee-archive.schema | 15 ++++ .../ldap/slapd/dcm4chee-archive-modify.ldif | 20 +++++ .../ldap/slapd/dcm4chee-archive.ldif | 13 +++ .../conf/json/JsonArchiveConfiguration.java | 12 +++ .../conf/ldap/LdapArchiveConfiguration.java | 17 ++++ .../dcm4chee/arc/conf/ArchiveAEExtension.java | 15 ++++ .../arc/conf/ArchiveDeviceExtension.java | 18 +++++ .../dcm4chee/arc/query/util/QueryBuilder.java | 81 +++++++++++++++++++ .../dcm4chee/arc/query/util/QueryParam.java | 5 ++ .../org/dcm4chee/arc/query/QueryContext.java | 3 + .../arc/query/impl/QueryContextImpl.java | 3 +- .../arc/query/impl/QueryServiceImpl.java | 4 +- .../org/dcm4chee/arc/query/impl/UPSQuery.java | 8 +- 15 files changed, 254 insertions(+), 8 deletions(-) diff --git a/dcm4chee-arc-assembly/src/main/resources/ldap/apacheds/dcm4chee-archive.ldif b/dcm4chee-arc-assembly/src/main/resources/ldap/apacheds/dcm4chee-archive.ldif index 0039e8b7ee..8276c86989 100644 --- a/dcm4chee-arc-assembly/src/main/resources/ldap/apacheds/dcm4chee-archive.ldif +++ b/dcm4chee-arc-assembly/src/main/resources/ldap/apacheds/dcm4chee-archive.ldif @@ -1,4 +1,4 @@ -# Generated by Apache Directory Studio on December 5, 2024, 3:25:00 PM +# Generated by Apache Directory Studio on December 16, 2024, 5:44:37 PM # SCHEMA "DCM4CHEE-ARCHIVE" dn: cn=dcm4chee-archive, ou=schema @@ -6,7 +6,6 @@ objectclass: metaSchema objectclass: top cn: dcm4chee-archive m-dependencies: dcm4che -m-dependencies: system m-dependencies: dicom dn: ou=attributetypes, cn=dcm4chee-archive, ou=schema @@ -6514,6 +6513,35 @@ m-equality: caseExactMatch m-substr: caseExactSubstringsMatch m-syntax: 1.3.6.1.4.1.1466.115.121.1.15 +dn: m-oid=1.2.40.0.13.1.15.110.3.488, ou=attributetypes, cn=dcm4chee-archive, ou + =schema +objectclass: metaAttributeType +objectclass: metaTop +objectclass: top +m-oid: 1.2.40.0.13.1.15.110.3.488 +m-name: dcmUPS2MWLCFindSCP +m-description: Indicates to feed Modality Worklist C-FIND SCP service from manag + ed list of Unified Procedure Step (UPS) instances mapped to MWL items; false if + absent. +m-equality: booleanMatch +m-syntax: 1.3.6.1.4.1.1466.115.121.1.7 +m-singleValue: TRUE + +dn: m-oid=1.2.40.0.13.1.15.110.3.489, ou=attributetypes, cn=dcm4chee-archive, ou + =schema +objectclass: metaAttributeType +objectclass: metaTop +objectclass: top +m-oid: 1.2.40.0.13.1.15.110.3.489 +m-name: dcmUPS2MWLScheduledStationNameCode +m-description: Defines list of Station Name Codes in format (CV, CSD, "CM") used + to map Scheduled Station AE Titles (0040,0001) in MWL C-FIND RQ/RSPs to Statio + n Name Code Sequence (0040,4025) items of Unified Procedure Step (UPS) instance + s with the AE Title in the code meaning. +m-equality: caseExactMatch +m-substr: caseExactSubstringsMatch +m-syntax: 1.3.6.1.4.1.1466.115.121.1.15 + dn: ou=comparators, cn=dcm4chee-archive, ou=schema objectclass: organizationalUnit objectclass: top @@ -7230,6 +7258,8 @@ m-may: dcmUPSEventWebSocketQueueSize m-may: dcmUPSEventSCU m-may: dcmUPSEventSCUKeepAlive m-may: dcmUPSUpdateWithoutTransactionUID +m-may: dcmUPS2MWLCFindSCP +m-may: dcmUPS2MWLScheduledStationNameCode m-may: dcmStorageID m-may: dcmMetadataStorageID m-may: dcmSeriesMetadataStorageID @@ -7682,6 +7712,7 @@ m-may: dcmUPSWorklistLabel m-may: dcmUPSEventSCU m-may: dcmUPSEventSCUKeepAlive m-may: dcmUPSUpdateWithoutTransactionUID +m-may: dcmUPS2MWLCFindSCP m-may: dcmStorageID m-may: dcmObjectStorageID m-may: dcmObjectStorageCount diff --git a/dcm4chee-arc-assembly/src/main/resources/ldap/opendj/12-dcm4chee-archive.ldif b/dcm4chee-arc-assembly/src/main/resources/ldap/opendj/12-dcm4chee-archive.ldif index ecf6f5cf2c..26b5df5003 100644 --- a/dcm4chee-arc-assembly/src/main/resources/ldap/opendj/12-dcm4chee-archive.ldif +++ b/dcm4chee-arc-assembly/src/main/resources/ldap/opendj/12-dcm4chee-archive.ldif @@ -2376,6 +2376,16 @@ attributeTypes: ( 1.2.40.0.13.1.15.110.3.487 NAME 'hl7NoPatientUpdateMessageType EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +attributeTypes: ( 1.2.40.0.13.1.15.110.3.488 NAME 'dcmUPS2MWLCFindSCP' + DESC 'Indicates to feed Modality Worklist C-FIND SCP service from managed list of Unified Procedure Step (UPS) instances mapped to MWL items; false if absent.' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + SINGLE-VALUE ) +attributeTypes: ( 1.2.40.0.13.1.15.110.3.489 NAME 'dcmUPS2MWLScheduledStationNameCode' + DESC 'Defines list of Station Name Codes in format (CV, CSD, "CM") used to map Scheduled Station AE Titles (0040,0001) in MWL C-FIND RQ/RSPs to Station Name Code Sequence (0040,4025) items of Unified Procedure Step (UPS) instances with the AE Title in the code meaning.' + EQUALITY caseExactMatch + SUBSTR caseExactSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) objectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' DESC 'DICOM Archive Device related information' SUP top AUXILIARY @@ -2402,6 +2412,8 @@ objectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ + dcmUPS2MWLScheduledStationNameCode $ dcmStorageID $ dcmMetadataStorageID $ dcmSeriesMetadataStorageID $ @@ -2689,6 +2701,7 @@ objectClasses: ( 1.2.40.0.13.1.15.110.4.5 NAME 'dcmArchiveNetworkAE' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ dcmStorageID $ dcmObjectStorageID $ dcmObjectStorageCount $ diff --git a/dcm4chee-arc-assembly/src/main/resources/ldap/schema/dcm4chee-archive.schema b/dcm4chee-arc-assembly/src/main/resources/ldap/schema/dcm4chee-archive.schema index d59e24dba6..f115e90fa4 100644 --- a/dcm4chee-arc-assembly/src/main/resources/ldap/schema/dcm4chee-archive.schema +++ b/dcm4chee-arc-assembly/src/main/resources/ldap/schema/dcm4chee-archive.schema @@ -2853,6 +2853,18 @@ attributetype ( 1.2.40.0.13.1.15.110.3.487 NAME 'hl7NoPatientUpdateMessageType' SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +attributetype ( 1.2.40.0.13.1.15.110.3.488 NAME 'dcmUPS2MWLCFindSCP' + DESC 'Indicates to feed Modality Worklist C-FIND SCP service from managed list of Unified Procedure Step (UPS) instances mapped to MWL items; false if absent.' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + SINGLE-VALUE ) + +attributetype ( 1.2.40.0.13.1.15.110.3.489 NAME 'dcmUPS2MWLScheduledStationNameCode' + DESC 'Defines list of Station Name Codes in format (CV, CSD, "CM") used to map Scheduled Station AE Titles (0040,0001) in MWL C-FIND RQ/RSPs to Station Name Code Sequence (0040,4025) items of Unified Procedure Step (UPS) instances with the AE Title in the code meaning.' + EQUALITY caseExactMatch + SUBSTR caseExactSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) + objectclass ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' DESC 'DICOM Archive Device related information' SUP top AUXILIARY @@ -2879,6 +2891,8 @@ objectclass ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ + dcmUPS2MWLScheduledStationNameCode $ dcmStorageID $ dcmMetadataStorageID $ dcmSeriesMetadataStorageID $ @@ -3167,6 +3181,7 @@ objectclass ( 1.2.40.0.13.1.15.110.4.5 NAME 'dcmArchiveNetworkAE' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ dcmStorageID $ dcmObjectStorageID $ dcmObjectStorageCount $ diff --git a/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive-modify.ldif b/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive-modify.ldif index 4466084b8f..d9eda5fc4f 100644 --- a/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive-modify.ldif +++ b/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive-modify.ldif @@ -2371,6 +2371,21 @@ olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.486 NAME 'dcmCheckExistFilePath' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.487 NAME 'hl7NoPatientUpdateMessageType' + DESC 'Patient record will be not be updated by HL7 messages of specified Message Type(s) (MessageType^TriggerEvent).' + EQUALITY caseExactMatch + SUBSTR caseExactSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.488 NAME 'dcmUPS2MWLCFindSCP' + DESC 'Indicates to feed Modality Worklist C-FIND SCP service from managed list of Unified Procedure Step (UPS) instances mapped to MWL items; false if absent.' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + SINGLE-VALUE ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.489 NAME 'dcmUPS2MWLScheduledStationNameCode' + DESC 'Defines list of Station Name Codes in format (CV, CSD, "CM") used to map Scheduled Station AE Titles (0040,0001) in MWL C-FIND RQ/RSPs to Station Name Code Sequence (0040,4025) items of Unified Procedure Step (UPS) instances with the AE Title in the code meaning.' + EQUALITY caseExactMatch + SUBSTR caseExactSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) - replace: olcObjectClasses olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' @@ -2399,6 +2414,8 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ + dcmUPS2MWLScheduledStationNameCode $ dcmStorageID $ dcmMetadataStorageID $ dcmSeriesMetadataStorageID $ @@ -2534,6 +2551,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' hl7OutgoingLogFilePattern $ hl7OutgoingErrorLogFilePattern $ hl7NoPatientCreateMessageType $ + hl7NoPatientUpdateMessageType $ dcmUnzipVendorDataToURI $ dcmPurgeQueueMessagePollingInterval $ dcmPurgeQueueMessageFetchSize $ @@ -2685,6 +2703,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.5 NAME 'dcmArchiveNetworkAE' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ dcmStorageID $ dcmObjectStorageID $ dcmObjectStorageCount $ @@ -2839,6 +2858,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.11 NAME 'dcmArchiveHL7Application' hl7OutgoingLogFilePattern $ hl7OutgoingErrorLogFilePattern $ hl7NoPatientCreateMessageType $ + hl7NoPatientUpdateMessageType $ hl7UseNullValue $ hl7ImportReportAdjustIUID $ hl7PrimaryAssigningAuthorityOfPatientID $ diff --git a/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive.ldif b/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive.ldif index ffca6ea2fb..e8dd74cebc 100644 --- a/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive.ldif +++ b/dcm4chee-arc-assembly/src/main/resources/ldap/slapd/dcm4chee-archive.ldif @@ -2375,6 +2375,16 @@ olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.487 NAME 'hl7NoPatientUpdateMessageT EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.488 NAME 'dcmUPS2MWLCFindSCP' + DESC 'Indicates to feed Modality Worklist C-FIND SCP service from managed list of Unified Procedure Step (UPS) instances mapped to MWL items; false if absent.' + EQUALITY booleanMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 + SINGLE-VALUE ) +olcAttributeTypes: ( 1.2.40.0.13.1.15.110.3.489 NAME 'dcmUPS2MWLScheduledStationNameCode' + DESC 'Defines list of Station Name Codes in format (CV, CSD, "CM") used to map Scheduled Station AE Titles (0040,0001) in MWL C-FIND RQ/RSPs to Station Name Code Sequence (0040,4025) items of Unified Procedure Step (UPS) instances with the AE Title in the code meaning.' + EQUALITY caseExactMatch + SUBSTR caseExactSubstringsMatch + SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 ) olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' DESC 'DICOM Archive Device related information' SUP top AUXILIARY @@ -2401,6 +2411,8 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.4 NAME 'dcmArchiveDevice' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ + dcmUPS2MWLScheduledStationNameCode $ dcmStorageID $ dcmMetadataStorageID $ dcmSeriesMetadataStorageID $ @@ -2688,6 +2700,7 @@ olcObjectClasses: ( 1.2.40.0.13.1.15.110.4.5 NAME 'dcmArchiveNetworkAE' dcmUPSEventSCU $ dcmUPSEventSCUKeepAlive $ dcmUPSUpdateWithoutTransactionUID $ + dcmUPS2MWLCFindSCP $ dcmStorageID $ dcmObjectStorageID $ dcmObjectStorageCount $ diff --git a/dcm4chee-arc-conf-json/src/main/java/org/dcm4chee/arc/conf/json/JsonArchiveConfiguration.java b/dcm4chee-arc-conf-json/src/main/java/org/dcm4chee/arc/conf/json/JsonArchiveConfiguration.java index 2e9defcbef..a6a013d205 100644 --- a/dcm4chee-arc-conf-json/src/main/java/org/dcm4chee/arc/conf/json/JsonArchiveConfiguration.java +++ b/dcm4chee-arc-conf-json/src/main/java/org/dcm4chee/arc/conf/json/JsonArchiveConfiguration.java @@ -440,6 +440,8 @@ protected void storeTo(Device device, JsonWriter writer) { writer.writeNotDef("dcmAuditHL7MsgLimit", arcDev.getAuditHL7MsgLimit(), 1000); writer.writeNotDef("dcmMatchSOPClassOnInstanceLevel", arcDev.isMatchSOPClassOnInstanceLevel(), false); writer.writeNotDef("dcmUPSUpdateWithoutTransactionUID", arcDev.isUPSUpdateWithoutTransactionUID(), false); + writer.writeNotDef("dcmUPS2MWLCFindSCP", arcDev.isUPS2MWLCFindSCP(), false); + writer.writeNotEmpty("dcmUPS2MWLScheduledStationNameCode", arcDev.getUPS2MWLScheduledStationNames()); writer.writeNotNullOrDef("dcmKeyValueRetentionPollingInterval", arcDev.getKeyValueRetentionPollingInterval(), null); writer.writeNotDef("dcmKeyValueRetentionFetchSize", arcDev.getKeyValueRetentionFetchSize(), 100); @@ -1387,6 +1389,7 @@ protected void storeTo(ApplicationEntity ae, JsonWriter writer) { writer.writeNotNull("dcmFilterByIssuerOfPatientID", arcAE.getFilterByIssuerOfPatientID()); writer.writeNotNull("dcmMatchSOPClassOnInstanceLevel", arcAE.getMatchSOPClassOnInstanceLevel()); writer.writeNotNull("dcmUPSUpdateWithoutTransactionUID", arcAE.getUPSUpdateWithoutTransactionUID()); + writer.writeNotNull("dcmUPS2MWLCFindSCP", arcAE.getUPS2MWLCFindSCP()); writeExportRule(writer, arcAE.getExportRules()); writeExportPrefetchRules(writer, arcAE.getExportPriorsRules()); writeMPPSForwardRule(writer, arcAE.getMPPSForwardRules()); @@ -2204,6 +2207,12 @@ private void loadFrom(ArchiveDeviceExtension arcDev, JsonReader reader, Configur case "dcmUPSUpdateWithoutTransactionUID": arcDev.setUPSUpdateWithoutTransactionUID(reader.booleanValue()); break; + case "dcmUPS2MWLCFindSCP": + arcDev.setUPS2MWLCFindSCP(reader.booleanValue()); + break; + case "dcmUPS2MWLScheduledStationNameCode": + arcDev.setUPS2MWLScheduledStationNames(reader.codeArray()); + break; case "dcmKeyValueRetentionPollingInterval": arcDev.setKeyValueRetentionPollingInterval(Duration.valueOf(reader.stringValue())); break; @@ -4430,6 +4439,9 @@ private void loadFrom(ArchiveAEExtension arcAE, JsonReader reader, Configuration case "dcmUPSUpdateWithoutTransactionUID": arcAE.setUPSUpdateWithoutTransactionUID(reader.booleanValue()); break; + case "dcmUPS2MWLCFindSCP": + arcAE.setUPS2MWLCFindSCP(reader.booleanValue()); + break; case "dcmExportRule": loadExportRule(arcAE.getExportRules(), reader); break; diff --git a/dcm4chee-arc-conf-ldap/src/main/java/org/dcm4chee/arc/conf/ldap/LdapArchiveConfiguration.java b/dcm4chee-arc-conf-ldap/src/main/java/org/dcm4chee/arc/conf/ldap/LdapArchiveConfiguration.java index 4ab7bec3b5..f48f5206f3 100644 --- a/dcm4chee-arc-conf-ldap/src/main/java/org/dcm4chee/arc/conf/ldap/LdapArchiveConfiguration.java +++ b/dcm4chee-arc-conf-ldap/src/main/java/org/dcm4chee/arc/conf/ldap/LdapArchiveConfiguration.java @@ -548,6 +548,10 @@ protected void storeTo(ConfigurationChanges.ModifiedObject ldapObj, Device devic ext.isMatchSOPClassOnInstanceLevel(), false); LdapUtils.storeNotDef(ldapObj, attrs, "dcmUPSUpdateWithoutTransactionUID", ext.isUPSUpdateWithoutTransactionUID(), false); + LdapUtils.storeNotDef(ldapObj, attrs, "dcmUPS2MWLCFindSCP", + ext.isUPS2MWLCFindSCP(), false); + LdapUtils.storeNotEmpty(ldapObj, attrs, "dcmUPS2MWLScheduledStationNameCode", + ext.getUPS2MWLScheduledStationNames()); LdapUtils.storeNotNullOrDef(ldapObj, attrs, "dcmKeyValueRetentionPollingInterval", ext.getKeyValueRetentionPollingInterval(), null); LdapUtils.storeNotDef(ldapObj, attrs, "dcmKeyValueRetentionFetchSize", @@ -928,6 +932,8 @@ protected void loadFrom(Device device, Attributes attrs) throws NamingException ext.setAuditHL7MsgLimit(LdapUtils.intValue(attrs.get("dcmAuditHL7MsgLimit"), 1000)); ext.setMatchSOPClassOnInstanceLevel(LdapUtils.booleanValue(attrs.get("dcmMatchSOPClassOnInstanceLevel"), false)); ext.setUPSUpdateWithoutTransactionUID(LdapUtils.booleanValue(attrs.get("dcmUPSUpdateWithoutTransactionUID"), false)); + ext.setUPS2MWLCFindSCP(LdapUtils.booleanValue(attrs.get("dcmUPS2MWLCFindSCP"), false)); + ext.setUPS2MWLScheduledStationNames(LdapUtils.codeArray(attrs.get("dcmUPS2MWLScheduledStationNameCode"))); ext.setKeyValueRetentionPollingInterval(toDuration(attrs.get("dcmKeyValueRetentionPollingInterval"), null)); ext.setKeyValueRetentionFetchSize(LdapUtils.intValue(attrs.get("dcmKeyValueRetentionFetchSize"), 100)); ext.setKeyValueRetentionPeriod(toDuration(attrs.get("dcmKeyValueRetentionPeriod"), null)); @@ -1609,6 +1615,12 @@ protected void storeDiffs(ConfigurationChanges.ModifiedObject ldapObj, Device pr aa.isUPSUpdateWithoutTransactionUID(), bb.isUPSUpdateWithoutTransactionUID(), false); + LdapUtils.storeDiff(ldapObj, mods, "dcmUPS2MWLCFindSCP", + aa.isUPS2MWLCFindSCP(), + bb.isUPS2MWLCFindSCP(), + false); + LdapUtils.storeDiff(ldapObj, mods, "dcmUPS2MWLScheduledStationNameCode", + aa.getUPS2MWLScheduledStationNames(), bb.getUPS2MWLScheduledStationNames()); LdapUtils.storeDiffObject(ldapObj, mods, "dcmKeyValueRetentionPollingInterval", aa.getKeyValueRetentionPollingInterval(), bb.getKeyValueRetentionPollingInterval(), null); LdapUtils.storeDiff(ldapObj, mods, "dcmKeyValueRetentionFetchSize", @@ -1983,6 +1995,8 @@ protected void storeTo(ConfigurationChanges.ModifiedObject ldapObj, ApplicationE ext.getMatchSOPClassOnInstanceLevel(), null); LdapUtils.storeNotNullOrDef(ldapObj, attrs, "dcmUPSUpdateWithoutTransactionUID", ext.getUPSUpdateWithoutTransactionUID(), null); + LdapUtils.storeNotNullOrDef(ldapObj, attrs, "dcmUPS2MWLCFindSCP", + ext.getUPS2MWLCFindSCP(), null); } @Override @@ -2157,6 +2171,7 @@ protected void loadFrom(ApplicationEntity ae, Attributes attrs) throws NamingExc ext.setFilterByIssuerOfPatientID(LdapUtils.booleanValue(attrs.get("dcmFilterByIssuerOfPatientID"), null)); ext.setMatchSOPClassOnInstanceLevel(LdapUtils.booleanValue(attrs.get("dcmMatchSOPClassOnInstanceLevel"), null)); ext.setUPSUpdateWithoutTransactionUID(LdapUtils.booleanValue(attrs.get("dcmUPSUpdateWithoutTransactionUID"), null)); + ext.setUPS2MWLCFindSCP(LdapUtils.booleanValue(attrs.get("dcmUPS2MWLCFindSCP"), null)); } @Override @@ -2439,6 +2454,8 @@ protected void storeDiffs(ConfigurationChanges.ModifiedObject ldapObj, Applicati aa.getMatchSOPClassOnInstanceLevel(), bb.getMatchSOPClassOnInstanceLevel(), null); LdapUtils.storeDiffObject(ldapObj, mods, "dcmUPSUpdateWithoutTransactionUID", aa.getUPSUpdateWithoutTransactionUID(), bb.getUPSUpdateWithoutTransactionUID(), null); + LdapUtils.storeDiffObject(ldapObj, mods, "dcmUPS2MWLCFindSCP", + aa.getUPS2MWLCFindSCP(), bb.getUPS2MWLCFindSCP(), null); if (remove) mods.add(new ModificationItem(DirContext.REMOVE_ATTRIBUTE, LdapUtils.attr("objectClass", "dcmArchiveNetworkAE"))); diff --git a/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveAEExtension.java b/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveAEExtension.java index c9b968db3a..b2eb4e0894 100644 --- a/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveAEExtension.java +++ b/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveAEExtension.java @@ -68,6 +68,7 @@ public class ArchiveAEExtension extends AEExtension { private String[] upsEventSCUs = {}; private int upsEventSCUKeepAlive; private Boolean upsUpdateWithoutTransactionUID; + private Boolean ups2MWLCFindSCP; private String[] objectStorageIDs = {}; private int objectStorageCount = 1; private String[] metadataStorageIDs = {}; @@ -275,6 +276,20 @@ public boolean upsUpdateWithoutTransactionUID() { : getArchiveDeviceExtension().isUPSUpdateWithoutTransactionUID(); } + public Boolean getUPS2MWLCFindSCP() { + return ups2MWLCFindSCP; + } + + public void setUPS2MWLCFindSCP(Boolean ups2MWLCFindSCP) { + this.ups2MWLCFindSCP = ups2MWLCFindSCP; + } + + public boolean ups2MWLCFindSCP() { + return ups2MWLCFindSCP != null + ? ups2MWLCFindSCP + : getArchiveDeviceExtension().isUPS2MWLCFindSCP(); + } + public String[] getObjectStorageIDs() { return objectStorageIDs; } diff --git a/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveDeviceExtension.java b/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveDeviceExtension.java index 9711426447..3213331a2a 100644 --- a/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveDeviceExtension.java +++ b/dcm4chee-arc-conf/src/main/java/org/dcm4chee/arc/conf/ArchiveDeviceExtension.java @@ -81,6 +81,8 @@ public class ArchiveDeviceExtension extends DeviceExtension { private volatile String[] upsEventSCUs = {}; private volatile int upsEventSCUKeepAlive; private volatile boolean upsUpdateWithoutTransactionUID; + private volatile boolean ups2MWLCFindSCP; + private volatile Code[] ups2MWLScheduledStationNames = {}; private volatile String fuzzyAlgorithmClass; private volatile String bulkDataDescriptorID; private volatile String[] seriesMetadataStorageIDs = {}; @@ -436,6 +438,22 @@ public void setUPSUpdateWithoutTransactionUID(boolean upsUpdateWithoutTransactio this.upsUpdateWithoutTransactionUID = upsUpdateWithoutTransactionUID; } + public boolean isUPS2MWLCFindSCP() { + return ups2MWLCFindSCP; + } + + public void setUPS2MWLCFindSCP(boolean ups2MWLCFindSCP) { + this.ups2MWLCFindSCP = ups2MWLCFindSCP; + } + + public Code[] getUPS2MWLScheduledStationNames() { + return ups2MWLScheduledStationNames; + } + + public void setUPS2MWLScheduledStationNames(Code[] ups2MWLScheduledStationNames) { + this.ups2MWLScheduledStationNames = ups2MWLScheduledStationNames; + } + public String getFuzzyAlgorithmClass() { return fuzzyAlgorithmClass; } diff --git a/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryBuilder.java b/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryBuilder.java index 68182e31be..401fb12e01 100644 --- a/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryBuilder.java +++ b/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryBuilder.java @@ -46,6 +46,7 @@ import jakarta.persistence.metamodel.SingularAttribute; import org.dcm4che3.data.*; import org.dcm4che3.data.PersonName; +import org.dcm4che3.dcmr.AcquisitionModality; import org.dcm4che3.dict.archive.PrivateTag; import org.dcm4che3.net.service.QueryRetrieveLevel2; import org.dcm4che3.soundex.FuzzyStr; @@ -56,6 +57,7 @@ import java.util.*; import java.util.function.Function; +import java.util.stream.Stream; /** * @author Gunter Zeilinger @@ -458,6 +460,15 @@ public List upsPredicates(CriteriaQuery q, return predicates; } + public List mwlupsPredicates(CriteriaQuery q, + Join patient, Root ups, + IDWithIssuer[] pids, Issuer issuer, Attributes keys, QueryParam queryParam) { + List predicates = new ArrayList<>(); + patientLevelPredicates(predicates, q, patient, pids, issuer, keys, queryParam, null); + mwlupsLevelPredicates(predicates, q, ups, keys, queryParam); + return predicates; + } + public List mppsPredicates(CriteriaQuery q, From patient, Root mpps, IDWithIssuer[] pids, Issuer issuer, Attributes keys, QueryParam queryParam) { @@ -1131,6 +1142,76 @@ private void upsLevelPredicates(List predicates, CriteriaQuery notSubscribedBy(predicates, q, ups, queryParam.getNotSubscribedByAET()); } + private void mwlupsLevelPredicates(List predicates, CriteriaQuery q, + Root ups, Attributes keys, QueryParam queryParam) { + Attributes mwlsps = keys.getNestedDataset(Tag.ScheduledProcedureStepSequence); + if (mwlsps != null) { + anyOf(predicates, ups.get(UPS_.upsLabel), mwlsps.getStrings(Tag.ScheduledProcedureStepDescription), true); + dateRange(predicates, ups.get(UPS_.scheduledStartDateAndTime), + mwlsps.getDateRange(Tag.ScheduledProcedureStepStartDateAndTime), FormatDate.DT); + String modality = mwlsps.getString(Tag.Modality); + if (modality != null) { + Code code = AcquisitionModality.codeOf(modality); + if (code != null) { + codes(predicates, q, ups, UPS_.scheduledStationClassCodes, code.toItem()); + } + } + String aet = mwlsps.getString(Tag.ScheduledStationAETitle); + if (aet != null) { + Stream.of(queryParam.getUPS2MWLScheduledStationNames()) + .filter(code -> code.getCodeMeaning().equals(aet)) + .findFirst() + .ifPresent(code -> codes(predicates, q, ups, UPS_.scheduledStationNameCodes, code.toItem())); + } + code(predicates, ups.get(UPS_.scheduledWorkitemCode), + mwlsps.getNestedDataset(Tag.ScheduledWorkitemCodeSequence)); + } + predicates.add(cb.notEqual(ups.get(UPS_.scheduledStartDateAndTime), "*")); + predicates.add(cb.equal(ups.get(UPS_.procedureStepState), UPSState.SCHEDULED)); + anyOf(predicates, ups.get(UPS_.worklistLabel), keys.getStrings(Tag.WorklistLabel), true); + String admissionID = keys.getString(Tag.AdmissionID, "*"); + if (!isUniversalMatching(admissionID)) { + Issuer issuer = Issuer.valueOf(keys.getNestedDataset(Tag.IssuerOfAdmissionIDSequence)); + if (issuer == null) + issuer = queryParam.getDefaultIssuerOfAdmissionID(); + if (wildCard(predicates, ups.get(UPS_.admissionID), admissionID) && issuer != null) { + issuer(predicates, + ups.get(UPS_.admissionIDLocalNamespaceEntityID), + ups.get(UPS_.admissionIDUniversalEntityID), + ups.get(UPS_.admissionIDUniversalEntityIDType), + issuer); + } + } + Subquery sq = q.subquery(UPSRequest.class); + Root sqUPS = sq.correlate(ups); + Join request = sqUPS.join(UPS_.referencedRequests); + List requestPredicates = new ArrayList<>(); + String accNo = keys.getString(Tag.AccessionNumber, "*"); + if (!isUniversalMatching(accNo)) { + Issuer issuerOfAccessionNumber = Issuer.valueOf(keys + .getNestedDataset(Tag.IssuerOfAccessionNumberSequence)); + if (issuerOfAccessionNumber == null) + issuerOfAccessionNumber = queryParam.getDefaultIssuerOfAccessionNumber(); + if (wildCard(requestPredicates, request.get(UPSRequest_.accessionNumber), accNo) && issuerOfAccessionNumber != null) { + issuer(requestPredicates, + request.get(UPSRequest_.accessionNumberLocalNamespaceEntityID), + request.get(UPSRequest_.accessionNumberUniversalEntityID), + request.get(UPSRequest_.accessionNumberUniversalEntityIDType), + issuerOfAccessionNumber); + } + } + anyOf(requestPredicates, + request.get(UPSRequest_.requestingService), + keys.getStrings(Tag.RequestingService), true); + personName(requestPredicates, q, request, UPSRequest_.requestingPhysician, + keys.getString(Tag.RequestingPhysician, "*"), queryParam); + anyOf(requestPredicates, + request.get(UPSRequest_.requestedProcedureID), + keys.getStrings(Tag.RequestedProcedureID), false); + if (!requestPredicates.isEmpty()) + predicates.add(cb.exists(sq.select(request).where(requestPredicates.toArray(new Predicate[0])))); + } + private void mppsLevelPredicates(List predicates, CriteriaQuery q, Root mpps, Attributes keys, QueryParam queryParam) { anyOf(predicates, mpps.get(MPPS_.sopInstanceUID), keys.getStrings(Tag.SOPInstanceUID), true); diff --git a/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryParam.java b/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryParam.java index e7e40afded..da40ced754 100644 --- a/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryParam.java +++ b/dcm4chee-arc-query-util/src/main/java/org/dcm4chee/arc/query/util/QueryParam.java @@ -40,6 +40,7 @@ package org.dcm4chee.arc.query.util; +import org.dcm4che3.data.Code; import org.dcm4che3.data.Issuer; import org.dcm4che3.net.ApplicationEntity; import org.dcm4che3.soundex.FuzzyStr; @@ -140,6 +141,10 @@ public AttributeFilter getAttributeFilter(Entity entity) { return arcDev.getAttributeFilter(entity); } + public Code[] getUPS2MWLScheduledStationNames() { + return arcDev.getUPS2MWLScheduledStationNames(); + } + public String getViewID() { return qrView.getViewID(); } diff --git a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/QueryContext.java b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/QueryContext.java index 7a20448448..d49b0668eb 100644 --- a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/QueryContext.java +++ b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/QueryContext.java @@ -47,6 +47,7 @@ import org.dcm4che3.net.Association; import org.dcm4che3.net.service.QueryRetrieveLevel2; import org.dcm4chee.arc.conf.ArchiveAEExtension; +import org.dcm4chee.arc.conf.ArchiveDeviceExtension; import org.dcm4chee.arc.keycloak.HttpServletRequestInfo; import org.dcm4chee.arc.query.util.OrderByTag; import org.dcm4chee.arc.query.util.QueryParam; @@ -119,6 +120,8 @@ public interface QueryContext { boolean isConsiderPurgedInstances(); + ArchiveDeviceExtension getArchiveDeviceExtension(); + Storage getStorage(String storageID); void putStorage(String storageID, Storage storage); diff --git a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryContextImpl.java b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryContextImpl.java index ba42da4b3e..7dd9a06157 100644 --- a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryContextImpl.java +++ b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryContextImpl.java @@ -262,7 +262,8 @@ && getArchiveDeviceExtension().isPurgeInstanceRecords() && (queryKeys.containsValue(Tag.StudyInstanceUID) || queryKeys.containsValue(Tag.SeriesInstanceUID)); } - private ArchiveDeviceExtension getArchiveDeviceExtension() { + @Override + public ArchiveDeviceExtension getArchiveDeviceExtension() { return ae.getDevice().getDeviceExtension(ArchiveDeviceExtension.class); } diff --git a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryServiceImpl.java b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryServiceImpl.java index 3e6e60f434..835700eb8b 100644 --- a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryServiceImpl.java +++ b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/QueryServiceImpl.java @@ -301,7 +301,9 @@ public Query createInstanceQuery(QueryContext ctx) { @Override public Query createMWLQuery(QueryContext ctx) { queryEvent.fire(ctx); - return new MWLQuery(ctx, em); + return ctx.getArchiveAEExtension().ups2MWLCFindSCP() + ? new MWLUPSQuery(ctx, em) + : new MWLQuery(ctx, em); } @Override diff --git a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/UPSQuery.java b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/UPSQuery.java index 9cd5d819ec..c4361662d6 100644 --- a/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/UPSQuery.java +++ b/dcm4chee-arc-query/src/main/java/org/dcm4chee/arc/query/impl/UPSQuery.java @@ -58,8 +58,8 @@ * @since Sep 2019 */ public class UPSQuery extends AbstractQuery { - private Root ups; - private Join patient; + protected Root ups; + protected Join patient; private Path patientAttrBlob; private Path upsAttrBlob; @@ -108,13 +108,13 @@ public boolean isOptionalKeysNotSupported() { return false; } - private CriteriaQuery order(CriteriaQuery q) { + protected CriteriaQuery order(CriteriaQuery q) { if (context.getOrderByTags() != null) q.orderBy(builder.orderWorkitems(patient, ups, context.getOrderByTags())); return q; } - private CriteriaQuery restrict(CriteriaQuery q, Join patient, Root ups) { + protected CriteriaQuery restrict(CriteriaQuery q, Join patient, Root ups) { List predicates = builder.upsPredicates(q, patient, ups, context.getPatientIDs(), context.getIssuerOfPatientID(),