Web request event driven Lua code - lua

What i have seen and currently implemented in my Lua scripts running on my ESP8266 WiFi module, are polling the server page every few seconds and than checking whether the value has changed or not. If value has changed than take some action.
Here's my code
tmr.alarm(1,10000, 1, function()
if (wifi.sta.getip() == nil) then
print("IP unavaiable, Waiting...")
else
tmr.stop(1)
tmr.alarm(1,10000, 1, function()
event_driven_func()
end)
end
end)
function event_driven_func()
print("Inside event_driven_func function"..node.heap());
conn = nil
conn=net.createConnection(net.TCP,0)
conn:on("receive", function(conn, payload)
-- local buf = "";
startRead = false
Data = ""
print("payload : "..payload)
for i = 1, #payload do
c = payload:sub(i,i)
if (c=="{") then
startRead=true
end
if (startRead) then
Data=Data..c
end
if (c=="}") then
startRead=false
print("json string detected Do something now ...");
end
end
conn:close()
collectgarbage("collect");
end)
conn:connect(80,"my_server.co.in")
conn:on("connection", function(conn, payload)
conn:send("GET /xyz.php HTTP/1.0\r\nHost: my_server.co.in\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n")
end)
end
But this is highly inefficient.
How to implement or better a pseduo-code, a event driven code where whenever the value changes there's an interrupt to the code and then event_driven_func executes ?
Or any better efficient way.
Application example :
Say i've number of youtube subscribers showing on a server page, whenever the subscribers change it should make a "POST" web request to one of my json page xyz.php (this will be done using IFTTT ) and then a event will execute the function in Lua code so that value will be reflected.

Related

Why does GetAsync return nil?

I have tried to fix this script, but i cannot.
It is the primary script which maintains most server side stuff.
I tried to make an DataStore handler, but it returns nil.
Code:
local rep = game:GetService("ReplicatedStorage")
local DrawingData = game:GetService("DataStoreService"):GetDataStore("DrawingData")
rep.SendToRandom.OnServerEvent:Connect(function(plr,data)
end)
rep.LoadFromDatastore.OnServerInvoke = function(plr,...)
local success,gotten =pcall(DrawingData.SetAsync,DrawingData,...)
if not success then
gotten = {}
warn("Failure with DataStore get, replacing with placeholder.")
end
return gotten
end
rep.SaveToDatastore.OnServerInvoke =function(plr,data:string)
local a = {DrawingData:GetAsync("GM")}
local gotten = tonumber(a[1])
DrawingData:SetAsync(tostring(gotten + 1),data)
DrawingData:SetAsync("GM",tostring(gotten + 1))
return gotten + 1
end
Output:
Failure with DataStore get, replacing with placeholder.
Saving works, and loading should work.
But, loading doesn't.
Looks like you're using SetAsync and GetAsync without actual UserId to store it to, you should save and load the data to a UserId unless the data is meant to be accessed by everyone (which I do NOT recommend).
From what I can tell you are trying to save data for individual players, but you are storing their data or their data I suppose to a singular DataStore, which will only make it more difficult for the game to load once it is prompted to load again.
Here is what I wrote based on what I believe you are trying to accomplish
local rep = game:GetService("ReplicatedStorage")
local DrawingData = game:GetService("DataStoreService"):GetDataStore("DrawingData")
rep.LoadFromDatastore.OnServerInvoke = function(plr,...)
local success, response = pcall(function(...)
return DrawingData:SetAsync(plr.UserId, ...)
end, ...)
if success ~= true or type(response) ~= "table" or table.getn(response) == 0 then
return {}, type(response) == "string" and warn(response)
end
return response
end
rep.SaveToDatastore.OnServerInvoke =function(plr, data)
local success, response = pcall(function()
return DrawingData:GetAsync(plr.UserId)
end)
if success ~= true or type(response) ~= "table" or table.getn(response) == 0 then
response = {}
end
table.insert(response, data)
local success, response
while success ~= true do
success, response = pcall(function()
return DrawingData:SetAsync(plr.UserId, response)
end)
if success ~= true then
warn(response)
end
end
end

LUA using function variable outside of it

I am trying to edit a script in LUA but I couldn't get access of a local defined in a function
LUA Code
function getSafeMoney()
local SafeMoney = nil
QBCore.Functions.ExecuteSql(false, 'SELECT * FROM `moneysafes` WHERE `safe` = "mechanic"', function(result)
SafeMoney = json.decode(json.encode(result[1])).money;
end)
return SafeMoney
end
print(getSafeMoney())
Result :
nil
here's the sql function as sysdevs asked
QBCore.Functions.ExecuteSql = function(wait, query, cb)
local rtndata = {}
local waiting = true
exports['ghmattimysql']:execute(query, {}, function(data)
if cb ~= nil and wait == false then
cb(data)
end
rtndata = data
waiting = false
end)
if wait then
while waiting do
Citizen.Wait(5)
end
if cb ~= nil and wait == true then
cb(rtndata)
end
end
return rtndata
end
Your second function is not holding the main function I assume, I am bad at lua aswell, but I think that changing
QBCore.Functions.ExecuteSql(false, ............
to
QBCore.Functions.ExecuteSql(true, ............
This will probably fix your problem, still your code is ambiguous and needs concentration if you could provide more information I might be able to help more

Tarantool fiber behavior with fiber.yield() and fiber.testcancel()

I ran into an unexpected behavior while building Tarantool app based on fibers.
Simple reproducer of my code looks like this:
local log = require('log')
local fiber = require('fiber')
box.cfg{}
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
fiber.yield()
end
return 0
end
local wrapfunc = function()
local ok, resp = pcall(func)
log.info(ok)
log.info(resp)
end
for _ = 1, 100 do
local myfiber = fiber.create(wrapfunc)
fiber.sleep(0.02)
fiber.kill(myfiber)
end
and it prints to log false, fiber is cancelled. Moreover, if I use the following func:
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
pcall(fiber.yield)
end
return 0
end
it prints to log true, 1, and if I use
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
if pcall(fiber.yield) ~= true then
return 2
end
end
return 0
end
it prints to log true, 2.
I expected that after yielding from running myfiber, if control returns to the external fiber and it calls fiber.kill(myfiber), the next time control returns to the cancelled myfiber we will be in the end of cycle iteration and on the next iteration code will successfully return 1. However, the work of func ends with throwing error fiber is cancelled, not with return. So how the real life cycle of yielding fiber works?
Actually, there is no unexpected behaviour here. I believe it mostly documentation issue. Let me explain. I've simplified your example a bit:
#!/usr/bin/env tarantool
local fiber = require('fiber')
local f1 = function() fiber.yield() end
local f2 = function() pcall(fiber.yield) end
local func = function(fn)
fn()
if not pcall(fiber.testcancel) then
return 'fiber.testcancel() failed'
end
end
local fiber1 = fiber.create(function() print(pcall(func, f1)) end)
fiber.kill(fiber1)
local fiber2 = fiber.create(function() print(pcall(func, f2)) end)
fiber.kill(fiber2)
The output would be:
false fiber is cancelled
true fiber.testcancel() failed
When you call fiber.kill, fiber.yield() or fiber.sleep() just raises an error, so your fiber is not able to reach fiber.testcancel and just dies. When you do pcall(fiber.yield), you basically suppress this error and proceed. Then fiber.testcancel checks its fiber status and reraise an exception. But this is a silly example.
Now, with bigger chunks of code, when lots of function invocations involved, you usually want to catch those errors during yield, do some finalisation work and call fiber.testcancel() to promote error upwards (imagine multiple checks of this kind in different parts of big stacktrace). I believe it is the basic use case, fiber.testcancel was introduced for, besides discussions if its design is usable or not.
P.s. And yes, occasional exceptions of such yield calls are not documented. At least I could not find anything in the fiber page

HTTP GET in LUA/NodeMCU

I'm trying to send a HTTP GET each time I press a button :
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PWD")
function loop()
if wifi.sta.status() == 5 then
-- Stop the loop
tmr.stop(0)
else
print("Connecting...")
end
end
tmr.alarm(0, 100, 1, function() loop() end)
print(wifi.sta.getip())
outpin_led = 1
inpin_button = 3
gpio.mode(outpin_led,gpio.OUTPUT)
gpio.mode(inpin_button,gpio.INPUT)
light_on = false
function light()
if not light_on then
-- turn the light on
gpio.write(outpin_led,gpio.HIGH)
light_on = true
http.get("https://google.com", function(clt, data)
print(data)
end)
else
-- turn the light off
gpio.write(outpin_led,gpio.LOW)
light_on = false
end
end
gpio.trig(inpin_button,"down",light)
The line containing http.get is throwing this error message :
> PANIC: unprotected error in call to Lua API (stdin:6: attempt to index global 'http' (a nil value))
I made sure my NodeMCU build contained the http module by compiling it through http://nodemcu-build.com/
Any idea ?
Thanks.
As Marcel Stör pointed out, it was indeed an issue during the flashing of the firmware.
Thanks a lot for your reply and for your work on http://nodemcu-build.com/.

The wifi.sta module connects if a loop is running?

Im trying to detect when the module actually connects to my wifi AP, since .connect does not have a callback im doing something simple like this:
wifi.sta.config("SSID","password")
wifi.sta.connect()
tmr.delay(1000000)
i = 0
while(wifi.sta.status() ~= 5 and i < 10) do
print("Waiting")
print(wifi.sta.status())
i = i + 1
tmr.delay(1000000)
end
But the output of .sta.status() is always 1 inside the loop. When it finish, if I send the command =wifi.sta.status() manually from the IDE it tells me 5. Why?
If you use a recent dev firmware you can do something really event based :
wifi.setmode(wifi.STATION)
wifi.sta.config(SSID, PASSWORD)
function Success()
tmr.stop(0)
if (SERIAL_PRINT) then
print("IP: " .. wifi.sta.getip())
end
wifi.sta.eventMonStop()
wifi.sta.eventMonReg(wifi.STA_GOTIP, "unreg")
dofile("mainProgram.lua")
end
function Failure()
if (SERIAL_PRINT) then
print("Unable to connect")
end
wifi.sta.eventMonStop()
wifi.sta.eventMonReg(wifi.STA_GOTIP, "unreg")
return 0
end
tmr.alarm(0,30000,0, function() Failure() end)
wifi.sta.connect()
wifi.sta.eventMonReg(wifi.STA_GOTIP, function() Success() end)
wifi.sta.eventMonStart()
EDIT: Please have a look to the documentation for a list of all events. If you wish to use this code, you'll have to handle the failure more cleanly.
Using tmr.delay doesnot let run the event loop, you should use a timer callback.
Then the code could be something like :
wifi.sta.config("SSID","password")
wifi.sta.connect()
i=0
tmr.alarm(1, 1000, 1, function()
if (wifi.sta.status() ~= 5 and i < 10) then
print("Status:"..wifi.sta.status())
i = i + 1
else
tmr.stop(1)
if (wifi.sta.status() == 5) then
print("IP:"..wifi.sta.getip())
else
print("Status:"..wifi.sta.status())
end
end
end)

Resources