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

#include <retrieve_scu.h>

Collaboration diagram for kcenon::pacs::services::retrieve_scu:
Collaboration graph

Public Member Functions

 retrieve_scu (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct a Retrieve SCU with default configuration.
 
 retrieve_scu (const retrieve_scu_config &config, std::shared_ptr< di::ILogger > logger=nullptr)
 Construct a Retrieve SCU with custom configuration.
 
 ~retrieve_scu ()=default
 
 retrieve_scu (const retrieve_scu &)=delete
 
retrieve_scuoperator= (const retrieve_scu &)=delete
 
 retrieve_scu (retrieve_scu &&)=delete
 
retrieve_scuoperator= (retrieve_scu &&)=delete
 
network::Result< retrieve_resultmove (network::association &assoc, const core::dicom_dataset &query_keys, std::string_view destination_ae, retrieve_progress_callback progress=nullptr)
 Perform a C-MOVE operation with raw dataset.
 
network::Result< retrieve_resultget (network::association &assoc, const core::dicom_dataset &query_keys, retrieve_progress_callback progress=nullptr)
 Perform a C-GET operation with raw dataset.
 
network::Result< retrieve_resultretrieve_study (network::association &assoc, std::string_view study_uid, retrieve_progress_callback progress=nullptr)
 Retrieve a study by Study Instance UID.
 
network::Result< retrieve_resultretrieve_series (network::association &assoc, std::string_view series_uid, retrieve_progress_callback progress=nullptr)
 Retrieve a series by Series Instance UID.
 
network::Result< retrieve_resultretrieve_instance (network::association &assoc, std::string_view sop_instance_uid, retrieve_progress_callback progress=nullptr)
 Retrieve a single instance by SOP Instance UID.
 
network::Result< std::monostate > cancel (network::association &assoc, uint16_t message_id)
 Send a C-CANCEL request to stop an ongoing retrieve.
 
void set_config (const retrieve_scu_config &config)
 Update the SCU configuration.
 
void set_move_destination (std::string_view ae_title)
 Set the move destination AE title.
 
const retrieve_scu_configconfig () const noexcept
 Get the current configuration.
 
size_t retrieves_performed () const noexcept
 Get the number of retrieves performed since construction.
 
size_t instances_retrieved () const noexcept
 Get the total number of instances retrieved since construction.
 
size_t bytes_retrieved () const noexcept
 Get the total bytes retrieved since construction (C-GET only)
 
void reset_statistics () noexcept
 Reset statistics counters to zero.
 

Private Member Functions

network::Result< retrieve_resultperform_move (network::association &assoc, const core::dicom_dataset &query_keys, std::string_view destination_ae, retrieve_progress_callback progress)
 Internal C-MOVE implementation.
 
network::Result< retrieve_resultperform_get (network::association &assoc, const core::dicom_dataset &query_keys, retrieve_progress_callback progress)
 Internal C-GET implementation.
 
uint16_t next_message_id () noexcept
 Get the next message ID for DIMSE operations.
 
std::string_view get_move_sop_class_uid () const noexcept
 Get MOVE SOP Class UID based on current configuration.
 
std::string_view get_get_sop_class_uid () const noexcept
 Get GET SOP Class UID based on current configuration.
 
core::dicom_dataset build_study_query (std::string_view study_uid) const
 Build query dataset for study retrieval.
 
core::dicom_dataset build_series_query (std::string_view series_uid) const
 Build query dataset for series retrieval.
 
core::dicom_dataset build_instance_query (std::string_view sop_instance_uid) const
 Build query dataset for instance retrieval.
 

Private Attributes

std::shared_ptr< di::ILoggerlogger_
 Logger instance for service logging.
 
retrieve_scu_config config_
 Configuration.
 
std::atomic< uint16_t > message_id_counter_ {1}
 Message ID counter.
 
std::atomic< size_t > retrieves_performed_ {0}
 Statistics: number of retrieves performed.
 
std::atomic< size_t > instances_retrieved_ {0}
 Statistics: total number of instances retrieved.
 
std::atomic< size_t > bytes_retrieved_ {0}
 Statistics: total bytes retrieved.
 

Detailed Description

Definition at line 352 of file retrieve_scu.h.

Constructor & Destructor Documentation

◆ retrieve_scu() [1/4]

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

Construct a Retrieve SCU with default configuration.

Parameters
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 25 of file retrieve_scu.cpp.

26 : logger_(logger ? std::move(logger) : di::null_logger()) {}
std::shared_ptr< di::ILogger > logger_
Logger instance for service logging.
std::shared_ptr< ILogger > null_logger()
Get a shared null logger instance.
Definition ilogger.h:271

◆ retrieve_scu() [2/4]

kcenon::pacs::services::retrieve_scu::retrieve_scu ( const retrieve_scu_config & config,
std::shared_ptr< di::ILogger > logger = nullptr )
explicit

Construct a Retrieve SCU with custom configuration.

Parameters
configConfiguration options
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 28 of file retrieve_scu.cpp.

30 : logger_(logger ? std::move(logger) : di::null_logger()), config_(config) {}
retrieve_scu_config config_
Configuration.
const retrieve_scu_config & config() const noexcept
Get the current configuration.

◆ ~retrieve_scu()

kcenon::pacs::services::retrieve_scu::~retrieve_scu ( )
default

◆ retrieve_scu() [3/4]

kcenon::pacs::services::retrieve_scu::retrieve_scu ( const retrieve_scu & )
delete

◆ retrieve_scu() [4/4]

kcenon::pacs::services::retrieve_scu::retrieve_scu ( retrieve_scu && )
delete

Member Function Documentation

◆ build_instance_query()

core::dicom_dataset kcenon::pacs::services::retrieve_scu::build_instance_query ( std::string_view sop_instance_uid) const
nodiscardprivate

Build query dataset for instance retrieval.

Definition at line 526 of file retrieve_scu.cpp.

527 {
528
529 using namespace core;
530 using namespace encoding;
531
532 dicom_dataset ds;
533 ds.set_string(tags::query_retrieve_level, vr_type::CS, "IMAGE");
534 ds.set_string(tags::sop_instance_uid, vr_type::UI, std::string(sop_instance_uid));
535
536 return ds;
537}
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag query_retrieve_level
Query/Retrieve Level.

References kcenon::pacs::core::tags::query_retrieve_level, and kcenon::pacs::core::tags::sop_instance_uid.

Referenced by retrieve_instance().

Here is the caller graph for this function:

◆ build_series_query()

core::dicom_dataset kcenon::pacs::services::retrieve_scu::build_series_query ( std::string_view series_uid) const
nodiscardprivate

Build query dataset for series retrieval.

Definition at line 513 of file retrieve_scu.cpp.

514 {
515
516 using namespace core;
517 using namespace encoding;
518
519 dicom_dataset ds;
520 ds.set_string(tags::query_retrieve_level, vr_type::CS, "SERIES");
521 ds.set_string(tags::series_instance_uid, vr_type::UI, std::string(series_uid));
522
523 return ds;
524}
constexpr dicom_tag series_instance_uid
Series Instance UID.

References kcenon::pacs::core::tags::query_retrieve_level, and kcenon::pacs::core::tags::series_instance_uid.

Referenced by retrieve_series().

Here is the caller graph for this function:

◆ build_study_query()

core::dicom_dataset kcenon::pacs::services::retrieve_scu::build_study_query ( std::string_view study_uid) const
nodiscardprivate

Build query dataset for study retrieval.

Definition at line 500 of file retrieve_scu.cpp.

501 {
502
503 using namespace core;
504 using namespace encoding;
505
506 dicom_dataset ds;
507 ds.set_string(tags::query_retrieve_level, vr_type::CS, "STUDY");
508 ds.set_string(tags::study_instance_uid, vr_type::UI, std::string(study_uid));
509
510 return ds;
511}
constexpr dicom_tag study_instance_uid
Study Instance UID.

References kcenon::pacs::core::tags::query_retrieve_level, and kcenon::pacs::core::tags::study_instance_uid.

Referenced by retrieve_study().

Here is the caller graph for this function:

◆ bytes_retrieved()

size_t kcenon::pacs::services::retrieve_scu::bytes_retrieved ( ) const
nodiscardnoexcept

Get the total bytes retrieved since construction (C-GET only)

Returns
Total bytes of dataset data received

Definition at line 455 of file retrieve_scu.cpp.

455 {
456 return bytes_retrieved_.load(std::memory_order_relaxed);
457}
std::atomic< size_t > bytes_retrieved_
Statistics: total bytes retrieved.

References bytes_retrieved_.

◆ cancel()

network::Result< std::monostate > kcenon::pacs::services::retrieve_scu::cancel ( network::association & assoc,
uint16_t message_id )
nodiscard

Send a C-CANCEL request to stop an ongoing retrieve.

Parameters
assocThe association on which the retrieve is running
message_idThe message ID of the retrieve to cancel
Returns
Success or error

Definition at line 405 of file retrieve_scu.cpp.

407 {
408
409 using namespace network::dimse;
410
414
415 auto context_id = assoc.accepted_context_id(sop_class_uid);
416 if (!context_id) {
419 "No accepted presentation context for cancel");
420 }
421
422 // Build C-CANCEL-RQ message
423 dimse_message cancel_rq{command_field::c_cancel_rq, message_id};
424 return assoc.send_dimse(*context_id, cancel_rq);
425}
std::string_view get_get_sop_class_uid() const noexcept
Get GET SOP Class UID based on current configuration.
std::string_view get_move_sop_class_uid() const noexcept
Get MOVE SOP Class UID based on current configuration.
constexpr dicom_tag message_id
Message ID.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr int no_acceptable_context
Definition result.h:103
@ c_move
Request SCP to send to third party (requires move destination)
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234
retrieve_mode mode
Retrieve mode (C-MOVE or C-GET)

References kcenon::pacs::network::association::accepted_context_id(), kcenon::pacs::services::c_move, config_, get_get_sop_class_uid(), get_move_sop_class_uid(), kcenon::pacs::services::retrieve_scu_config::mode, kcenon::pacs::error_codes::no_acceptable_context, kcenon::pacs::pacs_error(), and kcenon::pacs::network::association::send_dimse().

Here is the call graph for this function:

◆ config()

const retrieve_scu_config & kcenon::pacs::services::retrieve_scu::config ( ) const
nodiscardnoexcept

Get the current configuration.

Returns
Reference to the current configuration

Definition at line 439 of file retrieve_scu.cpp.

439 {
440 return config_;
441}

References config_.

Referenced by set_config().

Here is the caller graph for this function:

◆ get()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::get ( network::association & assoc,
const core::dicom_dataset & query_keys,
retrieve_progress_callback progress = nullptr )
nodiscard

Perform a C-GET operation with raw dataset.

Sends a C-GET request with the provided query keys to retrieve matching images. The images are received directly on the same association via C-STORE sub-operations.

Parameters
assocThe established association to use
query_keysThe DICOM dataset containing query keys
progressOptional callback for progress updates
Returns
Result containing retrieve_result on success, or error message

Definition at line 186 of file retrieve_scu.cpp.

