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

Storage Commitment Push Model SCP. More...

#include <storage_commitment_scp.h>

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

Public Member Functions

 storage_commitment_scp (std::shared_ptr< storage::storage_interface > storage, std::shared_ptr< di::ILogger > logger=nullptr)
 Construct Storage Commitment SCP with storage backend.
 
 ~storage_commitment_scp () override=default
 
std::vector< std::string > supported_sop_classes () const override
 Get the list of SOP Class UIDs supported by this service.
 
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.
 
std::string_view service_name () const noexcept override
 Get the service name for logging/debugging.
 
size_t actions_processed () const noexcept
 
size_t instances_committed () const noexcept
 
size_t instances_failed () const noexcept
 
void reset_statistics () noexcept
 
- 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_action (network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
 
commitment_result verify_instances (const std::string &transaction_uid, const std::vector< sop_reference > &references)
 
network::Result< std::monostate > send_event_report (network::association &assoc, uint8_t context_id, const commitment_result &result)
 
network::Result< std::monostate > send_n_action_response (network::association &assoc, uint8_t context_id, uint16_t message_id, network::dimse::status_code status)
 

Static Private Member Functions

static std::vector< sop_referenceparse_referenced_sop_sequence (const core::dicom_dataset &dataset)
 
static core::dicom_dataset build_event_report_dataset (const commitment_result &result)
 

Private Attributes

std::shared_ptr< storage::storage_interfacestorage_
 
std::atomic< size_t > actions_processed_ {0}
 
std::atomic< size_t > instances_committed_ {0}
 
std::atomic< size_t > instances_failed_ {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

Storage Commitment Push Model SCP.

Processes N-ACTION requests containing Referenced SOP Sequences, verifies each instance exists in storage, and sends an N-EVENT-REPORT with per-instance success/failure status.

Protocol Flow

SCU (Modality) SCP (this)
│ │
│── N-ACTION-RQ ──────────────────►│
│ Transaction UID │
│ Referenced SOP Sequence │
│◄── N-ACTION-RSP (Success) ──────│
│ │
│ ... SCP verifies storage ... │
│ │
│◄── N-EVENT-REPORT-RQ ──────────│
│ Transaction UID │
│ Success/Failed Sequences │
│── N-EVENT-REPORT-RSP ──────────►│

Definition at line 59 of file storage_commitment_scp.h.

Constructor & Destructor Documentation

◆ storage_commitment_scp()

kcenon::pacs::services::storage_commitment_scp::storage_commitment_scp ( std::shared_ptr< storage::storage_interface > storage,
std::shared_ptr< di::ILogger > logger = nullptr )
explicit

Construct Storage Commitment SCP with storage backend.

Parameters
storageThe storage interface for verifying instance existence
loggerLogger instance for service logging

Definition at line 23 of file storage_commitment_scp.cpp.

26 : scp_service(std::move(logger))
27 , storage_(std::move(storage)) {}
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
std::shared_ptr< storage::storage_interface > storage_

◆ ~storage_commitment_scp()

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

Member Function Documentation

◆ actions_processed()

size_t kcenon::pacs::services::storage_commitment_scp::actions_processed ( ) const
nodiscardnoexcept

Definition at line 64 of file storage_commitment_scp.cpp.

64 {
65 return actions_processed_.load();
66}

References actions_processed_.

◆ build_event_report_dataset()

core::dicom_dataset kcenon::pacs::services::storage_commitment_scp::build_event_report_dataset ( const commitment_result & result)
staticnodiscardprivate

Definition at line 266 of file storage_commitment_scp.cpp.

267 {
268
269 core::dicom_dataset ds;
270
271 // Transaction UID (0008,1195)
273 result.transaction_uid);
274
275 // Referenced SOP Sequence (0008,1199) — successful instances
276 if (!result.success_references.empty()) {
277 auto& success_seq = ds.get_or_create_sequence(
279
280 for (const auto& ref : result.success_references) {
281 core::dicom_dataset item;
283 encoding::vr_type::UI, ref.sop_class_uid);
285 encoding::vr_type::UI, ref.sop_instance_uid);
286 success_seq.push_back(std::move(item));
287 }
288 }
289
290 // Failed SOP Sequence (0008,1198) — failed instances
291 if (!result.failed_references.empty()) {
292 auto& failed_seq = ds.get_or_create_sequence(
294
295 for (const auto& [ref, reason] : result.failed_references) {
296 core::dicom_dataset item;
298 encoding::vr_type::UI, ref.sop_class_uid);
300 encoding::vr_type::UI, ref.sop_instance_uid);
301 item.set_numeric<uint16_t>(core::tags::failure_reason,
303 static_cast<uint16_t>(reason));
304 failed_seq.push_back(std::move(item));
305 }
306 }
307
308 return ds;
309}
constexpr dicom_tag failed_sop_sequence
Failed SOP Sequence — instances that failed commitment (PS3.4 J.3)
constexpr dicom_tag referenced_sop_sequence
Referenced SOP Sequence — instances in commitment request/success (PS3.4 J.3)
constexpr dicom_tag referenced_sop_class_uid
Referenced SOP Class UID (in Sequence)
constexpr dicom_tag failure_reason
Failure Reason — reason code for commitment failure (PS3.4 Table J.3-2)
constexpr dicom_tag referenced_sop_instance_uid
Referenced SOP Instance UID (in Sequence)
constexpr dicom_tag item
Item.
constexpr dicom_tag transaction_uid
Transaction UID — identifies a Storage Commitment transaction (PS3.4 J.3)
@ UI
Unique Identifier (64 chars max)
@ US
Unsigned Short (2 bytes)

