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

#include <mpps_scu.h>

Collaboration diagram for kcenon::pacs::services::mpps_scu:
Collaboration graph

Public Member Functions

 mpps_scu (std::shared_ptr< di::ILogger > logger=nullptr)
 Construct MPPS SCU with default configuration.
 
 mpps_scu (const mpps_scu_config &config, std::shared_ptr< di::ILogger > logger=nullptr)
 Construct MPPS SCU with custom configuration.
 
 ~mpps_scu ()=default
 
 mpps_scu (const mpps_scu &)=delete
 
mpps_scuoperator= (const mpps_scu &)=delete
 
 mpps_scu (mpps_scu &&)=delete
 
mpps_scuoperator= (mpps_scu &&)=delete
 
network::Result< mpps_resultcreate (network::association &assoc, const mpps_create_data &data)
 Create a new MPPS instance (N-CREATE)
 
network::Result< mpps_resultset (network::association &assoc, const mpps_set_data &data)
 Update an existing MPPS instance (N-SET)
 
network::Result< mpps_resultcomplete (network::association &assoc, std::string_view mpps_uid, const std::vector< performed_series_info > &performed_series)
 Complete an MPPS instance (convenience method)
 
network::Result< mpps_resultdiscontinue (network::association &assoc, std::string_view mpps_uid, std::string_view reason="")
 Discontinue an MPPS instance (convenience method)
 
size_t creates_performed () const noexcept
 Get the number of N-CREATE operations performed.
 
size_t sets_performed () const noexcept
 Get the number of N-SET operations performed.
 
void reset_statistics () noexcept
 Reset statistics counters to zero.
 

Private Member Functions

core::dicom_dataset build_create_dataset (const mpps_create_data &data) const
 Build DICOM dataset for N-CREATE request.
 
core::dicom_dataset build_set_dataset (const mpps_set_data &data) const
 Build DICOM dataset for N-SET request.
 
std::string generate_mpps_uid () const
 Generate a unique MPPS SOP Instance UID.
 
std::string get_current_date () const
 Get current date in DICOM DA format (YYYYMMDD)
 
std::string get_current_time () const
 Get current time in DICOM TM format (HHMMSS)
 
uint16_t next_message_id () noexcept
 Get the next message ID for DIMSE operations.
 

Private Attributes

std::shared_ptr< di::ILoggerlogger_
 Logger instance.
 
mpps_scu_config config_
 Configuration.
 
std::atomic< uint16_t > message_id_counter_ {1}
 Message ID counter.
 
std::atomic< size_t > creates_performed_ {0}
 Statistics: N-CREATE operations performed.
 
std::atomic< size_t > sets_performed_ {0}
 Statistics: N-SET operations performed.
 

Detailed Description

Examples
mpps_scu/main.cpp.

Definition at line 254 of file mpps_scu.h.

Constructor & Destructor Documentation

◆ mpps_scu() [1/4]

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

Construct MPPS SCU with default configuration.

Parameters
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 86 of file mpps_scu.cpp.

87 : logger_(logger ? std::move(logger) : di::null_logger()) {}
std::shared_ptr< di::ILogger > logger_
Logger instance.
Definition mpps_scu.h:414
std::shared_ptr< ILogger > null_logger()
Get a shared null logger instance.
Definition ilogger.h:271

◆ mpps_scu() [2/4]

kcenon::pacs::services::mpps_scu::mpps_scu ( const mpps_scu_config & config,
std::shared_ptr< di::ILogger > logger = nullptr )
explicit

Construct MPPS SCU with custom configuration.

Parameters
configConfiguration options
loggerLogger instance for service logging (nullptr uses null_logger)

Definition at line 89 of file mpps_scu.cpp.

91 : logger_(logger ? std::move(logger) : di::null_logger()),
92 config_(config) {}
mpps_scu_config config_
Configuration.
Definition mpps_scu.h:417

◆ ~mpps_scu()

kcenon::pacs::services::mpps_scu::~mpps_scu ( )
default

◆ mpps_scu() [3/4]

kcenon::pacs::services::mpps_scu::mpps_scu ( const mpps_scu & )
delete

◆ mpps_scu() [4/4]

kcenon::pacs::services::mpps_scu::mpps_scu ( mpps_scu && )
delete

Member Function Documentation

◆ build_create_dataset()

