PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::services::query_scp Class Referencefinal

#include <query_scp.h>

Inheritance diagram for kcenon::pacs::services::query_scp:
Inheritance graph
Collaboration diagram for kcenon::pacs::services::query_scp:
Collaboration graph

Public Member Functions

 query_scp (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct Query SCP with optional logger.
 
 ~query_scp () override=default
 
void set_handler (query_handler handler)
 Set the query handler function.
 
void set_max_results (size_t max) noexcept
 Set maximum number of results to return.
 
size_t max_results () const noexcept
 Get maximum number of results.
 
void set_cancel_check (cancel_check check)
 Set the cancel check function.
 
void set_audit_handler (std::shared_ptr< kcenon::pacs::security::atna_service_auditor > auditor)
 Set the ATNA audit handler for C-FIND operations.
 
std::vector< std::string > supported_sop_classes () const override
 Get supported SOP Class UIDs.
 
network::Result< std::monostate > handle_message (network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request) override
 Handle an incoming DIMSE message (C-FIND-RQ)
 
std::string_view service_name () const noexcept override
 Get the service name.
 
size_t queries_processed () const noexcept
 Get total number of queries processed.
 
void reset_statistics () noexcept
 Reset statistics counters.
 
- Public Member Functions inherited from kcenon::pacs::services::scp_service
 scp_service (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct SCP service with optional logger.
 
virtual ~scp_service ()=default
 
 scp_service (const scp_service &)=delete
 
scp_serviceoperator= (const scp_service &)=delete
 
 scp_service (scp_service &&)=default
 
scp_serviceoperator= (scp_service &&)=default
 
void set_logger (std::shared_ptr< di::ILogger > logger)
 Set the logger instance.
 
const std::shared_ptr< di::ILogger > & logger () const noexcept
 Get the current logger instance.
 
bool supports_sop_class (std::string_view sop_class_uid) const
 Check if this service supports a specific SOP Class.
 

Private Member Functions

std::optional< query_levelextract_query_level (const core::dicom_dataset &dataset) const
 Extract query level from request dataset.
 
network::Result< std::monostate > send_pending_response (network::association &assoc, uint8_t context_id, uint16_t message_id, std::string_view sop_class_uid, const core::dicom_dataset &result)
 Send a pending C-FIND response with matching dataset.
 
network::Result< std::monostate > send_final_response (network::association &assoc, uint8_t context_id, uint16_t message_id, std::string_view sop_class_uid, network::dimse::status_code status)
 Send the final C-FIND response (success or cancel)
 

Private Attributes

query_handler handler_
 
cancel_check cancel_check_
 
std::shared_ptr< kcenon::pacs::security::atna_service_auditorauditor_
 
size_t max_results_ {0}
 
std::atomic< size_t > queries_processed_ {0}
 

Additional Inherited Members

- Protected Attributes inherited from kcenon::pacs::services::scp_service
std::shared_ptr< di::ILoggerlogger_
 Logger instance for service logging.
 

Detailed Description

Definition at line 189 of file query_scp.h.

Constructor & Destructor Documentation

◆ query_scp()

kcenon::pacs::services::query_scp::query_scp ( std::shared_ptr< di::ILogger > logger = nullptr)
explicit

Construct Query SCP with optional logger.

Parameters
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 40 of file query_scp.cpp.

41 : scp_service(std::move(logger)) {}
const std::shared_ptr< di::ILogger > & logger() const noexcept
Get the current logger instance.
Definition scp_service.h:93
scp_service(std::shared_ptr< di::ILogger > logger=nullptr)
Construct SCP service with optional logger.
Definition scp_service.h:64

◆ ~query_scp()

kcenon::pacs::services::query_scp::~query_scp ( )
overridedefault

Member Function Documentation

◆ extract_query_level()

std::optional< query_level > kcenon::pacs::services::query_scp::extract_query_level ( const core::dicom_dataset & dataset) const
nodiscardprivate

Extract query level from request dataset.

Parameters
datasetThe query dataset
Returns
Parsed query level, or std::nullopt if invalid

Definition at line 215 of file query_scp.cpp.

216 {
217
218 // Get Query/Retrieve Level tag (0008,0052)
219 auto level_str = dataset.get_string(core::tags::query_retrieve_level);
220
221 if (level_str.empty()) {
222 return std::nullopt;
223 }
224
225 return parse_query_level(level_str);
226}
constexpr dicom_tag query_retrieve_level
Query/Retrieve Level.
std::optional< query_level > parse_query_level(std::string_view level_str) noexcept
Parse query level from DICOM string.
Definition query_scp.h:90

References kcenon::pacs::core::dicom_dataset::get_string(), kcenon::pacs::services::parse_query_level(), and kcenon::pacs::core::tags::query_retrieve_level.

Referenced by handle_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ handle_message()

network::Result< std::monostate > kcenon::pacs::services::query_scp::handle_message ( network::association & assoc,
uint8_t context_id,
const network::dimse::dimse_message & request )
nodiscardoverridevirtual

Handle an incoming DIMSE message (C-FIND-RQ)

Processes the C-FIND request, queries the database through the handler, and sends pending responses for each match followed by a final success.

Parameters
assocThe association on which the message was received
context_idThe presentation context ID for the message
requestThe incoming C-FIND-RQ message
Returns
Success or error if the message is not a valid C-FIND-RQ

Implements kcenon::pacs::services::scp_service.

Definition at line 79 of file query_scp.cpp.

82 {
83
84 using namespace network::dimse;
85 auto start_time = std::chrono::steady_clock::now();
86 std::string calling_ae{assoc.calling_ae()};
87
88 // Verify the message is a C-FIND request
89 if (request.command() != command_field::c_find_rq) {
92 "Expected C-FIND-RQ but received " +
93 std::string(to_string(request.command())));
94 }
95
96 // Verify we have a handler
97 if (!handler_) {
98 kcenon::common::get_event_bus().publish(
100 calling_ae,
102 "No query handler configured"
103 }
104 );
107 "No query handler configured");
108 }
109
110 // Get the SOP Class UID from the request
111 auto sop_class_uid = request.affected_sop_class_uid();
112
113 // Verify we have a dataset
114 if (!request.has_dataset()) {
115 return send_final_response(
116 assoc, context_id, request.message_id(),
117 sop_class_uid, status_error_cannot_understand);
118 }
119
120 // Extract query level
121 const auto& query_keys = request.dataset().value().get();
122 auto level = extract_query_level(query_keys);
123
124 if (!level.has_value()) {
125 // Invalid or missing query level
126 return send_final_response(
127 assoc, context_id, request.message_id(),
128 sop_class_uid, status_error_cannot_understand);
129 }
130
131 // Call the handler to get matching results
132 auto results = handler_(level.value(), query_keys, calling_ae);
133
134 // Apply max results limit if configured
135 if (max_results_ > 0 && results.size() > max_results_) {
136 results.resize(max_results_);
137 }
138
139 // Send pending responses for each result
140 for (const auto& result : results) {
141 // Check for cancel request
142 if (cancel_check_ && cancel_check_()) {
143 // Increment statistics
145
146 // Send cancel response
147 return send_final_response(
148 assoc, context_id, request.message_id(),
149 sop_class_uid, status_cancel);
150 }
151
152 // Send pending response with matching dataset
153 auto send_result = send_pending_response(
154 assoc, context_id, request.message_id(),
155 sop_class_uid, result);
156
157 if (send_result.is_err()) {
158 return send_result;
159 }
160 }
161
162 // Increment statistics
164
165 // Calculate execution time
166 auto end_time = std::chrono::steady_clock::now();
167 auto execution_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
168 end_time - start_time).count();
169
170 // Publish query executed event
171 kcenon::common::get_event_bus().publish(
173 to_event_level(level.value()),
174 calling_ae,
175 results.size(),
176 static_cast<uint64_t>(execution_time_ms)
177 }
178 );
179
180 // Emit ATNA audit event
181 if (auditor_) {
183 calling_ae,
184 std::string(assoc.called_ae()),
185 std::string(to_string(level.value())),
186 true);
187 }
188
189 // Send final success response (no dataset)
190 return send_final_response(
191 assoc, context_id, request.message_id(),
192 sop_class_uid, status_success);
193}
void audit_query(const std::string &calling_ae, const std::string &called_ae, const std::string &query_level, bool success)
Audit a C-FIND (Query) event.
std::optional< query_level > extract_query_level(const core::dicom_dataset &dataset) const
Extract query level from request dataset.
network::Result< std::monostate > send_pending_response(network::association &assoc, uint8_t context_id, uint16_t message_id, std::string_view sop_class_uid, const core::dicom_dataset &result)
Send a pending C-FIND response with matching dataset.
std::shared_ptr< kcenon::pacs::security::atna_service_auditor > auditor_
Definition query_scp.h:356
std::atomic< size_t > queries_processed_
Definition query_scp.h:358
network::Result< std::monostate > send_final_response(network::association &assoc, uint8_t context_id, uint16_t message_id, std::string_view sop_class_uid, network::dimse::status_code status)
Send the final C-FIND response (success or cancel)
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr int find_handler_not_set
Definition result.h:154
constexpr int find_unexpected_command
Definition result.h:157
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
Definition mpps_scp.h:60
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Definition result.h:249
Event published when a C-FIND query is executed.
Definition events.h:189
Event published when a C-FIND query fails.
Definition events.h:210

References kcenon::pacs::network::dimse::dimse_message::affected_sop_class_uid(), kcenon::pacs::security::atna_service_auditor::audit_query(), auditor_, kcenon::pacs::network::association::called_ae(), kcenon::pacs::network::association::calling_ae(), cancel_check_, kcenon::pacs::network::dimse::dimse_message::command(), kcenon::pacs::network::dimse::dimse_message::dataset(), extract_query_level(), kcenon::pacs::error_codes::find_handler_not_set, kcenon::pacs::error_codes::find_unexpected_command, handler_, kcenon::pacs::network::dimse::dimse_message::has_dataset(), max_results_, kcenon::pacs::network::dimse::dimse_message::message_id(), kcenon::pacs::pacs_void_error(), queries_processed_, send_final_response(), send_pending_response(), and kcenon::pacs::services::to_string().

Here is the call graph for this function:

◆ max_results()

size_t kcenon::pacs::services::query_scp::max_results ( ) const
nodiscardnoexcept

Get maximum number of results.

Returns
Maximum results (0 = unlimited)

Definition at line 55 of file query_scp.cpp.

55 {
56 return max_results_;
57}

References max_results_.

◆ queries_processed()

size_t kcenon::pacs::services::query_scp::queries_processed ( ) const
nodiscardnoexcept