References kcenon::pacs::services::commitment_result::failed_references, kcenon::pacs::core::tags::failed_sop_sequence, kcenon::pacs::core::tags::failure_reason, kcenon::pacs::core::dicom_dataset::get_or_create_sequence(), kcenon::pacs::core::tags::referenced_sop_class_uid, kcenon::pacs::core::tags::referenced_sop_instance_uid, kcenon::pacs::core::tags::referenced_sop_sequence, kcenon::pacs::core::dicom_dataset::set_string(), kcenon::pacs::services::commitment_result::success_references, kcenon::pacs::core::tags::transaction_uid, kcenon::pacs::services::commitment_result::transaction_uid, kcenon::pacs::encoding::UI, and kcenon::pacs::encoding::US.

Referenced by send_event_report().

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

◆ handle_message()

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

Handle an incoming DIMSE message.

Processes the request and sends appropriate response(s) via the association.

Parameters
assocThe association on which the message was received
context_idThe presentation context ID for the message
requestThe incoming DIMSE request message
Returns
Success or error result

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

Definition at line 37 of file storage_commitment_scp.cpp.

40 {
41
42 using namespace network::dimse;
43
44 switch (request.command()) {
45 case command_field::n_action_rq:
46 return handle_n_action(assoc, context_id, request);
47
48 default:
51 "Unexpected command for Storage Commitment SCP: " +
52 std::string(to_string(request.command())));
53 }
54}
network::Result< std::monostate > handle_n_action(network::association &assoc, uint8_t context_id, const network::dimse::dimse_message &request)
constexpr int storage_commitment_unexpected_command
Definition result.h:180
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_action(), kcenon::pacs::pacs_void_error(), kcenon::pacs::error_codes::storage_commitment_unexpected_command, and kcenon::pacs::services::to_string().

Here is the call graph for this function:

◆ handle_n_action()

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

Definition at line 86 of file storage_commitment_scp.cpp.

89 {
90
91 using namespace network::dimse;
92
93 // Validate Requested SOP Class UID
94 auto sop_class_uid = request.command_set().get_string(
95 tag_requested_sop_class_uid);
96 if (sop_class_uid.empty()) {
97 sop_class_uid = request.affected_sop_class_uid();
98 }
99
100 if (sop_class_uid != storage_commitment_push_model_sop_class_uid) {
102 assoc, context_id, request.message_id(),
103 status_refused_sop_class_not_supported);
104 }
105
106 // Validate Action Type ID = 1 (Request Storage Commitment)
107 auto action_type = request.action_type_id();
108 if (!action_type.has_value() ||
109 action_type.value() != storage_commitment_action_type_request) {
111 assoc, context_id, request.message_id(),
112 status_error_no_such_action_type);
113 }
114
115 // Verify we have a dataset
116 if (!request.has_dataset()) {
118 assoc, context_id, request.message_id(),
119 status_error_missing_attribute);
120 }
121
122 const auto& dataset = request.dataset().value().get();
123
124 // Extract Transaction UID (0008,1195)
125 auto transaction_uid = dataset.get_string(core::tags::transaction_uid);
126 if (transaction_uid.empty()) {
128 assoc, context_id, request.message_id(),
129 status_error_missing_attribute);
130 }
131
132 // Parse Referenced SOP Sequence (0008,1199)
133 auto references = parse_referenced_sop_sequence(dataset);
134 if (references.empty()) {
136 assoc, context_id, request.message_id(),
137 status_error_missing_attribute);
138 }
139
140 // Send N-ACTION-RSP with Success (acknowledge the request)
141 auto rsp_result = send_n_action_response(
142 assoc, context_id, request.message_id(),
143 status_success);
144 if (rsp_result.is_err()) {
145 return rsp_result;
146 }
147
148 // Update statistics
150
151 // Verify instances against storage
152 auto result = verify_instances(transaction_uid, references);
153
154 // Update instance statistics
155 instances_committed_ += result.success_references.size();
156 instances_failed_ += result.failed_references.size();
157
158 // Send N-EVENT-REPORT with verification results
159 return send_event_report(assoc, context_id, result);
160}
commitment_result verify_instances(const std::string &transaction_uid, const std::vector< sop_reference > &references)
static std::vector< sop_reference > parse_referenced_sop_sequence(const core::dicom_dataset &dataset)
network::Result< std::monostate > send_n_action_response(network::association &assoc, uint8_t context_id, uint16_t message_id, network::dimse::status_code status)
network::Result< std::monostate > send_event_report(network::association &assoc, uint8_t context_id, const commitment_result &result)
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr std::string_view storage_commitment_push_model_sop_class_uid
Storage Commitment Push Model SOP Class UID (PS3.4 Table J.3-1)
constexpr uint16_t storage_commitment_action_type_request
N-ACTION: Request Storage Commitment (Action Type ID = 1)

