Hedgehog  3.1.0
A library to generate hybrid pipeline workflow systems
Loading...
Searching...
No Matches
dot_printer.h
Go to the documentation of this file.
1// NIST-developed software is provided by NIST as a public service. You may use, copy and distribute copies of the
2// software in any medium, provided that you keep intact this entire notice. You may improve, modify and create
3// derivative works of the software or any portion of the software, and you may copy and distribute such modifications
4// or works. Modified works should carry a notice stating that you changed the software and should note the date and
5// nature of any such change. Please explicitly acknowledge the National Institute of Standards and Technology as the
6// source of the software. NIST-developed software is expressly provided "AS IS." NIST MAKES NO WARRANTY OF ANY KIND,
7// EXPRESS, IMPLIED, IN FACT OR ARISING BY OPERATION OF LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
8// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT AND DATA ACCURACY. NIST NEITHER REPRESENTS NOR
9// WARRANTS THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT ANY DEFECTS WILL BE
10// CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS
11// THEREOF, INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY, OR USEFULNESS OF THE SOFTWARE. You
12// are solely responsible for determining the appropriateness of using and distributing the software and you assume
13// all risks associated with its use, including but not limited to the risks and costs of program errors, compliance
14// with applicable laws, damage to or loss of data, programs or equipment, and the unavailability or interruption of
15// operation. This software is not intended to be used in any situation where a failure could cause risk of injury or
16// damage to property. The software developed by NIST employees is not subject to copyright protection within the
17// United States.
18
19
20
21#ifndef HEDGEHOG_DOT_PRINTER_H
22#define HEDGEHOG_DOT_PRINTER_H
23
24#include <fstream>
25#include <filesystem>
26#include <iostream>
27#include <cmath>
28#include <utility>
29#include <set>
30
31#include "printer.h"
36#include "../../core/abstractions/base/node/graph_node_abstraction.h"
37#include "../../core/abstractions/base/node/task_node_abstraction.h"
38#include "../../core/abstractions/base/any_groupable_abstraction.h"
39
41namespace hh {
42
45class DotPrinter : public Printer {
46 private:
48 class Edge {
49 private:
50 std::string id_{};
51
52 std::string type_{};
53
54 std::string extraLabel_{};
55
56 std::set<std::string>
57 arrivals_{},
58 exits_{};
59
60 bool declarationPrinted_ = false;
61
62 core::abstraction::GraphNodeAbstraction *
63 belongingGraph_ = nullptr;
64
65 public:
70 explicit Edge(std::string id, std::string type, core::abstraction::GraphNodeAbstraction *const belongingGraph)
71 : id_(std::move(id)), type_(std::move(type)), belongingGraph_(belongingGraph) {}
72
74 virtual ~Edge() = default;
75
78 [[nodiscard]] std::string const &id() const { return id_; }
79
82 [[nodiscard]] std::string const &extraLabel() const { return extraLabel_; }
83
86 [[nodiscard]] core::abstraction::GraphNodeAbstraction *belongingGraph() const { return belongingGraph_; }
87
90 void addExtraLabel(std::string extraLabel) { extraLabel_ = std::move(extraLabel); }
91
94 void addArrival(std::string arrival) { arrivals_.insert(std::move(arrival)); }
95
98 void addExit(std::string exit) { exits_.insert(std::move(exit)); }
99
102 void printDeclaration(std::ostream &os) {
103 if (!declarationPrinted_) {
104 declarationPrinted_ = true;
105 os << "\"" << id_ << "\"" << "[label=\"" << type_ << "\\n" << extraLabel_ << "\", shape=rect];\n";
106 }
107 }
108
111 void printEdges(std::ostream &os) const {
112 for (auto &arrival : arrivals_) {
113 os << "\"" << arrival << "\" -> \"" << id_ << "\"[penwidth=1, dir=none];\n";
114 }
115 for (auto &exit : exits_) {
116 os << "\"" << id_ << "\" -> \"" << exit << "\"[penwidth=1];\n";
117 }
118 }
119
123 bool operator==(Edge const &rhs) const { return id_ == rhs.id_ && type_ == rhs.type_; }
124 };
125
126 std::ofstream outputFile_ = {};
127 ColorScheme colorScheme_ = {};
128 StructureOptions structureOptions_ = {};
129 DebugOptions debugOptions_ = {};
130 std::unique_ptr<ColorPicker> colorPicker_ = nullptr;
131
132 std::chrono::nanoseconds
133 graphTotalExecution_ = std::chrono::nanoseconds::max(),
134 minExecutionDurationInAllGraphs_ =
135 std::chrono::nanoseconds::max(),
136 maxExecutionDurationInAllGraphs_ =
137 std::chrono::nanoseconds::min(),
138 rangeExecutionDurationInAllGraphs_ =
139 std::chrono::nanoseconds::min(),
140 minWaitDurationInAllGraphs_ =
141 std::chrono::nanoseconds::max(),
142 maxWaitDurationInAllGraphs_ =
143 std::chrono::nanoseconds::min(),
144 rangeWaitDurationInAllGraphs_ =
145 std::chrono::nanoseconds::min();
146
147 std::unique_ptr<std::vector<Edge>> edges_ = nullptr;
148
149 public:
160 DotPrinter(std::filesystem::path const &dotFilePath,
161 ColorScheme colorScheme,
162 StructureOptions structureOptions,
163 DebugOptions debugOptions,
164 core::abstraction::GraphNodeAbstraction const *graph,
165 std::unique_ptr<ColorPicker> colorPicker,
166 bool verbose)
167 : colorScheme_(colorScheme),
168 structureOptions_(structureOptions),
169 debugOptions_(debugOptions),
170 colorPicker_(std::move(colorPicker)),
171 edges_(std::make_unique<std::vector<Edge>>()) {
172 assert(graph != nullptr);
173
174 testPath(dotFilePath, verbose);
175
176 if (colorPicker_ == nullptr) {
177 throw (
178 std::runtime_error("A dot printer should be constructed with a valid ColorPicker (colorPicker != nullptr)")
179 );
180 }
181
182 auto minMaxExecTime = graph->minMaxExecutionDuration();
183 auto minMaxWaitTime = graph->minMaxWaitDuration();
184
185 minExecutionDurationInAllGraphs_ = minMaxExecTime.first;
186 maxExecutionDurationInAllGraphs_ = minMaxExecTime.second;
187
188 minWaitDurationInAllGraphs_ = minMaxWaitTime.first;
189 maxWaitDurationInAllGraphs_ = minMaxWaitTime.second;
190
191 // Compute range
192 rangeExecutionDurationInAllGraphs_ =
193 maxExecutionDurationInAllGraphs_ == minExecutionDurationInAllGraphs_ ?
194 std::chrono::nanoseconds(1) :
195 maxExecutionDurationInAllGraphs_ - minExecutionDurationInAllGraphs_;
196
197 rangeWaitDurationInAllGraphs_ =
198 maxWaitDurationInAllGraphs_ == minWaitDurationInAllGraphs_ ?
199 std::chrono::nanoseconds(1) :
200 maxWaitDurationInAllGraphs_ - minWaitDurationInAllGraphs_;
201
202 graphTotalExecution_ =
203 graph->executionDuration() == std::chrono::nanoseconds::zero() ?
204 std::chrono::system_clock::now() - graph->startExecutionTimeStamp() : graph->executionDuration();
205 }
206
208 ~DotPrinter() override { outputFile_.close(); }
209
212 void printGraphHeader(core::abstraction::GraphNodeAbstraction const *graph) override {
213 // If the graph is the outer graph, i.e. the main graph
214 if (!graph->isRegistered()) {
215 outputFile_
216 << "digraph " << graph->id()
217 << " {\nlabel=\"" << graph->name();
218 if (debugOptions_ == DebugOptions::ALL) { outputFile_ << " " << graph->id(); }
219
220 outputFile_ << "\\nExecution duration:" << durationPrinter(this->graphTotalExecution_)
221 << "\\nCreation duration:" << durationPrinter(graph->graphConstructionDuration())
222 << "\"; fontsize=25; penwidth=5; labelloc=top; labeljust=left; \n";
223
224 // If the graph is an inner graph, i.e. a graph of the outer graph
225 } else {
226 outputFile_ << "subgraph cluster" << graph->id() << " {\nlabel=\"" << graph->name();
227 if (debugOptions_ == DebugOptions::ALL) {
228 outputFile_ << " " << graph->id();
229 }
230 outputFile_
231 << "\"; fontsize=25; penwidth=5; fillcolor=\""
232 << colorFormatConvertor(graph->printOptions().background())
233 << "\";\n";
234 }
235 outputFile_.flush();
236 }
237
240 void printGraphFooter(core::abstraction::GraphNodeAbstraction const *graph) override {
241
242 // Print all edge declarations that has not already been printed
243 for (auto &edge : *edges_) {
244 if (edge.belongingGraph() == graph) {
245 edge.printDeclaration(outputFile_);
246 }
247 }
248
249 // If the graph is the outer graph
250 if (!graph->isRegistered()) {
251 // Print all the stored edges
252
253 for (auto const &edge : *(this->edges_)) { edge.printEdges(outputFile_); }
254 }
255 // Close the dot subgraph
256 outputFile_ << "}\n";
257 outputFile_.flush();
258 }
259
262 void printNodeInformation(core::abstraction::NodeAbstraction const *node) override {
263 // If the node is not a graph
264 if (auto task = dynamic_cast<core::abstraction::TaskNodeAbstraction const *>(node)) {
265 //If all group node to be printed
266 if (this->structureOptions_ == StructureOptions::ALL || this->structureOptions_ == StructureOptions::THREADING) {
267 // Get and print the node information
268 outputFile_ << getTaskInformation(task);
269 // If only one node per group need to be printed with gathered information
270 } else {
271 if (auto copyableNode = dynamic_cast<core::abstraction::AnyGroupableAbstraction const *>(task)) {
272 // If the node is the group main node
273 if (copyableNode == copyableNode->groupRepresentative()) {
274 // Get and print the node information
275 outputFile_ << getTaskInformation(task);
276 }
277 } else {
278 // Case for printing state manager while not printing all nodes
279 outputFile_ << getTaskInformation(task);
280 }
281 }
282 }
283 outputFile_.flush();
284 }
285
292 void printEdge(core::abstraction::NodeAbstraction const *from, core::abstraction::NodeAbstraction const *to,
293 std::string const &edgeType,
294 size_t const &queueSize, size_t const &maxQueueSize) override {
295
296 std::ostringstream oss;
297 std::string idToFind, label;
298
299 for (auto &source : from->ids()) {
300 for (auto &dest : to->ids()) {
301 auto edge = getOrCreateEdge(dest.second, edgeType, to->belongingGraph());
302
303 if (edge->extraLabel().empty()) {
304 if (this->structureOptions_ == StructureOptions::QUEUE || this->structureOptions_ == StructureOptions::ALL) {
305 oss << "QS=" << queueSize << "\\nMQS=" << maxQueueSize;
306 edge->addExtraLabel(oss.str());
307 oss.str("");
308 }
309 }
310 edge->addArrival(source.first);
311 edge->addExit(dest.first);
312
313 if (this->structureOptions_ == StructureOptions::THREADING ||
314 this->structureOptions_ == StructureOptions::ALL) {
315 if (auto copyableSender = dynamic_cast<core::abstraction::AnyGroupableAbstraction const *>(from)) {
316 for (auto groupMember : *copyableSender->group()) { edge->addArrival(groupMember->nodeId()); }
317 }
318 }
319 }
320 }
321 }
322
327 void printGroup(core::abstraction::NodeAbstraction *representative,
328 std::vector<core::abstraction::NodeAbstraction *> const &group) override {
329 bool const printAllGroupMembers =
330 this->structureOptions_ == StructureOptions::THREADING || this->structureOptions_ == StructureOptions::ALL;
331
332 auto copyableRepr = dynamic_cast<core::abstraction::AnyGroupableAbstraction *>(representative);
333 auto printRepr = dynamic_cast<core::abstraction::PrintableAbstraction *>(representative);
334 if (copyableRepr == nullptr || printRepr == nullptr) {
335 std::ostringstream oss;
336 oss << "Internal error in: " << __FUNCTION__
337 << " a group of node should be created with node that derives from AnyGroupableAbstraction and PrintableAbstraction";
338 throw std::runtime_error(oss.str());
339 }
340
341 // Print header
342 // If all group node to be printed
343 if (printAllGroupMembers) {
344 // Create a dot subgraph for the task group
345 outputFile_ << "subgraph cluster" << representative->id()
346 << " {\nlabel=\"\"; penwidth=3; style=filled; fillcolor=\"#ebf0fa\"; color=\"#4e78cf\";\n";
347
348 }
349
350 printRepr->visit(this);
351
352 if (printAllGroupMembers) {
353 for (auto groupMember : group) {
354 if(auto printGroupMember = dynamic_cast<core::abstraction::PrintableAbstraction *>(groupMember)){
355 printGroupMember->visit(this);
356 }else {
357 std::ostringstream oss;
358 oss << "Internal error in: " << __FUNCTION__
359 << " a group of node should be created with nodes that derive from AnyGroupableAbstraction and PrintableAbstraction";
360 throw std::runtime_error(oss.str());
361 }
362 }
363 }
364
365 if (printAllGroupMembers) {
366 outputFile_ << "}\n";
367 }
368
369 outputFile_.flush();
370 }
371
374 void printSource(core::abstraction::NodeAbstraction const *source) override {
375 outputFile_ << source->id() << " [label=\"" << source->name();
376 if (debugOptions_ == DebugOptions::ALL) {
377 outputFile_ << " " << source->id() << " \\(Graph:" << source->belongingGraph()->id() << "\\)";
378 }
379 outputFile_ << "\", shape=invhouse];\n";
380 outputFile_.flush();
381 }
382
385 void printSink(core::abstraction::NodeAbstraction const *sink) override {
386 outputFile_ << sink->id() << " [label=\"" << sink->name();
387 if (debugOptions_ == DebugOptions::ALL) {
388 outputFile_ << " " << sink->id() << " \\(Graph:" << sink->belongingGraph()->id() << "\\)";
389 }
390 outputFile_ << "\", shape=point];\n";
391 outputFile_.flush();
392 }
393
397 void printExecutionPipelineHeader(core::abstraction::ExecutionPipelineNodeAbstraction const *ep,
398 core::abstraction::NodeAbstraction const *switchNode) override {
399 //Print the dot subgraph header
400 outputFile_ << "subgraph cluster" << ep->id() << " {\nlabel=\"" << ep->name();
401 if (debugOptions_ == DebugOptions::ALL) { outputFile_ << " " << ep->id() << " / " << switchNode->id(); }
402 // Print a "triangle" node to represent the execution pipeline switch
403 outputFile_ << "\"; penwidth=1; style=dotted; style=filled; fillcolor=\""
404 << colorFormatConvertor(ep->printOptions().background())
405 << "\";\n "
406 << switchNode->id() << "[label=\"\", shape=triangle];\n";
407 outputFile_.flush();
408 }
409
411 void printExecutionPipelineFooter() override {
412 outputFile_ << "}\n";
413 outputFile_.flush();
414 }
415
416 private:
422 std::vector<Edge>::iterator getOrCreateEdge(
423 std::string const &id, std::string const &type,
425 std::ostringstream ossId;
426 ossId << "edge" << id << type;
427 Edge temp(ossId.str(), type, belongingGraph);
428 auto it = std::find(this->edges_->begin(), this->edges_->end(), temp);
429 if (it != this->edges_->end()) { return it; }
430 else { return this->edges_->insert(this->edges_->end(), temp); }
431 }
432
436 std::string getTaskInformation(core::abstraction::TaskNodeAbstraction const *task) {
437 std::stringstream ss;
438
439 auto copyableTask = dynamic_cast<core::abstraction::AnyGroupableAbstraction const *>(task);
440 auto slotTask = dynamic_cast<core::abstraction::SlotAbstraction const *>(task);
441
442 bool printAllNodes =
443 this->structureOptions_ == StructureOptions::THREADING || this->structureOptions_ == StructureOptions::ALL;
444
445 // Print the name
446 ss << task->id() << " [label=\"" << task->name();
447 // Print the id (address) in case of debug
448 if (debugOptions_ == DebugOptions::ALL) {
449 ss << " " << task->id() << " \\(" << task->belongingGraph()->id() << "\\)";
450 }
451
452 // If the group has to be presented as a single dot node
453 if (!printAllNodes) {
454 if (copyableTask && copyableTask->isInGroup()) {
455 ss << " x " << copyableTask->numberThreads();
456 }
457 }
458 // If debug information printed
459 if (debugOptions_ == DebugOptions::ALL) {
460 if (slotTask) {
461 // Print number of active input connection
462 ss << "\\nActive inputs connection: " << slotTask->connectedNotifiers().size();
463 }
464 // If all nodes in a group need to be printed
465 if (printAllNodes) {
466 ss << "\\nThread Active?: " << std::boolalpha << task->isActive();
467 // If all nodes in a group should NOT be printed
468 } else {
469 if (copyableTask) {
470 ss << "\\nActive threads: " << copyableTask->numberActiveThreadInGroup();
471 }
472 }
473 }
474 // If all nodes in a group need to be printed OR is state manager
475 if (printAllNodes || !copyableTask) {
476 ss << "\\nNumber Elements Received: " << task->numberReceivedElements();
477 ss << "\\nWait Duration: " << durationPrinter(task->waitDuration());
478 ss << "\\nDequeue + Execution Duration: " << durationPrinter(task->executionDuration());
479 ss << "\\nExecution Duration Per Element: " << durationPrinter(task->perElementExecutionDuration());
480 if (task->hasMemoryManagerAttached()) {
481 ss << "\\nMemory manager (" << task->memoryManager()->managedType() << "): "
482 << task->memoryManager()->currentSize() << "/" << task->memoryManager()->capacity();
483 ss << "\\nMemory Wait Duration: " << durationPrinter(task->memoryWaitDuration());
484 }
485 // If all nodes in a group should NOT be printed
486 } else {
487 //Get the time in the groups
488 if (copyableTask) {
489 // Print the number of element received per task
490 ss << "\\nNumber of Elements Received Per Task: ";
491 if (copyableTask->numberThreads() > 1) {
492 auto minmaxElements = copyableTask->minmaxNumberElementsReceivedGroup();
493 auto meanSDNumberElements = copyableTask->meanSDNumberElementsReceivedGroup();
494 ss << "\\n"
495 << " Min: " << minmaxElements.first << "\\n"
496 << " Avg: " << std::setw(3) << meanSDNumberElements.first
497 << " +- " << std::setw(3) << meanSDNumberElements.second
498 << "\\n"
499 << " Max: " << std::setw(3) << minmaxElements.second << "\\n";
500 } else {
501 ss << task->numberReceivedElements() << "\\n";
502 }
503 // Print the wait time
504 ss << "Wait Time: ";
505 if (copyableTask->numberThreads() > 1) {
506 auto minmaxWait = copyableTask->minmaxWaitDurationGroup();
507 auto meanSDWait = copyableTask->meanSDWaitDurationGroup();
508 ss << "\\n"
509 << " Min: " << durationPrinter(minmaxWait.first) << "\\n"
510 << " Avg: " << durationPrinter(meanSDWait.first) << " +- "
511 << durationPrinter(meanSDWait.second) << "\\n"
512 << " Max: " << durationPrinter(minmaxWait.second) << "\\n";
513 } else {
514 ss << durationPrinter(task->waitDuration()) << "\\n";
515 }
516 // Print the execution time
517 ss << "Dequeue + Execution Time: ";
518 if (copyableTask->numberThreads() > 1) {
519 auto minmaxExec = copyableTask->minmaxExecutionDurationGroup();
520 auto meanSDExec = copyableTask->meanSDExecutionDurationGroup();
521 ss << "\\n"
522 << " Min: " << durationPrinter(minmaxExec.first) << "\\n"
523 << " Avg: " << durationPrinter(meanSDExec.first) << " +- "
524 << durationPrinter(meanSDExec.second) << "\\n"
525 << " Max: " << durationPrinter(minmaxExec.second) << "\\n";
526 } else {
527 ss << durationPrinter(task->executionDuration()) << "\\n";
528 }
529
530 // Print the execution time per Element
531 ss << "Execution Time Per Element: ";
532 if (copyableTask->numberThreads() > 1) {
533 auto minmaxExecPerElement = copyableTask->minmaxExecTimePerElementGroup();
534 auto meanSDExecPerElement = copyableTask->meanSDExecTimePerElementGroup();
535 ss << "\\n"
536 << " Min: " << durationPrinter(minmaxExecPerElement.first) << "\\n"
537 << " Avg: " << durationPrinter(meanSDExecPerElement.first) << " +- "
538 << durationPrinter(meanSDExecPerElement.second) << "\\n"
539 << " Max: " << durationPrinter(minmaxExecPerElement.second) << "\\n";
540 } else {
541 ss << durationPrinter(task->perElementExecutionDuration()) << "\\n";
542 }
543
544 // Print the memory wait time
545 if (task->hasMemoryManagerAttached()) {
546 ss << "Memory manager (" << task->memoryManager()->managedType() << "): "
547 << task->memoryManager()->currentSize() << "/" << task->memoryManager()->capacity();
548 ss << "\\nMemory Wait Time: ";
549 if (copyableTask->numberThreads() == 1) {
550 ss << durationPrinter(task->memoryWaitDuration()) << "\\n";
551 } else {
552 auto minmaxWaitMemory = copyableTask->minmaxMemoryWaitTimeGroup();
553 auto meanSDWaitMemory = copyableTask->meanSDMemoryWaitTimePerElementGroup();
554 ss << "\\n"
555 << " Min: " << durationPrinter(minmaxWaitMemory.first) << "\\n"
556 << " Avg: " << durationPrinter(meanSDWaitMemory.first) << " +-"
557 << durationPrinter(meanSDWaitMemory.second) << "\\n"
558 << " Max: " << durationPrinter(minmaxWaitMemory.second) << "\\n";
559 }
560 }
561 }
562 }
563 // If extra information has been defined by the user, print it
564 auto extraInfo = task->extraPrintingInformation();
565 if (!extraInfo.empty()) { ss << "\\n" << extraInfo; }
566
567 ss << "\"";
568 ss << ",shape=rect";
569 // Change the color of the rect depending on the user choice
570 switch (this->colorScheme_) {
571 case ColorScheme::EXECUTION:ss << ",color=" << this->getExecRGB(task->executionDuration()) << ", penwidth=3";
572 break;
573 case ColorScheme::WAIT:ss << ",color=" << this->getWaitRGB(task->waitDuration()) << ", penwidth=3";
574 break;
575 default:break;
576 }
577
578 ss << R"(, style=filled, fillcolor=")"
579 << colorFormatConvertor(task->printOptions().background())
580 << R"(", fontcolor=")"
581 << colorFormatConvertor(task->printOptions().font())
582 << "\"];\n";
583 return ss.str();
584 }
585
590 void testPath(std::filesystem::path const &dotFilePath, bool verbose) {
591 auto directoryPath = dotFilePath.parent_path();
592 std::ostringstream oss;
593 if (!dotFilePath.has_filename()) {
594 oss << "The path: " << dotFilePath << " does not represent a file.";
595 throw (std::runtime_error(oss.str()));
596 }
597 if (!std::filesystem::exists(directoryPath)) {
598 oss << "The file " << dotFilePath.filename() << " can not be store in " << directoryPath
599 << " because the directory does not exist.";
600 throw (std::runtime_error(oss.str()));
601 }
602 if (!std::filesystem::exists(directoryPath)) {
603 oss << "The file " << dotFilePath.filename() << " can not be store in " << directoryPath
604 << " because the directory does not exist.";
605 throw (std::runtime_error(oss.str()));
606 }
607 if (verbose) {
608 if (std::filesystem::exists(dotFilePath)) {
609 std::cout << "The file " << dotFilePath.filename() << " will be overwritten." << std::endl;
610 } else {
611 std::cout << "The file " << dotFilePath.filename() << " will be created." << std::endl;
612 }
613 }
614 outputFile_ = std::ofstream(dotFilePath);
615 }
616
620 std::string getExecRGB(std::chrono::nanoseconds const &ns) {
621 return colorPicker_
622 ->getRGBFromRange(ns, this->minExecutionDurationInAllGraphs_, this->rangeExecutionDurationInAllGraphs_);
623 }
624
628 std::string getWaitRGB(std::chrono::nanoseconds const &ns) {
629 return colorPicker_->getRGBFromRange(ns, this->minWaitDurationInAllGraphs_, this->rangeWaitDurationInAllGraphs_);
630 }
631
635 static std::string durationPrinter(std::chrono::nanoseconds const &ns) {
636 std::ostringstream oss;
637
638 // Cast with precision loss
639 auto s = std::chrono::duration_cast<std::chrono::seconds>(ns);
640 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(ns);
641 auto us = std::chrono::duration_cast<std::chrono::microseconds>(ns);
642
643 if (s > std::chrono::seconds::zero()) {
644 oss << s.count() << "." << std::setfill('0') << std::setw(3) << (ms - s).count() << "s";
645 } else if (ms > std::chrono::milliseconds::zero()) {
646 oss << ms.count() << "." << std::setfill('0') << std::setw(3) << (us - ms).count() << "ms";
647 } else if (us > std::chrono::microseconds::zero()) {
648 oss << us.count() << "." << std::setfill('0') << std::setw(3) << (ns - us).count() << "us";
649 } else {
650 oss << std::setw(3) << ns.count() << "ns";
651 }
652 return oss.str();
653 }
654
658 static std::string colorFormatConvertor(hh::tool::PrintOptions::Color const &color) {
659 std::ostringstream ss;
660 ss << "#"
661 << std::setw(2) << std::setfill('0') << std::hex << (int) color.r_
662 << std::setw(2) << std::setfill('0') << std::hex << (int) color.g_
663 << std::setw(2) << std::setfill('0') << std::hex << (int) color.b_
664 << std::setw(2) << std::setfill('0') << std::hex << (int) color.a_;
665
666 return ss.str();
667 }
668};
669
670}
671
672#endif //HEDGEHOG_DOT_PRINTER_H
Hedgehog main namespace.
StructureOptions
Enum structural options.
DebugOptions
Enum to enable debug printing.
Definition: debug_options.h:27
ColorScheme
Enum color options.
Definition: color_scheme.h:27
Simple color representation.
Definition: print_options.h:32
uint8_t g_
Green color value.
Definition: print_options.h:35
uint8_t a_
Alpha color value.
Definition: print_options.h:37
uint8_t b_
Blue color value.
Definition: print_options.h:36
uint8_t r_
Red color value.
Definition: print_options.h:34