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

#include <association.h>

Collaboration diagram for kcenon::pacs::network::association:
Collaboration graph

Public Types

using clock = std::chrono::steady_clock
 
using duration = std::chrono::milliseconds
 
using time_point = clock::time_point
 

Public Member Functions

 association ()
 Default constructor (creates idle association).
 
 association (association &&other) noexcept
 Move constructor.
 
associationoperator= (association &&other) noexcept
 Move assignment operator.
 
 ~association ()
 Destructor (aborts if still established).
 
 association (const association &)=delete
 
associationoperator= (const association &)=delete
 
association_state state () const noexcept
 Get current association state.
 
bool is_established () const noexcept
 Check if association is established and ready for DIMSE.
 
bool is_closed () const noexcept
 Check if association has been released or aborted.
 
std::string_view calling_ae () const noexcept
 Get calling AE title.
 
std::string_view called_ae () const noexcept
 Get called AE title.
 
uint32_t max_pdu_size () const noexcept
 Get negotiated maximum PDU size.
 
std::string_view remote_implementation_class () const noexcept
 Get remote implementation class UID.
 
std::string_view remote_implementation_version () const noexcept
 Get remote implementation version name.
 
bool has_accepted_context (std::string_view abstract_syntax) const
 Check if a presentation context for the abstract syntax was accepted.
 
std::optional< uint8_t > accepted_context_id (std::string_view abstract_syntax) const
 Get the presentation context ID for an abstract syntax.
 
Result< encoding::transfer_syntaxcontext_transfer_syntax (uint8_t pc_id) const
 Get the transfer syntax for an accepted context.
 
const std::vector< accepted_presentation_context > & accepted_contexts () const noexcept
 Get all accepted presentation contexts.
 
Result< std::monostate > send_dimse (uint8_t context_id, const dimse::dimse_message &msg)
 Send a DIMSE message.
 
Result< std::pair< uint8_t, dimse::dimse_message > > receive_dimse (duration timeout=default_timeout)
 Receive a DIMSE message.
 
associate_rq build_associate_rq () const
 Build A-ASSOCIATE-RQ PDU for sending.
 
associate_ac build_associate_ac () const
 Build A-ASSOCIATE-AC PDU for sending.
 
bool process_associate_ac (const associate_ac &ac)
 Process received A-ASSOCIATE-AC PDU.
 
void process_associate_rj (const associate_rj &rj)
 Process received A-ASSOCIATE-RJ PDU.
 
std::optional< rejection_infoget_rejection_info () const
 Get rejection info if association was rejected.
 
Result< std::monostate > release (duration timeout=default_timeout)
 Gracefully release the association.
 
void process_release_rq ()
 Process received A-RELEASE-RQ.
 
void process_release_rp ()
 Process received A-RELEASE-RP.
 
void abort (uint8_t source=0, uint8_t reason=0)
 Abort the association immediately.
 
void process_abort (const abort_source &source, const abort_reason &reason)
 Process received A-ABORT PDU.
 
void set_state (association_state new_state)
 Force state transition (for testing).
 
void set_peer (association *peer)
 Set peer association for in-memory testing.
 
void enqueue_message (uint8_t context_id, dimse::dimse_message msg)
 Enqueue message from peer (for in-memory testing).
 
void update_peer (association *old_peer, association *new_peer)
 Update peer pointer (for in-memory testing).
 

Static Public Member Functions

static Result< associationconnect (const std::string &host, uint16_t port, const association_config &config, duration timeout=default_timeout)
 Initiate an SCU association to a remote SCP.
 
static association accept (const associate_rq &rq, const scp_config &config)
 Accept an incoming SCP association.
 
static associate_rj reject (reject_result result, uint8_t source, uint8_t reason)
 Reject an incoming association request.
 

Static Public Attributes

static constexpr duration default_timeout {30000}
 Default timeout for operations.
 

Private Types

using message_type = std::pair<uint8_t, dimse::dimse_message>
 Incoming message queue for in-memory testing (thread-safe)
 
using message_queue_type = kcenon::thread::detail::concurrent_queue<message_type>
 

Private Member Functions

bool can_transition_to (association_state new_state) const
 Validate state transition.
 
void transition (association_state new_state)
 Perform state transition.
 
void build_context_map ()
 Build presentation context map from accepted contexts.
 
void negotiate_contexts (const associate_rq &rq, const scp_config &config)
 Negotiate presentation contexts for SCP.
 

Private Attributes

association_state state_ {association_state::idle}
 Current state.
 
std::string calling_ae_
 Calling AE Title.
 
std::string called_ae_
 Called AE Title.
 
std::string our_ae_
 Our AE Title (may be calling or called depending on role)
 
uint32_t max_pdu_size_ {DEFAULT_MAX_PDU_LENGTH}
 Negotiated maximum PDU size.
 
std::string our_implementation_class_
 Our implementation class UID.
 
std::string our_implementation_version_
 Our implementation version name.
 
std::string remote_implementation_class_
 Remote implementation class UID.
 
std::string remote_implementation_version_
 Remote implementation version name.
 
std::vector< proposed_presentation_contextproposed_contexts_
 Proposed presentation contexts (SCU)
 
std::vector< accepted_presentation_contextaccepted_contexts_
 Accepted presentation contexts.
 
std::map< std::string, uint8_t > abstract_syntax_to_context_
 Map from abstract syntax to accepted context ID.
 
std::map< uint8_t, encoding::transfer_syntaxcontext_to_transfer_syntax_
 Map from context ID to transfer syntax.
 
std::optional< rejection_inforejection_info_
 Rejection information (if rejected)
 
uint8_t abort_source_ {0}
 Abort source (if aborted)
 
uint8_t abort_reason_ {0}
 Abort reason (if aborted)
 
std::mutex mutex_
 Thread safety mutex.
 
bool is_scu_ {true}
 Is this an SCU (true) or SCP (false)?
 
associationpeer_ {nullptr}
 Peer association for in-memory testing.
 
std::unique_ptr< message_queue_typeincoming_queue_
 

Detailed Description

Definition at line 279 of file association.h.

Member Typedef Documentation

◆ clock

using kcenon::pacs::network::association::clock = std::chrono::steady_clock

Definition at line 285 of file association.h.

◆ duration

using kcenon::pacs::network::association::duration = std::chrono::milliseconds

Definition at line 286 of file association.h.

◆ message_queue_type

using kcenon::pacs::network::association::message_queue_type = kcenon::thread::detail::concurrent_queue<message_type>
private

Definition at line 638 of file association.h.

◆ message_type

Incoming message queue for in-memory testing (thread-safe)

Definition at line 637 of file association.h.

◆ time_point

Definition at line 287 of file association.h.

Constructor & Destructor Documentation

◆ association() [1/3]

kcenon::pacs::network::association::association ( )
default

Default constructor (creates idle association).

◆ association() [2/3]

kcenon::pacs::network::association::association ( association && other)
noexcept

Move constructor.

Definition at line 103 of file association.cpp.

103 {
104 std::lock_guard<std::mutex> lock(other.mutex_);
105 state_ = other.state_;
106 calling_ae_ = std::move(other.calling_ae_);
107 called_ae_ = std::move(other.called_ae_);
108 our_ae_ = std::move(other.our_ae_);
109 max_pdu_size_ = other.max_pdu_size_;
110 our_implementation_class_ = std::move(other.our_implementation_class_);
111 our_implementation_version_ = std::move(other.our_implementation_version_);
112 remote_implementation_class_ = std::move(other.remote_implementation_class_);
113 remote_implementation_version_ = std::move(other.remote_implementation_version_);
114 proposed_contexts_ = std::move(other.proposed_contexts_);
115 accepted_contexts_ = std::move(other.accepted_contexts_);
116 abstract_syntax_to_context_ = std::move(other.abstract_syntax_to_context_);
117 context_to_transfer_syntax_ = std::move(other.context_to_transfer_syntax_);
118 rejection_info_ = std::move(other.rejection_info_);
119 abort_source_ = other.abort_source_;
120 abort_reason_ = other.abort_reason_;
121 is_scu_ = other.is_scu_;
122 peer_ = other.peer_;
123 incoming_queue_ = std::move(other.incoming_queue_);
124 other.incoming_queue_ = std::make_unique<message_queue_type>();
125
127 other.peer_ = nullptr;
128
129 if (peer_) {
130 peer_->update_peer(&other, this);
131 }
132}
std::map< std::string, uint8_t > abstract_syntax_to_context_
Map from abstract syntax to accepted context ID.
std::unique_ptr< message_queue_type > incoming_queue_
std::string remote_implementation_class_
Remote implementation class UID.
std::vector< proposed_presentation_context > proposed_contexts_
Proposed presentation contexts (SCU)
uint8_t abort_reason_
Abort reason (if aborted)
std::string called_ae_
Called AE Title.
std::string our_implementation_class_
Our implementation class UID.
std::string our_ae_
Our AE Title (may be calling or called depending on role)
void update_peer(association *old_peer, association *new_peer)
Update peer pointer (for in-memory testing).
bool is_scu_
Is this an SCU (true) or SCP (false)?
std::string calling_ae_
Calling AE Title.
association_state state_
Current state.
std::string our_implementation_version_
Our implementation version name.
std::map< uint8_t, encoding::transfer_syntax > context_to_transfer_syntax_
Map from context ID to transfer syntax.
association * peer_
Peer association for in-memory testing.
uint8_t abort_source_
Abort source (if aborted)
uint32_t max_pdu_size_
Negotiated maximum PDU size.
std::string remote_implementation_version_
Remote implementation version name.
std::vector< accepted_presentation_context > accepted_contexts_
Accepted presentation contexts.
std::optional< rejection_info > rejection_info_
Rejection information (if rejected)
@ idle
Sta1: No TCP connection, waiting for transport.

References kcenon::pacs::network::idle.

◆ ~association()

kcenon::pacs::network::association::~association ( )

Destructor (aborts if still established).

Definition at line 168 of file association.cpp.

168 {
169 std::lock_guard<std::mutex> lock(mutex_);
171 // Abort silently on destruction
173 }
174}
std::mutex mutex_
Thread safety mutex.
@ established
Sta6: Association established, ready for DIMSE.
@ aborted
Association aborted (error condition)

References kcenon::pacs::network::aborted, kcenon::pacs::network::established, mutex_, and state_.

◆ association() [3/3]

kcenon::pacs::network::association::association ( const association & )
delete

Member Function Documentation

◆ abort()

void kcenon::pacs::network::association::abort ( uint8_t source = 0,
uint8_t reason = 0 )

Abort the association immediately.

Parameters
sourceAbort source (0=service-user, 2=service-provider)
reasonAbort reason

Definition at line 582 of file association.cpp.

582 {
583 std::lock_guard<std::mutex> lock(mutex_);
584
586 abort_reason_ = reason;
588
589 // Wake up any waiters on the lock-free queue
590 incoming_queue_->notify_all();
591}
const atna_coded_value source
Source Role ID (110153)

References abort_reason_, abort_source_, kcenon::pacs::network::aborted, incoming_queue_, mutex_, and state_.

Referenced by kcenon::pacs::network::dicom_server::handle_association().

Here is the caller graph for this function:

◆ accept()

association kcenon::pacs::network::association::accept ( const associate_rq & rq,
const scp_config & config )
staticnodiscard

Accept an incoming SCP association.

Parameters
rqThe received A-ASSOCIATE-RQ PDU
configSCP configuration for negotiation
Returns
Configured association ready for DIMSE exchange

Definition at line 227 of file association.cpp.

229 {
230
231 association assoc;
232 assoc.is_scu_ = false;
233 assoc.calling_ae_ = rq.calling_ae_title;
234 assoc.called_ae_ = rq.called_ae_title;
235 assoc.our_ae_ = config.ae_title;
236 assoc.max_pdu_size_ = (std::min)(config.max_pdu_length, rq.user_info.max_pdu_length);
237 assoc.our_implementation_class_ = config.implementation_class_uid;
238 assoc.our_implementation_version_ = config.implementation_version_name;
239 assoc.remote_implementation_class_ = rq.user_info.implementation_class_uid;
240 assoc.remote_implementation_version_ = rq.user_info.implementation_version_name;
241
242 // Negotiate presentation contexts
243 assoc.negotiate_contexts(rq, config);
244
245 // Build lookup maps
246 assoc.build_context_map();
247
248 // Establish association if at least one context was accepted
249 // Note: For SCP, TCP connection is already established when accept() is called,
250 // so we directly set state to established (bypassing state machine)
251 bool has_accepted = std::any_of(
252 assoc.accepted_contexts_.begin(),
253 assoc.accepted_contexts_.end(),
254 [](const auto& ctx) { return ctx.is_accepted(); });
255
256 if (has_accepted) {
257 // SCP: A-ASSOCIATE-RQ received and accepted, go directly to established
258 assoc.state_ = association_state::established;
259 } else {
260 // No acceptable contexts - will need to reject
261 assoc.state_ = association_state::idle;
262 }
263
264 return assoc;
265}

References accepted_contexts_, kcenon::pacs::network::scp_config::ae_title, build_context_map(), called_ae_, kcenon::pacs::network::associate_rq::called_ae_title, calling_ae_, kcenon::pacs::network::associate_rq::calling_ae_title, kcenon::pacs::network::established, kcenon::pacs::network::idle, kcenon::pacs::network::scp_config::implementation_class_uid, kcenon::pacs::network::user_information::implementation_class_uid, kcenon::pacs::network::scp_config::implementation_version_name, kcenon::pacs::network::user_information::implementation_version_name, is_scu_, kcenon::pacs::network::scp_config::max_pdu_length, kcenon::pacs::network::user_information::max_pdu_length, max_pdu_size_, negotiate_contexts(), our_ae_, our_implementation_class_, our_implementation_version_, remote_implementation_class_, remote_implementation_version_, state_, and kcenon::pacs::network::associate_rq::user_info.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::handle_associate_rq(), and kcenon::pacs::network::dicom_server::simulate_association_request().

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

◆ accepted_context_id()

std::optional< uint8_t > kcenon::pacs::network::association::accepted_context_id ( std::string_view abstract_syntax) const
nodiscard

