Skip to content

Commit fe08a66

Browse files
committed
sns: experiment with magnitude persistent id
ref. #2639 global slot to identify specific sensor+num+type+slot combination for now, not used anywhere but terminal listing
1 parent cfa17df commit fe08a66

File tree

4 files changed

+146
-38
lines changed

4 files changed

+146
-38
lines changed

code/espurna/config/types.h

+3
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@
287287
// These should remain over time, do not modify them, only add new ones at the end
288288
//--------------------------------------------------------------------------------
289289

290+
#define SENSOR_DUMMY_ID 0
290291
#define SENSOR_DHTXX_ID 1
291292
#define SENSOR_DALLAS_ID 2
292293
#define SENSOR_EMON_ANALOG_ID 3
@@ -334,6 +335,8 @@
334335
#define SENSOR_INA219_ID 45
335336
#define SENSOR_A02YYU_ID 46
336337

338+
#define SENSOR_ID_MAX 47
339+
337340
//--------------------------------------------------------------------------------
338341
// Magnitudes
339342
// These should remain over time, do not modify their values, only add new ones at the end

code/espurna/sensor.cpp

+140-37
Original file line numberDiff line numberDiff line change
@@ -366,15 +366,67 @@ class BaseSensorPtr {
366366

367367
using BaseFilterPtr = std::unique_ptr<BaseFilter>;
368368

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+
};
372378

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+
}
377427

428+
class Magnitude {
429+
public:
378430
Magnitude() = delete;
379431

380432
Magnitude(const Magnitude&) = delete;
@@ -383,13 +435,16 @@ class Magnitude {
383435
Magnitude(Magnitude&& other) noexcept = default;
384436
Magnitude& operator=(Magnitude&&) noexcept = default;
385437

386-
Magnitude(BaseSensorPtr, unsigned char slot, unsigned char type);
438+
Magnitude(BaseSensorPtr sensor) :
439+
sensor(std::move(sensor))
440+
{}
387441

388442
BaseSensorPtr sensor; // Sensor object, *cannot be empty*
389-
unsigned char slot; // Sensor slot # taken by the magnitude, used to access the measurement
390443
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
391445

392446
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
393448

394449
Unit units { Unit::None }; // Current units of measurement
395450
unsigned char decimals { 0u }; // Number of decimals in textual representation
@@ -422,17 +477,6 @@ static_assert(
422477
"std::vector<Magnitude> should only use move ctor"
423478
);
424479

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-
436480
bool isEmon(BaseSensorPtr sensor) {
437481
return (sensor->kind() == BaseEmonSensor::Kind)
438482
|| (sensor->kind() == BaseAnalogEmonSensor::Kind);
@@ -1252,6 +1296,25 @@ constexpr bool ratio_supported(unsigned char type) {
12521296

12531297
} // namespace traits
12541298

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+
12551318
namespace build {
12561319

12571320
static constexpr double correction(unsigned char type) {
@@ -1283,6 +1346,20 @@ String format(const Magnitude& magnitude, ValuePair value) {
12831346
return format(magnitude, value.value);
12841347
}
12851348

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+
12861363
String name(unsigned char type) {
12871364
const char* result = nullptr;
12881365

@@ -1539,7 +1616,7 @@ String topic(const Magnitude& magnitude) {
15391616

15401617
String topicWithIndex(const Magnitude& magnitude) {
15411618
auto out = topic(magnitude);
1542-
if (sensor::build::useIndex() || (Magnitude::counts(magnitude.type) > 1)) {
1619+
if (sensor::build::useIndex() || (types_count(magnitude.type) > 1)) {
15431620
out += '/' + String(magnitude.index_global, 10);
15441621
}
15451622

@@ -1682,16 +1759,19 @@ ReadHandlers report_handlers;
16821759

16831760
} // namespace internal
16841761

1685-
size_t count(unsigned char type) {
1686-
return Magnitude::counts(type);
1687-
}
1688-
16891762
size_t count() {
16901763
return internal::magnitudes.size();
16911764
}
16921765

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));
16951775
return internal::magnitudes.back();
16961776
}
16971777

@@ -1731,7 +1811,7 @@ void forEachInstance(T&& callback) {
17311811
template <typename T>
17321812
void forEachCounted(T&& callback) {
17331813
for (unsigned char type = MAGNITUDE_NONE + 1; type < MAGNITUDE_MAX; ++type) {
1734-
if (count(type)) {
1814+
if (types_count(type)) {
17351815
callback(type);
17361816
}
17371817
}
@@ -1741,7 +1821,7 @@ void forEachCounted(T&& callback) {
17411821
template <typename T>
17421822
bool forEachCountedCheck(T&& callback) {
17431823
for (unsigned char type = MAGNITUDE_NONE + 1; type < MAGNITUDE_MAX; ++type) {
1744-
if (count(type) && callback(type)) {
1824+
if (types_count(type) && callback(type)) {
17451825
return true;
17461826
}
17471827
}
@@ -1783,6 +1863,7 @@ Value value(const Magnitude& magnitude, double value, Unit units) {
17831863
return Value{
17841864
.type = magnitude.type,
17851865
.index = magnitude.index_global,
1866+
.slot = format_slot(magnitude),
17861867
.units = units,
17871868
.decimals = magnitude.decimals,
17881869
.topic = topicWithIndex(magnitude),
@@ -3057,7 +3138,7 @@ void update(const Magnitude& magnitude, bool persistent) {
30573138

30583139
void reset() {
30593140
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) {
30613142
delSetting(settings::keys::get(settings::prefix::get(type), settings::suffix::Ratio, index));
30623143
}
30633144
}
@@ -3291,7 +3372,7 @@ void types(JsonObject& root) {
32913372
espurna::web::ws::EnumerablePayload payload{root, STRING_VIEW("types")};
32923373
payload(STRING_VIEW("values"), {MAGNITUDE_NONE + 1, MAGNITUDE_MAX},
32933374
[](size_t type) {
3294-
return Magnitude::counts(type) > 0;
3375+
return magnitude::types_count(type) > 0;
32953376
},
32963377
{{STRING_VIEW("type"), [](JsonArray& out, size_t index) {
32973378
out.add(index);
@@ -3601,7 +3682,7 @@ bool tryHandle(ApiRequest& request, unsigned char type, T&& callback) {
36013682
size_t index = 0;
36023683
if (request.wildcards()) {
36033684
const auto param = request.wildcard(0);
3604-
if (!::tryParseId(param, magnitude::count(type), index)) {
3685+
if (!::tryParseId(param, magnitude::types_count(type), index)) {
36053686
return false;
36063687
}
36073688
}
@@ -3632,7 +3713,7 @@ void setup() {
36323713

36333714
magnitude::forEachCounted([](unsigned char type) {
36343715
auto pattern = magnitude::topic(type);
3635-
if (sensor::build::useIndex() || (magnitude::count(type) > 1)) {
3716+
if (sensor::build::useIndex() || (magnitude::types_count(type) > 1)) {
36363717
pattern += STRING_VIEW("/+");
36373718
}
36383719

@@ -3685,7 +3766,7 @@ void report(const Value& report, const Magnitude& magnitude) {
36853766
}
36863767

36873768
void callback(unsigned int type, StringView topic, StringView payload) {
3688-
if (!magnitude::count(MAGNITUDE_ENERGY)) {
3769+
if (!magnitude::types_count(MAGNITUDE_ENERGY)) {
36893770
return;
36903771
}
36913772

@@ -3700,7 +3781,7 @@ void callback(unsigned int type, StringView topic, StringView payload) {
37003781
}
37013782

37023783
size_t index;
3703-
if (!tryParseIdPath(t, magnitude::count(MAGNITUDE_ENERGY), index)) {
3784+
if (!tryParseIdPath(t, magnitude::types_count(MAGNITUDE_ENERGY), index)) {
37043785
break;
37053786
}
37063787

@@ -3730,6 +3811,26 @@ void setup() {
37303811
namespace terminal {
37313812
namespace commands {
37323813

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+
37333834
PROGMEM_STRING(Magnitudes, "MAGNITUDES");
37343835

37353836
void magnitudes(::terminal::CommandContext&& ctx) {
@@ -3741,7 +3842,8 @@ void magnitudes(::terminal::CommandContext&& ctx) {
37413842
size_t index = 0;
37423843
for (const auto& magnitude : magnitude::internal::magnitudes) {
37433844
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(),
37453847
magnitude::description(magnitude).c_str(),
37463848
magnitude::format_with_units(magnitude, magnitude.last).c_str(),
37473849
magnitude::format_with_units(magnitude, magnitude.reported).c_str());
@@ -3820,6 +3922,7 @@ void energy(::terminal::CommandContext&& ctx) {
38203922
}
38213923

38223924
static constexpr ::terminal::Command List[] PROGMEM {
3925+
{Sensors, commands::sensors},
38233926
{Magnitudes, commands::magnitudes},
38243927
{Expected, commands::expected},
38253928
{ResetRatios, commands::reset_ratios},
@@ -4014,7 +4117,7 @@ bool init() {
40144117

40154118
const auto slots = sensor->count();
40164119
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);
40184121
configure_magnitude(result);
40194122

40204123
// Energy tracking is implemented by looking at the specific magnitude & it's index at read time

code/espurna/sensor.h

+2
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,8 @@ struct Value {
230230
unsigned char type;
231231
unsigned char index;
232232

233+
String slot;
234+
233235
Unit units;
234236
unsigned char decimals;
235237
String topic;

code/espurna/sensors/DummySensor.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct Sensor : public BaseEmonSensor {
3838
{}
3939

4040
unsigned char id() const override {
41-
return 0;
41+
return SENSOR_DUMMY_ID;
4242
}
4343

4444
unsigned char count() const override {

0 commit comments

Comments
 (0)