PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::integration_test::dcmtk_tool Class Reference

Wrapper class for DCMTK command-line tools. More...

#include <dcmtk_tool.h>

Collaboration diagram for kcenon::pacs::integration_test::dcmtk_tool:
Collaboration graph

Static Public Member Functions

static bool is_available ()
 Check if DCMTK is available on the system.
 
static std::optional< std::string > version ()
 Get DCMTK version string.
 
static dcmtk_result echoscu (const std::string &host, uint16_t port, const std::string &called_ae, const std::string &calling_ae="ECHOSCU", std::chrono::seconds timeout=std::chrono::seconds{30})
 Run C-ECHO (echoscu) client.
 
static dcmtk_result storescu (const std::string &host, uint16_t port, const std::string &called_ae, const std::vector< std::filesystem::path > &files, const std::string &calling_ae="STORESCU", std::chrono::seconds timeout=std::chrono::seconds{60})
 Run C-STORE (storescu) client.
 
static dcmtk_result findscu (const std::string &host, uint16_t port, const std::string &called_ae, const std::string &query_level, const std::vector< std::pair< std::string, std::string > > &keys, const std::string &calling_ae="FINDSCU", std::chrono::seconds timeout=std::chrono::seconds{30})
 Run C-FIND (findscu) client.
 
static dcmtk_result movescu (const std::string &host, uint16_t port, const std::string &called_ae, const std::string &dest_ae, const std::string &query_level, const std::vector< std::pair< std::string, std::string > > &keys, const std::string &calling_ae="MOVESCU", std::chrono::seconds timeout=std::chrono::seconds{120})
 Run C-MOVE (movescu) client.
 
static std::chrono::seconds default_scp_startup_timeout ()
 Default startup timeout for DCMTK SCP servers.
 
static background_process_guard storescp (uint16_t port, const std::string &ae_title, const std::filesystem::path &output_dir, std::chrono::seconds startup_timeout=default_scp_startup_timeout())
 Start C-STORE SCP (storescp) server.
 
static background_process_guard echoscp (uint16_t port, const std::string &ae_title, std::chrono::seconds startup_timeout=default_scp_startup_timeout())
 Start C-ECHO SCP (echoscp) server.
 

Static Private Member Functions

static std::string find_tool_path (const std::string &tool_name)
 Find the full path to a DCMTK tool.
 
static dcmtk_result run_tool (const std::string &tool_name, const std::vector< std::string > &args, std::chrono::seconds timeout)
 Run a DCMTK tool and capture output.
 
static process_launcher::pid_type start_tool_background (const std::string &tool_name, const std::vector< std::string > &args)
 Start a DCMTK tool in background.
 

Detailed Description

Wrapper class for DCMTK command-line tools.

Provides static methods to invoke DCMTK CLI tools with standardized timeout handling, error checking, and output parsing.

Definition at line 60 of file dcmtk_tool.h.

Member Function Documentation

◆ default_scp_startup_timeout()

static std::chrono::seconds kcenon::pacs::integration_test::dcmtk_tool::default_scp_startup_timeout ( )
inlinestatic

Default startup timeout for DCMTK SCP servers.

Uses adaptive timeout based on environment:

  • Normal: 15s for quick responsiveness
  • CI: 60s to account for slower VM/container environments
Returns
Appropriate startup timeout for current environment

Definition at line 259 of file dcmtk_tool.h.

259 {
260 return is_ci_environment()
261 ? std::chrono::seconds{60}
262 : std::chrono::seconds{15};
263 }
bool is_ci_environment()
Check if running in a CI environment.

References kcenon::pacs::integration_test::is_ci_environment().

Here is the call graph for this function:

◆ echoscp()

static background_process_guard kcenon::pacs::integration_test::dcmtk_tool::echoscp ( uint16_t port,
const std::string & ae_title,
std::chrono::seconds startup_timeout = default_scp_startup_timeout() )
inlinestatic

Start C-ECHO SCP (echoscp) server.

Parameters
portPort to listen on
ae_titleAE title for server
startup_timeoutMaximum time to wait for server to start (default: adaptive based on environment)
Returns
Background process guard for the server

Definition at line 311 of file dcmtk_tool.h.

314 {
315
316 std::vector<std::string> args = {
317 "-aet", ae_title,
318 std::to_string(port)
319 };
320
321 auto pid = start_tool_background("echoscp", args);
322 background_process_guard guard(pid);
323
324 if (pid > 0) {
325 if (!process_launcher::wait_for_port(port, startup_timeout)) {
326 guard.stop();
327 return background_process_guard(process_launcher::invalid_pid);
328 }
329 }
330
331 return guard;
332 }
static process_launcher::pid_type start_tool_background(const std::string &tool_name, const std::vector< std::string > &args)
Start a DCMTK tool in background.
Definition dcmtk_tool.h:396
static constexpr pid_type invalid_pid
Invalid PID constant (0 is reserved on both platforms)
static bool wait_for_port(uint16_t port, std::chrono::seconds timeout=std::chrono::seconds{10}, const std::string &host="127.0.0.1")
Wait for a port to be listening.

References kcenon::pacs::integration_test::process_launcher::invalid_pid, start_tool_background(), kcenon::pacs::integration_test::background_process_guard::stop(), and kcenon::pacs::integration_test::process_launcher::wait_for_port().

Referenced by TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ echoscu()

static dcmtk_result kcenon::pacs::integration_test::dcmtk_tool::echoscu ( const std::string & host,
uint16_t port,
const std::string & called_ae,
const std::string & calling_ae = "ECHOSCU",
std::chrono::seconds timeout = std::chrono::seconds{30} )
inlinestatic

Run C-ECHO (echoscu) client.

Parameters
hostServer host address
portServer port
called_aeCalled AE title
calling_aeCalling AE title (default: ECHOSCU)
timeoutOperation timeout
Returns
DCMTK execution result

Definition at line 111 of file dcmtk_tool.h.

116 {30}) {
117
118 std::vector<std::string> args = {
119 "-aec", called_ae,
120 "-aet", calling_ae,
121 host,
122 std::to_string(port)
123 };
124
125 return run_tool("echoscu", args, timeout);
126 }
static dcmtk_result run_tool(const std::string &tool_name, const std::vector< std::string > &args, std::chrono::seconds timeout)
Run a DCMTK tool and capture output.
Definition dcmtk_tool.h:371

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the caller graph for this function:

◆ find_tool_path()

static std::string kcenon::pacs::integration_test::dcmtk_tool::find_tool_path ( const std::string & tool_name)
inlinestaticprivate

Find the full path to a DCMTK tool.

Parameters
tool_nameName of the tool (e.g., "echoscu")
Returns
Full path to the tool, or just the tool name if not found

Definition at line 344 of file dcmtk_tool.h.

344 {
345 // First, check common installation paths
346 std::vector<std::string> search_paths = {
347 "/usr/local/bin",
348 "/usr/bin",
349 "/opt/homebrew/bin", // macOS Homebrew (Apple Silicon)
350 "/opt/local/bin" // MacPorts
351 };
352
353 for (const auto& path : search_paths) {
354 std::filesystem::path full_path = std::filesystem::path(path) / tool_name;
355 if (std::filesystem::exists(full_path)) {
356 return full_path.string();
357 }
358 }
359
360 // Fall back to relying on PATH
361 return tool_name;
362 }

Referenced by run_tool(), and start_tool_background().

Here is the caller graph for this function:

◆ findscu()

static dcmtk_result kcenon::pacs::integration_test::dcmtk_tool::findscu ( const std::string & host,
uint16_t port,
const std::string & called_ae,
const std::string & query_level,
const std::vector< std::pair< std::string, std::string > > & keys,
const std::string & calling_ae = "FINDSCU",
std::chrono::seconds timeout = std::chrono::seconds{30} )
inlinestatic

Run C-FIND (findscu) client.

Parameters
hostServer host address
portServer port
called_aeCalled AE title
query_levelQuery/Retrieve level (PATIENT, STUDY, SERIES, IMAGE)
keysQuery keys as tag-value pairs (e.g., {"PatientID", "TEST001"})
calling_aeCalling AE title (default: FINDSCU)
timeoutOperation timeout
Returns
DCMTK execution result

Definition at line 171 of file dcmtk_tool.h.

178 {30}) {
179
180 std::vector<std::string> args = {
181 "-aec", called_ae,
182 "-aet", calling_ae,
183 "-W" // Use worklist model for MWL or study root for others
184 };
185
186 // Set query level
187 if (query_level == "PATIENT" || query_level == "STUDY" ||
188 query_level == "SERIES" || query_level == "IMAGE") {
189 args.push_back("-k");
190 args.push_back("QueryRetrieveLevel=" + query_level);
191 }
192
193 // Add query keys
194 for (const auto& [key, value] : keys) {
195 args.push_back("-k");
196 args.push_back(key + "=" + value);
197 }
198
199 args.push_back(host);
200 args.push_back(std::to_string(port));
201
202 return run_tool("findscu", args, timeout);
203 }

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the caller graph for this function:

◆ is_available()

static bool kcenon::pacs::integration_test::dcmtk_tool::is_available ( )
inlinestatic

Check if DCMTK is available on the system.

Returns
true if echoscu command is found in PATH

Definition at line 70 of file dcmtk_tool.h.

70 {
71 auto result = run_tool("echoscu", {"--version"}, std::chrono::seconds{5});
72 return result.exit_code == 0;
73 }

References kcenon::pacs::integration_test::dcmtk_result::exit_code, and run_tool().

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ movescu()

static dcmtk_result kcenon::pacs::integration_test::dcmtk_tool::movescu ( const std::string & host,
uint16_t port,
const std::string & called_ae,
const std::string & dest_ae,
const std::string & query_level,
const std::vector< std::pair< std::string, std::string > > & keys,
const std::string & calling_ae = "MOVESCU",
std::chrono::seconds timeout = std::chrono::seconds{120} )
inlinestatic

Run C-MOVE (movescu) client.

Parameters
hostServer host address
portServer port
called_aeCalled AE title
dest_aeDestination AE title
query_levelQuery/Retrieve level
keysQuery keys as tag-value pairs
calling_aeCalling AE title (default: MOVESCU)
timeoutOperation timeout
Returns
DCMTK execution result

Definition at line 217 of file dcmtk_tool.h.

225 {120}) {
226
227 std::vector<std::string> args = {
228 "-aec", called_ae,
229 "-aet", calling_ae,
230 "-aem", dest_ae, // Move destination AE title
231 "-k", "QueryRetrieveLevel=" + query_level
232 };
233
234 // Add query keys
235 for (const auto& [key, value] : keys) {
236 args.push_back("-k");
237 args.push_back(key + "=" + value);
238 }
239
240 args.push_back(host);
241 args.push_back(std::to_string(port));
242
243 return run_tool("movescu", args, timeout);
244 }

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the caller graph for this function:

◆ run_tool()

static dcmtk_result kcenon::pacs::integration_test::dcmtk_tool::run_tool ( const std::string & tool_name,
const std::vector< std::string > & args,
std::chrono::seconds timeout )
inlinestaticprivate

Run a DCMTK tool and capture output.

Parameters
tool_nameName of the tool
argsCommand-line arguments
timeoutMaximum execution time
Returns
DCMTK execution result

Definition at line 371 of file dcmtk_tool.h.

374 {
375
376 dcmtk_result result;
377
378 std::string tool_path = find_tool_path(tool_name);
379 auto process_res = process_launcher::run(tool_path, args, timeout);
380
381 result.exit_code = process_res.exit_code;
382 result.stdout_output = std::move(process_res.stdout_output);
383 result.stderr_output = std::move(process_res.stderr_output);
384 result.duration = process_res.duration;
385 result.timed_out = process_res.timed_out;
386
387 return result;
388 }
static std::string find_tool_path(const std::string &tool_name)
Find the full path to a DCMTK tool.
Definition dcmtk_tool.h:344
static process_result run(const std::string &executable, const std::vector< std::string > &args={}, std::chrono::seconds timeout=std::chrono::seconds{30})
Run a process and wait for completion.

References kcenon::pacs::integration_test::dcmtk_result::duration, kcenon::pacs::integration_test::dcmtk_result::exit_code, find_tool_path(), kcenon::pacs::integration_test::process_launcher::run(), kcenon::pacs::integration_test::dcmtk_result::stderr_output, kcenon::pacs::integration_test::dcmtk_result::stdout_output, and kcenon::pacs::integration_test::dcmtk_result::timed_out.

Referenced by is_available(), and version().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ start_tool_background()

static process_launcher::pid_type kcenon::pacs::integration_test::dcmtk_tool::start_tool_background ( const std::string & tool_name,
const std::vector< std::string > & args )
inlinestaticprivate

Start a DCMTK tool in background.

Parameters
tool_nameName of the tool
argsCommand-line arguments
Returns
Process ID

Definition at line 396 of file dcmtk_tool.h.

398 {
399
400 std::string tool_path = find_tool_path(tool_name);
401 return process_launcher::start_background(tool_path, args);
402 }
static pid_type start_background(const std::string &executable, const std::vector< std::string > &args={})

References find_tool_path(), and kcenon::pacs::integration_test::process_launcher::start_background().

Referenced by echoscp(), and storescp().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ storescp()

static background_process_guard kcenon::pacs::integration_test::dcmtk_tool::storescp ( uint16_t port,
const std::string & ae_title,
const std::filesystem::path & output_dir,
std::chrono::seconds startup_timeout = default_scp_startup_timeout() )
inlinestatic

Start C-STORE SCP (storescp) server.

Parameters
portPort to listen on
ae_titleAE title for server
output_dirDirectory to store received files
startup_timeoutMaximum time to wait for server to start (default: adaptive based on environment)
Returns
Background process guard for the server

Definition at line 274 of file dcmtk_tool.h.

278 {
279
280 // Ensure output directory exists
281 std::filesystem::create_directories(output_dir);
282
283 std::vector<std::string> args = {
284 "-aet", ae_title,
285 "-od", output_dir.string(),
286 std::to_string(port)
287 };
288
289 auto pid = start_tool_background("storescp", args);
290 background_process_guard guard(pid);
291
292 // Wait for server to start accepting connections
293 if (pid > 0) {
294 if (!process_launcher::wait_for_port(port, startup_timeout)) {
295 guard.stop();
296 return background_process_guard(process_launcher::invalid_pid);
297 }
298 }
299
300 return guard;
301 }

References kcenon::pacs::integration_test::process_launcher::invalid_pid, start_tool_background(), kcenon::pacs::integration_test::background_process_guard::stop(), and kcenon::pacs::integration_test::process_launcher::wait_for_port().

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ storescu()

static dcmtk_result kcenon::pacs::integration_test::dcmtk_tool::storescu ( const std::string & host,
uint16_t port,
const std::string & called_ae,
const std::vector< std::filesystem::path > & files,
const std::string & calling_ae = "STORESCU",
std::chrono::seconds timeout = std::chrono::seconds{60} )
inlinestatic

Run C-STORE (storescu) client.

Parameters
hostServer host address
portServer port
called_aeCalled AE title
filesDICOM files to send
calling_aeCalling AE title (default: STORESCU)
timeoutOperation timeout
Returns
DCMTK execution result

Definition at line 138 of file dcmtk_tool.h.

144 {60}) {
145
146 std::vector<std::string> args = {
147 "-aec", called_ae,
148 "-aet", calling_ae,
149 host,
150 std::to_string(port)
151 };
152
153 for (const auto& file : files) {
154 args.push_back(file.string());
155 }
156
157 return run_tool("storescu", args, timeout);
158 }

Referenced by TEST_CASE(), TEST_CASE(), TEST_CASE(), TEST_CASE(), and TEST_CASE().

Here is the caller graph for this function:

◆ version()

static std::optional< std::string > kcenon::pacs::integration_test::dcmtk_tool::version ( )
inlinestatic

Get DCMTK version string.

Returns
Version string if available, nullopt otherwise

Definition at line 79 of file dcmtk_tool.h.

79 {
80 auto result = run_tool("echoscu", {"--version"}, std::chrono::seconds{5});
81 if (result.exit_code != 0) {
82 return std::nullopt;
83 }
84
85 // Parse version from output (first line usually contains version)
86 auto& output = result.stdout_output.empty()
87 ? result.stderr_output
88 : result.stdout_output;
89
90 std::istringstream stream(output);
91 std::string first_line;
92 if (std::getline(stream, first_line) && !first_line.empty()) {
93 return first_line;
94 }
95 return std::nullopt;
96 }
std::string stdout_output
Standard output.
Definition dcmtk_tool.h:38

References run_tool(), and kcenon::pacs::integration_test::dcmtk_result::stdout_output.

Referenced by TEST_CASE().

Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following file: