PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
ups_query_scp.cpp
Go to the documentation of this file.
1
7
12
13namespace kcenon::pacs::services {
14
15// =============================================================================
16// Construction
17// =============================================================================
18
19ups_query_scp::ups_query_scp(std::shared_ptr<di::ILogger> logger)
20 : scp_service(std::move(logger)) {}
21
22// =============================================================================
23// Configuration
24// =============================================================================
25
27 handler_ = std::move(handler);
28}
29
30void ups_query_scp::set_max_results(size_t max) noexcept {
31 max_results_ = max;
32}
33
34size_t ups_query_scp::max_results() const noexcept {
35 return max_results_;
36}
37
41
42// =============================================================================
43// scp_service Interface Implementation
44// =============================================================================
45
46std::vector<std::string> ups_query_scp::supported_sop_classes() const {
47 return {
49 };
50}
51
54 uint8_t context_id,
55 const network::dimse::dimse_message& request) {
56
57 using namespace network::dimse;
58
59 // Verify the message is a C-FIND request
60 if (request.command() != command_field::c_find_rq) {
63 "Expected C-FIND-RQ but received " +
64 std::string(to_string(request.command())));
65 }
66
67 // Verify we have a handler
68 if (!handler_) {
71 "No UPS query handler configured");
72 }
73
74 // Get the SOP Class UID from the request
75 auto sop_class_uid = request.affected_sop_class_uid();
76
77 // Verify the SOP Class is UPS Query FIND
78 if (sop_class_uid != ups_query_find_sop_class_uid) {
80 assoc, context_id, request.message_id(),
81 status_refused_sop_class_not_supported);
82 }
83
84 // Verify we have a dataset (query keys)
85 if (!request.has_dataset()) {
87 assoc, context_id, request.message_id(),
88 status_error_cannot_understand);
89 }
90
91 // Get the query keys from the request
92 const auto& query_keys = request.dataset().value().get();
93
94 // Get calling AE for logging and access control
95 std::string calling_ae{assoc.calling_ae()};
96
97 logger_->debug("Processing UPS C-FIND query from " + calling_ae);
98
99 // Call the handler to get matching workitems
100 auto results = handler_(query_keys, calling_ae);
101
102 // Apply max results limit if configured
103 if (max_results_ > 0 && results.size() > max_results_) {
104 results.resize(max_results_);
105 }
106
107 // Send pending responses for each result
108 for (const auto& result : results) {
109 // Check for cancel request
110 if (cancel_check_ && cancel_check_()) {
112
113 return send_final_response(
114 assoc, context_id, request.message_id(),
115 status_cancel);
116 }
117
118 // Send pending response with matching workitem
119 auto send_result = send_pending_response(
120 assoc, context_id, request.message_id(),
121 result);
122
123 if (send_result.is_err()) {
124 return send_result;
125 }
126
128 }
129
130 // Increment statistics
132
133 logger_->info("UPS C-FIND completed: " + std::to_string(results.size()) +
134 " workitems matched");
135
136 // Send final success response (no dataset)
137 return send_final_response(
138 assoc, context_id, request.message_id(),
139 status_success);
140}
141
142std::string_view ups_query_scp::service_name() const noexcept {
143 return "UPS Query SCP";
144}
145
146// =============================================================================
147// Statistics
148// =============================================================================
149
150size_t ups_query_scp::queries_processed() const noexcept {
151 return queries_processed_.load();
152}
153
154size_t ups_query_scp::items_returned() const noexcept {
155 return items_returned_.load();
156}
157
162
163// =============================================================================
164// Private Implementation
165// =============================================================================
166
169 uint8_t context_id,
170 uint16_t message_id,
171 const core::dicom_dataset& result) {
172
173 using namespace network::dimse;
174
175 auto response = make_c_find_rsp(
176 message_id,
178 status_pending
179 );
180
181 response.set_dataset(result);
182
183 return assoc.send_dimse(context_id, response);
184}
185
188 uint8_t context_id,
189 uint16_t message_id,
191
192 using namespace network::dimse;
193
194 auto response = make_c_find_rsp(
195 message_id,
197 status
198 );
199
200 return assoc.send_dimse(context_id, response);
201}
202
203} // namespace kcenon::pacs::services
Result< std::monostate > send_dimse(uint8_t context_id, const dimse::dimse_message &msg)
Send a DIMSE message.
std::string_view calling_ae() const noexcept
Get calling AE title.
auto message_id() const noexcept -> uint16_t
Get the message ID.
auto affected_sop_class_uid() const -> std::string
Get the Affected SOP Class UID.
auto has_dataset() const noexcept -> bool
Check if the message has an associated data set.
auto dataset() -> kcenon::pacs::Result< std::reference_wrapper< core::dicom_dataset > >
Get mutable reference to the data set.
auto command() const noexcept -> command_field
Get the command field.
std::shared_ptr< di::ILogger > logger_
Logger instance for service logging.
std::string_view service_name() const noexcept override
Get the service name for logging/debugging.
network::Result< std::monostate > send_pending_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const core::dicom_dataset &result)
network::Result< std::monostate > send_final_response(network::association &assoc, uint8_t context_id, uint16_t message_id, network::dimse::status_code status)
void set_max_results(size_t max) noexcept
void set_cancel_check(ups_query_cancel_check check)
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.
void set_handler(ups_query_handler handler)
size_t queries_processed() const noexcept
std::vector< std::string > supported_sop_classes() const override
Get the list of SOP Class UIDs supported by this service.
ups_query_scp(std::shared_ptr< di::ILogger > logger=nullptr)
Construct UPS Query SCP with optional logger.
std::atomic< size_t > queries_processed_
DIMSE command field enumeration.
Compile-time constants for commonly used DICOM tags.
constexpr int ups_unexpected_command
Definition result.h:210
constexpr int ups_handler_not_set
Definition result.h:209
uint16_t status_code
DIMSE status code type alias.
std::function< bool()> ups_query_cancel_check
Cancel check function type for UPS queries.
std::function< std::vector< core::dicom_dataset >( const core::dicom_dataset &query_keys, const std::string &calling_ae)> ups_query_handler
UPS query handler function type.
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
Definition mpps_scp.h:60
constexpr std::string_view ups_query_find_sop_class_uid
UPS Query Information Model - FIND SOP Class UID (PS3.4 Table CC.2-1)
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Definition result.h:249
Result<T> type aliases and helpers for PACS system.
DIMSE status codes.
DICOM UPS Query SCP service (C-FIND handler for UPS workitems)