PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dicom_storage_collector.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
18#pragma once
19
20#include <chrono>
21#include <cstdint>
22#include <memory>
23#include <mutex>
24#include <string>
25#include <unordered_map>
26#include <vector>
27
28#include "../pacs_metrics.h"
29
31
37 std::string name;
38 double value;
39 std::string type; // "gauge" or "counter"
40 std::string unit; // "bytes", "count", etc.
41 std::chrono::system_clock::time_point timestamp;
42 std::unordered_map<std::string, std::string> labels;
43
44 storage_metric(std::string n,
45 double v,
46 std::string t,
47 std::string u = "",
48 std::unordered_map<std::string, std::string> l = {})
49 : name(std::move(n))
50 , value(v)
51 , type(std::move(t))
52 , unit(std::move(u))
53 , timestamp(std::chrono::system_clock::now())
54 , labels(std::move(l)) {}
55};
56
82public:
83 // =========================================================================
84 // Construction
85 // =========================================================================
86
91 explicit dicom_storage_collector(std::string ae_title = "PACS_SCP");
92
97
98 // Non-copyable, non-movable
103
104 // =========================================================================
105 // Collector Plugin Interface
106 // =========================================================================
107
113 [[nodiscard]] bool initialize(
114 const std::unordered_map<std::string, std::string>& config);
115
120 [[nodiscard]] std::vector<storage_metric> collect();
121
126 [[nodiscard]] std::string get_name() const;
127
132 [[nodiscard]] std::vector<std::string> get_metric_types() const;
133
138 [[nodiscard]] bool is_healthy() const;
139
144 [[nodiscard]] std::unordered_map<std::string, double> get_statistics() const;
145
146 // =========================================================================
147 // Configuration
148 // =========================================================================
149
154 void set_ae_title(std::string ae_title);
155
160 [[nodiscard]] std::string get_ae_title() const;
161
166 void set_pool_metrics_enabled(bool enabled);
167
172 [[nodiscard]] bool is_pool_metrics_enabled() const;
173
174private:
175 // Configuration
176 std::string ae_title_;
177 bool initialized_{false};
179
180 // Statistics
181 mutable std::mutex stats_mutex_;
182 std::uint64_t collection_count_{0};
183 std::chrono::steady_clock::time_point init_time_;
184
185 // Previous values for rate calculation
186 std::uint64_t prev_bytes_sent_{0};
187 std::uint64_t prev_bytes_received_{0};
188 std::chrono::steady_clock::time_point prev_collection_time_;
189
190 // Helper methods
191 void collect_transfer_metrics(std::vector<storage_metric>& metrics);
192 void collect_pool_metrics(std::vector<storage_metric>& metrics);
193
194 [[nodiscard]] storage_metric create_metric(
195 const std::string& name,
196 double value,
197 const std::string& type,
198 const std::string& unit = "") const;
199};
200
201// ─────────────────────────────────────────────────────────────────────────────
202// Inline Implementation
203// ─────────────────────────────────────────────────────────────────────────────
204
206 : ae_title_(std::move(ae_title)) {}
207
209 const std::unordered_map<std::string, std::string>& config) {
210 // Extract AE title from config if provided
211 if (auto it = config.find("ae_title"); it != config.end()) {
212 ae_title_ = it->second;
213 }
214
215 // Check for pool metrics configuration
216 if (auto it = config.find("collect_pool_metrics"); it != config.end()) {
217 collect_pool_metrics_ = (it->second == "true" || it->second == "1");
218 }
219
220 init_time_ = std::chrono::steady_clock::now();
222 initialized_ = true;
223 return true;
224}
225
226inline std::vector<storage_metric> dicom_storage_collector::collect() {
227 std::vector<storage_metric> metrics;
228
229 if (!initialized_) {
230 return metrics;
231 }
232
233 // Collect transfer metrics
235
236 // Collect pool metrics if enabled
238 collect_pool_metrics(metrics);
239 }
240
241 // Update statistics
242 {
243 std::lock_guard<std::mutex> lock(stats_mutex_);
245 }
246
247 return metrics;
248}
249
251 std::vector<storage_metric>& metrics) {
252
253 const auto& transfer = pacs_metrics::global_metrics().transfer();
254 const auto now = std::chrono::steady_clock::now();
255
256 // Current values
257 const auto bytes_sent = transfer.bytes_sent.load(std::memory_order_relaxed);
258 const auto bytes_received = transfer.bytes_received.load(std::memory_order_relaxed);
259 const auto images_stored = transfer.images_stored.load(std::memory_order_relaxed);
260 const auto images_retrieved = transfer.images_retrieved.load(std::memory_order_relaxed);
261
262 // Total bytes sent (counter)
263 metrics.push_back(create_metric(
264 "dicom_bytes_sent_total",
265 static_cast<double>(bytes_sent),
266 "counter",
267 "bytes"));
268
269 // Total bytes received (counter)
270 metrics.push_back(create_metric(
271 "dicom_bytes_received_total",
272 static_cast<double>(bytes_received),
273 "counter",
274 "bytes"));
275
276 // Images stored (counter)
277 metrics.push_back(create_metric(
278 "dicom_images_stored_total",
279 static_cast<double>(images_stored),
280 "counter",
281 "count"));
282
283 // Images retrieved (counter)
284 metrics.push_back(create_metric(
285 "dicom_images_retrieved_total",
286 static_cast<double>(images_retrieved),
287 "counter",
288 "count"));
289
290 // Calculate throughput rates
291 const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
293
294 if (elapsed.count() > 0) {
295 // Bytes per second sent (gauge)
296 const auto sent_delta = bytes_sent - prev_bytes_sent_;
297 const double send_rate =
298 static_cast<double>(sent_delta) * 1000.0 / static_cast<double>(elapsed.count());
299 metrics.push_back(create_metric(
300 "dicom_bytes_sent_rate",
301 send_rate,
302 "gauge",
303 "bytes_per_second"));
304
305 // Bytes per second received (gauge)
306 const auto received_delta = bytes_received - prev_bytes_received_;
307 const double receive_rate =
308 static_cast<double>(received_delta) * 1000.0 / static_cast<double>(elapsed.count());
309 metrics.push_back(create_metric(
310 "dicom_bytes_received_rate",
311 receive_rate,
312 "gauge",
313 "bytes_per_second"));
314 }
315
316 // Update previous values for next rate calculation
317 prev_bytes_sent_ = bytes_sent;
318 prev_bytes_received_ = bytes_received;
320}
321
323 std::vector<storage_metric>& metrics) {
324
325 const auto& pacs = pacs_metrics::global_metrics();
326
327 // Element pool metrics
328 const auto& element_pool = pacs.element_pool();
329 metrics.push_back(create_metric(
330 "dicom_element_pool_acquisitions_total",
331 static_cast<double>(element_pool.total_acquisitions.load(std::memory_order_relaxed)),
332 "counter",
333 "count"));
334 metrics.push_back(create_metric(
335 "dicom_element_pool_hit_ratio",
336 element_pool.hit_ratio(),
337 "gauge",
338 "ratio"));
339 metrics.push_back(create_metric(
340 "dicom_element_pool_size",
341 static_cast<double>(element_pool.current_pool_size.load(std::memory_order_relaxed)),
342 "gauge",
343 "count"));
344
345 // Dataset pool metrics
346 const auto& dataset_pool = pacs.dataset_pool();
347 metrics.push_back(create_metric(
348 "dicom_dataset_pool_acquisitions_total",
349 static_cast<double>(dataset_pool.total_acquisitions.load(std::memory_order_relaxed)),
350 "counter",
351 "count"));
352 metrics.push_back(create_metric(
353 "dicom_dataset_pool_hit_ratio",
354 dataset_pool.hit_ratio(),
355 "gauge",
356 "ratio"));
357 metrics.push_back(create_metric(
358 "dicom_dataset_pool_size",
359 static_cast<double>(dataset_pool.current_pool_size.load(std::memory_order_relaxed)),
360 "gauge",
361 "count"));
362
363 // PDU buffer pool metrics
364 const auto& pdu_pool = pacs.pdu_buffer_pool();
365 metrics.push_back(create_metric(
366 "dicom_pdu_buffer_pool_acquisitions_total",
367 static_cast<double>(pdu_pool.total_acquisitions.load(std::memory_order_relaxed)),
368 "counter",
369 "count"));
370 metrics.push_back(create_metric(
371 "dicom_pdu_buffer_pool_hit_ratio",
372 pdu_pool.hit_ratio(),
373 "gauge",
374 "ratio"));
375 metrics.push_back(create_metric(
376 "dicom_pdu_buffer_pool_size",
377 static_cast<double>(pdu_pool.current_pool_size.load(std::memory_order_relaxed)),
378 "gauge",
379 "count"));
380}
381
382inline std::string dicom_storage_collector::get_name() const {
383 return "dicom_storage_collector";
384}
385
386inline std::vector<std::string> dicom_storage_collector::get_metric_types() const {
387 std::vector<std::string> types = {
388 // Transfer metrics
389 "dicom_bytes_sent_total",
390 "dicom_bytes_received_total",
391 "dicom_images_stored_total",
392 "dicom_images_retrieved_total",
393 "dicom_bytes_sent_rate",
394 "dicom_bytes_received_rate"
395 };
396
398 // Element pool
399 types.push_back("dicom_element_pool_acquisitions_total");
400 types.push_back("dicom_element_pool_hit_ratio");
401 types.push_back("dicom_element_pool_size");
402 // Dataset pool
403 types.push_back("dicom_dataset_pool_acquisitions_total");
404 types.push_back("dicom_dataset_pool_hit_ratio");
405 types.push_back("dicom_dataset_pool_size");
406 // PDU buffer pool
407 types.push_back("dicom_pdu_buffer_pool_acquisitions_total");
408 types.push_back("dicom_pdu_buffer_pool_hit_ratio");
409 types.push_back("dicom_pdu_buffer_pool_size");
410 }
411
412 return types;
413}
414
416 return initialized_;
417}
418
419inline std::unordered_map<std::string, double>
421 std::lock_guard<std::mutex> lock(stats_mutex_);
422
423 std::unordered_map<std::string, double> stats;
424 stats["collection_count"] = static_cast<double>(collection_count_);
425
426 if (initialized_) {
427 const auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
428 std::chrono::steady_clock::now() - init_time_);
429 stats["uptime_seconds"] = static_cast<double>(uptime.count());
430 }
431
432 return stats;
433}
434
435inline void dicom_storage_collector::set_ae_title(std::string ae_title) {
436 ae_title_ = std::move(ae_title);
437}
438
439inline std::string dicom_storage_collector::get_ae_title() const {
440 return ae_title_;
441}
442
444 collect_pool_metrics_ = enabled;
445}
446
450
452 const std::string& name,
453 double value,
454 const std::string& type,
455 const std::string& unit) const {
456 return storage_metric(
457 name,
458 value,
459 type,
460 unit,
461 {{"ae", ae_title_}});
462}
463
464} // namespace kcenon::pacs::monitoring
Collector for DICOM storage and data transfer metrics.
void collect_transfer_metrics(std::vector< storage_metric > &metrics)
dicom_storage_collector(const dicom_storage_collector &)=delete
dicom_storage_collector(std::string ae_title="PACS_SCP")
Default constructor.
std::string get_ae_title() const
Get the current AE title.
dicom_storage_collector & operator=(dicom_storage_collector &&)=delete
void set_pool_metrics_enabled(bool enabled)
Enable or disable pool metrics collection.
bool is_healthy() const
Check if the collector is healthy.
std::vector< std::string > get_metric_types() const
Get supported metric types.
bool initialize(const std::unordered_map< std::string, std::string > &config)
Initialize the collector with configuration.
std::string get_name() const
Get the collector name.
bool is_pool_metrics_enabled() const
Check if pool metrics collection is enabled.
std::chrono::steady_clock::time_point prev_collection_time_
std::unordered_map< std::string, double > get_statistics() const
Get collector statistics.
std::vector< storage_metric > collect()
Collect current storage metrics.
void set_ae_title(std::string ae_title)
Set the AE title for metric labels.
dicom_storage_collector & operator=(const dicom_storage_collector &)=delete
void collect_pool_metrics(std::vector< storage_metric > &metrics)
storage_metric create_metric(const std::string &name, double value, const std::string &type, const std::string &unit="") const
dicom_storage_collector(dicom_storage_collector &&)=delete
const data_transfer_metrics & transfer() const noexcept
Get data transfer metrics.
static pacs_metrics & global_metrics() noexcept
Get the global singleton instance.
Operation metrics collection for PACS DICOM services.
Standard metric structure for storage data.
std::unordered_map< std::string, std::string > labels
storage_metric(std::string n, double v, std::string t, std::string u="", std::unordered_map< std::string, std::string > l={})
std::chrono::system_clock::time_point timestamp
std::string_view name