This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Copas - TCP status and Control
#1
Hi im using an example of Copas and wanted to add some additonal features:

1. Status of connection to object
    - i guess i can just write to an object at line " alert('[tcp-client] connection ok') " and line " alert('[tcp-client] connection failed (conn): ' .. err) " 
2. Object to control TCP connect, reconnect and disconnect
    - Aside from just disabling and enabling the script is there a method to call functions for connect, reconnect and disconnect from an object while the script is running? 
3. Detection of loss of TCP connection and attempt to reconnect
    - i tried adding a kep alive function that sends a string 'ping' and try to check if it fails to send, however this does not work. I disconnect the ethernet to the end device and the 'ping'         message keeps sending and copas/ tcp does not fail or recognise the end device is no longer connected?

Code:
if not ready then
  socket = require("socket")
  copas = require("copas")
  alert('not Ready')
  ready = true

  function parse(data)
    alert('parsing: ' .. data)
  end

  function fromKNX(command)
    alert('from KNX.. ' .. command)
  end

  local server = socket.udp()
  server:setsockname("127.0.0.1", 23456)

  function handler(skts)
    skts = copas.wrap(skts)
    alert("UDP connection handler")
    while true do
      local s, err
      alert("UDP receiving...")
      s, err = skts:receive(2048)
      if not s then
        alert("UDP Receive error: " .. err)
        break
      end
      alert("Received data, bytes: " .. s)
      fromKNX(s)
    end
  end

  copas.addserver(server, handler, 1)
end

-- Define the init function
function init()
  alert('Initialization function called')
  -- Add some initialization code here
end

function connect_tcp()
  alert('Attempting to connect to TCP server')
  local skt, err = socket.connect('x.x.x.x’, xxxx)
  if skt then
    skt:settimeout(0)
    alert('[tcp-client] connection ok')
    copas.addthread(function()
      while true do
        local resp, err = copas.receive(skt, '*l')
        if not resp then
          alert("Receive error: " .. err)
          copas.removeserver(skt)
          skt = nil
          break
        end
        parse(resp)
      end
      -- Attempt to reconnect after disconnect
      connect_tcp()
    end)

    -- Keep-alive mechanism: send ping every 5 seconds
    copas.addthread(function()
      while skt do
        local success, err = skt:send("ping\n")
        if not success then
          alert("Ping send error: " .. err)
          copas.removeserver(skt)
          skt:close()
          skt = nil
          break
        end
        copas.sleep(5)
      end
      -- Attempt to reconnect after ping failure
      connect_tcp()
    end)

    warningfailed = true
    init()
  else
    if warningfailed then
      alert('[tcp-client] connection failed (conn): ' .. err)
    end
    warningfailed = false
    -- Retry connection after a delay
    copas.addthread(function()
      copas.sleep(5)  -- Wait for 5 seconds before retrying
      connect_tcp()
    end)
  end
end

if not skt then
  alert('not socket')
  connect_tcp()
end

copas.loop()
Reply
#2
Please describe your task in more detail. Most likely this can be done without copas, just with normal sockets and correctly set timeout values.
Reply
#3
(20.05.2024, 07:24)admin Wrote: Please describe your task in more detail. Most likely this can be done without copas, just with normal sockets and correctly set timeout values.

Im using copas as the device being connecting to only accepts 1 TCP conenction on 1 port for send and recieve. local UDP server on the LM sends event driven messages via copas TCP conenction which also recieves and updates objects.

I'd like to periodically check the connection is still alive and update a status object. 

The device uses the same port for commisisoning, so i want to add some buttons to a page to disconnect, connect or do a reconnect ( refresh the connection ). So when commisisoning is required we can disconnect, and then reconnect when commisioning is complete.
Reply
#4
I've got a bit further with this and im now looking to develop it into an app but im having trouble getting the above example lua code to run from a .lp page.

How do i start / stop a lua script file from a button on an lp page, similar to enable and disable of a resident script with 0 seconds? this would be to start and stop the copas script example posted previously.

And how can i call functions or modules from the same lua file using button press's?
Reply
#5
You can't use copas or any long-running code from the web server context directly. For app daemons use this code:
Code:
require('apps.daemon').stop('myappname')
require('apps.daemon').start('myappname')

To handle user actions either use a separate .lp file that is called remotely using fetch() or $.get(). Or create a form and use getvar() in the .lp file to check whether a certain parameter has been passed or not. Check this sample app: https://forum.logicmachine.net/showthrea...5#pid33575
Reply
#6
Thanks, I can start and stop the Daemon.

My previous code was a library and returned a table of functions, start_server, stop_server, add_client, Remove_client etc which could be called.
When i start the library using the apps.daemon handler, the library runs but then im not sure how to access the fuctions. Can they be called from other scripts? or do i need to adjust the code so the event listener starts when the daemon runs and then use event messages to trigger functions within? or restructure another way?

My resident script looked like this:

local NetworkModule = require("user.CNI_Comms")

-- Start the Copas loop, event listener
NetworkModuleConfusedtart_server()

-- Add clients using the NetworkModule:add_client method
local client1 = NetworkModule:add_client("x.x.x.x", port)
local client2 = NetworkModule:add_client("x.x.x.x", port)
Reply
#7
Please describe what your library/app is doing in more detail.
Reply
#8
(15.07.2024, 10:57)admin Wrote: Please describe what your library/app is doing in more detail.
Clients are ethernet interfaces from a lighitng control system, the interfaces support 1 tcp connection only at a time.

The Library returns a table of functions

Start_server() Starts a Copas loop and checks a json file for a list of previously saved clients and connect's clients upon initialization

add_client() adds a new tcp client to conenct and then add the client to the json file
remove_client() disconnects an existing client and removes them from the json file

Each clients data is parsed and used to write to objects on change

start_udp_server starts when start_server is called and starts the listening server for object events to send data back and change lighting states. I added functions here to add and remove clients via event comma separated event strings so an event script could add and remove clients, but would prefer to call the function directly if possible.

This is all working fine but now i want to develop it into an app to make it more user friendly.

I've started to create a .lp page as attached. Currently it checks and loads a json file then displays the contents, at the moment it saves a form, then the entries can be edited or removed.

When an entry is added i want to call the add_client function.
When an entry is removed i want to call the remove_client function

At the top are buttons that would call the start_server or stop_server to start and stop everything.

Then there are buttons for each entry to allow individual conenction control.

Attached Files Thumbnail(s)
   
Reply
#9
so i misunderstood how the daemon was working, I've reshuffled a few things and added the library to the apps.lib folder and can use the daemon to call the start_server function. Ill look at the template some more and see how how i go.

thanks
Reply


Forum Jump: