[Ovmsdev] New Metric Units
Michael Geddes
frog at bunyip.wheelycreek.net
Mon Nov 28 07:36:54 HKT 2022
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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at webb-johnson.net> wrote:
>>>>>>>>
>>>>>>>>> Or perhaps something more specific?
>>>>>>>>>
>>>>>>>>> HasValue()
>>>>>>>>>
>>>>>>>>> Mark
>>>>>>>>>
>>>>>>>>> On 8 Nov 2022, at 9:01 PM, Michael Balzer <dexter at 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 at 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 at 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 at 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
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20221128/e6f8671f/attachment-0001.htm>
More information about the OvmsDev
mailing list