Get the presentation context ID for an abstract syntax.

Returns
Context ID if accepted, std::nullopt if not available

Definition at line 335 of file association.cpp.

336 {
337
338 std::lock_guard<std::mutex> lock(mutex_);
339 auto it = abstract_syntax_to_context_.find(std::string(abstract_syntax));
340 if (it != abstract_syntax_to_context_.end()) {
341 return it->second;
342 }
343 return std::nullopt;
344}
@ abstract_syntax
Abstract Syntax Sub-item.

References kcenon::pacs::network::abstract_syntax, abstract_syntax_to_context_, and mutex_.

Referenced by kcenon::pacs::services::query_scu::cancel(), kcenon::pacs::services::retrieve_scu::cancel(), kcenon::pacs::services::worklist_scu::cancel(), kcenon::pacs::services::ups_push_scu::change_state(), kcenon::pacs::services::mpps_scu::create(), kcenon::pacs::services::ups_push_scu::create(), kcenon::pacs::services::query_scu::find_impl(), kcenon::pacs::services::print_scu::find_print_context(), kcenon::pacs::services::query_scu::find_streaming(), kcenon::pacs::services::n_get_scu::get(), kcenon::pacs::services::ups_push_scu::get(), kcenon::pacs::services::retrieve_scp::handle_c_get(), kcenon::pacs::services::retrieve_scu::perform_get(), kcenon::pacs::services::retrieve_scu::perform_move(), kcenon::pacs::services::worklist_scu::query_impl(), kcenon::pacs::services::worklist_scu::query_streaming(), kcenon::pacs::services::ups_push_scu::request_cancel(), kcenon::pacs::services::ups_watch_scu::send_watch_action(), kcenon::pacs::services::mpps_scu::set(), kcenon::pacs::services::ups_push_scu::set(), kcenon::pacs::services::storage_scu::store_impl(), and TEST_CASE().

Here is the caller graph for this function:

◆ accepted_contexts()

const std::vector< accepted_presentation_context > & kcenon::pacs::network::association::accepted_contexts ( ) const
nodiscardnoexcept

Get all accepted presentation contexts.

Definition at line 360 of file association.cpp.

360 {
361 // Note: Not thread-safe for iteration, but const reference is OK
362 return accepted_contexts_;
363}

References accepted_contexts_.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::dispatch_to_service(), kcenon::pacs::network::v2::dicom_association_handler::handle_associate_rq(), and kcenon::pacs::network::v2::dicom_association_handler::handle_p_data_tf().

Here is the caller graph for this function:

◆ build_associate_ac()

associate_ac kcenon::pacs::network::association::build_associate_ac ( ) const
nodiscard

Build A-ASSOCIATE-AC PDU for sending.

Definition at line 457 of file association.cpp.

457 {
458 std::lock_guard<std::mutex> lock(mutex_);
459
460 associate_ac ac;
461 ac.calling_ae_title = calling_ae_;
462 ac.called_ae_title = called_ae_;
463 ac.application_context = DICOM_APPLICATION_CONTEXT;
464
465 // Convert accepted contexts
466 for (const auto& ctx : accepted_contexts_) {
467 ac.presentation_contexts.push_back(
468 presentation_context_ac{ctx.id, ctx.result, ctx.transfer_syntax});
469 }
470
471 // User information
472 ac.user_info.max_pdu_length = max_pdu_size_;
473 ac.user_info.implementation_class_uid = our_implementation_class_;
474 ac.user_info.implementation_version_name = our_implementation_version_;
475
476 return ac;
477}
@ associate_ac
A-ASSOCIATE-AC (Association Accept)
@ presentation_context_ac
Presentation Context Item (AC)
constexpr const char * DICOM_APPLICATION_CONTEXT
Default DICOM Application Context Name (PS3.7)
Definition pdu_types.h:267

References accepted_contexts_, kcenon::pacs::network::associate_ac::application_context, called_ae_, kcenon::pacs::network::associate_ac::called_ae_title, calling_ae_, kcenon::pacs::network::associate_ac::calling_ae_title, kcenon::pacs::network::DICOM_APPLICATION_CONTEXT, kcenon::pacs::network::user_information::implementation_class_uid, kcenon::pacs::network::user_information::implementation_version_name, kcenon::pacs::network::user_information::max_pdu_length, max_pdu_size_, mutex_, our_implementation_class_, our_implementation_version_, kcenon::pacs::network::associate_ac::presentation_contexts, and kcenon::pacs::network::associate_ac::user_info.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::send_associate_ac().

Here is the caller graph for this function:

◆ build_associate_rq()

associate_rq kcenon::pacs::network::association::build_associate_rq ( ) const
nodiscard

Build A-ASSOCIATE-RQ PDU for sending.

Definition at line 435 of file association.cpp.

435 {
436 std::lock_guard<std::mutex> lock(mutex_);
437
438 associate_rq rq;
439 rq.calling_ae_title = calling_ae_;
440 rq.called_ae_title = called_ae_;
441 rq.application_context = DICOM_APPLICATION_CONTEXT;
442
443 // Convert proposed contexts
444 for (const auto& pc : proposed_contexts_) {
445 rq.presentation_contexts.push_back(
446 presentation_context_rq{pc.id, pc.abstract_syntax, pc.transfer_syntaxes});
447 }
448
449 // User information
450 rq.user_info.max_pdu_length = max_pdu_size_;
451 rq.user_info.implementation_class_uid = our_implementation_class_;
452 rq.user_info.implementation_version_name = our_implementation_version_;
453
454 return rq;
455}
@ associate_rq
A-ASSOCIATE-RQ (Association Request)
@ presentation_context_rq
Presentation Context Item (RQ)

References kcenon::pacs::network::associate_rq::application_context, called_ae_, kcenon::pacs::network::associate_rq::called_ae_title, calling_ae_, kcenon::pacs::network::associate_rq::calling_ae_title, kcenon::pacs::network::DICOM_APPLICATION_CONTEXT, kcenon::pacs::network::user_information::implementation_class_uid, kcenon::pacs::network::user_information::implementation_version_name, kcenon::pacs::network::user_information::max_pdu_length, max_pdu_size_, mutex_, our_implementation_class_, our_implementation_version_, kcenon::pacs::network::associate_rq::presentation_contexts, proposed_contexts_, and kcenon::pacs::network::associate_rq::user_info.

Referenced by connect().

Here is the caller graph for this function:

◆ build_context_map()

void kcenon::pacs::network::association::build_context_map ( )
private

Build presentation context map from accepted contexts.

Definition at line 675 of file association.cpp.

675 {
678
679 for (const auto& ctx : accepted_contexts_) {
680 if (ctx.is_accepted()) {
681 abstract_syntax_to_context_[ctx.abstract_syntax] = ctx.id;
682
683 encoding::transfer_syntax ts{ctx.transfer_syntax};
684 if (ts.is_valid()) {
685 context_to_transfer_syntax_.emplace(ctx.id, std::move(ts));
686 }
687 }
688 }
689}
Transfer Syntax UIDs.
Definition main.cpp:78

References abstract_syntax_to_context_, accepted_contexts_, and context_to_transfer_syntax_.

