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

#include <mpps_scp.h>

Inheritance diagram for kcenon::pacs::services::mpps_scp:
Inheritance graph
Collaboration diagram for kcenon::pacs::services::mpps_scp:
Collaboration graph

Public Member Functions

 mpps_scp (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct MPPS SCP with optional logger.
 
 ~mpps_scp () override=default
 
void set_create_handler (mpps_create_handler handler)
 Set the N-CREATE handler function.
 
void set_set_handler (mpps_set_handler handler)
 Set the N-SET handler function.
 
std::vector< std::string > supported_sop_classes () const override
 Get supported SOP Class UIDs.
 
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 (N-CREATE-RQ or N-SET-RQ)
 
std::string_view service_name () const noexcept override
 Get the service name.
 
size_t creates_processed () const noexcept
 Get total number of N-CREATE requests processed.
 
size_t sets_processed () const noexcept
 Get total number of N-SET requests processed.
 
size_t mpps_completed () const noexcept
 Get number of MPPS completed successfully.
 
size_t mpps_discontinued () const noexcept
 Get number of MPPS discontinued.
 
void reset_statistics () noexcept
 Reset statistics counters.
 
- Public Member Functions inherited from kcenon::pacs::services::scp_service
 scp_service (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct SCP service with optional logger.
 
virtual ~scp_service ()=default
 
 scp_service (const scp_service &)=delete
 
scp_serviceoperator= (const scp_service &)=delete
 
 scp_service (scp_service &&)=default
 
scp_serviceoperator= (scp_service &&)=default
 
void set_logger (std::shared_ptr< di::ILogger > logger)
 Set the logger instance.
 
const std::shared_ptr< di::ILogger > & logger () const noexcept
 Get the current logger instance.
 
bool supports_sop_class (std::string_view sop_class_uid) const
 Check if this service supports a specific SOP Class.
 

Private Member Functions

network::Result< std::monostate > handle_n_create (network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
 Handle N-CREATE request.
 
network::Result< std::monostate > handle_n_set (network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
 Handle N-SET request.
 
network::Result< std::monostate > send_n_create_response (network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid, network::dimse::status_code status)
 Send N-CREATE response.
 
network::Result< std::monostate > send_n_set_response (network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid, network::dimse::status_code status)
 Send N-SET response.
 

Private Attributes

mpps_create_handler create_handler_
 
mpps_set_handler set_handler_
 
std::atomic< size_t > creates_processed_ {0}
 
std::atomic< size_t > sets_processed_ {0}
 
std::atomic< size_t > mpps_completed_ {0}
 
std::atomic< size_t > mpps_discontinued_ {0}
 

Additional Inherited Members

- Protected Attributes inherited from kcenon::pacs::services::scp_service
std::shared_ptr< di::ILoggerlogger_
 Logger instance for service logging.
 

Detailed Description

Definition at line 231 of file mpps_scp.h.

Constructor & Destructor Documentation

◆ mpps_scp()

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

Construct MPPS SCP with optional logger.

Parameters
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 23 of file mpps_scp.cpp.

24 : scp_service(std::move(logger)) {}
const std::shared_ptr< di::ILogger > & logger() const noexcept
Get the current logger instance.
Definition scp_service.h:93
scp_service(std::shared_ptr< di::ILogger > logger=nullptr)
Construct SCP service with optional logger.
Definition scp_service.h:64

◆ ~mpps_scp()

kcenon::pacs::services::mpps_scp::~mpps_scp ( )
overridedefault

Member Function Documentation

◆ creates_processed()

size_t kcenon::pacs::services::mpps_scp::creates_processed ( ) const
nodiscardnoexcept

Get total number of N-CREATE requests processed.

Returns
Number of MPPS instances created

Definition at line 77 of file mpps_scp.cpp.

77 {
78 return creates_processed_.load();
79}
std::atomic< size_t > creates_processed_
Definition mpps_scp.h:416

References creates_processed_.

◆ handle_message()

network::Result< std::monostate > kcenon::pacs::services::mpps_scp::handle_message ( network::association & assoc,
uint8_t context_id,
const network::dimse::dimse_message & request )
nodiscardoverridevirtual

Handle an incoming DIMSE message (N-CREATE-RQ or N-SET-RQ)

Processes N-CREATE and N-SET requests for MPPS management.

Parameters
assocThe association on which the message was received
context_idThe presentation context ID for the message
requestThe incoming N-CREATE-RQ or N-SET-RQ message
Returns
Success or error if the message cannot be processed

Implements kcenon::pacs::services::scp_service.

Definition at line 46 of file mpps_scp.cpp.

49 {
50
51 using namespace network::dimse;
52
53 // Route to appropriate handler based on command type
54 switch (request.command()) {
55 case command_field::n_create_rq:
56 return handle_n_create(assoc, context_id, request);
57
58 case command_field::n_set_rq:
59 return handle_n_set(assoc, context_id, request);
60
61 default:
64 "Unexpected command for MPPS SCP: " +
65 std::string(to_string(request.command())));
66 }
67}
network::Result< std::monostate > handle_n_create(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
Handle N-CREATE request.
Definition mpps_scp.cpp:104
network::Result< std::monostate > handle_n_set(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
Handle N-SET request.
Definition mpps_scp.cpp:189
constexpr int mpps_unexpected_command
Definition result.h:174
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

References kcenon::pacs::network::dimse::dimse_message::command(), handle_n_create(), handle_n_set(), kcenon::pacs::error_codes::mpps_unexpected_command, kcenon::pacs::pacs_void_error(), and kcenon::pacs::services::to_string().

Here is the call graph for this function:

◆ handle_n_create()

network::Result< std::monostate > kcenon::pacs::services::mpps_scp::handle_n_create ( network::association & assoc,
uint8_t context_id,
const network::dimse::dimse_message & request )
nodiscardprivate

Handle N-CREATE request.

Creates a new MPPS instance with IN PROGRESS status.

Parameters
assocThe association
context_idThe presentation context ID
requestThe N-CREATE-RQ message
Returns
Success or error

Definition at line 104 of file mpps_scp.cpp.

107 {
108
109 using namespace network::dimse;
110
111 // Verify we have a handler configured
112 if (!create_handler_) {
115 "No N-CREATE handler configured for MPPS SCP");
116 }
117
118 // Verify the SOP Class is MPPS
119 auto sop_class_uid = request.affected_sop_class_uid();
120 if (sop_class_uid != mpps_sop_class_uid) {
122 assoc, context_id, request.message_id(),
123 "", status_refused_sop_class_not_supported);
124 }
125
126 // Get the SOP Instance UID from the request
127 auto sop_instance_uid = request.affected_sop_instance_uid();
128 if (sop_instance_uid.empty()) {
130 assoc, context_id, request.message_id(),
131 "", status_error_missing_attribute);
132 }
133
134 // Verify we have a dataset
135 if (!request.has_dataset()) {
137 assoc, context_id, request.message_id(),
138 sop_instance_uid, status_error_cannot_understand);
139 }
140
141 const auto& dataset = request.dataset().value().get();
142
143 // Build MPPS instance from request data
144 mpps_instance instance;
145 instance.sop_instance_uid = sop_instance_uid;
146 instance.status = mpps_status::in_progress;
147 instance.data = dataset;
148
149 // Extract Performed Station AE Title if present
150 if (dataset.contains(mpps_tags::performed_station_ae_title)) {
151 instance.station_ae = dataset.get_string(mpps_tags::performed_station_ae_title);
152 }
153
154 // Validate initial status is IN PROGRESS
155 if (dataset.contains(mpps_tags::performed_procedure_step_status)) {
156 auto status_str = dataset.get_string(mpps_tags::performed_procedure_step_status);
157 auto parsed_status = parse_mpps_status(status_str);
158
159 if (!parsed_status.has_value() ||
160 parsed_status.value() != mpps_status::in_progress) {
161 // N-CREATE must have IN PROGRESS status
163 assoc, context_id, request.message_id(),
164 sop_instance_uid, status_error_cannot_understand);
165 }
166 }
167
168 // Call the handler to create the MPPS instance
169 auto result = create_handler_(instance);
170 if (result.is_err()) {
172 assoc, context_id, request.message_id(),
173 sop_instance_uid, status_error_unable_to_process);
174 }
175
176 // Update statistics
178
179 // Send success response
181 assoc, context_id, request.message_id(),
182 sop_instance_uid, status_success);
183}
network::Result< std::monostate > send_n_create_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid, network::dimse::status_code status)
Send N-CREATE response.
Definition mpps_scp.cpp:291
mpps_create_handler create_handler_
Definition mpps_scp.h:413
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr int mpps_handler_not_set
Definition result.h:172
constexpr core::dicom_tag performed_station_ae_title
Performed Station AE Title (0040,0241)
Definition mpps_scp.h:429
constexpr core::dicom_tag performed_procedure_step_status
Performed Procedure Step Status (0040,0252)
Definition mpps_scp.h:444
@ in_progress
Procedure is currently being performed.
constexpr std::string_view mpps_sop_class_uid
MPPS (Modality Performed Procedure Step) SOP Class UID.
Definition mpps_scp.h:36
auto parse_mpps_status(std::string_view str) -> std::optional< mpps_status >
Parse DICOM string to mpps_status enum.
Definition mpps_scp.h:79

