PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
retrieve_scu.h
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
22#ifndef PACS_SERVICES_RETRIEVE_SCU_HPP
23#define PACS_SERVICES_RETRIEVE_SCU_HPP
24
25#include "query_scu.h"
26#include "retrieve_scp.h"
31
32#include <atomic>
33#include <chrono>
34#include <cstdint>
35#include <functional>
36#include <string>
37#include <vector>
38
39namespace kcenon::pacs::services {
40
41// =============================================================================
42// Retrieve Mode Enumeration
43// =============================================================================
44
50enum class retrieve_mode {
51 c_move,
52 c_get
53};
54
60[[nodiscard]] constexpr std::string_view to_string(retrieve_mode mode) noexcept {
61 switch (mode) {
62 case retrieve_mode::c_move: return "C-MOVE";
63 case retrieve_mode::c_get: return "C-GET";
64 default: return "Unknown";
65 }
66}
67
68// =============================================================================
69// Retrieve Progress Structure
70// =============================================================================
71
78 uint16_t remaining{0};
79 uint16_t completed{0};
80 uint16_t failed{0};
81 uint16_t warning{0};
82 std::chrono::steady_clock::time_point start_time;
83
88 [[nodiscard]] uint16_t total() const noexcept {
89 return remaining + completed + failed + warning;
90 }
91
96 [[nodiscard]] float percent() const noexcept {
97 uint16_t t = total();
98 if (t == 0) return 0.0f;
99 return (static_cast<float>(completed + failed + warning) / t) * 100.0f;
100 }
101
106 [[nodiscard]] std::chrono::milliseconds elapsed() const noexcept {
107 return std::chrono::duration_cast<std::chrono::milliseconds>(
108 std::chrono::steady_clock::now() - start_time);
109 }
110};
111
112// =============================================================================
113// Retrieve Result Structure
114// =============================================================================
115
123 uint16_t completed{0};
124
126 uint16_t failed{0};
127
129 uint16_t warning{0};
130
132 uint16_t final_status{0};
133
135 std::chrono::milliseconds elapsed{0};
136
138 std::vector<core::dicom_dataset> received_instances;
139
144 [[nodiscard]] bool is_success() const noexcept {
145 return final_status == 0x0000 && failed == 0;
146 }
147
152 [[nodiscard]] bool is_cancelled() const noexcept {
153 return final_status == 0xFE00;
154 }
155
160 [[nodiscard]] bool has_failures() const noexcept {
161 return failed > 0;
162 }
163
168 [[nodiscard]] bool has_warnings() const noexcept {
169 return warning > 0;
170 }
171};
172
173// =============================================================================
174// Progress Callback Type
175// =============================================================================
176
184using retrieve_progress_callback = std::function<void(const retrieve_progress&)>;
185
186// =============================================================================
187// Instance Receive Callback Type (for C-GET)
188// =============================================================================
189
198using instance_receive_callback = std::function<bool(const core::dicom_dataset&)>;
199
200// =============================================================================
201// Retrieve SCU Configuration
202// =============================================================================
203
226
227// =============================================================================
228// Retrieve SCU Class
229// =============================================================================
230
353public:
354 // =========================================================================
355 // Construction
356 // =========================================================================
357
363 explicit retrieve_scu(std::shared_ptr<di::ILogger> logger = nullptr);
364
372 std::shared_ptr<di::ILogger> logger = nullptr);
373
374 ~retrieve_scu() = default;
375
376 // Non-copyable, non-movable (due to atomic members)
377 retrieve_scu(const retrieve_scu&) = delete;
381
382 // =========================================================================
383 // C-MOVE Operations
384 // =========================================================================
385
401 const core::dicom_dataset& query_keys,
402 std::string_view destination_ae,
403 retrieve_progress_callback progress = nullptr);
404
405 // =========================================================================
406 // C-GET Operations
407 // =========================================================================
408
423 const core::dicom_dataset& query_keys,
424 retrieve_progress_callback progress = nullptr);
425
426 // =========================================================================
427 // Convenience Methods
428 // =========================================================================
429
442 std::string_view study_uid,
443 retrieve_progress_callback progress = nullptr);
444
457 std::string_view series_uid,
458 retrieve_progress_callback progress = nullptr);
459
472 std::string_view sop_instance_uid,
473 retrieve_progress_callback progress = nullptr);
474
475 // =========================================================================
476 // C-CANCEL Support
477 // =========================================================================
478
488 uint16_t message_id);
489
490 // =========================================================================
491 // Configuration
492 // =========================================================================
493
500
506 void set_move_destination(std::string_view ae_title);
507
513 [[nodiscard]] const retrieve_scu_config& config() const noexcept;
514
515 // =========================================================================
516 // Statistics
517 // =========================================================================
518
523 [[nodiscard]] size_t retrieves_performed() const noexcept;
524
529 [[nodiscard]] size_t instances_retrieved() const noexcept;
530
535 [[nodiscard]] size_t bytes_retrieved() const noexcept;
536
540 void reset_statistics() noexcept;
541
542private:
543 // =========================================================================
544 // Private Implementation
545 // =========================================================================
546
550 [[nodiscard]] network::Result<retrieve_result> perform_move(
551 network::association& assoc,
552 const core::dicom_dataset& query_keys,
553 std::string_view destination_ae,
555
559 [[nodiscard]] network::Result<retrieve_result> perform_get(
560 network::association& assoc,
561 const core::dicom_dataset& query_keys,
563
567 [[nodiscard]] uint16_t next_message_id() noexcept;
568
572 [[nodiscard]] std::string_view get_move_sop_class_uid() const noexcept;
573
577 [[nodiscard]] std::string_view get_get_sop_class_uid() const noexcept;
578
582 [[nodiscard]] core::dicom_dataset build_study_query(
583 std::string_view study_uid) const;
584
588 [[nodiscard]] core::dicom_dataset build_series_query(
589 std::string_view series_uid) const;
590
594 [[nodiscard]] core::dicom_dataset build_instance_query(
595 std::string_view sop_instance_uid) const;
596
597 // =========================================================================
598 // Private Members
599 // =========================================================================
600
602 std::shared_ptr<di::ILogger> logger_;
603
606
608 std::atomic<uint16_t> message_id_counter_{1};
609
611 std::atomic<size_t> retrieves_performed_{0};
612
614 std::atomic<size_t> instances_retrieved_{0};
615
617 std::atomic<size_t> bytes_retrieved_{0};
618};
619
620} // namespace kcenon::pacs::services
621
622#endif // PACS_SERVICES_RETRIEVE_SCU_HPP
DICOM Association management per PS3.8.
network::Result< retrieve_result > retrieve_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.
std::atomic< size_t > instances_retrieved_
Statistics: total number of instances retrieved.
network::Result< retrieve_result > retrieve_series(network::association &assoc, std::string_view series_uid, retrieve_progress_callback progress=nullptr)
Retrieve a series by Series Instance UID.
retrieve_scu(const retrieve_scu &)=delete
retrieve_scu_config config_
Configuration.
retrieve_scu & operator=(retrieve_scu &&)=delete
network::Result< retrieve_result > perform_get(network::association &assoc, const core::dicom_dataset &query_keys, retrieve_progress_callback progress)
Internal C-GET implementation.
const retrieve_scu_config & config() const noexcept
Get the current configuration.
size_t retrieves_performed() const noexcept
Get the number of retrieves performed since construction.
core::dicom_dataset build_instance_query(std::string_view sop_instance_uid) const
Build query dataset for instance retrieval.
network::Result< retrieve_result > retrieve_study(network::association &assoc, std::string_view study_uid, retrieve_progress_callback progress=nullptr)
Retrieve a study by Study Instance UID.
void set_move_destination(std::string_view ae_title)
Set the move destination AE title.
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.
void reset_statistics() noexcept
Reset statistics counters to zero.
std::string_view get_get_sop_class_uid() const noexcept
Get GET SOP Class UID based on current configuration.
std::atomic< size_t > retrieves_performed_
Statistics: number of retrieves performed.
size_t instances_retrieved() const noexcept
Get the total number of instances retrieved since construction.
uint16_t next_message_id() noexcept
Get the next message ID for DIMSE operations.
std::atomic< uint16_t > message_id_counter_
Message ID counter.
std::atomic< size_t > bytes_retrieved_
Statistics: total bytes retrieved.
retrieve_scu & operator=(const retrieve_scu &)=delete
void set_config(const retrieve_scu_config &config)
Update the SCU configuration.
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.
retrieve_scu(retrieve_scu &&)=delete
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.
std::string_view get_move_sop_class_uid() const noexcept
Get MOVE SOP Class UID based on current configuration.
std::shared_ptr< di::ILogger > logger_
Logger instance for service logging.
size_t bytes_retrieved() const noexcept
Get the total bytes retrieved since construction (C-GET only)
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.
retrieve_scu(std::shared_ptr< di::ILogger > logger=nullptr)
Construct a Retrieve SCU with default configuration.
DICOM Dataset - ordered collection of Data Elements.
DIMSE message encoding and decoding.
Logger interface for dependency injection.
retrieve_mode
DICOM Retrieve Mode (C-MOVE vs C-GET)
@ c_get
Receive directly from SCP on same association.
@ c_move
Request SCP to send to third party (requires move destination)
std::function< bool(const core::dicom_dataset &)> instance_receive_callback
Callback type for receiving instances during C-GET.
std::function< void(const retrieve_progress &)> retrieve_progress_callback
Callback type for retrieve progress updates.
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
Definition mpps_scp.h:60
query_level
DICOM Query/Retrieve level enumeration.
Definition query_scp.h:63
@ study
Study level - query study information.
query_model
DICOM Query/Retrieve Information Model.
Definition query_scu.h:47
@ study_root
Study Root Query/Retrieve Information Model.
DICOM Query SCU service (C-FIND sender)
DICOM Retrieve SCP service (C-MOVE/C-GET handler)
Progress information for a retrieve operation.
uint16_t failed
Number of failed sub-operations.
uint16_t remaining
Number of remaining sub-operations.
uint16_t completed
Number of completed sub-operations.
uint16_t warning
Number of sub-operations with warnings.
uint16_t total() const noexcept
Get total number of sub-operations.
float percent() const noexcept
Get completion percentage.
std::chrono::milliseconds elapsed() const noexcept
Get elapsed time since start.
std::chrono::steady_clock::time_point start_time
Result of a retrieve operation (C-MOVE or C-GET)
uint16_t warning
Number of sub-operations with warnings.
uint16_t completed
Number of successfully completed sub-operations.
uint16_t failed
Number of failed sub-operations.
bool is_success() const noexcept
Check if the retrieve was fully successful.
bool has_failures() const noexcept
Check if any sub-operations failed.
bool has_warnings() const noexcept
Check if any sub-operations had warnings.
uint16_t final_status
Final DIMSE status code.
std::vector< core::dicom_dataset > received_instances
Received instances (for C-GET mode only)
std::chrono::milliseconds elapsed
Retrieve execution time.
bool is_cancelled() const noexcept
Check if the retrieve was cancelled.
Configuration for Retrieve SCU service.
std::chrono::milliseconds timeout
Timeout for receiving responses (milliseconds)
query_level level
Query level (Study, Series, or Image)
query_model model
Query information model (Patient Root or Study Root)
retrieve_mode mode
Retrieve mode (C-MOVE or C-GET)
uint16_t priority
Priority for DIMSE operations (0=medium, 1=high, 2=low)
std::string move_destination
Move destination AE title (required for C-MOVE mode)