I’m trying to set up a bit of code to control a wifi webcam having some http api.
To factorize code, i created a lambda “moveCamera” that will be called from 4 rules , one for each direction.
For some reasons (basically, i show only snapshots on my panel, not a stream), i need to force a refresh of an Angular variable containing the snapshot url. This is done by either a timer, OR after moving my camera. Because i have those 2 use cases , i also put the code in a lambda called “updateCameraSnapshotUrl”
Now 1st question :
When moveCamera calls updateCameraSnapshotUrl, i see an error in the log :
[ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Camera controlR': cannot invoke method public abstract java.lang.Object org.eclipse.xtext.xbase.lib.Functions$Function0.apply() on null
so i end up duplicating the code rather than using my lambda.
My 2nd question : as you will see in below code, i used the ip+port of the camera between quotes, instead of an available global val. Because if a use the global val, its value is empty in the lambdas. Any idea why ?
Here is the whole rules code :
val String cameraHostPort = "192.168.0.44:80"
val Functions.Function0<void> updateCameraSnapshotUrl = [|
logInfo("CameraHost", cameraHostPort)
var url = "http://"+"192.168.0.44:80"+"/snapshot.cgi?user=admin&pwd=888888&_ts="
url += now.hashCode
postUpdate(CameraSnapshotUrl, url)
null
]
val Functions.Function1<Integer, void> moveCamera = [
Integer commandId |
sendHttpGetRequest("http://"+"192.168.0.44:80"+"/decoder_control.cgi?loginuse=admin&loginpas=888888&command="+(commandId)+"&onestep=0")
Thread::sleep(10)
sendHttpGetRequest("http://"+"192.168.0.44:80"+"/decoder_control.cgi?loginuse=admin&loginpas=888888&command="+(commandId+1)+"&onestep=0")
// Lambda from lambda does NOT work
//updateCameraSnapshotUrl.apply()
// ---> Rule 'Camera controlR': cannot invoke method public abstract java.lang.Object org.eclipse.xtext.xbase.lib.Functions$Function0.apply() on null
var url = "http://"+"192.168.0.44:80"+"/snapshot.cgi?user=admin&pwd=888888&_ts="
url += now.hashCode
postUpdate(CameraSnapshotUrl, url)
null
]
rule "Camera update"
when Time cron "/20 * * * * ?"
then
updateCameraSnapshotUrl.apply()
end
rule "Camera controlL"
when Item Webcam_Starcam_Control_Left received command
then
moveCamera.apply(4)
end
rule "Camera controlR"
when Item Webcam_Starcam_Control_Right received command
then
moveCamera.apply(6)
end
You can refer to lambda X inside lambda Y, but remember that everything external referred to in a lambda (except Items) must be passed into it.
You’ll need to add another parameter and pass in the name of the lambda function.
Examples (ignore the Timer stuff, the interest is passing one function to another)
For your string (IP) you have a choice of either passing it in as another parameter, or put it into a String Item where you can get at it.
Thanks guy. Both solutions of passing lambda as parameter , and a static string as an Item work fine.
Now i’d have the tendency to think that code is more heavy with this implementation.
It’s not good for encapsulation to have to give any implem details at the caller level. Maintenance effort will be far more costly. (Playing a little bit with Scala, i initially thought lambda could have access to other functions).
defining Items + init at startup + “xxx.state.toString” is full of boilerplate, especially if i want to do the same for username, password, and whatever else is needed at global level.
Anyway, it’s like that and it works, that’s the essential for now.