#!/usr/bin/lua

require "uci"
cur = uci.cursor()

lfs = require "lfs"
json = require "json"
socket = require "socket"

require "wm_util"

io.stdout:setvbuf('no')

function dump(o)
   if type(o) == 'table' then
      local s = '{ '
      for k,v in pairs(o) do
         if type(k) ~= 'number' then k = '"'..k..'"' end
         s = s .. '['..k..'] = ' .. dump(v) .. ','
      end
      return s .. '} '
   else
      return tostring(o)
   end
end

function get_device_list(config_name)

  local devices
  devices = {}

  cur.foreach(config_name, "device", function(s)
    devices[#devices+1] = s[".name"]
  end)

  return devices

end

function get_device(config_name,device_name)

  local device
  device = {}
  
  cur.foreach(config_name, "device", function(s)
    if s[".name"] == device_name then
      device = s
    end
  end)  
  
  return device
  
end

function find_device(name,subsystem)

  local search_base

  if subsystem == "iio" then
    search_base = "/sys/bus/iio/devices"
    for file in lfs.dir(search_base) do
      if get_file_content(search_base.."/"..file.."/name") == name then
        return search_base.."/"..file
      end
    end
  elseif subsystem == "hwmon" then
    search_base = "/sys/class/hwmon"
    for file in lfs.dir(search_base) do
      if get_file_content(search_base.."/"..file.."/device/name") == name then
        return search_base.."/"..file.."/device"
      end
    end  
  end
  
  return nil

end

function get_parameter(record)
  return tonumber(get_file_content(record["path"])) * record["scale"] + record["correction"]
end

function string.fromhex(str)
  return (str:gsub('..', function (cc)
    return string.char(tonumber(cc, 16))
  end))
end

function string.tohex(str)
  return (str:gsub('.', function (c)
    return string.format('%02X', string.byte(c))
  end))
end

function get_mhz(record)
  local p = record["rs232"]  
  p:read(9,100)
  p:write(string.fromhex("ff0186000000000079"))
  local e, s = p:read(9,1000)
  if (e == 0) and (s:len() == 9) and (s:byte(1) == 255) then
    local crc = 0
    for i = 2, 8 do
      crc = crc + s:byte(i)
      if (crc>=256) then
        crc = crc - 256
      end  
    end  
    crc = 255 - crc
    crc = crc + 1;
    if crc == s:byte(9) then
      return s:byte(3)*256+s:byte(4)
    end  
  end
  
  return nil
   
end

function search_rs232_const(rs232,prefix,value)

  for k,v in pairs(rs232) do
  
    if k == prefix..value:upper() then
      return v
    end  
  
  end

  return nil

end

function init_serial_device(device,subsystem,parameters)

  rs232 = require("luars232")

  pcall(function ()

    local e, port = rs232.open(device["port"])

    local baud = device["baud"]; if baud == nil then baud = 9600; end
    local bits = device["bits"]; if bits == nil then bits = 8; end
    local stop_bits = device["stop_bits"]; if stop_bits == nil then stop_bits = 1; end
    local parity = device["parity"]; if parity == nil then parity = "NONE"; end 
    local flowctl = device["flowctl"]; if flowctl == nil then flowctl = "OFF"; end
    
    assert(port:set_baud_rate(search_rs232_const(rs232,"RS232_BAUD_",baud)) == rs232.RS232_ERR_NOERROR)
    assert(port:set_data_bits(search_rs232_const(rs232,"RS232_DATA_",bits)) == rs232.RS232_ERR_NOERROR)
    assert(port:set_parity(search_rs232_const(rs232,"RS232_PARITY_",parity)) == rs232.RS232_ERR_NOERROR)
    assert(port:set_stop_bits(search_rs232_const(rs232,"RS232_STOP_",stop_bits)) == rs232.RS232_ERR_NOERROR)
    assert(port:set_flow_control(search_rs232_const(rs232,"RS232_FLOW_",flowctl)) == rs232.RS232_ERR_NOERROR)
    
    getparameter = {}

    getparameter["rs232"] = port
  
    if subsystem == "mhz" then
    
      getparameter["function"] = get_mhz 
      getparameter["name"] = "CO2PPM"
      getparameter["sensor"] = "MHZ19"
      parameters[#parameters+1] = getparameter

    end

    parameters[#parameters+1] = getparameter
    
  end)

end

function init_i2c_device(device,subsystem,parameters)

  if not i2c_bus then
    i2c_bus = 0
  end

  pcall(function ()
    local f = io.open("/sys/class/i2c-dev/i2c-"..i2c_bus.."/device/new_device","w")
    io.output(f)
    io.write(device["name"].." "..device["address"])
    io.close(f)
  end)  

  device_path=find_device(device["name"],subsystem)

  if device_path and device["set_param"] then
    for key,record in pairs(device["set_param"]) do
      setparam = split(record,":")
      setpath = device_path.."/"..setparam[1]
      pcall(function ()
        local f = io.open(setpath,"w")
        io.output(f)
        io.write(setparam[2])
        io.close(f)
      end)  
    end
  end

  if device_path and device["parameter"] then

    for key,record in pairs(device["parameter"]) do

      getparam = split(record,":")
      getparameter = {}
      getparameter["path"] = device_path.."/"..getparam[1]
      getparameter["name"] = getparam[2]
      getscale = getparam[3]
      getcorrection = getparam[4]
      if not getscale then
        getscale = 1
      end  
      if not getcorrection then
        getcorrection = 0
      end
      getparameter["scale"] = tonumber(getscale)
      getparameter["sensor"] = device["name"]:upper()
      getparameter["correction"] = tonumber(getcorrection)
      getparameter["function"] = get_parameter
      parameters[#parameters+1] = getparameter

    end

  end
  
end

function init_device(device,parameters)

  if device["module"] then
    os.execute("modprobe "..device["module"])
  end    

  if device["type"] then

    devtype = split(device["type"],":")
    bus = devtype[1]
    subsystem = devtype[2]
    
    if (bus == "i2c") then

      init_i2c_device(device,subsystem,parameters)
    
    elseif (bus == "serial") then
      
      init_serial_device(device,subsystem,parameters) 
    
    end
    
  end

end

function init(config_name)

  local parameters= {}

  i2c_bus = uci.get(config_name,"hardware","i2c_bus")

  local devices = get_device_list(config_name)
  
  for key,devname in pairs(devices) do
  
    device = get_device(config_name,devname)
    
    if device then
      init_device(device,parameters)
    end  
  
  end

  return parameters

end

function get_parameters(parameters)
  local results = {}
  for key,record in pairs(parameters) do

    if not results[record["sensor"]] then 
      results[record["sensor"]] = {}
    end
    pcall(function ()
        results[record["sensor"]][record["name"]] = record["function"](record)
      end)
  end
  
  return results
end

config_name = arg[1]
if not config_name then
  config_name = "weathermon"
end  

web_id = get_devid(config_name)

parameters = init(config_name)

local delay = uci.get(config_name,"process","delay")

local working_dir = uci.get(config_name,"process","working_dir")
if working_dir then
  lfs.mkdir(working_dir)
end

if not delay then
  delay = 60
end

while true do
    values = get_parameters(parameters)
    records = {}
    records[web_id] = {}
    for key,record in pairs(values) do
      records[web_id][key] = record
      records[web_id]["timestamp"] = os.date("%Y-%m-%dT%H:%M:%S")
    end
    for key,value in pairs(values) do
      value["device"] = key
      print(json.encode(value))
    end
  socket.sleep(delay)
end