@@ -366,15 +366,67 @@ class BaseSensorPtr {
366
366
367
367
using BaseFilterPtr = std::unique_ptr<BaseFilter>;
368
368
369
- class Magnitude {
370
- private:
371
- static unsigned char _counts[MAGNITUDE_MAX];
369
+ // .001.002.003.004
370
+ using Slot = std::array<char , 16 >;
371
+
372
+ struct SlotValues {
373
+ unsigned char id;
374
+ unsigned char index;
375
+ unsigned char type;
376
+ unsigned char slot;
377
+ };
372
378
373
- public:
374
- static size_t counts (unsigned char type) {
375
- return _counts[type];
376
- }
379
+ constexpr char dec_digit2char (char c) {
380
+ return ((c >= 0 ) && (c <= 9 ))
381
+ ? (' 0' + c)
382
+ : ' 0' ;
383
+ }
384
+
385
+ #if __cplusplus >= 201603L
386
+ constexpr
387
+ #endif
388
+ std::array<char , 3 > format_slot_number_impl (unsigned char number) {
389
+ return std::array<char , 3 >{
390
+ number > 100
391
+ ? dec_digit2char (number / 100 )
392
+ : ' 0' ,
393
+ number > 10
394
+ ? dec_digit2char ((number / 10 ) % 10 )
395
+ : ' 0' ,
396
+ number
397
+ ? dec_digit2char (number % 10 )
398
+ : ' 0' ,
399
+ };
400
+ }
401
+
402
+ // aka std::to_chars(it, it + 3, 10), prefixed with zeroes and a dot
403
+ // (unchecked for bounds, always assuming `format_slot` as caller)
404
+ template <typename T>
405
+ T append_slot_number_impl (T it, unsigned char value) {
406
+ const auto raw = format_slot_number_impl (value);
407
+
408
+ *(it++) = ' .' ;
409
+ *(it++) = raw[0 ];
410
+ *(it++) = raw[1 ];
411
+ *(it++) = raw[2 ];
412
+
413
+ return it;
414
+ }
415
+
416
+ Slot make_slot (SlotValues values) {
417
+ Slot out;
418
+
419
+ auto it = out.begin ();
420
+ it = append_slot_number_impl (it, values.id );
421
+ it = append_slot_number_impl (it, values.index );
422
+ it = append_slot_number_impl (it, values.type );
423
+ append_slot_number_impl (it, values.slot );
424
+
425
+ return out;
426
+ }
377
427
428
+ class Magnitude {
429
+ public:
378
430
Magnitude () = delete ;
379
431
380
432
Magnitude (const Magnitude&) = delete ;
@@ -383,13 +435,16 @@ class Magnitude {
383
435
Magnitude (Magnitude&& other) noexcept = default ;
384
436
Magnitude& operator =(Magnitude&&) noexcept = default ;
385
437
386
- Magnitude (BaseSensorPtr, unsigned char slot, unsigned char type);
438
+ Magnitude (BaseSensorPtr sensor) :
439
+ sensor (std::move(sensor))
440
+ {}
387
441
388
442
BaseSensorPtr sensor; // Sensor object, *cannot be empty*
389
- unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement
390
443
unsigned char type; // Type of measurement, returned by the BaseSensor::type(slot)
444
+ unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement
391
445
392
446
unsigned char index_global; // N'th magnitude of it's type, across all of the active sensors
447
+ unsigned char slot_global; // Global slot aka index of the sensor, across all of the active sensors
393
448
394
449
Unit units { Unit::None }; // Current units of measurement
395
450
unsigned char decimals { 0u }; // Number of decimals in textual representation
@@ -422,17 +477,6 @@ static_assert(
422
477
" std::vector<Magnitude> should only use move ctor"
423
478
);
424
479
425
- Magnitude::Magnitude (BaseSensorPtr sensor, unsigned char slot, unsigned char type) :
426
- sensor (std::move(sensor)),
427
- slot (slot),
428
- type (type),
429
- index_global (_counts[type])
430
- {
431
- ++_counts[type];
432
- }
433
-
434
- unsigned char Magnitude::_counts[MAGNITUDE_MAX] = {0 };
435
-
436
480
bool isEmon (BaseSensorPtr sensor) {
437
481
return (sensor->kind () == BaseEmonSensor::Kind)
438
482
|| (sensor->kind () == BaseAnalogEmonSensor::Kind);
@@ -1252,6 +1296,25 @@ constexpr bool ratio_supported(unsigned char type) {
1252
1296
1253
1297
} // namespace traits
1254
1298
1299
+ namespace internal {
1300
+
1301
+ unsigned char instance_count[SENSOR_ID_MAX]{};
1302
+ unsigned char types_count[MAGNITUDE_MAX]{};
1303
+
1304
+ } // namespace internal
1305
+
1306
+ unsigned char instance_count_add (unsigned char id) {
1307
+ return ++internal::instance_count[id];
1308
+ }
1309
+
1310
+ unsigned char types_count_add (unsigned char type) {
1311
+ return ++internal::types_count[type];
1312
+ }
1313
+
1314
+ unsigned char types_count (unsigned char type) {
1315
+ return internal::types_count[type];
1316
+ }
1317
+
1255
1318
namespace build {
1256
1319
1257
1320
static constexpr double correction (unsigned char type) {
@@ -1283,6 +1346,20 @@ String format(const Magnitude& magnitude, ValuePair value) {
1283
1346
return format (magnitude, value.value );
1284
1347
}
1285
1348
1349
+ String format_slot (const Magnitude& magnitude) {
1350
+ const auto slot = make_slot (
1351
+ SlotValues{
1352
+ .id = magnitude.sensor ->id (),
1353
+ .index = magnitude.slot_global ,
1354
+ .type = magnitude.type ,
1355
+ .slot = magnitude.slot ,
1356
+ });
1357
+
1358
+ const auto out = StringView (slot.data (), slot.size ());
1359
+
1360
+ return out.toString ();
1361
+ }
1362
+
1286
1363
String name (unsigned char type) {
1287
1364
const char * result = nullptr ;
1288
1365
@@ -1539,7 +1616,7 @@ String topic(const Magnitude& magnitude) {
1539
1616
1540
1617
String topicWithIndex (const Magnitude& magnitude) {
1541
1618
auto out = topic (magnitude);
1542
- if (sensor::build::useIndex () || (Magnitude::counts (magnitude.type ) > 1 )) {
1619
+ if (sensor::build::useIndex () || (types_count (magnitude.type ) > 1 )) {
1543
1620
out += ' /' + String (magnitude.index_global , 10 );
1544
1621
}
1545
1622
@@ -1682,16 +1759,19 @@ ReadHandlers report_handlers;
1682
1759
1683
1760
} // namespace internal
1684
1761
1685
- size_t count (unsigned char type) {
1686
- return Magnitude::counts (type);
1687
- }
1688
-
1689
1762
size_t count () {
1690
1763
return internal::magnitudes.size ();
1691
1764
}
1692
1765
1693
- Magnitude& add (BaseSensorPtr sensor, unsigned char slot, unsigned char type) {
1694
- internal::magnitudes.emplace_back (sensor, slot, type);
1766
+ Magnitude& add (BaseSensorPtr sensor, unsigned char type, unsigned char slot) {
1767
+ Magnitude out (sensor);
1768
+
1769
+ out.type = type;
1770
+ out.slot = slot;
1771
+ out.index_global = types_count_add (type);
1772
+ out.slot_global = instance_count_add (sensor->id ());
1773
+
1774
+ internal::magnitudes.emplace_back (std::move (out));
1695
1775
return internal::magnitudes.back ();
1696
1776
}
1697
1777
@@ -1731,7 +1811,7 @@ void forEachInstance(T&& callback) {
1731
1811
template <typename T>
1732
1812
void forEachCounted (T&& callback) {
1733
1813
for (unsigned char type = MAGNITUDE_NONE + 1 ; type < MAGNITUDE_MAX; ++type) {
1734
- if (count (type)) {
1814
+ if (types_count (type)) {
1735
1815
callback (type);
1736
1816
}
1737
1817
}
@@ -1741,7 +1821,7 @@ void forEachCounted(T&& callback) {
1741
1821
template <typename T>
1742
1822
bool forEachCountedCheck (T&& callback) {
1743
1823
for (unsigned char type = MAGNITUDE_NONE + 1 ; type < MAGNITUDE_MAX; ++type) {
1744
- if (count (type) && callback (type)) {
1824
+ if (types_count (type) && callback (type)) {
1745
1825
return true ;
1746
1826
}
1747
1827
}
@@ -1783,6 +1863,7 @@ Value value(const Magnitude& magnitude, double value, Unit units) {
1783
1863
return Value{
1784
1864
.type = magnitude.type ,
1785
1865
.index = magnitude.index_global ,
1866
+ .slot = format_slot (magnitude),
1786
1867
.units = units,
1787
1868
.decimals = magnitude.decimals ,
1788
1869
.topic = topicWithIndex (magnitude),
@@ -3057,7 +3138,7 @@ void update(const Magnitude& magnitude, bool persistent) {
3057
3138
3058
3139
void reset () {
3059
3140
for (auto type : magnitude::traits::ratio_types) {
3060
- for (size_t index = 0 ; index < Magnitude::counts (type); ++index ) {
3141
+ for (size_t index = 0 ; index < magnitude::types_count (type); ++index ) {
3061
3142
delSetting (settings::keys::get (settings::prefix::get (type), settings::suffix::Ratio, index ));
3062
3143
}
3063
3144
}
@@ -3291,7 +3372,7 @@ void types(JsonObject& root) {
3291
3372
espurna::web::ws::EnumerablePayload payload{root, STRING_VIEW (" types" )};
3292
3373
payload (STRING_VIEW (" values" ), {MAGNITUDE_NONE + 1 , MAGNITUDE_MAX},
3293
3374
[](size_t type) {
3294
- return Magnitude::counts (type) > 0 ;
3375
+ return magnitude::types_count (type) > 0 ;
3295
3376
},
3296
3377
{{STRING_VIEW (" type" ), [](JsonArray& out, size_t index ) {
3297
3378
out.add (index );
@@ -3601,7 +3682,7 @@ bool tryHandle(ApiRequest& request, unsigned char type, T&& callback) {
3601
3682
size_t index = 0 ;
3602
3683
if (request.wildcards ()) {
3603
3684
const auto param = request.wildcard (0 );
3604
- if (!::tryParseId (param, magnitude::count (type), index )) {
3685
+ if (!::tryParseId (param, magnitude::types_count (type), index )) {
3605
3686
return false ;
3606
3687
}
3607
3688
}
@@ -3632,7 +3713,7 @@ void setup() {
3632
3713
3633
3714
magnitude::forEachCounted ([](unsigned char type) {
3634
3715
auto pattern = magnitude::topic (type);
3635
- if (sensor::build::useIndex () || (magnitude::count (type) > 1 )) {
3716
+ if (sensor::build::useIndex () || (magnitude::types_count (type) > 1 )) {
3636
3717
pattern += STRING_VIEW (" /+" );
3637
3718
}
3638
3719
@@ -3685,7 +3766,7 @@ void report(const Value& report, const Magnitude& magnitude) {
3685
3766
}
3686
3767
3687
3768
void callback (unsigned int type, StringView topic, StringView payload) {
3688
- if (!magnitude::count (MAGNITUDE_ENERGY)) {
3769
+ if (!magnitude::types_count (MAGNITUDE_ENERGY)) {
3689
3770
return ;
3690
3771
}
3691
3772
@@ -3700,7 +3781,7 @@ void callback(unsigned int type, StringView topic, StringView payload) {
3700
3781
}
3701
3782
3702
3783
size_t index ;
3703
- if (!tryParseIdPath (t, magnitude::count (MAGNITUDE_ENERGY), index )) {
3784
+ if (!tryParseIdPath (t, magnitude::types_count (MAGNITUDE_ENERGY), index )) {
3704
3785
break ;
3705
3786
}
3706
3787
@@ -3730,6 +3811,26 @@ void setup() {
3730
3811
namespace terminal {
3731
3812
namespace commands {
3732
3813
3814
+ PROGMEM_STRING (Sensors, " SENSORS" );
3815
+
3816
+ void sensors (::terminal::CommandContext&& ctx) {
3817
+ if (!magnitude::count ()) {
3818
+ terminalError (ctx, F (" No magnitudes" ));
3819
+ return ;
3820
+ }
3821
+
3822
+ size_t index = 0 ;
3823
+ for (const auto & magnitude : magnitude::internal::magnitudes) {
3824
+ ctx.output .printf_P (PSTR (" %2zu * %s @ %s => %s\n " ),
3825
+ index ++,
3826
+ magnitude::topicWithIndex (magnitude).c_str (),
3827
+ magnitude::description (magnitude).c_str (),
3828
+ magnitude::format_slot (magnitude).c_str ());
3829
+ }
3830
+
3831
+ terminalOK (ctx);
3832
+ }
3833
+
3733
3834
PROGMEM_STRING (Magnitudes, " MAGNITUDES" );
3734
3835
3735
3836
void magnitudes (::terminal::CommandContext&& ctx) {
@@ -3741,7 +3842,8 @@ void magnitudes(::terminal::CommandContext&& ctx) {
3741
3842
size_t index = 0 ;
3742
3843
for (const auto & magnitude : magnitude::internal::magnitudes) {
3743
3844
ctx.output .printf_P (PSTR (" %2zu * %s @ %s read %s reported %s\n " ),
3744
- index ++, magnitude::topicWithIndex (magnitude).c_str (),
3845
+ index ++,
3846
+ magnitude::topicWithIndex (magnitude).c_str (),
3745
3847
magnitude::description (magnitude).c_str (),
3746
3848
magnitude::format_with_units (magnitude, magnitude.last ).c_str (),
3747
3849
magnitude::format_with_units (magnitude, magnitude.reported ).c_str ());
@@ -3820,6 +3922,7 @@ void energy(::terminal::CommandContext&& ctx) {
3820
3922
}
3821
3923
3822
3924
static constexpr ::terminal::Command List[] PROGMEM {
3925
+ {Sensors, commands::sensors},
3823
3926
{Magnitudes, commands::magnitudes},
3824
3927
{Expected, commands::expected},
3825
3928
{ResetRatios, commands::reset_ratios},
@@ -4014,7 +4117,7 @@ bool init() {
4014
4117
4015
4118
const auto slots = sensor->count ();
4016
4119
for (auto slot = 0 ; slot < slots; ++slot) {
4017
- auto & result = magnitude::add (sensor, slot, sensor->type (slot));
4120
+ auto & result = magnitude::add (sensor, sensor->type (slot), slot );
4018
4121
configure_magnitude (result);
4019
4122
4020
4123
// Energy tracking is implemented by looking at the specific magnitude & it's index at read time
0 commit comments