References kcenon::pacs::network::dimse::dimse_message::affected_sop_class_uid(), kcenon::pacs::network::dimse::dimse_message::affected_sop_instance_uid(), create_handler_, creates_processed_, kcenon::pacs::services::mpps_instance::data, kcenon::pacs::network::dimse::dimse_message::dataset(), kcenon::pacs::network::dimse::dimse_message::has_dataset(), kcenon::pacs::services::in_progress, kcenon::pacs::network::dimse::dimse_message::message_id(), kcenon::pacs::error_codes::mpps_handler_not_set, kcenon::pacs::services::mpps_sop_class_uid, kcenon::pacs::pacs_void_error(), kcenon::pacs::services::parse_mpps_status(), kcenon::pacs::services::mpps_tags::performed_procedure_step_status, kcenon::pacs::services::mpps_tags::performed_station_ae_title, send_n_create_response(), kcenon::pacs::services::mpps_instance::sop_instance_uid, kcenon::pacs::services::mpps_instance::station_ae, and kcenon::pacs::services::mpps_instance::status.

Referenced by handle_message().

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

◆ handle_n_set()

network::Result< std::monostate > kcenon::pacs::services::mpps_scp::handle_n_set ( network::association & assoc,
uint8_t context_id,
const network::dimse::dimse_message & request )
nodiscardprivate

