PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dicom_association_handler.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
19#ifndef PACS_NETWORK_V2_DICOM_ASSOCIATION_HANDLER_HPP
20#define PACS_NETWORK_V2_DICOM_ASSOCIATION_HANDLER_HPP
21
28
29#include <atomic>
30#include <chrono>
31#include <functional>
32#include <map>
33#include <memory>
34#include <mutex>
35#include <optional>
36#include <string>
37#include <vector>
38
39// Note: pdu_data and Result types are provided by
40// pacs/integration/dicom_session.hpp which is included above
41
42// Forward declaration for public network_system interface
44class i_session;
45} // namespace kcenon::network::interfaces
46
48
49// =============================================================================
50// Handler State
51// =============================================================================
52
59enum class handler_state {
60 idle,
63 releasing,
64 closed
65};
66
72[[nodiscard]] constexpr const char* to_string(handler_state state) noexcept {
73 switch (state) {
74 case handler_state::idle: return "Idle";
75 case handler_state::awaiting_response: return "Awaiting Response";
76 case handler_state::established: return "Established";
77 case handler_state::releasing: return "Releasing";
78 case handler_state::closed: return "Closed";
79 default: return "Unknown";
80 }
81}
82
83// =============================================================================
84// Handler Callbacks
85// =============================================================================
86
91 std::function<void(const std::string& session_id,
92 const std::string& calling_ae,
93 const std::string& called_ae)>;
94
99 std::function<void(const std::string& session_id, bool graceful)>;
100
105 std::function<void(const std::string& session_id, const std::string& error)>;
106
107// =============================================================================
108// DICOM Association Handler Class
109// =============================================================================
110
149 : public std::enable_shared_from_this<dicom_association_handler> {
150public:
151 // =========================================================================
152 // Type Aliases
153 // =========================================================================
154
155 using session_ptr = std::shared_ptr<kcenon::network::interfaces::i_session>;
156 using service_map = std::map<std::string, services::scp_service*>;
157 using clock = std::chrono::steady_clock;
158 using time_point = clock::time_point;
159 using duration = std::chrono::milliseconds;
160
162 static constexpr size_t pdu_header_size = 6;
163
165 static constexpr size_t max_pdu_size = 64 * 1024 * 1024; // 64 MB
166
167 // =========================================================================
168 // Construction / Destruction
169 // =========================================================================
170
179 session_ptr session,
180 const server_config& config,
181 const service_map& services);
182
187
188 // Non-copyable, non-movable
193
194 // =========================================================================
195 // Lifecycle Management
196 // =========================================================================
197
204 void start();
205
214 void stop(bool graceful = false);
215
216 // =========================================================================
217 // Server-Level Event Forwarding
218 // =========================================================================
219
230 void feed_data(const std::vector<uint8_t>& data);
231
237 void handle_disconnect();
238
246 void handle_error(std::error_code ec);
247
248 // =========================================================================
249 // State Queries
250 // =========================================================================
251
256 [[nodiscard]] handler_state state() const noexcept;
257
262 [[nodiscard]] bool is_established() const noexcept;
263
268 [[nodiscard]] bool is_closed() const noexcept;
269
274 [[nodiscard]] std::string session_id() const;
275
280 [[nodiscard]] std::string calling_ae() const;
281
286 [[nodiscard]] std::string called_ae() const;
287
292 [[nodiscard]] Result<std::reference_wrapper<association>> get_association();
293 [[nodiscard]] Result<std::reference_wrapper<const association>> get_association() const;
294
299 [[nodiscard]] time_point last_activity() const noexcept;
300
301 // =========================================================================
302 // Callbacks
303 // =========================================================================
304
310
316
322
323 // =========================================================================
324 // Statistics
325 // =========================================================================
326
331 [[nodiscard]] uint64_t pdus_received() const noexcept;
332
337 [[nodiscard]] uint64_t pdus_sent() const noexcept;
338
343 [[nodiscard]] uint64_t messages_processed() const noexcept;
344
345 // =========================================================================
346 // Security / Access Control
347 // =========================================================================
348
353 void set_access_control(std::shared_ptr<security::access_control_manager> acm);
354
359 void set_access_control_enabled(bool enabled);
360
361private:
362 // =========================================================================
363 // Network Callbacks
364 // =========================================================================
365
367 void on_data_received(const std::vector<uint8_t>& data);
368
370 void on_disconnected(const std::string& session_id);
371
373 void on_error(std::error_code ec);
374
375 // =========================================================================
376 // PDU Processing
377 // =========================================================================
378
380 void process_buffer();
381
383 void process_pdu(const integration::pdu_data& pdu);
384
386 void handle_associate_rq(const std::vector<uint8_t>& payload);
387
389 void handle_p_data_tf(const std::vector<uint8_t>& payload);
390
392 void handle_release_rq();
393
395 void handle_abort(const std::vector<uint8_t>& payload);
396
397 // =========================================================================
398 // Response Sending
399 // =========================================================================
400
402 void send_associate_ac();
403
405 void send_associate_rj(reject_result result, uint8_t source, uint8_t reason);
406
408 void send_p_data_tf(const std::vector<uint8_t>& payload);
409
411 void send_release_rp();
412
414 void send_abort(abort_source source, abort_reason reason);
415
417 void send_pdu(pdu_type type, const std::vector<uint8_t>& payload);
418
419 // =========================================================================
420 // Service Dispatching
421 // =========================================================================
422
424 Result<std::monostate> dispatch_to_service(
425 uint8_t context_id,
426 const dimse::dimse_message& msg);
427
429 [[nodiscard]] services::scp_service* find_service(const std::string& sop_class_uid) const;
430
431 // =========================================================================
432 // State Management
433 // =========================================================================
434
436 void transition_to(handler_state new_state);
437
439 void touch();
440
442 void report_error(const std::string& error);
443
445 void close_handler(bool graceful);
446
447 // =========================================================================
448 // Member Variables
449 // =========================================================================
450
453
456
459
462
465
467 std::vector<uint8_t> receive_buffer_;
468
470 [[maybe_unused]] uint32_t expected_pdu_length_{0};
471
474
477
479 std::atomic<uint64_t> pdus_received_{0};
480 std::atomic<uint64_t> pdus_sent_{0};
481 std::atomic<uint64_t> messages_processed_{0};
482
487
489 mutable std::mutex mutex_;
490 mutable std::mutex callback_mutex_;
491
493 std::shared_ptr<security::access_control_manager> access_control_;
494
496 std::optional<security::user_context> user_context_;
497
500};
501
502} // namespace kcenon::pacs::network::v2
503
504#endif // PACS_NETWORK_V2_DICOM_ASSOCIATION_HANDLER_HPP
Core RBAC logic.
DICOM Association management per PS3.8.
Bridges network_system sessions with DICOM protocol handling.
uint64_t pdus_received() const noexcept
Get number of PDUs received.
Result< std::monostate > dispatch_to_service(uint8_t context_id, const dimse::dimse_message &msg)
Dispatch DIMSE message to appropriate service.
void transition_to(handler_state new_state)
Transition to new state.
void send_abort(abort_source source, abort_reason reason)
Send A-ABORT PDU.
void process_buffer()
Process accumulated buffer for complete PDUs.
void feed_data(const std::vector< uint8_t > &data)
Feed received data to the handler.
void on_disconnected(const std::string &session_id)
Handle session disconnection.
dicom_association_handler(const dicom_association_handler &)=delete
void set_access_control_enabled(bool enabled)
Enable or disable access control enforcement.
handler_state state() const noexcept
Get current handler state.
void send_associate_rj(reject_result result, uint8_t source, uint8_t reason)
Send A-ASSOCIATE-RJ response.
uint64_t pdus_sent() const noexcept
Get number of PDUs sent.
services::scp_service * find_service(const std::string &sop_class_uid) const
Find service for SOP Class UID.
association_established_callback established_callback_
Callbacks.
dicom_association_handler & operator=(const dicom_association_handler &)=delete
bool is_established() const noexcept
Check if the association is established.
std::map< std::string, services::scp_service * > service_map
std::shared_ptr< security::access_control_manager > access_control_
Access control manager for RBAC.
void send_p_data_tf(const std::vector< uint8_t > &payload)
Send P-DATA-TF PDU.
service_map services_
Service registry (non-owning pointers)
bool access_control_enabled_
Whether access control is enabled.
std::string session_id() const
Get the session identifier.
void handle_associate_rq(const std::vector< uint8_t > &payload)
Handle A-ASSOCIATE-RQ PDU.
dicom_association_handler(session_ptr session, const server_config &config, const service_map &services)
Construct a handler for a network session.
uint32_t expected_pdu_length_
Expected PDU length (0 if waiting for header)
Result< std::reference_wrapper< association > > get_association()
Get the underlying association object.
pdu_type current_pdu_type_
Current PDU type being received.
void on_data_received(const std::vector< uint8_t > &data)
Handle data received from session.
static constexpr size_t pdu_header_size
PDU header size (type + reserved + length)
std::atomic< handler_state > state_
Current handler state.
void set_established_callback(association_established_callback callback)
Set callback for association established event.
void set_closed_callback(association_closed_callback callback)
Set callback for association closed event.
time_point last_activity() const noexcept
Get time of last activity.
bool is_closed() const noexcept
Check if the handler is closed.
dicom_association_handler(dicom_association_handler &&)=delete
std::string calling_ae() const
Get the calling AE title.
~dicom_association_handler()
Destructor (stops handler if still running).
void handle_error(std::error_code ec)
Handle session error.
std::shared_ptr< kcenon::network::interfaces::i_session > session_ptr
void set_access_control(std::shared_ptr< security::access_control_manager > acm)
Set the access control manager for RBAC.
void stop(bool graceful=false)
Stop the handler and close the session.
void send_pdu(pdu_type type, const std::vector< uint8_t > &payload)
Send raw PDU data.
uint64_t messages_processed() const noexcept
Get number of DIMSE messages processed.
std::optional< security::user_context > user_context_
User context for this association (set after A-ASSOCIATE negotiation)
void report_error(const std::string &error)
Report error through callback.
void set_error_callback(handler_error_callback callback)
Set callback for error events.
void handle_p_data_tf(const std::vector< uint8_t > &payload)
Handle P-DATA-TF PDU.
static constexpr size_t max_pdu_size
Maximum PDU size for safety checks.
void on_error(std::error_code ec)
Handle session error.
dicom_association_handler & operator=(dicom_association_handler &&)=delete
std::vector< uint8_t > receive_buffer_
PDU receive buffer.
std::string called_ae() const
Get the called AE title.
void handle_abort(const std::vector< uint8_t > &payload)
Handle A-ABORT PDU.
void process_pdu(const integration::pdu_data &pdu)
Process a complete PDU.
DICOM session wrapper for network_system sessions.
constexpr const char * to_string(handler_state state) noexcept
Convert handler_state to string representation.
std::function< void(const std::string &session_id, bool graceful)> association_closed_callback
Callback type for association closed events.
handler_state
State machine states for the association handler.
@ awaiting_response
Sent response, awaiting next PDU.
@ closed
Association closed (released or aborted)
@ releasing
Graceful release in progress.
@ established
Association established, processing DIMSE.
@ idle
Initial state, waiting for A-ASSOCIATE-RQ.
std::function< void(const std::string &session_id, const std::string &error)> handler_error_callback
Callback type for error events.
std::function< void(const std::string &session_id, const std::string &calling_ae, const std::string &called_ae)> association_established_callback
Callback type for association established events.
reject_result
Reject result values.
Definition pdu_types.h:92
pdu_type
PDU (Protocol Data Unit) types as defined in DICOM PS3.8.
Definition pdu_types.h:25
abort_reason
Abort reason values when source is service-provider.
Definition pdu_types.h:79
std::variant< associate_rq, associate_ac, associate_rj, p_data_tf_pdu, release_rq_pdu, release_rp_pdu, abort_pdu > pdu
Variant type that can hold any PDU.
Definition pdu_decoder.h:62
abort_source
Abort source values.
Definition pdu_types.h:70
Base class for DICOM SCP (Service Class Provider) services.
DICOM Server configuration structures.