Get total number of queries processed.

Returns
Number of C-FIND requests handled

Definition at line 203 of file query_scp.cpp.

203 {
204 return queries_processed_.load();
205}

References queries_processed_.

◆ reset_statistics()

void kcenon::pacs::services::query_scp::reset_statistics ( )
noexcept

Reset statistics counters.

Definition at line 207 of file query_scp.cpp.

207 {
209}

References queries_processed_.

◆ send_final_response()

network::Result< std::monostate > kcenon::pacs::services::query_scp::send_final_response ( network::association & assoc,
uint8_t context_id,
uint16_t message_id,
std::string_view sop_class_uid,
network::dimse::status_code status )
nodiscardprivate

Send the final C-FIND response (success or cancel)

Parameters
assocThe association
context_idThe presentation context ID
message_idThe original request message ID
sop_class_uidThe SOP Class UID
statusThe final status code
Returns
Success or error

Definition at line 251 of file query_scp.cpp.

256 {
257
258 using namespace network::dimse;
259
260 // Create C-FIND response with final status (no dataset)
261 auto response = make_c_find_rsp(
262 message_id,
263 sop_class_uid,
264 status
265 );
266
267 // Send the response
268 return assoc.send_dimse(context_id, response);
269}

References kcenon::pacs::network::association::send_dimse().

Referenced by handle_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ send_pending_response()

network::Result< std::monostate > kcenon::pacs::services::query_scp::send_pending_response ( network::association & assoc,
uint8_t context_id,
uint16_t message_id,
std::string_view sop_class_uid,
const core::dicom_dataset & result )
nodiscardprivate

Send a pending C-FIND response with matching dataset.

Parameters
assocThe association
context_idThe presentation context ID
message_idThe original request message ID
sop_class_uidThe SOP Class UID
resultThe matching dataset
Returns
Success or error

Definition at line 228 of file query_scp.cpp.

