PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
n_get_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
14
18
19namespace kcenon::pacs::services {
20
21// =============================================================================
22// Construction
23// =============================================================================
24
25n_get_scp::n_get_scp(std::shared_ptr<di::ILogger> logger)
26 : scp_service(std::move(logger)) {}
27
28// =============================================================================
29// Configuration
30// =============================================================================
31
33 handler_ = std::move(handler);
34}
35
36void n_get_scp::add_supported_sop_class(std::string sop_class_uid) {
37 supported_sop_classes_.push_back(std::move(sop_class_uid));
38}
39
40// =============================================================================
41// scp_service Interface Implementation
42// =============================================================================
43
44std::vector<std::string> n_get_scp::supported_sop_classes() const {
46}
47
50 uint8_t context_id,
51 const network::dimse::dimse_message& request) {
52
53 using namespace network::dimse;
54
55 // Verify command type is N-GET-RQ
56 if (request.command() != command_field::n_get_rq) {
59 "Unexpected command for N-GET SCP: " +
60 std::string(to_string(request.command())));
61 }
62
63 // Verify we have a handler configured
64 if (!handler_) {
67 "No N-GET handler configured");
68 }
69
70 // Extract Requested SOP Class UID
71 auto sop_class_uid = request.requested_sop_class_uid();
72 if (sop_class_uid.empty()) {
74 assoc, context_id, request.message_id(),
75 "", "", status_error_missing_attribute);
76 }
77
78 // Verify the SOP Class is supported
79 if (!supports_sop_class(sop_class_uid)) {
81 assoc, context_id, request.message_id(),
82 sop_class_uid, "", status_refused_sop_class_not_supported);
83 }
84
85 // Extract Requested SOP Instance UID
86 auto sop_instance_uid = request.requested_sop_instance_uid();
87 if (sop_instance_uid.empty()) {
89 assoc, context_id, request.message_id(),
90 sop_class_uid, "", status_error_missing_attribute);
91 }
92
93 // Extract Attribute Identifier List (empty = all attributes)
94 auto attribute_tags = request.attribute_identifier_list();
95
96 logger_->debug("N-GET request for instance: " + sop_instance_uid +
97 " (attributes requested: " +
98 (attribute_tags.empty() ? "all" :
99 std::to_string(attribute_tags.size())) + ")");
100
101 // Call the handler to retrieve attributes
102 auto result = handler_(sop_class_uid, sop_instance_uid, attribute_tags);
103 if (result.is_err()) {
104 logger_->warn("N-GET handler failed for instance: " + sop_instance_uid +
105 " - " + result.error().message);
106 return send_n_get_response(
107 assoc, context_id, request.message_id(),
108 sop_class_uid, sop_instance_uid,
109 status_error_invalid_object_instance);
110 }
111
112 // Update statistics
114
115 // Send success response with dataset
116 auto dataset = std::move(result.value());
117 return send_n_get_response(
118 assoc, context_id, request.message_id(),
119 sop_class_uid, sop_instance_uid,
120 status_success, &dataset);
121}
122
123std::string_view n_get_scp::service_name() const noexcept {
124 return "N-GET SCP";
125}
126
127// =============================================================================
128// Statistics
129// =============================================================================
130
131size_t n_get_scp::gets_processed() const noexcept {
132 return gets_processed_.load();
133}
134
136 gets_processed_ = 0;
137}
138
139// =============================================================================
140// Private Implementation
141// =============================================================================
142
145 uint8_t context_id,
146 uint16_t message_id,
147 const std::string& sop_class_uid,
148 const std::string& sop_instance_uid,
150 core::dicom_dataset* dataset) {
151
152 using namespace network::dimse;
153
154 // Create N-GET response using factory function
155 auto response = make_n_get_rsp(
156 message_id, sop_class_uid, sop_instance_uid, status);
157
158 // Attach dataset if provided (successful responses include attributes)
159 if (dataset != nullptr) {
160 response.set_dataset(std::move(*dataset));
161 }
162
163 // Send the response
164 return assoc.send_dimse(context_id, response);
165}
166
167} // namespace kcenon::pacs::services
Result< std::monostate > send_dimse(uint8_t context_id, const dimse::dimse_message &msg)
Send a DIMSE message.
auto message_id() const noexcept -> uint16_t
Get the message ID.
auto attribute_identifier_list() const -> std::vector< core::dicom_tag >
Get the Attribute Identifier List (for N-GET)
auto requested_sop_class_uid() const -> std::string
Get the Requested SOP Class UID (for N-SET, N-GET, N-ACTION, N-DELETE)
auto requested_sop_instance_uid() const -> std::string
Get the Requested SOP Instance UID (for N-SET, N-GET, N-ACTION, N-DELETE)
auto command() const noexcept -> command_field
Get the command field.
std::vector< std::string > supported_sop_classes_
Definition n_get_scp.h:212
network::Result< std::monostate > send_n_get_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_class_uid, const std::string &sop_instance_uid, network::dimse::status_code status, core::dicom_dataset *dataset=nullptr)
Send N-GET response.
void set_handler(n_get_handler handler)
Set the N-GET handler function.
Definition n_get_scp.cpp:32
std::atomic< size_t > gets_processed_
Definition n_get_scp.h:214
std::vector< std::string > supported_sop_classes() const override
Get supported SOP Class UIDs.
Definition n_get_scp.cpp:44
n_get_scp(std::shared_ptr< di::ILogger > logger=nullptr)
Construct N-GET SCP with optional logger.
Definition n_get_scp.cpp:25
void reset_statistics() noexcept
Reset statistics counters.
size_t gets_processed() const noexcept
Get total number of N-GET requests processed.
network::Result< std::monostate > handle_message(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request) override
Handle an incoming N-GET-RQ message.
Definition n_get_scp.cpp:48
void add_supported_sop_class(std::string sop_class_uid)
Add a SOP Class UID that this SCP supports.
Definition n_get_scp.cpp:36
std::string_view service_name() const noexcept override
Get the service name.
bool supports_sop_class(std::string_view sop_class_uid) const
Check if this service supports a specific SOP Class.
std::shared_ptr< di::ILogger > logger_
Logger instance for service logging.
DIMSE command field enumeration.
DICOM N-GET SCP service for attribute retrieval.
constexpr int n_get_handler_not_set
Definition result.h:190
constexpr int n_get_unexpected_command
Definition result.h:191
uint16_t status_code
DIMSE status code type alias.
std::function< network::Result< core::dicom_dataset >( const std::string &sop_class_uid, const std::string &sop_instance_uid, const std::vector< core::dicom_tag > &attribute_tags)> n_get_handler
N-GET handler function type.
Definition n_get_scp.h:51
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.