189 {
190
191 return perform_get(assoc, query_keys, progress);
192}
network::Result< retrieve_result > perform_get(network::association &assoc, const core::dicom_dataset &query_keys, retrieve_progress_callback progress)
Internal C-GET implementation.

References perform_get().

Referenced by retrieve_instance(), retrieve_series(), and retrieve_study().

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

◆ get_get_sop_class_uid()

std::string_view kcenon::pacs::services::retrieve_scu::get_get_sop_class_uid ( ) const
nodiscardprivatenoexcept

Get GET SOP Class UID based on current configuration.

Definition at line 489 of file retrieve_scu.cpp.

489 {
490 switch (config_.model) {
495 default:
497 }
498}
constexpr std::string_view study_root_get_sop_class_uid
Study Root Query/Retrieve Information Model - GET.
constexpr std::string_view patient_root_get_sop_class_uid
Patient Root Query/Retrieve Information Model - GET.
@ study_root
Study Root Query/Retrieve Information Model.
@ patient_root
Patient Root Query/Retrieve Information Model.
query_model model
Query information model (Patient Root or Study Root)

References config_, kcenon::pacs::services::retrieve_scu_config::model, kcenon::pacs::services::patient_root, kcenon::pacs::services::patient_root_get_sop_class_uid, kcenon::pacs::services::study_root, and kcenon::pacs::services::study_root_get_sop_class_uid.

Referenced by cancel(), and perform_get().

Here is the caller graph for this function:

◆ get_move_sop_class_uid()

std::string_view kcenon::pacs::services::retrieve_scu::get_move_sop_class_uid ( ) const
nodiscardprivatenoexcept

Get MOVE SOP Class UID based on current configuration.

Definition at line 478 of file retrieve_scu.cpp.

478 {
479 switch (config_.model) {
484 default:
486 }
487}
constexpr std::string_view study_root_move_sop_class_uid
Study Root Query/Retrieve Information Model - MOVE.
constexpr std::string_view patient_root_move_sop_class_uid
Patient Root Query/Retrieve Information Model - MOVE.

References config_, kcenon::pacs::services::retrieve_scu_config::model, kcenon::pacs::services::patient_root, kcenon::pacs::services::patient_root_move_sop_class_uid, kcenon::pacs::services::study_root, and kcenon::pacs::services::study_root_move_sop_class_uid.

Referenced by cancel(), and perform_move().

Here is the caller graph for this function:

◆ instances_retrieved()

size_t kcenon::pacs::services::retrieve_scu::instances_retrieved ( ) const
nodiscardnoexcept

Get the total number of instances retrieved since construction.

Returns
Total count of successful sub-operations

Definition at line 451 of file retrieve_scu.cpp.

451 {
452 return instances_retrieved_.load(std::memory_order_relaxed);
453}
std::atomic< size_t > instances_retrieved_
Statistics: total number of instances retrieved.

References instances_retrieved_.

◆ move()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::move ( network::association & assoc,
const core::dicom_dataset & query_keys,
std::string_view destination_ae,
retrieve_progress_callback progress = nullptr )
nodiscard

Perform a C-MOVE operation with raw dataset.

Sends a C-MOVE request with the provided query keys to retrieve matching images. The SCP will send the images to the specified destination AE title.

Parameters
assocThe established association to use
query_keysThe DICOM dataset containing query keys
destination_aeThe destination AE title to send images to
progressOptional callback for progress updates
Returns
Result containing retrieve_result on success, or error message

Definition at line 36 of file retrieve_scu.cpp.

40 {
41
42 return perform_move(assoc, query_keys, destination_ae, progress);
43}
network::Result< retrieve_result > perform_move(network::association &assoc, const core::dicom_dataset &query_keys, std::string_view destination_ae, retrieve_progress_callback progress)
Internal C-MOVE implementation.

References perform_move().

Referenced by retrieve_instance(), retrieve_series(), and retrieve_study().

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

◆ next_message_id()

uint16_t kcenon::pacs::services::retrieve_scu::next_message_id ( )
nodiscardprivatenoexcept

Get the next message ID for DIMSE operations.

Definition at line 469 of file retrieve_scu.cpp.

469 {
470 uint16_t id = message_id_counter_.fetch_add(1, std::memory_order_relaxed);
471 // Wrap around at 0xFFFF, skip 0 (reserved)
472 if (id == 0) {
473 id = message_id_counter_.fetch_add(1, std::memory_order_relaxed);
474 }
475 return id;
476}
std::atomic< uint16_t > message_id_counter_
Message ID counter.
@ id
Implant Displaced (alternate code)

References message_id_counter_.

Referenced by perform_get(), and perform_move().

Here is the caller graph for this function:

◆ operator=() [1/2]

retrieve_scu & kcenon::pacs::services::retrieve_scu::operator= ( const retrieve_scu & )
delete

◆ operator=() [2/2]

retrieve_scu & kcenon::pacs::services::retrieve_scu::operator= ( retrieve_scu && )
delete

◆ perform_get()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::perform_get ( network::association & assoc,
const core::dicom_dataset & query_keys,
retrieve_progress_callback progress )
nodiscardprivate

Internal C-GET implementation.

Definition at line 194 of file retrieve_scu.cpp.

197 {
198
199 using namespace network::dimse;
200
201 auto start_time = std::chrono::steady_clock::now();
203
204 // Verify association is established
205 if (!assoc.is_established()) {
208 "Association not established");
209 }
210
211 // Get SOP Class UID
213
214 // Get accepted presentation context for this SOP class
215 auto context_id = assoc.accepted_context_id(sop_class_uid);
216 if (!context_id) {
219 "No accepted presentation context for SOP Class: " +
220 std::string(sop_class_uid));
221 }
222
223 // Build C-GET-RQ message
224 dimse_message request{command_field::c_get_rq, message_id};
225 request.set_affected_sop_class_uid(sop_class_uid);
226 request.set_priority(config_.priority);
227 request.set_dataset(query_keys);
228
229 logger_->debug_fmt("Sending C-GET request (message_id={}, sop_class={})",
230 message_id, sop_class_uid);
231
232 // Send the request
233 auto send_result = assoc.send_dimse(*context_id, request);
234 if (send_result.is_err()) {
235 return send_result.error();
236 }
237
238 // Initialize progress tracking
239 retrieve_progress prog;
240 prog.start_time = start_time;
241
242 // Receive responses (C-GET-RSP and C-STORE-RQ interleaved)
243 retrieve_result result;
244 bool retrieve_complete = false;
245
246 while (!retrieve_complete) {
247 auto recv_result = assoc.receive_dimse(config_.timeout);
248 if (recv_result.is_err()) {
249 return recv_result.error();
250 }
251
252 auto& [recv_context_id, msg] = recv_result.value();
253 auto cmd = msg.command();
254
255 if (cmd == command_field::c_get_rsp) {
256 auto status = msg.status();
257
258 // Extract sub-operation counts
259 if (auto remaining = msg.remaining_subops()) {
260 prog.remaining = *remaining;
261 }
262 if (auto completed = msg.completed_subops()) {
263 prog.completed = *completed;
264 result.completed = *completed;
265 }
266 if (auto failed = msg.failed_subops()) {
267 prog.failed = *failed;
268 result.failed = *failed;
269 }
270 if (auto warning = msg.warning_subops()) {
271 prog.warning = *warning;
272 result.warning = *warning;
273 }
274
275 // Call progress callback
276 if (progress) {
277 progress(prog);
278 }
279
280 // Check completion status
281 if (status == status_success) {
282 retrieve_complete = true;
283 result.final_status = static_cast<uint16_t>(status);
284 } else if (status == status_cancel) {
285 retrieve_complete = true;
286 result.final_status = static_cast<uint16_t>(status);
287 } else if ((status & 0xF000) == 0xA000 ||
288 (status & 0xF000) == 0xC000) {
289 // Error status
290 retrieve_complete = true;
291 result.final_status = static_cast<uint16_t>(status);
292 }
293
294 } else if (cmd == command_field::c_store_rq) {
295 // Incoming C-STORE sub-operation - store the received instance
296 if (msg.has_dataset()) {
297 auto dataset_result = msg.dataset();
298 if (dataset_result.is_ok()) {
299 result.received_instances.push_back(dataset_result.value().get());
300
301 // Update bytes transferred (approximate)
302 bytes_retrieved_.fetch_add(1024, std::memory_order_relaxed);
303 }
304 }
305
306 // Send C-STORE response
307 auto sop_class = msg.affected_sop_class_uid();
308 auto sop_instance = msg.affected_sop_instance_uid();
309
310 auto store_rsp = make_c_store_rsp(
311 msg.message_id(),
312 sop_class,
313 sop_instance,
314 status_success
315 );
316
317 auto send_rsp_result = assoc.send_dimse(recv_context_id, store_rsp);
318 if (send_rsp_result.is_err()) {
319 logger_->warn_fmt("Failed to send C-STORE response: {}",
320 send_rsp_result.error().message);
321 }
322 }
323 }
324
325 auto end_time = std::chrono::steady_clock::now();
326 result.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
327 end_time - start_time);
328
329 // Update statistics
330 retrieves_performed_.fetch_add(1, std::memory_order_relaxed);
331 instances_retrieved_.fetch_add(result.completed, std::memory_order_relaxed);
332
333 logger_->debug_fmt("C-GET completed: {} completed, {} failed, {} received in {} ms",
334 result.completed, result.failed,
335 result.received_instances.size(), result.elapsed.count());
336
337 return result;
338}
std::atomic< size_t > retrieves_performed_
Statistics: number of retrieves performed.
uint16_t next_message_id() noexcept
Get the next message ID for DIMSE operations.
constexpr dicom_tag status
Status.
constexpr int association_not_established
Definition result.h:202
@ warning
Printer has a non-critical issue.
@ completed
Procedure completed successfully.
std::chrono::milliseconds timeout
Timeout for receiving responses (milliseconds)
uint16_t priority
Priority for DIMSE operations (0=medium, 1=high, 2=low)

References kcenon::pacs::network::association::accepted_context_id(), kcenon::pacs::error_codes::association_not_established, bytes_retrieved_, kcenon::pacs::services::completed, kcenon::pacs::services::retrieve_progress::completed, kcenon::pacs::services::retrieve_result::completed, config_, kcenon::pacs::services::retrieve_result::elapsed, kcenon::pacs::services::retrieve_progress::failed, kcenon::pacs::services::retrieve_result::failed, kcenon::pacs::services::retrieve_result::final_status, get_get_sop_class_uid(), instances_retrieved_, kcenon::pacs::network::association::is_established(), logger_, next_message_id(), kcenon::pacs::error_codes::no_acceptable_context, kcenon::pacs::pacs_error(), kcenon::pacs::services::retrieve_scu_config::priority, kcenon::pacs::network::association::receive_dimse(), kcenon::pacs::services::retrieve_result::received_instances, kcenon::pacs::services::retrieve_progress::remaining, retrieves_performed_, kcenon::pacs::network::association::send_dimse(), kcenon::pacs::services::retrieve_progress::start_time, kcenon::pacs::services::retrieve_scu_config::timeout, kcenon::pacs::services::warning, kcenon::pacs::services::retrieve_progress::warning, and kcenon::pacs::services::retrieve_result::warning.

