19#ifndef KCENON_HAS_COMMON_SYSTEM
20#define KCENON_HAS_COMMON_SYSTEM 0
23#ifdef PACS_WITH_NETWORK_SYSTEM
24#include <kcenon/network/facade/tcp_facade.h>
27#if KCENON_HAS_COMMON_SYSTEM
28using kcenon::common::error_info;
65 for (
const auto& sop_class : service->supported_sop_classes()) {
75 std::vector<std::string> sop_classes;
79 sop_classes.push_back(
uid);
102 return error_info(
"AE Title exceeds 16 characters");
132#ifdef PACS_WITH_NETWORK_SYSTEM
135 kcenon::network::facade::tcp_facade facade;
136 kcenon::network::facade::tcp_facade::server_config srv_cfg;
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());
145 return std::move(raw);
147 }(facade.create_server(srv_cfg));
150 server_->set_connection_callback(
151 [
this](std::shared_ptr<kcenon::network::interfaces::i_session> session) {
155 server_->set_disconnection_callback(
156 [
this](std::string_view session_id) {
161 [
this](std::string_view session_id,
162 const std::vector<uint8_t>& data) {
167 [
this](std::string_view session_id,
168 std::error_code ec) {
174 if (result.is_err()) {
180 return std::monostate{};
181 }
catch (
const std::exception& e) {
186 return error_info(std::string(
"Exception during server start: ") + e.what());
192 return error_info(
"Unknown exception during server start");
196 return error_info(
"dicom_server_v2 requires PACS_WITH_NETWORK_SYSTEM");
205#ifdef PACS_WITH_NETWORK_SYSTEM
217 auto deadline = clock::now() + timeout;
220 while (!
handlers_.empty() && clock::now() < deadline) {
223 std::this_thread::sleep_for(std::chrono::milliseconds{100});
231 std::vector<std::shared_ptr<dicom_association_handler>> handlers_to_stop;
234 handlers_to_stop.reserve(
handlers_.size());
235 for (
auto& [session_id, handler] :
handlers_) {
236 handlers_to_stop.push_back(handler);
242 for (
auto& handler : handlers_to_stop) {
244 handler->stop(
false);
249 handlers_to_stop.clear();
252 std::this_thread::sleep_for(std::chrono::milliseconds{50});
258 server_->set_connection_callback(
nullptr);
259 server_->set_disconnection_callback(
nullptr);
260 server_->set_receive_callback(
nullptr);
261 server_->set_error_callback(
nullptr);
327 std::shared_ptr<kcenon::network::interfaces::i_session> session) {
339 report_error(
"Max associations limit reached, rejecting connection");
344#ifdef PACS_WITH_NETWORK_SYSTEM
361 std::string sid(session_id);
366 handler->handle_disconnect();
374 std::string_view session_id,
375 const std::vector<uint8_t>& data) {
377 std::string sid(session_id);
383 handler->feed_data(data);
395 std::string_view session_id,
396 std::error_code ec) {
403 std::string sid(session_id);
405 std::ostringstream oss;
406 oss <<
"Network error on session " << sid
407 <<
": " << ec.message() <<
" (" << ec.value() <<
")";
413 handler->handle_error(ec);
425 std::shared_ptr<kcenon::network::interfaces::i_session> session) {
427#ifdef PACS_WITH_NETWORK_SYSTEM
428 const std::string session_id(session->id());
430 const std::string session_id;
438 auto handler = std::make_shared<dicom_association_handler>(
439 std::move(session),
config_, service_map);
443 std::lock_guard<std::mutex> acl_lock(
acl_mutex_);
451 auto weak_this = std::weak_ptr<dicom_server_v2*>(
457 handler->set_established_callback(
458 [
this](
const std::string& sid,
const std::string& calling_ae,
459 const std::string& called_ae) {
476 handler->set_closed_callback(
477 [
this](
const std::string& sid,
bool graceful) {
490 handler->set_error_callback(
491 [
this](
const std::string& ,
const std::string& error) {
519std::shared_ptr<dicom_association_handler>
534 auto now = clock::now();
535 std::vector<std::string> timed_out;
539 for (
const auto& [session_id, handler] :
handlers_) {
540 auto idle_duration = std::chrono::duration_cast<std::chrono::seconds>(
541 now - handler->last_activity());
544 timed_out.push_back(session_id);
550 for (
const auto& session_id : timed_out) {
553 handler->stop(
false);
579 std::shared_ptr<security::access_control_manager> acm) {
587std::shared_ptr<security::access_control_manager>
std::map< std::string, services::scp_service * > service_map
DICOM server using network_system's messaging_server for connection management.
void on_network_error(std::string_view session_id, std::error_code ec)
Handle network error.
server_config config_
Server configuration.
std::mutex acl_mutex_
Access control mutex.
void report_error(const std::string &error)
Report error through callback.
dicom_server_v2(const server_config &config)
Construct server with configuration.
std::atomic< bool > running_
Running flag.
server_statistics get_statistics() const
Get server statistics.
std::vector< services::scp_service_ptr > services_
Registered SCP services.
void on_error(error_callback callback)
Set callback for error events.
association_closed_callback on_closed_cb_
Association closed callback.
const server_config & config() const noexcept
Get server configuration.
std::shared_ptr< security::access_control_manager > access_control_
Access control manager for RBAC.
void create_handler(std::shared_ptr< kcenon::network::interfaces::i_session > session)
Create and register a new handler for a session.
std::unordered_map< std::string, std::shared_ptr< dicom_association_handler > > handlers_
Active association handlers (keyed by session ID)
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)
bool is_running() const noexcept
Check if server is running.
void register_service(services::scp_service_ptr service)
Register an SCP service.
dicom_association_handler::service_map build_service_map() const
Build service map from registered services.
void on_association_established(association_established_callback callback)
Set callback for association established events.
std::mutex stats_mutex_
Statistics mutex.
std::function< void(const std::string &session_id, bool graceful)> association_closed_callback
Callback type for association closed events.
association_established_callback on_established_cb_
Association established callback.
void wait_for_shutdown()
Wait for server shutdown.
std::mutex services_mutex_
Service mutex (protects services_ and sop_class_to_service_)
void check_idle_timeouts()
Check for idle timeouts on handlers.
std::map< std::string, services::scp_service * > sop_class_to_service_
Map from SOP Class UID to service (non-owning pointers)
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.
~dicom_server_v2()
Destructor (stops server if running)
std::function< void(const std::string &error)> error_callback
Callback type for error events.
std::shared_ptr< dicom_association_handler > find_handler(const std::string &session_id) const
Find handler by session ID.
void remove_handler(const std::string &session_id)
Remove handler by session ID.
std::mutex callback_mutex_
Callback mutex.
void on_association_closed(association_closed_callback callback)
Set callback for association closed events.
void set_access_control(std::shared_ptr< security::access_control_manager > acm)
Set the access control manager for RBAC.
error_callback on_error_cb_
Error callback.
void stop(duration timeout=std::chrono::seconds{30})
Stop the server gracefully.
size_t active_associations() const noexcept
Get number of active associations.
std::mutex handlers_mutex_
Handler mutex (protects handlers_ map)
bool is_access_control_enabled() const noexcept
Check if access control is enabled.
void on_disconnection(std::string_view session_id)
Handle disconnection notification.
Result< std::monostate > start()
Start the server.
void set_access_control_enabled(bool enabled)
Enable or disable access control enforcement.
std::mutex shutdown_mutex_
Shutdown mutex.
std::atomic< bool > access_control_enabled_
Whether access control is enabled.
std::chrono::milliseconds duration
std::vector< std::string > supported_sop_classes() const
Get list of supported SOP Class UIDs.
std::shared_ptr< security::access_control_manager > get_access_control() const noexcept
Get the access control manager.
void on_connection(std::shared_ptr< kcenon::network::interfaces::i_session > session)
Handle new connection from server.
server_statistics stats_
Server statistics.
std::condition_variable shutdown_cv_
Shutdown condition variable.
DICOM server implementation using network_system's messaging_server.
constexpr size_t AE_TITLE_LENGTH
AE Title length (fixed 16 characters, space-padded)
std::shared_ptr< scp_service > scp_service_ptr
Shared pointer type for SCP services.
kcenon::common::error_info error_info
Error information type.
size_t max_associations
Maximum concurrent associations (0 = unlimited)
std::chrono::seconds idle_timeout
Idle timeout for associations (0 = no timeout)
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)
Statistics for server monitoring.
std::chrono::steady_clock::time_point last_activity
Time of last activity.
uint64_t total_associations
Total associations since server start.
uint64_t messages_processed
Total DIMSE messages processed.
size_t active_associations
Currently active associations.
uint64_t bytes_sent
Total bytes sent.
std::chrono::steady_clock::time_point start_time
Server start time.
uint64_t bytes_received
Total bytes received.
uint64_t rejected_associations
Total associations rejected due to limit.