2022-04-05 22:04:46 +00:00
|
|
|
/*
|
|
|
|
; Project: Open Vehicle Monitor System
|
|
|
|
; Date: 14th March 2017
|
|
|
|
;
|
|
|
|
; Changes:
|
|
|
|
; 1.0 Initial release
|
|
|
|
;
|
|
|
|
; (C) 2011 Michael Stegen / Stegen Electronics
|
|
|
|
; (C) 2011-2017 Mark Webb-Johnson
|
|
|
|
; (C) 2011 Sonny Chen @ EPRO/DX
|
|
|
|
;
|
|
|
|
; Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
; of this software and associated documentation files (the "Software"), to deal
|
|
|
|
; in the Software without restriction, including without limitation the rights
|
|
|
|
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
; copies of the Software, and to permit persons to whom the Software is
|
|
|
|
; furnished to do so, subject to the following conditions:
|
|
|
|
;
|
|
|
|
; The above copyright notice and this permission notice shall be included in
|
|
|
|
; all copies or substantial portions of the Software.
|
|
|
|
;
|
|
|
|
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
; THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __METRICS_H__
|
|
|
|
#define __METRICS_H__
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
#include <map>
|
|
|
|
#include <list>
|
|
|
|
#include <string>
|
|
|
|
#include <bitset>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include <set>
|
|
|
|
#include <vector>
|
|
|
|
#include <atomic>
|
|
|
|
#include "ovms_utils.h"
|
|
|
|
#include "ovms_mutex.h"
|
|
|
|
#include "dbc_number.h"
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
#include "ovms_script.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "ovms_log.h"
|
|
|
|
#define TAG ((const char*)"metric")
|
|
|
|
|
|
|
|
#define METRICS_MAX_MODIFIERS 32
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
typedef enum : uint8_t
|
|
|
|
{
|
|
|
|
Other = 0,
|
|
|
|
Native = Other,
|
|
|
|
|
|
|
|
Kilometers = 10,
|
|
|
|
Miles = 11,
|
|
|
|
Meters = 12,
|
|
|
|
Feet = 13,
|
|
|
|
|
|
|
|
Celcius = 20,
|
|
|
|
Fahrenheit = 21,
|
|
|
|
|
|
|
|
kPa = 30,
|
|
|
|
Pa = 31,
|
|
|
|
PSI = 32,
|
2022-04-21 18:50:06 +00:00
|
|
|
Bar = 33,
|
2022-04-21 18:55:17 +00:00
|
|
|
mBar = 34,
|
2022-04-05 22:04:46 +00:00
|
|
|
|
|
|
|
Volts = 40,
|
|
|
|
Amps = 41,
|
|
|
|
AmpHours = 42,
|
|
|
|
kW = 43,
|
|
|
|
kWh = 44,
|
|
|
|
Watts = 45,
|
|
|
|
WattHours = 46,
|
2022-04-21 18:50:06 +00:00
|
|
|
kVA = 47,
|
2022-04-05 22:04:46 +00:00
|
|
|
|
|
|
|
Seconds = 50,
|
|
|
|
Minutes = 51,
|
|
|
|
Hours = 52,
|
|
|
|
TimeUTC = 53,
|
|
|
|
TimeLocal = 54,
|
|
|
|
|
|
|
|
Degrees = 60,
|
|
|
|
|
|
|
|
Kph = 61,
|
|
|
|
Mph = 62,
|
|
|
|
|
|
|
|
// Acceleration:
|
|
|
|
KphPS = 71, // Kph per second
|
|
|
|
MphPS = 72, // Mph per second
|
|
|
|
MetersPSS = 73, // Meters per second^2
|
|
|
|
|
|
|
|
dbm = 80, // Signal Quality (in dBm)
|
|
|
|
sq = 81, // Signal Quality (in SQ units)
|
|
|
|
|
|
|
|
Percentage = 90,
|
2022-04-21 18:50:06 +00:00
|
|
|
rpm = 91,
|
2022-04-05 22:04:46 +00:00
|
|
|
|
|
|
|
// Energy consumption:
|
|
|
|
WattHoursPK = 100, // Wh/km
|
|
|
|
WattHoursPM = 101, // Wh/mi
|
|
|
|
|
|
|
|
// Torque:
|
|
|
|
Nm = 110,
|
|
|
|
} metric_unit_t;
|
|
|
|
|
|
|
|
typedef enum : uint8_t
|
|
|
|
{
|
|
|
|
NeverDefined = 0,
|
|
|
|
FirstDefined,
|
|
|
|
Defined
|
|
|
|
} metric_defined_t;
|
|
|
|
|
|
|
|
extern const char* OvmsMetricUnitLabel(metric_unit_t units);
|
|
|
|
extern int UnitConvert(metric_unit_t from, metric_unit_t to, int value);
|
|
|
|
extern float UnitConvert(metric_unit_t from, metric_unit_t to, float value);
|
|
|
|
|
|
|
|
typedef uint32_t persistent_value_t;
|
|
|
|
|
|
|
|
struct persistent_values
|
|
|
|
{
|
|
|
|
std::size_t namehash;
|
|
|
|
persistent_value_t value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct persistent_metrics
|
|
|
|
{
|
|
|
|
u_long magic;
|
|
|
|
int version;
|
|
|
|
unsigned int serial;
|
|
|
|
size_t size;
|
|
|
|
int used;
|
|
|
|
persistent_values values[100];
|
|
|
|
};
|
|
|
|
|
|
|
|
extern persistent_values *pmetrics_find(const char *name);
|
|
|
|
extern persistent_values *pmetrics_register(const char *name);
|
|
|
|
|
|
|
|
class OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetric(const char* name, uint16_t autostale = 0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
virtual ~OvmsMetric();
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
std::string AsUnitString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
virtual float AsFloat(const float defvalue = 0, metric_unit_t units = Other);
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
virtual void DukPush(DukContext &dc);
|
|
|
|
#endif
|
|
|
|
virtual bool SetValue(std::string value);
|
|
|
|
virtual bool SetValue(dbcNumber& value);
|
|
|
|
virtual void operator=(std::string value);
|
|
|
|
virtual uint32_t LastModified();
|
|
|
|
virtual uint32_t Age();
|
|
|
|
virtual bool CheckPersist();
|
|
|
|
virtual bool IsDefined();
|
|
|
|
virtual bool IsFirstDefined();
|
|
|
|
virtual bool IsPersistent();
|
|
|
|
virtual bool IsStale();
|
|
|
|
virtual bool IsString() { return false; };
|
|
|
|
virtual bool IsFresh();
|
|
|
|
virtual void RefreshPersist();
|
|
|
|
virtual void SetStale(bool stale);
|
|
|
|
virtual void SetAutoStale(uint16_t seconds);
|
|
|
|
virtual metric_unit_t GetUnits();
|
|
|
|
virtual bool IsModified(size_t modifier);
|
|
|
|
virtual bool IsModifiedAndClear(size_t modifier);
|
|
|
|
virtual void ClearModified(size_t modifier);
|
|
|
|
virtual void SetModified(bool changed=true);
|
|
|
|
virtual void Clear();
|
|
|
|
|
|
|
|
public:
|
|
|
|
OvmsMetric* m_next;
|
|
|
|
const char* m_name;
|
|
|
|
std::atomic_ulong m_modified;
|
|
|
|
uint32_t m_lastmodified;
|
|
|
|
uint16_t m_autostale;
|
|
|
|
metric_unit_t m_units;
|
|
|
|
metric_defined_t m_defined;
|
|
|
|
bool m_stale;
|
|
|
|
bool m_persist;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OvmsMetricBool : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricBool(const char* name, uint16_t autostale=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
virtual ~OvmsMetricBool();
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
float AsFloat(const float defvalue = 0, metric_unit_t units = Other);
|
|
|
|
int AsBool(const bool defvalue = false);
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc);
|
|
|
|
#endif
|
|
|
|
bool SetValue(bool value);
|
|
|
|
void operator=(bool value) { SetValue(value); }
|
|
|
|
bool SetValue(std::string value);
|
|
|
|
bool SetValue(dbcNumber& value);
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
void Clear();
|
|
|
|
bool CheckPersist();
|
|
|
|
void RefreshPersist();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
bool m_value;
|
|
|
|
bool* m_valuep;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OvmsMetricInt : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricInt(const char* name, uint16_t autostale=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
virtual ~OvmsMetricInt();
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
float AsFloat(const float defvalue = 0, metric_unit_t units = Other);
|
|
|
|
int AsInt(const int defvalue = 0, metric_unit_t units = Other);
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc);
|
|
|
|
#endif
|
|
|
|
bool SetValue(int value, metric_unit_t units = Other);
|
|
|
|
void operator=(int value) { SetValue(value); }
|
|
|
|
bool SetValue(std::string value);
|
|
|
|
bool SetValue(dbcNumber& value);
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
void Clear();
|
|
|
|
bool CheckPersist();
|
|
|
|
void RefreshPersist();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
int m_value;
|
|
|
|
int* m_valuep;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OvmsMetricFloat : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricFloat(const char* name, uint16_t autostale=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
virtual ~OvmsMetricFloat();
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
float AsFloat(const float defvalue = 0, metric_unit_t units = Other);
|
|
|
|
int AsInt(const int defvalue = 0, metric_unit_t units = Other);
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc);
|
|
|
|
#endif
|
|
|
|
bool SetValue(float value, metric_unit_t units = Other);
|
|
|
|
void operator=(float value) { SetValue(value); }
|
|
|
|
bool SetValue(std::string value);
|
|
|
|
bool SetValue(dbcNumber& value);
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
void Clear();
|
|
|
|
virtual bool CheckPersist();
|
|
|
|
virtual void RefreshPersist();
|
|
|
|
|
|
|
|
protected:
|
|
|
|
float m_value;
|
|
|
|
float* m_valuep;
|
|
|
|
};
|
|
|
|
|
|
|
|
class OvmsMetricString : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricString(const char* name, uint16_t autostale=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
virtual ~OvmsMetricString();
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1);
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc);
|
|
|
|
#endif
|
|
|
|
bool SetValue(std::string value);
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
void Clear();
|
|
|
|
virtual bool IsString() { return true; };
|
|
|
|
|
|
|
|
protected:
|
|
|
|
OvmsMutex m_mutex;
|
|
|
|
std::string m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* OvmsMetricBitset<bits>: metric wrapper for std::bitset<bits>
|
|
|
|
* - string representation as comma separated bit positions (beginning at startpos) of set bits
|
|
|
|
*/
|
|
|
|
template <size_t N, int startpos=1>
|
|
|
|
class OvmsMetricBitset : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricBitset(const char* name, uint16_t autostale=0, metric_unit_t units = Other)
|
|
|
|
: OvmsMetric(name, autostale, units, false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
virtual ~OvmsMetricBitset()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return std::string(defvalue);
|
|
|
|
std::ostringstream ss;
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
for (int i = 0; i < N; i++)
|
|
|
|
{
|
|
|
|
if (m_value[i])
|
|
|
|
{
|
|
|
|
if (ss.tellp() > 0)
|
|
|
|
ss << ',';
|
|
|
|
ss << startpos + i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
std::string json = "[";
|
|
|
|
json += AsString(defvalue, units, precision);
|
|
|
|
json += "]";
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetValue(std::string value)
|
|
|
|
{
|
|
|
|
std::bitset<N> n_value;
|
|
|
|
std::istringstream vs(value);
|
|
|
|
std::string token;
|
|
|
|
int elem;
|
|
|
|
while(std::getline(vs, token, ','))
|
|
|
|
{
|
|
|
|
std::istringstream ts(token);
|
|
|
|
ts >> elem;
|
|
|
|
elem -= startpos;
|
|
|
|
if (elem >= 0 && elem < N)
|
|
|
|
n_value[elem] = 1;
|
|
|
|
}
|
|
|
|
return SetValue(n_value);
|
|
|
|
}
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
|
|
|
|
std::bitset<N> AsBitset(const std::bitset<N> defvalue = std::bitset<N>(0), metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return defvalue;
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
return m_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc)
|
|
|
|
{
|
|
|
|
std::bitset<N> value;
|
|
|
|
{
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
value = m_value;
|
|
|
|
}
|
|
|
|
dc.PushArray();
|
|
|
|
int cnt = 0;
|
|
|
|
for (int i = 0; i < N; i++)
|
|
|
|
{
|
|
|
|
if (value[i])
|
|
|
|
{
|
|
|
|
dc.Push(i);
|
|
|
|
dc.PutProp(-2, cnt++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool SetValue(std::bitset<N> value, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
bool modified = false;
|
|
|
|
if (m_value != value)
|
|
|
|
{
|
|
|
|
m_value = value;
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
m_mutex.Unlock();
|
|
|
|
SetModified(modified);
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void operator=(std::bitset<N> value) { SetValue(value); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
OvmsMutex m_mutex;
|
|
|
|
std::bitset<N> m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* OvmsMetricSet<type>: metric wrapper for std::set<type>
|
|
|
|
* - string representation as comma separated values
|
|
|
|
* - be aware this is a memory eater, only use if necessary
|
|
|
|
*/
|
|
|
|
template <typename ElemType>
|
|
|
|
class OvmsMetricSet : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricSet(const char* name, uint16_t autostale=0, metric_unit_t units = Other)
|
|
|
|
: OvmsMetric(name, autostale, units, false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
virtual ~OvmsMetricSet()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return std::string(defvalue);
|
|
|
|
std::ostringstream ss;
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
for (auto i = m_value.begin(); i != m_value.end(); i++)
|
|
|
|
{
|
|
|
|
if (ss.tellp() > 0)
|
|
|
|
ss << ',';
|
|
|
|
ss << *i;
|
|
|
|
}
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
std::string json = "[";
|
|
|
|
json += AsString(defvalue, units, precision);
|
|
|
|
json += "]";
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetValue(std::string value)
|
|
|
|
{
|
|
|
|
std::set<ElemType> n_value;
|
|
|
|
std::istringstream vs(value);
|
|
|
|
std::string token;
|
|
|
|
ElemType elem;
|
|
|
|
while(std::getline(vs, token, ','))
|
|
|
|
{
|
|
|
|
std::istringstream ts(token);
|
|
|
|
ts >> elem;
|
|
|
|
n_value.insert(elem);
|
|
|
|
}
|
|
|
|
return SetValue(n_value);
|
|
|
|
}
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
|
|
|
|
std::set<ElemType> AsSet(const std::set<ElemType> defvalue = std::set<ElemType>(), metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return defvalue;
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
return m_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc)
|
|
|
|
{
|
|
|
|
std::set<ElemType> value;
|
|
|
|
{
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
value = m_value;
|
|
|
|
}
|
|
|
|
dc.PushArray();
|
|
|
|
int cnt = 0;
|
|
|
|
for (auto i = value.begin(); i != value.end(); i++)
|
|
|
|
{
|
|
|
|
dc.Push(*i);
|
|
|
|
dc.PutProp(-2, cnt++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool SetValue(std::set<ElemType> value, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
bool modified = false;
|
|
|
|
if (m_value != value)
|
|
|
|
{
|
|
|
|
m_value = value;
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
m_mutex.Unlock();
|
|
|
|
SetModified(modified);
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
void operator=(std::set<ElemType> value) { SetValue(value); }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
OvmsMutex m_mutex;
|
|
|
|
std::set<ElemType> m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* OvmsMetricVector<type>: metric wrapper for std::vector<type>
|
|
|
|
* - string representation as comma separated values
|
|
|
|
* - TODO: escaping / string encoding for non-numeric types
|
|
|
|
* - TODO: unit conversions
|
|
|
|
*
|
|
|
|
* Usage example:
|
|
|
|
* OvmsMetricVector<float>* vf = new OvmsMetricVector<float>("test.volts", SM_STALE_MIN, Volts);
|
|
|
|
* vf->SetElemValue(3, 1.23);
|
|
|
|
* vf->SetElemValue(17, 2.34);
|
|
|
|
* float myvals[3] = { 5.5, 6.6, 7.7 };
|
|
|
|
* vf->SetElemValues(10, 3, myvals);
|
|
|
|
*
|
|
|
|
* Note: use ExtRamAllocator<type> for large vectors (= use SPIRAM)
|
|
|
|
*
|
|
|
|
* Persistence can only be used on ElemTypes fitting into a pmetrics storage container.
|
|
|
|
* A persistent vector will need 1+size pmetrics slots. It will allocate new slots as
|
|
|
|
* needed when growing, but the slots will remain used when shrinking the vector. If any
|
|
|
|
* new element cannot allocate a pmetrics slot, the whole vector loses its persistence.
|
|
|
|
*
|
|
|
|
* Unit conversion currently casts to and from float for the conversion, it's assumed to
|
|
|
|
* only be necessary for floating point values here. If you need int conversion, rework
|
|
|
|
* UnitConvert() into a template.
|
|
|
|
*/
|
|
|
|
template
|
|
|
|
<
|
|
|
|
typename ElemType,
|
|
|
|
class Allocator = std::allocator<ElemType>
|
|
|
|
>
|
|
|
|
class OvmsMetricVector : public OvmsMetric
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetricVector(const char* name, uint16_t autostale=0, metric_unit_t units = Other, bool persist = false)
|
|
|
|
: OvmsMetric(name, autostale, units, persist)
|
|
|
|
{
|
|
|
|
m_valuep_size = NULL;
|
|
|
|
if (!persist)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Ensure ElemType fits into persistent value:
|
|
|
|
if (sizeof(ElemType) > sizeof(persistent_value_t))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Vector persistence is currently implemented by a size entry with the metric name
|
|
|
|
// + one additional entry per element using the metric name extended by the element index.
|
|
|
|
// This may be optimized when the persistence system supports arrays.
|
|
|
|
struct persistent_values *vp = pmetrics_register(m_name);
|
|
|
|
if (!vp)
|
|
|
|
return;
|
|
|
|
m_valuep_size = reinterpret_cast<std::size_t*>(&vp->value);
|
|
|
|
std::size_t psize = *m_valuep_size;
|
|
|
|
if (SetPersistSize(psize))
|
|
|
|
{
|
|
|
|
SetModified(true);
|
|
|
|
ESP_LOGI(TAG, "persist %s = %s", m_name, AsUnitString().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual ~OvmsMetricVector()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool SetPersistSize(std::size_t new_size)
|
|
|
|
{
|
|
|
|
// Used by the constructor for initial read and by later SetValue() calls
|
|
|
|
// on vector size changes:
|
|
|
|
// m_persist false => register & read persistent values
|
|
|
|
// m_persist true => only register new elements
|
|
|
|
//
|
|
|
|
// Persistent element pointers will be retained on shrinking, and reused
|
|
|
|
// when the size grows again. Overall vector persistence is cancelled if any
|
|
|
|
// new element fails to register.
|
|
|
|
|
|
|
|
if (!m_valuep_size)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
std::size_t old_size = m_valuep_elem.size();
|
|
|
|
if (new_size <= old_size)
|
|
|
|
{
|
|
|
|
*m_valuep_size = new_size;
|
|
|
|
m_persist = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_valuep_elem.resize(new_size);
|
|
|
|
if (!m_persist)
|
|
|
|
m_value.resize(new_size);
|
|
|
|
|
|
|
|
struct persistent_values *vp;
|
|
|
|
char elem_name[100];
|
|
|
|
for (std::size_t i = old_size; i < new_size; i++)
|
|
|
|
{
|
|
|
|
snprintf(elem_name, sizeof(elem_name), "%s_%u", m_name, i);
|
|
|
|
vp = pmetrics_register(elem_name);
|
|
|
|
if (!vp)
|
|
|
|
{
|
|
|
|
// if any element fails to register, the whole vector persistence fails:
|
|
|
|
ESP_LOGE(TAG, "%s persistence lost: can't register slot for elem index %u", m_name, i);
|
|
|
|
*m_valuep_size = 0;
|
|
|
|
m_valuep_size = NULL;
|
|
|
|
m_valuep_elem.resize(0);
|
|
|
|
if (!m_persist)
|
|
|
|
m_value.resize(0);
|
|
|
|
m_persist = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_valuep_elem[i] = reinterpret_cast<ElemType*>(&vp->value);
|
|
|
|
if (!m_persist)
|
|
|
|
m_value[i] = *m_valuep_elem[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*m_valuep_size = new_size;
|
|
|
|
m_persist = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool CheckPersist()
|
|
|
|
{
|
|
|
|
if (!m_persist || !m_valuep_size || !IsDefined())
|
|
|
|
return true;
|
|
|
|
if (*m_valuep_size != m_value.size())
|
|
|
|
{
|
|
|
|
ESP_LOGE(TAG, "CheckPersist: bad value for %s", m_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
persistent_values *vp = pmetrics_find(m_name);
|
|
|
|
if (vp == NULL)
|
|
|
|
{
|
|
|
|
ESP_LOGE(TAG, "CheckPersist: can't find %s", m_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_valuep_size != reinterpret_cast<std::size_t*>(&vp->value))
|
|
|
|
{
|
|
|
|
ESP_LOGE(TAG, "CheckPersist: bad address for %s", m_name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < m_value.size(); i++)
|
|
|
|
{
|
|
|
|
if (*m_valuep_elem[i] != m_value[i])
|
|
|
|
{
|
|
|
|
ESP_LOGE(TAG, "CheckPersist: bad value for %s[%d]", m_name, i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RefreshPersist()
|
|
|
|
{
|
|
|
|
if (m_persist && m_valuep_size && IsDefined())
|
|
|
|
{
|
|
|
|
*m_valuep_size = m_value.size();
|
|
|
|
for (int i = 0; i < m_value.size(); i++)
|
|
|
|
*m_valuep_elem[i] = m_value[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual std::string AsString(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return std::string(defvalue);
|
|
|
|
std::ostringstream ss;
|
|
|
|
if (precision >= 0)
|
|
|
|
{
|
|
|
|
ss.precision(precision);
|
|
|
|
ss << fixed;
|
|
|
|
}
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
for (auto i = m_value.begin(); i != m_value.end(); i++)
|
|
|
|
{
|
|
|
|
if (ss.tellp() > 0)
|
|
|
|
ss << ',';
|
|
|
|
if (units != Other && units != m_units)
|
|
|
|
ss << (ElemType) UnitConvert(m_units, units, (float)*i);
|
|
|
|
else
|
|
|
|
ss << *i;
|
|
|
|
}
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string ElemAsString(size_t n, const char* defvalue = "", metric_unit_t units = Other, int precision = -1, bool addunitlabel = false)
|
|
|
|
{
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
if (!IsDefined() || m_value.size() <= n)
|
|
|
|
return std::string(defvalue);
|
|
|
|
std::ostringstream ss;
|
|
|
|
if (precision >= 0)
|
|
|
|
{
|
|
|
|
ss.precision(precision);
|
|
|
|
ss << fixed;
|
|
|
|
}
|
|
|
|
if (units != Other && units != m_units)
|
|
|
|
ss << (ElemType) UnitConvert(m_units, units, (float)m_value[n]);
|
|
|
|
else
|
|
|
|
ss << m_value[n];
|
|
|
|
if (addunitlabel)
|
|
|
|
ss << OvmsMetricUnitLabel(units == Native ? GetUnits() : units);
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ElemAsUnitString(size_t n, const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
return ElemAsString(n, defvalue, units, precision, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual std::string AsJSON(const char* defvalue = "", metric_unit_t units = Other, int precision = -1)
|
|
|
|
{
|
|
|
|
std::string json = "[";
|
|
|
|
json += AsString(defvalue, units, precision);
|
|
|
|
json += "]";
|
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE
|
|
|
|
void DukPush(DukContext &dc)
|
|
|
|
{
|
|
|
|
std::vector<ElemType, Allocator> value;
|
|
|
|
{
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
value = m_value;
|
|
|
|
}
|
|
|
|
dc.PushArray();
|
|
|
|
int cnt = 0;
|
|
|
|
for (auto i = value.begin(); i != value.end(); i++)
|
|
|
|
{
|
|
|
|
dc.Push(*i);
|
|
|
|
dc.PutProp(-2, cnt++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
virtual bool SetValue(std::string value)
|
|
|
|
{
|
|
|
|
std::vector<ElemType, Allocator> n_value;
|
|
|
|
std::istringstream vs(value);
|
|
|
|
std::string token;
|
|
|
|
ElemType elem;
|
|
|
|
while(std::getline(vs, token, ','))
|
|
|
|
{
|
|
|
|
std::istringstream ts(token);
|
|
|
|
ts >> elem;
|
|
|
|
n_value.push_back(elem);
|
|
|
|
}
|
|
|
|
return SetValue(n_value);
|
|
|
|
}
|
|
|
|
void operator=(std::string value) { SetValue(value); }
|
|
|
|
|
|
|
|
bool SetValue(const std::vector<ElemType, Allocator>& value, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
bool modified = false, resized = false;
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
if (m_value.size() != value.size())
|
|
|
|
{
|
|
|
|
m_value.resize(value.size());
|
|
|
|
if (m_persist)
|
|
|
|
SetPersistSize(value.size());
|
|
|
|
resized = true;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < value.size(); i++)
|
|
|
|
{
|
|
|
|
ElemType ivalue;
|
|
|
|
if (units != Other && units != m_units)
|
|
|
|
ivalue = (ElemType) UnitConvert(units, m_units, (float)value[i]);
|
|
|
|
else
|
|
|
|
ivalue = value[i];
|
|
|
|
if (resized || m_value[i] != ivalue)
|
|
|
|
{
|
|
|
|
m_value[i] = ivalue;
|
|
|
|
modified = true;
|
|
|
|
if (m_persist)
|
|
|
|
*m_valuep_elem[i] = ivalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_mutex.Unlock();
|
|
|
|
SetModified(modified);
|
|
|
|
}
|
|
|
|
return modified;
|
|
|
|
}
|
|
|
|
void operator=(std::vector<ElemType, Allocator> value) { SetValue(value); }
|
|
|
|
|
|
|
|
void ClearValue()
|
|
|
|
{
|
|
|
|
if (IsDefined())
|
|
|
|
{
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
if (m_persist)
|
|
|
|
SetPersistSize(0);
|
|
|
|
m_value.clear();
|
|
|
|
m_mutex.Unlock();
|
|
|
|
SetModified(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<ElemType, Allocator> AsVector(const std::vector<ElemType, Allocator> defvalue = std::vector<ElemType, Allocator>(), metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
if (!IsDefined())
|
|
|
|
return defvalue;
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
return m_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
ElemType GetElemValue(size_t n)
|
|
|
|
{
|
|
|
|
ElemType val{};
|
|
|
|
OvmsMutexLock lock(&m_mutex);
|
|
|
|
if (m_value.size() > n)
|
|
|
|
val = m_value[n];
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetElemValue(size_t n, const ElemType nvalue, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
ElemType value;
|
|
|
|
if (units != Other && units != m_units)
|
|
|
|
value = (ElemType) UnitConvert(units, m_units, (float)nvalue);
|
|
|
|
else
|
|
|
|
value = nvalue;
|
|
|
|
bool modified = false, resized = false;
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
if (m_value.size() < n+1)
|
|
|
|
{
|
|
|
|
m_value.resize(n+1);
|
|
|
|
if (m_persist)
|
|
|
|
SetPersistSize(n+1);
|
|
|
|
resized = true;
|
|
|
|
}
|
|
|
|
if (resized || m_value[n] != value)
|
|
|
|
{
|
|
|
|
m_value[n] = value;
|
|
|
|
modified = true;
|
|
|
|
if (m_persist)
|
|
|
|
*m_valuep_elem[n] = value;
|
|
|
|
}
|
|
|
|
m_mutex.Unlock();
|
|
|
|
}
|
|
|
|
SetModified(modified);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetElemValues(size_t start, size_t cnt, const ElemType* values, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
bool modified = false, resized = false;
|
|
|
|
if (m_mutex.Lock())
|
|
|
|
{
|
|
|
|
if (m_value.size() < start+cnt)
|
|
|
|
{
|
|
|
|
m_value.resize(start+cnt);
|
|
|
|
if (m_persist)
|
|
|
|
SetPersistSize(start+cnt);
|
|
|
|
resized = true;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < cnt; i++)
|
|
|
|
{
|
|
|
|
ElemType ivalue;
|
|
|
|
if (units != Other && units != m_units)
|
|
|
|
ivalue = (ElemType) UnitConvert(units, m_units, (float)values[i]);
|
|
|
|
else
|
|
|
|
ivalue = values[i];
|
|
|
|
if (resized || m_value[start+i] != ivalue)
|
|
|
|
{
|
|
|
|
m_value[start+i] = ivalue;
|
|
|
|
modified = true;
|
|
|
|
if (m_persist)
|
|
|
|
*m_valuep_elem[start+i] = ivalue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_mutex.Unlock();
|
|
|
|
}
|
|
|
|
SetModified(modified);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetSize()
|
|
|
|
{
|
|
|
|
return m_value.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
OvmsMutex m_mutex;
|
|
|
|
std::vector<ElemType, Allocator> m_value;
|
|
|
|
std::size_t* m_valuep_size;
|
|
|
|
std::vector<ElemType*, Allocator> m_valuep_elem;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
typedef std::function<void(OvmsMetric*)> MetricCallback;
|
|
|
|
|
|
|
|
class MetricCallbackEntry
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
MetricCallbackEntry(const char* caller, MetricCallback callback);
|
|
|
|
virtual ~MetricCallbackEntry();
|
|
|
|
|
|
|
|
public:
|
|
|
|
const char *m_caller;
|
|
|
|
MetricCallback m_callback;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef std::list<MetricCallbackEntry*> MetricCallbackList;
|
|
|
|
typedef std::map<const char*, MetricCallbackList*, CmpStrOp> MetricCallbackMap;
|
|
|
|
|
|
|
|
class OvmsMetrics
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OvmsMetrics();
|
|
|
|
virtual ~OvmsMetrics();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void RegisterMetric(OvmsMetric* metric);
|
|
|
|
void DeregisterMetric(OvmsMetric* metric);
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool Set(const char* metric, const char* value);
|
|
|
|
bool SetInt(const char* metric, int value);
|
|
|
|
bool SetBool(const char* metric, bool value);
|
|
|
|
bool SetFloat(const char* metric, float value);
|
|
|
|
OvmsMetric* Find(const char* metric);
|
|
|
|
|
|
|
|
OvmsMetricInt *InitInt(const char* metric, uint16_t autostale=0, int value=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
OvmsMetricBool *InitBool(const char* metric, uint16_t autostale=0, bool value=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
OvmsMetricFloat *InitFloat(const char* metric, uint16_t autostale=0, float value=0, metric_unit_t units = Other, bool persist = false);
|
|
|
|
OvmsMetricString *InitString(const char* metric, uint16_t autostale=0, const char* value=NULL, metric_unit_t units = Other);
|
|
|
|
template <size_t N>
|
|
|
|
OvmsMetricBitset<N> *InitBitset(const char* metric, uint16_t autostale=0, const char* value=NULL, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
OvmsMetricBitset<N> *m = (OvmsMetricBitset<N> *)Find(metric);
|
|
|
|
if (m==NULL) m = new OvmsMetricBitset<N>(metric, autostale, units);
|
|
|
|
if (value && !m->IsDefined())
|
|
|
|
m->SetValue(value);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
template <typename ElemType>
|
|
|
|
OvmsMetricSet<ElemType> *InitSet(const char* metric, uint16_t autostale=0, const char* value=NULL, metric_unit_t units = Other)
|
|
|
|
{
|
|
|
|
OvmsMetricSet<ElemType> *m = (OvmsMetricSet<ElemType> *)Find(metric);
|
|
|
|
if (m==NULL) m = new OvmsMetricSet<ElemType>(metric, autostale, units);
|
|
|
|
if (value && !m->IsDefined())
|
|
|
|
m->SetValue(value);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
template <typename ElemType>
|
|
|
|
OvmsMetricVector<ElemType> *InitVector(const char* metric, uint16_t autostale=0, const char* value=NULL, metric_unit_t units = Other, bool persist = false)
|
|
|
|
{
|
|
|
|
OvmsMetricVector<ElemType> *m = (OvmsMetricVector<ElemType> *)Find(metric);
|
|
|
|
if (m==NULL) m = new OvmsMetricVector<ElemType>(metric, autostale, units, persist);
|
|
|
|
if (value && !m->IsDefined())
|
|
|
|
m->SetValue(value);
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
void RegisterListener(const char* caller, const char* name, MetricCallback callback);
|
|
|
|
void DeregisterListener(const char* caller);
|
|
|
|
void NotifyModified(OvmsMetric* metric);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
MetricCallbackMap m_listeners;
|
|
|
|
|
|
|
|
public:
|
|
|
|
size_t RegisterModifier();
|
|
|
|
|
|
|
|
public:
|
|
|
|
void EventSystemShutDown(std::string event, void* data);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
size_t m_nextmodifier;
|
|
|
|
|
|
|
|
public:
|
|
|
|
OvmsMetric* m_first;
|
|
|
|
bool m_trace;
|
|
|
|
};
|
|
|
|
|
|
|
|
extern OvmsMetrics MyMetrics;
|
|
|
|
|
|
|
|
#undef TAG
|
|
|
|
|
|
|
|
#endif //#ifndef __METRICS_H__
|