PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
network_adapter.cpp
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
14
15#include <kcenon/network/facade/tcp_facade.h>
16
17#include <filesystem>
18#include <future>
19#include <stdexcept>
20
22
23// =============================================================================
24// Server Creation
25// =============================================================================
26
27std::unique_ptr<network::dicom_server>
29 const tls_config& tls_cfg) {
30 // Validate configuration
31 if (config.ae_title.empty()) {
32 return nullptr;
33 }
34
35 if (config.port == 0) {
36 return nullptr;
37 }
38
39 // Validate TLS configuration if enabled
40 if (tls_cfg.enabled && !tls_cfg.is_valid()) {
41 return nullptr;
42 }
43
44 // Create DICOM server with the provided configuration
45 // The dicom_server internally uses network_system for TCP operations
46 return std::make_unique<network::dicom_server>(config);
47}
48
49// =============================================================================
50// Client Connection
51// =============================================================================
52
55 // Validate configuration
56 if (config.host.empty()) {
57 return Result<session_ptr>(error_info("Connection failed: empty host"));
58 }
59
60 if (config.port == 0) {
61 return Result<session_ptr>(error_info("Connection failed: invalid port"));
62 }
63
64 // Validate TLS configuration if enabled
65 if (config.tls.enabled && !config.tls.is_valid()) {
66 return Result<session_ptr>(error_info("Connection failed: invalid TLS configuration"));
67 }
68
69 try {
70 // Create TCP client via tcp_facade
71 kcenon::network::facade::tcp_facade facade;
72 kcenon::network::facade::tcp_facade::client_config client_cfg;
73 client_cfg.host = config.host;
74 client_cfg.port = config.port;
75 client_cfg.client_id = "pacs_client";
76 client_cfg.timeout = config.timeout;
77 client_cfg.use_ssl = config.tls.enabled;
78 if (config.tls.enabled && !config.tls.ca_path.empty()) {
79 client_cfg.ca_cert_path = config.tls.ca_path.string();
80 }
81 client_cfg.verify_certificate = config.tls.verify_peer;
82
83 auto client_result = compat::factory_result(facade.create_client(client_cfg));
84 if (client_result.is_err()) {
85 return Result<session_ptr>(error_info("Connection failed: unable to create client"));
86 }
87 auto client = std::move(client_result.value());
88
89 // Set up promise/future for synchronous connection
90 std::promise<std::error_code> connect_promise;
91 auto connect_future = connect_promise.get_future();
92
93 // Use deprecated callbacks for synchronous connect pattern
94 // (observer pattern would be preferred for long-lived clients)
95#if defined(__clang__) || defined(__GNUC__)
96#pragma GCC diagnostic push
97#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
98#endif
99 client->set_connected_callback([&connect_promise]() {
100 connect_promise.set_value(std::error_code{});
101 });
102
103 client->set_error_callback([&connect_promise](std::error_code ec) {
104 try {
105 connect_promise.set_value(ec);
106 } catch (const std::future_error&) {
107 // Promise already satisfied
108 }
109 });
110#if defined(__clang__) || defined(__GNUC__)
111#pragma GCC diagnostic pop
112#endif
113
114 // Start connection
115 auto start_result = client->start(config.host, config.port);
116 if (start_result.is_err()) {
117 return Result<session_ptr>(error_info("Connection failed: unable to start client"));
118 }
119
120 // Wait for connection with timeout
121 auto status = connect_future.wait_for(config.timeout);
122 if (status == std::future_status::timeout) {
123 (void)client->stop();
124 return Result<session_ptr>(error_info("Connection failed: timeout"));
125 }
126
127 // Check connection result
128 auto ec = connect_future.get();
129 if (ec) {
130 (void)client->stop();
131 return Result<session_ptr>(error_info("Connection failed: " + ec.message()));
132 }
133
134 // Connection successful but client-based session wrapping
135 // is not yet fully implemented for the public API
136 return Result<session_ptr>(error_info("Connection not yet fully implemented"));
137
138 } catch (const std::exception& e) {
139 return Result<session_ptr>(
140 error_info(std::string("Connection failed: ") + e.what()));
141 }
142}
143
145network_adapter::connect(const std::string& host,
146 uint16_t port,
147 std::chrono::milliseconds timeout) {
148 connection_config config{host, port};
149 config.timeout = timeout;
150 return connect(config);
151}
152
153// =============================================================================
154// TLS Configuration
155// =============================================================================
156
159 if (!config.enabled) {
160 return Result<std::monostate>(std::monostate{});
161 }
162
163 // Validate certificate path
164 if (!std::filesystem::exists(config.cert_path)) {
166 "TLS configuration error: certificate file not found: " +
167 config.cert_path.string()));
168 }
169
170 // Validate key path
171 if (!std::filesystem::exists(config.key_path)) {
173 "TLS configuration error: key file not found: " +
174 config.key_path.string()));
175 }
176
177 // Validate CA path if specified
178 if (!config.ca_path.empty() && !std::filesystem::exists(config.ca_path)) {
180 "TLS configuration error: CA certificate file not found: " +
181 config.ca_path.string()));
182 }
183
184 // TLS version validation
188 "TLS configuration error: unsupported TLS version"));
189 }
190
191 return Result<std::monostate>(std::monostate{});
192}
193
194// =============================================================================
195// Session Wrapping
196// =============================================================================
197
200 std::shared_ptr<network_system::session::messaging_session> session) {
201 if (!session) {
202 return nullptr;
203 }
204 return std::make_shared<dicom_session>(std::move(session));
205}
206
209 std::shared_ptr<network_system::session::secure_session> session) {
210 if (!session) {
211 return nullptr;
212 }
213 return std::make_shared<dicom_session>(std::move(session));
214}
215
218 std::shared_ptr<kcenon::network::interfaces::i_session> session) {
219 if (!session) {
220 return nullptr;
221 }
222 return std::make_shared<dicom_session>(std::move(session));
223}
224
225} // namespace kcenon::pacs::integration
Simple result type for error handling when common_system is unavailable.
static Result< std::monostate > configure_tls(const tls_config &config)
Configure TLS settings.
static std::unique_ptr< network::dicom_server > create_server(const network::server_config &config, const tls_config &tls_cfg={})
Create a DICOM server using network_system.
std::shared_ptr< dicom_session > session_ptr
Session pointer type.
static session_ptr wrap_session(std::shared_ptr< network_system::session::messaging_session > session)
Wrap a network_system session for DICOM communication.
static Result< session_ptr > connect(const connection_config &config)
Connect to a remote DICOM peer.
Multi-threaded DICOM server for handling multiple associations.
DICOM session wrapper for network_system sessions.
auto factory_result(kcenon::common::Result< T > result) -> kcenon::common::Result< T >
kcenon::common::Result< T > Result
Result type alias for PACS operations.
Definition result.h:30
kcenon::common::error_info error_info
Error information type.
Definition result.h:40
Adapter for integrating network_system for DICOM protocol.
Configuration for client connections.
std::chrono::milliseconds timeout
Connection timeout.
tls_config tls
TLS configuration (optional)
Configuration for TLS/SSL secure transport.
bool enabled
Enable TLS for connections.
enum kcenon::pacs::integration::tls_config::tls_version min_version
bool is_valid() const noexcept
Check if TLS configuration is valid.
std::filesystem::path cert_path
Path to certificate file (PEM format)
std::filesystem::path ca_path
Path to CA certificate file for verification (optional)
bool verify_peer
Verify peer certificate.
std::filesystem::path key_path
Path to private key file (PEM format)
Configuration for DICOM server.
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)