Error in receiving data from server - lua

I want to make my test function below print out the message "k isn't nil" but my code doesn't work. It has already received k value from my server but it doesn't check the line if k~=nil then. Below is my code. Thanks for any incoming advice.
local function receiveData( )
local l,e = client:receive()
if l~=nil then
print(l)
return l,e
else
timer.performWithDelay(100,receiveData)
end
end
function test( )
k = receiveData()
if k ~=nil then
print("k isn't nil")
end
end
test()

The problem is that if the data is not received on the first try then k is nil and test returns. The receiveData will be called again at 100 millisecond intervals until data is received, but the return is discarded by performWithDelay and by then test has returned (see first sentence of this answer).
The solution is to set a callback that receiveData can call when the data eventually arrives. The callback can then process the data. Replace return l,e by onReceiveData(l,e) and have that do something that test waits for in a while loop. Of course receiveData could directly set this flag being watched by test but once your app gets larger it is a good idea to separate receive from process.
function receiveData()
...
-- then:
local data = nil
function onReceiveData(l,e)
data = l
print('ready to process data', data, e)
end
funtion test()
receiveData()
while data == nil do sleep(100) end
print('data received and processed')
end
test()
where sleep(100) is what you can come up with since there is no builtin function that does that in Lua or even Corona (although Corona has system.getTimer() which returns ms since app start, so you could have
function sleep(ms)
local start = system.getTimer()
while system.getTimer() - start < ms do
end
end
I'm not too keen on the empty while loop but in a test utility function it is OK. If you are using the socket library it has a sleep function -- check out the Lua wiki for other options).

Are you sure you received the data? What does your program print in the console?
You may consider the following modification
local function receiveData( )
local l,e = client:receive()
if l~=nil then
print(l)
return l,e
else
timer.performWithDelay(100,function() l, e = receiveData() end)
end
return l, e
end
So my guess is, that when receiveData gets called second time, your return values (l, e) are discarded (because performWithDelay doesn't do anything with them).

Related

Lua process vararg in function process only 1st parameter

I'm trying to get a bit tricky logging, but can't get why ... processing only 1st parameter in a function called
I have this function
local logger = function (name, ...)
-- Expected table processing here, but no.
print("[" .. name .. "] log called with " .. ...)
end
return setmetatable({}, {__index = function(self, name)
local log = function(...)
return logger(name, ...)
end
self[name] = log
return log
end})
And how it's called
local testf = require "test_log"["TestA"]
testf("TestB", "TestC")
testf("TestC", "TestB")
But getting back this result
[TestA] log called with TestB
[TestA] log called with TestC
The problem I can't get 2nd (and further) parameters from testf function and can't get why.
Thanks in advance!
Your code uses only first parameter
local s = ''
for i=1,select('#',...) do s = s .. select(i, ...) end
print("[" .. name .. "] log called with " .. s)
Also, you can use s = table.concat({...}), but it will produce different results in cases where vararg contains nil values
You can't concatenate ... because it's not a value. Instead, Lua just takes the first value of the list.
If you want to concatenate more than one value, use table.concat first:
local concatenated = table.concat({...})
You could also do something like this instead if you're feeling particularly smart today:
local logger = function (...)
print(string.format("[%s] log called with"..string.rep(" %s", select("#")), ...))
end

is there a fix for Nil value for local user

This is for a phone script ive been working on and im trying to update some code but I have been getting a single error.
Error #phone/server.lua:37: attempt to index a nil value (local 'user')
ref (#phone/server.lua:37)
handler (#framework/server/main.lua:242)
getNumberPhone (#phone/server.lua:36)
handler (#phone/server.lua:268)
I tried the one way I was shown here before with no luck.
line 37
function getNumberPhone(source, n)
local n = 0
TriggerEvent('f:getPlayer', source, function(user)
n = user.getPhoneNumber()
end)
return n
end
line 242
AddEventHandler("f:getPlayer", function(user, cb)
if not cb then return end
if(Users)then
if(Users[user])then
cb(Users[user])
else
cb(nil)
end
else
cb(nil)
end
end)
line 36
function getNumberPhone(source, n)
local n = 0
TriggerEvent('f:getPlayer', source, function(user)
n = user.getPhoneNumber()
end)
return n
end
line 286
RegisterServerEvent('gcPhone:allUpdate')
AddEventHandler('gcPhone:allUpdate', function()
local source = source
local identifier = GetPlayerIdentifiers(source)[1]
TriggerClientEvent("gcPhone:myPhoneNumber", source, getNumberPhone(source))
TriggerClientEvent("gcPhone:allMessage", source, getMessages(identifier,source))
TriggerClientEvent("gcPhone:contactList", source, getContacts(identifier))
end)
Explaining the Error
In Lua, this is one of the most common errors you will run into, so it is very important for you to know how to solve it.
The error: attempt to index a nil value
To understand what this error means, you only need to understand these concepts.
In Lua, only tables can be indexed (i.e. myTable[myIndex])
In Lua, if a variable evaluates to nil, then attempting to index it as if it were a table throws an error
So it should be a little easier to understand the error you received. A more explicit way to describe this error would be something like "the Lua interpreter attempted to index your variable user on line 37 but user evaluated to nil."
Your Specific Case
On line 242 you are calling a callback and passing nil
cb(nil)
This callback sends this nil value to line 37 as the user parameter
TriggerEvent('f:getPlayer', source, function(user)
n = user.getPhoneNumber()
end)
So when you try to run user.getPhoneNumber() you are actually running nil.getPhoneNumber(), which throws the error you saw.
Ways to fix this type of error
1) Whenever you are working with a variable that could be nil, create an if statement checking to see if it is nil before proceeding.
2) Make sure you never set that variable to nil.
Ways to fix your specific error
1) On line 36, create this if statement
TriggerEvent('f:getPlayer', source, function(user)
if user ~= nil then
n = user.getPhoneNumber()
end
end)
Or do a similar nil check like this
TriggerEvent('f:getPlayer', source, function(user)
if user then
n = user.getPhoneNumber()
end
end)
2) Always pass a user in your callback. For example, on line 242 and elsewhere, pass an actual user object.
cb(Users[someUser])

Calling function from other file produces runtime error

I'm using Corona SDK for the first time and read up on how to call functions from other files, but I seem to be having issues. Here are the two scripts so far:
timer.lua
local M = {}
function M.Timer(n, count) --(period, how many times repeated)
if count > 0 then
local iter= os.time()+n
while iter ~= os.time() do
end
M.onTime(count)
count = count - 1
M.Timer(n,count)
end
end
function M.onTime(count)
display.newtext(count,250,50,native.systemFont,16)
end
return M
main.lua
local timeTool = require("timer")
timeTool.Timer(1,5)
They are located in the same directory. When I run main.lua on the simulator, I get the error attempt to call field 'Timer' (a nil value). This leads me to believe that the main file failed in acquiring the contents of the timer script, but from what I've seen, I am using the correct syntax. Is there something I missed, or am I using the wrong method for calling functions from other scripts?

Lua FIFO / QUEUE file

I'm quite new to Lua and Embedded programming. I'm working on a project:
IoT node that can be abstractedinto two parts: sensor and a board that runs Open WRT with Lua 5.1. I'm writing script that would be called from crontab every minute.
In my script I'm accessing data from the sensor via package written with C. The result of reading data from sensor is 'hexadecimal numbers returned in string:
4169999a4180cccd41c9851f424847ae4508e0003ddb22d141700000418e666641c87ae14248147b450800003dc8b439
Then convert it (string) to values I need and POST it to API.
Problem:
Sometimes API is not reachable due to poor network connection.
So I need to implement system where I would read a data from a sensor and then if API is not responding, I would save it to a FIFO queue (buffer). And then next time when script is called to read it would be sending 'old' records first and the newest one and the end.
local queue_filespec = [[/path/to/your/queue/file]]
-- Initially your "queue file" (regular file!) must contain single line:
-- return {}
local function operation_with_queue(func)
local queue = dofile(queue_filespec)
local result = func(queue)
for k, v in ipairs(queue) do
queue[k] = ("%q,\n"):format(v)
end
table.insert(queue, "}\n")
queue[0] = "return {\n"
queue = table.concat(queue, "", 0)
local f = assert(io.open(queue_filespec, "w"))
f:write(queue)
f:close()
return result
end
function add_to_queue(some_data)
operation_with_queue(
function(queue)
table.insert(queue, some_data)
end
)
end
function extract_from_queue()
-- returns nil if queue is empty
return operation_with_queue(
function(queue)
return table.remove(queue, 1)
end
)
end
Usage example:
add_to_queue(42)
add_to_queue("Hello")
print(extract_from_queue()) --> 42
print(extract_from_queue()) --> Hello
print(extract_from_queue()) --> nil

NodeMCU webserver closing connection after first send?

I have an small web server running on my ESP-12 with nodemcu firmware:
sv=net.createServer(net.TCP,10)
sv:listen(80,function(c)
c:on("receive", function(c, pl)
if(string.find(pl,"GET / ")) then
print("Asking for index")
c:send("Line 1")
c:send("Line 2")
c:send("Line 3")
c:close()
end
end)
c:on("sent",function(conn)
print("sended something...")
end)
end)
It seems my connection is getting closed after the first send, in my browser I only see the "line 1" text, line 2 a 3 does not appear, and in my serial console im just seeing the "sended something" text one time, even commenting the close statement and letting the connection to timeout does not change the behavior. What am I missing here?
I don't think that you can use send multiple times. Whenever I use one of my ESP8266 as a server I use a buffer variable :
sv=net.createServer(net.TCP,10)
-- 'c' -> connection, 'pl' -> payload
sv:listen(80,function(c)
c:on("receive", function(c, pl)
if(string.find(pl,"GET / ")) then
print("Asking for index")
local buffer = ""
buffer = buffer.."Line 1"
buffer = buffer.."Line 2"
buffer = buffer.."Line 3"
c:send(buffer)
c:close()
end
end)
c:on("sent",function(c)
print("sended something...")
end)
end)
EDIT: After reading the docs again, send can take another argument with a callback function, it can maybe be used to have multiple send command. Never tried it though :(.
EDIT 2: If you have a really long string to send, it's better to use table.concat
The net.socket:send() documentation provides a nice example which I repeat here.
srv = net.createServer(net.TCP)
function receiver(sck, data)
local response = {}
-- if you're sending back HTML over HTTP you'll want something like this instead
-- local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}
response[#response + 1] = "lots of data"
response[#response + 1] = "even more data"
response[#response + 1] = "e.g. content read from a file"
-- sends and removes the first element from the 'response' table
local function send(localSocket)
if #response > 0 then
localSocket:send(table.remove(response, 1))
else
localSocket:close()
response = nil
end
end
-- triggers the send() function again once the first chunk of data was sent
sck:on("sent", send)
send(sck)
end
srv:listen(80, function(conn)
conn:on("receive", receiver)
end)

Resources