Referenced by accept(), and process_associate_ac().

Here is the caller graph for this function:

◆ called_ae()

std::string_view kcenon::pacs::network::association::called_ae ( ) const
nodiscardnoexcept

Get called AE title.

Definition at line 305 of file association.cpp.

305 {
306 std::lock_guard<std::mutex> lock(mutex_);
307 return called_ae_;
308}

References called_ae_, and mutex_.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::called_ae(), kcenon::pacs::services::query_scp::handle_message(), kcenon::pacs::services::storage_scp::handle_message(), and kcenon::pacs::example::pacs_server_app::setup_server().

Here is the caller graph for this function:

◆ calling_ae()

◆ can_transition_to()

bool kcenon::pacs::network::association::can_transition_to ( association_state new_state) const
nodiscardprivate

Validate state transition.

Definition at line 630 of file association.cpp.

630 {
631 // Define valid state transitions per PS3.8
632 switch (state_) {
634 return new_state == association_state::awaiting_associate_ac ||
636
638 return new_state == association_state::established ||
639 new_state == association_state::idle ||
640 new_state == association_state::aborted;
641
643 return new_state == association_state::established ||
644 new_state == association_state::idle ||
645 new_state == association_state::aborted;
646
648 return new_state == association_state::awaiting_release_rp ||
650 new_state == association_state::aborted;
651
653 return new_state == association_state::released ||
654 new_state == association_state::aborted;
655
657 return new_state == association_state::released ||
658 new_state == association_state::aborted;
659
662 return false; // Terminal states
663
664 default:
665 return false;
666 }
667}
@ released
Association gracefully released.
@ awaiting_release_rp
Sta7: Awaiting A-RELEASE response (initiator)
@ awaiting_associate_ac
Sta5: Awaiting A-ASSOCIATE response (SCU)
@ awaiting_release_rq
Sta8: Awaiting potential A-RELEASE request.
@ awaiting_associate_rq
Sta2: Awaiting A-ASSOCIATE request (SCP)

References kcenon::pacs::network::aborted, kcenon::pacs::network::awaiting_associate_ac, kcenon::pacs::network::awaiting_associate_rq, kcenon::pacs::network::awaiting_release_rp, kcenon::pacs::network::awaiting_release_rq, kcenon::pacs::network::established, kcenon::pacs::network::idle, kcenon::pacs::network::released, and state_.

Referenced by transition().

Here is the caller graph for this function:

◆ connect()

Result< association > kcenon::pacs::network::association::connect ( const std::string & host,
uint16_t port,
const association_config & config,
duration timeout = default_timeout )
staticnodiscard

Initiate an SCU association to a remote SCP.

Parameters
hostRemote host address
portRemote port (typically 104 or 11112)
configAssociation configuration
timeoutConnection timeout
Returns
Result containing established association or error

Definition at line 180 of file association.cpp.

184 {
185
186 association assoc;
187 assoc.is_scu_ = true;
188 assoc.calling_ae_ = config.calling_ae_title;
189 assoc.called_ae_ = config.called_ae_title;
190 assoc.our_ae_ = config.calling_ae_title;
191 assoc.max_pdu_size_ = config.max_pdu_length;
192 assoc.our_implementation_class_ = config.implementation_class_uid;
193 assoc.our_implementation_version_ = config.implementation_version_name;
194
195 // Copy proposed contexts
196 for (const auto& pc : config.proposed_contexts) {
197 assoc.proposed_contexts_.push_back(pc);
198 }
199
200 // Transition to awaiting state
202
203 // Check for in-memory server (Test Hook)
204 auto* server = dicom_server::get_server_on_port(port);
205 if (server) {
206 auto rq = assoc.build_associate_rq();
207 auto result = server->simulate_association_request(rq, &assoc);
208 if (result.is_ok()) {
209 if (assoc.process_associate_ac(result.value())) {
210 return assoc;
211 } else {
212 return error_info{no_acceptable_context, "Association negotiation failed", "network"};
213 }
214 } else {
215 return result.error();
216 }
217 }
218
219 // Note: Actual network connection should be handled by network_system
220 // This implementation prepares the association for PDU exchange
221 (void)host; // Will be used with network_system integration
222 (void)port;
223
224 return assoc;
225}
static dicom_server * get_server_on_port(uint16_t port)
Get server instance listening on port (for in-memory testing).
kcenon::common::error_info error_info
Error information type.
Definition result.h:40

References kcenon::pacs::network::awaiting_associate_ac, build_associate_rq(), called_ae_, kcenon::pacs::network::association_config::called_ae_title, calling_ae_, kcenon::pacs::network::association_config::calling_ae_title, kcenon::pacs::network::dicom_server::get_server_on_port(), kcenon::pacs::network::association_config::implementation_class_uid, kcenon::pacs::network::association_config::implementation_version_name, is_scu_, kcenon::pacs::network::association_config::max_pdu_length, max_pdu_size_, kcenon::pacs::network::no_acceptable_context, our_ae_, our_implementation_class_, our_implementation_version_, process_associate_ac(), kcenon::pacs::network::association_config::proposed_contexts, proposed_contexts_, and transition().

Referenced by kcenon::pacs::client::remote_node_manager::acquire_association(), kcenon::pacs::workflow::auto_prefetch_service::prefetch_study(), kcenon::pacs::workflow::auto_prefetch_service::query_prior_studies(), and kcenon::pacs::web::endpoints::register_remote_nodes_endpoints_impl().

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

◆ context_transfer_syntax()

Result< encoding::transfer_syntax > kcenon::pacs::network::association::context_transfer_syntax ( uint8_t pc_id) const
nodiscard

Get the transfer syntax for an accepted context.

Parameters
pc_idPresentation Context ID
Returns
Result containing the transfer syntax or error if context not found

Definition at line 346 of file association.cpp.

347 {
348
349 std::lock_guard<std::mutex> lock(mutex_);
350 auto it = context_to_transfer_syntax_.find(pc_id);
351 if (it == context_to_transfer_syntax_.end()) {
353 "Presentation context ID not found: " + std::to_string(pc_id),
354 "network"};
355 }
356 return it->second;
357}

References context_to_transfer_syntax_, mutex_, and kcenon::pacs::network::no_acceptable_context.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::handle_p_data_tf().

Here is the caller graph for this function:

◆ enqueue_message()

void kcenon::pacs::network::association::enqueue_message ( uint8_t context_id,
dimse::dimse_message msg )

Enqueue message from peer (for in-memory testing).

Definition at line 614 of file association.cpp.

614 {
615 // Lock-free enqueue with automatic notification
616 incoming_queue_->enqueue({context_id, std::move(msg)});
617}

References incoming_queue_.

Referenced by send_dimse().

Here is the caller graph for this function:

◆ get_rejection_info()

std::optional< rejection_info > kcenon::pacs::network::association::get_rejection_info ( ) const
nodiscard

Get rejection info if association was rejected.

Definition at line 534 of file association.cpp.

534 {
535 std::lock_guard<std::mutex> lock(mutex_);
536 return rejection_info_;
537}

References mutex_, and rejection_info_.

◆ has_accepted_context()

bool kcenon::pacs::network::association::has_accepted_context ( std::string_view abstract_syntax) const
nodiscard

Check if a presentation context for the abstract syntax was accepted.

Definition at line 329 of file association.cpp.

329 {
330 std::lock_guard<std::mutex> lock(mutex_);
331 return abstract_syntax_to_context_.find(std::string(abstract_syntax)) !=
333}

References kcenon::pacs::network::abstract_syntax, abstract_syntax_to_context_, and mutex_.

◆ is_closed()

bool kcenon::pacs::network::association::is_closed ( ) const
nodiscardnoexcept

Check if association has been released or aborted.

Definition at line 289 of file association.cpp.

289 {
290 std::lock_guard<std::mutex> lock(mutex_);
294}

References kcenon::pacs::network::aborted, kcenon::pacs::network::idle, mutex_, kcenon::pacs::network::released, and state_.

◆ is_established()

bool kcenon::pacs::network::association::is_established ( ) const
nodiscardnoexcept

Check if association is established and ready for DIMSE.

Definition at line 284 of file association.cpp.

284 {
285 std::lock_guard<std::mutex> lock(mutex_);
287}

References kcenon::pacs::network::established, mutex_, and state_.

Referenced by kcenon::pacs::services::ups_push_scu::change_state(), kcenon::pacs::services::mpps_scu::create(), kcenon::pacs::services::ups_push_scu::create(), kcenon::pacs::services::print_scu::create_film_box(), kcenon::pacs::services::print_scu::create_film_session(), kcenon::pacs::services::print_scu::delete_film_box(), kcenon::pacs::services::print_scu::delete_film_session(), kcenon::pacs::services::query_scu::find_impl(), kcenon::pacs::services::query_scu::find_streaming(), kcenon::pacs::services::n_get_scu::get(), kcenon::pacs::services::ups_push_scu::get(), kcenon::pacs::services::retrieve_scu::perform_get(), kcenon::pacs::services::retrieve_scu::perform_move(), kcenon::pacs::services::print_scu::print_film_box(), kcenon::pacs::services::worklist_scu::query_impl(), kcenon::pacs::services::print_scu::query_printer_status(), kcenon::pacs::services::worklist_scu::query_streaming(), kcenon::pacs::services::ups_push_scu::request_cancel(), kcenon::pacs::services::ups_watch_scu::send_watch_action(), kcenon::pacs::services::mpps_scu::set(), kcenon::pacs::services::ups_push_scu::set(), kcenon::pacs::services::print_scu::set_image_box(), and kcenon::pacs::services::storage_scu::store_impl().

Here is the caller graph for this function:

◆ max_pdu_size()

uint32_t kcenon::pacs::network::association::max_pdu_size ( ) const
nodiscardnoexcept

Get negotiated maximum PDU size.

Definition at line 310 of file association.cpp.

310 {
311 std::lock_guard<std::mutex> lock(mutex_);
312 return max_pdu_size_;
313}

References max_pdu_size_, and mutex_.

◆ negotiate_contexts()

void kcenon::pacs::network::association::negotiate_contexts ( const associate_rq & rq,
const scp_config & config )
private

Negotiate presentation contexts for SCP.

Definition at line 691 of file association.cpp.

693 {
694
695 accepted_contexts_.clear();
696
697 for (const auto& pc_rq : rq.presentation_contexts) {
698 // Check if we support the abstract syntax
699 bool supports_abstract = config.supported_abstract_syntaxes.empty() ||
700 std::find(config.supported_abstract_syntaxes.begin(),
701 config.supported_abstract_syntaxes.end(),
702 pc_rq.abstract_syntax) != config.supported_abstract_syntaxes.end();
703
704 if (!supports_abstract) {
705 accepted_contexts_.push_back(accepted_presentation_context{
706 pc_rq.id,
707 pc_rq.abstract_syntax,
708 "",
710 });
711 continue;
712 }
713
714 // Find first supported transfer syntax
715 std::string accepted_ts;
716 for (const auto& proposed_ts : pc_rq.transfer_syntaxes) {
717 bool supports_ts = config.supported_transfer_syntaxes.empty() ||
718 std::find(config.supported_transfer_syntaxes.begin(),
719 config.supported_transfer_syntaxes.end(),
720 proposed_ts) != config.supported_transfer_syntaxes.end();
721
722 if (supports_ts) {
723 accepted_ts = proposed_ts;
724 break;
725 }
726 }
727
728 if (accepted_ts.empty()) {
729 accepted_contexts_.push_back(accepted_presentation_context{
730 pc_rq.id,
731 pc_rq.abstract_syntax,
732 "",
734 });
735 } else {
736 accepted_contexts_.push_back(accepted_presentation_context{
737 pc_rq.id,
738 pc_rq.abstract_syntax,
739 accepted_ts,
741 });
742 }
743 }
744}
@ abstract_syntax_not_supported
Abstract-syntax-not-supported.
@ transfer_syntaxes_not_supported
Transfer-syntaxes-not-supported.

References kcenon::pacs::network::abstract_syntax_not_supported, kcenon::pacs::network::acceptance, accepted_contexts_, kcenon::pacs::network::associate_rq::presentation_contexts, kcenon::pacs::network::scp_config::supported_abstract_syntaxes, kcenon::pacs::network::scp_config::supported_transfer_syntaxes, and kcenon::pacs::network::transfer_syntaxes_not_supported.

Referenced by accept().

Here is the caller graph for this function:

◆ operator=() [1/2]

association & kcenon::pacs::network::association::operator= ( association && other)
noexcept

Move assignment operator.

Definition at line 134 of file association.cpp.

134 {
135 if (this != &other) {
136 std::scoped_lock lock(mutex_, other.mutex_);
137 state_ = other.state_;
138 calling_ae_ = std::move(other.calling_ae_);
139 called_ae_ = std::move(other.called_ae_);
140 our_ae_ = std::move(other.our_ae_);
141 max_pdu_size_ = other.max_pdu_size_;
142 our_implementation_class_ = std::move(other.our_implementation_class_);
143 our_implementation_version_ = std::move(other.our_implementation_version_);
144 remote_implementation_class_ = std::move(other.remote_implementation_class_);
145 remote_implementation_version_ = std::move(other.remote_implementation_version_);
146 proposed_contexts_ = std::move(other.proposed_contexts_);
147 accepted_contexts_ = std::move(other.accepted_contexts_);
148 abstract_syntax_to_context_ = std::move(other.abstract_syntax_to_context_);
149 context_to_transfer_syntax_ = std::move(other.context_to_transfer_syntax_);
150 rejection_info_ = std::move(other.rejection_info_);
151 abort_source_ = other.abort_source_;
152 abort_reason_ = other.abort_reason_;
153 is_scu_ = other.is_scu_;
154 peer_ = other.peer_;
155 incoming_queue_ = std::move(other.incoming_queue_);
156 other.incoming_queue_ = std::make_unique<message_queue_type>();
157
159 other.peer_ = nullptr;
160
161 if (peer_) {
162 peer_->update_peer(&other, this);
163 }
164 }
165 return *this;
166}

References kcenon::pacs::network::idle.

◆ operator=() [2/2]