core::dicom_dataset kcenon::pacs::services::mpps_scu::build_create_dataset ( const mpps_create_data & data) const
nodiscardprivate

Build DICOM dataset for N-CREATE request.

Definition at line 350 of file mpps_scu.cpp.

350 {
351 using namespace core;
352 using namespace encoding;
353
354 dicom_dataset ds;
355
356 // Performed Procedure Step Status - always IN PROGRESS for N-CREATE
357 ds.set_string(mpps_tags::performed_procedure_step_status, vr_type::CS, "IN PROGRESS");
358
359 // Performed Procedure Step Timing
360 std::string start_date = data.procedure_step_start_date.empty()
361 ? get_current_date() : data.procedure_step_start_date;
362 std::string start_time = data.procedure_step_start_time.empty()
363 ? get_current_time() : data.procedure_step_start_time;
364
365 ds.set_string(mpps_tags::performed_procedure_step_start_date, vr_type::DA, start_date);
366 ds.set_string(mpps_tags::performed_procedure_step_start_time, vr_type::TM, start_time);
367
368 // Modality and Station
369 if (!data.modality.empty()) {
370 ds.set_string(tags::modality, vr_type::CS, data.modality);
371 }
372 if (!data.station_ae_title.empty()) {
373 ds.set_string(mpps_tags::performed_station_ae_title, vr_type::AE, data.station_ae_title);
374 }
375 if (!data.station_name.empty()) {
376 ds.set_string(mpps_tags::performed_station_name, vr_type::SH, data.station_name);
377 }
378
379 // Procedure Step ID
380 if (!data.scheduled_procedure_step_id.empty()) {
381 ds.set_string(mpps_tags::performed_procedure_step_id, vr_type::SH,
382 data.scheduled_procedure_step_id);
383 }
384
385 // Procedure Description
386 if (!data.procedure_description.empty()) {
387 ds.set_string(mpps_tags::performed_procedure_step_description, vr_type::LO,
388 data.procedure_description);
389 }
390
391 // Patient Information (required)
392 std::string patient_name = data.patient_name.empty() ? "ANONYMOUS" : data.patient_name;
393 ds.set_string(tags::patient_name, vr_type::PN, patient_name);
394
395 if (!data.patient_id.empty()) {
396 ds.set_string(tags::patient_id, vr_type::LO, data.patient_id);
397 }
398 if (!data.patient_birth_date.empty()) {
399 ds.set_string(tags::patient_birth_date, vr_type::DA, data.patient_birth_date);
400 }
401 if (!data.patient_sex.empty()) {
402 ds.set_string(tags::patient_sex, vr_type::CS, data.patient_sex);
403 }
404
405 // Study reference
406 if (!data.study_instance_uid.empty()) {
407 ds.set_string(tags::study_instance_uid, vr_type::UI, data.study_instance_uid);
408 }
409 if (!data.accession_number.empty()) {
410 ds.set_string(tags::accession_number, vr_type::SH, data.accession_number);
411 }
412
413 // Performing Information
414 if (!data.performing_physician.empty()) {
415 ds.set_string(mpps_tags::performing_physicians_name, vr_type::PN,
416 data.performing_physician);
417 }
418 if (!data.operator_name.empty()) {
419 ds.set_string(mpps_tags::operators_name, vr_type::PN, data.operator_name);
420 }
421
422 return ds;
423}
std::string get_current_time() const
Get current time in DICOM TM format (HHMMSS)
Definition mpps_scu.cpp:504
std::string get_current_date() const
Get current date in DICOM DA format (YYYYMMDD)
Definition mpps_scu.cpp:491
constexpr dicom_tag patient_id
Patient ID.
constexpr dicom_tag patient_birth_date
Patient's Birth Date.
constexpr dicom_tag accession_number
Accession Number.
constexpr dicom_tag modality
Modality.
constexpr dicom_tag patient_sex
Patient's Sex.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag patient_name
Patient's Name.
constexpr core::dicom_tag operators_name
Operators' Name (0008,1070)
Definition mpps_scu.h:457
constexpr core::dicom_tag performed_procedure_step_id
Performed Procedure Step ID (0040,0253)
Definition mpps_scp.h:447
constexpr core::dicom_tag performing_physicians_name
Performing Physician's Name (0008,1050)
Definition mpps_scu.h:454
constexpr core::dicom_tag performed_station_name
Performed Station Name (0040,0242)
Definition mpps_scp.h:432
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
constexpr core::dicom_tag performed_procedure_step_start_date
Performed Procedure Step Start Date (0040,0244)
Definition mpps_scu.h:436
constexpr core::dicom_tag performed_procedure_step_start_time
Performed Procedure Step Start Time (0040,0245)
Definition mpps_scu.h:439
constexpr core::dicom_tag performed_procedure_step_description
Performed Procedure Step Description (0040,0254)
Definition mpps_scu.h:442

References kcenon::pacs::core::tags::accession_number, kcenon::pacs::services::mpps_create_data::accession_number, get_current_date(), get_current_time(), kcenon::pacs::core::tags::modality, kcenon::pacs::services::mpps_create_data::modality, kcenon::pacs::services::mpps_create_data::operator_name, kcenon::pacs::services::mpps_tags::operators_name, kcenon::pacs::core::tags::patient_birth_date, kcenon::pacs::services::mpps_create_data::patient_birth_date, kcenon::pacs::core::tags::patient_id, kcenon::pacs::services::mpps_create_data::patient_id, kcenon::pacs::core::tags::patient_name, kcenon::pacs::services::mpps_create_data::patient_name, kcenon::pacs::core::tags::patient_sex, kcenon::pacs::services::mpps_create_data::patient_sex, kcenon::pacs::services::mpps_tags::performed_procedure_step_description, kcenon::pacs::services::mpps_tags::performed_procedure_step_id, kcenon::pacs::services::mpps_tags::performed_procedure_step_start_date, kcenon::pacs::services::mpps_tags::performed_procedure_step_start_time, kcenon::pacs::services::mpps_tags::performed_procedure_step_status, kcenon::pacs::services::mpps_tags::performed_station_ae_title, kcenon::pacs::services::mpps_tags::performed_station_name, kcenon::pacs::services::mpps_create_data::performing_physician, kcenon::pacs::services::mpps_tags::performing_physicians_name, kcenon::pacs::services::mpps_create_data::procedure_description, kcenon::pacs::services::mpps_create_data::procedure_step_start_date, kcenon::pacs::services::mpps_create_data::procedure_step_start_time, kcenon::pacs::services::mpps_create_data::scheduled_procedure_step_id, kcenon::pacs::services::mpps_create_data::station_ae_title, kcenon::pacs::services::mpps_create_data::station_name, kcenon::pacs::core::tags::study_instance_uid, and kcenon::pacs::services::mpps_create_data::study_instance_uid.

Referenced by create().

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

◆ build_set_dataset()

core::dicom_dataset kcenon::pacs::services::mpps_scu::build_set_dataset ( const mpps_set_data & data) const
nodiscardprivate

Build DICOM dataset for N-SET request.

Definition at line 425 of file mpps_scu.cpp.

425 {
426 using namespace core;
427 using namespace encoding;
428
429 dicom_dataset ds;
430
431 // Update status
432 ds.set_string(mpps_tags::performed_procedure_step_status, vr_type::CS,
433 std::string(to_string(data.status)));
434
435 // End date/time
436 std::string end_date = data.procedure_step_end_date.empty()
437 ? get_current_date() : data.procedure_step_end_date;
438 std::string end_time = data.procedure_step_end_time.empty()
439 ? get_current_time() : data.procedure_step_end_time;
440
441 ds.set_string(mpps_tags::performed_procedure_step_end_date, vr_type::DA, end_date);
442 ds.set_string(mpps_tags::performed_procedure_step_end_time, vr_type::TM, end_time);
443
444 // Add performed series sequence for COMPLETED status
445 if (data.status == mpps_status::completed && !data.performed_series.empty()) {
446 auto& seq = ds.get_or_create_sequence(mpps_tags::performed_series_sequence);
447 for (const auto& s : data.performed_series) {
448 dicom_dataset series_item;
449
450 if (!s.series_uid.empty()) {
451 series_item.set_string(tags::series_instance_uid, vr_type::UI, s.series_uid);
452 }
453 if (!s.series_description.empty()) {
454 series_item.set_string(mpps_tags::series_description, vr_type::LO,
455 s.series_description);
456 }
457 if (!s.modality.empty()) {
458 series_item.set_string(tags::modality, vr_type::CS, s.modality);
459 }
460 if (!s.performing_physician.empty()) {
461 series_item.set_string(mpps_tags::performing_physicians_name, vr_type::PN,
462 s.performing_physician);
463 }
464 if (!s.operator_name.empty()) {
465 series_item.set_string(mpps_tags::operators_name, vr_type::PN, s.operator_name);
466 }
467
468 seq.push_back(std::move(series_item));
469 }
470 }
471
472 return ds;
473}
constexpr dicom_tag series_instance_uid
Series Instance UID.
constexpr core::dicom_tag performed_series_sequence
Performed Series Sequence (0040,0340)
Definition mpps_scp.h:450
constexpr core::dicom_tag performed_procedure_step_end_date
Performed Procedure Step End Date (0040,0250)
Definition mpps_scp.h:438
constexpr core::dicom_tag performed_procedure_step_end_time
Performed Procedure Step End Time (0040,0251)
Definition mpps_scp.h:441
constexpr core::dicom_tag series_description
Series Description (0008,103E)
Definition mpps_scu.h:460
@ completed
Procedure completed successfully.
auto to_string(mpps_status status) -> std::string_view
Convert mpps_status to DICOM string representation.
Definition mpps_scp.h:60

References kcenon::pacs::services::completed, get_current_date(), get_current_time(), kcenon::pacs::core::tags::modality, kcenon::pacs::services::mpps_tags::operators_name, kcenon::pacs::services::mpps_tags::performed_procedure_step_end_date, kcenon::pacs::services::mpps_tags::performed_procedure_step_end_time, kcenon::pacs::services::mpps_tags::performed_procedure_step_status, kcenon::pacs::services::mpps_set_data::performed_series, kcenon::pacs::services::mpps_tags::performed_series_sequence, kcenon::pacs::services::mpps_tags::performing_physicians_name, kcenon::pacs::services::mpps_set_data::procedure_step_end_date, kcenon::pacs::services::mpps_set_data::procedure_step_end_time, kcenon::pacs::services::mpps_tags::series_description, kcenon::pacs::core::tags::series_instance_uid, kcenon::pacs::services::mpps_set_data::status, and kcenon::pacs::services::to_string().

Referenced by set().

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

◆ complete()

network::Result< mpps_result > kcenon::pacs::services::mpps_scu::complete ( network::association & assoc,
std::string_view mpps_uid,
const std::vector< performed_series_info > & performed_series )
nodiscard

Complete an MPPS instance (convenience method)

Updates the MPPS to COMPLETED status with performed series information. Automatically fills in current date/time for end timestamps.

Parameters
assocThe established association to use
mpps_uidThe MPPS SOP Instance UID
performed_seriesInformation about performed series
Returns
Result containing mpps_result on success, or error message
Examples
mpps_scu/main.cpp.

Definition at line 299 of file mpps_scu.cpp.

302 {
303
304 mpps_set_data data;
305 data.mpps_sop_instance_uid = std::string(mpps_uid);
306 data.status = mpps_status::completed;
307 data.procedure_step_end_date = get_current_date();
308 data.procedure_step_end_time = get_current_time();
309 data.performed_series = performed_series;
310
311 return set(assoc, data);
312}
network::Result< mpps_result > set(network::association &assoc, const mpps_set_data &data)
Update an existing MPPS instance (N-SET)
Definition mpps_scu.cpp:197

References kcenon::pacs::services::completed, get_current_date(), get_current_time(), kcenon::pacs::services::mpps_set_data::mpps_sop_instance_uid, kcenon::pacs::services::mpps_set_data::performed_series, kcenon::pacs::services::mpps_set_data::procedure_step_end_date, kcenon::pacs::services::mpps_set_data::procedure_step_end_time, set(), and kcenon::pacs::services::mpps_set_data::status.

Here is the call graph for this function:

◆ create()

network::Result< mpps_result > kcenon::pacs::services::mpps_scu::create ( network::association & assoc,
const mpps_create_data & data )
nodiscard

Create a new MPPS instance (N-CREATE)

Starts a new Modality Performed Procedure Step with IN PROGRESS status. If mpps_sop_instance_uid is empty, a unique UID will be auto-generated.

Parameters
assocThe established association to use
dataThe MPPS creation data
Returns
Result containing mpps_result on success, or error message
Examples
mpps_scu/main.cpp.

Definition at line 98 of file mpps_scu.cpp.

100 {
101
102 using namespace network::dimse;
103
104 auto start_time = std::chrono::steady_clock::now();
105
106 // Verify association is established
107 if (!assoc.is_established()) {
110 "Association not established");
111 }
112
113 // Get accepted presentation context for MPPS
114 auto context_id = assoc.accepted_context_id(mpps_sop_class_uid);
115 if (!context_id) {
118 "No accepted presentation context for MPPS SOP Class");
119 }
120
121 // Generate or use provided MPPS UID
122 std::string mpps_uid = data.mpps_sop_instance_uid;
123 if (mpps_uid.empty() && config_.auto_generate_uid) {
124 mpps_uid = generate_mpps_uid();
125 }
126
127 if (mpps_uid.empty()) {
130 "MPPS SOP Instance UID is required");
131 }
132
133 // Build the MPPS creation dataset
134 auto dataset = build_create_dataset(data);
135
136 // Create the N-CREATE request
137 auto request = make_n_create_rq(next_message_id(), mpps_uid, std::move(dataset));
138
139 logger_->debug("Sending N-CREATE request for MPPS: " + mpps_uid);
140
141 // Send the request
142 auto send_result = assoc.send_dimse(*context_id, request);
143 if (send_result.is_err()) {
144 logger_->error("Failed to send N-CREATE: " + send_result.error().message);
145 return send_result.error();
146 }
147
148 // Receive the response
149 auto recv_result = assoc.receive_dimse(config_.timeout);
150 if (recv_result.is_err()) {
151 logger_->error("Failed to receive N-CREATE response: " + recv_result.error().message);
152 return recv_result.error();
153 }
154
155 const auto& [recv_context_id, response] = recv_result.value();
156
157 // Verify it's an N-CREATE response
158 if (response.command() != command_field::n_create_rsp) {
161 "Expected N-CREATE-RSP but received " +
162 std::string(to_string(response.command())));
163 }
164
165 auto end_time = std::chrono::steady_clock::now();
166 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
167 end_time - start_time);
168
169 // Build result
170 mpps_result result;
171 result.mpps_sop_instance_uid = mpps_uid;
172 result.status = static_cast<uint16_t>(response.status());
173 result.elapsed = elapsed;
174
175 // Extract error comment if present
176 if (response.command_set().contains(tag_error_comment)) {
177 result.error_comment = response.command_set().get_string(tag_error_comment);
178 }
179
180 // Update statistics
181 creates_performed_.fetch_add(1, std::memory_order_relaxed);
182
183 if (result.is_success()) {
184 logger_->info("N-CREATE successful for MPPS: " + mpps_uid);
185 } else {
186 logger_->warn("N-CREATE returned status 0x" +
187 std::to_string(result.status) + " for MPPS: " + mpps_uid);
188 }
189
190 return result;
191}
core::dicom_dataset build_create_dataset(const mpps_create_data &data) const
Build DICOM dataset for N-CREATE request.
Definition mpps_scu.cpp:350
std::atomic< size_t > creates_performed_
Statistics: N-CREATE operations performed.
Definition mpps_scu.h:423
uint16_t next_message_id() noexcept
Get the next message ID for DIMSE operations.
Definition mpps_scu.cpp:517
std::string generate_mpps_uid() const
Generate a unique MPPS SOP Instance UID.
Definition mpps_scu.cpp:479
constexpr int mpps_context_not_accepted
Definition result.h:175
constexpr int mpps_missing_uid
Definition result.h:176
constexpr int mpps_unexpected_command
Definition result.h:174
constexpr int association_not_established
Definition result.h:202
constexpr std::string_view mpps_sop_class_uid
MPPS (Modality Performed Procedure Step) SOP Class UID.
Definition mpps_scp.h:36
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234
std::chrono::milliseconds timeout
Timeout for receiving DIMSE response.
Definition mpps_scu.h:166
bool auto_generate_uid
Auto-generate MPPS UID if not provided.
Definition mpps_scu.h:169

References kcenon::pacs::network::association::accepted_context_id(), kcenon::pacs::error_codes::association_not_established, kcenon::pacs::services::mpps_scu_config::auto_generate_uid, build_create_dataset(), config_, creates_performed_, kcenon::pacs::services::mpps_result::elapsed, kcenon::pacs::services::mpps_result::error_comment, generate_mpps_uid(), kcenon::pacs::network::association::is_established(), kcenon::pacs::services::mpps_result::is_success(), logger_, kcenon::pacs::error_codes::mpps_context_not_accepted, kcenon::pacs::error_codes::mpps_missing_uid, kcenon::pacs::services::mpps_sop_class_uid, kcenon::pacs::services::mpps_create_data::mpps_sop_instance_uid, kcenon::pacs::services::mpps_result::mpps_sop_instance_uid, kcenon::pacs::error_codes::mpps_unexpected_command, next_message_id(), kcenon::pacs::pacs_error(), kcenon::pacs::network::association::receive_dimse(), kcenon::pacs::network::association::send_dimse(), kcenon::pacs::services::mpps_result::status, kcenon::pacs::services::mpps_scu_config::timeout, and kcenon::pacs::services::to_string().

Referenced by operator=().

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

◆ creates_performed()

size_t kcenon::pacs::services::mpps_scu::creates_performed ( ) const
nodiscardnoexcept

Get the number of N-CREATE operations performed.

Returns
Count of create operations

Definition at line 333 of file mpps_scu.cpp.

333 {
334 return creates_performed_.load(std::memory_order_relaxed);
335}

References creates_performed_.

◆ discontinue()

network::Result< mpps_result > kcenon::pacs::services::mpps_scu::discontinue ( network::association & assoc,
std::string_view mpps_uid,
std::string_view reason = "" )
nodiscard

Discontinue an MPPS instance (convenience method)

Updates the MPPS to DISCONTINUED status. Automatically fills in current date/time for end timestamps.

Parameters
assocThe established association to use
mpps_uidThe MPPS SOP Instance UID
reasonOptional discontinuation reason
Returns
Result containing mpps_result on success, or error message
Examples
mpps_scu/main.cpp.

Definition at line 314 of file mpps_scu.cpp.

317 {
318
319 mpps_set_data data;
320 data.mpps_sop_instance_uid = std::string(mpps_uid);
321 data.status = mpps_status::discontinued;
322 data.procedure_step_end_date = get_current_date();
323 data.procedure_step_end_time = get_current_time();
324 data.discontinuation_reason = std::string(reason);
325
326 return set(assoc, data);
327}
@ discontinued
Procedure was stopped/cancelled.

References kcenon::pacs::services::mpps_set_data::discontinuation_reason, kcenon::pacs::services::discontinued, get_current_date(), get_current_time(), kcenon::pacs::services::mpps_set_data::mpps_sop_instance_uid, kcenon::pacs::services::mpps_set_data::procedure_step_end_date, kcenon::pacs::services::mpps_set_data::procedure_step_end_time, set(), and kcenon::pacs::services::mpps_set_data::status.

Here is the call graph for this function:

◆ generate_mpps_uid()

std::string kcenon::pacs::services::mpps_scu::generate_mpps_uid ( ) const
nodiscardprivate

Generate a unique MPPS SOP Instance UID.

Definition at line 479 of file mpps_scu.cpp.

479 {
480 static std::mt19937_64 gen{std::random_device{}()};
481 static std::uniform_int_distribution<uint64_t> dist;
482
483 auto now = std::chrono::system_clock::now();
484 auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
485 now.time_since_epoch()).count();
486
487 return std::string(uid_root) + "." + std::to_string(timestamp) +
488 "." + std::to_string(dist(gen) % 100000);
489}

Referenced by create().

Here is the caller graph for this function:

◆ get_current_date()

std::string kcenon::pacs::services::mpps_scu::get_current_date ( ) const
nodiscardprivate

Get current date in DICOM DA format (YYYYMMDD)

Definition at line 491 of file mpps_scu.cpp.

491 {
492 auto now = std::time(nullptr);
493 std::tm tm{};
494#if defined(_WIN32)
495 localtime_s(&tm, &now);
496#else
497 localtime_r(&now, &tm);
498#endif
499 std::ostringstream oss;
500 oss << std::put_time(&tm, "%Y%m%d");
501 return oss.str();
502}

Referenced by build_create_dataset(), build_set_dataset(), complete(), and discontinue().

Here is the caller graph for this function:

◆ get_current_time()

std::string kcenon::pacs::services::mpps_scu::get_current_time ( ) const
nodiscardprivate

Get current time in DICOM TM format (HHMMSS)