Referenced by get().

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

◆ perform_move()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::perform_move ( network::association & assoc,
const core::dicom_dataset & query_keys,
std::string_view destination_ae,
retrieve_progress_callback progress )
nodiscardprivate

Internal C-MOVE implementation.

Definition at line 45 of file retrieve_scu.cpp.

49 {
50
51 using namespace network::dimse;
52
53 auto start_time = std::chrono::steady_clock::now();
55
56 // Verify association is established
57 if (!assoc.is_established()) {
60 "Association not established");
61 }
62
63 // Validate move destination
64 if (destination_ae.empty()) {
67 "Move destination AE title is required");
68 }
69
70 // Get SOP Class UID
72
73 // Get accepted presentation context for this SOP class
74 auto context_id = assoc.accepted_context_id(sop_class_uid);
75 if (!context_id) {
78 "No accepted presentation context for SOP Class: " +
79 std::string(sop_class_uid));
80 }
81
82 // Build C-MOVE-RQ message
83 dimse_message request{command_field::c_move_rq, message_id};
84 request.set_affected_sop_class_uid(sop_class_uid);
85 request.set_priority(config_.priority);
86
87 // Set Move Destination
88 request.command_set().set_string(
89 tag_move_destination,
91 std::string(destination_ae));
92
93 request.set_dataset(query_keys);
94
95 logger_->debug_fmt("Sending C-MOVE request (message_id={}, dest={}, sop_class={})",
96 message_id, destination_ae, sop_class_uid);
97
98 // Send the request
99 auto send_result = assoc.send_dimse(*context_id, request);
100 if (send_result.is_err()) {
101 return send_result.error();
102 }
103
104 // Initialize progress tracking
105 retrieve_progress prog;
106 prog.start_time = start_time;
107
108 // Receive responses
109 retrieve_result result;
110 bool move_complete = false;
111
112 while (!move_complete) {
113 auto recv_result = assoc.receive_dimse(config_.timeout);
114 if (recv_result.is_err()) {
115 return recv_result.error();
116 }
117
118 const auto& [recv_context_id, response] = recv_result.value();
119
120 // Verify it's a C-MOVE response
121 if (response.command() != command_field::c_move_rsp) {
124 "Expected C-MOVE-RSP but received " +
125 std::string(to_string(response.command())));
126 }
127
128 auto status = response.status();
129
130 // Extract sub-operation counts
131 if (auto remaining = response.remaining_subops()) {
132 prog.remaining = *remaining;
133 }
134 if (auto completed = response.completed_subops()) {
135 prog.completed = *completed;
136 result.completed = *completed;
137 }
138 if (auto failed = response.failed_subops()) {
139 prog.failed = *failed;
140 result.failed = *failed;
141 }
142 if (auto warning = response.warning_subops()) {
143 prog.warning = *warning;
144 result.warning = *warning;
145 }
146
147 // Call progress callback
148 if (progress) {
149 progress(prog);
150 }
151
152 // Check completion status
153 if (status == status_success) {
154 move_complete = true;
155 result.final_status = static_cast<uint16_t>(status);
156 } else if (status == status_cancel) {
157 move_complete = true;
158 result.final_status = static_cast<uint16_t>(status);
159 } else if ((status & 0xF000) == 0xA000 ||
160 (status & 0xF000) == 0xC000) {
161 // Error status (Refused or Failed)
162 move_complete = true;
163 result.final_status = static_cast<uint16_t>(status);
164 }
165 // status_pending (0xFF00) continues the loop
166 }
167
168 auto end_time = std::chrono::steady_clock::now();
169 result.elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
170 end_time - start_time);
171
172 // Update statistics
173 retrieves_performed_.fetch_add(1, std::memory_order_relaxed);
174 instances_retrieved_.fetch_add(result.completed, std::memory_order_relaxed);
175
176 logger_->debug_fmt("C-MOVE completed: {} completed, {} failed in {} ms",
177 result.completed, result.failed, result.elapsed.count());
178
179 return result;
180}
@ AE
Application Entity (16 chars max)
constexpr int retrieve_missing_destination
Definition result.h:162
constexpr int retrieve_unexpected_command
Definition result.h:165
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
Definition mpps_scp.h:60

