PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
worklist_scp.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
11
16
17namespace kcenon::pacs::services {
18
19// =============================================================================
20// Construction
21// =============================================================================
22
23worklist_scp::worklist_scp(std::shared_ptr<di::ILogger> logger)
24 : scp_service(std::move(logger)) {}
25
26// =============================================================================
27// Configuration
28// =============================================================================
29
31 handler_ = std::move(handler);
32}
33
34void worklist_scp::set_max_results(size_t max) noexcept {
35 max_results_ = max;
36}
37
38size_t worklist_scp::max_results() const noexcept {
39 return max_results_;
40}
41
45
46// =============================================================================
47// scp_service Interface Implementation
48// =============================================================================
49
50std::vector<std::string> worklist_scp::supported_sop_classes() const {
51 return {
53 };
54}
55
58 uint8_t context_id,
59 const network::dimse::dimse_message& request) {
60
61 using namespace network::dimse;
62
63 // Verify the message is a C-FIND request
64 if (request.command() != command_field::c_find_rq) {
67 "Expected C-FIND-RQ but received " +
68 std::string(to_string(request.command())));
69 }
70
71 // Verify we have a handler
72 if (!handler_) {
75 "No worklist handler configured");
76 }
77
78 // Get the SOP Class UID from the request
79 auto sop_class_uid = request.affected_sop_class_uid();
80
81 // Verify the SOP Class is Modality Worklist
82 if (sop_class_uid != worklist_find_sop_class_uid) {
84 assoc, context_id, request.message_id(),
85 status_refused_sop_class_not_supported);
86 }
87
88 // Verify we have a dataset (query keys)
89 if (!request.has_dataset()) {
91 assoc, context_id, request.message_id(),
92 status_error_cannot_understand);
93 }
94
95 // Get the query keys from the request
96 const auto& query_keys = request.dataset().value().get();
97
98 // Get calling AE for logging and access control
99 std::string calling_ae{assoc.calling_ae()};
100
101 // Call the handler to get matching worklist items
102 auto results = handler_(query_keys, calling_ae);
103
104 // Apply max results limit if configured
105 if (max_results_ > 0 && results.size() > max_results_) {
106 results.resize(max_results_);
107 }
108
109 // Send pending responses for each result
110 for (const auto& result : results) {
111 // Check for cancel request
112 if (cancel_check_ && cancel_check_()) {
113 // Increment statistics
115
116 // Send cancel response
117 return send_final_response(
118 assoc, context_id, request.message_id(),
119 status_cancel);
120 }
121
122 // Send pending response with matching worklist item
123 auto send_result = send_pending_response(
124 assoc, context_id, request.message_id(),
125 result);
126
127 if (send_result.is_err()) {
128 return send_result;
129 }
130
131 // Track items returned
133 }
134
135 // Increment statistics
137
138 // Send final success response (no dataset)
139 return send_final_response(
140 assoc, context_id, request.message_id(),
141 status_success);
142}
143
144std::string_view worklist_scp::service_name() const noexcept {
145 return "Worklist SCP";
146}
147
148// =============================================================================
149// Statistics
150// =============================================================================
151
152size_t worklist_scp::queries_processed() const noexcept {
153 return queries_processed_.load();
154}
155
156size_t worklist_scp::items_returned() const noexcept {
157 return items_returned_.load();
158}
159
164
165// =============================================================================
166// Private Implementation
167// =============================================================================
168
171 uint8_t context_id,
172 uint16_t message_id,
173 const core::dicom_dataset& result) {
174
175 using namespace network::dimse;
176
177 // Create C-FIND response with pending status
178 auto response = make_c_find_rsp(
179 message_id,
181 status_pending
182 );
183
184 // Attach the matching worklist item dataset
185 response.set_dataset(result);
186
187 // Send the response
188 return assoc.send_dimse(context_id, response);
189}
190
193 uint8_t context_id,
194 uint16_t message_id,
196
197 using namespace network::dimse;
198
199 // Create C-FIND response with final status (no dataset)
200 auto response = make_c_find_rsp(
201 message_id,
203 status
204 );
205
206 // Send the response
207 return assoc.send_dimse(context_id, response);
208}
209
210} // 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.
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 (MWL C-FIND-RQ)
size_t items_returned() const noexcept
Get total number of worklist items returned.
void set_max_results(size_t max) noexcept
Set maximum number of results to return.
network::Result< std::monostate > send_final_response(network::association &assoc, uint8_t context_id, uint16_t message_id, network::dimse::status_code status)
Send the final C-FIND response (success or cancel)
void reset_statistics() noexcept
Reset statistics counters.
std::string_view service_name() const noexcept override
Get the service name.
std::atomic< size_t > items_returned_
network::Result< std::monostate > send_pending_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const core::dicom_dataset &result)
Send a pending C-FIND response with matching worklist item.
size_t max_results() const noexcept
Get maximum number of results.
worklist_scp(std::shared_ptr< di::ILogger > logger=nullptr)
Construct Worklist SCP with optional logger.
std::atomic< size_t > queries_processed_
worklist_cancel_check cancel_check_
size_t queries_processed() const noexcept
Get total number of worklist queries processed.
void set_handler(worklist_handler handler)
Set the worklist handler function.
void set_cancel_check(worklist_cancel_check check)
Set the cancel check function.
std::vector< std::string > supported_sop_classes() const override
Get supported SOP Class UIDs.
DIMSE command field enumeration.
Compile-time constants for commonly used DICOM tags.
constexpr int worklist_handler_not_set
Definition result.h:186
constexpr int worklist_unexpected_command
Definition result.h:187
uint16_t status_code
DIMSE status code type alias.
std::function< bool()> worklist_cancel_check
Cancel check function type.
std::function< std::vector< core::dicom_dataset >( const core::dicom_dataset &query_keys, const std::string &calling_ae)> worklist_handler
constexpr std::string_view worklist_find_sop_class_uid
Modality Worklist Information Model - FIND SOP Class UID.
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
Result<T> type aliases and helpers for PACS system.
DIMSE status codes.
DICOM Modality Worklist SCP service (MWL C-FIND handler)