Accumulate a value that sometimes resets - pls check my code

I have a value that comes in from an api representing the number of kilometers a machine has driven. But sometimes, the mileage gets reset at the edge and starts again from zero. I need to continue to accumulate the value on my end through these resets.

Here is the (working) function node code:

// get the name of this unit
let name = msg.payload.name;

// set up the rest of the variables for this name
let total_kilometers = global.get(`${name}_total_kilometers`) || 0;
let new_kilometers = msg.payload.measurements.numeric["value.Total.Machine.Distance"].value || 0;
let prev_kilometers = global.get(`${name}_prev_kilometers`) || 0;

// if the new kilometers are greater than the previous, then the difference between them is added to the total
if (new_kilometers > prev_kilometers) {
    total_kilometers += new_kilometers - prev_kilometers;

    // otherwise if the new kilometers are less than previous, this indicates a reset has occurred
    // simply add the new kilometers to the running total  
} else if (new_kilometers < prev_kilometers) {
    total_kilometers += new_kilometers;

    // this probably doesn't need to be here, if the new kilometers and previous kilometers are the same, do nothing
} else if (new_kilometers === prev_kilometers) {
    total_kilometers = total_kilometers;
}

//node.warn("prev_kilometers: " + prev_kilometers);
//node.warn("new_kilometers: " + new_kilometers);
//node.warn("total_kilometers: " + total_kilometers);

// set the new kilometers as previous, ready for the next message
prev_kilometers = new_kilometers;

// write out the global variables to store them for next message
global.set(`${name}_prev_kilometers`, prev_kilometers);
global.set(`${name}_total_kilometers`, total_kilometers);

// pass on the message with the new running total as Total.Machine.Distance
msg.payload.measurements.numeric["value.Total.Machine.Distance"].value = total_kilometers;

return msg;

This seems to work for me. I pass on the result to influxDB and the correct accumulated mileage is displayed

Couple of questions:

  1. Can my code be simplified or made more efficient?

  2. What can I do to make my code more resilient?

  3. I sometimes get this error in debug:

"TypeError: Cannot read properties of undefined (reading 'value')"

This happens because some of the machines don't have this value (measurements.numeric["value.Total.Machine.Distance"].value). What do I need to add to my code to simply ignore those messages?

Thank you!

You need to add a if statement to check numeric exists and return null if it does not stoping the message from moving on.
eg

if(msg.payload.measurements.numeric){
   your code....
}else{
    msg = null;
}
return msg;

[edit] fixed typo's.

Interestingly, that does not seem to work for me. Here's what I tried:

if (msg.payload.measurements.numeric["value.Loaded_Hours"].value) {
    my code here;
    return msg;
} else {
    return null;
};

I still get the same errors in the debug
image

I also tried:

if (msg.payload.measurements.numeric["value.Loaded_Hours"].value != "undefined") {
    my code here;
    return msg;
} else {
    return null;
};

I experimented with:

if (msg.payload.measurements.numeric["value.Loaded_Hours"].value) {
    node.warn("value exists")
} else {
    node.warn("value does not exist")
};

Indeed this does correctly identify which messages the value does and does not exist for. So I'm just a bit confused as to why I would still get an undefined error, since that code segment should be being skipped entirely.

Is not referenced in your function node. we will need to see your incoming payload and any changes in you function code.

oops sorry about that, I have two of these functions, one for
msg.payload.measurements.numeric["value.Loaded_Hours"].value
and one for
msg.payload.measurements.numeric["value.Total.Machine.Distance"].value
the code is identical for both, and so are the errors.

Take careful note of the error, it says 'cannot read properties of undefined (reading value)' that means you have used, effectively undefined.value, so it is msg.payload.measurements.numeric["value.Loaded_Hours"] that you must test for undefined.

Good hint, I did test that and still got the errors. Now that I look more closely, the whole numeric object is empty:

How do I test properly for this condition?

Then my firist suggestion should work
you could also try adding a &&

if(msg.payload.measurements.numeric && msg.payload.measurements.numeric["value.Loaded_Hours"] ){
   your code....
}else{
    msg = null;
}
return msg;

There is a relatively new way to cope with such things in recent versions of javascript. It uses the ?. operator which can be used in expressions such as
a.b?.c?.d
That says that if b does not exist as a property of a or if c does not exist as a property of b then the whole expression returns undefined without generating an error. That can save a lot of testing for undefined. So in your case you could use

msg.payload.measurements?.numeric?.["value.Total.Machine.Distance"]?.value

and then test the result for undefined rather than test the indidual properties. Note that when used with the ["property"] you need ?.["property"] rather than just ?["property"] I am not sure why.
In your case if it is only ever the contents of numeric that are empty, but the rest will always be there (if numeric is there) then perhaps all you need is

msg.payload.measurements.numeric?.["value.Total.Machine.Distance"].value

I believe that optional chaining is available in nodejs 14 and above, but you should be using at least 14 nowadays anyway.

[Edit] Looking at what you posted, numeric is present but is empty so I think you want
msg.payload.measurements.numeric.["value.Total.Machine.Distance"]?.value

thank you Colin, I will try that!

Do you see anything crazy with the rest of my code? I'm so new to js and you guys are such pros, so I would love to learn anything I can.

Cheers @Colin and @E1cid

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.