#!/usr/bin/lua -- Created by jiaqiang.ye@lnxall.com -- 2022/06/09 local cjson = require "cjson" local posix = require "posix" local invoker = require "invoker" local mosq = require "mosquitto" -- global variables local g_mqtt_hdl = nil -- MQTT connection handle local g_int_hdl = nil -- MQTT connection to internal broker local g_mqtt_con = false -- true if successfully connected to MQTT broker local g_int_con = false -- true if successfully connected to internal broker local g_broker_info = {} -- MQTT Broker information local g_loop_int = false -- whether to loop messages to internal broker local g_boardsn = nil -- gateway board SN local g_services = nil -- services from object_model_cfg.json local g_pkhdls = {} -- PK_Type handler functions local g_devicesn = 'jZ4IXQmD' -- SN from /app/nodes_cfg.json -- MQTT Broker information g_broker_info[1] = "10.11.22.10" g_broker_info[2] = 1883 g_broker_info[3] = "hx_admin" g_broker_info[4] = "hx_admin" local function load_json(fpath) local filp = io.open(fpath, "rb") if not filp then return nil end local fild = filp:read("*a") filp:close(); filp = nil if not fild or string.len(fild) == 0 then return nil end local okay, jdat = pcall(cjson.decode, fild) if not okay then return nil end return jdat end local function load_node_cfgs() -- local objs = load_json("/app/node/object_model_cfg.json") -- if not objs then return false end local ncfgs = load_json("/app/node/nodes_cfg.json") if not ncfgs then return false end local emptab, idx = {}, 0 local tempid, tsn = nil, nil ncfgs = ncfgs["nodes_cfg"] or emptab if not ncfgs then return false end while true do idx = idx + 1 local ncfg = ncfgs[idx] if type(ncfg) ~= "table" then break end local cport = ncfg["connect_port"] if cport == "VINTF_MQTT" then tempid = ncfg["template_id"] tsn = ncfg["sn"] break end end if type(tempid) ~= "string" or type(tsn) ~= "string" then io.stderr:write("Failed to find VINTF_MQTT\n") io.stderr:flush() return false end g_devicesn = tsn -- global device SN return true end local function get_pktype(jdat) local emptab = {} -- empty table local rval = jdat["FSU_Publish"] or emptab rval = rval["PK_Type"] or emptab return rval["Name"] end local function process_tsem(tabn, tsem) local rval, jdx = 0, 1 while true do local semd = tsem[jdx] if type(semd) ~= "table" then break end local id, mval = semd["Id"], semd["MeasuredVal"] if type(id) == "string" and type(mval) == "string" then if #mval > 0 then rval = rval + 1 tabn[id] = mval end end jdx = jdx + 1 end return rval end g_pkhdls["SEND_ALARM"] = function (pload, nowt) local emptab, identifier = {}, "SEND_ALARM" local alarms = pload["Info"] or emptab alarms = alarms["Values"] or emptab alarms = alarms["TAlarmList"] or emptab alarms = alarms["TAlarm"] if type(alarms) ~= "table" then io.stderr:write("Error, no alarms found.\n") io.stderr:flush() return false end local valcnt, valtab, idx = 0, {}, 0 while true do idx = idx + 1 local alrm = alarms[idx] if type(alrm) ~= "table" then break end valcnt = valcnt + 0x1 valtab[valcnt] = alrm end if valcnt == 0 then io.stderr:write("Error, no alarm element found.\n") io.stderr:flush() return false end local dattab = { ["sn"] = g_devicesn, ["mi"] = 0, ["time"] = nowt, ["identifier"] = identifier, ["tags"] = valtab } local datj = cjson.encode(dattab) local topic = string.format("ipc/%s/VINTF_MQTT/device/%s/data_filtered/service/%s", g_boardsn, g_devicesn, identifier) g_int_hdl:publish(topic, datj) g_int_hdl:loop(100, 4); return true end g_pkhdls["GET_DATA_ACK"] = function (pload, nowt) local emptab, identifier = {}, "GET_DATA_ACK" local ackd = pload["FSU_Publish"] or emptab ackd = ackd["Info"] or emptab ackd = ackd["Values"] or emptab ackd = ackd["DeviceList"] or emptab ackd = ackd["Device"] or emptab local valcnt, idx, valtab = 0, 1, {} while true do local devi = ackd[idx] local typn = type(devi) if typn ~= "table" then break end local TSemaphore = devi["TSemaphore"] typn = type(TSemaphore) if typn == "table" then valcnt = valcnt + process_tsem(valtab, TSemaphore) end idx = idx + 1 end if valcnt == 0 then io.stderr:write("Error, no data found for GET_DATA_ACK!\n") io.stderr:flush() return false end local dattab = { ["sn"] = g_devicesn, ["mi"] = 0, ["time"] = nowt, ["identifier"] = identifier, ["tags"] = valtab } local datj = cjson.encode(dattab) local topic = string.format("ipc/%s/VINTF_MQTT/device/%s/data_filtered/service/%s", g_boardsn, g_devicesn, identifier) g_int_hdl:publish(topic, datj) g_int_hdl:loop(100, 4); return true end local function mqtt_msgcb(msgid, msgtopic, msgpayload) local nowt = os.time() io.stdout:write(string.format("Received message topic: %s\n", msgtopic)) io.stdout:write(string.format("Message payload length: %d\n", string.len(msgpayload))) local okay, payload = pcall(cjson.decode, msgpayload) if not okay then io.stderr:write("Error, failed to decode payload:\n") io.stderr:write(msgpayload) io.stderr:flush() return false end local pktype = get_pktype(payload) if not pktype then io.stderr:write("Error, PK_Type not found.\n") io.stderr:flush() return false end local handler = g_pkhdls[pktype] if not handler then io.stderr:write(string.format("Error, handler not found for '%s'\n", pktype)) io.stderr:write(msgpayload) return false end return handler(payload, nowt) end local function get_boardsn() local eval, pbuf = invoker.invoke(invoker.OUTPUT + invoker.NOSTDIO, "uci", "-X", "show", "system.system.hostname") if eval ~= 0 or type(pbuf) ~= "string" then io.stderr:write("Error, failed to get board sn!\n") io.stderr:flush() return nil end local bsn = string.match(pbuf, "='(%x+)'") if not bsn or string.len(bsn) ~= 12 then io.stderr:write("Error, invalid board sn found.\n") io.stderr:write(type(bsn)) io.stderr:flush() return nil end print(string.format("BSN found: %s", bsn)) return bsn end local function mqtt_discon() g_mqtt_con = false io.stderr:write("Error, HX MQTT Broker disconnected!\n") return true end local function mqtt_conn(okay, conmsg) local mqtt = g_mqtt_hdl if not mqtt then g_mqtt_con = false io.stderr:write("Error, invalid MQTT handle!\n") return false end -- update global connection flag g_mqtt_con = okay and true or false if not okay then local typn = type(conmsg) if typn ~= "string" then conmsg = "unknown" end io.stderr:write(string.format("Error, connect to broker has failed: %s\n", conmsg)) return false end io.stdout:write("HX Broker connected!\n") if not mqtt:subscribe("/rz/#") then io.stderr:write("Error, failed to subscribe HX topic!\n") return false end return true end local function mqtt_int_conn(okay, errmsg) g_int_con = okay and true or false if not okay then io.stderr:write("Error, failed to connect to internal broker!\n") io.stderr:flush() return false end io.stdout:write("Internal broker connected!\n") io.stdout:flush() return true end local function mqtt_int_disconn() g_int_con = false io.stdout:write("Internal broker disconnected!\n") io.stdout:flush() return true end -- try to connect to internal broker local function mqtt_int_connect(ipaddr, portno) local mqtt = mosq.new() mqtt.ON_CONNECT = mqtt_int_conn mqtt.ON_DISCONNECT = mqtt_int_disconn if g_int_con then g_int_con = false end if not mqtt:connect_async(ipaddr, portno) then mqtt:destory(); mqtt = nil io.stderr:write("Error, failed to connect to internal broker!\n") io.stderr:flush() return false end local nowt = invoker.uptime() while not g_int_con do local thetime = invoker.uptime() if (thetime - nowt) > 10 then mqtt:destory(); mqtt = nil io.stderr:write("Error, failed to connect to internal broker!\n") io.stderr:flush() return false end mqtt:loop(5000, 50) end g_int_hdl = mqtt return true end -- Establish MQTT connection to HuaXin Broker local function mqtt_hx_connect(ipaddr, portno, hxuser, hxpasswd) if g_mqtt_con then g_mqtt_con = false end local nodeid = string.format("lnxall_%d", posix.getpid()) local mqtt, errmsg = mosq.new(nodeid, true) -- true -> clean session if not mqtt then local typn = type(errmsg) if typn ~= "string" then errmsg = "unknown" end io.stderr:write(string.format("%s\n", errmsg)) io.stderr:flush() return false end -- set MQTT event functions mqtt.ON_CONNECT = mqtt_conn mqtt.ON_MESSAGE = mqtt_msgcb mqtt.ON_DISCONNECT = mqtt_discon if hxuser and hxpasswd then mqtt:login_set(hxuser, hxpasswd) end if not mqtt:connect_async(ipaddr, portno) then g_mqtt_hdl = nil mqtt:destory(); mqtt = nil io.stderr:write("Error, connect_async has failed!\n") return false end g_mqtt_hdl = mqtt -- store mqtt handle to globle variable return true end local function main_func() if not mqtt_int_connect("127.0.0.1", 1883) then return nil end local nowt, thetime = invoker.uptime(), 0 while not mqtt_hx_connect(unpack(g_broker_info)) do thetime = invoker.uptime() if (thetime - nowt) > 120 then return nil end io.stderr:write("MQTT connect has failed!\n") io.stderr:flush() posix.nanosleep{tv_sec = 5, tv_nsec = 0} end nowt = invoker.uptime() io.stdout:write("Waiting MQTT connection...\n") while not g_mqtt_con do thetime = invoker.uptime() if (thetime - nowt) > 30 then g_mqtt_hdl:destroy() g_mqtt_hdl = nil io.stderr:write("Error, failed to connect to MQTT Broker!\n") io.stderr:flush() return nil end g_mqtt_hdl:loop(5000, 50) end while g_mqtt_con do g_mqtt_hdl:loop(2000, 10) g_int_hdl:loop(100, 2) end return nil end mosq.init() while true do g_boardsn = get_boardsn() if g_boardsn then break end posix.nanosleep{tv_sec = 5, tv_nsec = 0} end if not load_node_cfgs() then io.stderr:write("Error, failed to determine device SN\n") io.stderr:flush() os.exit(1) end main_func(); os.exit(1)