From f1f73043cfba0b8355d5feb1b12f5a55759fc6d9 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Thu, 18 Apr 2024 05:02:22 +0000 Subject: [PATCH] P16-1075 Add formating hints to MIBs --- sdk/include/pixie/mib.hpp | 44 +++++++- sdk/src/pixie/mib.cpp | 173 ++++++++++++++++++++++++++++-- tests/unit/sdk/test_pixie_mib.cpp | 48 ++++++++- 3 files changed, 249 insertions(+), 16 deletions(-) diff --git a/sdk/include/pixie/mib.hpp b/sdk/include/pixie/mib.hpp index 84052e69..e58b0e74 100644 --- a/sdk/include/pixie/mib.hpp +++ b/sdk/include/pixie/mib.hpp @@ -63,6 +63,20 @@ enum struct type { timestamp /**< Time stamp in nanoseconds */ }; +/** + * String formating hints + */ +enum struct hint { + fmt_defaults, /**< Use the default format for the MIB type */ + fmt_no_quotes, /**< If string do not wrap in quoutes, default is quotes */ + fmt_boolnum, /**< If boolean use 0 or 1, default is boolalpha */ + fmt_dec, /**< If integer or uinteger use decimal, default is decimal */ + fmt_oct, /**< If integer or uinteger use octal */ + fmt_hex, /**< If integer or uinteger use hexadecimal */ + fmt_fixed, /**< If real use fixed point, default is fixed point */ + fmt_iso8601 /**< If timestamp use ISO8601 format */ +}; + /** * The type of the types for a MIB */ @@ -441,6 +455,22 @@ struct node_base { using lock_type = std::mutex; using lock_guard = std::lock_guard; + using hint_flag_type = uint64_t; + + static constexpr hint_flag_type fmt_mask = 0x1f; + static constexpr hint_flag_type fmt_base = 0; + static constexpr hint_flag_type fixed_mask = 0x1f; + static constexpr hint_flag_type fixed_base = 5; + static constexpr hint_flag_type fmt_defaults = 0; + static constexpr hint_flag_type fmt_no_quotes = 1; + static constexpr hint_flag_type fmt_boolnum = 2; + static constexpr hint_flag_type fmt_dec = 3; + static constexpr hint_flag_type fmt_oct = 4; + static constexpr hint_flag_type fmt_hex = 5; + static constexpr hint_flag_type fmt_fixed = 6; + static constexpr hint_flag_type fmt_iso8601 = 7; + + lock_type lock; const name_type name; const type type_; const bool read_only; @@ -451,7 +481,7 @@ struct node_base { event_func set_event; event_func timer_event; bool in_event_call; - lock_type lock; + hint_flag_type hints; node_base(const name_type& name, const type type_, const bool enabled); /* @@ -486,6 +516,8 @@ struct node_base { void call_timer_event_func(); std::string str(bool attributes); + void set_hint(hint hint_); + bool get_hint(hint hint_); }; /** @@ -556,6 +588,8 @@ struct node { template bool operator<=(const T& val) const; template bool operator>=(const T& val) const; + node& operator=(hint hint_); + operator string(); operator boolean(); operator integer(); @@ -573,6 +607,8 @@ struct node { void set_value(const char* val); std::string str(bool attributes = false); + void set_hint(hint hint_); + bool get_hint(hint hint_) const; bool valid() const { if (base) return true; return false; } @@ -685,10 +721,12 @@ template node_base::node_base( template void node_base::set(const T& val) { lock_guard guard(lock); if (read_only) { - throw error(error::code::read_only, "mib::node::set: MIB is read-only"); + throw error( + error::code::read_only, "mib::node::set: MIB is read-only: " + name); } if (write_lock) { - throw error(error::code::read_only, "mib::node::set: MIB is write locked"); + throw error( + error::code::read_only, "mib::node::set: MIB is write locked: " + name); } if (std::is_same_v || std::is_same_v || diff --git a/sdk/src/pixie/mib.cpp b/sdk/src/pixie/mib.cpp index 3a076d10..fef09de5 100644 --- a/sdk/src/pixie/mib.cpp +++ b/sdk/src/pixie/mib.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace xia { namespace mib { @@ -205,7 +206,7 @@ data_type::~data_type() { node_base::node_base(const name_type& name_, const type type__, const bool enabled_) : name(name_), type_(type__), read_only(false), write_lock(false), - enabled(enabled_), in_event_call(false) { + enabled(enabled_), in_event_call(false), hints(0) { /* * A mib can only ever be one type. The union has classes and they * could contain references to resources such as memory. The @@ -488,15 +489,48 @@ void node_base::call_timer_event_func() { } std::string node_base::str(bool attributes) { - std::ostringstream oss; lock_guard guard(lock); call_get_event_func(); + std::ostringstream oss; + oss << std::boolalpha + << std::dec + << std::showbase + << std::fixed; + bool s_fmt_no_quotes = false; + bool ts_fmt_iso8601 = false; + switch (hints & (fmt_mask << fmt_base)) { + case fmt_no_quotes: + s_fmt_no_quotes = true; + break; + case fmt_boolnum: + oss << std::noboolalpha; + break; + case fmt_dec: + break; + case fmt_oct: + oss << std::oct; + break; + case fmt_hex: + oss << std::hex; + break; + case fmt_fixed: + break; + case fmt_iso8601: + ts_fmt_iso8601 = true; + break; + default: + break; + } switch (type_) { case type::string: - oss << '"' << v.s << '"'; + if (s_fmt_no_quotes) { + oss << v.s; + } else { + oss << '"' << v.s << '"'; + } break; case type::boolean: - oss << std::boolalpha << v.b; + oss << v.b; break; case type::integer: oss << static_cast(v.u); @@ -505,12 +539,27 @@ std::string node_base::str(bool attributes) { oss << v.u; break; case type::real: - oss << std::fixed << std::setprecision(9) << v.r; + oss << std::setprecision(9) << v.r; break; case type::timestamp: - oss << v.t.nsecs / 1000000000UL - << '.' << std::setw(9) << std::setfill('0') - << v.t.nsecs % 1000000000UL; + if (ts_fmt_iso8601) { + /* + * Stage the conversion of nanoseconds to a system clock + * time point to cater for different clock precisions on + * different host platforms. Use a temporary system clock + * time point to add the duration to to deal with possible + * differences in the clock types. + */ + std::chrono::duration td = std::chrono::nanoseconds{v.t.nsecs}; + util::time::datetime_timepoint temp_dtp; + auto dp = + std::chrono::time_point_cast(temp_dtp + td); + oss << util::time::datetime_iso8601(dp); + } else { + oss << v.t.nsecs / 1000000000UL + << '.' << std::setw(9) << std::setfill('0') + << v.t.nsecs % 1000000000UL; + } break; } if (attributes) { @@ -530,6 +579,101 @@ std::string node_base::str(bool attributes) { return oss.str(); } +void node_base::set_hint(hint hint_) { + lock_guard guard(lock); + switch (hint_) { + case hint::fmt_defaults: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_defaults; + break; + case hint::fmt_no_quotes: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_no_quotes; + break; + case hint::fmt_boolnum: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_boolnum; + break; + case hint::fmt_dec: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_dec; + break; + case hint::fmt_oct: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_oct; + break; + case hint::fmt_hex: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_hex; + break; + case hint::fmt_fixed: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_fixed; + break; + case hint::fmt_iso8601: + hints &= ~(fmt_mask << fmt_base); + hints |= fmt_iso8601; + break; + default: + break; + } +} + +bool node_base::get_hint(hint hint_) { + lock_guard guard(lock); + bool result = false; + switch (hint_) { + case hint::fmt_defaults: + if ((hints & fmt_defaults) != 0) { + result = true; + } + break; + case hint::fmt_no_quotes: + if ((hints & fmt_no_quotes) != 0) { + result = true; + } + break; + case hint::fmt_boolnum: + if ((hints & fmt_boolnum) != 0) { + result = true; + } + break; + case hint::fmt_dec: + if ((hints & fmt_dec) != 0) { + result = true; + } + break; + case hint::fmt_oct: + if ((hints & fmt_oct) != 0) { + result = true; + } + break; + case hint::fmt_hex: + if ((hints & fmt_hex) != 0) { + result = true; + } + break; + case hint::fmt_fixed: + if ((hints & fmt_fixed) != 0) { + result = true; + } + break; + case hint::fmt_iso8601: + if ((hints & fmt_iso8601) != 0) { + result = true; + } + break; + default: + break; + } + return result; +} + +node& node::operator=(hint hint_) { + set_hint(hint_); + return *this; +} + void node::check_base() const { if (!valid()) { throw error(error::code::read_only, "mib::node::check: not a valid node"); @@ -605,7 +749,8 @@ void node::lock_writes() { node_base::lock_guard guard(base->lock); if (base->read_only) { throw error( - error::code::read_only, "mib::node::lock-write: MIB is read-only"); + error::code::read_only, + "mib::node::lock-write: MIB is read-only: " + base->name); } base->write_lock = true; } @@ -690,6 +835,16 @@ std::string node::str(bool attributes) { return base->str(attributes); } +void node::set_hint(hint hint_) { + check_base(); + base->set_hint(hint_); +} + +bool node::get_hint(hint hint_) const { + check_base(); + return base->get_hint(hint_); +} + void add(const name_type& name, const type type_, const bool enabled) { mib.add(name, type_, enabled); } diff --git a/tests/unit/sdk/test_pixie_mib.cpp b/tests/unit/sdk/test_pixie_mib.cpp index f069b5de..139f4e9f 100644 --- a/tests/unit/sdk/test_pixie_mib.cpp +++ b/tests/unit/sdk/test_pixie_mib.cpp @@ -99,7 +99,7 @@ TEST_SUITE("xia::pixie::mib") { CHECK_NOTHROW(s1.lock_writes()); CHECK(s1.write_locked() == true); CHECK_THROWS_WITH_AS(s1 = "write while locked", - "mib::node::set: MIB is write locked", xia::mib::error); + "mib::node::set: MIB is write locked: s.1", xia::mib::error); CHECK_NOTHROW(s1.unlock_writes()); CHECK(s1.write_locked() == false); CHECK_NOTHROW(s1 = "write while locked"); @@ -286,8 +286,8 @@ TEST_SUITE("xia::pixie::mib") { CHECK_NOTHROW(xia::mib::add_ro_real("r.2.ro", 3.33333)); CHECK_NOTHROW(xia::mib::add_ro_timestamp("t.2.ro", xia::mib::timestamp(987654321))); auto s = xia::mib::node("s.2.ro"); - CHECK_THROWS_WITH_AS(s = "abc", "mib::node::set: MIB is read-only", xia::mib::error); - CHECK_THROWS_WITH_AS(s.lock_writes(), "mib::node::lock-write: MIB is read-only", xia::mib::error); + CHECK_THROWS_WITH_AS(s = "abc", "mib::node::set: MIB is read-only: s.2.ro", xia::mib::error); + CHECK_THROWS_WITH_AS(s.lock_writes(), "mib::node::lock-write: MIB is read-only: s.2.ro", xia::mib::error); } TEST_CASE("MIB: events") { auto s1 = xia::mib::node("s.1"); @@ -509,7 +509,8 @@ TEST_SUITE("xia::pixie::mib") { std::mutex lock; std::string s = "locked ro mib read"; auto ro1 = xia::mib::read_write_lock("rw.2", s, lock); - CHECK_THROWS_WITH_AS(ro1.nod = "write locked set", "mib::node::set: MIB is write locked", xia::mib::error); + CHECK_THROWS_WITH_AS(ro1.nod = "write locked set", + "mib::node::set: MIB is write locked: rw.2", xia::mib::error); CHECK(*ro1 == "locked ro mib read"); std::atomic_int as1 = 100; auto rw3 = xia::mib::read_write("rw.3", as1, xia::mib::rw_mode::rw); @@ -635,4 +636,43 @@ TEST_SUITE("xia::pixie::mib") { xia::mib::timestamp t(1712711064001386000); CHECK(n == t); } + TEST_CASE("MIB: hints") { + xia::mib::node n; + CHECK_NOTHROW(n = xia::mib::find("s.1")); + CHECK(n.str() == "\"string string\""); + CHECK_NOTHROW(n = xia::mib::hint::fmt_no_quotes); + CHECK(n.str() == "string string"); + CHECK_NOTHROW(n = xia::mib::find("b.1")); + CHECK_NOTHROW(n = true); + CHECK(n.str() == "true"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_boolnum); + CHECK(n.str() == "1"); + CHECK_NOTHROW(n = xia::mib::find("i.1")); + CHECK_NOTHROW(n = 128); + CHECK(n.str() == "128"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_dec); + CHECK(n.str() == "128"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_oct); + CHECK(n.str() == "0200"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_hex); + CHECK(n.str() == "0x80"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_hex); + CHECK_NOTHROW(n = xia::mib::find("u.1")); + CHECK_NOTHROW(n = 256); + CHECK(n.str() == "256"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_oct); + CHECK(n.str() == "0400"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_hex); + CHECK(n.str() == "0x100"); + CHECK_NOTHROW(n = xia::mib::find("r.1")); + CHECK_NOTHROW(n = 1.123456789); + CHECK(n.str() == "1.123456789"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_fixed); + CHECK(n.str() == "1.123456789"); + CHECK_NOTHROW(n = xia::mib::find("t.1")); + CHECK_NOTHROW(n.set_value("2024-04-10T01:04:24.123456")); + CHECK(n.str() == "1712711064.123456000"); + CHECK_NOTHROW(n = xia::mib::hint::fmt_iso8601); + CHECK(n.str() == "2024-04-10T01:04:24.123Z"); + } }