Object log CSV export to FTP

Export last hour CSV object log file to external or local FTP server from LogicMachine #

Task #

Once an hour, create a CSV file with all object logs and send to an external FTP server with IP 192.168.1.11, login ftplogin, password ftppassword. In case you want to download the file manually from LM local FTP server, it is available in directory /home/ftp

Scheduled script #

In Scripting -> Scheduled add the script which will run once an hour and add following code into the Script editor.

Export_csv ftp_log_csv

require('socket.ftp')

-- ftp file
url = 'ftp://ftplogin:ftppassword@192.168.1.11/%s.csv'
date = os.date('%Y-%m-%d_%H-%M')
ftpfile = string.format(url, date)
-- get past hour data (3600 seconds)
logtime = os.time() - 60 * 60

-- csv buffer
buffer = {
  '"date","type","destination","name","datatype",' ..
  '"value","source","sender","login/meta"'
}

-- get object logs
query = [[
  SELECT ol.*, o.name, o.datatype
  FROM objectlog ol
  LEFT JOIN objects o ON ol.address=o.id
  WHERE logtime >= ?
  ORDER BY id DESC
]]

function fmtdpt(dpt)
  if dpt >= 1000 then
    dpt = string.format('%0.3f', dpt / 1000)
  end

  return dpt
end

for _, row in ipairs(db:getall(query, logtime)) do
  id = tonumber(row.address) or 0
  ia = tonumber(row.src) or 0

  logdate = os.date('%Y.%m.%d %H:%M:%S', math.floor(row.logtime))
  etype = row.eventtype
  textdpt = nil

  if (etype == 'write' or etype == 'response') and row.datatype then
    textdpt = fmtdpt(row.datatype)
    value = grp.decodevalue(row.datahex, row.datatype)
  else
    value = ''
  end

  buffer[ #buffer + 1 ] = string.format('%q,%q,%q,%q,%q,%q,%q,%q,%q',
    logdate,
    etype,
    buslib.decodega(id),
    row.name or '',
    tostring(textdpt or ''),
    tostring(value),
    ia > 0 and buslib.decodeia(ia) or 'local',
    row.sender or '',
    row.meta or ''
  )
end

-- upload to ftp only when there's data in buffer
if #buffer > 1 then
  res, err = socket.ftp.put(ftpfile, table.concat(buffer, '\r\n'))
end

-- error while uploading
if err then
  alert('FTP upload error: %s', tostring(err))
end

Write to an existing FTP file #

Replace these lines:

if #buffer > 1 then
  res, err = socket.ftp.put(ftpfile, table.concat(buffer, '\r\n'))
end

With these:

if #buffer > 1 then
  data = table.concat(buffer, '\r\n')
  ltn12 = require('ltn12')

  res, err = socket.ftp.put({
    host = '192.168.1.40',
    user = 'ftp',
    password = 'ftp',
    command = 'appe',
    argument = 'log.csv',
    source = ltn12.source.string(data)
  })
end

Local FTP CSV storage #

Modify the original script as follows.

Step 1 #

Replace these lines:

url = 'ftp://ftplogin:ftppassword@192.168.1.11/%s.csv'

With these:

url = '/home/ftp/%s.csv'

Step 2 #

Replace these lines:

-- upload to ftp only when there's data in buffer
if #buffer > 1 then
  result, err = socket.ftp.put(ftpfile, table.concat(buffer, '\r\n'))
end

-- error while uploading
if err then
  alert('FTP upload error: %s', tostring(err))
end

With these:

if #buffer > 1 then
  data = table.concat(buffer, '\r\n')
  io.writefile(ftpfile, data)
end

Step 3 #

Create a scheduled script that runs once a day to remove old logs (60 is a number of days to keep old logs):

function cleanup(dir, days)
  local now, files, mtime, delta

  now = os.time()
  files = io.ls(dir) or {}

  for _, file in ipairs(files) do
    file = dir .. '/' .. file
    _, mtime = io.stat(file)

    if mtime then
      delta = (now - mtime) / 86400
      if delta > days then
        os.remove(file)
      end
    end
  end
end

cleanup('/home/ftp', 60)