Handle N-SET request.

Updates an existing MPPS instance to COMPLETED or DISCONTINUED.

Parameters
assocThe association
context_idThe presentation context ID
requestThe N-SET-RQ message
Returns
Success or error

Definition at line 189 of file mpps_scp.cpp.

192 {
193
194 using namespace network::dimse;
195
196 // Verify we have a handler configured
197 if (!set_handler_) {
200 "No N-SET handler configured for MPPS SCP");
201 }
202
203 // Verify the SOP Class is MPPS
204 auto sop_class_uid = request.affected_sop_class_uid();
205 if (sop_class_uid.empty()) {
206 // For N-SET, use requested SOP class if affected is empty
207 sop_class_uid = request.command_set().get_string(
208 tag_requested_sop_class_uid);
209 }
210
211 if (sop_class_uid != mpps_sop_class_uid) {
212 return send_n_set_response(
213 assoc, context_id, request.message_id(),
214 "", status_refused_sop_class_not_supported);
215 }
216
217 // Get the SOP Instance UID (from Requested SOP Instance UID for N-SET)
218 auto sop_instance_uid = request.command_set().get_string(
219 tag_requested_sop_instance_uid);
220
221 if (sop_instance_uid.empty()) {
222 // Try affected SOP instance UID as fallback
223 sop_instance_uid = request.affected_sop_instance_uid();
224 }
225
226 if (sop_instance_uid.empty()) {
227 return send_n_set_response(
228 assoc, context_id, request.message_id(),
229 "", status_error_missing_attribute);
230 }
231
232 // Verify we have a dataset with modifications
233 if (!request.has_dataset()) {
234 return send_n_set_response(
235 assoc, context_id, request.message_id(),
236 sop_instance_uid, status_error_cannot_understand);
237 }
238
239 const auto& dataset = request.dataset().value().get();
240
241 // Extract and validate the new status
242 if (!dataset.contains(mpps_tags::performed_procedure_step_status)) {
243 return send_n_set_response(
244 assoc, context_id, request.message_id(),
245 sop_instance_uid, status_error_missing_attribute);
246 }
247
248 auto status_str = dataset.get_string(mpps_tags::performed_procedure_step_status);
249 auto new_status = parse_mpps_status(status_str);
250
251 if (!new_status.has_value()) {
252 return send_n_set_response(
253 assoc, context_id, request.message_id(),
254 sop_instance_uid, status_error_cannot_understand);
255 }
256
257 // Validate status transition (must be to COMPLETED or DISCONTINUED)
258 if (new_status.value() == mpps_status::in_progress) {
259 // Cannot set back to IN PROGRESS
260 return send_n_set_response(
261 assoc, context_id, request.message_id(),
262 sop_instance_uid, status_error_cannot_understand);
263 }
264
265 // Call the handler to update the MPPS instance
266 auto result = set_handler_(sop_instance_uid, dataset, new_status.value());
267 if (result.is_err()) {
268 return send_n_set_response(
269 assoc, context_id, request.message_id(),
270 sop_instance_uid, status_error_unable_to_process);
271 }
272
273 // Update statistics
275 if (new_status.value() == mpps_status::completed) {
277 } else if (new_status.value() == mpps_status::discontinued) {
279 }
280
281 // Send success response
282 return send_n_set_response(
283 assoc, context_id, request.message_id(),
284 sop_instance_uid, status_success);
285}
mpps_set_handler set_handler_
Definition mpps_scp.h:414
network::Result< std::monostate > send_n_set_response(network::association &assoc, uint8_t context_id, uint16_t message_id, const std::string &sop_instance_uid, network::dimse::status_code status)
Send N-SET response.
Definition mpps_scp.cpp:314
std::atomic< size_t > sets_processed_
Definition mpps_scp.h:417
std::atomic< size_t > mpps_completed_
Definition mpps_scp.h:418
std::atomic< size_t > mpps_discontinued_
Definition mpps_scp.h:419
@ completed
Procedure completed successfully.
@ discontinued
Procedure was stopped/cancelled.

References kcenon::pacs::network::dimse::dimse_message::affected_sop_class_uid(), kcenon::pacs::network::dimse::dimse_message::affected_sop_instance_uid(), kcenon::pacs::network::dimse::dimse_message::command_set(), kcenon::pacs::services::completed, kcenon::pacs::network::dimse::dimse_message::dataset(), kcenon::pacs::services::discontinued, kcenon::pacs::network::dimse::dimse_message::has_dataset(), kcenon::pacs::services::in_progress, kcenon::pacs::network::dimse::dimse_message::message_id(), mpps_completed_, mpps_discontinued_, kcenon::pacs::error_codes::mpps_handler_not_set, kcenon::pacs::services::mpps_sop_class_uid, kcenon::pacs::pacs_void_error(), kcenon::pacs::services::parse_mpps_status(), kcenon::pacs::services::mpps_tags::performed_procedure_step_status, send_n_set_response(), set_handler_, and sets_processed_.

Referenced by handle_message().

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

◆ mpps_completed()

size_t kcenon::pacs::services::mpps_scp::mpps_completed ( ) const
nodiscardnoexcept

Get number of MPPS completed successfully.

Returns
Number of MPPS with COMPLETED status

Definition at line 85 of file mpps_scp.cpp.

85 {
86 return mpps_completed_.load();
87}

References mpps_completed_.

◆ mpps_discontinued()

size_t kcenon::pacs::services::mpps_scp::mpps_discontinued ( ) const
nodiscardnoexcept

Get number of MPPS discontinued.

Returns
Number of MPPS with DISCONTINUED status

Definition at line 89 of file mpps_scp.cpp.

89 {
90 return mpps_discontinued_.load();
91}

References mpps_discontinued_.

◆ reset_statistics()

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

Reset statistics counters.

Definition at line 93 of file mpps_scp.cpp.

93 {
98}

References creates_processed_, mpps_completed_, mpps_discontinued_, and sets_processed_.

◆ send_n_create_response()

network::Result< std::monostate > kcenon::pacs::services::mpps_scp::send_n_create_response ( network::association & assoc,
uint8_t context_id,
uint16_t message_id,
const std::string & sop_instance_uid,
network::dimse::status_code status )
nodiscardprivate

Send N-CREATE response.

Parameters
assocThe association
context_idThe presentation context ID
message_idThe original request message ID
sop_instance_uidThe SOP Instance UID
statusThe response status code
Returns
Success or error

Definition at line 291 of file mpps_scp.cpp.

296 {
297
298 using namespace network::dimse;
299
300 // Create N-CREATE response message
301 dimse_message response{command_field::n_create_rsp, 0};
302 response.set_message_id_responded_to(message_id);
303 response.set_affected_sop_class_uid(mpps_sop_class_uid);
304 response.set_status(status);
305
306 if (!sop_instance_uid.empty()) {
307 response.set_affected_sop_instance_uid(sop_instance_uid);
308 }
309
310 // Send the response
311 return assoc.send_dimse(context_id, response);
312}

References kcenon::pacs::services::mpps_sop_class_uid, and kcenon::pacs::network::association::send_dimse().

Referenced by handle_n_create().

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

◆ send_n_set_response()

network::Result< std::monostate > kcenon::pacs::services::mpps_scp::send_n_set_response ( network::association & assoc,
uint8_t context_id,
uint16_t message_id,
const std::string & sop_instance_uid,
network::dimse::status_code status )
nodiscardprivate

