PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
imaging_document_consumer.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13
14#include <algorithm>
15#include <sstream>
16
18
19using namespace kcenon::pacs::core;
20
21// =============================================================================
22// KOS-specific DICOM Tags (for reference parsing)
23// =============================================================================
24
25namespace consumer_tags {
26
28constexpr dicom_tag referenced_series_sequence{0x0008, 0x1115};
29constexpr dicom_tag referenced_sop_sequence{0x0008, 0x1199};
30constexpr dicom_tag referenced_sop_instance_uid{0x0008, 0x1155};
31
32} // namespace consumer_tags
33
34// =============================================================================
35// imaging_document_consumer Implementation
36// =============================================================================
37
41
43 [[maybe_unused]] const registry_query_params& params) const {
44
46
47 if (config_.registry_url.empty()) {
48 result.error_message = "XDS registry URL not configured";
49 return result;
50 }
51
52 // NOTE: Actual SOAP request to the XDS registry (ITI-18)
53 // would be implemented here using the network layer.
54 // The ITI-18 transaction uses SOAP/XML encoding with
55 // the AdhocQueryRequest/AdhocQueryResponse pattern.
56 result.success = true;
57
58 return result;
59}
60
62 const document_reference& doc_ref,
63 bool is_imaging) const {
64
66
67 const auto transaction =
68 is_imaging
71
72 if (config_.repository_url.empty()) {
73 result.error_message = "XDS repository URL not configured";
74 if (auditor_) {
76 transaction,
77 doc_ref.repository_unique_id, // remote source
78 config_.repository_url, // local destination (empty)
79 doc_ref.unique_id, // document/study identifier
80 doc_ref.patient_id,
81 {doc_ref.unique_id}, // SOP-level reference
82 false);
83 }
84 return result;
85 }
86
87 // NOTE: Actual SOAP/MTOM request to the XDS repository (ITI-43 / RAD-69)
88 // would be implemented here. The response contains the KOS
89 // document as an MTOM attachment.
90 result.success = true;
91
92 if (auditor_) {
94 transaction,
97 doc_ref.unique_id,
98 doc_ref.patient_id,
99 {doc_ref.unique_id},
100 true);
101 }
102
103 return result;
104}
105
107 std::shared_ptr<kcenon::pacs::security::atna_service_auditor> auditor) {
108 auditor_ = std::move(auditor);
109}
110
112 const core::dicom_dataset& kos_dataset) const {
113
115 result.success = true;
116
117 // Parse Current Requested Procedure Evidence Sequence (0040,A375)
118 const auto* evidence_elem = kos_dataset.get(
120
121 if (!evidence_elem || !evidence_elem->is_sequence()) {
122 result.error_message = "KOS document does not contain evidence sequence";
123 result.success = false;
124 return result;
125 }
126
127 for (const auto& study_item : evidence_elem->sequence_items()) {
128 // Extract Study Instance UID
129 auto study_uid = study_item.get_string(tags::study_instance_uid);
130 if (!study_uid.empty()) {
131 if (std::find(result.referenced_study_uids.begin(),
132 result.referenced_study_uids.end(),
133 study_uid) == result.referenced_study_uids.end()) {
134 result.referenced_study_uids.push_back(study_uid);
135 }
136 }
137
138 // Parse Referenced Series Sequence
139 const auto* series_elem = study_item.get(
141 if (!series_elem || !series_elem->is_sequence()) {
142 continue;
143 }
144
145 for (const auto& series_item : series_elem->sequence_items()) {
146 auto series_uid = series_item.get_string(tags::series_instance_uid);
147 if (!series_uid.empty()) {
148 if (std::find(result.referenced_series_uids.begin(),
149 result.referenced_series_uids.end(),
150 series_uid) == result.referenced_series_uids.end()) {
151 result.referenced_series_uids.push_back(series_uid);
152 }
153 }
154
155 // Parse Referenced SOP Sequence
156 const auto* sop_elem = series_item.get(
158 if (!sop_elem || !sop_elem->is_sequence()) {
159 continue;
160 }
161
162 for (const auto& sop_item : sop_elem->sequence_items()) {
163 auto instance_uid = sop_item.get_string(
165 if (!instance_uid.empty()) {
166 result.referenced_instance_uids.push_back(instance_uid);
167 }
168 }
169 }
170 }
171
172 return result;
173}
174
176 const std::string& study_uid,
177 const std::string& series_uid,
178 const std::string& instance_uid) const {
179
180 std::ostringstream url;
181 url << config_.wado_rs_url;
182
183 // Ensure base URL doesn't end with '/'
184 if (!config_.wado_rs_url.empty() && config_.wado_rs_url.back() == '/') {
185 // Remove the trailing '/' already added
186 } else {
187 url << '/';
188 }
189
190 url << "studies/" << study_uid
191 << "/series/" << series_uid
192 << "/instances/" << instance_uid;
193
194 return url.str();
195}
196
199 return config_;
200}
201
206
207} // namespace kcenon::pacs::services::xds
High-level facade for ATNA audit logging in DICOM services.
Represents a DICOM Dataset (ordered collection of Data Elements)
auto get(dicom_tag tag) noexcept -> dicom_element *
Get a pointer to the element with the given tag.
Represents a DICOM tag (Group, Element pair)
Definition dicom_tag.h:51
virtual void audit_xds_retrieve(xds_consumer_transaction transaction, const std::string &source_ae, const std::string &dest_ae, const std::string &study_uid, const std::string &patient_id, const std::vector< std::string > &sop_instance_uids, bool success)
Audit an XDS-I.b Retrieve Document Set transaction.
void set_config(const imaging_document_consumer_config &config)
Set configuration.
void set_audit_handler(std::shared_ptr< kcenon::pacs::security::atna_service_auditor > auditor)
Install an ATNA audit handler for XDS-I.b transactions.
std::shared_ptr< kcenon::pacs::security::atna_service_auditor > auditor_
const imaging_document_consumer_config & config() const noexcept
Get current configuration.
document_retrieval_result extract_references(const core::dicom_dataset &kos_dataset) const
Extract image references from a KOS dataset.
registry_query_result query_registry(const registry_query_params &params) const
Query the XDS registry for imaging documents.
std::string build_wado_rs_url(const std::string &study_uid, const std::string &series_uid, const std::string &instance_uid) const
Build a WADO-RS URL for retrieving a specific instance.
document_retrieval_result retrieve_document(const document_reference &doc_ref, bool is_imaging=true) const
Retrieve a specific document from the XDS repository.
Compile-time constants for commonly used DICOM tags.
IHE XDS-I.b Imaging Document Consumer Actor.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag series_instance_uid
Series Instance UID.
Document reference returned from a registry query.
std::string repository_unique_id
Repository unique ID (identifies where the document is stored)
std::vector< std::string > referenced_study_uids
Instance references extracted from the KOS.
Configuration for the Imaging Document Consumer actor.
std::string wado_rs_url
WADO-RS base URL for image retrieval (alternative to C-MOVE)
std::string registry_url
XDS Registry endpoint URL for queries (ITI-18)
std::string repository_url
XDS Repository endpoint URL for document retrieval (ITI-43)
Query parameters for XDS registry stored query (ITI-18)