It’s really fast but I don’t know we can make that assumption. One would have to review the Item Registry code to verify that is the case.
That might very well be the case. You might be able to test for that. Check to see if the Group is null before the findFirst line. If it is, that could be evidence that it is the problem.
Given your reasoning, the gDiffTemp would be the Group that would be changing while this Rule is running since you say gSetTemp doesn’t change. So focus on that Group.
The member of gActTemp definitely changed. But I suspect the state of gActTemp itself is still being calculated in the background when this Rule triggers. Also, the state of gDiffTemp is also potentially being recalculated from the previous run of this Rule, depending on how close together new ActTemps come in.
I think we need to gather more evidence before we file an issue, but if your guess is right, then there will likely need to be an issue filed. It will probably be a major pain to correct though so we should also look for a work-around.
This is also an assumption that we may not be able to make. We know it is not NULL at the time of checking it in the if statement. But the actual line where we use it occurs some milliseconds later and the state could have changed out from under us. It’s unlikely but if you have enough Items updating all at once it might occur, though rarely.
We can eliminate this possibility as well:
rule "calc diff temp"
when
Member of gActTemp changed
then
val actitem = triggeringItem // we don't really need to cast this to a NumberItem
val act = actitem.state
// skip the rule if the actitem changed to NULL
if(act == NULL)
{
logInfo("calc diff temp", "%s's state is NULL!", actitem.name)
return;
}
val setitem = gSetTemp.members.findFirst[ i | i.name == actitem.name.split("_").get(0) + "_SETTEMP" ]
// skip the rule if setitem or diffitem were not found in their respective Groups
if(setitem === null)
{
logInfo("calc diff temp", "Could not find setitem for %s", actitem.name)
return;
}
val set = setitem.state
if(set == NULL)
{
logInfo("calc diff temp", "%s's state is NULL!", setitem.name)
return;
}
val diffitem = gDiffTemp.members.findFirst[ i | i.name == actitem.name.split("_").get(0) + "_DIFFTEMP"]
if(diffitem == null)
{
logInfo("calc diff temp", "Could not find diffitem for %s", actitem.name)
return;
}
// at this point we have all three relevant Items and know their states are not NULL
var Number diff = (act as DecimalType) - (set as DecimalType)
diffitem.postUpdate(diff)
end
The big change is we save the state to a val and then only use the val in the Rule. This will prevent any changes taking place behind the scenes from impacting the Rule. Based on the observed behavior and your reasoning I don’t think this is the problem. But it will eliminate it as a possiblity.
I can think of a number of mitigations for the observed behavior but we need more info before I’ll know which one is most appropriate. But they include:
- Don’t use findFirst to get the Item out of gDiffTemp and instead use
postUpdate(actitem.name.split("_").get(0) + "_DIFFTEMP", diff)
. Since gDiffTemp is the only one that we think is causing problems, if we avoid trying to findFirst on the Group then the error should not appear. - Design Pattern: Gate Keeper, but it will require the members of gActTemp to receive a command, not just update.