233 {
234
235 using namespace network::dimse;
236
237 // Create C-FIND response with pending status
238 auto response = make_c_find_rsp(
239 message_id,
240 sop_class_uid,
241 status_pending
242 );
243
244 // Attach the matching dataset
245 response.set_dataset(result);
246
247 // Send the response
248 return assoc.send_dimse(context_id, response);
249}

References kcenon::pacs::network::association::send_dimse().

Referenced by handle_message().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ service_name()

std::string_view kcenon::pacs::services::query_scp::service_name ( ) const
nodiscardoverridevirtualnoexcept

Get the service name.

Returns
"Query SCP"

Implements kcenon::pacs::services::scp_service.

Definition at line 195 of file query_scp.cpp.

195 {
196 return "Query SCP";
197}

◆ set_audit_handler()

void kcenon::pacs::services::query_scp::set_audit_handler ( std::shared_ptr< kcenon::pacs::security::atna_service_auditor > auditor)

Set the ATNA audit handler for C-FIND operations.

When set, audit events are emitted for each completed C-FIND operation.

Parameters
auditorShared pointer to an ATNA service auditor

Definition at line 63 of file query_scp.cpp.

64 {
65 auditor_ = std::move(auditor);
66}

References auditor_.

◆ set_cancel_check()

void kcenon::pacs::services::query_scp::set_cancel_check ( cancel_check check)

Set the cancel check function.

The cancel check is called periodically during query processing to check if a C-CANCEL has been received.

Parameters
checkThe cancel check function

Definition at line 59 of file query_scp.cpp.

59 {
60 cancel_check_ = std::move(check);
61}

References cancel_check_.

◆ set_handler()

void kcenon::pacs::services::query_scp::set_handler ( query_handler handler)

Set the query handler function.

The handler is called for each C-FIND request to retrieve matching records from the database.

Parameters
handlerThe query handler function

Definition at line 47 of file query_scp.cpp.

47 {
48 handler_ = std::move(handler);
49}

References handler_.

◆ set_max_results()

void kcenon::pacs::services::query_scp::set_max_results ( size_t max)
noexcept

Set maximum number of results to return.

Parameters
maxMaximum results (0 = unlimited)

Definition at line 51 of file query_scp.cpp.

51 {
52 max_results_ = max;
53}

◆ supported_sop_classes()

std::vector< std::string > kcenon::pacs::services::query_scp::supported_sop_classes ( ) const
nodiscardoverridevirtual

Get supported SOP Class UIDs.

Returns
Vector containing Patient Root and Study Root Find SOP Classes

Implements kcenon::pacs::services::scp_service.

Definition at line 72 of file query_scp.cpp.

72 {
73 return {
76 };
77}
constexpr std::string_view study_root_find_sop_class_uid
Study Root Query/Retrieve Information Model - FIND.
Definition query_scp.h:42
constexpr std::string_view patient_root_find_sop_class_uid
Patient Root Query/Retrieve Information Model - FIND.
Definition query_scp.h:38

References kcenon::pacs::services::patient_root_find_sop_class_uid, and kcenon::pacs::services::study_root_find_sop_class_uid.

Member Data Documentation

◆ auditor_

std::shared_ptr<kcenon::pacs::security::atna_service_auditor> kcenon::pacs::services::query_scp::auditor_
private

Definition at line 356 of file query_scp.h.

Referenced by handle_message(), and set_audit_handler().

◆ cancel_check_

cancel_check kcenon::pacs::services::query_scp::cancel_check_
private

Definition at line 355 of file query_scp.h.

Referenced by handle_message(), and set_cancel_check().

◆ handler_

query_handler kcenon::pacs::services::query_scp::handler_
private

Definition at line 354 of file query_scp.h.

Referenced by handle_message(), and set_handler().

◆ max_results_

size_t kcenon::pacs::services::query_scp::max_results_ {0}
private

Definition at line 357 of file query_scp.h.

357{0}; // 0 = unlimited

Referenced by handle_message(), and max_results().

◆ queries_processed_

std::atomic<size_t> kcenon::pacs::services::query_scp::queries_processed_ {0}
private

Definition at line 358 of file query_scp.h.

358{0};

Referenced by handle_message(), queries_processed(), and reset_statistics().


The documentation for this class was generated from the following files: