117 {
119
120
121
122
123
124
125
126
127
128
129
130
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
144 config.
port = argc > 1 ?
static_cast<uint16_t
>(std::stoi(argv[1])) : 11112;
150
153 {
"Port", std::to_string(config.
port)},
157 {
"MPPS Enabled", config.
enable_mpps ?
"Yes" :
"No"}
158 });
159
161
162
163
164
165
166
167
168
169
170
172
174
175
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
183 return 1;
184 }
185
187
188
189
190
191
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
262 << "Shutdown signal received..."
265 });
266
267
269
270
271
272
273
275
276 const auto& stats =
pacs.statistics();
277
279 {"Total Associations", std::to_string(stats.associations_total.load())},
280 {"Active Associations", std::to_string(stats.associations_active.load())}
281 });
282
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
295 {"Bytes Received", std::to_string(stats.bytes_received.load()) + " bytes"}
296 });
297
298
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
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.
uint16_t port
TCP port to listen on.
std::string ae_title
Application Entity title (max 16 characters)
bool verbose_logging
Enable verbose logging.
bool enable_mpps
Enable MPPS service.
std::filesystem::path storage_path
Root path for DICOM file storage.
size_t max_associations
Maximum concurrent associations.
bool enable_worklist
Enable Modality Worklist service.