Definition at line 504 of file mpps_scu.cpp.

504 {
505 auto now = std::time(nullptr);
506 std::tm tm{};
507#if defined(_WIN32)
508 localtime_s(&tm, &now);
509#else
510 localtime_r(&now, &tm);
511#endif
512 std::ostringstream oss;
513 oss << std::put_time(&tm, "%H%M%S");
514 return oss.str();
515}

Referenced by build_create_dataset(), build_set_dataset(), complete(), and discontinue().

Here is the caller graph for this function:

◆ next_message_id()

uint16_t kcenon::pacs::services::mpps_scu::next_message_id ( )
nodiscardprivatenoexcept

Get the next message ID for DIMSE operations.

Definition at line 517 of file mpps_scu.cpp.

517 {
518 uint16_t id = message_id_counter_.fetch_add(1, std::memory_order_relaxed);
519 // Wrap around at 0xFFFF, skip 0 (reserved)
520 if (id == 0) {
521 id = message_id_counter_.fetch_add(1, std::memory_order_relaxed);
522 }
523 return id;
524}
std::atomic< uint16_t > message_id_counter_
Message ID counter.
Definition mpps_scu.h:420
@ id
Implant Displaced (alternate code)

References message_id_counter_.

Referenced by create(), and set().

Here is the caller graph for this function:

◆ operator=() [1/2]

mpps_scu & kcenon::pacs::services::mpps_scu::operator= ( const mpps_scu & )
delete

◆ operator=() [2/2]

mpps_scu & kcenon::pacs::services::mpps_scu::operator= ( mpps_scu && )
delete

References create(), and set().

Here is the call graph for this function:

◆ reset_statistics()

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

Reset statistics counters to zero.

Definition at line 341 of file mpps_scu.cpp.

341 {
342 creates_performed_.store(0, std::memory_order_relaxed);
343 sets_performed_.store(0, std::memory_order_relaxed);
344}
std::atomic< size_t > sets_performed_
Statistics: N-SET operations performed.
Definition mpps_scu.h:426

References creates_performed_, and sets_performed_.

◆ set()

network::Result< mpps_result > kcenon::pacs::services::mpps_scu::set ( network::association & assoc,
const mpps_set_data & data )
nodiscard

Update an existing MPPS instance (N-SET)

Updates an MPPS instance to COMPLETED or DISCONTINUED status.

Parameters
assocThe established association to use
dataThe MPPS update data
Returns
Result containing mpps_result on success, or error message

Definition at line 197 of file mpps_scu.cpp.

199 {
200
201 using namespace network::dimse;
202
203 auto start_time = std::chrono::steady_clock::now();
204
205 // Verify association is established
206 if (!assoc.is_established()) {
209 "Association not established");
210 }
211
212 // Validate MPPS UID is provided
213 if (data.mpps_sop_instance_uid.empty()) {
216 "MPPS SOP Instance UID is required for N-SET");
217 }
218
219 // Validate status is not IN PROGRESS (can only set to COMPLETED or DISCONTINUED)
220 if (data.status == mpps_status::in_progress) {
223 "Cannot set MPPS status back to IN PROGRESS");
224 }
225
226 // Get accepted presentation context for MPPS
227 auto context_id = assoc.accepted_context_id(mpps_sop_class_uid);
228 if (!context_id) {
231 "No accepted presentation context for MPPS SOP Class");
232 }
233
234 // Build the modification dataset
235 auto dataset = build_set_dataset(data);
236
237 // Create the N-SET request
238 auto request = make_n_set_rq(
240 data.mpps_sop_instance_uid,
241 std::move(dataset));
242
243 logger_->debug("Sending N-SET request for MPPS: " + data.mpps_sop_instance_uid);
244
245 // Send the request
246 auto send_result = assoc.send_dimse(*context_id, request);
247 if (send_result.is_err()) {
248 logger_->error("Failed to send N-SET: " + send_result.error().message);
249 return send_result.error();
250 }
251
252 // Receive the response
253 auto recv_result = assoc.receive_dimse(config_.timeout);
254 if (recv_result.is_err()) {
255 logger_->error("Failed to receive N-SET response: " + recv_result.error().message);
256 return recv_result.error();
257 }
258
259 const auto& [recv_context_id, response] = recv_result.value();
260
261 // Verify it's an N-SET response
262 if (response.command() != command_field::n_set_rsp) {
265 "Expected N-SET-RSP but received " +
266 std::string(to_string(response.command())));
267 }
268
269 auto end_time = std::chrono::steady_clock::now();
270 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
271 end_time - start_time);
272
273 // Build result
274 mpps_result result;
275 result.mpps_sop_instance_uid = data.mpps_sop_instance_uid;
276 result.status = static_cast<uint16_t>(response.status());
277 result.elapsed = elapsed;
278
279 // Extract error comment if present
280 if (response.command_set().contains(tag_error_comment)) {
281 result.error_comment = response.command_set().get_string(tag_error_comment);
282 }
283
284 // Update statistics
285 sets_performed_.fetch_add(1, std::memory_order_relaxed);
286
287 if (result.is_success()) {
288 logger_->info("N-SET successful for MPPS: " + data.mpps_sop_instance_uid +
289 " (status: " + std::string(to_string(data.status)) + ")");
290 } else {
291 logger_->warn("N-SET returned status 0x" +
292 std::to_string(result.status) + " for MPPS: " +
293 data.mpps_sop_instance_uid);
294 }
295
296 return result;
297}
core::dicom_dataset build_set_dataset(const mpps_set_data &data) const
Build DICOM dataset for N-SET request.
Definition mpps_scu.cpp:425
constexpr int mpps_invalid_status_transition
Definition result.h:177
@ in_progress
Procedure is currently being performed.

References kcenon::pacs::network::association::accepted_context_id(), kcenon::pacs::error_codes::association_not_established, build_set_dataset(), config_, kcenon::pacs::services::mpps_result::elapsed, kcenon::pacs::services::mpps_result::error_comment, kcenon::pacs::services::in_progress, kcenon::pacs::network::association::is_established(), kcenon::pacs::services::mpps_result::is_success(), logger_, kcenon::pacs::error_codes::mpps_context_not_accepted, kcenon::pacs::error_codes::mpps_invalid_status_transition, kcenon::pacs::error_codes::mpps_missing_uid, kcenon::pacs::services::mpps_sop_class_uid, kcenon::pacs::services::mpps_result::mpps_sop_instance_uid, kcenon::pacs::services::mpps_set_data::mpps_sop_instance_uid, kcenon::pacs::error_codes::mpps_unexpected_command, next_message_id(), kcenon::pacs::pacs_error(), kcenon::pacs::network::association::receive_dimse(), kcenon::pacs::network::association::send_dimse(), sets_performed_, kcenon::pacs::services::mpps_result::status, kcenon::pacs::services::mpps_set_data::status, kcenon::pacs::services::mpps_scu_config::timeout, and kcenon::pacs::services::to_string().

Referenced by complete(), discontinue(), and operator=().

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

◆ sets_performed()

size_t kcenon::pacs::services::mpps_scu::sets_performed ( ) const
nodiscardnoexcept

Get the number of N-SET operations performed.

Returns
Count of set operations

Definition at line 337 of file mpps_scu.cpp.

337 {
338 return sets_performed_.load(std::memory_order_relaxed);
339}

References sets_performed_.

Member Data Documentation

◆ config_

mpps_scu_config kcenon::pacs::services::mpps_scu::config_
private

Configuration.

Definition at line 417 of file mpps_scu.h.

Referenced by create(), and set().

◆ creates_performed_

std::atomic<size_t> kcenon::pacs::services::mpps_scu::creates_performed_ {0}
private

Statistics: N-CREATE operations performed.

Definition at line 423 of file mpps_scu.h.

423{0};

Referenced by create(), creates_performed(), and reset_statistics().

◆ logger_

std::shared_ptr<di::ILogger> kcenon::pacs::services::mpps_scu::logger_
private

Logger instance.

Definition at line 414 of file mpps_scu.h.

Referenced by create(), and set().

◆ message_id_counter_

std::atomic<uint16_t> kcenon::pacs::services::mpps_scu::message_id_counter_ {1}
private

Message ID counter.

Definition at line 420 of file mpps_scu.h.

420{1};

Referenced by next_message_id().

◆ sets_performed_

std::atomic<size_t> kcenon::pacs::services::mpps_scu::sets_performed_ {0}
private

Statistics: N-SET operations performed.

Definition at line 426 of file mpps_scu.h.

426{0};

Referenced by reset_statistics(), set(), and sets_performed().


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