Jens
Jens

Reputation: 1167

Lua: use of pcall

I have a piece of Lua code that is sending a udp message out, that is working. However I want to make it more error proof. So I have entered a wrong dns name to provoke a resolution failure. I do not really care what in that block fails, I just want to be able to handle the error gracefully. In other languages I would use try-catch. Here I understand pcall is supposed to fill that gap. So I am trying to use pcall and am trying to pass buttonPin to the sendMessageToServer() function.

This approach is not working, nothing is caught, the whole node crashes:

if(pcall(sendMessageToServer, buttonPin))
then
    ledOn(ledGreen)
    print("Message sent.")
else
    ledOn(ledRed)
    print("Error: Message could not be sent.")
end

This approach does the opposite: It doesn't crash and looks as if everything is ok, so pcall returns true:

if(pcall(function() sendMessageToServer(buttonPin) end))
then
    ledOn(ledGreen)
    print("Message sent.")
else
    ledOn(ledRed)
    print("Error: Message could not be sent.")
end

But it should run... if I simply remove the word pcall, making the function run regularly, the crash occurs as expected.

Best regards, Jens

UPDATE: Complete code and intentionally caused error

require ("config")
require ("cryptography")

function armAllButtons()

    ledOff(ledGreen)
    ledOff(ledYellow)
    ledOff(ledRed)

    for i,v in ipairs(buttonPins)
    do
        --print(i,v)
        armButton(v)
    end
end

function armButton(buttonPin)
    print("Arming pin "..buttonPin.." for button presses.")

    gpio.mode(buttonPin,gpio.INT,gpio.FLOAT)
    gpio.trig(buttonPin, direction, function () notifyButtonPressed(buttonPin) end)

    print("Waiting for button press on "..buttonPin.."...")
end

function notifyButtonPressed(buttonPin)
    --print("Button pressed. Notifiying server at "..serverIp..":"..serverPort)

    -- show status
    ledOn(ledYellow)

    print("Button at pin "..buttonPin.." pressed.")

    ledOff(ledGreen)
    ledOff(ledYellow)
    ledOff(ledRed)

    -- show status
    --if(pcall(sendMessageToServer,buttonPin))
    if(sendMessageToServer(buttonPin))
    then
        ledOn(ledGreen)
        print("Message sent.")
    else
        ledOn(ledRed)
        print("Error: Message could not be sent.")
    end

    -- Rearm pin for interrupts
    armButton(buttonPin)
end

function sendMessageToServer(buttonPin)
    print("Notifying server at "..serverIp..":"..serverPort)
    --TODO: Include some variable. The current code is vulnerable to replay attacks.

    conn = net.createConnection(net.UDP, 0)
    conn:connect(serverPort,serverIp)
    local msg = node.chipid()..";"..buttonPin..";ButtonPressed"
    local hash = getHashValue(msg..encryptionPassword)
    print("Sending "..msg.." with hash "..hash.." to server "..serverIp..":"..serverPort)
    conn:send(msg..';'..hash)
    conn:close()
    conn = nil
end

function ledOn(ledPin)
    gpio.write(ledPin,gpio.HIGH)
end

function ledOff(ledPin) 
    gpio.write(ledPin,gpio.LOW)
end


armAllButtons()

Error when not using pcall:

dofile("button.lua")
Arming pin 5 for button presses.
Waiting for button press on 5...
Arming pin 6 for button presses.
Waiting for button press on 6...
> Button at pin 5 pressed.
Notifying server at <incorrectDnsEntry>:36740
Sending 14695197;5;ButtonPressed with hash <someHashValue> to server <incorrectDnsEntry>:36740
Error: Message could not be sent.
Arming pin 5 for button presses.
Waiting for button press on 5...
DNS retry 1!
DNS retry 2!
DNS retry 3!
DNS retry 4!
DNS Fail!
?ˆÈ)ŠâF
‘ŽF
”Œ¦ú

NodeMCU 0.9.6 build 20150704  powered by Lua 5.1.4
Arming pin 5 for button presses.
Waiting for button press on 5...
Arming pin 6 for button presses.
Waiting for button press on 6...
IP unavaiable, Waiting...
> IP unavaiable, Waiting...
IP unavaiable, Waiting...
IP unavaiable, Waiting...
IP unavaiable, Waiting...
Config done, IP is 192.168.x.x

Display with pcall used:

Config done, IP is 192.168.x.x
Button at pin 5 pressed.
Notifying server at <incorrectDnsEntry>:36740
Sending 14695197;5;ButtonPressed with hash <someHashValue> to server <incorrectDnsEntry>:36740
Message sent.
Arming pin 5 for button presses.
Waiting for button press on 5...

So I'm not manually signalling the error.

Upvotes: 3

Views: 2281

Answers (1)

britzl
britzl

Reputation: 10242

You're using LuaSockets right? You should know that the functions in LuaSockets doesn't actually raise errors. Most functions, like udp send, return 1 when successful and nil + msg when something went wrong. If you want to raise an error you need to check the return value and raise the error yourself. A very common way of doing it is to wrap the call in an assert, like this:

assert(conn:send(msg..';'..hash))

When you're saying that it works without a pcall then you're actually fooling yourself. It looks as if you're doing like this:

if(sendMessageToServer(buttonPin))
then
    ledOn(ledGreen)
    print("Message sent.")
else
    ledOn(ledRed)
    print("Error: Message could not be sent.")
end

The above code will always print "Error: Message could not be sent." since the sendMessageToServer function never returns anything, in which case the if condition will always evaluate to false and you'll end up in the else block regardless if you managed to send the data or not.

Upvotes: 1

Related Questions