The Sparta Modeling Framework
Loading...
Searching...
No Matches
Collectable.hpp
Go to the documentation of this file.
1// <Collectable.hpp> -*- C++ -*-
2
10#pragma once
11
12#include <algorithm>
13#include <sstream>
14#include <functional>
15#include <type_traits>
16#include <iomanip>
17
20#include "sparta/collection/BitBucket.hpp"
21#include "sparta/pipeViewer/transaction_structures.hpp"
25#include "sparta/pairs/SpartaKeyPairs.hpp"
26#include "sparta/utils/Utils.hpp"
27#include "sparta/utils/MetaStructs.hpp"
28
29#include "boost/numeric/conversion/converter.hpp"
30
31namespace sparta{
32 namespace collection
33 {
34
35 template <typename U>
36 std::true_type derives_from_pair_definition_(const sparta::PairDefinition<U>*);
37 std::false_type derives_from_pair_definition_(...);
38
39 template <typename T>
40 concept use_pair_definition = requires {
41 typename T::SpartaPairDefinitionType;
42 requires decltype(derives_from_pair_definition_(
43 std::declval<typename T::SpartaPairDefinitionType*>()))::value;
44 };
45
46 template <typename T>
47 concept use_raw_type =
48 std::is_trivial_v<T> &&
49 std::is_standard_layout_v<T> &&
51
52 template <typename T>
53 concept use_tiny_strings = std::is_same_v<T, std::string> ||
54 std::is_same_v<std::decay_t<T>, const char*>;
55
56 template <typename T>
58 simdb::type_traits::is_pod_convertible_v<T> &&
62
63 template <typename T>
70
72 template <typename DataT, SchedulingPhase collection_phase = SchedulingPhase::Collection>
74 {
75 public:
76 using ValueType = MetaStruct::remove_any_pointer_t<DataT>;
77
86 const std::string& name,
87 const std::string& group,
88 uint32_t index,
89 uint64_t parentid = 0,
90 const std::string & desc = "Collectable <manual, no desc>") :
91 CollectableTreeNode(sparta::notNull(parent), name, group, index, desc),
92 event_set_(this),
93 ev_close_record_(&event_set_, name + "_pipeline_collectable_close_event",
95 {
96 }
97
107 const std::string& name,
108 const DataT * collected_object,
109 uint64_t parentid = 0,
110 const std::string & desc = "Collectable <no desc>") :
111 CollectableCommon(parent, name,
114 parentid,
115 desc)
116 {
117 collected_object_ = collected_object;
118
119 // Get an initial value, if available
120 if(collected_object) {
121 initialize(*collected_object);
122 }
123 }
124
133 const std::string& name,
134 uint64_t parentid = 0,
135 const std::string & desc = "Collectable <manual, no desc>") :
136 CollectableCommon(parent, name, nullptr, parentid, desc)
137 {
138 // Can't auto collect without setting collected_object_
140 }
141
144
149 void initialize(const ValueType & val) {
150 //TODO cnyce: handle initial value
151 (void)val;
152 }
153
154 template <typename T>
155 requires MetaStruct::is_any_pointer_v<T>
156 void initialize(const T & val) {
157 if (val) {
158 initialize(*val);
159 }
160 }
161
164 void collect(const ValueType & val) {
166 {
168 }
169 }
170
171 template <typename T>
172 requires MetaStruct::is_any_pointer_v<T>
173 void collect(const T & val) {
174 if (val) {
175 collect(*val);
176 } else {
177 closeRecord();
178 }
179 }
180
191 void collectWithDuration(const ValueType & val, sparta::Clock::Cycle duration) {
193 {
194 if(duration != 0) {
195 ev_close_record_.preparePayload(false)->schedule(duration);
196 }
197 collect(val);
198 }
199 }
200
201 template <typename T>
202 requires MetaStruct::is_any_pointer_v<T>
203 void collectWithDuration(const T & val, sparta::Clock::Cycle duration) {
204 if (val) {
205 collectWithDuration(*val, duration);
206 } else {
207 closeRecord();
208 }
209 }
210
216 void collectWithDuration(sparta::Clock::Cycle duration) {
217 // If pointer has become nullified, close the record
218 if(nullptr == collected_object_) {
219 closeRecord();
220 return;
221 }
223 }
224
227 void collect() override final {
228 // If pointer has become nullified, close the record
229 if(nullptr == collected_object_) {
230 closeRecord();
231 return;
232 }
234 }
235
238 void closeRecord(const bool & = false) override final {
240 entry_point_->closeRecord();
241 }
242 }
243
247 auto_collect_ = false;
248 }
249
251 void createSimDbEntryPoint(simdb::argos::ArgosCollector* argos_collector) override {
252 auto loc = getLocation();
253 auto clk_name = notNull(getClock())->getName();
254 auto type = encodeCollectedType();
255 entry_point_ = argos_collector->createScalarCollector(loc, clk_name, type);
256
257 auto tiny_strings = argos_collector->getTinyStrings();
258 auto enum_inspector = argos_collector->getEnumInspector();
259 auto bit_bucket = std::make_shared<CollectableBitBucket>(tiny_strings, enum_inspector);
260 setBitBucket(bit_bucket);
261 }
262
264 virtual void setBitBucket(const std::shared_ptr<BitBucket>& bit_bucket) {
265 bit_bucket_ = bit_bucket;
266 }
267
268 protected:
269
273 return event_set_;
274 }
275
278 void setCollecting_(bool collect, Collector * collector) override {
279 if(collect && !initial_bytes_.empty()) {
280 //TODO cnyce: handle initial value
281 initial_bytes_.clear();
282 }
283
284 // If the collected object is null, this Collectable
285 // object is to be explicitly collected
287 pipeline_col_ = dynamic_cast<PipelineCollector *>(collector);
288 sparta_assert(pipeline_col_ != nullptr,
289 "Collectables can only added to PipelineCollectors... for now");
290
291 if(collect) {
292 // Add this Collectable to the PipelineCollector's
293 // list of objects requiring collection
294 pipeline_col_->addToAutoCollection(this, collection_phase);
295 }
296 else {
297 // Remove this Collectable from the
298 // PipelineCollector's list of objects requiring
299 // collection
301 }
302 }
303
304 if(!collect) {
305 closeRecord();
306 }
307 }
308
310 virtual void performCollection_(const ValueType & val) = 0;
311
313 const DataT * collected_object_ = nullptr;
314
317
322
324 bool auto_collect_ = true;
325
328 std::vector<char> initial_bytes_;
329
332 std::shared_ptr<BitBucket> bit_bucket_;
333 };
334
335 #define INHERIT_COMMON_INTERFACE \
336 using ValueType = typename CollectableCommon<DataT, collection_phase>::ValueType; \
337 using CollectableCommon<DataT, collection_phase>::CollectableCommon; \
338 using CollectableCommon<DataT, collection_phase>::initialize; \
339 using CollectableCommon<DataT, collection_phase>::collect; \
340 using CollectableCommon<DataT, collection_phase>::collectWithDuration; \
341 using CollectableCommon<DataT, collection_phase>::closeRecord; \
342 using CollectableCommon<DataT, collection_phase>::setManualCollection; \
343 using CollectableCommon<DataT, collection_phase>::getEventSet_; \
344 using CollectableCommon<DataT, collection_phase>::bit_bucket_; \
345 using CollectableCommon<DataT, collection_phase>::entry_point_;
346
349 template<typename DataT, SchedulingPhase collection_phase = SchedulingPhase::Collection, typename = void>
350 class Collectable : public CollectableCommon<DataT, collection_phase>
351 {
352 public:
353 INHERIT_COMMON_INTERFACE
354
355 std::string encodeCollectedType(bool = false) const override final {
356 throw SpartaException("Uncollectable type encountered at ") << this->getLocation();
357 }
358
359 private:
360 void performCollection_(const ValueType &) override final {
361 }
362 };
363
366 template<typename DataT, SchedulingPhase collection_phase>
367 requires use_raw_type<MetaStruct::remove_any_pointer_t<DataT>>
368 class Collectable<DataT, collection_phase, void>
369 : public CollectableCommon<DataT, collection_phase>
370 {
371 public:
372 INHERIT_COMMON_INTERFACE
373
374 std::string encodeCollectedType(bool human_readable = false) const override final {
375 auto type = simdb::demangle_type<ValueType>();
376 if constexpr (std::is_enum_v<ValueType>) {
377 if (human_readable) {
378 using underlying_t = std::underlying_type_t<ValueType>;
379 type += " (enum: " + simdb::demangle_type<underlying_t>() + ")";
380 }
381 }
382 return type;
383 }
384
385 private:
386 void performCollection_(const ValueType & val) override final {
387 constexpr auto dummy_field_id = 0u;
388 bit_bucket_->writeField(val, dummy_field_id);
389
390 if (entry_point_) {
392 }
393 }
394 };
395
398 template<typename DataT, SchedulingPhase collection_phase>
399 requires use_tiny_strings<MetaStruct::remove_any_pointer_t<DataT>>
400 class Collectable<DataT, collection_phase, void>
401 : public CollectableCommon<DataT, collection_phase>
402 {
403 public:
404 INHERIT_COMMON_INTERFACE
405
406 std::string encodeCollectedType(bool = false) const override final {
407 return "string";
408 }
409
410 private:
411 void performCollection_(const ValueType & val) override final {
412 constexpr auto dummy_field_id = 0u;
413 bit_bucket_->writeField(val, dummy_field_id);
414
415 if (entry_point_) {
417 }
418 }
419 };
420
423 template<typename DataT, SchedulingPhase collection_phase>
424 requires use_cast_operator<MetaStruct::remove_any_pointer_t<DataT>>
425 class Collectable<DataT, collection_phase, void>
426 : public CollectableCommon<DataT, collection_phase>
427 {
428 public:
429 INHERIT_COMMON_INTERFACE
430
431 std::string encodeCollectedType(bool human_readable = false) const override final {
432 using converted_t = simdb::type_traits::pod_convertible_t<ValueType>;
433 auto type = simdb::demangle_type<converted_t>();
434 if (human_readable) {
435 type += " (built-in type using cast operator)";
436 }
437 return type;
438 }
439
440 private:
441 void performCollection_(const ValueType & val) override final {
442 constexpr auto dummy_field_id = 0u;
443 using converted_t = simdb::type_traits::pod_convertible_t<ValueType>;
444 auto converted_val = static_cast<converted_t>(val);
445 bit_bucket_->writeField(converted_val, dummy_field_id);
446
447 if (entry_point_) {
449 }
450 }
451 };
452
456 template<typename DataT, SchedulingPhase collection_phase>
457 requires use_dynamic_fields<MetaStruct::remove_any_pointer_t<DataT>>
458 class Collectable<DataT, collection_phase, void>
459 : public CollectableCommon<DataT, collection_phase>
460 {
461 public:
462 INHERIT_COMMON_INTERFACE
463
464 std::string encodeCollectedType(bool human_readable = false) const override final {
465 auto type = std::string("dynamic");
466 if (human_readable) {
467 type += " (" + simdb::demangle_type<ValueType>() + ")";
468 }
469 return type;
470 }
471
472 void createSimDbEntryPoint(simdb::argos::ArgosCollector* argos_collector) override final {
473 std::ostringstream oss;
474 oss << "Collecting non-trivial classes using operator<< only is not supported for now. Use PairDefinition.\n";
475 oss << " - path: " << this->getLocation() << "\n";
476 oss << " - type: " << simdb::demangle_type<MetaStruct::remove_any_pointer_t<DataT>>() << " (scalar)";
477 argos_collector->postNotif(oss.str(), simdb::argos::NotifType::WARNING);
478 }
479
480 private:
481 void setCollecting_(bool, Collector *) override final {
482 }
483
484 void performCollection_(const ValueType &) override final {
485 }
486 };
487
489 template<typename DataT, SchedulingPhase collection_phase>
490 requires use_pair_definition<MetaStruct::remove_any_pointer_t<DataT>>
491 class Collectable<DataT, collection_phase, void>
492 : public CollectableCommon<DataT, collection_phase>
493 , public PairCollector<typename MetaStruct::remove_any_pointer_t<DataT>::SpartaPairDefinitionType>
494 {
495 public:
496 INHERIT_COMMON_INTERFACE
497
498 std::string encodeCollectedType(bool human_readable = false) const override final {
499 auto type = simdb::demangle_type<ValueType>();
500 if (human_readable) {
501 type += " (using PairDefinition)";
502 }
503 return type;
504 }
505
509 simdb::DatabaseManager* db_mgr,
510 std::set<std::string>& serialized_types) override final
511 {
512 auto root_dtype = encodeCollectedType();
513 if(serialized_types.count(root_dtype)) {
514 return;
515 }
516
517 const int32_t schema_id =
518 db_mgr->INSERT(SQL_TABLE("DataTypeSchemas"), SQL_VALUES(root_dtype))->getId();
519
520 using PairDef = typename ValueType::SpartaPairDefinitionType;
521 PairDef pair_def;
522 sparta::PairCache pair_cache;
523 pair_def.finalizeKeys(&pair_cache);
524
525 const auto & names = pair_cache.getNameStrings();
526 const auto & dtypes = pair_def.getLeafArgosDtypeStrings();
527 const auto & formatters = pair_cache.getFormatVector();
528 sparta_assert(names.size() == dtypes.size());
529 sparta_assert(formatters.size() == names.size());
530
531 std::vector<std::string> format_strings;
532 for(size_t i = 0; i < formatters.size(); ++i) {
533 std::string fmt_str;
534 switch(formatters[i]) {
535 case PairFormatter::HEX:
536 fmt_str = "HEX"; break;
537 case PairFormatter::OCTAL:
538 fmt_str = "OCT"; break;
539 default: break;
540 }
541 format_strings.push_back(fmt_str);
542 }
543
544 bool verbose = false;
545 if (auto sim = this->getSimulation()) {
546 verbose = sim->getSimulationConfiguration()->simdb_config.verboseMode();
547 }
548
549 if (verbose) {
550 std::cout << "\nSerializing PairDefinition to database for '" << root_dtype << "'...\n";
551 }
552 for(size_t i = 0; i < names.size(); ++i) {
553 if (verbose) {
554 std::cout << "\t" << names[i] << ", " << dtypes[i];
555 if (!format_strings[i].empty()) {
556 std::cout << " (" << format_strings[i] << ")";
557 }
558 std::cout << "\n";
559 }
560
561 db_mgr->INSERT(SQL_TABLE("DataTypeNodes"),
562 SQL_VALUES(schema_id,
563 names[i],
564 dtypes[i],
565 format_strings[i]));
566 }
567 serialized_types.insert(root_dtype);
568 }
569
570 void setBitBucket(const std::shared_ptr<BitBucket>& bit_bucket) override final {
571 // Share the BitBucket with the Pairs
572 setBitBucket_(bit_bucket);
573
574 // Let the base class own the bit bucket
576 }
577
580 std::string dumpNameValuePairs(const DataT & val) {
581 collect_(val);
582 std::ostringstream ss;
583 for(const auto & pairs : this->getPEventLogVector()){
584 ss << pairs.first << "(" << pairs.second << ") ";
585 }
586 return ss.str();
587 }
588
589 private:
590 typedef typename ValueType::SpartaPairDefinitionType PairDef_t;
591 using PairCollector<PairDef_t>::collect_;
592 using PairCollector<PairDef_t>::setBitBucket_;
593
594 void performCollection_(const ValueType & val) override final {
595 if (!this->isIterableCollectorBin()) {
596 bit_bucket_->clear();
597 }
598 collect_(val);
599
600 if (entry_point_) {
601 static_cast<CollectableBitBucket*>(bit_bucket_.get())->writeTo(entry_point_);
602 }
603 }
604
605 void generateCollectionString_() override {}
606 };
607
608 }//namespace collection
609}//namespace sparta
File that defines the EventSet class.
File that defines the PayloadEvent class.
Class to facilitate pipeline collection operations.
File that defines the phases used in simulation.
Simulation setup base class.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
#define CREATE_SPARTA_HANDLER_WITH_DATA(clname, meth, dataT)
Set of Events that a unit (or sparta::TreeNode, sparta::Resource) contains and are visible through a ...
Definition EventSet.hpp:26
Class to schedule a Scheduleable in the future with a payload, typed on both the data type and the sc...
ScheduleableHandle preparePayload(const DataT &payload)
Prepare a Scheduleable Payload for scheduling either now or later.
void schedule()
Schedule this event with its pre-set delay using the pre-set Clock.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:204
app::Simulation * getSimulation() const
Gets the simulation (if any) associated with this tree.
static const group_idx_type GROUP_IDX_NONE
GroupIndex indicating that a node has no group index because it belongs to no group.
Definition TreeNode.hpp:302
std::string getLocation() const override final
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:313
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
BitBucket implementation for Collectable objects (whether "standalone" or inside an IterableCollector...
void writeTo(simdb::argos::EntryPoint *entry_point) override final
Called when using a standalone Collectable.
Common code for all Collectable implementations below.
void setCollecting_(bool collect, Collector *collector) override
void createSimDbEntryPoint(simdb::argos::ArgosCollector *argos_collector) override
Collectable classes must be able to register themselves with the ArgosCollector.
CollectableCommon(sparta::TreeNode *parent, const std::string &name, const std::string &group, uint32_t index, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated, part of a group.
bool auto_collect_
Should we auto-collect?
const DataT * collected_object_
The annotation object to be collected.
CollectableCommon(sparta::TreeNode *parent, const std::string &name, const DataT *collected_object, uint64_t parentid=0, const std::string &desc="Collectable <no desc>")
Construct the Collectable.
std::shared_ptr< BitBucket > bit_bucket_
CollectableCommon(sparta::TreeNode *parent, const std::string &name, uint64_t parentid=0, const std::string &desc="Collectable <manual, no desc>")
Construct the Collectable, no data object associated.
virtual void setBitBucket(const std::shared_ptr< BitBucket > &bit_bucket)
Set the BitBucket (byte buffers for SimDB EntryPoint)
void setManualCollection()
Do not perform any automatic collection The SchedulingPhase is ignored.
void collectWithDuration(const ValueType &val, sparta::Clock::Cycle duration)
Explicitly collect a value for the given duration.
PipelineCollector * pipeline_col_
Ze Collec-tor.
EventSet & getEventSet_()
Get a reference to the internal event set.
void collect(const ValueType &val)
virtual void performCollection_(const ValueType &val)=0
Subclasses are responsible for collecting values and writing the bytes.
virtual ~CollectableCommon()
Virtual destructor – does nothing.
void collectWithDuration(sparta::Clock::Cycle duration)
Calls collectWithDuration using the internal collected_object_ specified at construction.
void initialize(const ValueType &val)
For manual collection, provide an initial value.
void closeRecord(const bool &=false) override final
An abstract type of TreeNode that has virtual calls to start collection on this node,...
virtual std::string encodeCollectedType(bool human_readable=false) const =0
Encode the collected data type in a way Argos python deserializers will understand:
simdb::argos::EntryPoint * entry_point_
Entry point into the SimDB collection system.
bool isCollected() const
Determine whether or not this node has collection turned on or off.
bool isIterableCollectorBin() const
Check if this CollectableTreeNode is a Collectable inside an IterableCollector.
void setBitBucket(const std::shared_ptr< BitBucket > &bit_bucket) override final
Set the BitBucket (byte buffers for SimDB EntryPoint)
void createSimDbEntryPoint(simdb::argos::ArgosCollector *argos_collector) override final
Collectable classes must be able to register themselves with the ArgosCollector.
std::string dumpNameValuePairs(const DataT &val)
Strictly a Debug/Testing API. Never to be called in real modeler's code.
void serializeStructSchema(simdb::DatabaseManager *db_mgr, std::set< std::string > &serialized_types) override final
INHERIT_COMMON_INTERFACE std::string encodeCollectedType(bool human_readable=false) const override final
Encode the collected data type in a way Argos python deserializers will understand:
INHERIT_COMMON_INTERFACE std::string encodeCollectedType(bool=false) const override final
Encode the collected data type in a way Argos python deserializers will understand:
INHERIT_COMMON_INTERFACE std::string encodeCollectedType(bool=false) const override final
Encode the collected data type in a way Argos python deserializers will understand:
A non-templated base class that all Collectors should inherit from.
Definition Collector.hpp:23
A class that facilitates all universal pipeline collection operations such as outputting finalized re...
void removeFromAutoCollection(CollectableTreeNode *ctn)
Remove the given CollectableTreeNode from collection.
void addToAutoCollection(CollectableTreeNode *ctn, SchedulingPhase collection_phase=SchedulingPhase::Tick)
Add the CollectableTreeNode to auto collection.
Macros for handling exponential backoff.
T * notNull(T *p)
Ensures that a pointer is not null.
Definition Utils.hpp:235
static constexpr bool value
Test to see what happens when we invoke.