References kcenon::pacs::network::association::accepted_context_id(), kcenon::pacs::encoding::AE, kcenon::pacs::error_codes::association_not_established, kcenon::pacs::services::completed, kcenon::pacs::services::retrieve_progress::completed, kcenon::pacs::services::retrieve_result::completed, config_, kcenon::pacs::services::retrieve_result::elapsed, kcenon::pacs::services::retrieve_progress::failed, kcenon::pacs::services::retrieve_result::failed, kcenon::pacs::services::retrieve_result::final_status, get_move_sop_class_uid(), instances_retrieved_, kcenon::pacs::network::association::is_established(), logger_, next_message_id(), kcenon::pacs::error_codes::no_acceptable_context, kcenon::pacs::pacs_error(), kcenon::pacs::services::retrieve_scu_config::priority, kcenon::pacs::network::association::receive_dimse(), kcenon::pacs::services::retrieve_progress::remaining, kcenon::pacs::error_codes::retrieve_missing_destination, kcenon::pacs::error_codes::retrieve_unexpected_command, retrieves_performed_, kcenon::pacs::network::association::send_dimse(), kcenon::pacs::services::retrieve_progress::start_time, kcenon::pacs::services::retrieve_scu_config::timeout, kcenon::pacs::services::to_string(), kcenon::pacs::services::warning, kcenon::pacs::services::retrieve_progress::warning, and kcenon::pacs::services::retrieve_result::warning.

Referenced by move().

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

◆ reset_statistics()

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

Reset statistics counters to zero.

Definition at line 459 of file retrieve_scu.cpp.

459 {
460 retrieves_performed_.store(0, std::memory_order_relaxed);
461 instances_retrieved_.store(0, std::memory_order_relaxed);
462 bytes_retrieved_.store(0, std::memory_order_relaxed);
463}

References bytes_retrieved_, instances_retrieved_, and retrieves_performed_.

◆ retrieve_instance()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::retrieve_instance ( network::association & assoc,
std::string_view sop_instance_uid,
retrieve_progress_callback progress = nullptr )
nodiscard

Retrieve a single instance by SOP Instance UID.

Uses C-MOVE or C-GET based on current configuration.

Parameters
assocThe established association to use
sop_instance_uidThe SOP Instance UID to retrieve
progressOptional callback for progress updates
Returns
Result containing retrieve_result on success

Definition at line 382 of file retrieve_scu.cpp.

385 {
386
387 auto query_ds = build_instance_query(sop_instance_uid);
388
390 if (config_.move_destination.empty()) {
393 "Move destination is required for C-MOVE mode");
394 }
395 return move(assoc, query_ds, config_.move_destination, progress);
396 } else {
397 return get(assoc, query_ds, progress);
398 }
399}
core::dicom_dataset build_instance_query(std::string_view sop_instance_uid) const
Build query dataset for instance retrieval.
network::Result< retrieve_result > get(network::association &assoc, const core::dicom_dataset &query_keys, retrieve_progress_callback progress=nullptr)
Perform a C-GET operation with raw dataset.
network::Result< retrieve_result > move(network::association &assoc, const core::dicom_dataset &query_keys, std::string_view destination_ae, retrieve_progress_callback progress=nullptr)
Perform a C-MOVE operation with raw dataset.
std::string move_destination
Move destination AE title (required for C-MOVE mode)

References build_instance_query(), kcenon::pacs::services::c_move, config_, get(), kcenon::pacs::services::retrieve_scu_config::mode, move(), kcenon::pacs::services::retrieve_scu_config::move_destination, kcenon::pacs::pacs_error(), and kcenon::pacs::error_codes::retrieve_missing_destination.

Here is the call graph for this function:

◆ retrieve_series()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::retrieve_series ( network::association & assoc,
std::string_view series_uid,
retrieve_progress_callback progress = nullptr )
nodiscard

Retrieve a series by Series Instance UID.

Uses C-MOVE or C-GET based on current configuration.

Parameters
assocThe established association to use
series_uidThe Series Instance UID to retrieve
progressOptional callback for progress updates
Returns
Result containing retrieve_result on success

Definition at line 363 of file retrieve_scu.cpp.

366 {
367
368 auto query_ds = build_series_query(series_uid);
369
371 if (config_.move_destination.empty()) {
374 "Move destination is required for C-MOVE mode");
375 }
376 return move(assoc, query_ds, config_.move_destination, progress);
377 } else {
378 return get(assoc, query_ds, progress);
379 }
380}
core::dicom_dataset build_series_query(std::string_view series_uid) const
Build query dataset for series retrieval.

References build_series_query(), kcenon::pacs::services::c_move, config_, get(), kcenon::pacs::services::retrieve_scu_config::mode, move(), kcenon::pacs::services::retrieve_scu_config::move_destination, kcenon::pacs::pacs_error(), and kcenon::pacs::error_codes::retrieve_missing_destination.

Referenced by kcenon::pacs::client::job_manager::impl::execute_retrieve_job().

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

◆ retrieve_study()

network::Result< retrieve_result > kcenon::pacs::services::retrieve_scu::retrieve_study ( network::association & assoc,
std::string_view study_uid,
retrieve_progress_callback progress = nullptr )
nodiscard

Retrieve a study by Study Instance UID.

Uses C-MOVE or C-GET based on current configuration.

Parameters
assocThe established association to use
study_uidThe Study Instance UID to retrieve
progressOptional callback for progress updates
Returns
Result containing retrieve_result on success

Definition at line 344 of file retrieve_scu.cpp.

347 {
348
349 auto query_ds = build_study_query(study_uid);
350
352 if (config_.move_destination.empty()) {
355 "Move destination is required for C-MOVE mode");
356 }
357 return move(assoc, query_ds, config_.move_destination, progress);
358 } else {
359 return get(assoc, query_ds, progress);
360 }
361}
core::dicom_dataset build_study_query(std::string_view study_uid) const
Build query dataset for study retrieval.

References build_study_query(), kcenon::pacs::services::c_move, config_, get(), kcenon::pacs::services::retrieve_scu_config::mode, move(), kcenon::pacs::services::retrieve_scu_config::move_destination, kcenon::pacs::pacs_error(), and kcenon::pacs::error_codes::retrieve_missing_destination.

Referenced by kcenon::pacs::client::job_manager::impl::execute_retrieve_job(), and kcenon::pacs::workflow::auto_prefetch_service::prefetch_study().

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

◆ retrieves_performed()

size_t kcenon::pacs::services::retrieve_scu::retrieves_performed ( ) const
nodiscardnoexcept

Get the number of retrieves performed since construction.

Returns
Count of C-MOVE/C-GET requests sent

Definition at line 447 of file retrieve_scu.cpp.

447 {
448 return retrieves_performed_.load(std::memory_order_relaxed);
449}

References retrieves_performed_.

◆ set_config()

void kcenon::pacs::services::retrieve_scu::set_config ( const retrieve_scu_config & config)

Update the SCU configuration.

Parameters
configNew configuration options

Definition at line 431 of file retrieve_scu.cpp.

431 {
432 config_ = config;
433}

References config(), and config_.

Here is the call graph for this function:

◆ set_move_destination()

void kcenon::pacs::services::retrieve_scu::set_move_destination ( std::string_view ae_title)

Set the move destination AE title.

Parameters
ae_titleThe destination AE title for C-MOVE operations

Definition at line 435 of file retrieve_scu.cpp.

435 {
436 config_.move_destination = std::string(ae_title);
437}

References config_, and kcenon::pacs::services::retrieve_scu_config::move_destination.

Member Data Documentation

◆ bytes_retrieved_

std::atomic<size_t> kcenon::pacs::services::retrieve_scu::bytes_retrieved_ {0}
private

Statistics: total bytes retrieved.

Definition at line 617 of file retrieve_scu.h.

617{0};

Referenced by bytes_retrieved(), perform_get(), and reset_statistics().

◆ config_

◆ instances_retrieved_

std::atomic<size_t> kcenon::pacs::services::retrieve_scu::instances_retrieved_ {0}
private

Statistics: total number of instances retrieved.

Definition at line 614 of file retrieve_scu.h.

614{0};

Referenced by instances_retrieved(), perform_get(), perform_move(), and reset_statistics().

◆ logger_

std::shared_ptr<di::ILogger> kcenon::pacs::services::retrieve_scu::logger_
private

Logger instance for service logging.

Definition at line 602 of file retrieve_scu.h.

Referenced by perform_get(), and perform_move().

◆ message_id_counter_

std::atomic<uint16_t> kcenon::pacs::services::retrieve_scu::message_id_counter_ {1}
private

Message ID counter.

Definition at line 608 of file retrieve_scu.h.

608{1};

Referenced by next_message_id().

◆ retrieves_performed_

std::atomic<size_t> kcenon::pacs::services::retrieve_scu::retrieves_performed_ {0}
private

Statistics: number of retrieves performed.

Definition at line 611 of file retrieve_scu.h.

611{0};

Referenced by perform_get(), perform_move(), reset_statistics(), and retrieves_performed().


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