References kcenon::pacs::network::dimse::dimse_message::action_type_id(), actions_processed_, kcenon::pacs::network::dimse::dimse_message::affected_sop_class_uid(), kcenon::pacs::network::dimse::dimse_message::command_set(), kcenon::pacs::network::dimse::dimse_message::dataset(), kcenon::pacs::network::dimse::dimse_message::has_dataset(), instances_committed_, instances_failed_, kcenon::pacs::network::dimse::dimse_message::message_id(), parse_referenced_sop_sequence(), send_event_report(), send_n_action_response(), kcenon::pacs::services::storage_commitment_action_type_request, kcenon::pacs::services::storage_commitment_push_model_sop_class_uid, kcenon::pacs::core::tags::transaction_uid, and verify_instances().

Referenced by handle_message().

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

◆ instances_committed()

size_t kcenon::pacs::services::storage_commitment_scp::instances_committed ( ) const
nodiscardnoexcept

Definition at line 68 of file storage_commitment_scp.cpp.

68 {
69 return instances_committed_.load();
70}

References instances_committed_.

◆ instances_failed()

size_t kcenon::pacs::services::storage_commitment_scp::instances_failed ( ) const
nodiscardnoexcept

Definition at line 72 of file storage_commitment_scp.cpp.

72 {
73 return instances_failed_.load();
74}

References instances_failed_.

◆ parse_referenced_sop_sequence()

std::vector< sop_reference > kcenon::pacs::services::storage_commitment_scp::parse_referenced_sop_sequence ( const core::dicom_dataset & dataset)
staticnodiscardprivate

Definition at line 243 of file storage_commitment_scp.cpp.

244 {
245
246 std::vector<sop_reference> references;
247
248 const auto* seq = dataset.get_sequence(core::tags::referenced_sop_sequence);
249 if (seq == nullptr) {
250 return references;
251 }
252
253 for (const auto& item : *seq) {
254 sop_reference ref;
255 ref.sop_class_uid = item.get_string(core::tags::referenced_sop_class_uid);
256 ref.sop_instance_uid = item.get_string(core::tags::referenced_sop_instance_uid);
257
258 if (!ref.sop_class_uid.empty() && !ref.sop_instance_uid.empty()) {
259 references.push_back(std::move(ref));
260 }
261 }
262
263 return references;
264}

References kcenon::pacs::core::dicom_dataset::get_sequence(), kcenon::pacs::core::tags::referenced_sop_class_uid, kcenon::pacs::core::tags::referenced_sop_instance_uid, kcenon::pacs::core::tags::referenced_sop_sequence, kcenon::pacs::services::sop_reference::sop_class_uid, and kcenon::pacs::services::sop_reference::sop_instance_uid.

Referenced by handle_n_action().

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

◆ reset_statistics()

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

◆ send_event_report()

network::Result< std::monostate > kcenon::pacs::services::storage_commitment_scp::send_event_report ( network::association & assoc,
uint8_t context_id,
const commitment_result & result )
nodiscardprivate

Definition at line 190 of file storage_commitment_scp.cpp.

193 {
194
195 using namespace network::dimse;
196
197 // Determine event type: 1 = all success, 2 = failures exist
198 uint16_t event_type = result.failed_references.empty()
201
202 // Build N-EVENT-REPORT-RQ
203 auto event_rq = make_n_event_report_rq(
204 1, // message_id
207 event_type);
208
209 // Build and attach event dataset
210 auto event_dataset = build_event_report_dataset(result);
211 event_rq.set_dataset(std::move(event_dataset));
212
213 // Send N-EVENT-REPORT-RQ
214 return assoc.send_dimse(context_id, event_rq);
215}
static core::dicom_dataset build_event_report_dataset(const commitment_result &result)
constexpr std::string_view storage_commitment_push_model_sop_instance_uid
Storage Commitment Push Model SOP Instance UID (Well-Known)
constexpr uint16_t storage_commitment_event_type_success
N-EVENT-REPORT: Storage Commitment Request Successful (Event Type ID = 1)
constexpr uint16_t storage_commitment_event_type_failure
N-EVENT-REPORT: Storage Commitment Request Complete - Failures Exist (Event Type ID = 2)

References build_event_report_dataset(), kcenon::pacs::services::commitment_result::failed_references, kcenon::pacs::network::association::send_dimse(), kcenon::pacs::services::storage_commitment_event_type_failure, kcenon::pacs::services::storage_commitment_event_type_success, kcenon::pacs::services::storage_commitment_push_model_sop_class_uid, and kcenon::pacs::services::storage_commitment_push_model_sop_instance_uid.

Referenced by handle_n_action().

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

◆ send_n_action_response()

network::Result< std::monostate > kcenon::pacs::services::storage_commitment_scp::send_n_action_response ( network::association & assoc,
uint8_t context_id,
uint16_t message_id,
network::dimse::status_code status )
nodiscardprivate

Definition at line 221 of file storage_commitment_scp.cpp.

225 {
226
227 using namespace network::dimse;
228
229 auto response = make_n_action_rsp(
230 message_id,
234 status);
235
236 return assoc.send_dimse(context_id, response);
237}

References kcenon::pacs::network::association::send_dimse(), kcenon::pacs::services::storage_commitment_action_type_request, kcenon::pacs::services::storage_commitment_push_model_sop_class_uid, and kcenon::pacs::services::storage_commitment_push_model_sop_instance_uid.

Referenced by handle_n_action().

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

◆ service_name()

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

Get the service name for logging/debugging.

Returns
Human-readable service name

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

Definition at line 56 of file storage_commitment_scp.cpp.

56 {
57 return "Storage Commitment SCP";
58}

◆ supported_sop_classes()

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

Get the list of SOP Class UIDs supported by this service.

Returns
Vector of SOP Class UIDs that this service can handle

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

Definition at line 33 of file storage_commitment_scp.cpp.

33 {
35}

References kcenon::pacs::services::storage_commitment_push_model_sop_class_uid.

◆ verify_instances()

commitment_result kcenon::pacs::services::storage_commitment_scp::verify_instances ( const std::string & transaction_uid,
const std::vector< sop_reference > & references )
nodiscardprivate

Definition at line 166 of file storage_commitment_scp.cpp.

168 {
169
170 commitment_result result;
171 result.transaction_uid = transaction_uid;
172 result.timestamp = std::chrono::system_clock::now();
173
174 for (const auto& ref : references) {
175 if (storage_ && storage_->exists(ref.sop_instance_uid)) {
176 result.success_references.push_back(ref);
177 } else {
178 result.failed_references.emplace_back(
180 }
181 }
182
183 return result;
184}
@ no_such_object_instance
Referenced SOP Instance not found in storage.

References kcenon::pacs::services::commitment_result::failed_references, kcenon::pacs::services::no_such_object_instance, storage_, kcenon::pacs::services::commitment_result::success_references, kcenon::pacs::services::commitment_result::timestamp, and kcenon::pacs::services::commitment_result::transaction_uid.

Referenced by handle_n_action().

Here is the caller graph for this function:

Member Data Documentation

◆ actions_processed_

std::atomic<size_t> kcenon::pacs::services::storage_commitment_scp::actions_processed_ {0}
private

Definition at line 148 of file storage_commitment_scp.h.

148{0};

Referenced by actions_processed(), handle_n_action(), and reset_statistics().

◆ instances_committed_

std::atomic<size_t> kcenon::pacs::services::storage_commitment_scp::instances_committed_ {0}
private

Definition at line 149 of file storage_commitment_scp.h.

149{0};

Referenced by handle_n_action(), instances_committed(), and reset_statistics().

◆ instances_failed_

std::atomic<size_t> kcenon::pacs::services::storage_commitment_scp::instances_failed_ {0}
private

Definition at line 150 of file storage_commitment_scp.h.

150{0};

Referenced by handle_n_action(), instances_failed(), and reset_statistics().

◆ storage_

std::shared_ptr<storage::storage_interface> kcenon::pacs::services::storage_commitment_scp::storage_
private

Definition at line 146 of file storage_commitment_scp.h.

Referenced by verify_instances().


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