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

DICOM server using network_system's messaging_server for connection management. More...

#include <dicom_server_v2.h>

Collaboration diagram for kcenon::pacs::network::v2::dicom_server_v2:
Collaboration graph

Public Types

using clock = std::chrono::steady_clock
 
using duration = std::chrono::milliseconds
 
using time_point = clock::time_point
 
using association_established_callback
 Callback type for association established events.
 
using association_closed_callback
 Callback type for association closed events.
 
using error_callback = std::function<void(const std::string& error)>
 Callback type for error events.
 

Public Member Functions

 dicom_server_v2 (const server_config &config)
 Construct server with configuration.
 
 ~dicom_server_v2 ()
 Destructor (stops server if running)
 
 dicom_server_v2 (const dicom_server_v2 &)=delete
 
dicom_server_v2operator= (const dicom_server_v2 &)=delete
 
 dicom_server_v2 (dicom_server_v2 &&)=delete
 
dicom_server_v2operator= (dicom_server_v2 &&)=delete
 
void register_service (services::scp_service_ptr service)
 Register an SCP service.
 
std::vector< std::string > supported_sop_classes () const
 Get list of supported SOP Class UIDs.
 
Result< std::monostate > start ()
 Start the server.
 
void stop (duration timeout=std::chrono::seconds{30})
 Stop the server gracefully.
 
void wait_for_shutdown ()
 Wait for server shutdown.
 
bool is_running () const noexcept
 Check if server is running.
 
size_t active_associations () const noexcept
 Get number of active associations.
 
server_statistics get_statistics () const
 Get server statistics.
 
const server_configconfig () const noexcept
 Get server configuration.
 
void on_association_established (association_established_callback callback)
 Set callback for association established events.
 
void on_association_closed (association_closed_callback callback)
 Set callback for association closed events.
 
void on_error (error_callback callback)
 Set callback for error events.
 
void set_access_control (std::shared_ptr< security::access_control_manager > acm)
 Set the access control manager for RBAC.
 
std::shared_ptr< security::access_control_managerget_access_control () const noexcept
 Get the access control manager.
 
void set_access_control_enabled (bool enabled)
 Enable or disable access control enforcement.
 
bool is_access_control_enabled () const noexcept
 Check if access control is enabled.
 

Private Member Functions

void on_connection (std::shared_ptr< kcenon::network::interfaces::i_session > session)
 Handle new connection from server.
 
void on_disconnection (std::string_view session_id)
 Handle disconnection notification.
 
void on_receive (std::string_view session_id, const std::vector< uint8_t > &data)
 Handle receive data (forwarded to handler)
 
void on_network_error (std::string_view session_id, std::error_code ec)
 Handle network error.
 
void create_handler (std::shared_ptr< kcenon::network::interfaces::i_session > session)
 Create and register a new handler for a session.
 
void remove_handler (const std::string &session_id)
 Remove handler by session ID.
 
std::shared_ptr< dicom_association_handlerfind_handler (const std::string &session_id) const
 Find handler by session ID.
 
void check_idle_timeouts ()
 Check for idle timeouts on handlers.
 
dicom_association_handler::service_map build_service_map () const
 Build service map from registered services.
 
void report_error (const std::string &error)
 Report error through callback.
 

Private Attributes

server_config config_
 Server configuration.
 
std::shared_ptr< kcenon::network::interfaces::i_protocol_server > server_
 network_system's protocol server (via tcp_facade)
 
std::vector< services::scp_service_ptrservices_
 Registered SCP services.
 
std::map< std::string, services::scp_service * > sop_class_to_service_
 Map from SOP Class UID to service (non-owning pointers)
 
std::unordered_map< std::string, std::shared_ptr< dicom_association_handler > > handlers_
 Active association handlers (keyed by session ID)
 
server_statistics stats_
 Server statistics.
 
std::atomic< bool > running_ {false}
 Running flag.
 
std::mutex handlers_mutex_
 Handler mutex (protects handlers_ map)
 
std::mutex services_mutex_
 Service mutex (protects services_ and sop_class_to_service_)
 
std::mutex stats_mutex_
 Statistics mutex.
 
std::condition_variable shutdown_cv_
 Shutdown condition variable.
 
std::mutex shutdown_mutex_
 Shutdown mutex.
 
association_established_callback on_established_cb_
 Association established callback.
 
association_closed_callback on_closed_cb_
 Association closed callback.
 
error_callback on_error_cb_
 Error callback.
 
std::mutex callback_mutex_
 Callback mutex.
 
std::shared_ptr< security::access_control_manageraccess_control_
 Access control manager for RBAC.
 
std::atomic< bool > access_control_enabled_ {false}
 Whether access control is enabled.
 
std::mutex acl_mutex_
 Access control mutex.
 

Detailed Description

DICOM server using network_system's messaging_server for connection management.

This class provides the same functionality as dicom_server but uses network_system's messaging_server for TCP connection management. Key benefits include:

  • No manual thread management: Accept loop and I/O handled by ASIO internally
  • Built-in session tracking: Automatic cleanup on disconnect
  • TLS support: Ready for secure DICOM (future enhancement)
  • Proven scalability: ASIO's async model for efficient connection handling

Architecture

└── messaging_server (network_system)
└── messaging_session (per connection)
└── dicom_association_handler (DICOM protocol)
└── scp_service (DIMSE handling)
Bridges network_system sessions with DICOM protocol handling.
DICOM server using network_system's messaging_server for connection management.

Thread Safety

All public methods are thread-safe. The server uses internal mutexes to protect shared state and delegates I/O operations to network_system's thread model.

Migration Path

This class is API-compatible with dicom_server. To migrate:

  1. Replace dicom_server with dicom_server_v2
  2. No changes needed to service registration or callbacks
  3. Feature flag: PACS_USE_NETWORK_SYSTEM_SERVER (when available)

Definition at line 116 of file dicom_server_v2.h.

Member Typedef Documentation

◆ association_closed_callback

Initial value:
std::function<void(const std::string& session_id, bool graceful)>

Callback type for association closed events.

Definition at line 133 of file dicom_server_v2.h.

◆ association_established_callback

Initial value:
std::function<void(const std::string& session_id,
const std::string& calling_ae,
const std::string& called_ae)>

Callback type for association established events.

Definition at line 127 of file dicom_server_v2.h.

◆ clock

using kcenon::pacs::network::v2::dicom_server_v2::clock = std::chrono::steady_clock

Definition at line 122 of file dicom_server_v2.h.

◆ duration

using kcenon::pacs::network::v2::dicom_server_v2::duration = std::chrono::milliseconds

Definition at line 123 of file dicom_server_v2.h.

◆ error_callback

using kcenon::pacs::network::v2::dicom_server_v2::error_callback = std::function<void(const std::string& error)>

Callback type for error events.

Definition at line 137 of file dicom_server_v2.h.

◆ time_point

Definition at line 124 of file dicom_server_v2.h.

Constructor & Destructor Documentation

◆ dicom_server_v2() [1/3]

kcenon::pacs::network::v2::dicom_server_v2::dicom_server_v2 ( const server_config & config)
explicit

Construct server with configuration.

Parameters
configServer configuration

Definition at line 37 of file dicom_server_v2.cpp.

38 : config_(config) {
39 stats_.start_time = clock::now();
41}
server_config config_
Server configuration.
const server_config & config() const noexcept
Get server configuration.
server_statistics stats_
Server statistics.
std::chrono::steady_clock::time_point last_activity
Time of last activity.
std::chrono::steady_clock::time_point start_time
Server start time.

References kcenon::pacs::network::server_statistics::last_activity, kcenon::pacs::network::server_statistics::start_time, and stats_.

◆ ~dicom_server_v2()

kcenon::pacs::network::v2::dicom_server_v2::~dicom_server_v2 ( )

Destructor (stops server if running)

Definition at line 43 of file dicom_server_v2.cpp.

43 {
44 try {
45 if (running_) {
46 stop();
47 }
48 } catch (...) {
49 // Suppress exceptions in destructor to prevent std::terminate
50 }
51}
std::atomic< bool > running_
Running flag.
void stop(duration timeout=std::chrono::seconds{30})
Stop the server gracefully.

References running_, and stop().

Here is the call graph for this function:

◆ dicom_server_v2() [2/3]

kcenon::pacs::network::v2::dicom_server_v2::dicom_server_v2 ( const dicom_server_v2 & )
delete

◆ dicom_server_v2() [3/3]

kcenon::pacs::network::v2::dicom_server_v2::dicom_server_v2 ( dicom_server_v2 && )
delete

Member Function Documentation

◆ active_associations()

size_t kcenon::pacs::network::v2::dicom_server_v2::active_associations ( ) const
nodiscardnoexcept

Get number of active associations.

Returns
Current number of active DICOM associations

Definition at line 287 of file dicom_server_v2.cpp.

287 {
288 std::lock_guard<std::mutex> lock(handlers_mutex_);
289 return handlers_.size();
290}
std::unordered_map< std::string, std::shared_ptr< dicom_association_handler > > handlers_
Active association handlers (keyed by session ID)
std::mutex handlers_mutex_
Handler mutex (protects handlers_ map)

References handlers_, and handlers_mutex_.

Referenced by get_statistics().

Here is the caller graph for this function:

◆ build_service_map()

dicom_association_handler::service_map kcenon::pacs::network::v2::dicom_server_v2::build_service_map ( ) const
nodiscardprivate

Build service map from registered services.

Definition at line 562 of file dicom_server_v2.cpp.

562 {
563 std::lock_guard<std::mutex> lock(services_mutex_);
565}
std::mutex services_mutex_
Service mutex (protects services_ and sop_class_to_service_)
std::map< std::string, services::scp_service * > sop_class_to_service_
Map from SOP Class UID to service (non-owning pointers)

References services_mutex_, and sop_class_to_service_.

Referenced by create_handler().

Here is the caller graph for this function:

◆ check_idle_timeouts()

void kcenon::pacs::network::v2::dicom_server_v2::check_idle_timeouts ( )
private

Check for idle timeouts on handlers.

Definition at line 529 of file dicom_server_v2.cpp.

529 {
530 if (config_.idle_timeout.count() == 0) {
531 return; // No timeout configured
532 }
533
534 auto now = clock::now();
535 std::vector<std::string> timed_out;
536
537 {
538 std::lock_guard<std::mutex> lock(handlers_mutex_);
539 for (const auto& [session_id, handler] : handlers_) {
540 auto idle_duration = std::chrono::duration_cast<std::chrono::seconds>(
541 now - handler->last_activity());
542
543 if (idle_duration >= config_.idle_timeout) {
544 timed_out.push_back(session_id);
545 }
546 }
547 }
548
549 // Stop timed-out handlers
550 for (const auto& session_id : timed_out) {
551 auto handler = find_handler(session_id);
552 if (handler) {
553 handler->stop(false); // Force abort
554 }
555 }
556}
std::shared_ptr< dicom_association_handler > find_handler(const std::string &session_id) const
Find handler by session ID.
std::chrono::seconds idle_timeout
Idle timeout for associations (0 = no timeout)

References config_, find_handler(), handlers_, handlers_mutex_, and kcenon::pacs::network::server_config::idle_timeout.

Here is the call graph for this function:

◆ config()

const server_config & kcenon::pacs::network::v2::dicom_server_v2::config ( ) const
nodiscardnoexcept

Get server configuration.

Returns
Reference to current configuration

Definition at line 299 of file dicom_server_v2.cpp.

299 {
300 return config_;
301}

References config_.

◆ create_handler()

void kcenon::pacs::network::v2::dicom_server_v2::create_handler ( std::shared_ptr< kcenon::network::interfaces::i_session > session)
private

Create and register a new handler for a session.

Definition at line 424 of file dicom_server_v2.cpp.

425 {
426
427#ifdef PACS_WITH_NETWORK_SYSTEM
428 const std::string session_id(session->id());
429#else
430 const std::string session_id;
431 (void)session;
432#endif
433
434 // Build service map for handler
435 auto service_map = build_service_map();
436
437 // Create handler
438 auto handler = std::make_shared<dicom_association_handler>(
439 std::move(session), config_, service_map);
440
441 // Set up access control if configured
442 {
443 std::lock_guard<std::mutex> acl_lock(acl_mutex_);
444 if (access_control_) {
445 handler->set_access_control(access_control_);
446 handler->set_access_control_enabled(access_control_enabled_);
447 }
448 }
449
450 // Set up handler callbacks
451 auto weak_this = std::weak_ptr<dicom_server_v2*>(
452 std::shared_ptr<dicom_server_v2*>(nullptr, [](dicom_server_v2**) {}));
453 // Note: We can't use shared_from_this() since dicom_server_v2 doesn't inherit
454 // from enable_shared_from_this. Instead, capture 'this' directly since the
455 // handler's lifetime is bounded by the server's lifetime.
456
457 handler->set_established_callback(
458 [this](const std::string& sid, const std::string& calling_ae,
459 const std::string& called_ae) {
460 // Update statistics
461 {
462 std::lock_guard<std::mutex> lock(stats_mutex_);
464 stats_.last_activity = clock::now();
465 }
466
467 // Forward to user callback
468 {
469 std::lock_guard<std::mutex> lock(callback_mutex_);
470 if (on_established_cb_) {
471 on_established_cb_(sid, calling_ae, called_ae);
472 }
473 }
474 });
475
476 handler->set_closed_callback(
477 [this](const std::string& sid, bool graceful) {
478 // Forward to user callback
479 {
480 std::lock_guard<std::mutex> lock(callback_mutex_);
481 if (on_closed_cb_) {
482 on_closed_cb_(sid, graceful);
483 }
484 }
485
486 // Remove handler from map
487 remove_handler(sid);
488 });
489
490 handler->set_error_callback(
491 [this](const std::string& /*sid*/, const std::string& error) {
492 report_error(error);
493 });
494
495 // Register handler
496 {
497 std::lock_guard<std::mutex> lock(handlers_mutex_);
498 handlers_[session_id] = handler;
499 }
500
501 // Start handler (begins processing PDUs)
502 handler->start();
503}
std::mutex acl_mutex_
Access control mutex.
void report_error(const std::string &error)
Report error through callback.
association_closed_callback on_closed_cb_
Association closed callback.
std::shared_ptr< security::access_control_manager > access_control_
Access control manager for RBAC.
dicom_association_handler::service_map build_service_map() const
Build service map from registered services.
association_established_callback on_established_cb_
Association established callback.
void remove_handler(const std::string &session_id)
Remove handler by session ID.
std::atomic< bool > access_control_enabled_
Whether access control is enabled.
uint64_t total_associations
Total associations since server start.

References access_control_, access_control_enabled_, acl_mutex_, build_service_map(), callback_mutex_, config_, handlers_, handlers_mutex_, kcenon::pacs::network::server_statistics::last_activity, on_closed_cb_, on_established_cb_, remove_handler(), report_error(), stats_, stats_mutex_, and kcenon::pacs::network::server_statistics::total_associations.

Referenced by on_connection().

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

◆ find_handler()

std::shared_ptr< dicom_association_handler > kcenon::pacs::network::v2::dicom_server_v2::find_handler ( const std::string & session_id) const
nodiscardprivate

Find handler by session ID.

Definition at line 520 of file dicom_server_v2.cpp.

520 {
521 std::lock_guard<std::mutex> lock(handlers_mutex_);
522 auto it = handlers_.find(session_id);
523 if (it != handlers_.end()) {
524 return it->second;
525 }
526 return nullptr;
527}

References handlers_, and handlers_mutex_.

Referenced by check_idle_timeouts(), on_disconnection(), on_network_error(), and on_receive().

Here is the caller graph for this function:

◆ get_access_control()

std::shared_ptr< security::access_control_manager > kcenon::pacs::network::v2::dicom_server_v2::get_access_control ( ) const
nodiscardnoexcept

Get the access control manager.

Returns
Shared pointer to access control manager, or nullptr if not set

Definition at line 588 of file dicom_server_v2.cpp.

588 {
589 std::lock_guard<std::mutex> lock(acl_mutex_);
590 return access_control_;
591}

References access_control_, and acl_mutex_.

◆ get_statistics()

server_statistics kcenon::pacs::network::v2::dicom_server_v2::get_statistics ( ) const
nodiscard

Get server statistics.

Returns
Current server statistics

Definition at line 292 of file dicom_server_v2.cpp.

292 {
293 std::lock_guard<std::mutex> lock(stats_mutex_);
294 server_statistics result = stats_;
296 return result;
297}
size_t active_associations() const noexcept
Get number of active associations.
size_t active_associations
Currently active associations.

References kcenon::pacs::network::server_statistics::active_associations, active_associations(), stats_, and stats_mutex_.

Here is the call graph for this function:

◆ is_access_control_enabled()

bool kcenon::pacs::network::v2::dicom_server_v2::is_access_control_enabled ( ) const
nodiscardnoexcept

Check if access control is enabled.

Returns
true if access control is enforced

Definition at line 597 of file dicom_server_v2.cpp.

597 {
599}

References access_control_enabled_.

◆ is_running()

bool kcenon::pacs::network::v2::dicom_server_v2::is_running ( ) const
nodiscardnoexcept

Check if server is running.

Returns
true if server is accepting connections

Definition at line 283 of file dicom_server_v2.cpp.

283 {
284 return running_;
285}

References running_.

◆ on_association_closed()

void kcenon::pacs::network::v2::dicom_server_v2::on_association_closed ( association_closed_callback callback)

Set callback for association closed events.

Parameters
callbackFunction to call when an association is closed

Definition at line 312 of file dicom_server_v2.cpp.

312 {
313 std::lock_guard<std::mutex> lock(callback_mutex_);
314 on_closed_cb_ = std::move(callback);
315}

References callback_mutex_, and on_closed_cb_.

◆ on_association_established()

void kcenon::pacs::network::v2::dicom_server_v2::on_association_established ( association_established_callback callback)

Set callback for association established events.

Parameters
callbackFunction to call when an association is established

Definition at line 307 of file dicom_server_v2.cpp.

307 {
308 std::lock_guard<std::mutex> lock(callback_mutex_);
309 on_established_cb_ = std::move(callback);
310}

References callback_mutex_, and on_established_cb_.

◆ on_connection()

void kcenon::pacs::network::v2::dicom_server_v2::on_connection ( std::shared_ptr< kcenon::network::interfaces::i_session > session)
private

Handle new connection from server.

Definition at line 326 of file dicom_server_v2.cpp.

327 {
328
329 if (!running_ || !session) {
330 return;
331 }
332
333 // Check max associations limit
334 {
335 std::lock_guard<std::mutex> lock(handlers_mutex_);
336 if (config_.max_associations > 0 &&
338 // Reject due to resource limit
339 report_error("Max associations limit reached, rejecting connection");
340
341 std::lock_guard<std::mutex> stats_lock(stats_mutex_);
343
344#ifdef PACS_WITH_NETWORK_SYSTEM
345 session->close();
346#endif
347 return;
348 }
349 }
350
351 // Create handler for this session
352 create_handler(std::move(session));
353}
void create_handler(std::shared_ptr< kcenon::network::interfaces::i_session > session)
Create and register a new handler for a session.
size_t max_associations
Maximum concurrent associations (0 = unlimited)
uint64_t rejected_associations
Total associations rejected due to limit.

References config_, create_handler(), handlers_, handlers_mutex_, kcenon::pacs::network::server_config::max_associations, kcenon::pacs::network::server_statistics::rejected_associations, report_error(), running_, stats_, and stats_mutex_.

Referenced by start().

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

◆ on_disconnection()

void kcenon::pacs::network::v2::dicom_server_v2::on_disconnection ( std::string_view session_id)
private

Handle disconnection notification.

Definition at line 355 of file dicom_server_v2.cpp.

355 {
356 // Skip if server is shutting down
357 if (!running_) {
358 return;
359 }
360
361 std::string sid(session_id);
362
363 // Notify handler of disconnection before removing it
364 auto handler = find_handler(sid);
365 if (handler) {
366 handler->handle_disconnect();
367 }
368
369 // Remove handler for this session
370 remove_handler(sid);
371}

References find_handler(), remove_handler(), and running_.

Referenced by start().

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

◆ on_error()

void kcenon::pacs::network::v2::dicom_server_v2::on_error ( error_callback callback)

Set callback for error events.

Parameters
callbackFunction to call on server errors

Definition at line 317 of file dicom_server_v2.cpp.

317 {
318 std::lock_guard<std::mutex> lock(callback_mutex_);
319 on_error_cb_ = std::move(callback);
320}
error_callback on_error_cb_
Error callback.

References callback_mutex_, and on_error_cb_.

◆ on_network_error()

void kcenon::pacs::network::v2::dicom_server_v2::on_network_error ( std::string_view session_id,
std::error_code ec )
private

Handle network error.

Definition at line 394 of file dicom_server_v2.cpp.

396 {
397
398 // Skip if server is shutting down
399 if (!running_) {
400 return;
401 }
402
403 std::string sid(session_id);
404
405 std::ostringstream oss;
406 oss << "Network error on session " << sid
407 << ": " << ec.message() << " (" << ec.value() << ")";
408 report_error(oss.str());
409
410 // Notify handler of the error
411 auto handler = find_handler(sid);
412 if (handler) {
413 handler->handle_error(ec);
414 }
415
416 // Remove the handler - it will clean itself up
417 remove_handler(sid);
418}

References find_handler(), remove_handler(), report_error(), and running_.

Referenced by start().

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

◆ on_receive()

void kcenon::pacs::network::v2::dicom_server_v2::on_receive ( std::string_view session_id,
const std::vector< uint8_t > & data )
private

Handle receive data (forwarded to handler)

Definition at line 373 of file dicom_server_v2.cpp.

375 {
376
377 std::string sid(session_id);
378
379 // Find handler and forward data
380 auto handler = find_handler(sid);
381 if (handler) {
382 // Forward data to handler for PDU processing
383 handler->feed_data(data);
384
385 // Update statistics
386 {
387 std::lock_guard<std::mutex> lock(stats_mutex_);
388 stats_.bytes_received += data.size();
389 stats_.last_activity = clock::now();
390 }
391 }
392}
uint64_t bytes_received
Total bytes received.

References kcenon::pacs::network::server_statistics::bytes_received, find_handler(), kcenon::pacs::network::server_statistics::last_activity, stats_, and stats_mutex_.

Referenced by start().

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

◆ operator=() [1/2]

dicom_server_v2 & kcenon::pacs::network::v2::dicom_server_v2::operator= ( const dicom_server_v2 & )
delete

◆ operator=() [2/2]

dicom_server_v2 & kcenon::pacs::network::v2::dicom_server_v2::operator= ( dicom_server_v2 && )
delete

◆ register_service()

void kcenon::pacs::network::v2::dicom_server_v2::register_service ( services::scp_service_ptr service)

Register an SCP service.

The server takes ownership of the service and routes DIMSE messages to it based on the SOP Classes it supports.

Parameters
serviceThe SCP service to register
Note
Services must be registered before calling start()

Definition at line 57 of file dicom_server_v2.cpp.

57 {
58 if (!service) {
59 return;
60 }
61
62 std::lock_guard<std::mutex> lock(services_mutex_);
63
64 // Register SOP Class mappings
65 for (const auto& sop_class : service->supported_sop_classes()) {
66 sop_class_to_service_[sop_class] = service.get();
67 }
68
69 services_.push_back(std::move(service));
70}
std::vector< services::scp_service_ptr > services_
Registered SCP services.

References services_, services_mutex_, and sop_class_to_service_.

◆ remove_handler()

void kcenon::pacs::network::v2::dicom_server_v2::remove_handler ( const std::string & session_id)
private

Remove handler by session ID.

Definition at line 505 of file dicom_server_v2.cpp.

505 {
506 // Skip if server is shutting down - handlers are cleaned up in stop()
507 if (!running_) {
508 return;
509 }
510
511 std::lock_guard<std::mutex> lock(handlers_mutex_);
512 auto it = handlers_.find(session_id);
513 if (it != handlers_.end()) {
514 // Handler cleans itself up via stop() in destructor
515 handlers_.erase(it);
516 }
517}

References handlers_, handlers_mutex_, and running_.

Referenced by create_handler(), on_disconnection(), and on_network_error().

Here is the caller graph for this function:

◆ report_error()

void kcenon::pacs::network::v2::dicom_server_v2::report_error ( const std::string & error)
private

Report error through callback.

Definition at line 567 of file dicom_server_v2.cpp.

567 {
568 std::lock_guard<std::mutex> lock(callback_mutex_);
569 if (on_error_cb_) {
570 on_error_cb_(error);
571 }
572}

References callback_mutex_, and on_error_cb_.

Referenced by create_handler(), on_connection(), and on_network_error().

Here is the caller graph for this function:

◆ set_access_control()

void kcenon::pacs::network::v2::dicom_server_v2::set_access_control ( std::shared_ptr< security::access_control_manager > acm)

Set the access control manager for RBAC.

When set, the server will validate permissions before allowing DICOM operations. If not set, all operations are allowed.

Parameters
acmShared pointer to access control manager
Note
Should be set before calling start()

Definition at line 578 of file dicom_server_v2.cpp.

579 {
580 std::lock_guard<std::mutex> lock(acl_mutex_);
581 access_control_ = std::move(acm);
582 if (access_control_) {
584 }
585}

References access_control_, access_control_enabled_, and acl_mutex_.

◆ set_access_control_enabled()

void kcenon::pacs::network::v2::dicom_server_v2::set_access_control_enabled ( bool enabled)

Enable or disable access control enforcement.

Parameters
enabledIf true, access control is enforced; if false, all operations allowed

Definition at line 593 of file dicom_server_v2.cpp.

593 {
594 access_control_enabled_ = enabled;
595}

References access_control_enabled_.

◆ start()

Result< std::monostate > kcenon::pacs::network::v2::dicom_server_v2::start ( )
nodiscard

Start the server.

Binds to the configured port and begins accepting connections using network_system's messaging_server.

Returns
Success or error with description

Definition at line 89 of file dicom_server_v2.cpp.

89 {
90 if (running_.exchange(true)) {
91 return error_info("Server already running");
92 }
93
94 // Validate configuration
95 if (config_.ae_title.empty()) {
96 running_ = false;
97 return error_info("AE Title cannot be empty");
98 }
99
100 if (config_.ae_title.length() > AE_TITLE_LENGTH) {
101 running_ = false;
102 return error_info("AE Title exceeds 16 characters");
103 }
104
105 if (config_.port == 0) {
106 running_ = false;
107 return error_info("Invalid port number");
108 }
109
110 // Check that at least one service is registered
111 {
112 std::lock_guard<std::mutex> lock(services_mutex_);
113 if (services_.empty()) {
114 running_ = false;
115 return error_info("No services registered");
116 }
117 }
118
119 // Reset statistics
120 {
121 std::lock_guard<std::mutex> lock(stats_mutex_);
122 stats_.start_time = clock::now();
129 stats_.bytes_sent = 0;
130 }
131
132#ifdef PACS_WITH_NETWORK_SYSTEM
133 try {
134 // Create TCP server via tcp_facade
135 kcenon::network::facade::tcp_facade facade;
136 kcenon::network::facade::tcp_facade::server_config srv_cfg;
137 srv_cfg.server_id = config_.ae_title;
138 srv_cfg.port = config_.port;
139 // Handle both old API (returns shared_ptr) and new API (returns Result<shared_ptr>)
140 server_ = [](auto raw) -> std::shared_ptr<kcenon::network::interfaces::i_protocol_server> {
141 if constexpr (requires { raw.is_err(); raw.value(); }) {
142 if (raw.is_err()) throw std::runtime_error("Failed to create server");
143 return std::move(raw.value());
144 } else {
145 return std::move(raw);
146 }
147 }(facade.create_server(srv_cfg));
148
149 // Set up server-level callbacks
150 server_->set_connection_callback(
151 [this](std::shared_ptr<kcenon::network::interfaces::i_session> session) {
152 on_connection(std::move(session));
153 });
154
155 server_->set_disconnection_callback(
156 [this](std::string_view session_id) {
157 on_disconnection(session_id);
158 });
159
160 server_->set_receive_callback(
161 [this](std::string_view session_id,
162 const std::vector<uint8_t>& data) {
163 on_receive(session_id, data);
164 });
165
166 server_->set_error_callback(
167 [this](std::string_view session_id,
168 std::error_code ec) {
169 on_network_error(session_id, ec);
170 });
171
172 // Start the server on configured port
173 auto result = server_->start(config_.port);
174 if (result.is_err()) {
175 running_ = false;
176 server_.reset();
177 return error_info("Failed to start server");
178 }
179
180 return std::monostate{};
181 } catch (const std::exception& e) {
182 running_ = false;
183 if (server_) {
184 server_.reset();
185 }
186 return error_info(std::string("Exception during server start: ") + e.what());
187 } catch (...) {
188 running_ = false;
189 if (server_) {
190 server_.reset();
191 }
192 return error_info("Unknown exception during server start");
193 }
194#else
195 running_ = false;
196 return error_info("dicom_server_v2 requires PACS_WITH_NETWORK_SYSTEM");
197#endif
198}
void on_network_error(std::string_view session_id, std::error_code ec)
Handle network error.
void on_receive(std::string_view session_id, const std::vector< uint8_t > &data)
Handle receive data (forwarded to handler)
std::shared_ptr< kcenon::network::interfaces::i_protocol_server > server_
network_system's protocol server (via tcp_facade)
void on_disconnection(std::string_view session_id)
Handle disconnection notification.
void on_connection(std::shared_ptr< kcenon::network::interfaces::i_session > session)
Handle new connection from server.
constexpr size_t AE_TITLE_LENGTH
AE Title length (fixed 16 characters, space-padded)
Definition pdu_types.h:273
kcenon::common::error_info error_info
Error information type.
Definition result.h:40
uint16_t port
Port to listen on (default: 11112, standard alternate DICOM port)
std::string ae_title
Application Entity Title for this server (16 chars max)
uint64_t messages_processed
Total DIMSE messages processed.

References kcenon::pacs::network::server_statistics::active_associations, kcenon::pacs::network::server_config::ae_title, kcenon::pacs::network::AE_TITLE_LENGTH, kcenon::pacs::network::server_statistics::bytes_received, kcenon::pacs::network::server_statistics::bytes_sent, config_, kcenon::pacs::network::server_statistics::last_activity, kcenon::pacs::network::server_statistics::messages_processed, on_connection(), on_disconnection(), on_network_error(), on_receive(), kcenon::pacs::network::server_config::port, kcenon::pacs::network::server_statistics::rejected_associations, running_, server_, services_, services_mutex_, kcenon::pacs::network::server_statistics::start_time, stats_, stats_mutex_, and kcenon::pacs::network::server_statistics::total_associations.

Here is the call graph for this function:

◆ stop()

void kcenon::pacs::network::v2::dicom_server_v2::stop ( duration timeout = std::chrono::seconds{30})

Stop the server gracefully.

Stops accepting new connections and waits for active associations to complete or timeout.

Parameters
timeoutMaximum time to wait for associations to close

Definition at line 200 of file dicom_server_v2.cpp.

200 {
201 if (!running_.exchange(false)) {
202 return; // Already stopped
203 }
204
205#ifdef PACS_WITH_NETWORK_SYSTEM
206 // Phase 1: Stop accepting new connections
207 if (server_) {
208 try {
209 // Stop the server - this closes the acceptor
210 (void)server_->stop();
211 } catch (...) {
212 // Suppress exceptions during server stop
213 }
214 }
215
216 // Phase 2: Wait for handlers to complete gracefully
217 auto deadline = clock::now() + timeout;
218 {
219 std::unique_lock<std::mutex> lock(handlers_mutex_);
220 while (!handlers_.empty() && clock::now() < deadline) {
221 // Release lock while waiting
222 lock.unlock();
223 std::this_thread::sleep_for(std::chrono::milliseconds{100});
224 lock.lock();
225 }
226 }
227
228 // Phase 3: Force stop remaining handlers
229 // Collect handlers first, then stop them without holding the lock
230 // to avoid potential deadlock if handler->stop() triggers callbacks
231 std::vector<std::shared_ptr<dicom_association_handler>> handlers_to_stop;
232 {
233 std::lock_guard<std::mutex> lock(handlers_mutex_);
234 handlers_to_stop.reserve(handlers_.size());
235 for (auto& [session_id, handler] : handlers_) {
236 handlers_to_stop.push_back(handler);
237 }
238 handlers_.clear();
239 }
240
241 // Stop handlers without holding the lock
242 for (auto& handler : handlers_to_stop) {
243 try {
244 handler->stop(false); // Force abort
245 } catch (...) {
246 // Suppress exceptions during handler stop
247 }
248 }
249 handlers_to_stop.clear();
250
251 // Allow any pending callbacks to complete
252 std::this_thread::sleep_for(std::chrono::milliseconds{50});
253
254 // Clear callbacks to break reference cycles before server is destroyed.
255 // The server object itself is kept alive until dicom_server_v2 is destroyed
256 // to avoid use-after-free in the adapter's background I/O threads.
257 if (server_) {
258 server_->set_connection_callback(nullptr);
259 server_->set_disconnection_callback(nullptr);
260 server_->set_receive_callback(nullptr);
261 server_->set_error_callback(nullptr);
262 }
263#else
264 (void)timeout; // Unused without network_system
265#endif
266
267 // Notify shutdown waiters
268 {
269 std::lock_guard<std::mutex> lock(shutdown_mutex_);
270 shutdown_cv_.notify_all();
271 }
272}
std::condition_variable shutdown_cv_
Shutdown condition variable.
constexpr int timeout
Lock timeout exceeded.

References handlers_, handlers_mutex_, running_, server_, shutdown_cv_, and shutdown_mutex_.

Referenced by ~dicom_server_v2().

Here is the caller graph for this function:

◆ supported_sop_classes()

std::vector< std::string > kcenon::pacs::network::v2::dicom_server_v2::supported_sop_classes ( ) const
nodiscard

Get list of supported SOP Class UIDs.

Returns
Vector of all SOP Classes supported by registered services

Definition at line 72 of file dicom_server_v2.cpp.

72 {
73 std::lock_guard<std::mutex> lock(services_mutex_);
74
75 std::vector<std::string> sop_classes;
76 sop_classes.reserve(sop_class_to_service_.size());
77
78 for (const auto& [uid, _] : sop_class_to_service_) {
79 sop_classes.push_back(uid);
80 }
81
82 return sop_classes;
83}
std::string_view uid

References services_mutex_, sop_class_to_service_, and uid.

◆ wait_for_shutdown()

void kcenon::pacs::network::v2::dicom_server_v2::wait_for_shutdown ( )

Wait for server shutdown.

Blocks until the server is stopped (either by calling stop() or due to an error).

Definition at line 274 of file dicom_server_v2.cpp.

274 {
275 std::unique_lock<std::mutex> lock(shutdown_mutex_);
276 shutdown_cv_.wait(lock, [this]() { return !running_; });
277}

References running_, shutdown_cv_, and shutdown_mutex_.

Member Data Documentation

◆ access_control_

std::shared_ptr<security::access_control_manager> kcenon::pacs::network::v2::dicom_server_v2::access_control_
private

Access control manager for RBAC.

Definition at line 396 of file dicom_server_v2.h.

Referenced by create_handler(), get_access_control(), and set_access_control().

◆ access_control_enabled_

std::atomic<bool> kcenon::pacs::network::v2::dicom_server_v2::access_control_enabled_ {false}
private

Whether access control is enabled.

Definition at line 399 of file dicom_server_v2.h.

399{false};

Referenced by create_handler(), is_access_control_enabled(), set_access_control(), and set_access_control_enabled().

◆ acl_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::acl_mutex_
mutableprivate

Access control mutex.

Definition at line 402 of file dicom_server_v2.h.

Referenced by create_handler(), get_access_control(), and set_access_control().

◆ callback_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::callback_mutex_
mutableprivate

Callback mutex.

Definition at line 393 of file dicom_server_v2.h.

Referenced by create_handler(), on_association_closed(), on_association_established(), on_error(), and report_error().

◆ config_

server_config kcenon::pacs::network::v2::dicom_server_v2::config_
private

Server configuration.

Definition at line 348 of file dicom_server_v2.h.

Referenced by check_idle_timeouts(), config(), create_handler(), on_connection(), and start().

◆ handlers_

std::unordered_map<std::string, std::shared_ptr<dicom_association_handler> > kcenon::pacs::network::v2::dicom_server_v2::handlers_
private

Active association handlers (keyed by session ID)

Definition at line 360 of file dicom_server_v2.h.

Referenced by active_associations(), check_idle_timeouts(), create_handler(), find_handler(), on_connection(), remove_handler(), and stop().

◆ handlers_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::handlers_mutex_
mutableprivate

Handler mutex (protects handlers_ map)

Definition at line 369 of file dicom_server_v2.h.

Referenced by active_associations(), check_idle_timeouts(), create_handler(), find_handler(), on_connection(), remove_handler(), and stop().

◆ on_closed_cb_

association_closed_callback kcenon::pacs::network::v2::dicom_server_v2::on_closed_cb_
private

Association closed callback.

Definition at line 387 of file dicom_server_v2.h.

Referenced by create_handler(), and on_association_closed().

◆ on_error_cb_

error_callback kcenon::pacs::network::v2::dicom_server_v2::on_error_cb_
private

Error callback.

Definition at line 390 of file dicom_server_v2.h.

Referenced by on_error(), and report_error().

◆ on_established_cb_

association_established_callback kcenon::pacs::network::v2::dicom_server_v2::on_established_cb_
private

Association established callback.

Definition at line 384 of file dicom_server_v2.h.

Referenced by create_handler(), and on_association_established().

◆ running_

std::atomic<bool> kcenon::pacs::network::v2::dicom_server_v2::running_ {false}
private

◆ server_

std::shared_ptr<kcenon::network::interfaces::i_protocol_server> kcenon::pacs::network::v2::dicom_server_v2::server_
private

network_system's protocol server (via tcp_facade)

Definition at line 351 of file dicom_server_v2.h.

Referenced by start(), and stop().

◆ services_

std::vector<services::scp_service_ptr> kcenon::pacs::network::v2::dicom_server_v2::services_
private

Registered SCP services.

Definition at line 354 of file dicom_server_v2.h.

Referenced by register_service(), and start().

◆ services_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::services_mutex_
mutableprivate

Service mutex (protects services_ and sop_class_to_service_)

Definition at line 372 of file dicom_server_v2.h.

Referenced by build_service_map(), register_service(), start(), and supported_sop_classes().

◆ shutdown_cv_

std::condition_variable kcenon::pacs::network::v2::dicom_server_v2::shutdown_cv_
private

Shutdown condition variable.

Definition at line 378 of file dicom_server_v2.h.

Referenced by stop(), and wait_for_shutdown().

◆ shutdown_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::shutdown_mutex_
private

Shutdown mutex.

Definition at line 381 of file dicom_server_v2.h.

Referenced by stop(), and wait_for_shutdown().

◆ sop_class_to_service_

std::map<std::string, services::scp_service*> kcenon::pacs::network::v2::dicom_server_v2::sop_class_to_service_
private

Map from SOP Class UID to service (non-owning pointers)

Definition at line 357 of file dicom_server_v2.h.

Referenced by build_service_map(), register_service(), and supported_sop_classes().

◆ stats_

server_statistics kcenon::pacs::network::v2::dicom_server_v2::stats_
mutableprivate

Server statistics.

Definition at line 363 of file dicom_server_v2.h.

Referenced by create_handler(), dicom_server_v2(), get_statistics(), on_connection(), on_receive(), and start().

◆ stats_mutex_

std::mutex kcenon::pacs::network::v2::dicom_server_v2::stats_mutex_
mutableprivate

Statistics mutex.

Definition at line 375 of file dicom_server_v2.h.

Referenced by create_handler(), get_statistics(), on_connection(), on_receive(), and start().


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