PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
main.cpp File Reference

Level 4 Sample: Mini PACS - Complete DICOM server with all services. More...

#include "mini_pacs.h"
#include "console_utils.h"
#include "signal_handler.h"
#include <iostream>
#include <string>
Include dependency graph for main.cpp:

Go to the source code of this file.

Functions

int main (int argc, char *argv[])
 

Detailed Description

Level 4 Sample: Mini PACS - Complete DICOM server with all services.

This sample demonstrates a complete Mini PACS implementation integrating:

  • Verification SCP (C-ECHO) for connectivity testing
  • Storage SCP (C-STORE) for receiving images from modalities
  • Query SCP (C-FIND) at Patient/Study/Series/Image levels
  • Retrieve SCP (C-MOVE/C-GET) for image retrieval
  • Modality Worklist SCP (MWL C-FIND) for scheduled procedures
  • MPPS SCP (N-CREATE/N-SET) for procedure tracking

After completing this sample, you will understand:

  1. Service Integration - Combining multiple SCPs in one server
  2. Query/Retrieve (Q/R) - C-FIND and C-MOVE operations
  3. Modality Worklist - Scheduled procedure management
  4. MPPS - Modality Performed Procedure Step tracking
  5. Class-Based Design - Encapsulating PACS functionality
See also
DICOM PS3.4 - Storage, Query/Retrieve, Worklist, MPPS Service Classes

Definition in file main.cpp.

Function Documentation

◆ main()

int main ( int argc,
char * argv[] )

Definition at line 117 of file main.cpp.

117 {
118 print_header("Mini PACS - Level 4 Sample");
119
120 // ==========================================================================
121 // Part 1: Configuration
122 // ==========================================================================
123 // The Mini PACS integrates all core DICOM services:
124 // - Verification: Network connectivity testing (C-ECHO)
125 // - Storage: Image reception and archiving (C-STORE)
126 // - Query: Searching for patients/studies/series/images (C-FIND)
127 // - Retrieve: Sending images to destinations (C-MOVE/C-GET)
128 // - Worklist: Providing scheduled procedures to modalities (MWL)
129 // - MPPS: Tracking procedure progress from modalities
130
131 print_section("Part 1: Configuration");
132
133 std::cout << "Mini PACS provides a complete DICOM server with:\n";
134 std::cout << " - Verification SCP: C-ECHO for connectivity\n";
135 std::cout << " - Storage SCP: C-STORE for image reception\n";
136 std::cout << " - Query SCP: C-FIND at all levels\n";
137 std::cout << " - Retrieve SCP: C-MOVE/C-GET for retrieval\n";
138 std::cout << " - Worklist SCP: MWL for scheduled procedures\n";
139 std::cout << " - MPPS SCP: N-CREATE/N-SET for tracking\n\n";
140
141 // Parse command line arguments
142 mini_pacs_config config;
143 config.ae_title = "MINI_PACS";
144 config.port = argc > 1 ? static_cast<uint16_t>(std::stoi(argv[1])) : 11112;
145 config.storage_path = "./pacs_data";
146 config.max_associations = 50;
147 config.enable_worklist = true;
148 config.enable_mpps = true;
149 config.verbose_logging = true;
150
151 print_table("Mini PACS Configuration", {
152 {"AE Title", config.ae_title},
153 {"Port", std::to_string(config.port)},
154 {"Storage Path", config.storage_path.string()},
155 {"Max Associations", std::to_string(config.max_associations)},
156 {"Worklist Enabled", config.enable_worklist ? "Yes" : "No"},
157 {"MPPS Enabled", config.enable_mpps ? "Yes" : "No"}
158 });
159
160 print_success("Part 1 complete - Configuration ready!");
161
162 // ==========================================================================
163 // Part 2: Create and Start PACS
164 // ==========================================================================
165 // The mini_pacs class encapsulates all services and manages:
166 // - File storage with hierarchical organization
167 // - SQLite index database for fast queries
168 // - All SCP services with proper handler registration
169 // - Event handlers for association lifecycle
170
171 print_section("Part 2: Create and Start PACS");
172
173 mini_pacs pacs(config);
174
175 // Add sample worklist items for testing
176 add_sample_worklist_items(pacs);
177
178 std::cout << "Added " << pacs.get_worklist_items().size()
179 << " sample worklist items for MWL testing.\n\n";
180
181 if (!pacs.start()) {
182 print_error("Failed to start Mini PACS");
183 return 1;
184 }
185
186 print_success("Part 2 complete - Mini PACS started!");
187
188 // ==========================================================================
189 // Part 3: Display Service Information
190 // ==========================================================================
191
192 print_section("Part 3: Running Server");
193
194 std::cout << "\n";
195 std::cout << "╔══════════════════════════════════════════════════════════════╗\n";
196 std::cout << "║ Mini PACS Server Started ║\n";
197 std::cout << "╠══════════════════════════════════════════════════════════════╣\n";
198 std::cout << "║ AE Title: " << config.ae_title;
199 for (size_t i = config.ae_title.length(); i < 48; ++i) std::cout << " ";
200 std::cout << "║\n";
201 std::cout << "║ Port: " << config.port;
202 std::string port_str = std::to_string(config.port);
203 for (size_t i = port_str.length(); i < 48; ++i) std::cout << " ";
204 std::cout << "║\n";
205 std::cout << "╠══════════════════════════════════════════════════════════════╣\n";
206 std::cout << "║ Services: ║\n";
207 std::cout << "║ [x] Verification (C-ECHO) ║\n";
208 std::cout << "║ [x] Storage (C-STORE) ║\n";
209 std::cout << "║ [x] Query (C-FIND Patient/Study/Series/Image) ║\n";
210 std::cout << "║ [x] Retrieve (C-MOVE/C-GET) ║\n";
211 std::cout << "║ [x] Modality Worklist (MWL C-FIND) ║\n";
212 std::cout << "║ [x] MPPS (N-CREATE/N-SET) ║\n";
213 std::cout << "╠══════════════════════════════════════════════════════════════╣\n";
214 std::cout << "║ Test Commands: ║\n";
215 std::cout << "║ ║\n";
216 std::cout << "║ Connectivity: ║\n";
217 std::cout << "║ echoscu -aec MINI_PACS localhost " << config.port;
218 for (size_t i = port_str.length(); i < 22; ++i) std::cout << " ";
219 std::cout << "║\n";
220 std::cout << "║ ║\n";
221 std::cout << "║ Store Images: ║\n";
222 std::cout << "║ storescu -aec MINI_PACS localhost " << config.port << " *.dcm";
223 for (size_t i = port_str.length(); i < 16; ++i) std::cout << " ";
224 std::cout << "║\n";
225 std::cout << "║ ║\n";
226 std::cout << "║ Query Patient Level: ║\n";
227 std::cout << "║ findscu -aec MINI_PACS -P \\ ║\n";
228 std::cout << "║ -k QueryRetrieveLevel=PATIENT \\ ║\n";
229 std::cout << "║ -k PatientName=\"*\" localhost " << config.port;
230 for (size_t i = port_str.length(); i < 27; ++i) std::cout << " ";
231 std::cout << "║\n";
232 std::cout << "║ ║\n";
233 std::cout << "║ Query Study Level: ║\n";
234 std::cout << "║ findscu -aec MINI_PACS -S \\ ║\n";
235 std::cout << "║ -k QueryRetrieveLevel=STUDY \\ ║\n";
236 std::cout << "║ -k StudyDate=\"\" localhost " << config.port;
237 for (size_t i = port_str.length(); i < 29; ++i) std::cout << " ";
238 std::cout << "║\n";
239 std::cout << "║ ║\n";
240 std::cout << "║ Query Worklist: ║\n";
241 std::cout << "║ findscu -aec MINI_PACS -W \\ ║\n";
242 std::cout << "║ -k ScheduledProcedureStepStartDate=\"\" \\ ║\n";
243 std::cout << "║ -k Modality=\"\" localhost " << config.port;
244 for (size_t i = port_str.length(); i < 30; ++i) std::cout << " ";
245 std::cout << "║\n";
246 std::cout << "║ ║\n";
247 std::cout << "║ Retrieve Study: ║\n";
248 std::cout << "║ movescu -aec MINI_PACS -aem DEST \\ ║\n";
249 std::cout << "║ -k QueryRetrieveLevel=STUDY \\ ║\n";
250 std::cout << "║ -k StudyInstanceUID=\"...\" localhost " << config.port;
251 for (size_t i = port_str.length(); i < 20; ++i) std::cout << " ";
252 std::cout << "║\n";
253 std::cout << "╠══════════════════════════════════════════════════════════════╣\n";
254 std::cout << "║ Worklist Items: 3 scheduled procedures ║\n";
255 std::cout << "║ ║\n";
256 std::cout << "║ Press Ctrl+C to stop ║\n";
257 std::cout << "╚══════════════════════════════════════════════════════════════╝\n\n";
258
259 // Install signal handler for graceful shutdown
260 scoped_signal_handler sig_handler([&pacs]() {
261 std::cout << "\n" << colors::yellow
262 << "Shutdown signal received..."
263 << colors::reset << "\n";
264 pacs.stop();
265 });
266
267 // Wait for shutdown
268 sig_handler.wait();
269
270 // ==========================================================================
271 // Part 4: Statistics and Cleanup
272 // ==========================================================================
273
274 print_section("Final Statistics");
275
276 const auto& stats = pacs.statistics();
277
278 print_table("Association Statistics", {
279 {"Total Associations", std::to_string(stats.associations_total.load())},
280 {"Active Associations", std::to_string(stats.associations_active.load())}
281 });
282
283 print_table("Operation Statistics", {
284 {"C-ECHO Count", std::to_string(stats.c_echo_count.load())},
285 {"C-STORE Count", std::to_string(stats.c_store_count.load())},
286 {"C-FIND Count", std::to_string(stats.c_find_count.load())},
287 {"C-MOVE Count", std::to_string(stats.c_move_count.load())},
288 {"C-GET Count", std::to_string(stats.c_get_count.load())},
289 {"MWL Queries", std::to_string(stats.mwl_count.load())},
290 {"MPPS N-CREATE", std::to_string(stats.mpps_create_count.load())},
291 {"MPPS N-SET", std::to_string(stats.mpps_set_count.load())}
292 });
293
294 print_table("Data Statistics", {
295 {"Bytes Received", std::to_string(stats.bytes_received.load()) + " bytes"}
296 });
297
298 // Summary
299 print_box({
300 "Congratulations! You have learned:",
301 "",
302 "1. Service Integration - Combining multiple SCPs",
303 "2. Query/Retrieve - C-FIND and C-MOVE operations",
304 "3. Modality Worklist - Scheduled procedure queries",
305 "4. MPPS - Procedure progress tracking",
306 "5. Class-Based Design - Encapsulating PACS functionality",
307 "",
308 "Query the database:",
309 " sqlite3 ./pacs_data/index.db \"SELECT * FROM patients;\"",
310 " sqlite3 ./pacs_data/index.db \"SELECT * FROM studies;\"",
311 "",
312 "Next step: Level 5 - Production PACS (TLS, RBAC, REST API)"
313 });
314
315 print_success("Mini PACS terminated successfully.");
316
317 return 0;
318}
void wait()
Block until shutdown signal is received.
constexpr const char * reset
Reset all formatting.
constexpr const char * yellow
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_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.
Configuration options for Mini PACS.
Definition mini_pacs.h:51
uint16_t port
TCP port to listen on.
Definition mini_pacs.h:56
std::string ae_title
Application Entity title (max 16 characters)
Definition mini_pacs.h:53
bool verbose_logging
Enable verbose logging.
Definition mini_pacs.h:71
bool enable_mpps
Enable MPPS service.
Definition mini_pacs.h:68
std::filesystem::path storage_path
Root path for DICOM file storage.
Definition mini_pacs.h:59
size_t max_associations
Maximum concurrent associations.
Definition mini_pacs.h:62
bool enable_worklist
Enable Modality Worklist service.
Definition mini_pacs.h:65

References kcenon::pacs::samples::mini_pacs_config::ae_title, kcenon::pacs::samples::mini_pacs_config::enable_mpps, kcenon::pacs::samples::mini_pacs_config::enable_worklist, kcenon::pacs::samples::mini_pacs_config::max_associations, kcenon::pacs::samples::mini_pacs_config::port, kcenon::pacs::samples::print_box(), kcenon::pacs::samples::print_error(), kcenon::pacs::samples::print_header(), kcenon::pacs::samples::print_section(), kcenon::pacs::samples::print_success(), kcenon::pacs::samples::print_table(), kcenon::pacs::samples::colors::reset, kcenon::pacs::samples::mini_pacs_config::storage_path, kcenon::pacs::samples::mini_pacs_config::verbose_logging, kcenon::pacs::samples::scoped_signal_handler::wait(), and kcenon::pacs::samples::colors::yellow.

Here is the call graph for this function: