Fredmas Posté(e) le 22 juillet 2021 Auteur Signaler Posté(e) le 22 juillet 2021 Maybe I was not enough clear Yes I put your proposal intervalRunner (10, function () checkVariables (self) end) in onInit() instead of intervalRunner (10, checkVariables(self)). And it works very well to be able to call in my code QA variable. But putting your proposal intervalRunner (10, function () checkVariables (self) end) in onInit() instead of intervalRunner (10, checkVariables(self)), make the ":EXIT" not running anymore. When I put return ":EXIT" at the end of my code, intervalRunner ignore and continue to run and never stop. It seems like if fun()==":EXIT" or ref[1]==nil then return end doens't find anymore the return ":EXIT" coming from my code when I put intervalRunner (10, function () checkVariables (self) end) in onInit() instead of intervalRunner (10, checkVariables(self))
jang Posté(e) le 22 juillet 2021 Signaler Posté(e) le 22 juillet 2021 il y a 19 minutes, Fredmas a dit : Maybe I was not enough clear Yes I put your proposal intervalRunner (10, function () checkVariables (self) end) in onInit () instead of intervalRunner (10, checkVariables (self)) . And it works very well to be able to call in my code QA variable. But putting your proposal intervalRunner (10, function () checkVariables (self) end) in onInit () instead of intervalRunner (10, checkVariables (self)) , make the ": EXIT" not running anymore. When I put return ": EXIT" at the end of my code, intervalRunner ignore and continue to run and never stop. It seems like if fun () == ": EXIT" or ref [1] == nil then return end doens't find anymore the return ": EXIT" coming from my code when I put intervalRunner (10, function () checkVariables (self) end) in onInit () instead of intervalRunner (10, checkVariables (self)) Sorry, my bad. Solution is intervalRunner (10, function () return checkVariables (self) end)
jang Posté(e) le 22 juillet 2021 Signaler Posté(e) le 22 juillet 2021 We could do intervalRunner a little better function intervalRunner(seconds,fun,...) local args,nxt,ref={...},nil,{} local function loop() if fun(table.unpack(args))==':EXIT' or ref[1]==nil then return end nxt=nxt+seconds ref[1] = setTimeout(loop,1000*(nxt-os.time())) end local t = ((os.time()-3600) // seconds) * seconds nxt=t+seconds+3600 ref[1] = setTimeout(loop,1000*(nxt-os.time()-(seconds > 3600 and 3600 or 0))) return ref end Then we can do intervalRunner (10, checkVariables, self)
Fredmas Posté(e) le 22 juillet 2021 Auteur Signaler Posté(e) le 22 juillet 2021 il y a 16 minutes, jang a dit : Sorry, my bad. Solution is intervalRunner (10, function () return checkVariables (self) end) Perfect. I didn't think just to put "return" in the line in onInit Not so easy to learn from zero I understood and it works.
Fredmas Posté(e) le 22 juillet 2021 Auteur Signaler Posté(e) le 22 juillet 2021 il y a 12 minutes, jang a dit : We could do intervalRunner a little better function intervalRunner(seconds,fun,...) local args,nxt,ref={...},nil,{} local function loop() if fun(table.unpack(args))==':EXIT' or ref[1]==nil then return end nxt=nxt+seconds ref[1] = setTimeout(loop,1000*(nxt-os.time())) end local t = ((os.time()-3600) // seconds) * seconds nxt=t+seconds+3600 ref[1] = setTimeout(loop,1000*(nxt-os.time()-(seconds > 3600 and 3600 or 0))) return ref end Then we can do intervalRunner (10, checkVariables, self) sounds good but new for me, even if I already read weeks ago something about "unpack" here. But I will go step by step, as now I understood the way to code the previous version And I keep it in my notes for a next step
Fredmas Posté(e) le 24 juillet 2021 Auteur Signaler Posté(e) le 24 juillet 2021 Thanks a lot @jang and @Lazer I understood and learnt a lot about starting with QA and about the way to loop Everything is now running properly in the loop, and I continue to code my main function in this QA instead of scenes. The only thing I didn't succeed to put in place is the function stopIntervalRunner. Le 03/07/2021 à 11:19, jang a dit : You can also stop it with calling stopIntervalRunner(ref) with the ref that intervalRunner returns. I didn't succeed to understand where and how to call it. I tryed to write this function like that: function stopIntervalRunner(ref) if ref[1] then clearTimeout(ref[1]) ref[1]=nil end end Then I tryed to call it from my main code (or somewhere else) for a test, but I didn't find how and what to put for calling stopIntevalRunner(ref). I understood that you said: "with the ref that intervalRunner returns". But I probably failed to catch the ref returned by intervalRunner() But anyway, at the end I put in place another solution with a simple button like below, in case I need to stop intervalRunner, and it works good: function QuickApp:button2() if self:getVariable("QAOnOff") == "on" then self:setVariable("QAOnOff", "off") self:updateView("button2", "text", "QA is OFF. Pusch to switch on") else self:setVariable("QAOnOff", "on") self:updateView("button2", "text", "QA is ON. Push to switch off") intervalRunner(60, function() return mainCode(self) end) end end It is not perfect because I wrote here again the same line intervalRunner(60, function() return mainCode(self) end), duplicated from onInit(). Not good when we want to change the value and for the maintenance, but I will probably never change it, or not so often And in the main code I put at the beginning: function mainCode(self) if self:getVariable("QAOnOff") == "off" then print("QA is off") return ":EXIT" end blablabla end Have a good day, Bonne journée, Fred
jang Posté(e) le 24 juillet 2021 Signaler Posté(e) le 24 juillet 2021 function intervalRunner(seconds,fun,...) local args,nxt,ref={...},nil,{} local function loop() if fun(table.unpack(args))==':EXIT' or ref[1]==nil then return end nxt=nxt+seconds ref[1] = setTimeout(loop,1000*(nxt-os.time())) end local t = ((os.time()-3600) // seconds) * seconds nxt=t+seconds+3600 ref[1] = setTimeout(loop,1000*(nxt-os.time()-(seconds > 3600 and 3600 or 0))) return ref end function stopIntervalRunner(ref) if ref[1] then clearTimeout(ref[1]) ref[1]=nil end end function QuickApp:onInit() local ref,i=nil,0 ref = intervalRunner(2,function() i=i+1 print(i) if i == 5 then stopIntervalRunner(ref) end end) end
Fredmas Posté(e) le 24 juillet 2021 Auteur Signaler Posté(e) le 24 juillet 2021 You are too strong and fast for me. Thanks I didn't think to put ref in onInit() Updating your proposal to my QA, like below it seems to work simply for me --to initialize the QA function QuickApp:onInit() self:debug("onInit") ref = intervalRunner(2, function() return mainCode(self) end) end --to stop the QA function QuickApp:button3() stopIntervalRunner(ref) end function stopIntervalRunner(ref) if ref[1] then clearTimeout(ref[1]) ref[1]=nil end end --to loop the main code of this QA function intervalRunner(seconds,fun) ... end --the main code to run from this QA function mainCode(self) print("Hereafter the main code to run") end
jang Posté(e) le 24 juillet 2021 Signaler Posté(e) le 24 juillet 2021 You could make the button toggle intervalRunner --to initialize the QA function QuickApp:onInit() self:debug("onInit") ref={} -- Start running self:button3() end --to toggle running the QA function QuickApp:button3() if ref[1] then -- Running stopIntervalRunner(ref) self:updateView("<button3 id>","text","Halted") else ref = intervalRunner(2, function() return mainCode(self) end) self:updateView("<button3 id>","text","Running") end end "<button3 id>" should be the id of the button. 1
Fredmas Posté(e) le 24 juillet 2021 Auteur Signaler Posté(e) le 24 juillet 2021 Yes I like it It was something like that about what I was thinking drinking my afternoon coffee But I am not sure that I would think so fast: 1. to move the call of intervalRunner() from onInit() to a QuickApp object like this button3(), called itself by onInit() 2. to define ref as a table and not a value in onInit() But reading your proposal I understand the good idea and how it works Much more simple and easier to maintain than my previous way to do with button, variable and return ":EXIT"
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 (modifié) #3 Question 3 : définir correctement une fonction comme membre du QA J'ai compris que pour qu'une fonction prenne des commandes self par exemple, et donc utilise les méthodes du QA, cette fonction doit être membre du QuickApp (ajoutée au QuickApp class). Cependant à la lecture des sujets et des manuels Fibaro, on constate plusieurs types d'écriture et manières de déclarer une fonction pour qu'elle soit membre du QA, en tout cas j'en ai retenu au moins 2. Après avoir réalisé quelques essais simples, les 2 manières décrites ci-dessous fonctionnent, donc j'imagine que la subtilité se situe peut-être davantage au niveau de certaines utilisations des paramètres et des arguments. Afin d'éclaircir les codeurs découvrant le monde des QA, quelqu'un saurait-il expliquer qu'elles sont les différences entre les 2 écritures suivantes Ecriture #1 function onInit() self:debug("onInit") test(self) end function test(self) self:debug("hello") end Ecriture #2 function onInit() self:debug("onInit") self:test() end function QuickApp:test() self:debug("hello") end p.s. : petit hors sujet, pourquoi dans la plupart des QA que nous trouvons, le onInit est très souvent rédigé à la fin du code ? J'ai plutôt tendance à l'écrire au début, est-ce une erreur ? Modifié le 25 juillet 2021 par Fredmas
Lazer Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 Tes 2 écritures sont totalement différentes. Dans la 1ère, la fonction test() n'est pas membre de la classe QuickApp. Elle est globale, c'est à dire qu'elle est accessible dans tous les fichiers LUA du QA. Mais vu qu'elle n'est pas membre de QuickApp, alors elle n'est pas accessible depuis l'extérieur (depuis un autre QA ou scène avec fibaro.call() ou via l'API HTTP) Dans la 2nde, la fonction test() est bien membre de la classe QuickApp. A titre de "best practices", je dirais que les fonctions qui doivent être publiées, c'est à dire accessibles depuis les autres QA, doivent être membre de QuickApp, tandis que les autres fonctions doivent être locales (donc avec le passage de self en paramètres si nécessaire). Pour le QuickApp:onInit(), tu le mets où tu veux, c'est selon les préférences. Souvent en codage, on retrouver les 1ère fonctions en bas du code, parce que lorsqu'elle sont exécutées, les autres fonctions ont déjà été définies préalablement, donc elles sont connues au moment de l'exécution. Exemple : function QuickApp:onInit() hello() -- Erreur : la fonction hello() n'a pas encore été définie end local function hello() print("world") end Il y a 2 façons de contourner le problème : Mettre onInit() à la fin du code : local function hello() print("world") end function QuickApp:onInit() hello() -- OK end déclarer la fonction au début du code, et la définir plus loin : local hello function QuickApp:onInit() hello() -- OK end hello = function() print("world") end 1
Barelle Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 En lua la seconde forme est inutile, on peut déclarer dans l'ordre que l'on veut. La notion historique de FORWARD du Pascal n'est pas nécessaire.
Lazer Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 Tu es certain ? J'avais pris cette habitude, car il me semble que dans mes tests ça ne fonctionnait pas sinon.
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 il y a 21 minutes, Lazer a dit : Tes 2 écritures sont totalement différentes. Dans la 1ère, la fonction test() n'est pas membre de la classe QuickApp. Elle est globale, c'est à dire qu'elle est accessible dans tous les fichiers LUA du QA. Mais vu qu'elle n'est pas membre de QuickApp, alors elle n'est pas accessible depuis l'extérieur (depuis un autre QA ou scène avec fibaro.call() ou via l'API HTTP) Dans la 2nde, la fonction test() est bien membre de la classe QuickApp. A titre de "best practices", je dirais que les fonctions qui doivent être publiées, c'est à dire accessibles depuis les autres QA, doivent être membre de QuickApp, tandis que les autres fonctions doivent être locales (donc avec le passage de self en paramètres). Merci @Lazer ton explication est claire, et je comprends mieux les 2 écritures et leur utilité. Je vais relire mes codes en cours Du coup, pour vérifier si j'ai bien compris, j'ai pour habitude (et déformation professionnelle) de reformuler avec mes mots, et ça donne : - la première écriture est locale au QA concerné (tu as écrit globale dans ta phrase alors j'ai le doute), donc accessible uniquement dans le QA concerné et tous ses fichiers, mais pas en dehors de ce même QA. - la deuxième écriture est globale, donc utilise de la mémoire, mais la fonction est accessible en dehors du QA concerné si on l'appelle. Ai-je bon ? D'ailleurs, j'étais en train de réfléchir à justement écrire dans main tout ce qui concerne onInit, boutons, labels, boucles, etc., et créer un fichier pour ce que j'appelle le code fonctionnel, celui qui fait le boulot attendu Et ceci uniquement à fin de fluidifier la lecture du QA. Bonne ou mauvaise idée ?
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 Pour le onInit et sa position dans le code, merci pour les réponses. J'ai pris pour habitude de faire comme la deuxième méthode décrite par @Lazer, mais je comprends complètement l'intérêt de : il y a 32 minutes, Lazer a dit : Souvent en codage, on retrouver les 1ère fonctions en bas du code, parce que lorsqu'elle sont exécutées, les autres fonctions ont déjà été définies préalablement, donc elles sont connues au moment de l'exécution. Mais du coup, tout en écoutant attentivement, je vous laisse débattre au coin du comptoir de l'historique du FORWARD Pas à propos de Pascal (@mprinfo) mais bien des origines du Pascal. Bon ok je sors...
Lazer Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 Je comprends ton besoin de reformuler, mais je t'invite à oublier l'utilisation que tu fais des termes "local" et "global", car ils prêtent à confusion car ce sont des termes qui existent en LUA. Bref, tu as parfaitement compris la portée de la fonction hello() au sein du QA ou en dehors, mais ta formulation des fausse
Lazer Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 (modifié) Reprenons : -- Cette fonction est membre de la classe QuickApp. Elle est donc automatiquement publiée et accessible depuis l extérieur du QA function QuickApp:test() self:debug("hello") end -- Cette fonction est "globale" (par défaut), c'est à dire accessible dans tous les fichiers du QA : function test(self) self:debug("hello") end -- Cette fonction est locale (car spécifié) donc accessible uniquement dans le fichier en cours (ou bloc de code en cours si la fonction a été définie à l'intérieur d'une autre fonction/boucle/etc) : local function test(self) self:debug("hello") end J'ajoute que l'utilisation des variables globales (et une fonction est en quelque sorte une variable au sens LUA) est à éviter autant que possible, car elles pourraient entrer en collision avec d'autres variables nommées à l'identique dans d'autres parties du code. Une bonne pratique est de limiter la portée des variables à leur strict nécessaire, et à les passer en paramètres des fonctions quand nécessaire. Cela devient de plus en plus important à mesure que notre code grossit, et qu'on réutilise des bouts de codes dans d'autres développements. Un autre impact a lieu également sur les performances, car les variables globales elles sont rangées dans une super table globale nommée _G que l'interpréteur LUA doit parcourir à chaque fois qu'on fait appelle à ladite variable (ou fonction). C'est par conséquent plus lent que l'utilisation d'une variable (fonction) locale. Pour une variable qui est appelée une fois par minute, ça ne change rien. Pour une boucle qui le ferait plusieurs fois par secondes, la différence devient sensible. Rappel : Modifié le 25 juillet 2021 par Lazer
jang Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 QuickApp:onInit() is run after the whole file is loaded so it can be anywhere. Fibaro does something like... loadFile("QA file") -- Everything in the file is run - all functions and globals are defined (also locals on "top level"). local self = QuickApp() -- Now all methods are defined, create an instance if self.onInit() then self:onInit() end -- If the user declared an :onInit call it quickApp = qa -- Set the globaö 'quickApp' to the current QuickApp self. So :onInit() is called when everything is defined. Also note that Fibaro sets the global 'quickApp' to self after :onInit() is done. This means that we can use quickApp instead of passing around self to local functions if we are careful. It may not work if we deal with QuickAppChild instances as we then have many instances of "QuickApp" that we pass around... And yes, we need to "forward" declare local variables sometimes. This doesn't work local function bar(x) if x > 0 then foo(x-1) else print('done') end local function foo(x) bar(x-1) end foo(5) This work local foo local function bar(x) if x > 0 then foo(x-1) else print('done') end function foo(x) bar(x-1) end foo(5) The reason is that when bar is defined/compiled, 'foo' is assumed to be a global variable as it has not been mentioned/seen earlier. In the second example 'foo' is recognized as a local variable (even if it at the moment doesn't have a value) 1
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 (modifié) il y a une heure, Lazer a dit : Je comprends ton besoin de reformuler, mais je t'invite à oublier l'utilisation que tu fais des termes "local" et "global", car ils prêtent à confusion car ce sont des termes qui existent en LUA. Bref, tu as parfaitement compris la portée de la fonction hello() au sein du QA ou en dehors, mais ta formulation des fausse Le fait que je reformule n'est pas si mauvais, dans le sens où tu as certainement mis le doigt sur une belle incompréhension de ma part, puisqu'en écrivant ces mots, j'avais bien en tête globale et locale au sens variables LUA dans les scènes... Du coup je vais te relire et me relire pour vérifier ma compréhension... il y a une heure, Lazer a dit : Reprenons : Merci beaucoup, car je n'avais effectivement pas entièrement compris à 100% il y a une heure, Lazer a dit : J'ajoute que l'utilisation des variables globales (et une fonction est en quelque sorte une variable au sens LUA) est à éviter autant que possible, car elles pourraient entrer en collision avec d'autres variables nommées à l'identique dans d'autres parties du code. Une bonne pratique est de limiter la portée des variables à leur strict nécessaire, et à les passer en paramètres des fonctions quand nécessaire. Cela devient de plus en plus important à mesure que notre code grossit, et qu'on réutilise des bouts de codes dans d'autres développements. Un autre impact a lieu également sur les performances, car les variables globales elles sont rangées dans une super table globale nommée _G que l'interpréteur LUA doit parcourir à chaque fois qu'on fait appelle à ladite variable (ou fonction). C'est par conséquent plus lent que l'utilisation d'une variable (fonction) locale. Pour une variable qui est appelée une fois par minute, ça ne change rien. Pour une boucle qui le ferait plusieurs fois par secondes, la différence devient sensible. Merci, car c'est effectivement une question qui me trotte dans la tête. Grâce à mes petits débuts en QA je suis en train de supprimer mes variables globales qui étaient utilisées dans les scènes. Mais du coup, que penser des variables non spécifiées "local" dans le code du QA, mais dans l'onglet "variable" du QA ? Si j'ai bien compris elles ne sont pas globales dans le sens LUA/Fibaro, mais d'un point de vue calcul et lenteur dans le QA comparé aux variables locales dans le code du QA ? Modifié le 25 juillet 2021 par Fredmas
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 Thanks @jang I thing I understood your main explanation. The main thing not cristal clear for me is this sentence below and risks. Probably because I am not using children yet... I am too young in QA il y a 38 minutes, jang a dit : Also note that Fibaro sets the global 'quickApp' to self after :onInit() is done. This means that we can use quickApp instead of passing around self to local functions if we are careful. It may not work if we deal with QuickAppChild instances as we then have many instances of "QuickApp" that we pass around...
Barelle Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 (modifié) @Lazer https://stackoverflow.com/questions/6067369/forward-define-a-function-in-lua <edit> Pour illustrer, to QA Synology Surveillance Station appelle la fonction loop déclarée après onInit... </edit> Modifié le 25 juillet 2021 par Barelle
jang Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 local n = 10000000 -- loop 10 million times... local a = 1 -- our local variable b = 1 -- our global variable local t0 = os.clock() for i=1,n do a=a+1 end local t1 = os.clock()-t0 local t2 = os.clock() for i=1,n do b=b+1 end local t3 = os.clock()-t2 print("Local assignment ",n," times = ",t1,"s") print("Global assignment ",n," times = ",t3,"s") print("You saved ",t3-t1," seconds of your life") [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: Local assignment 10000000 times = 0.579324 s [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: Global assignment 10000000 times = 2.69236 s [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: You saved 2.113036 seconds of your life The HC3 is (or rather Lua is) quite fast. 1
jang Posté(e) le 25 juillet 2021 Signaler Posté(e) le 25 juillet 2021 (modifié) il y a 6 minutes, Fredmas a dit : Thanks @jang I thing I understood your main explanation. The main thing not cristal clear for me is this sentence below and risks. Probably because I am not using children yet... I am too young in QA Yes, for QuickAppChildren you first need a male QA and a female QA... Modifié le 25 juillet 2021 par jang 2
Fredmas Posté(e) le 25 juillet 2021 Auteur Signaler Posté(e) le 25 juillet 2021 il y a 2 minutes, jang a dit : [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: Local assignment 10000000 times = 0.579324 s [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: Global assignment 10000000 times = 2.69236 s [25.07.2021] [14:23:34] [DEBUG] [QUICKAPP400]: You saved 2.113036 seconds of your life The HC3 is (or rather Lua is) quite fast. thank you for the demonstration @jang il y a 3 minutes, jang a dit : Yes, for QuickAppChildren you need a male QA and a female QA... Unfortunately until now as I didn't investigate enough, Child in QA is close to be unknown for me. I will see later, even if I start to understand how it can be usefull for some application. But I will try to keep in mind you advise linn to: il y a 55 minutes, jang a dit : This means that we can use quickApp instead of passing around self to local functions if we are careful.
Messages recommandés