3

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.

Jens
  • 1,157
  • 1
  • 8
  • 17
  • You'd get more responses if you showed enough respect to at least type the language name properly. Lua is not an acronym, capitalize the L, that's all. Lua. Please fix the question. – warspyking Jan 08 '17 at 22:07
  • Can you show what the stacktrace looks like for the intentionally triggered error? How is the error being signaled? Are you using `error`/`lua_error` or is your code using some "out-of-band" way to signal this error that lua doesn't understand? – greatwolf Jan 09 '17 at 01:09
  • I have updated my original question to contain more code and the error messages. – Jens Jan 09 '17 at 18:27
  • 1
    `pcall` can only catch errors propagated with `error`/`lua_error`. From what I can tell, those errors you're seeing are just being outputted to stderr. How does the API you're working with communicate errors? You need to take that and rethrow them as lua errors using the mentioned function. – greatwolf Jan 10 '17 at 06:15
  • Looks that's true: https://github.com/nodemcu/nodemcu-firmware/blob/master/app/modules/net.c Line 215 and following. I'll see how I can work with stderr then. Thanks for your response! – Jens Jan 13 '17 at 13:04
  • 1
    There is something wrong with this question itself. You cannot simply wrap pcall'ed code into `function () ... end` and get different behavior. – user3125367 Jan 13 '17 at 16:25
  • Also, current code always takes `false` route at `if`, because `sendMessageToServer` never returns any [true] value. This test tests nothing. – user3125367 Jan 13 '17 at 16:33
  • To my understanding sendMessageToServer() doesn't necessarily need to return anything. Pcall is supposed to return true if there have been no (propagated errors): https://www.lua.org/pil/8.4.html "If there are no errors, pcall returns true" – Jens Jan 14 '17 at 12:30

1 Answers1

1

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.

britzl
  • 10,132
  • 7
  • 41
  • 38