Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement. On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set. I've also got a pull request that improves the precision of the km<->mi conversions and factors it out. //.ichael
Michael, adding unit conversion support to the shell and Duktape commands is a good idea. Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant. Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator. Regards, Michael Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on. I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater. If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels. //.ichael On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Right, so I've implemented some stuff that seems to work quite well. https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit). The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh. This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable) *OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm *OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh *OVMS# metric get xiq.v.trip.consumption*17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh*5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh*3.64233mi/kWh *OVMS# metric set xiq.c.speed 5 miph*Metric set *OVMS# metric get xiq.c.speed*8.04673km/h *OVMS# metric get xiq.c.speed miph*5Mph And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ? *(function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh"));})();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------ The basic stuff all works - it's just quibbling over the details.. but let's get them right! //.ichael On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat(" xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon. The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that. ValueUnit could be still useful then to provide a 'Value + Unit'. Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases? //.ichael On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption*17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh*5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh*3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph*Metric set
*OVMS# metric get xiq.c.speed*8.04673km/h
*OVMS# metric get xiq.c.speed miph*5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
*(function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh"));})();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat(" xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit) OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}]) It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form. OvmsMetrics.AsFloat( {metric} [,{unit}] ) and add the function Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit. Here's a sample function and the output! This also shows the types of the output. (function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })(); number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227 It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly. //.ichael On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption*17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh*5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh*3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph*Metric set
*OVMS# metric get xiq.c.speed*8.04673km/h
*OVMS# metric get xiq.c.speed miph*5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
*(function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh"));})();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v. trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat(" xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Michael, looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics). Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful. Regards, Michael Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in> or p/in.in <http://in.in> or lb_in.in <http://lb_in.in> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts). //. On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set
*OVMS# metric get xiq.c.speed *8.04673km/h
*OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v. trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat(" xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value". An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended. Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value. Regards, Michael Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in> or p/in.in <http://in.in> or lb_in.in <http://lb_in.in> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Another option: add another argument to 'GetValues()' to optionally modify the behaviour in this way. Regards, Michael Am 08.11.22 um 14:01 schrieb Michael Balzer:
That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in> or p/in.in <http://in.in> or lb_in.in <http://lb_in.in> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Or perhaps something more specific? HasValue() Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de <mailto:dexter@expeedo.de>> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net <mailto:frog@bunyip.wheelycreek.net>> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net <mailto:frog@bunyip.wheelycreek.net>> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in/> or p/in.in <http://in.in/> or lb_in.in <http://lb_in.in/> (yes, slightly weird, but predictable)
OVMS# metric units km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
OVMS# metric unit mi miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
OVMS# metric get xiq.v.trip.consumption 17.0597kWh/100km OVMS# metric get xiq.v.trip.consumption kpkwh 5.86177km/kWh OVMS# metric get xiq.v.trip.consumption mpkwh 3.64233mi/kWh
OVMS# metric set xiq.c.speed 5 miph Metric set OVMS# metric get xiq.c.speed 8.04673km/h OVMS# metric get xiq.c.speed miph 5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
(function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })(); --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net <mailto:frog@bunyip.wheelycreek.net>> wrote: > Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on. > > I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater. > > If it's ok with you then I might do that unit conversion proposal. > Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? > (kWh, WattHours , MetersPSS ) > I would probably add a command > metric units <spec> > to list all (matching) units and their associated Labels. > > //.ichael > > On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de <mailto:dexter@expeedo.de>> wrote: >> Michael, >> >> adding unit conversion support to the shell and Duktape commands is a good idea. >> >> Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant. >> >> Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator. >> >> Regards, >> Michael >> >> >> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>> Hi all, >>> Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. >>> Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! >>> ... but it also made me think these two metrics that are (with the consumption units added) defined like this: >>> m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>> m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH); >>> >>> These are effectively the same metric but in different units! >>> I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement. >>> >>> On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set. >>> >>> I've also got a pull request that improves the precision of the km<->mi conversions and factors it out. >>> >>> //.ichael >>> >>> >>> _______________________________________________ >>> OvmsDev mailing list >>> OvmsDev@lists.openvehicles.com <mailto:OvmsDev@lists.openvehicles.com> >>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >> >> -- >> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >> _______________________________________________ >> OvmsDev mailing list >> OvmsDev@lists.openvehicles.com <mailto:OvmsDev@lists.openvehicles.com> >> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com <mailto:OvmsDev@lists.openvehicles.com> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com <mailto:OvmsDev@lists.openvehicles.com> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com <mailto:OvmsDev@lists.openvehicles.com> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name. Another thought. How about if we did this (but also with GetValues() as well - see the special values below) OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String) and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String) I have already implemented the special values 'native' (existing), 'imperial' and 'metric'. I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice). //.ichael On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set
*OVMS# metric get xiq.c.speed *8.04673km/h
*OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v. trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat(" xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this? Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate. (function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })(); With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string] On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set
*OVMS# metric get xiq.c.speed *8.04673km/h
*OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v. trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
> Michael, > > adding unit conversion support to the shell and Duktape commands is > a good idea. > > Metrics are not meant to provide a user interface, they should be > defined to be efficient and non-redundant. > > Btw, metrics names also shall not use upper case characters, and > shall only use "." as a separator. > > Regards, > Michael > > > Am 05.11.22 um 11:22 schrieb Michael Geddes: > > Hi all, > Some of the code I copied from Kona/Kia code had both kwh/100km and > km/kwh metrics in the code as 'Other'. > Adding the various power consumption Units is not particularly hard > (I will have a pull-request soon) - though the conversions between them all > required some thought! > ... but it also made me think these two metrics that are (with the > consumption units added) defined like this: > m_v_trip_consumption1 = > MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); > m_v_trip_consumption2 = MyMetrics.InitFloat(" > xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); > > These are effectively the same metric but in different units! > I'm wondering if we would be better to have scripting and Duktape > support for converting metrics to different unit! This might be also quite > useful for those strange countries that insist on using miles as a > measurement. > > On top of the 'metric list' and 'metric set' we could add a 'metric > get' which gets a single value.. and add unit support for get/set. > > I've also got a pull request that improves the precision of the > km<->mi conversions and factors it out. > > //.ichael > > _______________________________________________ > OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev >
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Hello All, as described in https://docs.openvehicles.com/en/latest/introduction.html I’ll introduce myself ;-) I do devleopment of mainly iOS/Mac software for a living. I have driven a BMW i3 for the last 6 years and the BMW iPhone app was so unreliable that I created a status application for myself (and two friends). As BMW servers no longer give out data about the SOH I recently started looking into OVMS. As I’m using mqtt for my home equipment I started using ovms mqtt interface and filtering that to send the data to ABRP and my own application server. As I’m only interested in live data while driving/charging and the 12v battery of the i3 is not charged while parked I would like to have the ovms module sleep when the car is parked - so the battery does not drain as much. I have to add that I can’t charge the car at home or work. My current OVMS quest is to get deep sleep working correctly. I have access to four OVMS modules and a lab-power supply so my starting point was to get correct voltage readings in software. I first created correct measurements for the voltage level of the four modules. This gave me the data to have one calibration voltage measurement of a module and still have a fairly good voltage reading between 11 and 14 volts. Then I started adding that to the current codebase. That works and the module goes to sleep when the car is parked. What does not work is, that the module sometimes does not recognize it’s coming out of deep sleep and will fully boot and then go to sleep cause the battery level is too low. This is only happening when NOT connected to the serial port (bad for debugging). I started to pack the calibration and wakeup voltage in a RTC_DATA_ATTR variable before sleep and immediately check it ovms_boot.c . That helped but it sometimes RTC_DATA_ATTR would be empty even though I set it the last line before going to sleep. Then I thought ok, when the deep sleep is somewhat faulty, let the module go to sleep as early as possible in the boot process, so as soon as we have the configuration data with the calibration voltage and sleep then. That works ok, but the problem is, that in the boot process the cellular modem is powered on and stays on when going to deep sleep. So I decided to power off the cellular modem, but that will sometimes crash. So my questions: 1. Why does the module forget that it was in deep sleep before ? Is it the esp32 beeing faulty or something else ? 2. What would be the best way to immediately sleep again by reading the configuration without starting anything else - not even a second processor. 3. Is this the mailing list still active ? I would rather use the forum, but the forum is not really usable. Best Regards, Patrick aka @jollyjinx P.S.: If I can get OVMS to work for me I might adopt it to my application which then makes it possible for a broader audience to use (not just the three users it currently has) (currently it needs a backend where I’m using undocumented access to bmw servers, so I can’t open it up right now). Currently it looks like this:
Welcome, Patrick :-) @others: Patrick is jollyjinx on Github, he posted his 12V findings first in issue #711: https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/issues/711#... The BMW i3 is unique in not charging the 12V battery by itself while parked, it needs to be plugged in to enable trickle charging. The i3's 12V battery is also rated at a mere 20 Ah when new. Am 12.11.22 um 09:20 schrieb Patrick Stein:
As I’m only interested in live data while driving/charging and the 12v battery of the i3 is not charged while parked I would like to have the ovms module sleep when the car is parked - so the battery does not drain as much. I have to add that I can’t charge the car at home or work.
My current OVMS quest is to get deep sleep working correctly. As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
Assuming you do that, deep sleep will reduce the consumption to ~ 100 mW / 8-9 mA at 12 V, that should give you a minimum parking time of 46 days when assuming you shouldn't take more than 10 Ah out of the battery to avoid trouble.
the module sometimes does not recognize it’s coming out of deep sleep and will fully boot and then go to sleep cause the battery level is too low. This is only happening when NOT connected to the serial port (bad for debugging).# Did you try using a USB cable with the Vcc line (pin 1) cut?
I started to pack the calibration and wakeup voltage in a RTC_DATA_ATTR variable before sleep and immediately check it ovms_boot.c . That helped but it sometimes RTC_DATA_ATTR would be empty even though I set it the last line before going to sleep. Then I thought ok, when the deep sleep is somewhat faulty, let the module go to sleep as early as possible in the boot process, so as soon as we have the configuration data with the calibration voltage and sleep then. That works ok, but the problem is, that in the boot process the cellular modem is powered on and stays on when going to deep sleep. So I decided to power off the cellular modem, but that will sometimes crash.
So my questions:
1. Why does the module forget that it was in deep sleep before ? Is it the esp32 beeing faulty or something else ? 2. What would be the best way to immediately sleep again by reading the configuration without starting anything else - not even a second processor.
The ESP32 has quite its share of hardware bugs. You could try RTC_NOINIT_ATTR instead, and you could try setting the RTC RAM power explicitly (esp_sleep_pd_config). But I also found occasional RTC RAM corruptions even on normal reboots, that's why we do CRC checks on the boot data and persistent metrics. A more reliable approach could be to disable the standard auto init when going to sleep (see config auto init). My suggestion would be to add a third 'wakeup' auto init mode that tells the system to first check if it actually should do a full boot or return to deep sleep. That way you'll boot into the base operation mode and have access to the config store etc., while avoiding to power up optional components. If returning to sleep immediately, that would result in a necessary uptime of just ~ 5 seconds at base power consumption level.
3. Is this the mailing list still active ? I would rather use the forum, but the forum is not really usable.
The forum is meant for user support, developer discussions are done here and on github. Regards, Michael
Best Regards, Patrick aka @jollyjinx
P.S.: If I can get OVMS to work for me I might adopt it to my application which then makes it possible for a broader audience to use (not just the three users it currently has) (currently it needs a backend where I’m using undocumented access to bmw servers, so I can’t open it up right now).
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
On 13. Nov 2022, at 10:23, Michael Balzer <dexter@expeedo.de> wrote:
Welcome, Patrick :-)
Thanx for the warm welcome Michael.
@others: Patrick is jollyjinx on Github, he posted his 12V findings first in issue #711: https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/issues/711#...
The BMW i3 is unique in not charging the 12V battery by itself while parked, it needs to be plugged in to enable trickle charging. The i3's 12V battery is also rated at a mere 20 Ah when new.
Am 12.11.22 um 09:20 schrieb Patrick Stein:
As I’m only interested in live data while driving/charging and the 12v battery of the i3 is not charged while parked I would like to have the ovms module sleep when the car is parked - so the battery does not drain as much. I have to add that I can’t charge the car at home or work.
My current OVMS quest is to get deep sleep working correctly. As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. About the 12v antenna switch that would make total sense to me to be able to switch that on/off. I can solder and have the equipment, but I’m not really into hardware modding. Can someone point me on what wires on the board need to be modified for that ?
Assuming you do that, deep sleep will reduce the consumption to ~ 100 mW / 8-9 mA at 12 V, that should give you a minimum parking time of 46 days when assuming you shouldn't take more than 10 Ah out of the battery to avoid trouble.
that would be perfect :-D I usually do not keep my car parked for more than two weeks at a time.
the module sometimes does not recognize it’s coming out of deep sleep and will fully boot and then go to sleep cause the battery level is too low. This is only happening when NOT connected to the serial port (bad for debugging).# Did you try using a USB cable with the Vcc line (pin 1) cut?
No, I only have regular cables - but I will try that.
I started to pack the calibration and wakeup voltage in a RTC_DATA_ATTR variable before sleep and immediately check it ovms_boot.c . That helped but it sometimes RTC_DATA_ATTR would be empty even though I set it the last line before going to sleep. Then I thought ok, when the deep sleep is somewhat faulty, let the module go to sleep as early as possible in the boot process, so as soon as we have the configuration data with the calibration voltage and sleep then. That works ok, but the problem is, that in the boot process the cellular modem is powered on and stays on when going to deep sleep. So I decided to power off the cellular modem, but that will sometimes crash.
So my questions:
1. Why does the module forget that it was in deep sleep before ? Is it the esp32 beeing faulty or something else ? 2. What would be the best way to immediately sleep again by reading the configuration without starting anything else - not even a second processor.
The ESP32 has quite its share of hardware bugs.
You could try RTC_NOINIT_ATTR instead, and you could try setting the RTC RAM power explicitly (esp_sleep_pd_config).
But I also found occasional RTC RAM corruptions even on normal reboots, that's why we do CRC checks on the boot data and persistent metrics.
A more reliable approach could be to disable the standard auto init when going to sleep (see config auto init). My suggestion would be to add a third 'wakeup' auto init mode that tells the system to first check if it actually should do a full boot or return to deep sleep.
That way you'll boot into the base operation mode and have access to the config store etc., while avoiding to power up optional components. If returning to sleep immediately, that would result in a necessary uptime of just ~ 5 seconds at base power consumption level.
Thanx for pointing that out - remember I’m new to OVMS I just got the development environment working on my arm mac natively. I have to learn on how the boot process works - I thought it would start in ovms_boot, but other code runs before. your mention of config auto init means the make menuconfig process ? Maybe it’s easiest to put a separate esp32board in front of the OVMS module to only switch on OVMS when battery level is high enough - maybe board designers can think of really low power deep sleep in the next interation of the OVMS module. Regards , Patrick aka @jollyjinx
3. Is this the mailing list still active ? I would rather use the forum, but the forum is not really usable.
The forum is meant for user support, developer discussions are done here and on github.
Regards, Michael
Best Regards, Patrick aka @jollyjinx
P.S.: If I can get OVMS to work for me I might adopt it to my application which then makes it possible for a broader audience to use (not just the three users it currently has) (currently it needs a backend where I’m using undocumented access to bmw servers, so I can’t open it up right now).
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Patrick, Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection. I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results:
* base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW * sleep, GPS antenna attached ~ 16 mA / 180 mW * sleep, GPS antenna detached ~ 11 mA / 130 mW So nowhere near 250 mW. Mark, any idea about this?
About the 12v antenna switch that would make total sense to me to be able to switch that on/off. I can solder and have the equipment, but I’m not really into hardware modding. Can someone point me on what wires on the board need to be modified for that ? I meant making your own external GPS antenna cable including the relay. Within the module you could use a custom pigtail cable, but I wouldn't recommend that, as these are fragile.
A more reliable approach could be to disable the standard auto init when going to sleep (see config auto init). My suggestion would be to add a third 'wakeup' auto init mode that tells the system to first check if it actually should do a full boot or return to deep sleep.
That way you'll boot into the base operation mode and have access to the config store etc., while avoiding to power up optional components. If returning to sleep immediately, that would result in a necessary uptime of just ~ 5 seconds at base power consumption level. Thanx for pointing that out - remember I’m new to OVMS I just got the development environment working on my arm mac natively. I have to learn on how the boot process works - I thought it would start in ovms_boot, but other code runs before. your mention of config auto init means the make menuconfig process ? No, I mean the OVMS AutoInit system. The main controller for that is Housekeeping::Init(), which reads the config and calls the AutoInit() methods of the components enabled.
The boot process basically consists of these phases: 1. Hardware init (esp-idf bootloader) 2. Application init (static object inits in __attribute__ init_priority order) ending with the FreeRTOS scheduler start 3. Application start (entry point: app_main() in ovms_main.cpp) (system is now in FreeRTOS mode) 4. Housekeeping init (starting dynamic components & peripherals according to user configuration) Every phase resets the log time to 0. In the boot log, you can identify phase 2 begin/end by the "cpu_start" log lines, with the static object (object factory) inits logging their invocations including their init priority: … I (2081) cpu_start: Pro cpu start user code I (2086) spiram: Adding pool of 4096K of external SPI memory to heap allocator I (92) ovms_main: Set default logging level for * to INFO I (93) ovms_main: Initialising WATCHDOG... I (93) ovms-duktape: Initialising DUKTAPE Registry (1000) I (100) command: Initialising COMMAND (1010) I (105) command: Expanding DUKTAPE javascript engine *I (110) boot: Initialising BOOT (1100)* … Boot::Boot() is the static init of the MyBoot object, this is executed quite early at init_priority 1100. This is the place we currently check if the 12V level is sufficient when waking up from deep sleep. From here we can go back to sleep immediately, but cannot yet access the config store. Phase 3 (app_main()) starts with logging "ovms_main: Executing on CPU core 0", then essentially mounts the configuration partition "/store" and hands over control to Housekeeping: … I (1050) ovms_main: Starting HOUSEKEEPING... I (1050) housekeeping: Initialising HOUSEKEEPING Framework... … Houskeeping init finishes with starting the USB console: I (2450) housekeeping: Starting USB console... … Welcome to the Open Vehicle Monitoring System (OVMS) - Async Console Firmware: 3.3.003-97-g5ccff7cf/factory/edge Hardware: OVMS WIFI BLE BT cores=2 rev=ESP32/3 OVMS>
Maybe it’s easiest to put a separate esp32board in front of the OVMS module to only switch on OVMS when battery level is high enough - maybe board designers can think of really low power deep sleep in the next interation of the OVMS module.
Sure. Or you could use an OBD cable with a manual power switch. Or you could use an OBD cable that connects to a 12V source that's switched by the car. Regards, Michael -- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
@Michael: Thanx for all your answers - that helps me a lot. With your pointers I’ve started looking into esp documentation and found the *esp_get_deep_sleep_wake_stub* which would be exactly that kind of thing to run before the boot process, but using it does not work - it wakes from deepsleep and then enters a reset as it looks: ``` I (1491) 12battery: sleepImmediately()2 I (1491) 12battery: sleepImmediately()3 I (4501) 12battery: sleepImmediately()4 ets Jul 29 2019 12:21:46 rst:0x5 (DEEPSLEEP_RESET),boot:0x1f (SPI_FAST_FLASH_BOOT) ets Jul 29 2019 12:21:46 rst:0x7 (TG0WDT_SYS_RESET),boot:0x1f (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 ``` I found that there was a bugfix for a sleep not using the wake stub code in https://github.com/espressif/esp-idf/issues/5983 and it seems the fix in https://github.com/espressif/esp-idf/commit/ff8d05466e83049bca44e1584891bfaf... is not OVMS. Maybe that’s also the problem with the buggy coming out of deepsleep behaviour with OVMS as well ? Are there so many changes in OVMS esp_idf part that OVMS can’t use the newest (presumably less buggy?) version of esp_idf ? Excuse my newbie questions - I’m not used to embedded programming or esp development. Regards, Patrick aka @jollyjinx
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this?
About the 12v antenna switch that would make total sense to me to be able to switch that on/off. I can solder and have the equipment, but I’m not really into hardware modding. Can someone point me on what wires on the board need to be modified for that ? I meant making your own external GPS antenna cable including the relay. Within the module you could use a custom pigtail cable, but I wouldn't recommend that, as these are fragile.
A more reliable approach could be to disable the standard auto init when going to sleep (see config auto init). My suggestion would be to add a third 'wakeup' auto init mode that tells the system to first check if it actually should do a full boot or return to deep sleep.
That way you'll boot into the base operation mode and have access to the config store etc., while avoiding to power up optional components. If returning to sleep immediately, that would result in a necessary uptime of just ~ 5 seconds at base power consumption level.
Thanx for pointing that out - remember I’m new to OVMS I just got the development environment working on my arm mac natively. I have to learn on how the boot process works - I thought it would start in ovms_boot, but other code runs before. your mention of config auto init means the make menuconfig process ? No, I mean the OVMS AutoInit system. The main controller for that is Housekeeping::Init(), which reads the config and calls the AutoInit() methods of the components enabled.
The boot process basically consists of these phases: • Hardware init (esp-idf bootloader) • Application init (static object inits in __attribute__ init_priority order) ending with the FreeRTOS scheduler start • Application start (entry point: app_main() in ovms_main.cpp) (system is now in FreeRTOS mode) • Housekeeping init (starting dynamic components & peripherals according to user configuration) Every phase resets the log time to 0.
In the boot log, you can identify phase 2 begin/end by the "cpu_start" log lines, with the static object (object factory) inits logging their invocations including their init priority:
… I (2081) cpu_start: Pro cpu start user code I (2086) spiram: Adding pool of 4096K of external SPI memory to heap allocator I (92) ovms_main: Set default logging level for * to INFO I (93) ovms_main: Initialising WATCHDOG... I (93) ovms-duktape: Initialising DUKTAPE Registry (1000) I (100) command: Initialising COMMAND (1010) I (105) command: Expanding DUKTAPE javascript engine I (110) boot: Initialising BOOT (1100) …
Boot::Boot() is the static init of the MyBoot object, this is executed quite early at init_priority 1100.
This is the place we currently check if the 12V level is sufficient when waking up from deep sleep. From here we can go back to sleep immediately, but cannot yet access the config store.
Phase 3 (app_main()) starts with logging "ovms_main: Executing on CPU core 0", then essentially mounts the configuration partition "/store" and hands over control to Housekeeping:
… I (1050) ovms_main: Starting HOUSEKEEPING... I (1050) housekeeping: Initialising HOUSEKEEPING Framework... …
Houskeeping init finishes with starting the USB console:
I (2450) housekeeping: Starting USB console... … Welcome to the Open Vehicle Monitoring System (OVMS) - Async Console Firmware: 3.3.003-97-g5ccff7cf/factory/edge Hardware: OVMS WIFI BLE BT cores=2 rev=ESP32/3 OVMS>
Maybe it’s easiest to put a separate esp32board in front of the OVMS module to only switch on OVMS when battery level is high enough - maybe board designers can think of really low power deep sleep in the next interation of the OVMS module.
Sure. Or you could use an OBD cable with a manual power switch. Or you could use an OBD cable that connects to a 12V source that's switched by the car.
Regards, Michael
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Patrick, esp-idf 4 was a major break with both APIs and build framework of esp-idf 3. Espressif are now heading towards release 5, and I expect them to do the same again, obsoleting any work now done for esp-idf 4. We added some stuff to the 3.3 release we needed, that wasn't all merged by Espressif. esp-idf 4/5 also require using the new xtensa toolkit / gcc release, so will need some further code rework from that as well. TL;DR: it's not an easy migration. There sure are a lot of fixes to gain, but there will also be new bugs. We should wait until release 5 is official, then go that way. Regarding the deep sleep fix you found: you can try to backport that into the 3.3 code base if you think that will help. It seems the fix is about disabling concurrency when calculating the RTC RAM CRC. A thought on this: we update RTC RAM whenever a persistent metric is updated. Is it possible that happens while the ESP32 goes into deep sleep, corrupting the CRC? We could explicitly stop all tasks (taskENTER_CRITICAL() / vTaskSuspendAll()) just before calling esp_deep_sleep() to avoid that. Regards, Michael Am 15.11.22 um 09:20 schrieb Patrick Stein:
@Michael: Thanx for all your answers - that helps me a lot.
With your pointers I’ve started looking into esp documentation and found the *esp_get_deep_sleep_wake_stub* which would be exactly that kind of thing to run before the boot process, but using it does not work - it wakes from deepsleep and then enters a reset as it looks:
``` I (1491) 12battery: sleepImmediately()2 I (1491) 12battery: sleepImmediately()3 I (4501) 12battery: sleepImmediately()4 ets Jul 29 2019 12:21:46
rst:0x5 (DEEPSLEEP_RESET),boot:0x1f (SPI_FAST_FLASH_BOOT) ets Jul 29 2019 12:21:46
rst:0x7 (TG0WDT_SYS_RESET),boot:0x1f (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:2 ```
I found that there was a bugfix for a sleep not using the wake stub code in https://github.com/espressif/esp-idf/issues/5983 and it seems the fix in https://github.com/espressif/esp-idf/commit/ff8d05466e83049bca44e1584891bfaf... is not OVMS.
Maybe that’s also the problem with the buggy coming out of deepsleep behaviour with OVMS as well ? Are there so many changes in OVMS esp_idf part that OVMS can’t use the newest (presumably less buggy?) version of esp_idf ?
Excuse my newbie questions - I’m not used to embedded programming or esp development.
Regards, Patrick aka @jollyjinx
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this?
About the 12v antenna switch that would make total sense to me to be able to switch that on/off. I can solder and have the equipment, but I’m not really into hardware modding. Can someone point me on what wires on the board need to be modified for that ? I meant making your own external GPS antenna cable including the relay. Within the module you could use a custom pigtail cable, but I wouldn't recommend that, as these are fragile.
A more reliable approach could be to disable the standard auto init when going to sleep (see config auto init). My suggestion would be to add a third 'wakeup' auto init mode that tells the system to first check if it actually should do a full boot or return to deep sleep.
That way you'll boot into the base operation mode and have access to the config store etc., while avoiding to power up optional components. If returning to sleep immediately, that would result in a necessary uptime of just ~ 5 seconds at base power consumption level.
Thanx for pointing that out - remember I’m new to OVMS I just got the development environment working on my arm mac natively. I have to learn on how the boot process works - I thought it would start in ovms_boot, but other code runs before. your mention of config auto init means the make menuconfig process ? No, I mean the OVMS AutoInit system. The main controller for that is Housekeeping::Init(), which reads the config and calls the AutoInit() methods of the components enabled.
The boot process basically consists of these phases: • Hardware init (esp-idf bootloader) • Application init (static object inits in __attribute__ init_priority order) ending with the FreeRTOS scheduler start • Application start (entry point: app_main() in ovms_main.cpp) (system is now in FreeRTOS mode) • Housekeeping init (starting dynamic components & peripherals according to user configuration) Every phase resets the log time to 0.
In the boot log, you can identify phase 2 begin/end by the "cpu_start" log lines, with the static object (object factory) inits logging their invocations including their init priority:
… I (2081) cpu_start: Pro cpu start user code I (2086) spiram: Adding pool of 4096K of external SPI memory to heap allocator I (92) ovms_main: Set default logging level for * to INFO I (93) ovms_main: Initialising WATCHDOG... I (93) ovms-duktape: Initialising DUKTAPE Registry (1000) I (100) command: Initialising COMMAND (1010) I (105) command: Expanding DUKTAPE javascript engine I (110) boot: Initialising BOOT (1100) …
Boot::Boot() is the static init of the MyBoot object, this is executed quite early at init_priority 1100.
This is the place we currently check if the 12V level is sufficient when waking up from deep sleep. From here we can go back to sleep immediately, but cannot yet access the config store.
Phase 3 (app_main()) starts with logging "ovms_main: Executing on CPU core 0", then essentially mounts the configuration partition "/store" and hands over control to Housekeeping:
… I (1050) ovms_main: Starting HOUSEKEEPING... I (1050) housekeeping: Initialising HOUSEKEEPING Framework... …
Houskeeping init finishes with starting the USB console:
I (2450) housekeeping: Starting USB console... … Welcome to the Open Vehicle Monitoring System (OVMS) - Async Console Firmware: 3.3.003-97-g5ccff7cf/factory/edge Hardware: OVMS WIFI BLE BT cores=2 rev=ESP32/3 OVMS>
Maybe it’s easiest to put a separate esp32board in front of the OVMS module to only switch on OVMS when battery level is high enough - maybe board designers can think of really low power deep sleep in the next interation of the OVMS module. Sure. Or you could use an OBD cable with a manual power switch. Or you could use an OBD cable that connects to a 12V source that's switched by the car.
Regards, Michael
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this?
I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB. So it seems that the module requires a lot less over USB than over OBD. Best regards, Patrick
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference. I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc). With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry. It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message. Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this?
I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
Best regards, Patrick _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I my case I have no problem with the modem working on USB. But to make it clear. The test case I had was the following. I powered the module on 14V OBD and the USB cable connected and not drawing any power over USB. Then I set the voltage to 9V so I would sleep and it told me in the console that the modem was beeing powered down - even though I could see messages send to the modem just before sleep. When the module was in deep sleep ( going from over 1 Watt down to 490 mW ) and waited a few 1 minute cycles in deep sleep cycles I switched off the powersupply while in deep sleep between cycles and saw on the USB side the power beeing used was 290mW . Turned back the lab power supply for the next wakeup check and did a few cycle measurements while in deep sleep switching the power supply. Isn’t the modem fully powered down for deep sleep ? I looked at the deep sleep code and it tells me that the modem will be powered down. Might it be feasable to pysically cut the modem power supply and replace that part with a switch that can be switched from the esp32 ? I’m into software not hardware so excuse my simple thinking here. I do understand that low power was not a goal for the board designers. But my use case is different with the car being solely beeing powered by it’s little 12v battery. Regards , Patrick
On 21. Nov 2022, at 07:53, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this?
I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
Best regards, Patrick _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW. The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds. You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages: I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff) Another option is checking the "cellular status" command showing "State: PoweredOff". You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake. Regards, Michael Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this? I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Thanx a lot for all your feedback. That makes it clear to me that my approach to sleep the system immediately after wake as soon as it’s clear that the 12v battery is below the adjusted threshold won’t work. It will only work when the battery settings are still in non volatile deep sleep ram. The main problem is if the system is starting and not recognizing it’s coming out of sleep that approach will fail as the modem will start up and stay even when the esp is in deepsleep - right ? What you do think would be the best approach here to get all devices powered off quickly and going to the lowest possible power consumption while checking every 30/60 seconds for a car wakeup ? As I bought the OVMS modules to put them in three cars (mine and two friends) detaching and attaching the device or switching it off with manual switch is not feasable. I think reaching down to a 130mW sleep would be ok for most use cases (except vacations) but maybe it might be really easier to build something that detaches power from the OVMS module or put OVMS in the USB bus and cut the 12v obd line altogether. Regards - Patrick Stein Beste Grüße - Patrick Stein
On 21. Nov 2022, at 20:15, Michael Balzer <dexter@expeedo.de> wrote:
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW.
The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds.
You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages:
I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff)
Another option is checking the "cellular status" command showing "State: PoweredOff".
You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake.
Regards, Michael
Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection.
I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this? I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Patrick, unless the modem really occasionally powers on by itself, my proposed auto init solution should still work. You can btw emulate that partially by a simple event script turning off the modem auto init on a power manager event. But… I still cannot reproduce your reported behaviour on my modules. Not even at 6V supply on the 12V input & without any USB connection, exactly as in your setup. At 6V I see a sleep consumption (including the attached GPS antenna) of 19 mA after the power down by the power management component. Every 60 seconds, there's a brief wakeup period of ~ 2-3 seconds at up to ~ 46 mA, then it goes back to sleep. That cycle has been running for about an hour now at 6.2V without a single modem power up or full boot. In other words everything seems to work as designed, the ESP32 does not fail to recognize it's been sleeping, and the modem remains powered down. Maybe your test setup has some hidden flaw? A circuit break, broken cable, something like that, causing the module to actually do an occasional full power cycle? Regards, Michael Am 22.11.22 um 17:54 schrieb Patrick Stein:
Thanx a lot for all your feedback. That makes it clear to me that my approach to sleep the system immediately after wake as soon as it’s clear that the 12v battery is below the adjusted threshold won’t work. It will only work when the battery settings are still in non volatile deep sleep ram.
The main problem is if the system is starting and not recognizing it’s coming out of sleep that approach will fail as the modem will start up and stay even when the esp is in deepsleep - right ?
What you do think would be the best approach here to get all devices powered off quickly and going to the lowest possible power consumption while checking every 30/60 seconds for a car wakeup ? As I bought the OVMS modules to put them in three cars (mine and two friends) detaching and attaching the device or switching it off with manual switch is not feasable. I think reaching down to a 130mW sleep would be ok for most use cases (except vacations) but maybe it might be really easier to build something that detaches power from the OVMS module or put OVMS in the USB bus and cut the 12v obd line altogether.
Regards - Patrick Stein Beste Grüße - Patrick Stein
On 21. Nov 2022, at 20:15, Michael Balzer <dexter@expeedo.de> wrote:
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW.
The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds.
You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages:
I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff)
Another option is checking the "cellular status" command showing "State: PoweredOff".
You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake.
Regards, Michael
Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein:
> As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection. > I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this? I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
I’m quite busy as may right now , so my late reply: Michael, maybe you are right and my setup has some flaw, I’m using different ovms modules and some sleep at a higher I started a measuement table , but I get never get near the 6V*19mA = 114mW . The lowest I’ve seen in 288mW at 13.1V but the three other modules are using more than 400mW when asleep. Nevertheless I added some code so the system a) much better can read the voltage after one calibration and changed the sleep code in OVMS to wake only when reaching normal battery levels saving the calibration and wakeup voltage in the boot struct and using it only when it checked crc ok. But after some sleep cycles the system wakes up and think it’s a not a deepsleep. I then added code to esp_start to and get the system to sleep again before even starting the second core and that seems to work sometimes (just kept the system asleep for 24 hours at a time) but then forgets about the sleep calibration and wakes up gain, but I have stored it in the wrong memory (RTC_NOINIT_ATTR) . I wonder if it might not be easier to read the calibration and wakeup voltage from the configuration file. Some things that puzzles me: I saw the system sleeping at 288mW and then, after some time during sleep (not waking up) use more engery. Could it be that the modem wakes up or get’s in a higher power state after some time without the esp waking? Could it be that in the wakeup of the ESP processor some pins on the ESP are set to some other value starting some hardware even if the system goes to sleep right away ? Could it be that the modem uses different sleep power depending on which network connection LTE / 4G / GSM? was active before? Best , Patrick
On 22. Nov 2022, at 22:14, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
unless the modem really occasionally powers on by itself, my proposed auto init solution should still work. You can btw emulate that partially by a simple event script turning off the modem auto init on a power manager event.
But… I still cannot reproduce your reported behaviour on my modules. Not even at 6V supply on the 12V input & without any USB connection, exactly as in your setup.
At 6V I see a sleep consumption (including the attached GPS antenna) of 19 mA after the power down by the power management component. Every 60 seconds, there's a brief wakeup period of ~ 2-3 seconds at up to ~ 46 mA, then it goes back to sleep. That cycle has been running for about an hour now at 6.2V without a single modem power up or full boot.
In other words everything seems to work as designed, the ESP32 does not fail to recognize it's been sleeping, and the modem remains powered down.
Maybe your test setup has some hidden flaw? A circuit break, broken cable, something like that, causing the module to actually do an occasional full power cycle?
Regards, Michael
Am 22.11.22 um 17:54 schrieb Patrick Stein:
Thanx a lot for all your feedback. That makes it clear to me that my approach to sleep the system immediately after wake as soon as it’s clear that the 12v battery is below the adjusted threshold won’t work. It will only work when the battery settings are still in non volatile deep sleep ram.
The main problem is if the system is starting and not recognizing it’s coming out of sleep that approach will fail as the modem will start up and stay even when the esp is in deepsleep - right ?
What you do think would be the best approach here to get all devices powered off quickly and going to the lowest possible power consumption while checking every 30/60 seconds for a car wakeup ? As I bought the OVMS modules to put them in three cars (mine and two friends) detaching and attaching the device or switching it off with manual switch is not feasable. I think reaching down to a 130mW sleep would be ok for most use cases (except vacations) but maybe it might be really easier to build something that detaches power from the OVMS module or put OVMS in the USB bus and cut the 12v obd line altogether.
Regards - Patrick Stein Beste Grüße - Patrick Stein
On 21. Nov 2022, at 20:15, Michael Balzer <dexter@expeedo.de> wrote:
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW.
The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds.
You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages:
I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff)
Another option is checking the "cellular status" command showing "State: PoweredOff".
You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake.
Regards, Michael
Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote:
Am 14.11.22 um 10:25 schrieb Patrick Stein: >> As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection. >> > I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW • sleep, GPS antenna attached ~ 16 mA / 180 mW • sleep, GPS antenna detached ~ 11 mA / 130 mW
So nowhere near 250 mW. Mark, any idea about this? I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Beste Grüße - Patrick Stein
Patrick, Am 04.12.22 um 08:11 schrieb Patrick Stein:
maybe you are right and my setup has some flaw, I’m using different ovms modules and some sleep at a higher I started a measuement table , but I get never get near the 6V*19mA = 114mW . The lowest I’ve seen in 288mW at 13.1V but the three other modules are using more than 400mW when asleep. … I saw the system sleeping at 288mW and then, after some time during sleep (not waking up) use more engery. Could it be that the modem wakes up or get’s in a higher power state after some time without the esp waking?
As we already pointed out, that can only happen if you do not power down the modem. I told you to check the blue LED, you didn't post any result yet. If the blue LED is on or flashing, the modem is powered on. Regards, Michael
On 22. Nov 2022, at 22:14, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
unless the modem really occasionally powers on by itself, my proposed auto init solution should still work. You can btw emulate that partially by a simple event script turning off the modem auto init on a power manager event.
But… I still cannot reproduce your reported behaviour on my modules. Not even at 6V supply on the 12V input & without any USB connection, exactly as in your setup.
At 6V I see a sleep consumption (including the attached GPS antenna) of 19 mA after the power down by the power management component. Every 60 seconds, there's a brief wakeup period of ~ 2-3 seconds at up to ~ 46 mA, then it goes back to sleep. That cycle has been running for about an hour now at 6.2V without a single modem power up or full boot.
In other words everything seems to work as designed, the ESP32 does not fail to recognize it's been sleeping, and the modem remains powered down.
Maybe your test setup has some hidden flaw? A circuit break, broken cable, something like that, causing the module to actually do an occasional full power cycle?
Regards, Michael
Am 22.11.22 um 17:54 schrieb Patrick Stein:
Thanx a lot for all your feedback. That makes it clear to me that my approach to sleep the system immediately after wake as soon as it’s clear that the 12v battery is below the adjusted threshold won’t work. It will only work when the battery settings are still in non volatile deep sleep ram.
The main problem is if the system is starting and not recognizing it’s coming out of sleep that approach will fail as the modem will start up and stay even when the esp is in deepsleep - right ?
What you do think would be the best approach here to get all devices powered off quickly and going to the lowest possible power consumption while checking every 30/60 seconds for a car wakeup ? As I bought the OVMS modules to put them in three cars (mine and two friends) detaching and attaching the device or switching it off with manual switch is not feasable. I think reaching down to a 130mW sleep would be ok for most use cases (except vacations) but maybe it might be really easier to build something that detaches power from the OVMS module or put OVMS in the USB bus and cut the 12v obd line altogether.
Regards - Patrick Stein Beste Grüße - Patrick Stein
On 21. Nov 2022, at 20:15, Michael Balzer <dexter@expeedo.de> wrote:
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW.
The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds.
You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages:
I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff)
Another option is checking the "cellular status" command showing "State: PoweredOff".
You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake.
Regards, Michael
Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote:
Hello Michael,
> On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote: > > Am 14.11.22 um 10:25 schrieb Patrick Stein: >>> As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection. >>> >> I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. > I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: > • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW > • sleep, GPS antenna attached ~ 16 mA / 180 mW > • sleep, GPS antenna detached ~ 11 mA / 130 mW > > So nowhere near 250 mW. Mark, any idea about this? I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB.
So it seems that the module requires a lot less over USB than over OBD.
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev -- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Beste Grüße - Patrick Stein
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Michael, the blue led is always off and when connected via USB I saw the powering down notifications in the log that you pointed out to look for, sorry I did no mention that. It seems to me that the modem might be still in some kind of half ways on or down state without the led being on. Regards, Patrick
On 4. Dec 2022, at 08:38, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
Am 04.12.22 um 08:11 schrieb Patrick Stein:
maybe you are right and my setup has some flaw, I’m using different ovms modules and some sleep at a higher I started a measuement table , but I get never get near the 6V*19mA = 114mW . The lowest I’ve seen in 288mW at 13.1V but the three other modules are using more than 400mW when asleep. … I saw the system sleeping at 288mW and then, after some time during sleep (not waking up) use more engery. Could it be that the modem wakes up or get’s in a higher power state after some time without the esp waking?
As we already pointed out, that can only happen if you do not power down the modem.
I told you to check the blue LED, you didn't post any result yet. If the blue LED is on or flashing, the modem is powered on.
Regards, Michael
On 22. Nov 2022, at 22:14, Michael Balzer <dexter@expeedo.de> wrote:
Patrick,
unless the modem really occasionally powers on by itself, my proposed auto init solution should still work. You can btw emulate that partially by a simple event script turning off the modem auto init on a power manager event.
But… I still cannot reproduce your reported behaviour on my modules. Not even at 6V supply on the 12V input & without any USB connection, exactly as in your setup.
At 6V I see a sleep consumption (including the attached GPS antenna) of 19 mA after the power down by the power management component. Every 60 seconds, there's a brief wakeup period of ~ 2-3 seconds at up to ~ 46 mA, then it goes back to sleep. That cycle has been running for about an hour now at 6.2V without a single modem power up or full boot.
In other words everything seems to work as designed, the ESP32 does not fail to recognize it's been sleeping, and the modem remains powered down.
Maybe your test setup has some hidden flaw? A circuit break, broken cable, something like that, causing the module to actually do an occasional full power cycle?
Regards, Michael
Am 22.11.22 um 17:54 schrieb Patrick Stein:
Thanx a lot for all your feedback. That makes it clear to me that my approach to sleep the system immediately after wake as soon as it’s clear that the 12v battery is below the adjusted threshold won’t work. It will only work when the battery settings are still in non volatile deep sleep ram.
The main problem is if the system is starting and not recognizing it’s coming out of sleep that approach will fail as the modem will start up and stay even when the esp is in deepsleep - right ?
What you do think would be the best approach here to get all devices powered off quickly and going to the lowest possible power consumption while checking every 30/60 seconds for a car wakeup ? As I bought the OVMS modules to put them in three cars (mine and two friends) detaching and attaching the device or switching it off with manual switch is not feasable. I think reaching down to a 130mW sleep would be ok for most use cases (except vacations) but maybe it might be really easier to build something that detaches power from the OVMS module or put OVMS in the USB bus and cut the 12v obd line altogether.
Regards - Patrick Stein Beste Grüße - Patrick Stein
On 21. Nov 2022, at 20:15, Michael Balzer <dexter@expeedo.de> wrote:
Patrick, Mark is probably right assuming you've been measuring with the modem still powered on; with the modem still enabled, I also see a minimum consumption level at around 250-280 mW.
The modem is on by default on the initial boot (first power up), that's due to hardware defaults. So on an initial power up, you always need to wait for the system to power the modem down before doing measurements. That will normally take about 80-90 seconds.
You can easily see if the modem is off by observing the blue LED. From the log you can normally tell it's off by seeing these messages:
I (80323) cellular: State: Enter PoweredOff state D (80323) events: Signal(system.modem.poweredoff)
Another option is checking the "cellular status" command showing "State: PoweredOff".
You can also control modem power by the switch S1 on the modem board. You need to push it for 3 seconds to manually power off. Power on only needs about half a second. Be aware the driver will need some time and possibly some modem power cycles to resync if you change the power state manually when awake.
Regards, Michael
Am 21.11.22 um 07:53 schrieb Mark Webb-Johnson:
It should be the other way around (particular at low power sleep). The usb serial converter chip draws current but only from usb, so when powered via 12v that should be off and not consuming anything. Without a modem and in deep sleep that made a significant difference.
I guess the difference you are seeing is most likely the modem. That consumes the most power, but in some cases can’t be powered by usb due to insufficient power availability (particularly over low powered usb ports on laptops, etc).
With the modem powered on, there are significant differences in power consumption depending on whether it is locked onto a cell tower, or searching, and the frequency / technology negotiated. 4G is pretty power hungry.
It is hard to get much benefit from deep sleep with the modem powered on. Some other systems get around this by putting the modem in sleep mode as well; awoken by a sms message.
Regards, Mark
> On 21 Nov 2022, at 2:01 PM, Patrick Stein <4seasons-ovms@jinx.de> wrote: > > Hello Michael, > > >> On 14. Nov 2022, at 17:26, Michael Balzer <dexter@expeedo.de> wrote: >> >> Am 14.11.22 um 10:25 schrieb Patrick Stein: >>>> As written in the issue discussion, there's a minimum load from the unswitchable components. In deep sleep mode, that's around 155 mW with the GPS antenna attached, and ~ 100 mW with GPS antenna detached. An option to control the GPS antenna could be using a simple 12V relay, either on SW_12V or on the ignition 12V line, to switch the antenna's GND connection. >>>> >>> I’ve never seen such a low value - lowest I’ve seen is 250 mW in deep sleep with all my four modules. >> I did the measurements using a USB power meter, just verified with a multimeter for 12V (11.6V Lipo), which gave slightly higher results: >> • base load in Wifi mode, GPS antenna attached ~ 60 mA / 700 mW >> • sleep, GPS antenna attached ~ 16 mA / 180 mW >> • sleep, GPS antenna detached ~ 11 mA / 130 mW >> >> So nowhere near 250 mW. Mark, any idea about this? > I now know why you have different measurements. I bought a USB power meter and it just arrived and it’s now clear. I have just tried one module (v.3.3 with the current edge software) asleep at 9 Volts and it draws around 490 mW (54-55mA) , when I switch my lab power supply off it draws 290mW at 5.1Volts (55mA) over USB. > > So it seems that the module requires a lot less over USB than over OBD. > > -- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev -- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Beste Grüße - Patrick Stein
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I think this is pretty decent & complete now. I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs. Regards, Michael Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in/> or p/in.in <http://in.in/> or lb_in.in <http://lb_in.in/> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771 I'm hoping this P/R is ok in this form (made of 5 separate commits). I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance. The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice! I should also add 'bar' for pressure given that for some reason that's still a thing people want. //.ichael On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km
*OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh
*OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set
*OVMS# metric get xiq.c.speed *8.04673km/h
*OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n") print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
> Yeah - this was copied code from kia/kona and is what triggered > these ideas; I totally agree this shouldn't be doubled up on. > > I've got some commits centred round Metrics that I'll just check > over and push up ... and then I'll just have the single xiq.v. > trip.consumption metric (unless you have some ideas for the > namespace) which will be much neater. > > If it's ok with you then I might do that unit conversion proposal. > Would it ok if the unit specifications were the same as to the > programatic codes in ovms_metrics.h? > (kWh, WattHours , MetersPSS ) > I would probably add a command > metric units <spec> > to list all (matching) units and their associated Labels. > > //.ichael > > On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> > wrote: > >> Michael, >> >> adding unit conversion support to the shell and Duktape commands is >> a good idea. >> >> Metrics are not meant to provide a user interface, they should be >> defined to be efficient and non-redundant. >> >> Btw, metrics names also shall not use upper case characters, and >> shall only use "." as a separator. >> >> Regards, >> Michael >> >> >> Am 05.11.22 um 11:22 schrieb Michael Geddes: >> >> Hi all, >> Some of the code I copied from Kona/Kia code had both kwh/100km and >> km/kwh metrics in the code as 'Other'. >> Adding the various power consumption Units is not particularly hard >> (I will have a pull-request soon) - though the conversions between them all >> required some thought! >> ... but it also made me think these two metrics that are (with the >> consumption units added) defined like this: >> m_v_trip_consumption1 = >> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >> m_v_trip_consumption2 = MyMetrics.InitFloat(" >> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >> >> These are effectively the same metric but in different units! >> I'm wondering if we would be better to have scripting and Duktape >> support for converting metrics to different unit! This might be also quite >> useful for those strange countries that insist on using miles as a >> measurement. >> >> On top of the 'metric list' and 'metric set' we could add a 'metric >> get' which gets a single value.. and add unit support for get/set. >> >> I've also got a pull request that improves the precision of the >> km<->mi conversions and factors it out. >> >> //.ichael >> >> _______________________________________________ >> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >> >> >> -- >> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >> >> _______________________________________________ >> OvmsDev mailing list >> OvmsDev@lists.openvehicles.com >> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >> >
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button. [image: image.png] Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table). Thoughts / comments? //.ichael On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
> Right, so I've implemented some stuff that seems to work quite well. > > > https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 > should be ready now after a couple of stupid mistakes slipped through. > This absolutely needs somebody to review it please! (There's a reason why > I've converted some if()'s to switch() - which is that it will be used in > the follow-up commit). > > The commit that will follow on from that it implements the new > Units: kWh/100km, km/kWh and mi/kWh. > > This is a summary of what I've implemented for scripting - including > showing the unit codes I have so far. I've considered a few things: > * Should some of the longer unit codes be shortened (eg mi, mins, > m, ft, deg, perc) > * The unit codes could be much more regular and separated by > dots eg: > watthours -> w.h > kwhp100km -> kw.h_100km or kw.h/100km > miph -> mi_h or mi/h (or should it be mph). > psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, > but predictable) > > *OVMS# metric units* > km : km > miles : M > meters : m > feet : ft > celcius : °C > fahrenheit : °F > kpa : kPa > pa : Pa > psi : psi > volts : V > amps : A > amphours : Ah > kw : kW > kwh : kWh > watts : W > watthours : Wh > seconds : Sec > minutes : Min > hours : Hour > utc : UTC > degrees : ° > kmph : km/h > miph : Mph > kmphps : km/h/s > miphps : Mph/s > mpss : m/s² > dbm : dBm > sq : sq > percent : % > whpkm : Wh/km > whpmi : Wh/mi > kwhp100km : kWh/100km > kmpkwh : km/kWh > mipkwh : mi/kWh > nm : Nm > > *OVMS# metric unit mi* > miles : M > minutes : Min > miph : Mph > miphps : Mph/s > whpmi : Wh/mi > mipkwh : mi/kWh > > > *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km > > *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh > > *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh > > > *OVMS# metric set xiq.c.speed 5 miph *Metric set > > *OVMS# metric get xiq.c.speed *8.04673km/h > > *OVMS# metric get xiq.c.speed miph *5Mph > > And then in DukTape - there are some questions I have about the > implementation: > * Names of functions? Better ideas? > * Should ValueUnit output the units? > * In Value() there is the line bool decode = > duk_opt_boolean(ctx, 1, true); > * What does 'decode' mean here? > * Do I need it for ValueUnit() ? > > > > > > > > > > > > * (function() { print( > OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( > OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") > print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); > print("\n") print( > OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* > --- Output --- > 17.0597 > 17.0597kWh/100km > 3.64233mi/kWh > 5.86177 > ------ > > The basic stuff all works - it's just quibbling over the details.. > but let's get them right! > > //.ichael > > On Sat, 5 Nov 2022 at 20:09, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> Yeah - this was copied code from kia/kona and is what triggered >> these ideas; I totally agree this shouldn't be doubled up on. >> >> I've got some commits centred round Metrics that I'll just check >> over and push up ... and then I'll just have the single xiq.v. >> trip.consumption metric (unless you have some ideas for the >> namespace) which will be much neater. >> >> If it's ok with you then I might do that unit conversion proposal. >> Would it ok if the unit specifications were the same as to the >> programatic codes in ovms_metrics.h? >> (kWh, WattHours , MetersPSS ) >> I would probably add a command >> metric units <spec> >> to list all (matching) units and their associated Labels. >> >> //.ichael >> >> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >> wrote: >> >>> Michael, >>> >>> adding unit conversion support to the shell and Duktape commands >>> is a good idea. >>> >>> Metrics are not meant to provide a user interface, they should be >>> defined to be efficient and non-redundant. >>> >>> Btw, metrics names also shall not use upper case characters, and >>> shall only use "." as a separator. >>> >>> Regards, >>> Michael >>> >>> >>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>> >>> Hi all, >>> Some of the code I copied from Kona/Kia code had both kwh/100km >>> and km/kwh metrics in the code as 'Other'. >>> Adding the various power consumption Units is not particularly >>> hard (I will have a pull-request soon) - though the conversions between >>> them all required some thought! >>> ... but it also made me think these two metrics that are (with the >>> consumption units added) defined like this: >>> m_v_trip_consumption1 = >>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>> >>> These are effectively the same metric but in different units! >>> I'm wondering if we would be better to have scripting and Duktape >>> support for converting metrics to different unit! This might be also quite >>> useful for those strange countries that insist on using miles as a >>> measurement. >>> >>> On top of the 'metric list' and 'metric set' we could add a >>> 'metric get' which gets a single value.. and add unit support for get/set. >>> >>> I've also got a pull request that improves the precision of the >>> km<->mi conversions and factors it out. >>> >>> //.ichael >>> >>> _______________________________________________ >>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>> >>> >>> -- >>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>> >>> _______________________________________________ >>> OvmsDev mailing list >>> OvmsDev@lists.openvehicles.com >>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>> >>
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Hello Michael, I would opt for a top [metric | usa | europe | asia | custom ] switch that sets the buttons automatically to the specified fields - without the default setting. The default button makes it unclear what the actual setting is. When switching to custom it should stay first at the last used setting ( metric or imperial ) that one has a good starting point. Regards, Patrick
On 13. Nov 2022, at 08:42, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
<image.png>
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote: https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote: I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units 'imperial' and 'metric' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote: Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String) and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote: Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote: Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote: I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote: Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in or p/in.in or lb_in.in (yes, slightly weird, but predictable)
OVMS# metric units km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours : Ah kw : kW kwh : kWh watts : W watthours : Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph : Mph kmphps : km/h/s miphps : Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi : Wh/mi kwhp100km : kWh/100km kmpkwh : km/kWh mipkwh : mi/kWh nm : Nm
OVMS# metric unit mi miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
OVMS# metric get xiq.v.trip.consumption 17.0597kWh/100km OVMS# metric get xiq.v.trip.consumption kpkwh 5.86177km/kWh OVMS# metric get xiq.v.trip.consumption mpkwh 3.64233mi/kWh
OVMS# metric set xiq.c.speed 5 miph Metric set OVMS# metric get xiq.c.speed 8.04673km/h OVMS# metric get xiq.c.speed miph 5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
(function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })(); --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote: Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote: Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev _______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Michael, looks good. I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-)) @Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m. Regards, Michael Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in/> or p/in.in <http://in.in/> or lb_in.in <http://lb_in.in/> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes:
Hi all, Some of the code I copied from Kona/Kia code had both kwh/100km and km/kwh metrics in the code as 'Other'. Adding the various power consumption Units is not particularly hard (I will have a pull-request soon) - though the conversions between them all required some thought! ... but it also made me think these two metrics that are (with the consumption units added) defined like this: m_v_trip_consumption1 = MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); m_v_trip_consumption2 = MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh <http://xiq.v.trip.consumption.km/kWh>", 10, 0, kPkWH);
These are effectively the same metric but in different units! I'm wondering if we would be better to have scripting and Duktape support for converting metrics to different unit! This might be also quite useful for those strange countries that insist on using miles as a measurement.
On top of the 'metric list' and 'metric set' we could add a 'metric get' which gets a single value.. and add unit support for get/set.
I've also got a pull request that improves the precision of the km<->mi conversions and factors it out.
//.ichael
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better. On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' ) v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M"v.p.trip#user: 17.3984* Then we can use this in the dials to populate the values and captions! (not that I like Miles). I [image: image.png] The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno. There's also some complications with setting up the dials (for min/max values) - like for the speed. Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc. //.ichael On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() ( https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics ).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is !IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
> I've worked out what the decode flag is for and how it works, and I > think how optional params work. > I'm pretty sure I won't need the 'AsFloatUnit' function; the unit > would be an option to AsFloat(); I'll know that soon. > > The 'Value' function is more complicated because of the optional > decode bool. I guess I could add the Unit to the end of that. > > ValueUnit could be still useful then to provide a 'Value + Unit'. > > Question: Is there a reason we shouldn't be returning with > duk_push_null if the metric !IsDefined() in both AsFloat() and > Value(metric,true) cases? > > //.ichael > > On Sun, 6 Nov 2022 at 11:22, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> Right, so I've implemented some stuff that seems to work quite >> well. >> >> >> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >> should be ready now after a couple of stupid mistakes slipped through. >> This absolutely needs somebody to review it please! (There's a reason why >> I've converted some if()'s to switch() - which is that it will be used in >> the follow-up commit). >> >> The commit that will follow on from that it implements the new >> Units: kWh/100km, km/kWh and mi/kWh. >> >> This is a summary of what I've implemented for scripting - >> including showing the unit codes I have so far. I've considered a few >> things: >> * Should some of the longer unit codes be shortened (eg mi, >> mins, m, ft, deg, perc) >> * The unit codes could be much more regular and separated by >> dots eg: >> watthours -> w.h >> kwhp100km -> kw.h_100km or kw.h/100km >> miph -> mi_h or mi/h (or should it be mph). >> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >> weird, but predictable) >> >> *OVMS# metric units* >> km : km >> miles : M >> meters : m >> feet : ft >> celcius : °C >> fahrenheit : °F >> kpa : kPa >> pa : Pa >> psi : psi >> volts : V >> amps : A >> amphours : Ah >> kw : kW >> kwh : kWh >> watts : W >> watthours : Wh >> seconds : Sec >> minutes : Min >> hours : Hour >> utc : UTC >> degrees : ° >> kmph : km/h >> miph : Mph >> kmphps : km/h/s >> miphps : Mph/s >> mpss : m/s² >> dbm : dBm >> sq : sq >> percent : % >> whpkm : Wh/km >> whpmi : Wh/mi >> kwhp100km : kWh/100km >> kmpkwh : km/kWh >> mipkwh : mi/kWh >> nm : Nm >> >> *OVMS# metric unit mi* >> miles : M >> minutes : Min >> miph : Mph >> miphps : Mph/s >> whpmi : Wh/mi >> mipkwh : mi/kWh >> >> >> *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km >> >> *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh >> >> *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh >> >> >> *OVMS# metric set xiq.c.speed 5 miph *Metric set >> >> *OVMS# metric get xiq.c.speed *8.04673km/h >> >> *OVMS# metric get xiq.c.speed miph *5Mph >> >> And then in DukTape - there are some questions I have about the >> implementation: >> * Names of functions? Better ideas? >> * Should ValueUnit output the units? >> * In Value() there is the line bool decode = >> duk_opt_boolean(ctx, 1, true); >> * What does 'decode' mean here? >> * Do I need it for ValueUnit() ? >> >> >> >> >> >> >> >> >> >> >> >> * (function() { print( >> OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( >> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") >> print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); >> print("\n") print( >> OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* >> --- Output --- >> 17.0597 >> 17.0597kWh/100km >> 3.64233mi/kWh >> 5.86177 >> ------ >> >> The basic stuff all works - it's just quibbling over the details.. >> but let's get them right! >> >> //.ichael >> >> On Sat, 5 Nov 2022 at 20:09, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> Yeah - this was copied code from kia/kona and is what triggered >>> these ideas; I totally agree this shouldn't be doubled up on. >>> >>> I've got some commits centred round Metrics that I'll just check >>> over and push up ... and then I'll just have the single xiq.v. >>> trip.consumption metric (unless you have some ideas for the >>> namespace) which will be much neater. >>> >>> If it's ok with you then I might do that unit conversion proposal. >>> Would it ok if the unit specifications were the same as to the >>> programatic codes in ovms_metrics.h? >>> (kWh, WattHours , MetersPSS ) >>> I would probably add a command >>> metric units <spec> >>> to list all (matching) units and their associated Labels. >>> >>> //.ichael >>> >>> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >>> wrote: >>> >>>> Michael, >>>> >>>> adding unit conversion support to the shell and Duktape commands >>>> is a good idea. >>>> >>>> Metrics are not meant to provide a user interface, they should be >>>> defined to be efficient and non-redundant. >>>> >>>> Btw, metrics names also shall not use upper case characters, and >>>> shall only use "." as a separator. >>>> >>>> Regards, >>>> Michael >>>> >>>> >>>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>>> >>>> Hi all, >>>> Some of the code I copied from Kona/Kia code had both kwh/100km >>>> and km/kwh metrics in the code as 'Other'. >>>> Adding the various power consumption Units is not particularly >>>> hard (I will have a pull-request soon) - though the conversions between >>>> them all required some thought! >>>> ... but it also made me think these two metrics that are (with >>>> the consumption units added) defined like this: >>>> m_v_trip_consumption1 = >>>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>>> >>>> These are effectively the same metric but in different units! >>>> I'm wondering if we would be better to have scripting and Duktape >>>> support for converting metrics to different unit! This might be also quite >>>> useful for those strange countries that insist on using miles as a >>>> measurement. >>>> >>>> On top of the 'metric list' and 'metric set' we could add a >>>> 'metric get' which gets a single value.. and add unit support for get/set. >>>> >>>> I've also got a pull request that improves the precision of the >>>> km<->mi conversions and factors it out. >>>> >>>> //.ichael >>>> >>>> _______________________________________________ >>>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>> >>>> >>>> -- >>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>>> >>>> _______________________________________________ >>>> OvmsDev mailing list >>>> OvmsDev@lists.openvehicles.com >>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>> >>> _______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Also, all the command status displays should probably be in user units. There is already stuff there to check for miles/km... But that becomes simpler. For example: Not charging SOC: 895.0‰ Ideal range: 245M Est. range: 253M ODO: 6754.9M CAC: 974.7Ah SOH: 1000‰ I mean not my choice of units... But you can! Michael On Tue, 15 Nov 2022, 8:26 am Michael Geddes, <frog@bunyip.wheelycreek.net> wrote:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M"v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
> Michael, > > looks all good to me, once again nice find with the decode argument. > Adding decode to the Value() call was only for symmetry IIRC, the main use > was with GetValues() ( > https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics > ). > > Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". > > Returning null for an undefined metric seems like a natural choice, > but is a rather deep change, as for consistency not only the Duktape > metrics API but also the Web UI metrics API would need to be changed > accordingly. Unless you've got a real use case that needs that, we should > be careful. > > Regards, > Michael > > > Am 07.11.22 um 15:00 schrieb Michael Geddes: > > I have figured out a bunch of stuff and have implemented the > following: (having done away with needing AsFloatUnit) > > OvmsMetrics.Value( {metric} [, {decode}]) > OvmsMetrics.Value( {metric}, {unit} [,{decode}]) > > It turns out that the [decode] flag wasn't working anyway (since the > function was being registered as only having 1 param)... > This way it is still really 1 function.. but I check it the second > parameter is a 'boolean', and if not.. try the second form. > > OvmsMetrics.AsFloat( {metric} [,{unit}] ) > > and add the function > > Ovms.Metrics.ValueUnit( {metric} [,{unit}]) > This prints the value and the unit. > > Here's a sample function and the output! This also shows the types > of the output. > > (function() { > x = OvmsMetrics.Value("xiq.v.trip.consumption"); > print( (typeof x) + ": "+ x+"\n" ); > x = OvmsMetrics.Value("xiq.v.trip.consumption", false); > print( (typeof x) + ": "+ x +"\n" ); > x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x +"\n"); > })(); > > number: 17.0582 > string: 17.0582 > number: 5.86227 > string: 3.64264 > string: 17.0582kWh/100km > string: 3.64264mi/kWh > number: 17.0582 > number: 5.86227 > > > > It still might be an idea to use 'null' as a return value if the > metrics is !IsDefined() but that would be changing the existing > behaviour slightly. > > //.ichael > > On Mon, 7 Nov 2022 at 08:12, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> I've worked out what the decode flag is for and how it works, and I >> think how optional params work. >> I'm pretty sure I won't need the 'AsFloatUnit' function; the unit >> would be an option to AsFloat(); I'll know that soon. >> >> The 'Value' function is more complicated because of the optional >> decode bool. I guess I could add the Unit to the end of that. >> >> ValueUnit could be still useful then to provide a 'Value + Unit'. >> >> Question: Is there a reason we shouldn't be returning with >> duk_push_null if the metric !IsDefined() in both AsFloat() and >> Value(metric,true) cases? >> >> //.ichael >> >> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> Right, so I've implemented some stuff that seems to work quite >>> well. >>> >>> >>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>> should be ready now after a couple of stupid mistakes slipped through. >>> This absolutely needs somebody to review it please! (There's a reason why >>> I've converted some if()'s to switch() - which is that it will be used in >>> the follow-up commit). >>> >>> The commit that will follow on from that it implements the new >>> Units: kWh/100km, km/kWh and mi/kWh. >>> >>> This is a summary of what I've implemented for scripting - >>> including showing the unit codes I have so far. I've considered a few >>> things: >>> * Should some of the longer unit codes be shortened (eg mi, >>> mins, m, ft, deg, perc) >>> * The unit codes could be much more regular and separated by >>> dots eg: >>> watthours -> w.h >>> kwhp100km -> kw.h_100km or kw.h/100km >>> miph -> mi_h or mi/h (or should it be mph). >>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>> weird, but predictable) >>> >>> *OVMS# metric units* >>> km : km >>> miles : M >>> meters : m >>> feet : ft >>> celcius : °C >>> fahrenheit : °F >>> kpa : kPa >>> pa : Pa >>> psi : psi >>> volts : V >>> amps : A >>> amphours : Ah >>> kw : kW >>> kwh : kWh >>> watts : W >>> watthours : Wh >>> seconds : Sec >>> minutes : Min >>> hours : Hour >>> utc : UTC >>> degrees : ° >>> kmph : km/h >>> miph : Mph >>> kmphps : km/h/s >>> miphps : Mph/s >>> mpss : m/s² >>> dbm : dBm >>> sq : sq >>> percent : % >>> whpkm : Wh/km >>> whpmi : Wh/mi >>> kwhp100km : kWh/100km >>> kmpkwh : km/kWh >>> mipkwh : mi/kWh >>> nm : Nm >>> >>> *OVMS# metric unit mi* >>> miles : M >>> minutes : Min >>> miph : Mph >>> miphps : Mph/s >>> whpmi : Wh/mi >>> mipkwh : mi/kWh >>> >>> >>> *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km >>> >>> *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh >>> >>> *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh >>> >>> >>> *OVMS# metric set xiq.c.speed 5 miph *Metric set >>> >>> *OVMS# metric get xiq.c.speed *8.04673km/h >>> >>> *OVMS# metric get xiq.c.speed miph *5Mph >>> >>> And then in DukTape - there are some questions I have about the >>> implementation: >>> * Names of functions? Better ideas? >>> * Should ValueUnit output the units? >>> * In Value() there is the line bool decode = >>> duk_opt_boolean(ctx, 1, true); >>> * What does 'decode' mean here? >>> * Do I need it for ValueUnit() ? >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> * (function() { print( >>> OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( >>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") >>> print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); >>> print("\n") print( >>> OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* >>> --- Output --- >>> 17.0597 >>> 17.0597kWh/100km >>> 3.64233mi/kWh >>> 5.86177 >>> ------ >>> >>> The basic stuff all works - it's just quibbling over the details.. >>> but let's get them right! >>> >>> //.ichael >>> >>> On Sat, 5 Nov 2022 at 20:09, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> Yeah - this was copied code from kia/kona and is what triggered >>>> these ideas; I totally agree this shouldn't be doubled up on. >>>> >>>> I've got some commits centred round Metrics that I'll just check >>>> over and push up ... and then I'll just have the single xiq.v. >>>> trip.consumption metric (unless you have some ideas for the >>>> namespace) which will be much neater. >>>> >>>> If it's ok with you then I might do that unit conversion proposal. >>>> Would it ok if the unit specifications were the same as to the >>>> programatic codes in ovms_metrics.h? >>>> (kWh, WattHours , MetersPSS ) >>>> I would probably add a command >>>> metric units <spec> >>>> to list all (matching) units and their associated Labels. >>>> >>>> //.ichael >>>> >>>> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >>>> wrote: >>>> >>>>> Michael, >>>>> >>>>> adding unit conversion support to the shell and Duktape commands >>>>> is a good idea. >>>>> >>>>> Metrics are not meant to provide a user interface, they should >>>>> be defined to be efficient and non-redundant. >>>>> >>>>> Btw, metrics names also shall not use upper case characters, and >>>>> shall only use "." as a separator. >>>>> >>>>> Regards, >>>>> Michael >>>>> >>>>> >>>>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>>>> >>>>> Hi all, >>>>> Some of the code I copied from Kona/Kia code had both kwh/100km >>>>> and km/kwh metrics in the code as 'Other'. >>>>> Adding the various power consumption Units is not particularly >>>>> hard (I will have a pull-request soon) - though the conversions between >>>>> them all required some thought! >>>>> ... but it also made me think these two metrics that are (with >>>>> the consumption units added) defined like this: >>>>> m_v_trip_consumption1 = >>>>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>>>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>>>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>>>> >>>>> These are effectively the same metric but in different units! >>>>> I'm wondering if we would be better to have scripting and >>>>> Duktape support for converting metrics to different unit! This might be >>>>> also quite useful for those strange countries that insist on using miles as >>>>> a measurement. >>>>> >>>>> On top of the 'metric list' and 'metric set' we could add a >>>>> 'metric get' which gets a single value.. and add unit support for get/set. >>>>> >>>>> I've also got a pull request that improves the precision of the >>>>> km<->mi conversions and factors it out. >>>>> >>>>> //.ichael >>>>> >>>>> _______________________________________________ >>>>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>> >>>>> >>>>> -- >>>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>>>> >>>>> _______________________________________________ >>>>> OvmsDev mailing list >>>>> OvmsDev@lists.openvehicles.com >>>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>> >>>> > _______________________________________________ > OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev >
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I've got the Dials on the Dashboard displaying in user units using the unit#code and unit#user method described previously. There are two small issues at the moment: * Changing the Unit for a group doesn't trigger all that groups (otherwise unchanged) values to be sent. So it would be waiting for them to change value. * All the configuration for the dials/charts need to be reloaded - since the ranges and captions need really to be set up in GetDashboardConfig(). Presumably ok, those settings will be rather static - so not a big deal. The problem with GetDashboardConfig() is that it is set up by each vehicle so that it can modify ranges. I have implemented a helper class.. but for this to work at all, then any car overriding GetDashboardConfig() would have to be converted to something like this (which I'm ok to do..). This is my current implementation of the Ioniq5 version of that method - I think it's more readable anyway.. but thoughts? The helper will convert all those bands / min/max to the 'user setting' units. /** * GetDashboardConfig: Hyundai Ioniq specific dashboard setup */ void OvmsHyundaiIoniqEv::GetDashboardConfig(DashboardConfig &cfg) { // Speed: dash_guage_t speed_dash(NULL, Kph); speed_dash.SetMinMax(0, 170, 5); speed_dash.AddBand("green", 0, 70, 5); speed_dash.AddBand("yellow", 70, 120, 5); speed_dash.AddBand("red", 120, 170, 5); // Voltage: dash_guage_t voltage_dash(NULL, Volts); voltage_dash.SetMinMax(300, 810); voltage_dash.AddBand("red", 350, 450); voltage_dash.AddBand("yellow", 450, 500); voltage_dash.AddBand("green", 500, 800); // SOC: dash_guage_t soc_dash("SOC ", Percentage); soc_dash.SetMinMax(0, 100); soc_dash.AddBand("red", 0, 12.5); soc_dash.AddBand("yellow", 12.5, 25); soc_dash.AddBand("green", 25, 100); // Efficiency: dash_guage_t eff_dash(NULL, WattHoursPK); // Efficency has some inverse relationships .. so choose values that work either way first eff_dash.SetMinMax(50, 300); // Then force the minimum to zero (whichever way round); eff_dash.ZeroMin(); eff_dash.AddBand("green", 50, 150); eff_dash.AddBand("yellow", 150, 250); eff_dash.AddBand("red", 250, 300); // Power: dash_guage_t power_dash(NULL, kW); power_dash.SetMinMax(-30, 30); power_dash.AddBand("violet", -10, 0); power_dash.AddBand("green", 0, 15); power_dash.AddBand("yellow", 15, 25); power_dash.AddBand("red", 25, 30); // Charger temperature: dash_guage_t charget_dash("CHG ", Celcius); charget_dash.SetMinMax(10, 80); charget_dash.SetTick(20, 5); charget_dash.AddBand("normal", 10, 65); charget_dash.AddBand("red", 65, 80); // Battery temperature: dash_guage_t batteryt_dash("BAT ", Celcius); batteryt_dash.SetMinMax(-15, 65); batteryt_dash.SetTick(25, 5); batteryt_dash.AddBand("red", -15, 0); batteryt_dash.AddBand("normal", 0, 50); batteryt_dash.AddBand("red", 50, 65); // Inverter temperature: dash_guage_t invertert_dash("PEM ", Celcius); invertert_dash.SetMinMax(20, 80); invertert_dash.SetTick(20, 5); invertert_dash.AddBand("normal", 20, 70); invertert_dash.AddBand("red", 70, 80); // Motor temperature: dash_guage_t motort_dash("MOT ", Celcius); motort_dash.SetMinMax(10, 100); motort_dash.SetTick(25, 5); motort_dash.AddBand("normal", 10, 70); motort_dash.AddBand("red", 70, 100); std::ostringstream str; str << "yAxis: [" << speed_dash << "," << voltage_dash << "," << soc_dash << "," << eff_dash << "," << power_dash << "," << charget_dash << "," << batteryt_dash << "," << invertert_dash << "," << motort_dash << "]"; cfg.gaugeset1 = str.str(); } On Tue, 15 Nov 2022 at 15:27, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Also, all the command status displays should probably be in user units. There is already stuff there to check for miles/km... But that becomes simpler.
For example:
Not charging SOC: 895.0‰ Ideal range: 245M Est. range: 253M ODO: 6754.9M CAC: 974.7Ah SOH: 1000‰
I mean not my choice of units... But you can!
Michael
On Tue, 15 Nov 2022, 8:26 am Michael Geddes, <frog@bunyip.wheelycreek.net> wrote:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M"v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
> Or perhaps something more specific? > > HasValue() > > Mark > > On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote: > > Signed PGP part > That's basically a good approach, but be aware 'IsDefined()' has an > ambiguous meaning here, as with the API stem "OvmsMetrics" it would > naturally be expected to mean "is this metric defined", not "does this > metric have a defined value". > > An undefined metric currently can be derived from 'Values()' > returning undefined, but that's more an undocumented side effect than > intended. > > Maybe 'GetDefined()' could be a better name, leveraging this > behaviour, i.e. returning 'undefined' for an actually undefined metric, and > 'null' for a defined metric without a value. > > Regards, > Michael > > > Am 08.11.22 um 13:46 schrieb Michael Geddes: > > Ah yes. Arrays - will check those. Yeah, how about we add a > 'IsDefined' method to metrics instead of the null thing (it does sound like > it will upset too many applecarts). > > //. > > On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> > wrote: > >> Michael, >> >> looks all good to me, once again nice find with the decode >> argument. Adding decode to the Value() call was only for symmetry IIRC, the >> main use was with GetValues() ( >> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >> ). >> >> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >> >> Returning null for an undefined metric seems like a natural choice, >> but is a rather deep change, as for consistency not only the Duktape >> metrics API but also the Web UI metrics API would need to be changed >> accordingly. Unless you've got a real use case that needs that, we should >> be careful. >> >> Regards, >> Michael >> >> >> Am 07.11.22 um 15:00 schrieb Michael Geddes: >> >> I have figured out a bunch of stuff and have implemented the >> following: (having done away with needing AsFloatUnit) >> >> OvmsMetrics.Value( {metric} [, {decode}]) >> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >> >> It turns out that the [decode] flag wasn't working anyway (since >> the function was being registered as only having 1 param)... >> This way it is still really 1 function.. but I check it the second >> parameter is a 'boolean', and if not.. try the second form. >> >> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >> >> and add the function >> >> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >> This prints the value and the unit. >> >> Here's a sample function and the output! This also shows the types >> of the output. >> >> (function() { >> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >> print( (typeof x) + ": "+ x+"\n" ); >> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >> print( (typeof x) + ": "+ x +"\n" ); >> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >> false) >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + ": "+ x +"\n"); >> })(); >> >> number: 17.0582 >> string: 17.0582 >> number: 5.86227 >> string: 3.64264 >> string: 17.0582kWh/100km >> string: 3.64264mi/kWh >> number: 17.0582 >> number: 5.86227 >> >> >> >> It still might be an idea to use 'null' as a return value if the >> metrics is !IsDefined() but that would be changing the existing >> behaviour slightly. >> >> //.ichael >> >> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> I've worked out what the decode flag is for and how it works, and >>> I think how optional params work. >>> I'm pretty sure I won't need the 'AsFloatUnit' function; the unit >>> would be an option to AsFloat(); I'll know that soon. >>> >>> The 'Value' function is more complicated because of the optional >>> decode bool. I guess I could add the Unit to the end of that. >>> >>> ValueUnit could be still useful then to provide a 'Value + Unit'. >>> >>> Question: Is there a reason we shouldn't be returning with >>> duk_push_null if the metric !IsDefined() in both AsFloat() >>> and Value(metric,true) cases? >>> >>> //.ichael >>> >>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> Right, so I've implemented some stuff that seems to work quite >>>> well. >>>> >>>> >>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>> should be ready now after a couple of stupid mistakes slipped through. >>>> This absolutely needs somebody to review it please! (There's a reason why >>>> I've converted some if()'s to switch() - which is that it will be used in >>>> the follow-up commit). >>>> >>>> The commit that will follow on from that it implements the new >>>> Units: kWh/100km, km/kWh and mi/kWh. >>>> >>>> This is a summary of what I've implemented for scripting - >>>> including showing the unit codes I have so far. I've considered a few >>>> things: >>>> * Should some of the longer unit codes be shortened (eg mi, >>>> mins, m, ft, deg, perc) >>>> * The unit codes could be much more regular and separated by >>>> dots eg: >>>> watthours -> w.h >>>> kwhp100km -> kw.h_100km or kw.h/100km >>>> miph -> mi_h or mi/h (or should it be mph). >>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>> weird, but predictable) >>>> >>>> *OVMS# metric units* >>>> km : km >>>> miles : M >>>> meters : m >>>> feet : ft >>>> celcius : °C >>>> fahrenheit : °F >>>> kpa : kPa >>>> pa : Pa >>>> psi : psi >>>> volts : V >>>> amps : A >>>> amphours : Ah >>>> kw : kW >>>> kwh : kWh >>>> watts : W >>>> watthours : Wh >>>> seconds : Sec >>>> minutes : Min >>>> hours : Hour >>>> utc : UTC >>>> degrees : ° >>>> kmph : km/h >>>> miph : Mph >>>> kmphps : km/h/s >>>> miphps : Mph/s >>>> mpss : m/s² >>>> dbm : dBm >>>> sq : sq >>>> percent : % >>>> whpkm : Wh/km >>>> whpmi : Wh/mi >>>> kwhp100km : kWh/100km >>>> kmpkwh : km/kWh >>>> mipkwh : mi/kWh >>>> nm : Nm >>>> >>>> *OVMS# metric unit mi* >>>> miles : M >>>> minutes : Min >>>> miph : Mph >>>> miphps : Mph/s >>>> whpmi : Wh/mi >>>> mipkwh : mi/kWh >>>> >>>> >>>> *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km >>>> >>>> *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh >>>> >>>> *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh >>>> >>>> >>>> *OVMS# metric set xiq.c.speed 5 miph *Metric set >>>> >>>> *OVMS# metric get xiq.c.speed *8.04673km/h >>>> >>>> *OVMS# metric get xiq.c.speed miph *5Mph >>>> >>>> And then in DukTape - there are some questions I have about the >>>> implementation: >>>> * Names of functions? Better ideas? >>>> * Should ValueUnit output the units? >>>> * In Value() there is the line bool decode = >>>> duk_opt_boolean(ctx, 1, true); >>>> * What does 'decode' mean here? >>>> * Do I need it for ValueUnit() ? >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> * (function() { print( >>>> OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( >>>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") >>>> print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); >>>> print("\n") print( >>>> OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* >>>> --- Output --- >>>> 17.0597 >>>> 17.0597kWh/100km >>>> 3.64233mi/kWh >>>> 5.86177 >>>> ------ >>>> >>>> The basic stuff all works - it's just quibbling over the >>>> details.. but let's get them right! >>>> >>>> //.ichael >>>> >>>> On Sat, 5 Nov 2022 at 20:09, Michael Geddes < >>>> frog@bunyip.wheelycreek.net> wrote: >>>> >>>>> Yeah - this was copied code from kia/kona and is what triggered >>>>> these ideas; I totally agree this shouldn't be doubled up on. >>>>> >>>>> I've got some commits centred round Metrics that I'll just check >>>>> over and push up ... and then I'll just have the single xiq.v. >>>>> trip.consumption metric (unless you have some ideas for the >>>>> namespace) which will be much neater. >>>>> >>>>> If it's ok with you then I might do that unit conversion >>>>> proposal. >>>>> Would it ok if the unit specifications were the same as to the >>>>> programatic codes in ovms_metrics.h? >>>>> (kWh, WattHours , MetersPSS ) >>>>> I would probably add a command >>>>> metric units <spec> >>>>> to list all (matching) units and their associated Labels. >>>>> >>>>> //.ichael >>>>> >>>>> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >>>>> wrote: >>>>> >>>>>> Michael, >>>>>> >>>>>> adding unit conversion support to the shell and Duktape >>>>>> commands is a good idea. >>>>>> >>>>>> Metrics are not meant to provide a user interface, they should >>>>>> be defined to be efficient and non-redundant. >>>>>> >>>>>> Btw, metrics names also shall not use upper case characters, >>>>>> and shall only use "." as a separator. >>>>>> >>>>>> Regards, >>>>>> Michael >>>>>> >>>>>> >>>>>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>>>>> >>>>>> Hi all, >>>>>> Some of the code I copied from Kona/Kia code had both kwh/100km >>>>>> and km/kwh metrics in the code as 'Other'. >>>>>> Adding the various power consumption Units is not particularly >>>>>> hard (I will have a pull-request soon) - though the conversions between >>>>>> them all required some thought! >>>>>> ... but it also made me think these two metrics that are (with >>>>>> the consumption units added) defined like this: >>>>>> m_v_trip_consumption1 = >>>>>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>>>>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>>>>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>>>>> >>>>>> These are effectively the same metric but in different units! >>>>>> I'm wondering if we would be better to have scripting and >>>>>> Duktape support for converting metrics to different unit! This might be >>>>>> also quite useful for those strange countries that insist on using miles as >>>>>> a measurement. >>>>>> >>>>>> On top of the 'metric list' and 'metric set' we could add a >>>>>> 'metric get' which gets a single value.. and add unit support for get/set. >>>>>> >>>>>> I've also got a pull request that improves the precision of the >>>>>> km<->mi conversions and factors it out. >>>>>> >>>>>> //.ichael >>>>>> >>>>>> _______________________________________________ >>>>>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>>> >>>>>> >>>>>> -- >>>>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>>>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>>>>> >>>>>> _______________________________________________ >>>>>> OvmsDev mailing list >>>>>> OvmsDev@lists.openvehicles.com >>>>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>>> >>>>> >> _______________________________________________ >> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >> >> >> -- >> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >> >> _______________________________________________ >> OvmsDev mailing list >> OvmsDev@lists.openvehicles.com >> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >> > > _______________________________________________ > OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev >
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Michael, I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular. My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements. An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand. Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason. Regards, Michael Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
image.png
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes:
I have figured out a bunch of stuff and have implemented the following: (having done away with needing AsFloatUnit)
OvmsMetrics.Value( {metric} [, {decode}]) OvmsMetrics.Value( {metric}, {unit} [,{decode}])
It turns out that the [decode] flag wasn't working anyway (since the function was being registered as only having 1 param)... This way it is still really 1 function.. but I check it the second parameter is a 'boolean', and if not.. try the second form.
OvmsMetrics.AsFloat( {metric} [,{unit}] )
and add the function
Ovms.Metrics.ValueUnit( {metric} [,{unit}]) This prints the value and the unit.
Here's a sample function and the output! This also shows the types of the output.
(function() { x = OvmsMetrics.Value("xiq.v.trip.consumption"); print( (typeof x) + ": "+ x+"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption", false); print( (typeof x) + ": "+ x +"\n" ); x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") print( (typeof x) + ": "+ x +"\n"); x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") print( (typeof x) + ": "+ x +"\n"); })();
number: 17.0582 string: 17.0582 number: 5.86227 string: 3.64264 string: 17.0582kWh/100km string: 3.64264mi/kWh number: 17.0582 number: 5.86227
It still might be an idea to use 'null' as a return value if the metrics is!IsDefined() but that would be changing the existing behaviour slightly.
//.ichael
On Mon, 7 Nov 2022 at 08:12, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I've worked out what the decode flag is for and how it works, and I think how optional params work. I'm pretty sure I won't need the 'AsFloatUnit' function; the unit would be an option to AsFloat(); I'll know that soon.
The 'Value' function is more complicated because of the optional decode bool. I guess I could add the Unit to the end of that.
ValueUnit could be still useful then to provide a 'Value + Unit'.
Question: Is there a reason we shouldn't be returning with duk_push_null if the metric !IsDefined() in both AsFloat() and Value(metric,true) cases?
//.ichael
On Sun, 6 Nov 2022 at 11:22, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Right, so I've implemented some stuff that seems to work quite well.
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 should be ready now after a couple of stupid mistakes slipped through. This absolutely needs somebody to review it please! (There's a reason why I've converted some if()'s to switch() - which is that it will be used in the follow-up commit).
The commit that will follow on from that it implements the new Units: kWh/100km, km/kWh and mi/kWh.
This is a summary of what I've implemented for scripting - including showing the unit codes I have so far. I've considered a few things: * Should some of the longer unit codes be shortened (eg mi, mins, m, ft, deg, perc) * The unit codes could be much more regular and separated by dots eg: watthours -> w.h kwhp100km -> kw.h_100km or kw.h/100km miph -> mi_h or mi/h (or should it be mph). psi -> p_in.in <http://p_in.in/> or p/in.in <http://in.in/> or lb_in.in <http://lb_in.in/> (yes, slightly weird, but predictable)
*OVMS# metric units* km : km miles : M meters : m feet : ft celcius : °C fahrenheit : °F kpa : kPa pa : Pa psi : psi volts : V amps : A amphours: Ah kw : kW kwh : kWh watts : W watthours: Wh seconds : Sec minutes : Min hours : Hour utc : UTC degrees : ° kmph : km/h miph: Mph kmphps: km/h/s miphps: Mph/s mpss : m/s² dbm : dBm sq : sq percent : % whpkm : Wh/km whpmi: Wh/mi kwhp100km : kWh/100km kmpkwh: km/kWh mipkwh: mi/kWh nm : Nm
*OVMS# metric unit mi* miles : M minutes : Min miph : Mph miphps : Mph/s whpmi : Wh/mi mipkwh : mi/kWh
*OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh
*OVMS# metric set xiq.c.speed 5 miph *Metric set *OVMS# metric get xiq.c.speed *8.04673km/h *OVMS# metric get xiq.c.speed miph *5Mph
And then in DukTape - there are some questions I have about the implementation: * Names of functions? Better ideas? * Should ValueUnit output the units? * In Value() there is the line bool decode = duk_opt_boolean(ctx, 1, true); * What does 'decode' mean here? * Do I need it for ValueUnit() ?
* (function() { print( OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); print("\n")
print( OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* --- Output --- 17.0597 17.0597kWh/100km 3.64233mi/kWh 5.86177 ------
The basic stuff all works - it's just quibbling over the details.. but let's get them right!
//.ichael
On Sat, 5 Nov 2022 at 20:09, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - this was copied code from kia/kona and is what triggered these ideas; I totally agree this shouldn't be doubled up on.
I've got some commits centred round Metrics that I'll just check over and push up ... and then I'll just have the single xiq.v.trip.consumption metric (unless you have some ideas for the namespace) which will be much neater.
If it's ok with you then I might do that unit conversion proposal. Would it ok if the unit specifications were the same as to the programatic codes in ovms_metrics.h? (kWh, WattHours , MetersPSS ) I would probably add a command metric units <spec> to list all (matching) units and their associated Labels.
//.ichael
On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
adding unit conversion support to the shell and Duktape commands is a good idea.
Metrics are not meant to provide a user interface, they should be defined to be efficient and non-redundant.
Btw, metrics names also shall not use upper case characters, and shall only use "." as a separator.
Regards, Michael
Am 05.11.22 um 11:22 schrieb Michael Geddes: > Hi all, > Some of the code I > copied from Kona/Kia > code had both kwh/100km > and km/kwh metrics in > the code as 'Other'. > Adding the various power > consumption Units is not > particularly hard (I > will have a pull-request > soon) - though the > conversions between them > all required some thought! > ... but it also made me > think these two metrics > that are (with the > consumption units added) > defined like this: > m_v_trip_consumption1 = > MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", > 10, 0, kWHP100K); > m_v_trip_consumption2 = > MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh > <http://xiq.v.trip.consumption.km/kWh>", > 10, 0, kPkWH); > > These are effectively > the same metric but in > different units! > I'm wondering if we > would be better to have > scripting and Duktape > support for converting > metrics to different > unit! This might be > also quite useful for > those strange countries > that insist on using > miles as a measurement. > > On top of the 'metric > list' and 'metric set' > we could add a 'metric > get' which gets a single > value.. and add unit > support for get/set. > > I've also got a pull > request that improves > the precision of the > km<->mi conversions and > factors it out. > > //.ichael > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Yeah, ok. I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it). Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric) A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics. This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command. //.ichael On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
> Michael, > > looks all good to me, once again nice find with the decode argument. > Adding decode to the Value() call was only for symmetry IIRC, the main use > was with GetValues() ( > https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics > ). > > Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". > > Returning null for an undefined metric seems like a natural choice, > but is a rather deep change, as for consistency not only the Duktape > metrics API but also the Web UI metrics API would need to be changed > accordingly. Unless you've got a real use case that needs that, we should > be careful. > > Regards, > Michael > > > Am 07.11.22 um 15:00 schrieb Michael Geddes: > > I have figured out a bunch of stuff and have implemented the > following: (having done away with needing AsFloatUnit) > > OvmsMetrics.Value( {metric} [, {decode}]) > OvmsMetrics.Value( {metric}, {unit} [,{decode}]) > > It turns out that the [decode] flag wasn't working anyway (since the > function was being registered as only having 1 param)... > This way it is still really 1 function.. but I check it the second > parameter is a 'boolean', and if not.. try the second form. > > OvmsMetrics.AsFloat( {metric} [,{unit}] ) > > and add the function > > Ovms.Metrics.ValueUnit( {metric} [,{unit}]) > This prints the value and the unit. > > Here's a sample function and the output! This also shows the types > of the output. > > (function() { > x = OvmsMetrics.Value("xiq.v.trip.consumption"); > print( (typeof x) + ": "+ x+"\n" ); > x = OvmsMetrics.Value("xiq.v.trip.consumption", false); > print( (typeof x) + ": "+ x +"\n" ); > x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x +"\n"); > x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x +"\n"); > })(); > > number: 17.0582 > string: 17.0582 > number: 5.86227 > string: 3.64264 > string: 17.0582kWh/100km > string: 3.64264mi/kWh > number: 17.0582 > number: 5.86227 > > > > It still might be an idea to use 'null' as a return value if the > metrics is !IsDefined() but that would be changing the existing > behaviour slightly. > > //.ichael > > On Mon, 7 Nov 2022 at 08:12, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> I've worked out what the decode flag is for and how it works, and I >> think how optional params work. >> I'm pretty sure I won't need the 'AsFloatUnit' function; the unit >> would be an option to AsFloat(); I'll know that soon. >> >> The 'Value' function is more complicated because of the optional >> decode bool. I guess I could add the Unit to the end of that. >> >> ValueUnit could be still useful then to provide a 'Value + Unit'. >> >> Question: Is there a reason we shouldn't be returning with >> duk_push_null if the metric !IsDefined() in both AsFloat() and >> Value(metric,true) cases? >> >> //.ichael >> >> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> Right, so I've implemented some stuff that seems to work quite >>> well. >>> >>> >>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>> should be ready now after a couple of stupid mistakes slipped through. >>> This absolutely needs somebody to review it please! (There's a reason why >>> I've converted some if()'s to switch() - which is that it will be used in >>> the follow-up commit). >>> >>> The commit that will follow on from that it implements the new >>> Units: kWh/100km, km/kWh and mi/kWh. >>> >>> This is a summary of what I've implemented for scripting - >>> including showing the unit codes I have so far. I've considered a few >>> things: >>> * Should some of the longer unit codes be shortened (eg mi, >>> mins, m, ft, deg, perc) >>> * The unit codes could be much more regular and separated by >>> dots eg: >>> watthours -> w.h >>> kwhp100km -> kw.h_100km or kw.h/100km >>> miph -> mi_h or mi/h (or should it be mph). >>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>> weird, but predictable) >>> >>> *OVMS# metric units* >>> km : km >>> miles : M >>> meters : m >>> feet : ft >>> celcius : °C >>> fahrenheit : °F >>> kpa : kPa >>> pa : Pa >>> psi : psi >>> volts : V >>> amps : A >>> amphours : Ah >>> kw : kW >>> kwh : kWh >>> watts : W >>> watthours : Wh >>> seconds : Sec >>> minutes : Min >>> hours : Hour >>> utc : UTC >>> degrees : ° >>> kmph : km/h >>> miph : Mph >>> kmphps : km/h/s >>> miphps : Mph/s >>> mpss : m/s² >>> dbm : dBm >>> sq : sq >>> percent : % >>> whpkm : Wh/km >>> whpmi : Wh/mi >>> kwhp100km : kWh/100km >>> kmpkwh : km/kWh >>> mipkwh : mi/kWh >>> nm : Nm >>> >>> *OVMS# metric unit mi* >>> miles : M >>> minutes : Min >>> miph : Mph >>> miphps : Mph/s >>> whpmi : Wh/mi >>> mipkwh : mi/kWh >>> >>> >>> *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km >>> >>> *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh >>> >>> *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh >>> >>> >>> *OVMS# metric set xiq.c.speed 5 miph *Metric set >>> >>> *OVMS# metric get xiq.c.speed *8.04673km/h >>> >>> *OVMS# metric get xiq.c.speed miph *5Mph >>> >>> And then in DukTape - there are some questions I have about the >>> implementation: >>> * Names of functions? Better ideas? >>> * Should ValueUnit output the units? >>> * In Value() there is the line bool decode = >>> duk_opt_boolean(ctx, 1, true); >>> * What does 'decode' mean here? >>> * Do I need it for ValueUnit() ? >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> * (function() { print( >>> OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( >>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") >>> print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); >>> print("\n") print( >>> OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* >>> --- Output --- >>> 17.0597 >>> 17.0597kWh/100km >>> 3.64233mi/kWh >>> 5.86177 >>> ------ >>> >>> The basic stuff all works - it's just quibbling over the details.. >>> but let's get them right! >>> >>> //.ichael >>> >>> On Sat, 5 Nov 2022 at 20:09, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> Yeah - this was copied code from kia/kona and is what triggered >>>> these ideas; I totally agree this shouldn't be doubled up on. >>>> >>>> I've got some commits centred round Metrics that I'll just check >>>> over and push up ... and then I'll just have the single xiq.v. >>>> trip.consumption metric (unless you have some ideas for the >>>> namespace) which will be much neater. >>>> >>>> If it's ok with you then I might do that unit conversion proposal. >>>> Would it ok if the unit specifications were the same as to the >>>> programatic codes in ovms_metrics.h? >>>> (kWh, WattHours , MetersPSS ) >>>> I would probably add a command >>>> metric units <spec> >>>> to list all (matching) units and their associated Labels. >>>> >>>> //.ichael >>>> >>>> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >>>> wrote: >>>> >>>>> Michael, >>>>> >>>>> adding unit conversion support to the shell and Duktape commands >>>>> is a good idea. >>>>> >>>>> Metrics are not meant to provide a user interface, they should >>>>> be defined to be efficient and non-redundant. >>>>> >>>>> Btw, metrics names also shall not use upper case characters, and >>>>> shall only use "." as a separator. >>>>> >>>>> Regards, >>>>> Michael >>>>> >>>>> >>>>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>>>> >>>>> Hi all, >>>>> Some of the code I copied from Kona/Kia code had both kwh/100km >>>>> and km/kwh metrics in the code as 'Other'. >>>>> Adding the various power consumption Units is not particularly >>>>> hard (I will have a pull-request soon) - though the conversions between >>>>> them all required some thought! >>>>> ... but it also made me think these two metrics that are (with >>>>> the consumption units added) defined like this: >>>>> m_v_trip_consumption1 = >>>>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>>>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>>>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>>>> >>>>> These are effectively the same metric but in different units! >>>>> I'm wondering if we would be better to have scripting and >>>>> Duktape support for converting metrics to different unit! This might be >>>>> also quite useful for those strange countries that insist on using miles as >>>>> a measurement. >>>>> >>>>> On top of the 'metric list' and 'metric set' we could add a >>>>> 'metric get' which gets a single value.. and add unit support for get/set. >>>>> >>>>> I've also got a pull request that improves the precision of the >>>>> km<->mi conversions and factors it out. >>>>> >>>>> //.ichael >>>>> >>>>> _______________________________________________ >>>>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>> >>>>> >>>>> -- >>>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>>>> >>>>> _______________________________________________ >>>>> OvmsDev mailing list >>>>> OvmsDev@lists.openvehicles.com >>>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>> >>>> > _______________________________________________ > OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev >
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go. Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel. Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way. I've had a little play with the Proxy object .. so at least I know this should work: Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*). The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*. For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept. Any immediate thoughts? Dangers? I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there. //.ichael On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
> Or perhaps something more specific? > > HasValue() > > Mark > > On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote: > > Signed PGP part > That's basically a good approach, but be aware 'IsDefined()' has an > ambiguous meaning here, as with the API stem "OvmsMetrics" it would > naturally be expected to mean "is this metric defined", not "does this > metric have a defined value". > > An undefined metric currently can be derived from 'Values()' > returning undefined, but that's more an undocumented side effect than > intended. > > Maybe 'GetDefined()' could be a better name, leveraging this > behaviour, i.e. returning 'undefined' for an actually undefined metric, and > 'null' for a defined metric without a value. > > Regards, > Michael > > > Am 08.11.22 um 13:46 schrieb Michael Geddes: > > Ah yes. Arrays - will check those. Yeah, how about we add a > 'IsDefined' method to metrics instead of the null thing (it does sound like > it will upset too many applecarts). > > //. > > On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> > wrote: > >> Michael, >> >> looks all good to me, once again nice find with the decode >> argument. Adding decode to the Value() call was only for symmetry IIRC, the >> main use was with GetValues() ( >> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >> ). >> >> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >> >> Returning null for an undefined metric seems like a natural choice, >> but is a rather deep change, as for consistency not only the Duktape >> metrics API but also the Web UI metrics API would need to be changed >> accordingly. Unless you've got a real use case that needs that, we should >> be careful. >> >> Regards, >> Michael >> >> >> Am 07.11.22 um 15:00 schrieb Michael Geddes: >> >> I have figured out a bunch of stuff and have implemented the >> following: (having done away with needing AsFloatUnit) >> >> OvmsMetrics.Value( {metric} [, {decode}]) >> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >> >> It turns out that the [decode] flag wasn't working anyway (since >> the function was being registered as only having 1 param)... >> This way it is still really 1 function.. but I check it the second >> parameter is a 'boolean', and if not.. try the second form. >> >> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >> >> and add the function >> >> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >> This prints the value and the unit. >> >> Here's a sample function and the output! This also shows the types >> of the output. >> >> (function() { >> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >> print( (typeof x) + ": "+ x+"\n" ); >> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >> print( (typeof x) + ": "+ x +"\n" ); >> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >> false) >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >> print( (typeof x) + ": "+ x +"\n"); >> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + ": "+ x +"\n"); >> })(); >> >> number: 17.0582 >> string: 17.0582 >> number: 5.86227 >> string: 3.64264 >> string: 17.0582kWh/100km >> string: 3.64264mi/kWh >> number: 17.0582 >> number: 5.86227 >> >> >> >> It still might be an idea to use 'null' as a return value if the >> metrics is !IsDefined() but that would be changing the existing >> behaviour slightly. >> >> //.ichael >> >> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> I've worked out what the decode flag is for and how it works, and >>> I think how optional params work. >>> I'm pretty sure I won't need the 'AsFloatUnit' function; the unit >>> would be an option to AsFloat(); I'll know that soon. >>> >>> The 'Value' function is more complicated because of the optional >>> decode bool. I guess I could add the Unit to the end of that. >>> >>> ValueUnit could be still useful then to provide a 'Value + Unit'. >>> >>> Question: Is there a reason we shouldn't be returning with >>> duk_push_null if the metric !IsDefined() in both AsFloat() >>> and Value(metric,true) cases? >>> >>> //.ichael >>> >>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> Right, so I've implemented some stuff that seems to work quite >>>> well. >>>> >>>> >>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>> should be ready now after a couple of stupid mistakes slipped through. >>>> This absolutely needs somebody to review it please! (There's a reason why >>>> I've converted some if()'s to switch() - which is that it will be used in >>>> the follow-up commit). >>>> >>>> The commit that will follow on from that it implements the new >>>> Units: kWh/100km, km/kWh and mi/kWh. >>>> >>>> This is a summary of what I've implemented for scripting - >>>> including showing the unit codes I have so far. I've considered a few >>>> things: >>>> * Should some of the longer unit codes be shortened (eg mi, >>>> mins, m, ft, deg, perc) >>>> * The unit codes could be much more regular and separated by >>>> dots eg: >>>> watthours -> w.h >>>> kwhp100km -> kw.h_100km or kw.h/100km >>>> miph -> mi_h or mi/h (or should it be mph). >>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>> weird, but predictable) >>>> >>>> *OVMS# metric units* >>>> km : km >>>> miles : M >>>> meters : m >>>> feet : ft >>>> celcius : °C >>>> fahrenheit : °F >>>> kpa : kPa >>>> pa : Pa >>>> psi : psi >>>> volts : V >>>> amps : A >>>> amphours : Ah >>>> kw : kW >>>> kwh : kWh >>>> watts : W >>>> watthours : Wh >>>> seconds : Sec >>>> minutes : Min >>>> hours : Hour >>>> utc : UTC >>>> degrees : ° >>>> kmph : km/h >>>> miph : Mph >>>> kmphps : km/h/s >>>> miphps : Mph/s >>>> mpss : m/s² >>>> dbm : dBm >>>> sq : sq >>>> percent : % >>>> whpkm : Wh/km >>>> whpmi : Wh/mi >>>> kwhp100km : kWh/100km >>>> kmpkwh : km/kWh >>>> mipkwh : mi/kWh >>>> nm : Nm >>>> >>>> *OVMS# metric unit mi* >>>> miles : M >>>> minutes : Min >>>> miph : Mph >>>> miphps : Mph/s >>>> whpmi : Wh/mi >>>> mipkwh : mi/kWh >>>> >>>> >>>> *OVMS# metric get xiq.v.trip.consumption *17.0597kWh/100km >>>> >>>> *OVMS# metric get xiq.v.trip.consumption kpkwh *5.86177km/kWh >>>> >>>> *OVMS# metric get xiq.v.trip.consumption mpkwh *3.64233mi/kWh >>>> >>>> >>>> *OVMS# metric set xiq.c.speed 5 miph *Metric set >>>> >>>> *OVMS# metric get xiq.c.speed *8.04673km/h >>>> >>>> *OVMS# metric get xiq.c.speed miph *5Mph >>>> >>>> And then in DukTape - there are some questions I have about the >>>> implementation: >>>> * Names of functions? Better ideas? >>>> * Should ValueUnit output the units? >>>> * In Value() there is the line bool decode = >>>> duk_opt_boolean(ctx, 1, true); >>>> * What does 'decode' mean here? >>>> * Do I need it for ValueUnit() ? >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> * (function() { print( >>>> OvmsMetrics.Value("xiq.v.trip.consumption")); print("\n") print( >>>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); print("\n") >>>> print( OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); >>>> print("\n") print( >>>> OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); })();* >>>> --- Output --- >>>> 17.0597 >>>> 17.0597kWh/100km >>>> 3.64233mi/kWh >>>> 5.86177 >>>> ------ >>>> >>>> The basic stuff all works - it's just quibbling over the >>>> details.. but let's get them right! >>>> >>>> //.ichael >>>> >>>> On Sat, 5 Nov 2022 at 20:09, Michael Geddes < >>>> frog@bunyip.wheelycreek.net> wrote: >>>> >>>>> Yeah - this was copied code from kia/kona and is what triggered >>>>> these ideas; I totally agree this shouldn't be doubled up on. >>>>> >>>>> I've got some commits centred round Metrics that I'll just check >>>>> over and push up ... and then I'll just have the single xiq.v. >>>>> trip.consumption metric (unless you have some ideas for the >>>>> namespace) which will be much neater. >>>>> >>>>> If it's ok with you then I might do that unit conversion >>>>> proposal. >>>>> Would it ok if the unit specifications were the same as to the >>>>> programatic codes in ovms_metrics.h? >>>>> (kWh, WattHours , MetersPSS ) >>>>> I would probably add a command >>>>> metric units <spec> >>>>> to list all (matching) units and their associated Labels. >>>>> >>>>> //.ichael >>>>> >>>>> On Sat, 5 Nov 2022 at 18:48, Michael Balzer <dexter@expeedo.de> >>>>> wrote: >>>>> >>>>>> Michael, >>>>>> >>>>>> adding unit conversion support to the shell and Duktape >>>>>> commands is a good idea. >>>>>> >>>>>> Metrics are not meant to provide a user interface, they should >>>>>> be defined to be efficient and non-redundant. >>>>>> >>>>>> Btw, metrics names also shall not use upper case characters, >>>>>> and shall only use "." as a separator. >>>>>> >>>>>> Regards, >>>>>> Michael >>>>>> >>>>>> >>>>>> Am 05.11.22 um 11:22 schrieb Michael Geddes: >>>>>> >>>>>> Hi all, >>>>>> Some of the code I copied from Kona/Kia code had both kwh/100km >>>>>> and km/kwh metrics in the code as 'Other'. >>>>>> Adding the various power consumption Units is not particularly >>>>>> hard (I will have a pull-request soon) - though the conversions between >>>>>> them all required some thought! >>>>>> ... but it also made me think these two metrics that are (with >>>>>> the consumption units added) defined like this: >>>>>> m_v_trip_consumption1 = >>>>>> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", 10, 0, kWHP100K); >>>>>> m_v_trip_consumption2 = MyMetrics.InitFloat(" >>>>>> xiq.v.trip.consumption.km/kWh", 10, 0, kPkWH); >>>>>> >>>>>> These are effectively the same metric but in different units! >>>>>> I'm wondering if we would be better to have scripting and >>>>>> Duktape support for converting metrics to different unit! This might be >>>>>> also quite useful for those strange countries that insist on using miles as >>>>>> a measurement. >>>>>> >>>>>> On top of the 'metric list' and 'metric set' we could add a >>>>>> 'metric get' which gets a single value.. and add unit support for get/set. >>>>>> >>>>>> I've also got a pull request that improves the precision of the >>>>>> km<->mi conversions and factors it out. >>>>>> >>>>>> //.ichael >>>>>> >>>>>> _______________________________________________ >>>>>> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>>> >>>>>> >>>>>> -- >>>>>> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >>>>>> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >>>>>> >>>>>> _______________________________________________ >>>>>> OvmsDev mailing list >>>>>> OvmsDev@lists.openvehicles.com >>>>>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >>>>>> >>>>> >> _______________________________________________ >> OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev >> >> >> -- >> Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal >> Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 >> >> _______________________________________________ >> OvmsDev mailing list >> OvmsDev@lists.openvehicles.com >> http://lists.openvehicles.com/mailman/listinfo/ovmsdev >> > > _______________________________________________ > OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev >
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units. Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream. My thoughts on this so far: Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured. For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well. Proposal: a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'. b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low. The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be: { units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … } c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects: units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph" units["units.distance"] = "M" unitcodes["units.distance"] = "miles" …or provide a getter that tests for the key existence and returns an object with empty fields as necessary. With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings. d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like… var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter var trip_display = toUserValue("units.distance", 1234); Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]". The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin. Thoughts, comments? Regards, Michael Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload /metrics['prop']/ and /"prop" in metrics operations/).
The /get /function would return the underlying value if it exists in the /metrics_ /array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the /metrics_/ array - it would then do a subscribe+query on the websocket getting the current value and adding it into the /metrics_/ container. If it was unavailable then it would put /undefined/ into the array. The 'has' would do the get() and return true if the value was not == /undefined/.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
image.png
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes:
Ah yes. Arrays - will check those. Yeah, how about we add a 'IsDefined' method to metrics instead of the null thing (it does sound like it will upset too many applecarts).
//.
On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks all good to me, once again nice find with the decode argument. Adding decode to the Value() call was only for symmetry IIRC, the main use was with GetValues() (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics).
Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp".
Returning null for an undefined metric seems like a natural choice, but is a rather deep change, as for consistency not only the Duktape metrics API but also the Web UI metrics API would need to be changed accordingly. Unless you've got a real use case that needs that, we should be careful.
Regards, Michael
Am 07.11.22 um 15:00 schrieb Michael Geddes: > I have figured out a bunch of > stuff and have implemented the > following: (having done away > with needing AsFloatUnit) > > OvmsMetrics.Value( {metric} [, > {decode}]) > OvmsMetrics.Value( {metric}, > {unit} [,{decode}]) > > It turns out that the [decode] > flag wasn't working anyway > (since the function was being > registered as only having 1 > param)... > This way it is still really 1 > function.. but I check it the > second parameter is a 'boolean', > and if not.. try the second form. > > OvmsMetrics.AsFloat( {metric} > [,{unit}] ) > > and add the function > > Ovms.Metrics.ValueUnit( {metric} > [,{unit}]) > This prints the value and the unit. > > Here's a sample function and the > output! This also shows the > types of the output. > > (function() { > x = > OvmsMetrics.Value("xiq.v.trip.consumption"); > print( (typeof x) + ": "+ > x+"\n" ); > x = > OvmsMetrics.Value("xiq.v.trip.consumption", > false); > print( (typeof x) + ": "+ x > +"\n" ); > x = > OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x > +"\n"); > x = > OvmsMetrics.Value("xiq.v.trip.consumption", > "mipkwh", false) > print( (typeof x) + ": "+ x > +"\n"); > x = > OvmsMetrics.ValueUnit("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x > +"\n"); > x = > OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") > print( (typeof x) + ": "+ x > +"\n"); > x = > OvmsMetrics.AsFloat("xiq.v.trip.consumption") > print( (typeof x) + ": "+ x > +"\n"); > x = > OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") > print( (typeof x) + ": "+ x > +"\n"); > })(); > > number: 17.0582 > string: 17.0582 > number: 5.86227 > string: 3.64264 > string: 17.0582kWh/100km > string: 3.64264mi/kWh > number: 17.0582 > number: 5.86227 > > > It still might be an idea to use > 'null' as a return value if the > metrics is!IsDefined() but that > would be changing the existing > behaviour slightly. > > //.ichael > > On Mon, 7 Nov 2022 at 08:12, > Michael Geddes > <frog@bunyip.wheelycreek.net> wrote: > > I've worked out what the > decode flag is for and how > it works, and I think how > optional params work. > I'm pretty sure I won't > need the 'AsFloatUnit' > function; the unit would be > an option to AsFloat(); I'll > know that soon. > > The 'Value' function is more > complicated because of the > optional decode bool. I > guess I could add the Unit > to the end of that. > > ValueUnit could be still > useful then to provide a > 'Value + Unit'. > > Question: Is there a reason > we shouldn't be returning > with duk_push_null if the > metric !IsDefined() in both > AsFloat() and > Value(metric,true) cases? > > //.ichael > > On Sun, 6 Nov 2022 at 11:22, > Michael Geddes > <frog@bunyip.wheelycreek.net> > wrote: > > Right, so I've > implemented some stuff > that seems to work quite > well. > > https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 > should be ready now > after a couple of stupid > mistakes slipped > through. This > absolutely needs > somebody to review it > please! (There's a > reason why I've > converted some if()'s to > switch() - which is that > it will be used in the > follow-up commit). > > The commit that will > follow on from that it > implements the new > Units: kWh/100km, > km/kWh and mi/kWh. > > This is a summary of > what I've implemented > for scripting - > including showing the > unit codes I have > so far. I've considered > a few things: > * Should some of the > longer unit codes be > shortened (eg mi, mins, > m, ft, deg, perc) > * The unit codes could > be much more regular and > separated by dots eg: > watthours -> w.h > kwhp100km -> kw.h_100km > or kw.h/100km > miph -> mi_h or > mi/h (or should it be mph). > psi -> p_in.in > <http://p_in.in/> or > p/in.in <http://in.in/> > or lb_in.in > <http://lb_in.in/> (yes, > slightly weird, but > predictable) > > *OVMS# metric units* > km : km > miles : M > meters : m > feet : ft > celcius : °C > fahrenheit : °F > kpa : kPa > pa : Pa > psi : psi > volts : V > amps : A > amphours: Ah > kw : kW > kwh : kWh > watts : W > watthours: Wh > seconds : Sec > minutes : Min > hours : Hour > utc : UTC > degrees : ° > kmph : km/h > miph: Mph > kmphps: km/h/s > miphps: Mph/s > mpss : m/s² > dbm : dBm > sq : sq > percent : % > whpkm : Wh/km > whpmi: Wh/mi > kwhp100km : kWh/100km > kmpkwh: km/kWh > mipkwh: mi/kWh > nm : Nm > > *OVMS# metric unit mi* > miles : M > minutes : Min > miph : Mph > miphps : Mph/s > whpmi : Wh/mi > mipkwh : mi/kWh > > *OVMS# metric get > xiq.v.trip.consumption > *17.0597kWh/100km > *OVMS# metric get > xiq.v.trip.consumption kpkwh > *5.86177km/kWh > *OVMS# metric get > xiq.v.trip.consumption mpkwh > *3.64233mi/kWh > > *OVMS# metric set > xiq.c.speed 5 miph > *Metric set > *OVMS# metric get > xiq.c.speed > *8.04673km/h > *OVMS# metric get > xiq.c.speed miph > *5Mph > > And then in DukTape - > there are some questions > I have about the > implementation: > * Names of functions? > Better ideas? > * Should ValueUnit > output the units? > * In Value() there is > the line bool decode = > duk_opt_boolean(ctx, 1, > true); > * What does 'decode' > mean here? > * Do I need it for > ValueUnit() ? > > * > (function() { > print( > OvmsMetrics.Value("xiq.v.trip.consumption")); > print("\n") > print( > OvmsMetrics.ValueUnit("xiq.v.trip.consumption","")); > print("\n") > print( > OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh")); > print("\n") > > print( > OvmsMetrics.AsFloatUnit("xiq.v.trip.consumption","kmpkwh")); > })();* > --- Output --- > 17.0597 > 17.0597kWh/100km > 3.64233mi/kWh > 5.86177 > ------ > > The basic stuff all > works - it's just > quibbling over the > details.. but let's get > them right! > > //.ichael > > On Sat, 5 Nov 2022 at > 20:09, Michael Geddes > <frog@bunyip.wheelycreek.net> > wrote: > > Yeah - this was > copied code from > kia/kona and is what > triggered these > ideas; I totally > agree this shouldn't > be doubled up on. > > I've got some > commits centred > round Metrics that > I'll just check over > and push up ... and > then I'll just have > the single > xiq.v.trip.consumption > metric (unless you > have some ideas for > the namespace) which > will be much neater. > > If it's ok with you > then I might do that > unit conversion > proposal. > Would it ok if the > unit specifications > were the same as to > the > programatic codes in > ovms_metrics.h? > (kWh, WattHours > , MetersPSS ) > I would probably add > a command > metric units <spec> > to list all > (matching) units and > their associated Labels. > > //.ichael > > On Sat, 5 Nov 2022 > at 18:48, Michael > Balzer > <dexter@expeedo.de> > wrote: > > Michael, > > adding unit > conversion > support to the > shell and > Duktape commands > is a good idea. > > Metrics are not > meant to provide > a user > interface, they > should be > defined to be > efficient and > non-redundant. > > Btw, metrics > names also shall > not use upper > case characters, > and shall only > use "." as a > separator. > > Regards, > Michael > > > Am 05.11.22 um > 11:22 schrieb > Michael Geddes: >> Hi all, >> Some of the >> code I copied >> from Kona/Kia >> code had both >> kwh/100km and >> km/kwh metrics >> in the code as >> 'Other'. >> Adding the >> various power >> consumption >> Units is not >> particularly >> hard (I will >> have a >> pull-request >> soon) - though >> the conversions >> between them >> all required >> some thought! >> ... but it also >> made me think >> these two >> metrics that >> are (with the >> consumption >> units added) >> defined like this: >> m_v_trip_consumption1 >> = >> MyMetrics.InitFloat("xiq.v.trip.consumption.KWh/100km", >> 10, 0, kWHP100K); >> m_v_trip_consumption2 >> = >> MyMetrics.InitFloat("xiq.v.trip.consumption.km/kWh >> <http://xiq.v.trip.consumption.km/kWh>", >> 10, 0, kPkWH); >> >> These are >> effectively the >> same metric but >> in different units! >> I'm wondering >> if we would be >> better to have >> scripting and >> Duktape support >> for converting >> metrics to >> different >> unit! This >> might be also >> quite useful >> for those >> strange >> countries that >> insist on using >> miles as a >> measurement. >> >> On top of the >> 'metric list' >> and 'metric >> set' we could >> add a 'metric >> get' which gets >> a single >> value.. and add >> unit support >> for get/set. >> >> I've also got a >> pull request >> that improves >> the precision >> of the km<->mi >> conversions and >> factors it out. >> >> //.ichael >> >> _______________________________________________ >> OvmsDev mailing list >> OvmsDev@lists.openvehicles.com >> http://lists.openvehicles.com/mailman/listinfo/ovmsdev > > -- > Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal > Fon 02333 / 833 5735 * Handy 0176 / 206 989 26 > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev > > > _______________________________________________ > OvmsDev mailing list > OvmsDev@lists.openvehicles.com > http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break; Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages. I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions. { units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, … We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one. I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion. We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion). //.ichael On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*).
The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output:
19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
> Yeah - I like HasValue. I implemented IsDefined() but I will rename > it.. that's a much clearer name. > > Another thought. How about if we did this (but also with GetValues() > as well - see the special values below) > > OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 > (Number) > OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 > (String) > OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> > 3.64264 (Number) > OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> > 3.64264Mi/kWh (String) > OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> > 17.0582km/kWh (String) > > and > OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> > 3.64264Mi/kWh (String) > > I have already implemented the special values 'native' (existing), > 'imperial' and 'metric'. > > I was also thinking that in the future you could have 'user'. Where > for each group of values: > 'temperature', 'distance', 'shortdistance', 'power' etc.. you could > have a user preference. I probably won't implement it now,.but it could be > cool that any UI could just ask for the user defined units (rather than > having a separate choice). > > > > //.ichael > > On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson < > mark@webb-johnson.net> wrote: > >> Or perhaps something more specific? >> >> HasValue() >> >> Mark >> >> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> >> wrote: >> >> Signed PGP part >> That's basically a good approach, but be aware 'IsDefined()' has an >> ambiguous meaning here, as with the API stem "OvmsMetrics" it would >> naturally be expected to mean "is this metric defined", not "does this >> metric have a defined value". >> >> An undefined metric currently can be derived from 'Values()' >> returning undefined, but that's more an undocumented side effect than >> intended. >> >> Maybe 'GetDefined()' could be a better name, leveraging this >> behaviour, i.e. returning 'undefined' for an actually undefined metric, and >> 'null' for a defined metric without a value. >> >> Regards, >> Michael >> >> >> Am 08.11.22 um 13:46 schrieb Michael Geddes: >> >> Ah yes. Arrays - will check those. Yeah, how about we add a >> 'IsDefined' method to metrics instead of the null thing (it does sound like >> it will upset too many applecarts). >> >> //. >> >> On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> >> wrote: >> >>> Michael, >>> >>> looks all good to me, once again nice find with the decode >>> argument. Adding decode to the Value() call was only for symmetry IIRC, the >>> main use was with GetValues() ( >>> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >>> ). >>> >>> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >>> >>> Returning null for an undefined metric seems like a natural >>> choice, but is a rather deep change, as for consistency not only the >>> Duktape metrics API but also the Web UI metrics API would need to be >>> changed accordingly. Unless you've got a real use case that needs that, we >>> should be careful. >>> >>> Regards, >>> Michael >>> >>> >>> Am 07.11.22 um 15:00 schrieb Michael Geddes: >>> >>> I have figured out a bunch of stuff and have implemented the >>> following: (having done away with needing AsFloatUnit) >>> >>> OvmsMetrics.Value( {metric} [, {decode}]) >>> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >>> >>> It turns out that the [decode] flag wasn't working anyway (since >>> the function was being registered as only having 1 param)... >>> This way it is still really 1 function.. but I check it the second >>> parameter is a 'boolean', and if not.. try the second form. >>> >>> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >>> >>> and add the function >>> >>> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >>> This prints the value and the unit. >>> >>> Here's a sample function and the output! This also shows the types >>> of the output. >>> >>> (function() { >>> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >>> print( (typeof x) + ": "+ x+"\n" ); >>> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >>> print( (typeof x) + ": "+ x +"\n" ); >>> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>> print( (typeof x) + ": "+ x +"\n"); >>> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>> false) >>> print( (typeof x) + ": "+ x +"\n"); >>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>> print( (typeof x) + ": "+ x +"\n"); >>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>> print( (typeof x) + ": "+ x +"\n"); >>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>> print( (typeof x) + ": "+ x +"\n"); >>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>> print( (typeof x) + ": "+ x +"\n"); >>> })(); >>> >>> number: 17.0582 >>> string: 17.0582 >>> number: 5.86227 >>> string: 3.64264 >>> string: 17.0582kWh/100km >>> string: 3.64264mi/kWh >>> number: 17.0582 >>> number: 5.86227 >>> >>> >>> >>> It still might be an idea to use 'null' as a return value if the >>> metrics is !IsDefined() but that would be changing the existing >>> behaviour slightly. >>> >>> //.ichael >>> >>> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> I've worked out what the decode flag is for and how it works, and >>>> I think how optional params work. >>>> I'm pretty sure I won't need the 'AsFloatUnit' function; the >>>> unit would be an option to AsFloat(); I'll know that soon. >>>> >>>> The 'Value' function is more complicated because of the optional >>>> decode bool. I guess I could add the Unit to the end of that. >>>> >>>> ValueUnit could be still useful then to provide a 'Value + Unit'. >>>> >>>> Question: Is there a reason we shouldn't be returning with >>>> duk_push_null if the metric !IsDefined() in both AsFloat() >>>> and Value(metric,true) cases? >>>> >>>> //.ichael >>>> >>>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>>> frog@bunyip.wheelycreek.net> wrote: >>>> >>>>> Right, so I've implemented some stuff that seems to work quite >>>>> well. >>>>> >>>>> >>>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>>> should be ready now after a couple of stupid mistakes slipped through. >>>>> This absolutely needs somebody to review it please! (There's a reason why >>>>> I've converted some if()'s to switch() - which is that it will be used in >>>>> the follow-up commit). >>>>> >>>>> The commit that will follow on from that it implements the new >>>>> Units: kWh/100km, km/kWh and mi/kWh. >>>>> >>>>> This is a summary of what I've implemented for scripting - >>>>> including showing the unit codes I have so far. I've considered a few >>>>> things: >>>>> * Should some of the longer unit codes be shortened (eg mi, >>>>> mins, m, ft, deg, perc) >>>>> * The unit codes could be much more regular and separated by >>>>> dots eg: >>>>> watthours -> w.h >>>>> kwhp100km -> kw.h_100km or kw.h/100km >>>>> miph -> mi_h or mi/h (or should it be mph). >>>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>>> weird, but predictable) >>>>> >>>>> *OVMS# metric units* >>>>> km : km >>>>> miles : M >>>>> meters : m >>>>> feet : ft >>>>> celcius : °C >>>>> fahrenheit : °F >>>>> kpa : kPa >>>>> pa : Pa >>>>> psi : psi >>>>> volts : V >>>>> amps : A >>>>> amphours : Ah >>>>> kw : kW >>>>> kwh : kWh >>>>> watts : W >>>>> watthours : Wh >>>>> seconds : Sec >>>>> minutes : Min >>>>> hours : Hour >>>>> utc : UTC >>>>> degrees : ° >>>>> kmph : km/h >>>>> miph : Mph >>>>> kmphps : km/h/s >>>>> miphps : Mph/s >>>>> mpss : m/s² >>>>> dbm : dBm >>>>> sq : sq >>>>> percent : % >>>>> whpkm : Wh/km >>>>> whpmi : Wh/mi >>>>> kwhp100km : kWh/100km >>>>> kmpkwh : km/kWh >>>>> mipkwh : mi/kWh >>>>> nm : Nm >>>>> >>>>> *OVMS# metric unit mi* >>>>> miles : M >>>>> >>>>> >>>>
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?). mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); } The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself. int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; } Thoughts? //.ichael On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion.
We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*).
The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
> I think this is pretty decent & complete now. > > I also like the approach of the 'user' unit code. Moving all user > unit prefs into the module configuration is an old todo. Currently only the > distance unit is defined at the module side, temperature and pressure are > App prefs. > > Regards, > Michael > > > Am 11.11.22 um 09:54 schrieb Michael Geddes: > > Ok - so here's what I have implemented for Duktape and Metrics. (I > added IsDefined() as well). > Any thoughts on this? > > Noting > OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) > OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float > converted to given unit (new) > OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value > (same) > OvmsMetrics.Value( {metric} , false) -> Outputs Metric as > string and no units (same) > OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric > converted to given unit as native value. (new) > OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric > converted to given unit as string including any unit specifier. (new) > also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) > Adds similar behaviour to Value() above. > also the special units '*imperial*' and '*metric*' will convert to > the associated imperial / metric version of the units as appropriate. > > (function() { > dump = function (metric) { print( metric+ " ["+(typeof > metric)+"]\n" ); } > dump_obj = function (obj ) { > print('--- Object ----\n') > for (var k in obj) { > xk = obj[k]; > print( k+':'+ xk + ' ['+typeof xk+ "]\n"); > } > } > dump(OvmsMetrics.Value("xiq.v.trip.consumption")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); > dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", > false)); > dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); > dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) > dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", > false)) > dump_obj(OvmsMetrics.GetValues("trip", "metric")) > dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) > })(); > > With this output: > > 19.2308 [number] > 19.2308 [string] > 5.2 [number] > 3.23112mi/kWh [string] > 19.2308 [number] > 5.2 [number] > 309.49 [number] > 309.49Wh/mi [string] > --- Object ---- > v.p.trip:13 [number] > xiq.e.trip:0 [number] > xiq.e.trip.energy.recuperated:0 [number] > xiq.e.trip.energy.used:0 [number] > xiq.v.trip.consumption:19.2308 [number] > --- Object ---- > v.p.trip:8.07781M [string] > xiq.e.trip:0M [string] > xiq.e.trip.energy.recuperated:0kWh [string] > xiq.e.trip.energy.used:0kWh [string] > xiq.v.trip.consumption:309.49Wh/mi [string] > > > On Wed, 9 Nov 2022 at 05:47, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> Yeah - I like HasValue. I implemented IsDefined() but I will >> rename it.. that's a much clearer name. >> >> Another thought. How about if we did this (but also with >> GetValues() as well - see the special values below) >> >> OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 >> (Number) >> OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 >> (String) >> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> >> 3.64264 (Number) >> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> >> 3.64264Mi/kWh (String) >> OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> >> 17.0582km/kWh (String) >> >> and >> OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> >> 3.64264Mi/kWh (String) >> >> I have already implemented the special values 'native' (existing), >> 'imperial' and 'metric'. >> >> I was also thinking that in the future you could have 'user'. Where >> for each group of values: >> 'temperature', 'distance', 'shortdistance', 'power' etc.. you could >> have a user preference. I probably won't implement it now,.but it could be >> cool that any UI could just ask for the user defined units (rather than >> having a separate choice). >> >> >> >> //.ichael >> >> On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson < >> mark@webb-johnson.net> wrote: >> >>> Or perhaps something more specific? >>> >>> HasValue() >>> >>> Mark >>> >>> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> >>> wrote: >>> >>> Signed PGP part >>> That's basically a good approach, but be aware 'IsDefined()' has >>> an ambiguous meaning here, as with the API stem "OvmsMetrics" it would >>> naturally be expected to mean "is this metric defined", not "does this >>> metric have a defined value". >>> >>> An undefined metric currently can be derived from 'Values()' >>> returning undefined, but that's more an undocumented side effect than >>> intended. >>> >>> Maybe 'GetDefined()' could be a better name, leveraging this >>> behaviour, i.e. returning 'undefined' for an actually undefined metric, and >>> 'null' for a defined metric without a value. >>> >>> Regards, >>> Michael >>> >>> >>> Am 08.11.22 um 13:46 schrieb Michael Geddes: >>> >>> Ah yes. Arrays - will check those. Yeah, how about we add a >>> 'IsDefined' method to metrics instead of the null thing (it does sound like >>> it will upset too many applecarts). >>> >>> //. >>> >>> On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> >>> wrote: >>> >>>> Michael, >>>> >>>> looks all good to me, once again nice find with the decode >>>> argument. Adding decode to the Value() call was only for symmetry IIRC, the >>>> main use was with GetValues() ( >>>> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >>>> ). >>>> >>>> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >>>> >>>> Returning null for an undefined metric seems like a natural >>>> choice, but is a rather deep change, as for consistency not only the >>>> Duktape metrics API but also the Web UI metrics API would need to be >>>> changed accordingly. Unless you've got a real use case that needs that, we >>>> should be careful. >>>> >>>> Regards, >>>> Michael >>>> >>>> >>>> Am 07.11.22 um 15:00 schrieb Michael Geddes: >>>> >>>> I have figured out a bunch of stuff and have implemented the >>>> following: (having done away with needing AsFloatUnit) >>>> >>>> OvmsMetrics.Value( {metric} [, {decode}]) >>>> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >>>> >>>> It turns out that the [decode] flag wasn't working anyway (since >>>> the function was being registered as only having 1 param)... >>>> This way it is still really 1 function.. but I check it the >>>> second parameter is a 'boolean', and if not.. try the second form. >>>> >>>> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >>>> >>>> and add the function >>>> >>>> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >>>> This prints the value and the unit. >>>> >>>> Here's a sample function and the output! This also shows the >>>> types of the output. >>>> >>>> (function() { >>>> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >>>> print( (typeof x) + ": "+ x+"\n" ); >>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >>>> print( (typeof x) + ": "+ x +"\n" ); >>>> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>>> print( (typeof x) + ": "+ x +"\n"); >>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>>> false) >>>> print( (typeof x) + ": "+ x +"\n"); >>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>>> print( (typeof x) + ": "+ x +"\n"); >>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>>> print( (typeof x) + ": "+ x +"\n"); >>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>>> print( (typeof x) + ": "+ x +"\n"); >>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>>> print( (typeof x) + ": "+ x +"\n"); >>>> })(); >>>> >>>> number: 17.0582 >>>> string: 17.0582 >>>> number: 5.86227 >>>> string: 3.64264 >>>> string: 17.0582kWh/100km >>>> string: 3.64264mi/kWh >>>> number: 17.0582 >>>> number: 5.86227 >>>> >>>> >>>> >>>> It still might be an idea to use 'null' as a return value if the >>>> metrics is !IsDefined() but that would be changing the existing >>>> behaviour slightly. >>>> >>>> //.ichael >>>> >>>> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >>>> frog@bunyip.wheelycreek.net> wrote: >>>> >>>>> I've worked out what the decode flag is for and how it works, >>>>> and I think how optional params work. >>>>> I'm pretty sure I won't need the 'AsFloatUnit' function; the >>>>> unit would be an option to AsFloat(); I'll know that soon. >>>>> >>>>> The 'Value' function is more complicated because of the optional >>>>> decode bool. I guess I could add the Unit to the end of that. >>>>> >>>>> ValueUnit could be still useful then to provide a 'Value + Unit'. >>>>> >>>>> Question: Is there a reason we shouldn't be returning with >>>>> duk_push_null if the metric !IsDefined() in both AsFloat() >>>>> and Value(metric,true) cases? >>>>> >>>>> //.ichael >>>>> >>>>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>>>> frog@bunyip.wheelycreek.net> wrote: >>>>> >>>>>> Right, so I've implemented some stuff that seems to work quite >>>>>> well. >>>>>> >>>>>> >>>>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>>>> should be ready now after a couple of stupid mistakes slipped through. >>>>>> This absolutely needs somebody to review it please! (There's a reason why >>>>>> I've converted some if()'s to switch() - which is that it will be used in >>>>>> the follow-up commit). >>>>>> >>>>>> The commit that will follow on from that it implements the new >>>>>> Units: kWh/100km, km/kWh and mi/kWh. >>>>>> >>>>>> This is a summary of what I've implemented for scripting - >>>>>> including showing the unit codes I have so far. I've considered a few >>>>>> things: >>>>>> * Should some of the longer unit codes be shortened (eg mi, >>>>>> mins, m, ft, deg, perc) >>>>>> * The unit codes could be much more regular and separated by >>>>>> dots eg: >>>>>> watthours -> w.h >>>>>> kwhp100km -> kw.h_100km or kw.h/100km >>>>>> miph -> mi_h or mi/h (or should it be mph). >>>>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>>>> weird, but predictable) >>>>>> >>>>>> *OVMS# metric units* >>>>>> km : km >>>>>> miles : M >>>>>> meters : m >>>>>> feet : ft >>>>>> celcius : °C >>>>>> fahrenheit : °F >>>>>> kpa : kPa >>>>>> pa : Pa >>>>>> psi : psi >>>>>> volts : V >>>>>> amps : A >>>>>> amphours : Ah >>>>>> kw : kW >>>>>> kwh : kWh >>>>>> watts : W >>>>>> watthours : Wh >>>>>> seconds : Sec >>>>>> minutes : Min >>>>>> hours : Hour >>>>>> utc : UTC >>>>>> degrees : ° >>>>>> kmph : km/h >>>>>> miph : Mph >>>>>> kmphps : km/h/s >>>>>> miphps : Mph/s >>>>>> mpss : m/s² >>>>>> dbm : dBm >>>>>> sq : sq >>>>>> percent : % >>>>>> whpkm : Wh/km >>>>>> whpmi : Wh/mi >>>>>> kwhp100km : kWh/100km >>>>>> kmpkwh : km/kWh >>>>>> mipkwh : mi/kWh >>>>>> nm : Nm >>>>>> >>>>>> *OVMS# metric unit mi* >>>>>> miles : M >>>>>> >>>>>> >>>>>
Michael, the unit conversion JS code scheme is fine. Btw, you can optimize convert to… convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native)(value); }
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units). Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI. In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach. Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received. Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme. That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before. The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components. This in combination with the 'msg:units' event to signal reconfiguration should provide all we need. We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support. Regards, Michael Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion. We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload /metrics['prop']/ and /"prop" in metrics operations/).
The /get /function would return the underlying value if it exists in the /metrics_ /array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the /metrics_/ array - it would then do a subscribe+query on the websocket getting the current value and adding it into the /metrics_/ container. If it was unavailable then it would put /undefined/ into the array. The 'has' would do the get() and return true if the value was not == /undefined/.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
image.png
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> wrote:
Signed PGP part That's basically a good approach, but be aware 'IsDefined()' has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would naturally be expected to mean "is this metric defined", not "does this metric have a defined value".
An undefined metric currently can be derived from 'Values()' returning undefined, but that's more an undocumented side effect than intended.
Maybe 'GetDefined()' could be a better name, leveraging this behaviour, i.e. returning 'undefined' for an actually undefined metric, and 'null' for a defined metric without a value.
Regards, Michael
Am 08.11.22 um 13:46 schrieb Michael Geddes: > Ah yes. Arrays - will check > those. Yeah, how about we > add a 'IsDefined' method to > metrics instead of the null > thing (it does sound like it > will upset too many applecarts). > > //. > > On Tue, 8 Nov 2022 at 20:35, > Michael Balzer > <dexter@expeedo.de> wrote: > > Michael, > > looks all good to me, > once again nice find > with the decode > argument. Adding decode > to the Value() call was > only for symmetry IIRC, > the main use was with > GetValues() > (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics). > > Don't forget to test > arrays, e.g. > "v.t.pressure" & "v.t.temp". > > Returning null for an > undefined metric seems > like a natural choice, > but is a rather deep > change, as for > consistency not only the > Duktape metrics API but > also the Web UI metrics > API would need to be > changed accordingly. > Unless you've got a real > use case that needs > that, we should be careful. > > Regards, > Michael > > > Am 07.11.22 um 15:00 > schrieb Michael Geddes: >> I have figured out a >> bunch of stuff and have >> implemented the >> following: (having done >> away with needing >> AsFloatUnit) >> >> OvmsMetrics.Value( >> {metric} [, {decode}]) >> OvmsMetrics.Value( >> {metric}, {unit} >> [,{decode}]) >> >> It turns out that the >> [decode] flag wasn't >> working anyway (since >> the function was being >> registered as only >> having 1 param)... >> This way it is still >> really 1 function.. but >> I check it the second >> parameter is a >> 'boolean', and if not.. >> try the second form. >> >> OvmsMetrics.AsFloat( >> {metric} [,{unit}] ) >> >> and add the function >> >> Ovms.Metrics.ValueUnit( >> {metric} [,{unit}]) >> This prints the value >> and the unit. >> >> Here's a sample >> function and the >> output! This also shows >> the types of the output. >> >> (function() { >> x = >> OvmsMetrics.Value("xiq.v.trip.consumption"); >> print( (typeof x) + >> ": "+ x+"\n" ); >> x = >> OvmsMetrics.Value("xiq.v.trip.consumption", >> false); >> print( (typeof x) + >> ": "+ x +"\n" ); >> x = >> OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + >> ": "+ x +"\n"); >> x = >> OvmsMetrics.Value("xiq.v.trip.consumption", >> "mipkwh", false) >> print( (typeof x) + >> ": "+ x +"\n"); >> x = >> OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >> print( (typeof x) + >> ": "+ x +"\n"); >> x = >> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >> print( (typeof x) + >> ": "+ x +"\n"); >> x = >> OvmsMetrics.AsFloat("xiq.v.trip.consumption") >> print( (typeof x) + >> ": "+ x +"\n"); >> x = >> OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >> print( (typeof x) + >> ": "+ x +"\n"); >> })(); >> >> number: 17.0582 >> string: 17.0582 >> number: 5.86227 >> string: 3.64264 >> string: 17.0582kWh/100km >> string: 3.64264mi/kWh >> number: 17.0582 >> number: 5.86227 >> >> >> It still might be an >> idea to use 'null' as a >> return value if the >> metrics is!IsDefined() >> but that would be >> changing the existing >> behaviour slightly. >> >> //.ichael >> >> On Mon, 7 Nov 2022 at >> 08:12, Michael Geddes >> <frog@bunyip.wheelycreek.net> >> wrote: >> >> I've worked out >> what the decode >> flag is for and how >> it works, and I >> think how optional >> params work. >> I'm pretty sure I >> won't need the >> 'AsFloatUnit' >> function; the unit >> would be an option >> to AsFloat(); I'll >> know that soon. >> >> The 'Value' >> function is more >> complicated because >> of the optional >> decode bool. I >> guess I could add >> the Unit to the end >> of that. >> >> ValueUnit could be >> still useful then >> to provide a >> 'Value + Unit'. >> >> Question: Is there >> a reason we >> shouldn't be >> returning with >> duk_push_null if >> the metric >> !IsDefined() in >> both AsFloat() and >> Value(metric,true) >> cases? >> >> //.ichael >> >> On Sun, 6 Nov 2022 >> at 11:22, Michael >> Geddes >> <frog@bunyip.wheelycreek.net> >> wrote: >> >> Right, so I've >> implemented >> some stuff that >> seems to work >> quite well. >> >> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >> should be ready >> now after a >> couple of >> stupid mistakes >> slipped >> through. This >> absolutely >> needs somebody >> to review it >> please! >> (There's a >> reason why I've >> converted some >> if()'s to >> switch() - >> which is that >> it will be used >> in the >> follow-up commit). >> >> The commit that >> will follow on >> from that it >> implements the >> new Units: >> kWh/100km, >> km/kWh and mi/kWh. >> >> This is a >> summary of what >> I've >> implemented for >> scripting - >> including >> showing the >> unit codes I >> have so far. >> I've considered >> a few things: >> * Should some >> of the longer >> unit codes be >> shortened (eg >> mi, mins, m, >> ft, deg, perc) >> * The unit >> codes could be >> much more >> regular and >> separated by >> dots eg: >> watthours -> w.h >> kwhp100km -> >> kw.h_100km or >> kw.h/100km >> miph -> >> mi_h or mi/h >> (or should it >> be mph). >> psi -> >> p_in.in >> <http://p_in.in/> >> or p/in.in >> <http://in.in/> >> or lb_in.in >> <http://lb_in.in/> >> (yes, slightly >> weird, but >> predictable) >> >> *OVMS# metric >> units* >> km : km >> miles : M >> meters : m >> feet : ft >> celcius : °C >> fahrenheit : °F >> kpa : kPa >> pa : Pa >> psi : psi >> volts : V >> amps : A >> amphours: Ah >> kw : kW >> kwh : kWh >> watts : W >> watthours: Wh >> seconds : Sec >> minutes : Min >> hours : Hour >> utc : UTC >> degrees : ° >> kmph : km/h >> miph: Mph >> kmphps: km/h/s >> miphps: Mph/s >> mpss : m/s² >> dbm : dBm >> sq : sq >> percent : % >> whpkm : Wh/km >> whpmi: Wh/mi >> kwhp100km : >> kWh/100km >> kmpkwh: km/kWh >> mipkwh: mi/kWh >> nm : Nm >> >> *OVMS# metric >> unit mi* >> miles : M >>
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
I've implemented a way of having user units of metrics come through (I have metrics_user[] metrics_label[] and metrics_all[] implemented).. *however as far as dynamic subscription to metrics goes, I now believe I can't do what I want in a backwards compatible way.* This is where I'm at: I haven't used Promises before or async/wait etc, so in my naivity, I had (mistakenly it seems) thought I could define a function taking a *metric code* that * Sends a command to the websocket eg *metric fetchsub 1234 v.p.trip* * Waits for the result coming in over the web-socket or times out * Returns the result to the user *directly* so *metric['* *v.p.trip**'] * (or whatever) could have an accessor function that (for unsubscribed metrics) silently fetched and subscribed to the metric over the websocket and returned the value. I have actually implemented the fetching bit mostly .. (a Promise resolve call-back is put in a collection against a random id and when the event comes back it uses the id to grab the promise function and resolve with the result - and yes it has a timeout) ... except that in the end, the javascript still requires a Promise to be created that needs to execute its result as a call-back. I also believe I now understand why that is not possible, and that fundamentally the only way of doing async stuff at all ends up in some kind of call back (via a Promise directly or an async function Promise)... and that the only exceptions to this are within an async function (which in the end still returns a Promise). This makes blocking on a metric['v.p.trip'] proxy get; function not possible afaict. Unless somebody knows a mechansim that I could use? (outside of an async function, of course) I have implement the subscription model for metrics[] (which supports, for example, v.p.* ) .. but without the auto-subscription it is going to be less useful for plugins as far as backwards compatibility goes! you could have a call like this: applyMetric('v.p.trip', (value) => { ... put the value somewhere } ) //.ichael On Sat, 3 Dec 2022 at 02:29, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
the unit conversion JS code scheme is fine. Btw, you can optimize convert to…
convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native )(value); }
Yeah - I'm assuming that the gain from not looking up unnecessarily is lost from all the checking. Got it.
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI.
In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach.
Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received.
Yeah - I've got some classes so that different cars can specify all the ranges in native units and it will generate code for the current user units. I've already put this into use for all vehicle classes that return a custom guage configuration. At the moment it is still static code and we'll need to sink on a (not yet implmeneted) untits changed event to reload it.. but it would be simple enough to change the code generated to be dynamic (especially that we now have that conversion function). Even just having the user refresh the page is better than nothing at the moment (it's not like the user is going to be changing the units all the time).
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme.
Yep - already decided to do that. It makes more sense. At the moment I'm also implementing a way of starting off with no metrics supplied and then adding in which ones we need. Probably the JS is the bit I need to work out on that.
That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before.
The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components.
This in combination with the 'msg:units' event to signal reconfiguration should provide all we need.
We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support.
Regards, Michael
Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion.
We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*).
The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
[image: image.png]
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
> > https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771 > > I'm hoping this P/R is ok in this form (made of 5 separate commits). > > I will have a look at implementing the "user" unit code. The base > for how it would work is already a part of the above pull request. I'll > just look at the module configuration for distance. > > The 'power consumption' is one where it's not just a check-box.. > there're 5 possible choice! > > I should also add 'bar' for pressure given that for some reason > that's still a thing people want. > > //.ichael > > On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> > wrote: > >> I think this is pretty decent & complete now. >> >> I also like the approach of the 'user' unit code. Moving all user >> unit prefs into the module configuration is an old todo. Currently only the >> distance unit is defined at the module side, temperature and pressure are >> App prefs. >> >> Regards, >> Michael >> >> >> Am 11.11.22 um 09:54 schrieb Michael Geddes: >> >> Ok - so here's what I have implemented for Duktape and Metrics. (I >> added IsDefined() as well). >> Any thoughts on this? >> >> Noting >> OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) >> OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float >> converted to given unit (new) >> OvmsMetrics.Value( {metric} ) -> Outputs Metric in native >> value (same) >> OvmsMetrics.Value( {metric} , false) -> Outputs Metric as >> string and no units (same) >> OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric >> converted to given unit as native value. (new) >> OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric >> converted to given unit as string including any unit specifier. (new) >> also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) >> Adds similar behaviour to Value() above. >> also the special units '*imperial*' and '*metric*' will convert to >> the associated imperial / metric version of the units as appropriate. >> >> (function() { >> dump = function (metric) { print( metric+ " ["+(typeof >> metric)+"]\n" ); } >> dump_obj = function (obj ) { >> print('--- Object ----\n') >> for (var k in obj) { >> xk = obj[k]; >> print( k+':'+ xk + ' ['+typeof xk+ "]\n"); >> } >> } >> dump(OvmsMetrics.Value("xiq.v.trip.consumption")); >> dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); >> dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); >> dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >> false)); >> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); >> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); >> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) >> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", >> false)) >> dump_obj(OvmsMetrics.GetValues("trip", "metric")) >> dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) >> })(); >> >> With this output: >> >> 19.2308 [number] >> 19.2308 [string] >> 5.2 [number] >> 3.23112mi/kWh [string] >> 19.2308 [number] >> 5.2 [number] >> 309.49 [number] >> 309.49Wh/mi [string] >> --- Object ---- >> v.p.trip:13 [number] >> xiq.e.trip:0 [number] >> xiq.e.trip.energy.recuperated:0 [number] >> xiq.e.trip.energy.used:0 [number] >> xiq.v.trip.consumption:19.2308 [number] >> --- Object ---- >> v.p.trip:8.07781M [string] >> xiq.e.trip:0M [string] >> xiq.e.trip.energy.recuperated:0kWh [string] >> xiq.e.trip.energy.used:0kWh [string] >> xiq.v.trip.consumption:309.49Wh/mi [string] >> >> >> On Wed, 9 Nov 2022 at 05:47, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> Yeah - I like HasValue. I implemented IsDefined() but I will >>> rename it.. that's a much clearer name. >>> >>> Another thought. How about if we did this (but also with >>> GetValues() as well - see the special values below) >>> >>> OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 >>> (Number) >>> OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 >>> (String) >>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> >>> 3.64264 (Number) >>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> >>> 3.64264Mi/kWh (String) >>> OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> >>> 17.0582km/kWh (String) >>> >>> and >>> OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> >>> 3.64264Mi/kWh (String) >>> >>> I have already implemented the special values 'native' (existing), >>> 'imperial' and 'metric'. >>> >>> I was also thinking that in the future you could have 'user'. >>> Where for each group of values: >>> 'temperature', 'distance', 'shortdistance', 'power' etc.. you >>> could have a user preference. I probably won't implement it now,.but it >>> could be cool that any UI could just ask for the user defined units (rather >>> than having a separate choice). >>> >>> >>> >>> //.ichael >>> >>> On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson < >>> mark@webb-johnson.net> wrote: >>> >>>> Or perhaps something more specific? >>>> >>>> HasValue() >>>> >>>> Mark >>>> >>>> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> >>>> wrote: >>>> >>>> Signed PGP part >>>> That's basically a good approach, but be aware 'IsDefined()' has >>>> an ambiguous meaning here, as with the API stem "OvmsMetrics" it would >>>> naturally be expected to mean "is this metric defined", not "does this >>>> metric have a defined value". >>>> >>>> An undefined metric currently can be derived from 'Values()' >>>> returning undefined, but that's more an undocumented side effect than >>>> intended. >>>> >>>> Maybe 'GetDefined()' could be a better name, leveraging this >>>> behaviour, i.e. returning 'undefined' for an actually undefined metric, and >>>> 'null' for a defined metric without a value. >>>> >>>> Regards, >>>> Michael >>>> >>>> >>>> Am 08.11.22 um 13:46 schrieb Michael Geddes: >>>> >>>> Ah yes. Arrays - will check those. Yeah, how about we add a >>>> 'IsDefined' method to metrics instead of the null thing (it does sound like >>>> it will upset too many applecarts). >>>> >>>> //. >>>> >>>> On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> >>>> wrote: >>>> >>>>> Michael, >>>>> >>>>> looks all good to me, once again nice find with the decode >>>>> argument. Adding decode to the Value() call was only for symmetry IIRC, the >>>>> main use was with GetValues() ( >>>>> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >>>>> ). >>>>> >>>>> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >>>>> >>>>> Returning null for an undefined metric seems like a natural >>>>> choice, but is a rather deep change, as for consistency not only the >>>>> Duktape metrics API but also the Web UI metrics API would need to be >>>>> changed accordingly. Unless you've got a real use case that needs that, we >>>>> should be careful. >>>>> >>>>> Regards, >>>>> Michael >>>>> >>>>> >>>>> Am 07.11.22 um 15:00 schrieb Michael Geddes: >>>>> >>>>> I have figured out a bunch of stuff and have implemented the >>>>> following: (having done away with needing AsFloatUnit) >>>>> >>>>> OvmsMetrics.Value( {metric} [, {decode}]) >>>>> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >>>>> >>>>> It turns out that the [decode] flag wasn't working anyway (since >>>>> the function was being registered as only having 1 param)... >>>>> This way it is still really 1 function.. but I check it the >>>>> second parameter is a 'boolean', and if not.. try the second form. >>>>> >>>>> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >>>>> >>>>> and add the function >>>>> >>>>> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >>>>> This prints the value and the unit. >>>>> >>>>> Here's a sample function and the output! This also shows the >>>>> types of the output. >>>>> >>>>> (function() { >>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >>>>> print( (typeof x) + ": "+ x+"\n" ); >>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >>>>> print( (typeof x) + ": "+ x +"\n" ); >>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>>>> false) >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>>>> print( (typeof x) + ": "+ x +"\n"); >>>>> })(); >>>>> >>>>> number: 17.0582 >>>>> string: 17.0582 >>>>> number: 5.86227 >>>>> string: 3.64264 >>>>> string: 17.0582kWh/100km >>>>> string: 3.64264mi/kWh >>>>> number: 17.0582 >>>>> number: 5.86227 >>>>> >>>>> >>>>> >>>>> It still might be an idea to use 'null' as a return value if the >>>>> metrics is !IsDefined() but that would be changing the existing >>>>> behaviour slightly. >>>>> >>>>> //.ichael >>>>> >>>>> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >>>>> frog@bunyip.wheelycreek.net> wrote: >>>>> >>>>>> I've worked out what the decode flag is for and how it works, >>>>>> and I think how optional params work. >>>>>> I'm pretty sure I won't need the 'AsFloatUnit' function; the >>>>>> unit would be an option to AsFloat(); I'll know that soon. >>>>>> >>>>>> The 'Value' function is more complicated because of the >>>>>> optional decode bool. I guess I could add the Unit to the end of that. >>>>>> >>>>>> ValueUnit could be still useful then to provide a 'Value + >>>>>> Unit'. >>>>>> >>>>>> Question: Is there a reason we shouldn't be returning with >>>>>> duk_push_null if the metric !IsDefined() in both AsFloat() >>>>>> and Value(metric,true) cases? >>>>>> >>>>>> //.ichael >>>>>> >>>>>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>>>>> frog@bunyip.wheelycreek.net> wrote: >>>>>> >>>>>>> Right, so I've implemented some stuff that seems to work quite >>>>>>> well. >>>>>>> >>>>>>> >>>>>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>>>>> should be ready now after a couple of stupid mistakes slipped through. >>>>>>> This absolutely needs somebody to review it please! (There's a reason why >>>>>>> I've converted some if()'s to switch() - which is that it will be used in >>>>>>> the follow-up commit). >>>>>>> >>>>>>> The commit that will follow on from that it implements the new >>>>>>> Units: kWh/100km, km/kWh and mi/kWh. >>>>>>> >>>>>>> This is a summary of what I've implemented for scripting - >>>>>>> including showing the unit codes I have so far. I've considered a few >>>>>>> things: >>>>>>> * Should some of the longer unit codes be shortened (eg mi, >>>>>>> mins, m, ft, deg, perc) >>>>>>> * The unit codes could be much more regular and separated >>>>>>> by dots eg: >>>>>>> watthours -> w.h >>>>>>> kwhp100km -> kw.h_100km or kw.h/100km >>>>>>> miph -> mi_h or mi/h (or should it be mph). >>>>>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>>>>> weird, but predictable) >>>>>>> >>>>>>> *OVMS# metric units* >>>>>>> km : km >>>>>>> miles : M >>>>>>> meters : m >>>>>>> feet : ft >>>>>>> celcius : °C >>>>>>> fahrenheit : °F >>>>>>> kpa : kPa >>>>>>> pa : Pa >>>>>>> psi : psi >>>>>>> volts : V >>>>>>> amps : A >>>>>>> amphours : Ah >>>>>>> kw : kW >>>>>>> kwh : kWh >>>>>>> watts : W >>>>>>> watthours : Wh >>>>>>> seconds : Sec >>>>>>> minutes : Min >>>>>>> hours : Hour >>>>>>> utc : UTC >>>>>>> degrees : ° >>>>>>> kmph : km/h >>>>>>> miph : Mph >>>>>>> kmphps : km/h/s >>>>>>> miphps : Mph/s >>>>>>> mpss : m/s² >>>>>>> dbm : dBm >>>>>>> sq : sq >>>>>>> percent : % >>>>>>> whpkm : Wh/km >>>>>>> whpmi : Wh/mi >>>>>>> kwhp100km : kWh/100km >>>>>>> kmpkwh : km/kWh >>>>>>> mipkwh : mi/kWh >>>>>>> nm : Nm >>>>>>> >>>>>>> *OVMS# metric unit mi* >>>>>>> miles : M >>>>>>> >>>>>>> >>>>>>
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Michael, yes, while async getters seem to be possible by some JS voodoo, they probably won't work without changes to the applications. I also don't think they would form an application code pattern we should encourage, as they would try to hide asynchronous operations and would lead to each individual metric needing to be requested (at least initially) by a separate async call. Instead, please reconsider:
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
I designed this subscription scheme to follow the MQTT topic subscription scheme and be usable for any kind of subscription. Different data sources can be identified by the root topic. For notifications, it's `notify/`, for metrics, we can naturally assign `metrics/`. A subscription pattern `metrics/v/p/#` would for example subscribe to all `v.p.*` metrics. Auto subscribing & unsubscribing is managed by the framework via the `data-subscriptions` attribute of a `.receiver`. Examples: * https://docs.openvehicles.com/en/latest/components/ovms_webserver/docs/notif... * https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/tree/master... Btw, if (!) we keep the units dictionary subscription necessity, that could be a subscription to `units/#`. Regards, Michael Am 08.12.22 um 04:29 schrieb Michael Geddes:
I've implemented a way of having user units of metrics come through (I have metrics_user[] metrics_label[] and metrics_all[] implemented).. *however as far as dynamic subscription to metrics goes, I now believe I can't do what I want in a backwards compatible way.*
This is where I'm at:
I haven't used Promises before or async/wait etc, so in my naivity, I had (mistakenly it seems) thought I could define a function taking a /metric code/ that * Sends a command to the websocket eg /metric fetchsub 1234 v.p.trip/ * Waits for the result coming in over the web-socket or times out * Returns the result to the user *directly* so /metric['/ /v.p.trip//'] / (or whatever) could have an accessor function that (for unsubscribed metrics) silently fetched and subscribed to the metric over the websocket and returned the value.
I have actually implemented the fetching bit mostly .. (a Promise resolve call-back is put in a collection against a random id and when the event comes back it uses the id to grab the promise function and resolve with the result - and yes it has a timeout) ... except that in the end, the javascript still requires a Promise to be created that needs to execute its result as a call-back. I also believe I now understand why that is not possible, and that fundamentally the only way of doing async stuff at all ends up in some kind of call back (via a Promise directly or an async function Promise)... and that the only exceptions to this are within an async function (which in the end still returns a Promise).
This makes blocking on a metric['v.p.trip'] proxy get; function not possible afaict. Unless somebody knows a mechansim that I could use? (outside of an async function, of course)
I have implement the subscription model for metrics[] (which supports, for example, v.p.* ) .. but without the auto-subscription it is going to be less useful for plugins as far as backwards compatibility goes!
you could have a call like this: applyMetric('v.p.trip', (value) => { ... put the value somewhere } )
//.ichael
On Sat, 3 Dec 2022 at 02:29, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
the unit conversion JS code scheme is fine. Btw, you can optimize convert to…
convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native)(value); }
Yeah - I'm assuming that the gain from not looking up unnecessarily is lost from all the checking. Got it.
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units). Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI.
In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach.
Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received.
Yeah - I've got some classes so that different cars can specify all the ranges in native units and it will generate code for the current user units. I've already put this into use for all vehicle classes that return a custom guage configuration.
At the moment it is still static code and we'll need to sink on a (not yet implmeneted) untits changed event to reload it.. but it would be simple enough to change the code generated to be dynamic (especially that we now have that conversion function). Even just having the user refresh the page is better than nothing at the moment (it's not like the user is going to be changing the units all the time).
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme.
Yep - already decided to do that. It makes more sense. At the moment I'm also implementing a way of starting off with no metrics supplied and then adding in which ones we need. Probably the JS is the bit I need to work out on that.
That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before.
The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components.
This in combination with the 'msg:units' event to signal reconfiguration should provide all we need.
We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support.
Regards, Michael
Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion. We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload /metrics['prop']/ and /"prop" in metrics operations/).
The /get /function would return the underlying value if it exists in the /metrics_ /array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the /metrics_/ array - it would then do a subscribe+query on the websocket getting the current value and adding it into the /metrics_/ container. If it was unavailable then it would put /undefined/ into the array. The 'has' would do the get() and return true if the value was not == /undefined/.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
image.png
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes:
Ok - so here's what I have implemented for Duktape and Metrics. (I added IsDefined() as well). Any thoughts on this?
Noting OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float converted to given unit (new) OvmsMetrics.Value( {metric} ) -> Outputs Metric in native value (same) OvmsMetrics.Value( {metric} , false) -> Outputs Metric as string and no units (same) OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric converted to given unit as native value. (new) OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs Metric converted to given unit as string including any unit specifier. (new) also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) Adds similar behaviour to Value() above. also the special units '*imperial*' and '*metric*' will convert to the associated imperial / metric version of the units as appropriate.
(function() { dump = function (metric) { print( metric+ " ["+(typeof metric)+"]\n" ); } dump_obj = function (obj ) { print('--- Object ----\n') for (var k in obj) { xk = obj[k]; print( k+':'+ xk + ' ['+typeof xk+ "]\n"); } } dump(OvmsMetrics.Value("xiq.v.trip.consumption")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false)); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", false)) dump_obj(OvmsMetrics.GetValues("trip", "metric")) dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) })();
With this output: 19.2308 [number] 19.2308 [string] 5.2 [number] 3.23112mi/kWh [string] 19.2308 [number] 5.2 [number] 309.49 [number] 309.49Wh/mi [string] --- Object ---- v.p.trip:13 [number] xiq.e.trip:0 [number] xiq.e.trip.energy.recuperated:0 [number] xiq.e.trip.energy.used:0 [number] xiq.v.trip.consumption:19.2308 [number] --- Object ---- v.p.trip:8.07781M [string] xiq.e.trip:0M [string] xiq.e.trip.energy.recuperated:0kWh [string] xiq.e.trip.energy.used:0kWh [string] xiq.v.trip.consumption:309.49Wh/mi [string]
On Wed, 9 Nov 2022 at 05:47, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah - I like HasValue. I implemented IsDefined() but I will rename it.. that's a much clearer name.
Another thought. How about if we did this (but also with GetValues() as well - see the special values below)
OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 (String) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> 3.64264 (Number) OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> 3.64264Mi/kWh (String) OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> 17.0582km/kWh (String)
and OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) -> 3.64264Mi/kWh (String)
I have already implemented the special values 'native' (existing), 'imperial' and 'metric'.
I was also thinking that in the future you could have 'user'. Where for each group of values: 'temperature', 'distance', 'shortdistance', 'power' etc.. you could have a user preference. I probably won't implement it now,.but it could be cool that any UI could just ask for the user defined units (rather than having a separate choice).
//.ichael
On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson <mark@webb-johnson.net> wrote:
Or perhaps something more specific?
HasValue()
Mark
> On 8 Nov 2022, at 9:01 > PM, Michael Balzer > <dexter@expeedo.de> wrote: > > Signed PGP part > That's basically a good > approach, but be aware > 'IsDefined()' has an > ambiguous meaning here, > as with the API stem > "OvmsMetrics" it would > naturally be expected to > mean "is this metric > defined", not "does this > metric have a defined > value". > > An undefined metric > currently can be derived > from 'Values()' > returning undefined, but > that's more an > undocumented side effect > than intended. > > Maybe 'GetDefined()' > could be a better name, > leveraging this > behaviour, i.e. > returning 'undefined' > for an actually > undefined metric, and > 'null' for a defined > metric without a value. > > Regards, > Michael > > > Am 08.11.22 um 13:46 > schrieb Michael Geddes: >> Ah yes. Arrays - will >> check those. Yeah, how >> about we add a >> 'IsDefined' method to >> metrics instead of the >> null thing (it does >> sound like it will >> upset too many applecarts). >> >> //. >> >> On Tue, 8 Nov 2022 at >> 20:35, Michael Balzer >> <dexter@expeedo.de> wrote: >> >> Michael, >> >> looks all good to >> me, once again nice >> find with the >> decode argument. >> Adding decode to >> the Value() call >> was only for >> symmetry IIRC, the >> main use was with >> GetValues() >> (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics). >> >> Don't forget to >> test arrays, e.g. >> "v.t.pressure" & >> "v.t.temp". >> >> Returning null for >> an undefined metric >> seems like a >> natural choice, but >> is a rather deep >> change, as for >> consistency not >> only the Duktape >> metrics API but >> also the Web UI >> metrics API would >> need to be changed >> accordingly. Unless >> you've got a real >> use case that needs >> that, we should be >> careful. >> >> Regards, >> Michael >> >> >> Am 07.11.22 um >> 15:00 schrieb >> Michael Geddes: >>> I have figured out >>> a bunch of stuff >>> and have >>> implemented the >>> following: (having >>> done away with >>> needing AsFloatUnit) >>> >>> OvmsMetrics.Value( >>> {metric} [, {decode}]) >>> OvmsMetrics.Value( >>> {metric}, {unit} >>> [,{decode}]) >>> >>> It turns out that >>> the [decode] flag >>> wasn't working >>> anyway (since the >>> function was being >>> registered as only >>> having 1 param)... >>> This way it is >>> still really 1 >>> function.. but I >>> check it the >>> second parameter >>> is a 'boolean', >>> and if not.. try >>> the second form. >>> >>> OvmsMetrics.AsFloat( >>> {metric} [,{unit}] ) >>> >>> and add the function >>> >>> Ovms.Metrics.ValueUnit( >>> {metric} [,{unit}]) >>> This prints the >>> value and the unit. >>> >>> Here's a sample >>> function and the >>> output! This also >>> shows the types of >>> the output. >>> >>> (function() { >>> x = >>> OvmsMetrics.Value("xiq.v.trip.consumption"); >>> print( (typeof >>> x) + ": "+ x+"\n" ); >>> x = >>> OvmsMetrics.Value("xiq.v.trip.consumption", >>> false); >>> print( (typeof >>> x) + ": "+ x +"\n" ); >>> x = >>> OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> x = >>> OvmsMetrics.Value("xiq.v.trip.consumption", >>> "mipkwh", false) >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> x = >>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> x = >>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> x = >>> OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> x = >>> OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>> print( (typeof >>> x) + ": "+ x +"\n"); >>> })(); >>> >>> number: 17.0582 >>> string: 17.0582 >>> number: 5.86227 >>> string: 3.64264 >>> string: 17.0582kWh/100km >>> string: 3.64264mi/kWh >>> number: 17.0582 >>> number: 5.86227 >>> >>> >>> It still might be >>> an idea to use >>> 'null' as a return >>> value if the >>> metrics >>> is!IsDefined() but >>> that would be >>> changing the >>> existing behaviour >>> slightly. >>> >>> //.ichael >>> >>> On Mon, 7 Nov 2022 >>> at 08:12, Michael >>> Geddes >>> <frog@bunyip.wheelycreek.net> >>> wrote: >>> >>> I've worked >>> out what the >>> decode flag is >>> for and how it >>> works, and I >>> think how >>> optional >>> params work. >>> I'm pretty >>> sure I won't >>> need the >>> 'AsFloatUnit' >>> function; the >>> unit would be >>> an option to >>> AsFloat(); >>> I'll know that >>> soon. >>> >>> The 'Value' >>> function is >>> more >>> complicated >>> because of the >>> optional >>> decode bool. I >>> guess I could >>> add the Unit >>> to the end of >>> that. >>> >>> ValueUnit >>> could be still >>> useful then to >>> provide a >>> 'Value + Unit'. >>> >>> Question: Is >>> there a reason >>> we shouldn't >>> be returning >>> with >>> duk_push_null >>> if the >>> metric >>> !IsDefined() >>> in both >>> AsFloat() and >>> Value(metric,true) >>> cases? >>> >>> //.ichael >>> >>> On Sun, 6 Nov >>> 2022 at 11:22, >>> Michael Geddes >>> <frog@bunyip.wheelycreek.net> >>> wrote: >>> >>> Right, so >>> I've >>> implemented >>> some stuff >>> that seems >>> to work >>> quite well. >>> >>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>> should be >>> ready now >>> after a >>> couple of >>> stupid >>> mistakes >>> slipped >>> through. >>> This >>> absolutely >>> needs >>> somebody >>> to review >>> it please! >>> (There's a >>> reason why >>> I've >>> converted >>> some >>> if()'s to >>> switch() - >>> which is >>> that it >>> will be >>> used in >>> the >>> follow-up >>> commit). >>> >>> The commit >>> that will >>> follow on >>> from that >>> it >>> implements >>> the new >>> Units: >>> kWh/100km, >>> km/kWh >>> and mi/kWh. >>> >>> This is a >>> summary of >>> what I've >>> implemented >>> for >>> scripting >>> - >>> including >>> showing >>> the unit >>> codes I >>> have >>> so far. >>> I've >>> considered >>> a few things: >>> * Should >>> some of >>> the longer >>> unit codes >>> be >>> shortened >>> (eg mi, >>> mins, m, >>> ft, deg, perc) >>> * The >>> unit codes >>> could be >>> much more >>> regular >>> and >>> separated >>> by dots eg: >>> watthours >>> -> w.h >>> kwhp100km >>> -> >>> kw.h_100km >>> or kw.h/100km >>> miph >>> -> mi_h >>> or mi/h >>> (or should >>> it be mph). >>> psi >>> -> p_in.in >>> <http://p_in.in/> >>> or p/in.in >>> <http://in.in/> >>> or >>> lb_in.in >>> <http://lb_in.in/> >>> (yes, >>> slightly >>> weird, but >>> predictable) >>> >>> *OVMS# >>> metric units* >>> km >>> : km >>> miles : M >>> meters : m >>> feet >>> : ft >>> celcius >>> : °C >>> fahrenheit >>> : °F >>> kpa >>> : kPa >>> pa >>> : Pa >>> psi >>> : psi >>> volts : V >>> amps : A >>> amphours: Ah >>> kw >>> : kW >>> kwh >>> : kWh >>> watts : W >>> watthours: Wh >>> seconds >>> : Sec >>> minutes >>> : Min >>> hours >>> : Hour >>> utc >>> : UTC >>> degrees : ° >>> kmph >>> : km/h >>> miph: Mph >>> kmphps: km/h/s >>> miphps: Mph/s >>> mpss >>> : m/s² >>> dbm >>> : dBm >>> sq >>> : sq >>> percent : % >>> whpkm >>> : Wh/km >>> whpmi: Wh/mi >>> kwhp100km >>> : kWh/100km >>> kmpkwh: km/kWh >>> mipkwh: mi/kWh >>> nm >>> : Nm >>> >>> *OVMS# >>> metric >>> unit mi* >>> >>> miles : M >>>
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Michael, I'll go back and revise the topic subscriptions stuff wrt metrics etc. with the stuff I have done. I have implemented metric subscriptions and also implemented async getters in the dashboard just to see what the burden is, and tbh it's not too bad! I had no real expectation that this would end up in the final code and it was more just to further my understanding. What I've done does require a 'get and subscribe' function added to the websocket. *unit getsub CallID metricName* Which subscribes to the metric, but also returns a result something like { result : { ID: *CallID*, Metric: *metricName*, Value: *metricValue*, Units : { native: *unitcode*, unit: *userunitcode*, label: *label*} }} So I can create a Promise and put the Accept function agains the (random) *CallID* on an object, (and a timeout that calls reject) -so when the result comes in I can call the accept. It still doesn't get over the need for plugins to either subscribe to the metrics they want to access through metrics[]... or use the ametrics[] collection in an async function. Having a tag (as you mentioned) that says which metrics to subscribe to would be at least backwards compatible. I guess we could make it so that if a plugin was searched for such a tag and none existed, we could subscribe all? //.ichael On Sun, 11 Dec 2022 at 19:54, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
yes, while async getters seem to be possible by some JS voodoo, they probably won't work without changes to the applications.
I also don't think they would form an application code pattern we should encourage, as they would try to hide asynchronous operations and would lead to each individual metric needing to be requested (at least initially) by a separate async call.
Instead, please reconsider:
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
I designed this subscription scheme to follow the MQTT topic subscription scheme and be usable for any kind of subscription. Different data sources can be identified by the root topic. For notifications, it's `notify/`, for metrics, we can naturally assign `metrics/`. A subscription pattern `metrics/v/p/#` would for example subscribe to all `v.p.*` metrics.
Auto subscribing & unsubscribing is managed by the framework via the `data-subscriptions` attribute of a `.receiver`.
Examples:
- https://docs.openvehicles.com/en/latest/components/ovms_webserver/docs/notif... - https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/tree/master...
Btw, if (!) we keep the units dictionary subscription necessity, that could be a subscription to `units/#`.
Regards, Michael
Am 08.12.22 um 04:29 schrieb Michael Geddes:
I've implemented a way of having user units of metrics come through (I have metrics_user[] metrics_label[] and metrics_all[] implemented).. *however as far as dynamic subscription to metrics goes, I now believe I can't do what I want in a backwards compatible way.*
This is where I'm at:
I haven't used Promises before or async/wait etc, so in my naivity, I had (mistakenly it seems) thought I could define a function taking a *metric code* that * Sends a command to the websocket eg *metric fetchsub 1234 v.p.trip* * Waits for the result coming in over the web-socket or times out * Returns the result to the user *directly* so *metric['* *v.p.trip**'] * (or whatever) could have an accessor function that (for unsubscribed metrics) silently fetched and subscribed to the metric over the websocket and returned the value.
I have actually implemented the fetching bit mostly .. (a Promise resolve call-back is put in a collection against a random id and when the event comes back it uses the id to grab the promise function and resolve with the result - and yes it has a timeout) ... except that in the end, the javascript still requires a Promise to be created that needs to execute its result as a call-back. I also believe I now understand why that is not possible, and that fundamentally the only way of doing async stuff at all ends up in some kind of call back (via a Promise directly or an async function Promise)... and that the only exceptions to this are within an async function (which in the end still returns a Promise).
This makes blocking on a metric['v.p.trip'] proxy get; function not possible afaict. Unless somebody knows a mechansim that I could use? (outside of an async function, of course)
I have implement the subscription model for metrics[] (which supports, for example, v.p.* ) .. but without the auto-subscription it is going to be less useful for plugins as far as backwards compatibility goes!
you could have a call like this: applyMetric('v.p.trip', (value) => { ... put the value somewhere } )
//.ichael
On Sat, 3 Dec 2022 at 02:29, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
the unit conversion JS code scheme is fine. Btw, you can optimize convert to…
convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native )(value); }
Yeah - I'm assuming that the gain from not looking up unnecessarily is lost from all the checking. Got it.
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI.
In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach.
Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received.
Yeah - I've got some classes so that different cars can specify all the ranges in native units and it will generate code for the current user units. I've already put this into use for all vehicle classes that return a custom guage configuration.
At the moment it is still static code and we'll need to sink on a (not yet implmeneted) untits changed event to reload it.. but it would be simple enough to change the code generated to be dynamic (especially that we now have that conversion function). Even just having the user refresh the page is better than nothing at the moment (it's not like the user is going to be changing the units all the time).
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme.
Yep - already decided to do that. It makes more sense. At the moment I'm also implementing a way of starting off with no metrics supplied and then adding in which ones we need. Probably the JS is the bit I need to work out on that.
That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before.
The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components.
This in combination with the 'msg:units' event to signal reconfiguration should provide all we need.
We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support.
Regards, Michael
Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion.
We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*).
The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28*
*v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
[image: image.png]
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
> Michael, > > looks good. > > I think having an explicit 'default' option is better than taking > the 'Metric' equivalent for that, as in your example you already show unit > alternatives within the metric system to support different scalings (kW / > W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-)) > > @Patrick, I think that also answers your implicit question: > > The default button makes it unclear what the actual setting is. > > > The default (native unit) is always metric, but you may have a mix > of scalings, as we try to find the one that fits best for the given > application when defining a metric. For example the current driving energy > consumption is stored natively in Wh/km, while the energy used or > regenerated is in kWh, and the odometer & trip counters are in km, while > the altitude ist in m. > > Regards, > Michael > > > Am 13.11.22 um 08:42 schrieb Michael Geddes: > > Greetings, > so this is my idea of being able to select which units various > groups use (in addition to Distance). > This can be then accessed by the special 'user' unit code. (or > 'metrics list -u ' ) > The idea of [Default] selection below simply means storing the > value to blank - meaning use whatever unit the particular metric uses. The > other idea I had was to actually default it to the equivalent of 'Metric' > special unit code and not have the [Default] button. > > > [image: image.png] > > Currently I've made it so that if there are more than 3 choices > other than [default] that it uses the choice/combo box rather than the > Radio buttons. (ie this list is auto-generated from the Metric Units table > and the Metric Groups table). > > Thoughts / comments? > > //.ichael > > On Sat, 12 Nov 2022 at 17:35, Michael Geddes < > frog@bunyip.wheelycreek.net> wrote: > >> >> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771 >> >> I'm hoping this P/R is ok in this form (made of 5 separate commits). >> >> I will have a look at implementing the "user" unit code. The base >> for how it would work is already a part of the above pull request. I'll >> just look at the module configuration for distance. >> >> The 'power consumption' is one where it's not just a check-box.. >> there're 5 possible choice! >> >> I should also add 'bar' for pressure given that for some reason >> that's still a thing people want. >> >> //.ichael >> >> On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> >> wrote: >> >>> I think this is pretty decent & complete now. >>> >>> I also like the approach of the 'user' unit code. Moving all user >>> unit prefs into the module configuration is an old todo. Currently only the >>> distance unit is defined at the module side, temperature and pressure are >>> App prefs. >>> >>> Regards, >>> Michael >>> >>> >>> Am 11.11.22 um 09:54 schrieb Michael Geddes: >>> >>> Ok - so here's what I have implemented for Duktape and Metrics. (I >>> added IsDefined() as well). >>> Any thoughts on this? >>> >>> Noting >>> OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) >>> OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as float >>> converted to given unit (new) >>> OvmsMetrics.Value( {metric} ) -> Outputs Metric in native >>> value (same) >>> OvmsMetrics.Value( {metric} , false) -> Outputs Metric as >>> string and no units (same) >>> OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric >>> converted to given unit as native value. (new) >>> OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs >>> Metric converted to given unit as string including any unit specifier. >>> (new) >>> also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] ) >>> Adds similar behaviour to Value() above. >>> also the special units '*imperial*' and '*metric*' will convert >>> to the associated imperial / metric version of the units as appropriate. >>> >>> (function() { >>> dump = function (metric) { print( metric+ " ["+(typeof >>> metric)+"]\n" ); } >>> dump_obj = function (obj ) { >>> print('--- Object ----\n') >>> for (var k in obj) { >>> xk = obj[k]; >>> print( k+':'+ xk + ' ['+typeof xk+ "]\n"); >>> } >>> } >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption")); >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>> false)); >>> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); >>> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) >>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", >>> false)) >>> dump_obj(OvmsMetrics.GetValues("trip", "metric")) >>> dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) >>> })(); >>> >>> With this output: >>> >>> 19.2308 [number] >>> 19.2308 [string] >>> 5.2 [number] >>> 3.23112mi/kWh [string] >>> 19.2308 [number] >>> 5.2 [number] >>> 309.49 [number] >>> 309.49Wh/mi [string] >>> --- Object ---- >>> v.p.trip:13 [number] >>> xiq.e.trip:0 [number] >>> xiq.e.trip.energy.recuperated:0 [number] >>> xiq.e.trip.energy.used:0 [number] >>> xiq.v.trip.consumption:19.2308 [number] >>> --- Object ---- >>> v.p.trip:8.07781M [string] >>> xiq.e.trip:0M [string] >>> xiq.e.trip.energy.recuperated:0kWh [string] >>> xiq.e.trip.energy.used:0kWh [string] >>> xiq.v.trip.consumption:309.49Wh/mi [string] >>> >>> >>> On Wed, 9 Nov 2022 at 05:47, Michael Geddes < >>> frog@bunyip.wheelycreek.net> wrote: >>> >>>> Yeah - I like HasValue. I implemented IsDefined() but I will >>>> rename it.. that's a much clearer name. >>>> >>>> Another thought. How about if we did this (but also with >>>> GetValues() as well - see the special values below) >>>> >>>> OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 >>>> (Number) >>>> OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 >>>> (String) >>>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> >>>> 3.64264 (Number) >>>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> >>>> 3.64264Mi/kWh (String) >>>> OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) -> >>>> 17.0582km/kWh (String) >>>> >>>> and >>>> OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) >>>> -> 3.64264Mi/kWh (String) >>>> >>>> I have already implemented the special values 'native' >>>> (existing), 'imperial' and 'metric'. >>>> >>>> I was also thinking that in the future you could have 'user'. >>>> Where for each group of values: >>>> 'temperature', 'distance', 'shortdistance', 'power' etc.. you >>>> could have a user preference. I probably won't implement it now,.but it >>>> could be cool that any UI could just ask for the user defined units (rather >>>> than having a separate choice). >>>> >>>> >>>> >>>> //.ichael >>>> >>>> On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson < >>>> mark@webb-johnson.net> wrote: >>>> >>>>> Or perhaps something more specific? >>>>> >>>>> HasValue() >>>>> >>>>> Mark >>>>> >>>>> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> >>>>> wrote: >>>>> >>>>> Signed PGP part >>>>> That's basically a good approach, but be aware 'IsDefined()' has >>>>> an ambiguous meaning here, as with the API stem "OvmsMetrics" it would >>>>> naturally be expected to mean "is this metric defined", not "does this >>>>> metric have a defined value". >>>>> >>>>> An undefined metric currently can be derived from 'Values()' >>>>> returning undefined, but that's more an undocumented side effect than >>>>> intended. >>>>> >>>>> Maybe 'GetDefined()' could be a better name, leveraging this >>>>> behaviour, i.e. returning 'undefined' for an actually undefined metric, and >>>>> 'null' for a defined metric without a value. >>>>> >>>>> Regards, >>>>> Michael >>>>> >>>>> >>>>> Am 08.11.22 um 13:46 schrieb Michael Geddes: >>>>> >>>>> Ah yes. Arrays - will check those. Yeah, how about we add a >>>>> 'IsDefined' method to metrics instead of the null thing (it does sound like >>>>> it will upset too many applecarts). >>>>> >>>>> //. >>>>> >>>>> On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> >>>>> wrote: >>>>> >>>>>> Michael, >>>>>> >>>>>> looks all good to me, once again nice find with the decode >>>>>> argument. Adding decode to the Value() call was only for symmetry IIRC, the >>>>>> main use was with GetValues() ( >>>>>> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >>>>>> ). >>>>>> >>>>>> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >>>>>> >>>>>> Returning null for an undefined metric seems like a natural >>>>>> choice, but is a rather deep change, as for consistency not only the >>>>>> Duktape metrics API but also the Web UI metrics API would need to be >>>>>> changed accordingly. Unless you've got a real use case that needs that, we >>>>>> should be careful. >>>>>> >>>>>> Regards, >>>>>> Michael >>>>>> >>>>>> >>>>>> Am 07.11.22 um 15:00 schrieb Michael Geddes: >>>>>> >>>>>> I have figured out a bunch of stuff and have implemented the >>>>>> following: (having done away with needing AsFloatUnit) >>>>>> >>>>>> OvmsMetrics.Value( {metric} [, {decode}]) >>>>>> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >>>>>> >>>>>> It turns out that the [decode] flag wasn't working anyway >>>>>> (since the function was being registered as only having 1 param)... >>>>>> This way it is still really 1 function.. but I check it the >>>>>> second parameter is a 'boolean', and if not.. try the second form. >>>>>> >>>>>> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >>>>>> >>>>>> and add the function >>>>>> >>>>>> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >>>>>> This prints the value and the unit. >>>>>> >>>>>> Here's a sample function and the output! This also shows the >>>>>> types of the output. >>>>>> >>>>>> (function() { >>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >>>>>> print( (typeof x) + ": "+ x+"\n" ); >>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >>>>>> print( (typeof x) + ": "+ x +"\n" ); >>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>>>>> false) >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>> })(); >>>>>> >>>>>> number: 17.0582 >>>>>> string: 17.0582 >>>>>> number: 5.86227 >>>>>> string: 3.64264 >>>>>> string: 17.0582kWh/100km >>>>>> string: 3.64264mi/kWh >>>>>> number: 17.0582 >>>>>> number: 5.86227 >>>>>> >>>>>> >>>>>> >>>>>> It still might be an idea to use 'null' as a return value if >>>>>> the metrics is !IsDefined() but that would be changing the >>>>>> existing behaviour slightly. >>>>>> >>>>>> //.ichael >>>>>> >>>>>> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >>>>>> frog@bunyip.wheelycreek.net> wrote: >>>>>> >>>>>>> I've worked out what the decode flag is for and how it works, >>>>>>> and I think how optional params work. >>>>>>> I'm pretty sure I won't need the 'AsFloatUnit' function; the >>>>>>> unit would be an option to AsFloat(); I'll know that soon. >>>>>>> >>>>>>> The 'Value' function is more complicated because of the >>>>>>> optional decode bool. I guess I could add the Unit to the end of that. >>>>>>> >>>>>>> ValueUnit could be still useful then to provide a 'Value + >>>>>>> Unit'. >>>>>>> >>>>>>> Question: Is there a reason we shouldn't be returning with >>>>>>> duk_push_null if the metric !IsDefined() in both >>>>>>> AsFloat() and Value(metric,true) cases? >>>>>>> >>>>>>> //.ichael >>>>>>> >>>>>>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>>>>>> frog@bunyip.wheelycreek.net> wrote: >>>>>>> >>>>>>>> Right, so I've implemented some stuff that seems to work >>>>>>>> quite well. >>>>>>>> >>>>>>>> >>>>>>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>>>>>> should be ready now after a couple of stupid mistakes slipped through. >>>>>>>> This absolutely needs somebody to review it please! (There's a reason why >>>>>>>> I've converted some if()'s to switch() - which is that it will be used in >>>>>>>> the follow-up commit). >>>>>>>> >>>>>>>> The commit that will follow on from that it implements the >>>>>>>> new Units: kWh/100km, km/kWh and mi/kWh. >>>>>>>> >>>>>>>> This is a summary of what I've implemented for scripting - >>>>>>>> including showing the unit codes I have so far. I've considered a few >>>>>>>> things: >>>>>>>> * Should some of the longer unit codes be shortened (eg >>>>>>>> mi, mins, m, ft, deg, perc) >>>>>>>> * The unit codes could be much more regular and separated >>>>>>>> by dots eg: >>>>>>>> watthours -> w.h >>>>>>>> kwhp100km -> kw.h_100km or kw.h/100km >>>>>>>> miph -> mi_h or mi/h (or should it be mph). >>>>>>>> psi -> p_in.in or p/in.in or lb_in.in (yes, slightly >>>>>>>> weird, but predictable) >>>>>>>> >>>>>>>> *OVMS# metric units* >>>>>>>> km : km >>>>>>>> miles : M >>>>>>>> meters : m >>>>>>>> feet : ft >>>>>>>> celcius : °C >>>>>>>> fahrenheit : °F >>>>>>>> kpa : kPa >>>>>>>> pa : Pa >>>>>>>> psi : psi >>>>>>>> volts : V >>>>>>>> amps : A >>>>>>>> amphours : Ah >>>>>>>> kw : kW >>>>>>>> kwh : kWh >>>>>>>> watts : W >>>>>>>> watthours : Wh >>>>>>>> seconds : Sec >>>>>>>> minutes : Min >>>>>>>> hours : Hour >>>>>>>> utc : UTC >>>>>>>> degrees : ° >>>>>>>> kmph : km/h >>>>>>>> miph : Mph >>>>>>>> kmphps : km/h/s >>>>>>>> miphps : Mph/s >>>>>>>> mpss : m/s² >>>>>>>> dbm : dBm >>>>>>>> sq : sq >>>>>>>> percent : % >>>>>>>> whpkm : Wh/km >>>>>>>> whpmi : Wh/mi >>>>>>>> kwhp100km : kWh/100km >>>>>>>> kmpkwh : km/kWh >>>>>>>> mipkwh : mi/kWh >>>>>>>> nm : Nm >>>>>>>> >>>>>>>> *OVMS# metric unit mi* >>>>>>>> miles : M >>>>>>>> >>>>>>>> >>>>>>>
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
Michael, Am 13.12.22 um 00:00 schrieb Michael Geddes:
I'll go back and revise the topic subscriptions stuff wrt metrics etc. with the stuff I have done.
I have implemented metric subscriptions and also implemented async getters in the dashboard just to see what the burden is, and tbh it's not too bad! I had no real expectation that this would end up in the final code and it was more just to further my understanding. What I've done does require a 'get and subscribe' function added to the websocket.
*unit getsub /CallID/ m/etricName/* Which subscribes to the metric, but also returns a result something like { result : { ID: /CallID/, Metric: /metricName/, Value: /metricValue/, Units : { native: /unitcode/, unit: /userunitcode/, label: /label/} }}
So I can create a Promise and put the Accept function agains the (random) /CallID/ on an object, (and a timeout that calls reject) -so when the result comes in I can call the accept.
If the asynchronous nature isn't hidden (i.e. caller needs to use async/await tags) and using the async getters is an alternative to the pattern subscription scheme, that would be OK for inclusion. It adds another way to subscribe, that may be better for some applications. Keep in mind a page/fragment's getter subscriptions then also need to be unsubscribed on unloading the page/fragment.
It still doesn't get over the need for plugins to either subscribe to the metrics they want to access through metrics[]... or use the ametrics[] collection in an async function. Having a tag (as you mentioned) that says which metrics to subscribe to would be at least backwards compatible. I guess we could make it so that if a plugin was searched for such a tag and none existed, we could subscribe all?
Yes, and we could add an API version attribute, to be able to identify receivers that expect to have direct access to all metrics. That way we still enable a new receiver to be created that really doesn't need metrics (or does all subscriptions through the async getters). Something like… <div class="receiver"> → auto subscription to "metrics/#" if no explicit "metrics/…" subscriptions present <div class="receiver" data-apiversion="2"> → only process explicit subscriptions Regards, Michael
//.ichael
On Sun, 11 Dec 2022 at 19:54, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
yes, while async getters seem to be possible by some JS voodoo, they probably won't work without changes to the applications.
I also don't think they would form an application code pattern we should encourage, as they would try to hide asynchronous operations and would lead to each individual metric needing to be requested (at least initially) by a separate async call.
Instead, please reconsider:
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
I designed this subscription scheme to follow the MQTT topic subscription scheme and be usable for any kind of subscription. Different data sources can be identified by the root topic. For notifications, it's `notify/`, for metrics, we can naturally assign `metrics/`. A subscription pattern `metrics/v/p/#` would for example subscribe to all `v.p.*` metrics.
Auto subscribing & unsubscribing is managed by the framework via the `data-subscriptions` attribute of a `.receiver`.
Examples:
* https://docs.openvehicles.com/en/latest/components/ovms_webserver/docs/notif... * https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/tree/master...
Btw, if (!) we keep the units dictionary subscription necessity, that could be a subscription to `units/#`.
Regards, Michael
Am 08.12.22 um 04:29 schrieb Michael Geddes:
I've implemented a way of having user units of metrics come through (I have metrics_user[] metrics_label[] and metrics_all[] implemented).. *however as far as dynamic subscription to metrics goes, I now believe I can't do what I want in a backwards compatible way.*
This is where I'm at:
I haven't used Promises before or async/wait etc, so in my naivity, I had (mistakenly it seems) thought I could define a function taking a /metric code/ that * Sends a command to the websocket eg /metric fetchsub 1234 v.p.trip/ * Waits for the result coming in over the web-socket or times out * Returns the result to the user *directly* so /metric['/ /v.p.trip//'] / (or whatever) could have an accessor function that (for unsubscribed metrics) silently fetched and subscribed to the metric over the websocket and returned the value.
I have actually implemented the fetching bit mostly .. (a Promise resolve call-back is put in a collection against a random id and when the event comes back it uses the id to grab the promise function and resolve with the result - and yes it has a timeout) ... except that in the end, the javascript still requires a Promise to be created that needs to execute its result as a call-back. I also believe I now understand why that is not possible, and that fundamentally the only way of doing async stuff at all ends up in some kind of call back (via a Promise directly or an async function Promise)... and that the only exceptions to this are within an async function (which in the end still returns a Promise).
This makes blocking on a metric['v.p.trip'] proxy get; function not possible afaict. Unless somebody knows a mechansim that I could use? (outside of an async function, of course)
I have implement the subscription model for metrics[] (which supports, for example, v.p.* ) .. but without the auto-subscription it is going to be less useful for plugins as far as backwards compatibility goes!
you could have a call like this: applyMetric('v.p.trip', (value) => { ... put the value somewhere } )
//.ichael
On Sat, 3 Dec 2022 at 02:29, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
the unit conversion JS code scheme is fine. Btw, you can optimize convert to…
convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native)(value); }
Yeah - I'm assuming that the gain from not looking up unnecessarily is lost from all the checking. Got it.
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units). Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI.
In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach.
Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received.
Yeah - I've got some classes so that different cars can specify all the ranges in native units and it will generate code for the current user units. I've already put this into use for all vehicle classes that return a custom guage configuration.
At the moment it is still static code and we'll need to sink on a (not yet implmeneted) untits changed event to reload it.. but it would be simple enough to change the code generated to be dynamic (especially that we now have that conversion function). Even just having the user refresh the page is better than nothing at the moment (it's not like the user is going to be changing the units all the time).
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme.
Yep - already decided to do that. It makes more sense. At the moment I'm also implementing a way of starting off with no metrics supplied and then adding in which ones we need. Probably the JS is the bit I need to work out on that.
That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before.
The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components.
This in combination with the 'msg:units' event to signal reconfiguration should provide all we need.
We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support.
Regards, Michael
Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion. We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload /metrics['prop']/ and /"prop" in metrics operations/).
The /get /function would return the underlying value if it exists in the /metrics_ /array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the /metrics_/ array - it would then do a subscribe+query on the websocket getting the current value and adding it into the /metrics_/ container. If it was unavailable then it would put /undefined/ into the array. The 'has' would do the get() and return true if the value was not == /undefined/.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
I don't have much spare time currently, just some quick first comments: it's important to implement this as lightweight as possible, both in terms of network load and client CPU & memory requirements. Some devices already have issues, which can be seen by the "websocket overflow" messages. The web UI also should stay usable via cellular.
My impression is the new scheme, while only slightly raising the client requirements, adds substantially to the network requirements.
An option could be to separate the units -- or more, back when implementing this I thought about separating the names later on. Another question is if we normally generally need both the native and the converted values in the web UI. We maybe could provide an option to switch to converted values, or add an option to retreive or subscribe to a set of converted metrics on demand.
Standard plugins like ABRP and PwrMon rely on getting metric (native) units, and there probably are non-public plugins, e.g. for engineering & scientific projects, that depend on metric units to do their calculations and don't need anything else. We shouldn't make life harder for these applications without good reason.
Regards, Michael
Am 15.11.22 um 01:26 schrieb Michael Geddes:
If you're ok with the [default] option I'll stick with that. I mean in some ways it would be nice to have a button choice metric | usa | europe | asia | custom etc and I kind of considered something like that but figured it's only a handful of choices.. and it's an embedded device.. so simpler is better.
On a related note - I was thinking how it would be nice if the dashboard (etc) had access to the 'user' units, so went hunting down that little rabbit hole. Quite a nice mechanism with the web socket updating the "metrics" object in the UI. This is a snippet of one idea, which is that for any metric that has the possibility of a user unit, we set the extra values of the metric with '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. but it could be '/' or ':' or '>' but maybe not '.' )
v.p.odometer#unit: "M" v.p.odometer#user: 6754.91 v.p.satcount: 13 v.p.speed: 0 v.p.speed#unit: "km/h" v.p.speed#user: null *v.p.trip: 28* *v.p.trip#unit: "M" v.p.trip#user: 17.3984*
Then we can use this in the dials to populate the values and captions! (not that I like Miles). I
image.png
The other (similar) way was to have something like the following: "v.p.trip#user" : { "value": 17.3984, "unit": "M" } It wouldn't make the total message any shorter.. soo.. dunno.
There's also some complications with setting up the dials (for min/max values) - like for the speed.
Notice also that I'm returning null for undefined values. It's nice - but I'm not sure how javascript handles null when used / printed etc.
//.ichael
On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
looks good.
I think having an explicit 'default' option is better than taking the 'Metric' equivalent for that, as in your example you already show unit alternatives within the metric system to support different scalings (kW / W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-))
@Patrick, I think that also answers your implicit question:
The default button makes it unclear what the actual setting is.
The default (native unit) is always metric, but you may have a mix of scalings, as we try to find the one that fits best for the given application when defining a metric. For example the current driving energy consumption is stored natively in Wh/km, while the energy used or regenerated is in kWh, and the odometer & trip counters are in km, while the altitude ist in m.
Regards, Michael
Am 13.11.22 um 08:42 schrieb Michael Geddes:
Greetings, so this is my idea of being able to select which units various groups use (in addition to Distance). This can be then accessed by the special 'user' unit code. (or 'metrics list -u ' ) The idea of [Default] selection below simply means storing the value to blank - meaning use whatever unit the particular metric uses. The other idea I had was to actually default it to the equivalent of 'Metric' special unit code and not have the [Default] button.
image.png
Currently I've made it so that if there are more than 3 choices other than [default] that it uses the choice/combo box rather than the Radio buttons. (ie this list is auto-generated from the Metric Units table and the Metric Groups table).
Thoughts / comments?
//.ichael
On Sat, 12 Nov 2022 at 17:35, Michael Geddes <frog@bunyip.wheelycreek.net> wrote:
https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771
I'm hoping this P/R is ok in this form (made of 5 separate commits).
I will have a look at implementing the "user" unit code. The base for how it would work is already a part of the above pull request. I'll just look at the module configuration for distance.
The 'power consumption' is one where it's not just a check-box.. there're 5 possible choice!
I should also add 'bar' for pressure given that for some reason that's still a thing people want.
//.ichael
On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> wrote:
I think this is pretty decent & complete now.
I also like the approach of the 'user' unit code. Moving all user unit prefs into the module configuration is an old todo. Currently only the distance unit is defined at the module side, temperature and pressure are App prefs.
Regards, Michael
Am 11.11.22 um 09:54 schrieb Michael Geddes: > Ok - so here's what I have > implemented for Duktape and > Metrics. (I added > IsDefined() as well). > Any thoughts on this? > > Noting > OvmsMetrics.Float( {metric} > ) -> Outputs metric as float > (same) > OvmsMetrics.Float( > {metric}, {unit}) -> Outputs > metric as float converted to > given unit (new) > OvmsMetrics.Value( {metric} > ) -> Outputs Metric in > native value (same) > OvmsMetrics.Value( {metric} > , false) -> Outputs Metric > as string and no units (same) > OvmsMetrics.Value( {metric} > , {unit}) -> Outputs Metric > converted to given unit as > native value. (new) > OvmsMetrics.Value( {metric} > , {unit}, false ) -> > Outputs Metric converted to > given unit as string > including any unit > specifier. (new) > also OvmsMetric.GetValues( > {metric} [,{unit}] [, > {converted} ] ) Adds > similar behaviour to Value() > above. > also the special units > '*imperial*' and '*metric*' > will convert to the > associated imperial / metric > version of the units as > appropriate. > > (function() { > dump = function (metric) > { print( metric+ " > ["+(typeof metric)+"]\n" ); } > dump_obj = function (obj ) { > print('--- Object ----\n') > for (var k in obj) { > xk = obj[k]; > print( k+':'+ xk + ' > ['+typeof xk+ "]\n"); > } > } > dump(OvmsMetrics.Value("xiq.v.trip.consumption")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption", > false)); > dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption", > "mipkwh", false)); > dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); > dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); > dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) > dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", > false)) > dump_obj(OvmsMetrics.GetValues("trip", > "metric")) > dump_obj(OvmsMetrics.GetValues("trip", > "imperial", false)) > })(); > > With this output: > 19.2308 [number] > 19.2308 [string] > 5.2 [number] > 3.23112mi/kWh [string] > 19.2308 [number] > 5.2 [number] > 309.49 [number] > 309.49Wh/mi [string] > --- Object ---- > v.p.trip:13 [number] > xiq.e.trip:0 [number] > xiq.e.trip.energy.recuperated:0 [number] > xiq.e.trip.energy.used:0 [number] > xiq.v.trip.consumption:19.2308 [number] > --- Object ---- > v.p.trip:8.07781M [string] > xiq.e.trip:0M [string] > xiq.e.trip.energy.recuperated:0kWh [string] > xiq.e.trip.energy.used:0kWh [string] > xiq.v.trip.consumption:309.49Wh/mi [string] > > On Wed, 9 Nov 2022 at 05:47, > Michael Geddes > <frog@bunyip.wheelycreek.net> > wrote: > > Yeah - I like HasValue. > I implemented > IsDefined() but I will > rename it.. that's a > much clearer name. > > Another thought. How > about if we did this > (but also with > GetValues() as well - > see the special values > below) > > OvmsMetrics.Value("xiq.v.trip.consumption", > true) -> 17.0582 (Number) > OvmsMetrics.Value("xiq.v.trip.consumption", > false) -> 17.0582 (String) > OvmsMetrics.Value("xiq.v.trip.consumption", > "mipkwh", true) -> > 3.64264 (Number) > OvmsMetrics.Value("xiq.v.trip.consumption", > "mipkwh", false) -> > 3.64264Mi/kWh (String) > OvmsMetrics.Value("xiq.v.trip.consumption", > "native", false) -> > 17.0582km/kWh (String) > > and > OvmsMetrics.Value("xiq.v.trip.consumption", > "imperial", false) -> > 3.64264Mi/kWh (String) > > I have already > implemented the special > values 'native' > (existing), 'imperial' > and 'metric'. > > I was also thinking that > in the future you could > have 'user'. Where for > each group of values: > 'temperature', > 'distance', > 'shortdistance', 'power' > etc.. you could have a > user preference. I > probably won't implement > it now,.but it could be > cool that any UI could > just ask for the user > defined units (rather > than having a separate > choice). > > > > //.ichael > > On Tue, 8 Nov 2022 at > 21:57, Mark Webb-Johnson > <mark@webb-johnson.net> > wrote: > > Or perhaps something > more specific? > > HasValue() > > Mark > >> On 8 Nov 2022, at >> 9:01 PM, Michael >> Balzer >> <dexter@expeedo.de> >> wrote: >> >> Signed PGP part >> That's basically a >> good approach, but >> be aware >> 'IsDefined()' has >> an ambiguous >> meaning here, as >> with the API stem >> "OvmsMetrics" it >> would naturally be >> expected to mean >> "is this metric >> defined", not "does >> this metric have a >> defined value". >> >> An undefined metric >> currently can be >> derived from >> 'Values()' >> returning >> undefined, but >> that's more an >> undocumented side >> effect than intended. >> >> Maybe >> 'GetDefined()' >> could be a better >> name, leveraging >> this behaviour, >> i.e. returning >> 'undefined' for an >> actually undefined >> metric, and 'null' >> for a defined >> metric without a value. >> >> Regards, >> Michael >> >> >> Am 08.11.22 um >> 13:46 schrieb >> Michael Geddes: >>> Ah yes. Arrays - >>> will check those. >>> Yeah, how about we >>> add a 'IsDefined' >>> method to metrics >>> instead of the >>> null thing (it >>> does sound like it >>> will upset too >>> many applecarts). >>> >>> //. >>> >>> On Tue, 8 Nov 2022 >>> at 20:35, Michael >>> Balzer >>> <dexter@expeedo.de> >>> wrote: >>> >>> Michael, >>> >>> looks all good >>> to me, once >>> again nice >>> find with the >>> decode >>> argument. >>> Adding decode >>> to the Value() >>> call was only >>> for symmetry >>> IIRC, the main >>> use was with >>> GetValues() >>> (https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics). >>> >>> Don't forget >>> to test >>> arrays, e.g. >>> "v.t.pressure" >>> & "v.t.temp". >>> >>> Returning null >>> for an >>> undefined >>> metric seems >>> like a natural >>> choice, but is >>> a rather deep >>> change, as for >>> consistency >>> not only the >>> Duktape >>> metrics API >>> but also the >>> Web UI metrics >>> API would need >>> to be changed >>> accordingly. >>> Unless you've >>> got a real use >>> case that >>> needs that, we >>> should be careful. >>> >>> Regards, >>> Michael >>> >>> >>> Am 07.11.22 um >>> 15:00 schrieb >>> Michael Geddes: >>>> I have >>>> figured out a >>>> bunch of >>>> stuff and >>>> have >>>> implemented >>>> the >>>> following: >>>> (having done >>>> away with >>>> needing >>>> AsFloatUnit) >>>> >>>> OvmsMetrics.Value( >>>> {metric} [, >>>> {decode}]) >>>> OvmsMetrics.Value( >>>> {metric}, >>>> {unit} >>>> [,{decode}]) >>>> >>>> It turns out >>>> that the >>>> [decode] flag >>>> wasn't >>>> working >>>> anyway (since >>>> the function >>>> was being >>>> registered as >>>> only having 1 >>>> param)... >>>> This way it >>>> is still >>>> really 1 >>>> function.. >>>> but I check >>>> it the second >>>> parameter is >>>> a 'boolean', >>>> and if not.. >>>> try the >>>> second form. >>>> >>>> OvmsMetrics.AsFloat( >>>> {metric} >>>> [,{unit}] ) >>>> >>>> and add the >>>> function >>>> >>>> Ovms.Metrics.ValueUnit( >>>> {metric} >>>> [,{unit}]) >>>> This prints >>>> the value and >>>> the unit. >>>> >>>> Here's a >>>> sample >>>> function and >>>> the output! >>>> This also >>>> shows the >>>> types of the >>>> output. >>>> >>>> (function() { >>>> x = >>>> OvmsMetrics.Value("xiq.v.trip.consumption"); >>>> print( >>>> (typeof x) + >>>> ": "+ x+"\n" ); >>>> x = >>>> OvmsMetrics.Value("xiq.v.trip.consumption", >>>> false); >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n" ); >>>> x = >>>> OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> x = >>>> OvmsMetrics.Value("xiq.v.trip.consumption", >>>> "mipkwh", false) >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> x = >>>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> x = >>>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> x = >>>> OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> x = >>>> OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>>> print( >>>> (typeof x) + >>>> ": "+ x +"\n"); >>>> })(); >>>> >>>> number: 17.0582 >>>> string: 17.0582 >>>> number: 5.86227 >>>> string: 3.64264 >>>> string: 17.0582kWh/100km >>>> string: 3.64264mi/kWh >>>> number: 17.0582 >>>> number: 5.86227 >>>> >>>> >>>> It still >>>> might be an >>>> idea to use >>>> 'null' as a >>>> return value >>>> if the >>>> metrics >>>> is!IsDefined() >>>> but that >>>> would be >>>> changing the >>>> existing >>>> behaviour >>>> slightly. >>>> >>>> //.ichael >>>> >>>> On Mon, 7 Nov >>>> 2022 at >>>> 08:12, >>>> Michael >>>> Geddes >>>> <frog@bunyip.wheelycreek.net> >>>> wrote: >>>> >>>> I've >>>> worked >>>> out what >>>> the >>>> decode >>>> flag is >>>> for and >>>> how it >>>> works, >>>> and I >>>> think how >>>> optional >>>> params work. >>>> I'm >>>> pretty >>>> sure I >>>> won't >>>> need the >>>> 'AsFloatUnit' >>>> function; >>>> the unit >>>> would be >>>> an option >>>> to >>>> AsFloat(); >>>> I'll know >>>> that soon. >>>> >>>> The >>>> 'Value' >>>> function >>>> is more >>>> complicated >>>> because >>>> of the >>>> optional >>>> decode >>>> bool. I >>>> guess I >>>> could add >>>> the Unit >>>> to the >>>> end of that. >>>> >>>> ValueUnit >>>> could be >>>> still >>>> useful >>>> then to >>>> provide a >>>> 'Value + >>>> Unit'. >>>> >>>> Question: >>>> Is there >>>> a reason >>>> we >>>> shouldn't >>>> be >>>> returning >>>> with >>>> duk_push_null >>>> if the >>>> metric >>>> !IsDefined() >>>> in both >>>> AsFloat() >>>> and >>>> Value(metric,true) >>>> cases? >>>> >>>> //.ichael >>>> >>>> On Sun, 6 >>>> Nov 2022 >>>> at 11:22, >>>> Michael >>>> Geddes >>>> <frog@bunyip.wheelycreek.net> >>>> wrote: >>>> >>>> Right, >>>> so >>>> I've >>>> implemented >>>> some >>>> stuff >>>> that >>>> seems >>>> to >>>> work >>>> quite >>>> well. >>>> >>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>> should >>>> be >>>> ready >>>> now >>>> after >>>> a >>>> couple >>>> of >>>> stupid >>>> mistakes >>>> slipped >>>> through. >>>> This >>>> absolutely >>>> needs >>>> somebody >>>> to >>>> review >>>> it >>>> please! >>>> (There's >>>> a >>>> reason >>>> why >>>> I've >>>> converted >>>> some >>>> if()'s >>>> to >>>> switch() >>>> - >>>> which >>>> is >>>> that >>>> it >>>> will >>>> be >>>> used >>>> in >>>> the >>>> follow-up >>>> commit). >>>> >>>> The >>>> commit >>>> that >>>> will >>>> follow >>>> on >>>> from >>>> that >>>> it >>>> implements >>>> the >>>> new >>>> Units: >>>> kWh/100km, >>>> km/kWh >>>> and >>>> mi/kWh. >>>> >>>> This >>>> is a >>>> summary >>>> of >>>> what >>>> I've >>>> implemented >>>> for >>>> scripting >>>> - >>>> including >>>> showing >>>> the >>>> unit >>>> codes >>>> I >>>> have >>>> so far. >>>> I've >>>> considered >>>> a few >>>> things: >>>> * >>>> Should >>>> some >>>> of >>>> the >>>> longer >>>> unit >>>> codes >>>> be >>>> shortened >>>> (eg >>>> mi, >>>> mins, >>>> m, >>>> ft, >>>> deg, >>>> perc) >>>> * >>>> The >>>> unit >>>> codes >>>> could >>>> be >>>> much >>>> more >>>> regular >>>> and >>>> separated >>>> by >>>> dots >>>> eg: >>>> watthours >>>> -> w.h >>>> kwhp100km >>>> -> >>>> kw.h_100km >>>> or >>>> kw.h/100km >>>> >>>> miph >>>> -> >>>> mi_h >>>> or >>>> mi/h >>>> (or >>>> should >>>> it be >>>> mph). >>>> >>>> psi >>>> -> >>>> p_in.in >>>> <http://p_in.in/> >>>> or >>>> p/in.in >>>> <http://in.in/> >>>> or >>>> lb_in.in >>>> <http://lb_in.in/> >>>> (yes, >>>> slightly >>>> weird, >>>> but >>>> predictable) >>>> >>>> *OVMS# >>>> metric >>>> units* >>>> >>>> km : km >>>> >>>> miles >>>> : M >>>> >>>> meters >>>> : m >>>> >>>> feet : ft >>>> >>>> celcius >>>> : °C >>>> fahrenheit >>>> : °F >>>> >>>> kpa >>>> : kPa >>>> >>>> pa : Pa >>>> >>>> psi >>>> : psi >>>> >>>> volts >>>> : V >>>> >>>> amps : A >>>> amphours: >>>> Ah >>>> >>>> kw : kW >>>> >>>> kwh >>>> : kWh >>>> >>>> watts >>>> : W >>>> watthours: >>>> Wh >>>> >>>> seconds >>>> : Sec >>>> >>>> minutes >>>> : Min >>>> >>>> hours >>>> : Hour >>>> >>>> utc >>>> : UTC >>>> >>>> degrees >>>> : ° >>>> >>>> kmph >>>> : km/h >>>> miph: Mph >>>> kmphps: >>>> km/h/s >>>> miphps: >>>> Mph/s >>>> >>>> mpss >>>> : m/s² >>>> >>>> dbm >>>> : dBm >>>> >>>> sq : sq >>>> >>>> percent >>>> : % >>>> >>>> whpkm >>>> : Wh/km >>>> whpmi: >>>> Wh/mi >>>> kwhp100km >>>> : >>>> kWh/100km >>>> kmpkwh: >>>> km/kWh >>>> mipkwh: >>>> mi/kWh >>>> >>>> nm : Nm >>>> >>>> *OVMS# >>>> metric >>>> unit mi* >>>> >>>> miles >>>> : M >>>>
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
Oooh.. I can now see this might actually happen. Btw, one of the nice things about the async subscriptions is that the form (like the dashboard) doesn't freeze when loading up.. so the page loads and renders and then the metrics kinda of just slide on in. I don't believe it takes as long as loading all metrics - but also it feels from a UI perspective even shorter because the UI loads quicker. On Wed, 14 Dec 2022 at 05:42, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
Am 13.12.22 um 00:00 schrieb Michael Geddes:
I'll go back and revise the topic subscriptions stuff wrt metrics etc. with the stuff I have done.
I have implemented metric subscriptions and also implemented async getters in the dashboard just to see what the burden is, and tbh it's not too bad! I had no real expectation that this would end up in the final code and it was more just to further my understanding. What I've done does require a 'get and subscribe' function added to the websocket.
*unit getsub CallID metricName* Which subscribes to the metric, but also returns a result something like { result : { ID: *CallID*, Metric: *metricName*, Value: *metricValue*, Units : { native: *unitcode*, unit: *userunitcode*, label: *label*} }}
So I can create a Promise and put the Accept function agains the (random) *CallID* on an object, (and a timeout that calls reject) -so when the result comes in I can call the accept.
If the asynchronous nature isn't hidden (i.e. caller needs to use async/await tags) and using the async getters is an alternative to the pattern subscription scheme, that would be OK for inclusion. It adds another way to subscribe, that may be better for some applications. Keep in mind a page/fragment's getter subscriptions then also need to be unsubscribed on unloading the page/fragment.
Yep - definitely worked out that the async nature can't be hidden, it seems it's just not an option. Hmm.. unsubscribe - ouch - this might be 'fun'. Will see how it goes. The values need to be cleared from the metrics[] array indicating they need to be subscribed again.
It still doesn't get over the need for plugins to either subscribe to the metrics they want to access through metrics[]... or use the ametrics[] collection in an async function. Having a tag (as you mentioned) that says which metrics to subscribe to would be at least backwards compatible. I guess we could make it so that if a plugin was searched for such a tag and none existed, we could subscribe all?
Yes, and we could add an API version attribute, to be able to identify receivers that expect to have direct access to all metrics. That way we still enable a new receiver to be created that really doesn't need metrics (or does all subscriptions through the async getters). Something like…
<div class="receiver"> → auto subscription to "metrics/#" if no explicit "metrics/…" subscriptions present
<div class="receiver" data-apiversion="2"> → only process explicit subscriptions
I very much like the apiversion idea: Easy to check for. Also, I've been looking at the current subscription scheme - with a tag lookup. The way I currently implement subscriptions for the metrics is that I use a 'slot' per connection like the 'modified' flag which indicates that a metric is subscribed. I'm wondering if it would be ok if when the particular metrics/* namespace comes in, if I could divert that to using the same method I have now then it would keep things working quite quickly... would that work for you? //.ichael
Regards, Michael
//.ichael
On Sun, 11 Dec 2022 at 19:54, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
yes, while async getters seem to be possible by some JS voodoo, they probably won't work without changes to the applications.
I also don't think they would form an application code pattern we should encourage, as they would try to hide asynchronous operations and would lead to each individual metric needing to be requested (at least initially) by a separate async call.
Instead, please reconsider:
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
I designed this subscription scheme to follow the MQTT topic subscription scheme and be usable for any kind of subscription. Different data sources can be identified by the root topic. For notifications, it's `notify/`, for metrics, we can naturally assign `metrics/`. A subscription pattern `metrics/v/p/#` would for example subscribe to all `v.p.*` metrics.
Auto subscribing & unsubscribing is managed by the framework via the `data-subscriptions` attribute of a `.receiver`.
Examples:
- https://docs.openvehicles.com/en/latest/components/ovms_webserver/docs/notif... - https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/tree/master...
Btw, if (!) we keep the units dictionary subscription necessity, that could be a subscription to `units/#`.
Regards, Michael
Am 08.12.22 um 04:29 schrieb Michael Geddes:
I've implemented a way of having user units of metrics come through (I have metrics_user[] metrics_label[] and metrics_all[] implemented).. *however as far as dynamic subscription to metrics goes, I now believe I can't do what I want in a backwards compatible way.*
This is where I'm at:
I haven't used Promises before or async/wait etc, so in my naivity, I had (mistakenly it seems) thought I could define a function taking a *metric code* that * Sends a command to the websocket eg *metric fetchsub 1234 v.p.trip* * Waits for the result coming in over the web-socket or times out * Returns the result to the user *directly* so *metric['* *v.p.trip**'] * (or whatever) could have an accessor function that (for unsubscribed metrics) silently fetched and subscribed to the metric over the websocket and returned the value.
I have actually implemented the fetching bit mostly .. (a Promise resolve call-back is put in a collection against a random id and when the event comes back it uses the id to grab the promise function and resolve with the result - and yes it has a timeout) ... except that in the end, the javascript still requires a Promise to be created that needs to execute its result as a call-back. I also believe I now understand why that is not possible, and that fundamentally the only way of doing async stuff at all ends up in some kind of call back (via a Promise directly or an async function Promise)... and that the only exceptions to this are within an async function (which in the end still returns a Promise).
This makes blocking on a metric['v.p.trip'] proxy get; function not possible afaict. Unless somebody knows a mechansim that I could use? (outside of an async function, of course)
I have implement the subscription model for metrics[] (which supports, for example, v.p.* ) .. but without the auto-subscription it is going to be less useful for plugins as far as backwards compatibility goes!
you could have a call like this: applyMetric('v.p.trip', (value) => { ... put the value somewhere } )
//.ichael
On Sat, 3 Dec 2022 at 02:29, Michael Balzer <dexter@expeedo.de> wrote:
Michael,
the unit conversion JS code scheme is fine. Btw, you can optimize convert to…
convert = function (from, to, value) { return (unit_conversions[from + ">" + to] || unit_conversions.native )(value); }
Yeah - I'm assuming that the gain from not looking up unnecessarily is lost from all the checking. Got it.
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I think plugins will need to be updated anyway, as will all our standard pages & components, as up to now all units have been fixed in the UI.
In both approaches, all metrics displays (simple markup, tables, charts) will need to be reworked to use the user units. Only the basic markup type displays could partially be modified automatically (by walking through their '.unit' elements), but any extended use, even the range & energy displays in the dashboard's speed gauge, will need a config-aware approach.
Charts will need to fully reconfigure, as unit labels are used within different chart features, and axis limits & plotbands will need to be adjusted. For this, scripts can subscribe to a new 'msg:units' event sent when a (re-)configuration of units is received.
Yeah - I've got some classes so that different cars can specify all the ranges in native units and it will generate code for the current user units. I've already put this into use for all vehicle classes that return a custom guage configuration.
At the moment it is still static code and we'll need to sink on a (not yet implmeneted) untits changed event to reload it.. but it would be simple enough to change the code generated to be dynamic (especially that we now have that conversion function). Even just having the user refresh the page is better than nothing at the moment (it's not like the user is going to be changing the units all the time).
Btw, in case you didn't see this already: I implemented an auto subscription scheme for the 'stream' notifications -- these are by default very transmission intense and can cause substantial load on the module side as well. These subscriptions are managed automatically for all components and plugins by the framework (which btw also takes care of initializing all fragments added in the '#' container).
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
That leads us to another option: we could keep the metrics transmission in native values, add the units dictionary and provide all conversions in Javascript using this proxy getter scheme.
Yep - already decided to do that. It makes more sense. At the moment I'm also implementing a way of starting off with no metrics supplied and then adding in which ones we need. Probably the JS is the bit I need to work out on that.
That would keep the current metrics[] access scheme intact and unchanged, so all current frontend code & plugins would continue to work, using the native values as before.
The new proxy getters then can be used as the new way to access metrics by anyone interested in using user units, and we can go ahead by applying this to the standard pages and components.
This in combination with the 'msg:units' event to signal reconfiguration should provide all we need.
We can even easily combine this with providing a command or socket URL / parameter to switch the metrics into user mode. The standard web frontend won't need this then, but it would make using user units easy for devices without Javascript support.
Regards, Michael
Am 28.11.22 um 00:36 schrieb Michael Geddes:
Solved a couple of things. I have a 'unit conversion' code - which I current have put into a separate cpp file along with the two other C++ conversion functions. I wanted to do it this way so they are all in there together. (Does this make sense to do?).
mi_to_km = function(mi) { return mi * 1.609347; } km_to_mi = function(km) { return km * 0.6213700; } pkm_to_pmi = function(pkm) { return pkm * 1.609347; } pmi_to_pkm = function(pmi) { return pmi * 0.6213700; } const feet_per_mile = 5280; var unit_conversions = { "native": function (value) { return value;}, "km>miles": km_to_mi, "km>meters": function (value) { return value*1000; }, "km>feet": function (value) { return km_to_mi(value) * feet_per_mile; }, ...... "percent>permille": function (value) { return value*10.0; }, "percent>percent": function (value) { return value*0.10; } } convert_function = function (from, to) { var fn = undefined; if (from !== to && to !== "") fn = unit_conversions[from + ">" + to]; if (fn == undefined) fn = unit_conversions.native; return fn; } convert = function (from, to, value) { var fn = convert_function(from, to); return fn(value); }
The other problem of looking at the uri of the websocket I have solved by creating a 'SocketCreator' MgHandler class in the MG_EV_WEBSOCKET_HANDSHAKE_REQUEST event (and looks at the uri)... that waits for the MG_EV_WEBSOCKET_HANDSHAKE_DONE and deletes itself.
int OvmsSocketCreator::HandleEvent(int ev, void *p) { if ( ev != MG_EV_WEBSOCKET_HANDSHAKE_DONE) return ev; // new websocket connection MyWebServer.CreateWebSocketHandler(m_nc, m_socket_type ); m_nc = NULL; delete this; return 0; }
Thoughts?
//.ichael
On Sun, 27 Nov 2022 at 07:18, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
I knew we had to keep the default webstream as it was. I was thinking of having a different websocket Uri to trigger filtered mode (starting without sending all units).. Though I am struggling with how to get and then pass the Uri information into the web socket constructor! The event that currently creates it doesn't seem to have access to the Uri. (see below - p is NULL for HANDSHAKE_DONE). case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) p; // how to pass uri info to the event below!? } break; case MG_EV_WEBSOCKET_HANDSHAKE_DONE: // new websocket connection { MyWebServer.CreateWebSocketHandler(nc); } break;
Especially, I was also considering the plugins that run from the '#' container that may not know the container had switched to user units! (so I'm not sure about option a.), so from what I can tell, the base metrics[] needs to maintain native units imho. This is also why I was looking at the 'auto-subscribe' idea since the outside container doesn't know which metrics a plugin might use, and a plugin wouldn't know to subscribe to the messages.
I do like the separate units description message - though we would probably need to add the 'native' if we want to do conversions.
{ units: { "v.p.speed": { code: "kmph", *native "kmph",* label: "km/h" }, …
We could maintain a units collection exactly as above with some proxy arrays to get at values without exceptions. For eg: m.label["v.p.speed"] could look up the units_ collection being maintained and return blank if the entry doesn't exist. Then m.value["v.p.speed"] would give the user unit and m.nativevalue["v.p.speed"] the native value. The latter two would use the metrics[] array or whatever mechanism we had. We could add m.text["v.p.speed"] that would give a text version with the value and unit if it had one.
I had contemplated the idea of providing a JavaScript unit conversion and was working around it. Downside is it's a third set of conversion functions to maintain... On the other hand using that we could just keep the metrics being sent as it is now, have the groups sent as you proposed (along with the native unit code) , and have the above m.value[] proxy collection use a 'touser' function assigned to the units_ collection above that provided native to user conversion.
We could send a javascript library constructed with just the necessary functions for the required conversions of native to user (or the whole lot.. whichever). . -- var conversions = { unit: function ( value) { return value } km_miles: function (value) { return value * 0.6213700; } } We could perform the lookup when constructing the units and assign the touser property. (And have a 'unit' function that does no conversion).
//.ichael
On Sat, 26 Nov 2022, 10:43 pm Michael Balzer, <dexter@expeedo.de> wrote:
The metrics subscription scheme is an option, and the auto-subscribe feature via a getter is a nice idea. But I wouldn't apply that to metrics value conversions and units.
Also we would still need the current set of metrics to be subscribed by default, as there are also non Javascript devices (e.g. smart buttons, Wifi displays) reading the WebSocket stream.
My thoughts on this so far:
Basically the web UI, as any frontend, should adapt to unit configurations seamlessly. The web UI includes many command outputs, which already automatically switch units as configured.
For all practical purposes, the web UI needs to interact with users in their preferred units. Only some plugins and functions will need certain values in metric (native) units for calculations, and these will also need a simple way to convert calculation results back to user units for displaying. So I think we need to provide the unit configuration and value conversion tools in the web framework as well.
Proposal:
a) We provide a config option and a WebSocket command to switch the WebSocket metrics transmission mode to user / native units. To keep plugin compatibility, the default is 'native'.
b) We introduce a separate units dictionary object containing the user units for both metrics and the unit groups in their code & label representation. The units dictionary only needs to be sent initially, when new metrics are registered, when the metrics mode is changed for the current connection, and when some user unit configuration is changed, keeping the bandwidth and processing requirements low.
The units dictionary can combine both metrics and group units, as the unit group names are fully distinct from the metrics namespace. The transport scheme could be:
{ units: { "v.p.speed": { code: "kmph", label: "km/h" }, … "units.distance": { code: "miles", label: "M" }, … }
c) In the web framework, accessing units should be as simple as possible and avoid throwing exceptions for undefined entries, so we could e.g. split these into separate code & label objects:
units["v.p.speed"] = "km/h" // consistently accompanies metrics["v.p.speed"] unitcodes["v.p.speed"] = "kmph"
units["units.distance"] = "M" unitcodes["units.distance"] = "miles"
…or provide a getter that tests for the key existence and returns an object with empty fields as necessary.
With this, all metrics displays can easily be changed to display the actual unit labels instead of using fixed strings.
d) To provide value conversion we implement UnitConvert() in Javascript plus some wrappers that automatically look up the unit for a given metrics/group name and do the conversion to/from native units, something like…
var speed_kph = toNativeValue("v.p.speed"); // optional second arg to convert any data var speed_kph = metrics_native["v.p.speed"]; // using a getter
var trip_display = toUserValue("units.distance", 1234);
Plugins for scientific/technical applications that depend on native (metric) units can use the new metrics transmission mode control command to force native mode. Or they can choose to migrate from "metrics[]" to "metrics_native[]".
The metrics mode config option can come with a note informing users that there may be some old plugins not compatible with non-native units. They can then check their plugins for this and make an informed decision on wether to enable user units and/or wether to install a specific plugin.
Thoughts, comments?
Regards, Michael
Am 25.11.22 um 03:13 schrieb Michael Geddes:
I have an idea which might reduce traffic for maintaining the metrics[] array in the browser and cope with the user units. I'll start by saying I'm not a JS developer per se.. so a newb in JS really. Still, it's mainly just another language so .. we'll give it a go.
Firstly: * Implement the 'changed' filters as below for the web-socket.. for both normal and 'user' values. * Add a function that subscribes to a value (and returns the current value of it)..including to 'user' value/unitlabel.
Subscribing the normal way to the metrics over the websocket would have the normal effect.. but we would have a new way that would subscribe in a filtered way.
I've had a little play with the Proxy object .. so at least I know this should work:
Have a metrics_ array that is the real associative array for metrics[] and then define a Proxy that has (at the least) 'get' and 'has' defined (giving us the ability to overload *metrics['prop']* and *"prop" in metrics operations*).
The *get *function would return the underlying value if it exists in the *metrics_ *array (which is maintained through the websocket from currently subscribed values in the current manner). If the value is not in the *metrics_* array - it would then do a subscribe+query on the websocket getting the current value and adding it into the *metrics_* container. If it was unavailable then it would put *undefined* into the array. The 'has' would do the get() and return true if the value was not == *undefined*.
For the 'query the websocket' bit, I'm assuming I would be working with promises or futures or some such: I'll do the research and do it properly unless somebody can help me out with it. That's the bit I was going to work on next for the proof-of-concept.
Any immediate thoughts? Dangers?
I also noticed there was a bit that went through html element properties and looked for metrics .. this could be used to bulk subscribe to any metric values required there.
//.ichael
On Thu, 17 Nov 2022 at 07:52, Michael Geddes < frog@bunyip.wheelycreek.net> wrote:
Yeah, ok.
I will get all the other 'user unit' stuff done as a line in the sand, and then move to working out the web stuff. I'm still finding my way though all the client side javascript, which looks very cool.. but I've not really done jQuery before (just enough to recognise it).
Subscribing to metrics with/without user units makes a lot of sense. Obviously the default needs to be 'Subscribe to all metrics but not user units' to maintain compatibility... but I was also thinking it might be nice if we could filter down even the normal subscribed events. We could have: * Web socket command to filter units (flag on websocket to say 'filtered' + flag bitset on each metric similar to 'dirty') Then either: * Web socket command to turn on user units (single flag on that websocket) or * Web socket command to turn on user units for specific metrics (flag bitset on each metric)
A parameter to the URI for the websocket could start the socket in 'filtered' mode to avoid the initial rush of metrics.
This could drastically reduce traffic and time for the metrics command to execute. It would be possible to also check (on a 'filtered' websocket) for any changes to metrics for that websocket slot before queueing the 'metric update' socket command.
//.ichael
On Thu, 17 Nov 2022 at 00:35, Michael Balzer <dexter@expeedo.de> wrote:
> Michael, > > I don't have much spare time currently, just some quick first > comments: it's important to implement this as lightweight as possible, both > in terms of network load and client CPU & memory requirements. Some devices > already have issues, which can be seen by the "websocket overflow" > messages. The web UI also should stay usable via cellular. > > My impression is the new scheme, while only slightly raising the > client requirements, adds substantially to the network requirements. > > An option could be to separate the units -- or more, back when > implementing this I thought about separating the names later on. Another > question is if we normally generally need both the native and the converted > values in the web UI. We maybe could provide an option to switch to > converted values, or add an option to retreive or subscribe to a set of > converted metrics on demand. > > Standard plugins like ABRP and PwrMon rely on getting metric > (native) units, and there probably are non-public plugins, e.g. for > engineering & scientific projects, that depend on metric units to do their > calculations and don't need anything else. We shouldn't make life harder > for these applications without good reason. > > Regards, > Michael > > > Am 15.11.22 um 01:26 schrieb Michael Geddes: > > If you're ok with the [default] option I'll stick with that. I mean > in some ways it would be nice to have a button choice > metric | usa | europe | asia | custom etc and I kind of considered > something like that but figured it's only a handful of choices.. and it's > an embedded device.. so simpler is better. > > On a related note - I was thinking how it would be nice if the > dashboard (etc) had access to the 'user' units, so went hunting down that > little rabbit hole. Quite a nice mechanism with the web socket updating the > "metrics" object in the UI. > This is a snippet of one idea, which is that for any metric that has > the possibility of a user unit, we set the extra values of the metric with > '#unit' and '#user' appended - see below. (I've chosen '#' arbitrarily.. > but it could be '/' or ':' or '>' but maybe not '.' ) > > v.p.odometer#unit: "M" > v.p.odometer#user: 6754.91 > v.p.satcount: 13 > v.p.speed: 0 > v.p.speed#unit: "km/h" > v.p.speed#user: null > *v.p.trip: 28* > > *v.p.trip#unit: "M" v.p.trip#user: 17.3984* > > Then we can use this in the dials to populate the values and > captions! (not that I like Miles). > I > > [image: image.png] > > The other (similar) way was to have something like the following: > "v.p.trip#user" : { "value": 17.3984, "unit": "M" } > It wouldn't make the total message any shorter.. soo.. dunno. > > There's also some complications with setting up the dials (for > min/max values) - like for the speed. > > Notice also that I'm returning null for undefined values. It's nice > - but I'm not sure how javascript handles null when used / printed etc. > > //.ichael > > On Sun, 13 Nov 2022 at 21:06, Michael Balzer <dexter@expeedo.de> > wrote: > >> Michael, >> >> looks good. >> >> I think having an explicit 'default' option is better than taking >> the 'Metric' equivalent for that, as in your example you already show unit >> alternatives within the metric system to support different scalings (kW / >> W, kWh / Wh). (Btw… waiting for someone to miss Horsepower & BTU here ;-)) >> >> @Patrick, I think that also answers your implicit question: >> >> The default button makes it unclear what the actual setting is. >> >> >> The default (native unit) is always metric, but you may have a mix >> of scalings, as we try to find the one that fits best for the given >> application when defining a metric. For example the current driving energy >> consumption is stored natively in Wh/km, while the energy used or >> regenerated is in kWh, and the odometer & trip counters are in km, while >> the altitude ist in m. >> >> Regards, >> Michael >> >> >> Am 13.11.22 um 08:42 schrieb Michael Geddes: >> >> Greetings, >> so this is my idea of being able to select which units various >> groups use (in addition to Distance). >> This can be then accessed by the special 'user' unit code. (or >> 'metrics list -u ' ) >> The idea of [Default] selection below simply means storing the >> value to blank - meaning use whatever unit the particular metric uses. The >> other idea I had was to actually default it to the equivalent of 'Metric' >> special unit code and not have the [Default] button. >> >> >> [image: image.png] >> >> Currently I've made it so that if there are more than 3 choices >> other than [default] that it uses the choice/combo box rather than the >> Radio buttons. (ie this list is auto-generated from the Metric Units table >> and the Metric Groups table). >> >> Thoughts / comments? >> >> //.ichael >> >> On Sat, 12 Nov 2022 at 17:35, Michael Geddes < >> frog@bunyip.wheelycreek.net> wrote: >> >>> >>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/771 >>> >>> I'm hoping this P/R is ok in this form (made of 5 >>> separate commits). >>> >>> I will have a look at implementing the "user" unit code. The base >>> for how it would work is already a part of the above pull request. I'll >>> just look at the module configuration for distance. >>> >>> The 'power consumption' is one where it's not just a check-box.. >>> there're 5 possible choice! >>> >>> I should also add 'bar' for pressure given that for some reason >>> that's still a thing people want. >>> >>> //.ichael >>> >>> On Sat, 12 Nov 2022 at 16:24, Michael Balzer <dexter@expeedo.de> >>> wrote: >>> >>>> I think this is pretty decent & complete now. >>>> >>>> I also like the approach of the 'user' unit code. Moving all user >>>> unit prefs into the module configuration is an old todo. Currently only the >>>> distance unit is defined at the module side, temperature and pressure are >>>> App prefs. >>>> >>>> Regards, >>>> Michael >>>> >>>> >>>> Am 11.11.22 um 09:54 schrieb Michael Geddes: >>>> >>>> Ok - so here's what I have implemented for Duktape and Metrics. >>>> (I added IsDefined() as well). >>>> Any thoughts on this? >>>> >>>> Noting >>>> OvmsMetrics.Float( {metric} ) -> Outputs metric as float (same) >>>> OvmsMetrics.Float( {metric}, {unit}) -> Outputs metric as >>>> float converted to given unit (new) >>>> OvmsMetrics.Value( {metric} ) -> Outputs Metric in native >>>> value (same) >>>> OvmsMetrics.Value( {metric} , false) -> Outputs Metric as >>>> string and no units (same) >>>> OvmsMetrics.Value( {metric} , {unit}) -> Outputs Metric >>>> converted to given unit as native value. (new) >>>> OvmsMetrics.Value( {metric} , {unit}, false ) -> Outputs >>>> Metric converted to given unit as string including any unit specifier. >>>> (new) >>>> also OvmsMetric.GetValues( {metric} [,{unit}] [, {converted} ] >>>> ) Adds similar behaviour to Value() above. >>>> also the special units '*imperial*' and '*metric*' will convert >>>> to the associated imperial / metric version of the units as appropriate. >>>> >>>> (function() { >>>> dump = function (metric) { print( metric+ " ["+(typeof >>>> metric)+"]\n" ); } >>>> dump_obj = function (obj ) { >>>> print('--- Object ----\n') >>>> for (var k in obj) { >>>> xk = obj[k]; >>>> print( k+':'+ xk + ' ['+typeof xk+ "]\n"); >>>> } >>>> } >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption")); >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption", false)); >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh")); >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>>> false)); >>>> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption")); >>>> dump(OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh")); >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial")) >>>> dump(OvmsMetrics.Value("xiq.v.trip.consumption","imperial", >>>> false)) >>>> dump_obj(OvmsMetrics.GetValues("trip", "metric")) >>>> dump_obj(OvmsMetrics.GetValues("trip", "imperial", false)) >>>> })(); >>>> >>>> With this output: >>>> >>>> 19.2308 [number] >>>> 19.2308 [string] >>>> 5.2 [number] >>>> 3.23112mi/kWh [string] >>>> 19.2308 [number] >>>> 5.2 [number] >>>> 309.49 [number] >>>> 309.49Wh/mi [string] >>>> --- Object ---- >>>> v.p.trip:13 [number] >>>> xiq.e.trip:0 [number] >>>> xiq.e.trip.energy.recuperated:0 [number] >>>> xiq.e.trip.energy.used:0 [number] >>>> xiq.v.trip.consumption:19.2308 [number] >>>> --- Object ---- >>>> v.p.trip:8.07781M [string] >>>> xiq.e.trip:0M [string] >>>> xiq.e.trip.energy.recuperated:0kWh [string] >>>> xiq.e.trip.energy.used:0kWh [string] >>>> xiq.v.trip.consumption:309.49Wh/mi [string] >>>> >>>> >>>> On Wed, 9 Nov 2022 at 05:47, Michael Geddes < >>>> frog@bunyip.wheelycreek.net> wrote: >>>> >>>>> Yeah - I like HasValue. I implemented IsDefined() but I will >>>>> rename it.. that's a much clearer name. >>>>> >>>>> Another thought. How about if we did this (but also with >>>>> GetValues() as well - see the special values below) >>>>> >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", true) -> 17.0582 >>>>> (Number) >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", false) -> 17.0582 >>>>> (String) >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", true) -> >>>>> 3.64264 (Number) >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", false) -> >>>>> 3.64264Mi/kWh (String) >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", "native", false) >>>>> -> 17.0582km/kWh (String) >>>>> >>>>> and >>>>> OvmsMetrics.Value("xiq.v.trip.consumption", "imperial", false) >>>>> -> 3.64264Mi/kWh (String) >>>>> >>>>> I have already implemented the special values 'native' >>>>> (existing), 'imperial' and 'metric'. >>>>> >>>>> I was also thinking that in the future you could have 'user'. >>>>> Where for each group of values: >>>>> 'temperature', 'distance', 'shortdistance', 'power' etc.. you >>>>> could have a user preference. I probably won't implement it now,.but it >>>>> could be cool that any UI could just ask for the user defined units (rather >>>>> than having a separate choice). >>>>> >>>>> >>>>> >>>>> //.ichael >>>>> >>>>> On Tue, 8 Nov 2022 at 21:57, Mark Webb-Johnson < >>>>> mark@webb-johnson.net> wrote: >>>>> >>>>>> Or perhaps something more specific? >>>>>> >>>>>> HasValue() >>>>>> >>>>>> Mark >>>>>> >>>>>> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter@expeedo.de> >>>>>> wrote: >>>>>> >>>>>> Signed PGP part >>>>>> That's basically a good approach, but be aware 'IsDefined()' >>>>>> has an ambiguous meaning here, as with the API stem "OvmsMetrics" it would >>>>>> naturally be expected to mean "is this metric defined", not "does this >>>>>> metric have a defined value". >>>>>> >>>>>> An undefined metric currently can be derived from 'Values()' >>>>>> returning undefined, but that's more an undocumented side effect than >>>>>> intended. >>>>>> >>>>>> Maybe 'GetDefined()' could be a better name, leveraging this >>>>>> behaviour, i.e. returning 'undefined' for an actually undefined metric, and >>>>>> 'null' for a defined metric without a value. >>>>>> >>>>>> Regards, >>>>>> Michael >>>>>> >>>>>> >>>>>> Am 08.11.22 um 13:46 schrieb Michael Geddes: >>>>>> >>>>>> Ah yes. Arrays - will check those. Yeah, how about we add a >>>>>> 'IsDefined' method to metrics instead of the null thing (it does sound like >>>>>> it will upset too many applecarts). >>>>>> >>>>>> //. >>>>>> >>>>>> On Tue, 8 Nov 2022 at 20:35, Michael Balzer <dexter@expeedo.de> >>>>>> wrote: >>>>>> >>>>>>> Michael, >>>>>>> >>>>>>> looks all good to me, once again nice find with the decode >>>>>>> argument. Adding decode to the Value() call was only for symmetry IIRC, the >>>>>>> main use was with GetValues() ( >>>>>>> https://docs.openvehicles.com/en/latest/userguide/scripting.html#ovmsmetrics >>>>>>> ). >>>>>>> >>>>>>> Don't forget to test arrays, e.g. "v.t.pressure" & "v.t.temp". >>>>>>> >>>>>>> Returning null for an undefined metric seems like a natural >>>>>>> choice, but is a rather deep change, as for consistency not only the >>>>>>> Duktape metrics API but also the Web UI metrics API would need to be >>>>>>> changed accordingly. Unless you've got a real use case that needs that, we >>>>>>> should be careful. >>>>>>> >>>>>>> Regards, >>>>>>> Michael >>>>>>> >>>>>>> >>>>>>> Am 07.11.22 um 15:00 schrieb Michael Geddes: >>>>>>> >>>>>>> I have figured out a bunch of stuff and have implemented the >>>>>>> following: (having done away with needing AsFloatUnit) >>>>>>> >>>>>>> OvmsMetrics.Value( {metric} [, {decode}]) >>>>>>> OvmsMetrics.Value( {metric}, {unit} [,{decode}]) >>>>>>> >>>>>>> It turns out that the [decode] flag wasn't working anyway >>>>>>> (since the function was being registered as only having 1 param)... >>>>>>> This way it is still really 1 function.. but I check it the >>>>>>> second parameter is a 'boolean', and if not.. try the second form. >>>>>>> >>>>>>> OvmsMetrics.AsFloat( {metric} [,{unit}] ) >>>>>>> >>>>>>> and add the function >>>>>>> >>>>>>> Ovms.Metrics.ValueUnit( {metric} [,{unit}]) >>>>>>> This prints the value and the unit. >>>>>>> >>>>>>> Here's a sample function and the output! This also shows the >>>>>>> types of the output. >>>>>>> >>>>>>> (function() { >>>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption"); >>>>>>> print( (typeof x) + ": "+ x+"\n" ); >>>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", false); >>>>>>> print( (typeof x) + ": "+ x +"\n" ); >>>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption","kmpkwh") >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> x = OvmsMetrics.Value("xiq.v.trip.consumption", "mipkwh", >>>>>>> false) >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> x = OvmsMetrics.ValueUnit("xiq.v.trip.consumption") >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> x = >>>>>>> OvmsMetrics.ValueUnit("xiq.v.trip.consumption","mipkwh") >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption") >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> x = OvmsMetrics.AsFloat("xiq.v.trip.consumption","kmpkwh") >>>>>>> print( (typeof x) + ": "+ x +"\n"); >>>>>>> })(); >>>>>>> >>>>>>> number: 17.0582 >>>>>>> string: 17.0582 >>>>>>> number: 5.86227 >>>>>>> string: 3.64264 >>>>>>> string: 17.0582kWh/100km >>>>>>> string: 3.64264mi/kWh >>>>>>> number: 17.0582 >>>>>>> number: 5.86227 >>>>>>> >>>>>>> >>>>>>> >>>>>>> It still might be an idea to use 'null' as a return value if >>>>>>> the metrics is !IsDefined() but that would be changing the >>>>>>> existing behaviour slightly. >>>>>>> >>>>>>> //.ichael >>>>>>> >>>>>>> On Mon, 7 Nov 2022 at 08:12, Michael Geddes < >>>>>>> frog@bunyip.wheelycreek.net> wrote: >>>>>>> >>>>>>>> I've worked out what the decode flag is for and how it works, >>>>>>>> and I think how optional params work. >>>>>>>> I'm pretty sure I won't need the 'AsFloatUnit' function; the >>>>>>>> unit would be an option to AsFloat(); I'll know that soon. >>>>>>>> >>>>>>>> The 'Value' function is more complicated because of the >>>>>>>> optional decode bool. I guess I could add the Unit to the end of that. >>>>>>>> >>>>>>>> ValueUnit could be still useful then to provide a 'Value + >>>>>>>> Unit'. >>>>>>>> >>>>>>>> Question: Is there a reason we shouldn't be returning with >>>>>>>> duk_push_null if the metric !IsDefined() in both >>>>>>>> AsFloat() and Value(metric,true) cases? >>>>>>>> >>>>>>>> //.ichael >>>>>>>> >>>>>>>> On Sun, 6 Nov 2022 at 11:22, Michael Geddes < >>>>>>>> frog@bunyip.wheelycreek.net> wrote: >>>>>>>> >>>>>>>>> Right, so I've implemented some stuff that seems to work >>>>>>>>> quite well. >>>>>>>>> >>>>>>>>> >>>>>>>>> https://github.com/openvehicles/Open-Vehicle-Monitoring-System-3/pull/764 >>>>>>>>> should be ready now after a couple of stupid mistakes slipped through. >>>>>>>>> This absolutely needs somebody to review it please! (There's a reason why >>>>>>>>> I've converted some if()'s to switch() - which is that it will be used in >>>>>>>>> the follow-up commit). >>>>>>>>> >>>>>>>>> The commit that will follow on from that it implements the >>>>>>>>> new Units: kWh/100km, km/kWh and mi/kWh. >>>>>>>>> >>>>>>>>> This is a summary of what I've implemented for scripting - >>>>>>>>> including showing the unit codes I have so far. I've considered a few >>>>>>>>> things: >>>>>>>>> * Should some of the longer unit codes be shortened (eg >>>>>>>>> mi, mins, m, ft, deg, perc) >>>>>>>>> * The unit codes could be much more regular and separated >>>>>>>>> by dots eg: >>>>>>>>> watthours -> w.h >>>>>>>>> kwhp100km -> kw.h_100km or kw.h/100km >>>>>>>>> miph -> mi_h or mi/h (or should it be mph). >>>>>>>>> psi -> p_in.in or p/in.in or lb_in.in (yes, >>>>>>>>> slightly weird, but predictable) >>>>>>>>> >>>>>>>>> *OVMS# metric units* >>>>>>>>> km : km >>>>>>>>> miles : M >>>>>>>>> meters : m >>>>>>>>> feet : ft >>>>>>>>> celcius : °C >>>>>>>>> fahrenheit : °F >>>>>>>>> kpa : kPa >>>>>>>>> pa : Pa >>>>>>>>> psi : psi >>>>>>>>> volts : V >>>>>>>>> amps : A >>>>>>>>> amphours : Ah >>>>>>>>> kw : kW >>>>>>>>> kwh : kWh >>>>>>>>> watts : W >>>>>>>>> watthours : Wh >>>>>>>>> seconds : Sec >>>>>>>>> minutes : Min >>>>>>>>> hours : Hour >>>>>>>>> utc : UTC >>>>>>>>> degrees : ° >>>>>>>>> kmph : km/h >>>>>>>>> miph : Mph >>>>>>>>> kmphps : km/h/s >>>>>>>>> miphps : Mph/s >>>>>>>>> mpss : m/s² >>>>>>>>> dbm : dBm >>>>>>>>> sq : sq >>>>>>>>> percent : % >>>>>>>>> whpkm : Wh/km >>>>>>>>> whpmi : Wh/mi >>>>>>>>> kwhp100km : kWh/100km >>>>>>>>> kmpkwh : km/kWh >>>>>>>>> mipkwh : mi/kWh >>>>>>>>> nm : Nm >>>>>>>>> >>>>>>>>> *OVMS# metric unit mi* >>>>>>>>> miles : M >>>>>>>>> >>>>>>>>> >>>>>>>>
OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
_______________________________________________ OvmsDev mailing listOvmsDev@lists.openvehicles.comhttp://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
participants (4)
-
Mark Webb-Johnson -
Michael Balzer -
Michael Geddes -
Patrick Stein