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.

Data buffering
#1
Hello,

maybe you know how do the data buffering in Lua and the LM. Maybe you have better solution than my.

Task is:
I must send a commands 1 after 1(not all in the same time) because device which accepts this commands is slow and there is no feedback.

E.g.
1. Pushbutton "Blind up" -> 2. GA script -> 3. Command(RS-485) -> 4. Command(Somfy RTS)


Command in Somfy RTS is very slow, normally 0,7s. So if someone want change slat angle this can produce 3 or more commands in 1 second. I want prepare the buffer for this commands because if I won't prepare the buffer a lot of commands will be not executed.

This is what I've prepared at this moment:


GA script (pushbutton up/down), it sends command to the buffer:

Code:
value = event.getvalue()
channel = Somfy.t0.ch00

if value then
 Somfy.buffer(channel.down)
else
 Somfy.buffer(channel.up)
end

This is Somfy.buffer function (reads the buffer from the table 'bufor' from the storage, sends command and delete this command):

Code:
buffer = function(command)
    bufor = storage.get('bufor')
    if type(bufor) == "table" then
    table.insert(bufor, command)
    else
    -- buffer initialisation
    bufor = {}
    end
    storage.set('bufor', bufor)
    end

I have also Resident script for sending commands over RS485. Perfectly would be if I could sending the commands with 0.7s break between the commands. But at this moment I can't. I've tried with Resident with time=0s but there was a big CPU usage or strange operation(sometimes it sends the commands from the buffer, sometimes not). At this moment it works but with Resident with time=1s and this code(and unfortunately this is not 100% reliable because when I send 10 commands this mechanism send only 7 or 8):
Code:
main = function()
 bufor = storage.get('bufor')

    if type(bufor) == "table" then
    if bufor[1] then
 Somfy.send(bufor[1].command)
 os.sleep(bufor[1].time)
 table.remove(bufor)
    else
    --bufor pusty
 os.sleep(1)
 end
    else
    -- buffer initialisation
    bufor = {}
    end

    bufor = storage.set('bufor', bufor)
 end

And this is the Somfy.send function which I use for sending commands via RS485:

Code:
 send = function (command)
 require('serial')
 port = serial.open('/dev/RS485', { baudrate = 4800, parity = 'odd', duplex = 'half', databits = 8, stopbits = 1 })
 port:flush()
 port:write(command)
 port:close()
 sleep(0.1)
  end



I must improve this solution, because I want have reliable(100%) solution and I want prepare faster solution (at this moment this is 1 command per second, and it will be good if this would be e.g. 0,7s).

Maybe you can give me some advice how improve this solution? Especially what are the principles of the resident script with time = 0s. Is this script is executed parallel with the other scripts or serial? In my script I am using storage for saving the buffer. Buffer is updated in this way:
- read from the storage, add new command to the buffer, save to storage(with event script),
- read from the storage, delete executed command, save to storage(with resident script 1s).
I think that in this solution there is a possibility that in a resident script I open the buffer table(from storage) and before saving by the resident script the table is changed by the event script but after this resident script save its calculated value and maybe this is the problem.

Maybe do you have some better mechanism for such bufferring commands or some advice how improve this scripts?
Thanks for all helpWink

I've checked more precisely that the minimum break time between 2 commands can be 0.42 so theoretically in 1 second I could send 2 commands. This would be very comfortable for user.
Reply
#2
Your script is not 100% working because there's race condition while accessing storage items.

The easiest approach is to use UDP client (event scripts) and server (resident) - this way you don't have to waste CPU power polling and OS will do the buffering for you while your script is sending data to RS-485. I'll prepare a short example later.
Reply
#3
Resident script with sleep time 0, which send whatever data comes from clients and waits for 0.5 sec after each send.
Code:
if not server then
  -- local UDP server on port 5432
  server = require('socket').udp()
  server:settimeout(1)
  server:setsockname('127.0.0.1', 5432)

  -- serial connection
  port = require('serial').open('/dev/RS485', {
    baudrate = 4800,
    parity = 'odd',
    duplex = 'half',
    databits = 8,
    stopbits = 1
  })
end

cmd = server:receive()
if cmd then
  port:write(cmd)
  os.sleep(0.5)
end

Functiont to send data from event scripts.
Code:
function send(cmd)
  require('socket').udp():sendto(cmd, '127.0.0.1', 5432)
end
Reply
#4
Perfect solutionWink Very fast and with minimum CPU usage (at this moment 0.03 when 10 commands 1 by 1). I will use this also in other applicationsWink

Thanks admin
Reply
#5
Unfortunately there is a problem with this solution because probably via this Resident script whole ethernet is blocked still. When I switch on this script I can't use other functions of sending data e.g. sending data via TCP. When this UDP script is switched on and I try to send something via TCP I have always information "connection refused".

I tried a few things and it seems that when I try prepare some other TCP connection via resident script it is not working but when I try send some data via TCP in the event script there is no collision.
Reply
#6
Connection refused means that other socket end does not accept the connection. Single local UDP socket should not block anything, check your other scripts.

If you are using send() in resident script, you can change the code to close the socket after each send (this is done automatically when script ends, so it will work properly for event scripts with older code). Otherwise it might lead to "No file descriptors available" error when too many sockets are open (>1024).

Code:
function send(cmd)
  local sock = require('socket').udp()
  if sock then
    sock:sendto(cmd, '127.0.0.1', 5432)
    sock:close()
  end
end
Reply
#7
I checked this and the problem is not in UDP client-server. It works great but when I try send the command via TCP my connection is refused. I try to send cmd to the RS485 server. This is the script:

Code:
if not server then
 -- local UDP server on port 5432
 server = require('socket').udp()
 server:settimeout(1)
 server:setsockname('127.0.0.1', 5432)
 
 -- serial connection
 port = require('serial').open('/dev/RS485', {
   baudrate = 4800,
   parity = 'odd',
   duplex = 'half',
   databits = 8,
   stopbits = 1
 })
end

cmd = server:receive()

if cmd then
function somfy_rf(cmd)
serial_server_ip = '192.168.2.15'
host, port = serial_server_ip, 10001
socket = require("socket")
client = socket.tcp()
client:settimeout(1)
client:connect(host, port)
client:send(cmd)
end
 somfy_rf(cmd)
 if #cmd == 12 then
   time = 6
 elseif #cmd == 13 then
   time = 0.6
 elseif msg == set_channel then
   time = 0.45
 end
 os.sleep(time)
end
Where is the error?

This is the network configuration of the serial server.

Attached Files Thumbnail(s)
       
Reply
#8
Try adding client:close() after client:send(cmd). But it all depends on how TCP server is created, it might still refuse connections for some time even when close is called. You might want to have and always-open TCP connection and do reconnect if send fails because remote TCP server might disconnect clients on timeout. This is all in theory and should be tested :) If close does not solve this issue I'll provide an example with automatic retry.
Reply
#9
Unfortunately it not help. The problem is 1-step before. I've checked and when I take the log from:

Code:
log(client:connect(host, port))


the result from Logs is
Code:
* arg: 1
 * nil
* arg: 2
 * string: connection refused
Reply
#10
Try rebooting that serial device and setting Inactivity time to 1.
Reply
#11
(30.09.2016, 12:12)admin Wrote: Try rebooting that serial device and setting Inactivity time to 1.

Unfortunately it not workingSad
Reply


Forum Jump: