PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
console_utils.cpp
Go to the documentation of this file.
1
6#include "console_utils.h"
7
10
11#include <algorithm>
12#include <cstdlib>
13#include <iomanip>
14#include <iostream>
15#include <sstream>
16
17#ifdef _WIN32
18#include <io.h>
19#include <windows.h>
20#define isatty _isatty
21#define fileno _fileno
22#else
23#include <unistd.h>
24#endif
25
26namespace kcenon::pacs::samples {
27
28// ============================================================================
29// Static State
30// ============================================================================
31
32namespace {
33bool g_color_enabled = true;
34bool g_color_checked = false;
35} // namespace
36
37// ============================================================================
38// Color Support Detection
39// ============================================================================
40
41auto supports_color() noexcept -> bool {
42 // Check if stdout is a terminal
43 if (!isatty(fileno(stdout))) {
44 return false;
45 }
46
47#ifdef _WIN32
48 // Enable virtual terminal processing on Windows 10+
49 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
50 if (hOut == INVALID_HANDLE_VALUE) {
51 return false;
52 }
53
54 DWORD dwMode = 0;
55 if (!GetConsoleMode(hOut, &dwMode)) {
56 return false;
57 }
58
59 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
60 if (!SetConsoleMode(hOut, dwMode)) {
61 // Fallback: check if TERM is set (e.g., running in mintty)
62 const char* term = std::getenv("TERM");
63 return term != nullptr;
64 }
65 return true;
66#else
67 // Check common environment variables
68 const char* term = std::getenv("TERM");
69 if (term == nullptr) {
70 return false;
71 }
72
73 // Check for dumb terminal
74 if (std::string_view{term} == "dumb") {
75 return false;
76 }
77
78 // Check for NO_COLOR environment variable
79 if (std::getenv("NO_COLOR") != nullptr) {
80 return false;
81 }
82
83 return true;
84#endif
85}
86
87void set_color_enabled(bool enabled) {
88 g_color_enabled = enabled;
89 g_color_checked = true;
90}
91
92auto is_color_enabled() noexcept -> bool {
93 if (!g_color_checked) {
94 g_color_enabled = supports_color();
95 g_color_checked = true;
96 }
97 return g_color_enabled;
98}
99
100// ============================================================================
101// Helper Functions
102// ============================================================================
103
104namespace {
105
106auto color(const char* code) -> const char* {
107 return is_color_enabled() ? code : "";
108}
109
110constexpr int kDefaultWidth = 78;
111
112} // namespace
113
114// ============================================================================
115// Message Printing
116// ============================================================================
117
118void print_header(std::string_view title) {
119 std::cout << color(colors::bold) << color(colors::cyan);
120 std::cout << "\n";
121 std::cout << std::string(kDefaultWidth, '=') << "\n";
122 std::cout << center_text(title, kDefaultWidth) << "\n";
123 std::cout << std::string(kDefaultWidth, '=') << "\n";
124 std::cout << color(colors::reset) << "\n";
125}
126
127void print_section(std::string_view title) {
128 std::string line = "--- " + std::string(title) + " ";
129 line += std::string(kDefaultWidth - line.length(), '-');
130
131 std::cout << "\n" << color(colors::bold) << color(colors::blue)
132 << line << color(colors::reset) << "\n\n";
133}
134
135void print_success(std::string_view message) {
136 std::cout << color(colors::green) << "[OK] " << color(colors::reset)
137 << message << "\n";
138}
139
140void print_error(std::string_view message) {
141 std::cout << color(colors::red) << "[ERROR] " << color(colors::reset)
142 << message << "\n";
143}
144
145void print_warning(std::string_view message) {
146 std::cout << color(colors::yellow) << "[WARN] " << color(colors::reset)
147 << message << "\n";
148}
149
150void print_info(std::string_view message) {
151 std::cout << color(colors::cyan) << "[INFO] " << color(colors::reset)
152 << message << "\n";
153}
154
155void print_debug(std::string_view message) {
156 std::cout << color(colors::dim) << "[DEBUG] " << message
157 << color(colors::reset) << "\n";
158}
159
160// ============================================================================
161// DICOM Data Display
162// ============================================================================
163
165 using namespace core::tags;
166
167 std::vector<std::pair<std::string, std::string>> patient_rows = {
168 {"Patient Name", ds.get_string(patient_name)},
169 {"Patient ID", ds.get_string(patient_id)},
170 {"Birth Date", ds.get_string(patient_birth_date)},
171 {"Sex", ds.get_string(patient_sex)},
172 };
173 print_table("Patient", patient_rows);
174
175 std::vector<std::pair<std::string, std::string>> study_rows = {
176 {"Study UID", truncate(ds.get_string(study_instance_uid), 40)},
177 {"Study Date", ds.get_string(study_date)},
178 {"Accession #", ds.get_string(accession_number)},
179 {"Description", ds.get_string(study_description)},
180 };
181 print_table("Study", study_rows);
182
183 std::vector<std::pair<std::string, std::string>> series_rows = {
184 {"Series UID", truncate(ds.get_string(series_instance_uid), 40)},
185 {"Modality", ds.get_string(modality)},
186 {"Series #", ds.get_string(series_number)},
187 {"Description", ds.get_string(series_description)},
188 };
189 print_table("Series", series_rows);
190
191 std::vector<std::pair<std::string, std::string>> image_rows = {
192 {"SOP Instance", truncate(ds.get_string(sop_instance_uid), 40)},
193 {"Rows", ds.get_string(rows)},
194 {"Columns", ds.get_string(columns)},
195 {"Bits Allocated", ds.get_string(bits_allocated)},
196 {"Photometric", ds.get_string(photometric_interpretation)},
197 };
198 print_table("Image", image_rows);
199}
200
202 size_t max_value_length) {
203 std::cout << color(colors::bold)
204 << std::left << std::setw(14) << "Tag"
205 << std::setw(6) << "VR"
206 << "Value"
207 << color(colors::reset) << "\n";
208 std::cout << std::string(kDefaultWidth, '-') << "\n";
209
210 for (const auto& [tag, element] : ds) {
211 std::cout << color(colors::cyan) << tag.to_string() << color(colors::reset)
212 << " ";
213
214 std::cout << color(colors::yellow)
215 << std::setw(4) << encoding::to_string(element.vr())
216 << color(colors::reset) << " ";
217
218 auto value_result = element.as_string();
219 std::string value = value_result.is_ok() ? value_result.value() : "<binary>";
220 std::cout << truncate(value, max_value_length) << "\n";
221 }
222}
223
224// ============================================================================
225// Box Drawing
226// ============================================================================
227
228void print_box(const std::vector<std::string>& lines) {
229 if (lines.empty()) {
230 return;
231 }
232
233 // Find maximum line length
234 size_t max_len = 0;
235 for (const auto& line : lines) {
236 max_len = std::max(max_len, line.length());
237 }
238 max_len = std::min(max_len, static_cast<size_t>(kDefaultWidth - 4));
239
240 std::cout << color(colors::dim);
241 std::cout << "+" << std::string(max_len + 2, '-') << "+\n";
242 for (const auto& line : lines) {
243 std::cout << "| " << std::left << std::setw(static_cast<int>(max_len))
244 << truncate(line, max_len) << " |\n";
245 }
246 std::cout << "+" << std::string(max_len + 2, '-') << "+\n";
247 std::cout << color(colors::reset);
248}
249
250void print_table(std::string_view title,
251 const std::vector<std::pair<std::string, std::string>>& rows) {
252 if (rows.empty()) {
253 return;
254 }
255
256 // Find maximum key length
257 size_t max_key_len = 0;
258 for (const auto& [key, _] : rows) {
259 max_key_len = std::max(max_key_len, key.length());
260 }
261
262 std::cout << color(colors::bold) << color(colors::magenta)
263 << title << ":" << color(colors::reset) << "\n";
264
265 for (const auto& [key, value] : rows) {
266 std::cout << " " << color(colors::dim)
267 << std::right << std::setw(static_cast<int>(max_key_len)) << key
268 << color(colors::reset) << ": "
269 << value << "\n";
270 }
271 std::cout << "\n";
272}
273
274// ============================================================================
275// Progress Display
276// ============================================================================
277
278void print_progress(size_t current, size_t total, int width,
279 std::string_view label) {
280 if (total == 0) {
281 return;
282 }
283
284 double progress = static_cast<double>(current) / static_cast<double>(total);
285 int filled = static_cast<int>(progress * width);
286 int percent = static_cast<int>(progress * 100.0);
287
288 std::cout << "\r" << label << ": [";
289 std::cout << color(colors::green);
290 for (int i = 0; i < filled; ++i) {
291 std::cout << "#";
292 }
293 std::cout << color(colors::dim);
294 for (int i = filled; i < width; ++i) {
295 std::cout << "-";
296 }
297 std::cout << color(colors::reset);
298 std::cout << "] " << std::setw(3) << percent << "% ("
299 << current << "/" << total << ")" << std::flush;
300}
301
303 std::cout << "\r" << std::string(kDefaultWidth, ' ') << "\r" << std::flush;
304}
305
306// ============================================================================
307// Formatting Utilities
308// ============================================================================
309
310auto format_bytes(uint64_t bytes) -> std::string {
311 constexpr uint64_t KB = 1024;
312 constexpr uint64_t MB = KB * 1024;
313 constexpr uint64_t GB = MB * 1024;
314
315 std::ostringstream oss;
316 oss << std::fixed << std::setprecision(1);
317
318 if (bytes >= GB) {
319 oss << static_cast<double>(bytes) / static_cast<double>(GB) << " GB";
320 } else if (bytes >= MB) {
321 oss << static_cast<double>(bytes) / static_cast<double>(MB) << " MB";
322 } else if (bytes >= KB) {
323 oss << static_cast<double>(bytes) / static_cast<double>(KB) << " KB";
324 } else {
325 oss << bytes << " B";
326 }
327
328 return oss.str();
329}
330
331auto format_duration(uint64_t milliseconds) -> std::string {
332 std::ostringstream oss;
333 oss << std::fixed << std::setprecision(1);
334
335 if (milliseconds >= 60000) {
336 oss << static_cast<double>(milliseconds) / 60000.0 << " min";
337 } else if (milliseconds >= 1000) {
338 oss << static_cast<double>(milliseconds) / 1000.0 << " s";
339 } else {
340 oss << milliseconds << " ms";
341 }
342
343 return oss.str();
344}
345
346auto center_text(std::string_view text, size_t width) -> std::string {
347 if (text.length() >= width) {
348 return std::string{text.substr(0, width)};
349 }
350
351 size_t padding = (width - text.length()) / 2;
352 return std::string(padding, ' ') + std::string{text} +
353 std::string(width - padding - text.length(), ' ');
354}
355
356auto truncate(std::string_view text, size_t max_length,
357 std::string_view suffix) -> std::string {
358 if (text.length() <= max_length) {
359 return std::string{text};
360 }
361
362 if (max_length <= suffix.length()) {
363 return std::string{suffix.substr(0, max_length)};
364 }
365
366 return std::string{text.substr(0, max_length - suffix.length())} +
367 std::string{suffix};
368}
369
370} // namespace kcenon::pacs::samples
Represents a DICOM Dataset (ordered collection of Data Elements)
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
Console output utilities for developer samples.
Compile-time constants for commonly used DICOM tags.
constexpr std::string_view to_string(vr_type vr) noexcept
Converts a vr_type to its two-character string representation.
Definition vr_type.h:83
constexpr const char * reset
Reset all formatting.
constexpr const char * red
constexpr const char * bold
Bold text.
constexpr const char * blue
constexpr const char * dim
Dim/faint text.
constexpr const char * yellow
constexpr const char * magenta
constexpr const char * green
constexpr const char * cyan
void print_dataset_summary(const core::dicom_dataset &ds)
Print a summary of a DICOM dataset.
void set_color_enabled(bool enabled)
Enable or disable color output globally.
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.
auto truncate(std::string_view text, size_t max_length, std::string_view suffix) -> std::string
Truncate a string to a maximum length.
void print_debug(std::string_view message)
Print a debug message.
auto supports_color() noexcept -> bool
Check if the terminal supports ANSI colors.
void print_progress(size_t current, size_t total, int width, std::string_view label)
Print a progress bar.
void print_header(std::string_view title)
Print a header/title with decorative border.
auto center_text(std::string_view text, size_t width) -> std::string
Center a string within a given width.
auto format_duration(uint64_t milliseconds) -> std::string
Format a duration in milliseconds.
auto format_bytes(uint64_t bytes) -> std::string
Format a byte size for human-readable display.
void print_info(std::string_view message)
Print an informational message.
void clear_line()
Clear the current line (for updating progress)
void print_success(std::string_view message)
Print a success message.
auto is_color_enabled() noexcept -> bool
Check if colors are currently enabled.
void print_dataset_elements(const core::dicom_dataset &ds, size_t max_value_length)
Print all elements in a DICOM dataset.
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.
void print_warning(std::string_view message)
Print a warning message.
std::string_view code