42void print_usage(
const char* program) {
43 std::cout <<
"Usage: " << program <<
" [config_file]\n\n";
44 std::cout <<
"Arguments:\n";
45 std::cout <<
" config_file Path to YAML configuration file\n";
46 std::cout <<
" (default: config/pacs_config.yaml)\n\n";
47 std::cout <<
"Examples:\n";
48 std::cout <<
" " << program <<
"\n";
49 std::cout <<
" " << program <<
" /etc/pacs/production.yaml\n";
50 std::cout <<
" " << program <<
" config/development.yaml\n";
61 return "HIPAA Safe Harbor";
63 return "Retain Longitudinal";
65 return "Retain Patient Characteristics";
67 return "GDPR Compliant";
71 return "Clean Descriptions";
79int main(
int argc,
char* argv[]) {
83 if (argc > 1 && (std::string(argv[1]) ==
"-h" || std::string(argv[1]) ==
"--help")) {
99 std::filesystem::path config_path = argc > 1
101 :
"config/pacs_config.yaml";
103 std::cout <<
"Configuration file: " << config_path <<
"\n\n";
107 if (std::filesystem::exists(config_path)) {
108 std::cout <<
"Loading configuration from file...\n";
111 if (!result.is_ok()) {
112 print_error(
"Failed to load configuration: " + result.error().message);
116 config = result.value();
119 std::cout <<
"Configuration file not found, using defaults...\n";
127 {
"DICOM Port", std::to_string(config.
server.
port)},
172 std::cout <<
"Creating Production PACS instance...\n";
187 <<
" level returned " << evt.result_count
188 <<
" results (" << evt.duration.count() <<
"ms)\n";
192 const char* type_str =
"";
193 switch (evt.event_type) {
194 case events::association_event::type::opened:
197 case events::association_event::type::closed:
200 case events::association_event::type::rejected:
201 type_str =
"rejected";
206 <<
": " << evt.
calling_ae <<
" -> " << evt.called_ae <<
"\n";
212 << evt.
calling_ae <<
" attempted " << evt.operation
213 <<
" - " << evt.reason <<
"\n";
216 print_success(
"Part 2 complete - Production PACS initialized!");
240 std::cout <<
"╔══════════════════════════════════════════════════════════════════╗\n";
241 std::cout <<
"║ Test Commands ║\n";
242 std::cout <<
"╠══════════════════════════════════════════════════════════════════╣\n";
243 std::cout <<
"║ DICOM Connectivity: ║\n";
251 std::cout <<
"║ ║\n";
252 std::cout <<
"║ Store Image: ║\n";
254 <<
" localhost " << config.
server.
port <<
" image.dcm";
262 std::cout <<
"║ ║\n";
263 std::cout <<
"║ REST API: ║\n";
264 std::cout <<
"║ Health: curl http://localhost:"
266 for (
size_t i = std::to_string(config.
rest_api.
port).length(); i < 9; ++i) {
270 std::cout <<
"║ Metrics: curl http://localhost:"
272 for (
size_t i = std::to_string(config.
rest_api.
port).length(); i < 8; ++i) {
279 std::cout <<
"║ ║\n";
280 std::cout <<
"║ TLS Connection: ║\n";
281 std::cout <<
"║ echoscu --tls --add-cert-file ca.pem -aec " << config.
server.
ae_title;
286 std::cout <<
"║ localhost " << config.
server.
port;
287 for (
size_t i = std::to_string(config.
server.
port).length(); i < 47; ++i) {
293 std::cout <<
"║ ║\n";
294 std::cout <<
"║ Press Ctrl+C to stop ║\n";
295 std::cout <<
"╚══════════════════════════════════════════════════════════════════╝\n\n";
300 <<
"Graceful shutdown initiated..."
314 const auto& stats =
pacs.statistics();
315 auto uptime = stats.uptime();
316 auto hours = std::chrono::duration_cast<std::chrono::hours>(uptime);
317 auto minutes = std::chrono::duration_cast<std::chrono::minutes>(uptime) % 60;
318 auto seconds = uptime.count() % 60;
320 std::ostringstream uptime_str;
321 uptime_str << hours.count() <<
"h " << minutes.count() <<
"m " << seconds <<
"s";
324 {
"Uptime", uptime_str.str()},
325 {
"Images Stored", std::to_string(stats.images_stored.load())},
326 {
"Images Anonymized", std::to_string(stats.images_anonymized.load())},
327 {
"Queries Executed", std::to_string(stats.queries_executed.load())},
328 {
"Access Denied", std::to_string(stats.access_denied_count.load())},
329 {
"REST Requests", std::to_string(stats.rest_requests.load())}
333 pacs.export_statistics(
"statistics.json");
334 print_info(
"Statistics exported to statistics.json");
338 "Congratulations! You have learned:",
340 "1. Configuration Management - YAML-based configuration",
341 "2. TLS Security - Secure DICOM connections",
342 "3. Access Control - Role-based permissions (RBAC)",
343 "4. Anonymization - De-identification profiles",
344 "5. REST API - Web-based PACS access",
345 "6. Health Monitoring - Service health checks",
346 "7. Event Architecture - Decoupled event handling",
348 "This Production PACS demonstrates enterprise-ready patterns",
349 "for building secure, scalable DICOM infrastructure.",
352 " - DICOM PS3.15: Security and System Management Profiles",
353 " - HIPAA Technical Safeguards",
354 " - GDPR Requirements for Medical Imaging"
static auto create_default() -> production_config
Create a default configuration.
static auto load(const std::filesystem::path &path) -> kcenon::common::Result< production_config >
Load configuration from a YAML file.
void wait()
Block until shutdown signal is received.
YAML configuration loader for Production PACS.
Console output utilities for developer samples.
constexpr const char * reset
Reset all formatting.
constexpr const char * red
constexpr const char * blue
constexpr const char * yellow
constexpr const char * green
constexpr const char * cyan
void print_error(std::string_view message)
Print an error message.
void print_table(std::string_view title, const std::vector< std::pair< std::string, std::string > > &rows)
Print a key-value table.
void print_header(std::string_view title)
Print a header/title with decorative border.
void print_info(std::string_view message)
Print an informational message.
void print_success(std::string_view message)
Print a success message.
void print_section(std::string_view title)
Print a section header.
void print_box(const std::vector< std::string > &lines)
Print text in a decorative box.
anonymization_profile
DICOM de-identification profiles based on PS3.15 Annex E.
@ clean_pixel
Clean Pixel Data - Remove burned-in annotations.
@ hipaa_safe_harbor
HIPAA Safe Harbor - 18 identifier removal.
@ gdpr_compliant
GDPR Compliant - European data protection.
@ retain_longitudinal
Retain Longitudinal - Preserve temporal relationships.
@ retain_patient_characteristics
Retain Patient Characteristics.
@ clean_descriptions
Clean Descriptions - Sanitize text fields.
@ basic
Basic Profile - Remove direct identifiers.
Production-grade PACS with enterprise features.
Cross-platform signal handling for graceful shutdown.
std::string default_role
Default role for unknown AE titles.
bool enabled
Enable access control enforcement.
security::anonymization_profile profile
Anonymization profile to use.
bool auto_anonymize
Automatically anonymize incoming images.
std::filesystem::path path
Path to SQLite database file.
Event fired when access is denied.
Event fired for association lifecycle events.
Event fired when an image is received and stored.
std::string sop_instance_uid
Event fired when a query is executed.
std::chrono::seconds health_check_interval
Interval between health checks (seconds)
bool metrics_enabled
Enable Prometheus-style metrics endpoint.
Complete production PACS configuration.
monitoring_config monitoring
Monitoring settings.
storage_config storage
Storage settings.
rest_api_config rest_api
REST API settings.
security_config security
Security settings.
server_config server
DICOM server settings.
std::uint16_t port
HTTP port for REST API.
bool cors_enabled
Enable CORS for web clients.
bool enabled
Enable REST API server.
access_control_config access_control
Access control settings.
anonymization_config anonymization
Anonymization settings.
std::uint16_t port
TCP port for DICOM connections.
tls_config tls
TLS configuration.
std::size_t max_associations
Maximum concurrent associations.
std::chrono::seconds idle_timeout
Idle timeout for associations (seconds)
std::string ae_title
Application Entity title (max 16 characters)
std::string duplicate_policy
Duplicate handling: "reject", "replace", "rename".
std::string naming_scheme
Naming scheme: "uid_flat", "uid_hierarchical", "date_based".
database_config database
Database configuration.
std::filesystem::path root_path
Root path for DICOM file storage.
bool enabled
Enable TLS for DICOM connections.