Send N-SET response.

Parameters
assocThe association
context_idThe presentation context ID
message_idThe original request message ID
sop_instance_uidThe SOP Instance UID
statusThe response status code
Returns
Success or error

Definition at line 314 of file mpps_scp.cpp.

319 {
320
321 using namespace network::dimse;
322
323 // Create N-SET response message
324 dimse_message response{command_field::n_set_rsp, 0};
325 response.set_message_id_responded_to(message_id);
326 response.set_affected_sop_class_uid(mpps_sop_class_uid);
327 response.set_status(status);
328
329 if (!sop_instance_uid.empty()) {
330 response.set_affected_sop_instance_uid(sop_instance_uid);
331 }
332
333 // Send the response
334 return assoc.send_dimse(context_id, response);
335}

References kcenon::pacs::services::mpps_sop_class_uid, and kcenon::pacs::network::association::send_dimse().

Referenced by handle_n_set().

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

◆ service_name()

std::string_view kcenon::pacs::services::mpps_scp::service_name ( ) const
nodiscardoverridevirtualnoexcept

Get the service name.

Returns
"MPPS SCP"

Implements kcenon::pacs::services::scp_service.

Definition at line 69 of file mpps_scp.cpp.

69 {
70 return "MPPS SCP";
71}

◆ set_create_handler()

void kcenon::pacs::services::mpps_scp::set_create_handler ( mpps_create_handler handler)

Set the N-CREATE handler function.

The handler is called for each N-CREATE request to create a new MPPS instance in the database or forward to RIS/HIS.

Parameters
handlerThe N-CREATE handler function

Definition at line 30 of file mpps_scp.cpp.

30 {
31 create_handler_ = std::move(handler);
32}

References create_handler_.

◆ set_set_handler()

void kcenon::pacs::services::mpps_scp::set_set_handler ( mpps_set_handler handler)

Set the N-SET handler function.

The handler is called for each N-SET request to update an existing MPPS instance with COMPLETED or DISCONTINUED status.

Parameters
handlerThe N-SET handler function

Definition at line 34 of file mpps_scp.cpp.

34 {
35 set_handler_ = std::move(handler);
36}

References set_handler_.

◆ sets_processed()

size_t kcenon::pacs::services::mpps_scp::sets_processed ( ) const
nodiscardnoexcept

Get total number of N-SET requests processed.

Returns
Number of MPPS instances updated

Definition at line 81 of file mpps_scp.cpp.

81 {
82 return sets_processed_.load();
83}

References sets_processed_.

◆ supported_sop_classes()

std::vector< std::string > kcenon::pacs::services::mpps_scp::supported_sop_classes ( ) const
nodiscardoverridevirtual

Get supported SOP Class UIDs.

Returns
Vector containing MPPS SOP Class UID

Implements kcenon::pacs::services::scp_service.

Definition at line 42 of file mpps_scp.cpp.

42 {
43 return {std::string(mpps_sop_class_uid)};
44}

References kcenon::pacs::services::mpps_sop_class_uid.

Member Data Documentation

◆ create_handler_

mpps_create_handler kcenon::pacs::services::mpps_scp::create_handler_
private

Definition at line 413 of file mpps_scp.h.

Referenced by handle_n_create(), and set_create_handler().

◆ creates_processed_

std::atomic<size_t> kcenon::pacs::services::mpps_scp::creates_processed_ {0}
private

Definition at line 416 of file mpps_scp.h.

416{0};

Referenced by creates_processed(), handle_n_create(), and reset_statistics().

◆ mpps_completed_

std::atomic<size_t> kcenon::pacs::services::mpps_scp::mpps_completed_ {0}
private

Definition at line 418 of file mpps_scp.h.

418{0};

Referenced by handle_n_set(), mpps_completed(), and reset_statistics().

◆ mpps_discontinued_

std::atomic<size_t> kcenon::pacs::services::mpps_scp::mpps_discontinued_ {0}
private

Definition at line 419 of file mpps_scp.h.

419{0};

Referenced by handle_n_set(), mpps_discontinued(), and reset_statistics().

◆ set_handler_

mpps_set_handler kcenon::pacs::services::mpps_scp::set_handler_
private

Definition at line 414 of file mpps_scp.h.

Referenced by handle_n_set(), and set_set_handler().

◆ sets_processed_

std::atomic<size_t> kcenon::pacs::services::mpps_scp::sets_processed_ {0}
private

Definition at line 417 of file mpps_scp.h.

417{0};

Referenced by handle_n_set(), reset_statistics(), and sets_processed().


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