eclipse-archived / smarthome

Eclipse SmartHome™ project
https://www.eclipse.org/smarthome/
Eclipse Public License 2.0
862 stars 784 forks source link

[Rules] File-level var/val Declarations and Initialization can leave null Values Unexpectedly #3162

Open jeffsf opened 7 years ago

jeffsf commented 7 years ago

At least as far as I have seen, and no matter the choices of val vs. var here

val Number x = 1
val Number y = x

Observed behavior -- a null value for y Expected behavior -- y would have the value 1

This is certainly unexpected, especially as val is supposed to be immutable.

See follow-on post for example code to explore all the combinations.

Use case is single-point initialization of multiple variables to the same object (a hash map, in my particular case, potentially a Procedure in the future) to provide better readability of rules along with their robust maintenance and extensibility. Along the lines of...

val <someType> preset_night_front = newSomeType(lots, of, stuff)
val <someType> preset_night_back = newSomeType(lots, of, stuff)
val <someType> preset_night_all = newSomeType(lots, of, stuff)
[...]
val <someType> remote_A_button_2_preset = preset_night_front
val <someType> remote_B_button_2_preset = preset_night_back
val <someType> remote_C_button_2_preset = preset_night_front
val <someType> remote_D_button_2_preset = preset_night_all
val <someType> remote_F_button_2_preset = preset_night_all
[...]
jeffsf commented 7 years ago

Simple .rules file to view if the identifiers return null or not

In my experience all of the val/var initialized from a previously declared variable are null

I haven't checked to see if there is a multiple-assignment syntax. It's better than nothing, but far from as maintainable as being able to call assignment each out in its own line.

Pick your favorite trigger...

val Number val_second_arg = 1

var Number var_second_arg = 2

val Number val_val_second_arg = val_second_arg

var Number var_val_second_arg = val_second_arg

val Number val_var_second_arg = var_second_arg

var Number var_var_second_arg = var_second_arg

rule "Test_Pico_Button_Up"
when
    Item pico_Test_Pico_up changed to ON
then
    if ( val_second_arg != null ) {
        logInfo("null_check", "val_second_arg is not null")
    } else {
        logInfo("null_check", "val_second_arg is NULL")
    }

    if ( var_second_arg != null ) {
        logInfo("null_check", "var_second_arg is not null")
    } else {
        logInfo("null_check", "var_second_arg is NULL")
    }

    if ( val_val_second_arg != null ) {
        logInfo("null_check", "val_val_second_arg is not null")
    } else {
        logInfo("null_check", "val_val_second_arg is NULL")
    }

    if ( var_val_second_arg != null ) {
        logInfo("null_check", "var_val_second_arg is not null")
    } else {
        logInfo("null_check", "var_val_second_arg is NULL")
    }

    if ( val_var_second_arg != null ) {
        logInfo("null_check", "val_var_second_arg is not null")
    } else {
        logInfo("null_check", "val_var_second_arg is NULL")
    }

    if ( var_var_second_arg != null ) {
        logInfo("null_check", "var_var_second_arg is not null")
    } else {
        logInfo("null_check", "var_var_second_arg is NULL")
    }
end
Rossko57 commented 7 years ago

A similar exercise in OpenHAB-1 produces an more enilghtening error message

var Number xxx = 1
var Number yyy = xxx

message -
2017-03-20 20:59:19.453 [WARN ] [m.r.i.engine.RuleContextHelper] - Variable 'yyy'
 on rule file 'general.rules' cannot be initialized with value 'xxx': An error occured during the script execution: The name 'xxx' cannot be resolved to an item or type.
jeffsf commented 7 years ago

Seemingly related is the failure of creating a constant to be later within a lambda (it gets used more than once in the complete file)

val delay_incdec =  10
val delay_stop   =  10

val dimmer_group_send_increase = [ GroupItem dimmer_group |
    dimmer_group.allMembers.forEach [ dimmer |
        sendCommand(dimmer, INCREASE)
        Thread::sleep(delay_incdec)
    ]
]
2017-03-21 22:00:17.526 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Vacuum_Packer_Button_Up_Down_released': Could not invoke method: java.lang.Thread.sleep(long) on instance: null
Rossko57 commented 7 years ago

It probably is related but it is "well known" that global variables are not visible in a global lambda https://community.openhab.org/t/is-there-a-way-to-create-functions-routines-in-openhab/3126 Quote "Global lambda have no access to variables or items so you have to pass everything you want to reference to it " https://community.openhab.org/t/reusable-functions-a-simple-lambda-example-with-copious-notes/15888

My guess is that global-within-a-file 'objects' cannot see their peers, only blocks within a file see all their 'parent generation'