association & kcenon::pacs::network::association::operator= ( const association & )
delete

◆ process_abort()

void kcenon::pacs::network::association::process_abort ( const abort_source & source,
const abort_reason & reason )

Process received A-ABORT PDU.

Definition at line 593 of file association.cpp.

593 {
594 std::lock_guard<std::mutex> lock(mutex_);
595
596 abort_source_ = static_cast<uint8_t>(source);
597 abort_reason_ = static_cast<uint8_t>(reason);
599
600 // Wake up any waiters on the lock-free queue
601 incoming_queue_->notify_all();
602}

References abort_reason_, abort_source_, kcenon::pacs::network::aborted, incoming_queue_, mutex_, and state_.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::handle_abort().

Here is the caller graph for this function:

◆ process_associate_ac()

bool kcenon::pacs::network::association::process_associate_ac ( const associate_ac & ac)

Process received A-ASSOCIATE-AC PDU.

Returns
true if association established successfully

Definition at line 479 of file association.cpp.

479 {
480 std::lock_guard<std::mutex> lock(mutex_);
481
483 return false;
484 }
485
486 // Store remote implementation info
487 remote_implementation_class_ = ac.user_info.implementation_class_uid;
488 remote_implementation_version_ = ac.user_info.implementation_version_name;
489
490 // Update max PDU size to negotiated value
491 max_pdu_size_ = (std::min)(max_pdu_size_, ac.user_info.max_pdu_length);
492
493 // Process accepted presentation contexts
494 accepted_contexts_.clear();
495 for (const auto& pc_ac : ac.presentation_contexts) {
496 // Find the corresponding proposed context
497 for (const auto& pc_rq : proposed_contexts_) {
498 if (pc_rq.id == pc_ac.id) {
499 accepted_contexts_.push_back(accepted_presentation_context{
500 pc_ac.id,
501 pc_rq.abstract_syntax,
502 pc_ac.transfer_syntax,
503 pc_ac.result
504 });
505 break;
506 }
507 }
508 }
509
510 // Build lookup maps
512
513 // Check if at least one context was accepted
514 bool has_accepted = std::any_of(
515 accepted_contexts_.begin(),
516 accepted_contexts_.end(),
517 [](const auto& ctx) { return ctx.is_accepted(); });
518
519 if (has_accepted) {
521 return true;
522 }
523
524 return false;
525}
void build_context_map()
Build presentation context map from accepted contexts.
void transition(association_state new_state)
Perform state transition.

References accepted_contexts_, kcenon::pacs::network::awaiting_associate_ac, build_context_map(), kcenon::pacs::network::established, kcenon::pacs::network::user_information::implementation_class_uid, kcenon::pacs::network::user_information::implementation_version_name, kcenon::pacs::network::user_information::max_pdu_length, max_pdu_size_, mutex_, kcenon::pacs::network::associate_ac::presentation_contexts, proposed_contexts_, remote_implementation_class_, remote_implementation_version_, state_, transition(), and kcenon::pacs::network::associate_ac::user_info.

Referenced by connect().

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

◆ process_associate_rj()

void kcenon::pacs::network::association::process_associate_rj ( const associate_rj & rj)

Process received A-ASSOCIATE-RJ PDU.

Definition at line 527 of file association.cpp.

527 {
528 std::lock_guard<std::mutex> lock(mutex_);
529
530 rejection_info_ = rejection_info{rj.result, rj.source, rj.reason};
532}

References kcenon::pacs::network::idle, mutex_, kcenon::pacs::network::associate_rj::reason, rejection_info_, kcenon::pacs::network::associate_rj::result, kcenon::pacs::network::associate_rj::source, and state_.

◆ process_release_rp()

void kcenon::pacs::network::association::process_release_rp ( )

Process received A-RELEASE-RP.

Definition at line 574 of file association.cpp.

574 {
575 std::lock_guard<std::mutex> lock(mutex_);
576
579 }
580}

References kcenon::pacs::network::awaiting_release_rp, mutex_, kcenon::pacs::network::released, and state_.

◆ process_release_rq()

void kcenon::pacs::network::association::process_release_rq ( )

Process received A-RELEASE-RQ.

Returns
A-RELEASE-RP PDU to send

Definition at line 566 of file association.cpp.

566 {
567 std::lock_guard<std::mutex> lock(mutex_);
568
571 }
572}

References kcenon::pacs::network::awaiting_release_rq, kcenon::pacs::network::established, mutex_, and state_.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::handle_release_rq().

Here is the caller graph for this function:

◆ receive_dimse()

Result< std::pair< uint8_t, dimse::dimse_message > > kcenon::pacs::network::association::receive_dimse ( duration timeout = default_timeout)
nodiscard

Receive a DIMSE message.

Parameters
timeoutReceive timeout
Returns
Received message with context ID, or error

Definition at line 402 of file association.cpp.

403 {
404
405 {
406 std::lock_guard<std::mutex> lock(mutex_);
408 return error_info{invalid_association_state, "Cannot receive DIMSE: association not established", "network"};
409 }
410 }
411
412 // Placeholder - actual receive would be implemented with network_system
413 // return error_info("Not implemented: requires network_system integration");
414
415 // Use lock-free queue with blocking wait
416 if (auto item = incoming_queue_->wait_dequeue(timeout)) {
417 return std::move(*item);
418 }
419
420 // Check if we were aborted/released while waiting
421 {
422 std::lock_guard<std::mutex> lock(mutex_);
424 return error_info{association_aborted, "Association aborted or released", "network"};
425 }
426 }
427
428 return error_info{receive_timeout, "Receive timeout", "network"};
429}

References kcenon::pacs::network::association_aborted, kcenon::pacs::network::established, incoming_queue_, mutex_, kcenon::pacs::network::receive_timeout, and state_.

Referenced by kcenon::pacs::services::ups_push_scu::change_state(), kcenon::pacs::services::mpps_scu::create(), kcenon::pacs::services::ups_push_scu::create(), kcenon::pacs::services::print_scu::create_film_box(), kcenon::pacs::services::print_scu::create_film_session(), kcenon::pacs::services::print_scu::delete_film_box(), kcenon::pacs::services::print_scu::delete_film_session(), kcenon::pacs::services::query_scu::find_impl(), kcenon::pacs::services::query_scu::find_streaming(), kcenon::pacs::services::n_get_scu::get(), kcenon::pacs::services::ups_push_scu::get(), kcenon::pacs::services::retrieve_scp::handle_c_get(), kcenon::pacs::services::retrieve_scu::perform_get(), kcenon::pacs::services::retrieve_scu::perform_move(), kcenon::pacs::services::print_scu::print_film_box(), kcenon::pacs::services::worklist_scu::query_impl(), kcenon::pacs::services::print_scu::query_printer_status(), kcenon::pacs::services::worklist_scu::query_streaming(), kcenon::pacs::services::ups_push_scu::request_cancel(), kcenon::pacs::services::ups_watch_scu::send_watch_action(), kcenon::pacs::services::mpps_scu::set(), kcenon::pacs::services::ups_push_scu::set(), kcenon::pacs::services::print_scu::set_image_box(), kcenon::pacs::services::storage_scu::store_impl(), and TEST_CASE().

Here is the caller graph for this function:

◆ reject()

associate_rj kcenon::pacs::network::association::reject ( reject_result result,
uint8_t source,
uint8_t reason )
staticnodiscard

Reject an incoming association request.

Parameters
resultRejection result (permanent/transient)
sourceRejection source
reasonRejection reason
Returns
Rejection PDU to send

Definition at line 267 of file association.cpp.

270 {
271
272 return associate_rj{result, source, reason};
273}
@ associate_rj
A-ASSOCIATE-RJ (Association Reject)

◆ release()

Result< std::monostate > kcenon::pacs::network::association::release ( duration timeout = default_timeout)
nodiscard

Gracefully release the association.

Sends A-RELEASE-RQ and waits for A-RELEASE-RP.

Parameters
timeoutTimeout for release handshake
Returns
Success or error

Definition at line 543 of file association.cpp.

543 {
544 std::lock_guard<std::mutex> lock(mutex_);
545
547 return error_info{already_released, "Association already released", "network"};
548 }
549
551 return error_info{invalid_association_state, "Cannot release: association not established", "network"};
552 }
553
554 // Transition to awaiting release response
556
557 // Note: Actual A-RELEASE-RQ sending and A-RELEASE-RP receiving
558 // would be handled by network_system integration
559
560 // For now, simulate successful release
562
563 return std::monostate{};
564}

References kcenon::pacs::network::already_released, kcenon::pacs::network::awaiting_release_rp, kcenon::pacs::network::established, mutex_, kcenon::pacs::network::released, and state_.

Referenced by TEST_CASE().

Here is the caller graph for this function:

◆ remote_implementation_class()

std::string_view kcenon::pacs::network::association::remote_implementation_class ( ) const
nodiscardnoexcept

Get remote implementation class UID.

Definition at line 315 of file association.cpp.

315 {
316 std::lock_guard<std::mutex> lock(mutex_);
318}

References mutex_, and remote_implementation_class_.

◆ remote_implementation_version()

std::string_view kcenon::pacs::network::association::remote_implementation_version ( ) const
nodiscardnoexcept

Get remote implementation version name.

Definition at line 320 of file association.cpp.

320 {
321 std::lock_guard<std::mutex> lock(mutex_);
323}

References mutex_, and remote_implementation_version_.

◆ send_dimse()

Result< std::monostate > kcenon::pacs::network::association::send_dimse ( uint8_t context_id,
const dimse::dimse_message & msg )
nodiscard

Send a DIMSE message.

Parameters
context_idPresentation Context ID to use
msgThe DIMSE message to send
Returns
Success or error

Definition at line 369 of file association.cpp.

371 {
372
373 std::lock_guard<std::mutex> lock(mutex_);
374
376 return error_info{invalid_association_state, "Cannot send DIMSE: association not established", "network"};
377 }
378
379 // Verify context exists
380 if (context_to_transfer_syntax_.find(context_id) ==
382 return error_info{kcenon::pacs::error_codes::dimse_error, "Invalid presentation context ID", "network"};
383 }
384
385 // Message encoding and network send would be handled by network_system
386 // For now, validate the message
387 if (!msg.is_valid()) {
388 return error_info{kcenon::pacs::error_codes::dimse_error, "Invalid DIMSE message", "network"};
389 }
390
391 // Placeholder for actual send implementation
392 (void)msg;
393
394 if (peer_) {
395 peer_->enqueue_message(context_id, msg);
396 return std::monostate{};
397 }
398
399 return std::monostate{};
400}
void enqueue_message(uint8_t context_id, dimse::dimse_message msg)
Enqueue message from peer (for in-memory testing).
constexpr int dimse_error
Definition result.h:90

References context_to_transfer_syntax_, kcenon::pacs::error_codes::dimse_error, enqueue_message(), kcenon::pacs::network::established, kcenon::pacs::network::dimse::dimse_message::is_valid(), mutex_, peer_, and state_.

Referenced by kcenon::pacs::services::query_scu::cancel(), kcenon::pacs::services::retrieve_scu::cancel(), kcenon::pacs::services::worklist_scu::cancel(), kcenon::pacs::services::ups_push_scu::change_state(), kcenon::pacs::services::mpps_scu::create(), kcenon::pacs::services::ups_push_scu::create(), kcenon::pacs::services::print_scp::create_film_box(), kcenon::pacs::services::print_scu::create_film_box(), kcenon::pacs::services::print_scu::create_film_session(), kcenon::pacs::services::print_scu::delete_film_box(), kcenon::pacs::services::print_scu::delete_film_session(), kcenon::pacs::services::query_scu::find_impl(), kcenon::pacs::services::query_scu::find_streaming(), kcenon::pacs::services::n_get_scu::get(), kcenon::pacs::services::ups_push_scu::get(), kcenon::pacs::services::retrieve_scp::handle_c_get(), kcenon::pacs::services::retrieve_scp::handle_c_move(), kcenon::pacs::services::storage_scp::handle_message(), kcenon::pacs::services::verification_scp::handle_message(), kcenon::pacs::services::print_scp::handle_n_action(), kcenon::pacs::services::print_scp::handle_n_get(), kcenon::pacs::services::retrieve_scu::perform_get(), kcenon::pacs::services::retrieve_scu::perform_move(), kcenon::pacs::services::print_scu::print_film_box(), kcenon::pacs::services::worklist_scu::query_impl(), kcenon::pacs::services::print_scu::query_printer_status(), kcenon::pacs::services::worklist_scu::query_streaming(), kcenon::pacs::services::ups_push_scu::request_cancel(), kcenon::pacs::services::storage_commitment_scu::request_commitment(), kcenon::pacs::services::storage_commitment_scp::send_event_report(), kcenon::pacs::services::query_scp::send_final_response(), kcenon::pacs::services::retrieve_scp::send_final_response(), kcenon::pacs::services::ups_query_scp::send_final_response(), kcenon::pacs::services::worklist_scp::send_final_response(), kcenon::pacs::services::storage_commitment_scp::send_n_action_response(), kcenon::pacs::services::ups_push_scp::send_n_action_response(), kcenon::pacs::services::ups_watch_scp::send_n_action_response(), kcenon::pacs::services::mpps_scp::send_n_create_response(), kcenon::pacs::services::ups_push_scp::send_n_create_response(), kcenon::pacs::services::n_get_scp::send_n_get_response(), kcenon::pacs::services::ups_push_scp::send_n_get_response(), kcenon::pacs::services::mpps_scp::send_n_set_response(), kcenon::pacs::services::ups_push_scp::send_n_set_response(), kcenon::pacs::services::query_scp::send_pending_response(), kcenon::pacs::services::retrieve_scp::send_pending_response(), kcenon::pacs::services::ups_query_scp::send_pending_response(), kcenon::pacs::services::worklist_scp::send_pending_response(), kcenon::pacs::services::print_scp::send_response(), kcenon::pacs::services::ups_watch_scu::send_watch_action(), kcenon::pacs::services::mpps_scu::set(), kcenon::pacs::services::ups_push_scu::set(), kcenon::pacs::services::print_scu::set_image_box(), kcenon::pacs::services::storage_scu::store_impl(), and TEST_CASE().

Here is the call graph for this function:

◆ set_peer()

void kcenon::pacs::network::association::set_peer ( association * peer)

Set peer association for in-memory testing.

Definition at line 609 of file association.cpp.

609 {
610 std::lock_guard<std::mutex> lock(mutex_);
611 peer_ = peer;
612}

References mutex_, and peer_.

Referenced by kcenon::pacs::network::dicom_server::simulate_association_request().

Here is the caller graph for this function:

◆ set_state()

void kcenon::pacs::network::association::set_state ( association_state new_state)

Force state transition (for testing).

Warning
Internal use only

Definition at line 604 of file association.cpp.

604 {
605 std::lock_guard<std::mutex> lock(mutex_);
606 state_ = new_state;
607}

References mutex_, and state_.

Referenced by kcenon::pacs::network::v2::dicom_association_handler::handle_associate_rq().

Here is the caller graph for this function:

◆ state()

association_state kcenon::pacs::network::association::state ( ) const
nodiscardnoexcept

Get current association state.

Definition at line 279 of file association.cpp.

279 {
280 std::lock_guard<std::mutex> lock(mutex_);
281 return state_;
282}

References mutex_, and state_.

◆ transition()

void kcenon::pacs::network::association::transition ( association_state new_state)
private

Perform state transition.

Definition at line 669 of file association.cpp.

669 {
670 if (can_transition_to(new_state)) {
671 state_ = new_state;
672 }
673}
bool can_transition_to(association_state new_state) const
Validate state transition.

References can_transition_to(), and state_.

Referenced by connect(), and process_associate_ac().

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

◆ update_peer()

void kcenon::pacs::network::association::update_peer ( association * old_peer,
association * new_peer )

Update peer pointer (for in-memory testing).

Definition at line 619 of file association.cpp.

619 {
620 std::lock_guard<std::mutex> lock(mutex_);
621 if (peer_ == old_peer) {
622 peer_ = new_peer;
623 }
624}

References mutex_, and peer_.

Member Data Documentation

◆ abort_reason_

uint8_t kcenon::pacs::network::association::abort_reason_ {0}
private

Abort reason (if aborted)

Definition at line 625 of file association.h.

625{0};

Referenced by abort(), and process_abort().

◆ abort_source_

uint8_t kcenon::pacs::network::association::abort_source_ {0}
private

Abort source (if aborted)

Definition at line 622 of file association.h.

622{0};

Referenced by abort(), and process_abort().

◆ abstract_syntax_to_context_

std::map<std::string, uint8_t> kcenon::pacs::network::association::abstract_syntax_to_context_
private

Map from abstract syntax to accepted context ID.

Definition at line 613 of file association.h.

Referenced by accepted_context_id(), build_context_map(), and has_accepted_context().

◆ accepted_contexts_

std::vector<accepted_presentation_context> kcenon::pacs::network::association::accepted_contexts_
private

Accepted presentation contexts.

Definition at line 610 of file association.h.

Referenced by accept(), accepted_contexts(), build_associate_ac(), build_context_map(), negotiate_contexts(), and process_associate_ac().

◆ called_ae_

std::string kcenon::pacs::network::association::called_ae_
private

Called AE Title.

Definition at line 586 of file association.h.

Referenced by accept(), build_associate_ac(), build_associate_rq(), called_ae(), and connect().

◆ calling_ae_

std::string kcenon::pacs::network::association::calling_ae_
private

Calling AE Title.

Definition at line 583 of file association.h.

Referenced by accept(), build_associate_ac(), build_associate_rq(), calling_ae(), and connect().

◆ context_to_transfer_syntax_

std::map<uint8_t, encoding::transfer_syntax> kcenon::pacs::network::association::context_to_transfer_syntax_
private

Map from context ID to transfer syntax.

Definition at line 616 of file association.h.

Referenced by build_context_map(), context_transfer_syntax(), and send_dimse().

◆ default_timeout

duration kcenon::pacs::network::association::default_timeout {30000}
staticconstexpr

Default timeout for operations.

Definition at line 290 of file association.h.

290{30000}; // 30 seconds

◆ incoming_queue_

std::unique_ptr<message_queue_type> kcenon::pacs::network::association::incoming_queue_
mutableprivate
Initial value:
{
std::make_unique<message_queue_type>()}

Definition at line 639 of file association.h.

639 {
640 std::make_unique<message_queue_type>()};

Referenced by abort(), enqueue_message(), process_abort(), and receive_dimse().

◆ is_scu_

bool kcenon::pacs::network::association::is_scu_ {true}
private

Is this an SCU (true) or SCP (false)?

Definition at line 631 of file association.h.

631{true};

Referenced by accept(), and connect().

◆ max_pdu_size_

uint32_t kcenon::pacs::network::association::max_pdu_size_ {DEFAULT_MAX_PDU_LENGTH}
private

Negotiated maximum PDU size.

Definition at line 592 of file association.h.

constexpr uint32_t DEFAULT_MAX_PDU_LENGTH
Maximum PDU length recommended by DICOM (16384 bytes)
Definition pdu_types.h:276

Referenced by accept(), build_associate_ac(), build_associate_rq(), connect(), max_pdu_size(), and process_associate_ac().

◆ mutex_

◆ our_ae_

std::string kcenon::pacs::network::association::our_ae_
private

Our AE Title (may be calling or called depending on role)

Definition at line 589 of file association.h.

Referenced by accept(), and connect().

◆ our_implementation_class_

std::string kcenon::pacs::network::association::our_implementation_class_
private

Our implementation class UID.

Definition at line 595 of file association.h.

Referenced by accept(), build_associate_ac(), build_associate_rq(), and connect().

◆ our_implementation_version_

std::string kcenon::pacs::network::association::our_implementation_version_
private

Our implementation version name.

Definition at line 598 of file association.h.

Referenced by accept(), build_associate_ac(), build_associate_rq(), and connect().

◆ peer_

association* kcenon::pacs::network::association::peer_ {nullptr}
private

Peer association for in-memory testing.

Definition at line 634 of file association.h.

634{nullptr};

Referenced by send_dimse(), set_peer(), and update_peer().

◆ proposed_contexts_

std::vector<proposed_presentation_context> kcenon::pacs::network::association::proposed_contexts_
private

Proposed presentation contexts (SCU)

Definition at line 607 of file association.h.

Referenced by build_associate_rq(), connect(), and process_associate_ac().

◆ rejection_info_

std::optional<rejection_info> kcenon::pacs::network::association::rejection_info_
private

Rejection information (if rejected)

Definition at line 619 of file association.h.

Referenced by get_rejection_info(), and process_associate_rj().

◆ remote_implementation_class_

std::string kcenon::pacs::network::association::remote_implementation_class_
private

Remote implementation class UID.

Definition at line 601 of file association.h.

Referenced by accept(), process_associate_ac(), and remote_implementation_class().

◆ remote_implementation_version_

std::string kcenon::pacs::network::association::remote_implementation_version_
private

Remote implementation version name.

Definition at line 604 of file association.h.

Referenced by accept(), process_associate_ac(), and remote_implementation_version().

◆ state_


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