#!/opt/lnxall_app/bin/lua local bit32 = require 'bit32' local posix = require 'posix' local cjson = require 'cjson' local invoker = require 'invoker' local g_tty = { [1] = '/dev/ttySPRU4', [2] = '/dev/ttySPRU5', [3] = '/dev/ttyS3', [4] = '/dev/ttySPRU3', [5] = '/dev/ttySPRU1', [6] = '/dev/ttySPRU2', [7] = '/dev/ttyS4', [8] = '/dev/ttySPRU0' } local function netdev_hasipv4(ndev) local okay, output = invoker.invoke(invoker.OUTPUT, "ip", "addr", "show", "dev", ndev) if okay ~= 0 or type(output) ~= "string" then return false end local ipaddr = string.match(output, "inet%s+([%d%.]+)/") if not ipaddr then io.stderr:write(string.format("Error, no ipv4 found for '%s'\n", ndev)) io.stderr:flush() return false end io.stdout:write(string.format("IPv4 address found for '%s': %s\n", ndev, ipaddr)) io.stdout:flush() return true end local function gpio_init(num, isin, initval) local gpath = string.format("/sys/class/gpio/gpio%d", num) if posix.access(gpath) ~= 0 then invoker.invoke(0, "/bin/sh", "-c", string.format("echo %d > /sys/class/gpio/export", num)) end local dirp = string.format("%s/direction", gpath) local fp = io.open(dirp, "wb") if not fp then io.stderr:write(string.format("Error, cannot open %s\n", dirp)) io.stderr:flush() return false end fp:write(isin and "in\n" or "out\n") fp:close(); fp = nil if not isin and initval then dirp = string.format("%s/value", gpath) fp = io.open(dirp, "wb") if not fp then io.stderr:write(string.format("Error, cannot open %s\n", dirp)) io.stderr:flush() return false end fp:write(string.format("%d\n", initval)) fp:close(); fp = nil end return true end local di_nums = { 291, 468, 471, 469, 470, 286, 298, 297, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389 } local function dido_init(dft) gpio_init(436, false, dft) gpio_init(437, false, dft) gpio_init(438, false, dft) gpio_init(439, false, dft) gpio_init(440, false, dft) gpio_init(442, false, dft) gpio_init(447, false, dft) gpio_init(448, false, dft) gpio_init(291, true) gpio_init(468, true) gpio_init(471, true) gpio_init(469, true) gpio_init(470, true) gpio_init(286, true) gpio_init(298, true) gpio_init(297, true) gpio_init(380, true) gpio_init(381, true) gpio_init(382, true) gpio_init(383, true) gpio_init(384, true) gpio_init(385, true) gpio_init(386, true) gpio_init(387, true) gpio_init(388, true) gpio_init(389, true) end local function netdev_exists(iface) if type(iface) ~= "string" or #iface == 0 then return false -- invalid method argument end local rval = invoker.invoke(invoker.NOSTDIO, "ip", "link", "show", "dev", iface) return rval == 0 end local function get_4gnet() if netdev_exists('modem0') then return 'modem0' end return netdev_exists('eth0') and 'eth0' or 'usb0' end local function get_di_vals() local vals = {} invoker.msleep(1000) for _, num in ipairs(di_nums) do local p = string.format("/sys/class/gpio/gpio%d/value", num) local val = invoker.readfile(p, invoker.TRIMEND) if not val then val = '0' io.stderr:write(string.format("Error, failed to read GPIO: %d\n", num)) io.stderr:flush() end vals[#vals + 1] = val end return table.concat(vals) end local function test_dido() -- output all zeros: dido_init(0) local in0, rval = get_di_vals(), true if in0 ~= "111111111111111111" then rval = false if type(in0) ~= "string" then in0 = "wtf0" end io.stderr:write(string.format("Error, DI/DO-0 test has failed: %s\n", in0)) io.stderr:flush() end -- output all ones: dido_init(1) local in1 = get_di_vals() if in1 ~= "000000000000000000" then rval = false if type(in1) ~= "string" then in1 = "wtf1" end io.stderr:write(string.format("Error, DI/DO-1 test has failed: %s\n", in1)) io.stderr:flush() end if rval then io.stdout:write("Test DI/DO OKAY!!!\n") io.stdout:flush() end return rval end local function test_rs485(p0, p1) local wtf = posix.stat(p0) if type(wtf) ~= "table" or wtf['type'] ~= 'character device' then io.stderr:write(string.format("Error, invalid RS485 device: %s\n", p0)) io.stderr:flush() return false end wtf = posix.stat(p1) if type(wtf) ~= "table" or wtf['type'] ~= 'character device' then io.stderr:write(string.format("Error, invalid RS485 device: %s\n", p1)) io.stderr:flush() return false end -- for debian based system, we have a very strong `stty invoker.invoke(0, 'stty', '-F', p0, "raw", "9600", "cs8", "-hupcl", "-onlcr", "-iexten", "-echo", "-echoe", "-echok", "-echoctl", "-echoke", "-parenb") invoker.invoke(0, 'stty', '-F', p1, "raw", "9600", "cs8", "-hupcl", "-onlcr", "-iexten", "-echo", "-echoe", "-echok", "-echoctl", "-echoke", "-parenb") local rawstr = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' local rstr = invoker.randstr(0x8, rawstr) local tmpfile = "/tmp/tmp-" .. rstr local catpid = invoker.invoke(invoker.NOWAIT, "/bin/sh", "-c", string.format("TTY_READ=1 exec tty_rw %s > %s", p0, tmpfile)) if type(catpid) ~= "number" then io.stderr:write(string.format("Error, failed to execute 'cat %s'\n", p0)) io.stderr:flush() posix.unlink(tmpfile) return false end invoker.msleep(10) invoker.invoke(0, 'tty_rw', p1, string.format('=%s=%s=\n', rstr, rstr)) invoker.msleep(50) invoker.kill(catpid, posix.SIGKILL) invoker.kill(catpid, posix.SIGTERM) invoker.waitpid(catpid, false) local output = invoker.readfile(tmpfile, invoker.TRIMEND) posix.unlink(tmpfile) if type(output) ~= "string" or not string.find(output, rstr, 1, true) then io.stderr:write(string.format("Error, test RS485 port has failed: %s => %s\n", p1, p0)) if type(output) == "string" then io.stderr:write(string.format("\tExpected: %s, output: %s\n", rstr, output)) end io.stderr:flush() return false end io.stdout:write(string.format("INFO: test RS485 port OKAY: %s => %s\n", p1, p0)) if type(output) == "string" then io.stdout:write(string.format("\tExpected: %s, output: %s\n", rstr, output)) end io.stdout:flush() return true end local function setup_can(candev) invoker.invoke(0, 'ip', 'link', 'set', 'dev', candev, 'down') invoker.msleep(100) invoker.invoke(0, 'ip', 'link', 'set', 'dev', candev, 'type', 'can', 'bitrate', '500000', 'triple-sampling', 'on') invoker.msleep(100) invoker.invoke(0, 'ip', 'link', 'set', 'dev', candev, 'up') invoker.msleep(100) end local function can_test(p0, p1) setup_can(p0) setup_can(p1) local cdata = invoker.randstr(16, "12345678ABCDEF") local tmpfile = "/tmp/candump.txt" posix.unlink(tmpfile) local cpid = invoker.invoke(invoker.NOWAIT, "/bin/sh", "-c", string.format("exec candump -L -T 4000 %s > %s 2>/dev/null", p0, tmpfile)) invoker.msleep(50) invoker.invoke(0, "cangen", "-x", "-n", "3", "-I", "0x124", "-D", cdata, p1) invoker.waitpid(cpid, false) local output = invoker.readfile(tmpfile) posix.unlink(tmpfile) if type(output) ~= "string" or not string.find(output, cdata, 1, true) then io.stderr:write(string.format("Error, CAN test has failed: %s => %s\n", p1, p0)) io.stderr:flush() return false end io.stdout:write(string.format("CAN test OKAY: %s => %s\n", p1, p0)) io.stdout:flush() return true end local function update_resolv() local res = invoker.readfile('/etc/resolv.conf') local myres = "nameserver 114.114.114.114\nnameserver 8.8.8.8\n" if res ~= myres then io.stdout:write("Updating /etc/resolv.conf...\n") io.stdout:flush() local filp = io.open('/etc/resolv.conf', 'wb') if filp then filp:write(myres) filp:close() filp = nil end end return true end local function ping_with(ifname, host) if not host then host = 'mqtt.lnxall.com' end if not netdev_hasipv4(ifname) then return false end io.stdout:write(string.format("Ping '%s' with interface '%s'...\n", host, ifname)) io.stdout:flush() local cmds = { "ping", "-c5", "-I", ifname, host } local _, output = invoker.invoke(invoker.OUTPUT + invoker.NOSTDIO, cmds) if type(output) == "string" and string.find(output, "packet loss", 1, true) then if string.find(output, "100% packet loss", 1, true) then return false end return true end return false end local function get_lan_host() local ipaddr = {} local arpfile = io.open("/proc/net/arp", "rb") if not arpfile then io.stderr:write("Error, failed to open arp file.\n") io.stderr:flush() return ipaddr end for line in arpfile:lines() do if string.find(line, "br-lan", 1, true) then local ip = string.match(line, "^([%d%.]+)") if invoker.isipv4(ip) then ipaddr[#ipaddr + 1] = ip end end end if #ipaddr == 0 then io.stderr:write("Error, no device connected to LAN\n") io.stderr:flush() end return ipaddr end local function test_led() local result = {} local leds = { "led1", "led2", "led3", "led4" } for idx = 1, 8 do local wval = string.format("%d\n", bit32.band(idx, 0x1)) for _, led in ipairs(leds) do local okay = false local ledp = string.format("/sys/class/leds/%s/brightness", led) if posix.access(ledp) == 0 then local lfile = io.open(ledp, "wb") if lfile then okay = true lfile:write(wval) lfile:close() lfile = nil end end if okay then result[led] = { interfaces = led, error_code = 0, error_msg = "success" } else result[led] = { interfaces = led, error_code = 1, error_msg = string.format("led_%s not found", led) } end end invoker.msleep(250) end local result_led = {} result_led[1] = result["led1"] result_led[2] = result["led2"] result_led[3] = result["led3"] result_led[3] = result["led4"] return result_led end local function maindiag() finalsummary = {} local summary = {} local okay, facini = invoker.invoke(invoker.OUTPUT, "factory", "get") if okay ~= 0 or type(facini) ~= "string" then io.stderr:write("Error, 'factory get' has failed.\n") io.stderr:flush() return 1 end local gwsn = string.match(facini, "SN=([^\r\n]+)") local hwver = string.match(facini, "HWVER=([^\r\n]+)") or "" local macaddr = string.match(facini, "MAC=([^\r\n]+)") or "" local gwpro = string.match(facini, "PRODUCT=([^\r\n]+)") if not gwsn or not gwpro then io.stderr:write("Error, gateway SN or PRODUCT not found.\n") io.stderr:flush() return 2 end local softver = invoker.readfile("/lib/dyiot/issue", invoker.TRIMEND) if type(softver) ~= "string" or #softver == 0 then softver = invoker.readfile("/etc/issue", invoker.TRIMEND) if type(softver) ~= "string" or #softver == 0 then softver = "unknown" end end finalsummary["sn"] = gwsn finalsummary["timestamp"] = os.time() finalsummary["MAC"] = macaddr finalsummary["HWVER"] = hwver finalsummary["SWVER"] = softver summary["Led"] = test_led() local ports = {} if test_rs485(g_tty[1], g_tty[2]) then ports[#ports + 1] = { interfaces = "RS485_1", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_1", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[2], g_tty[1]) then ports[#ports + 1] = { interfaces = "RS485_2", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_2", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[3], g_tty[4]) then ports[#ports + 1] = { interfaces = "RS485_3", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_3", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[4], g_tty[3]) then ports[#ports + 1] = { interfaces = "RS485_4", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_4", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[5], g_tty[6]) then ports[#ports + 1] = { interfaces = "RS485_5", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_5", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[6], g_tty[5]) then ports[#ports + 1] = { interfaces = "RS485_6", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_6", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[7], g_tty[8]) then ports[#ports + 1] = { interfaces = "RS485_7", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_7", error_msg = "CANNOT WORK", error_code = 1 } end if test_rs485(g_tty[8], g_tty[7]) then ports[#ports + 1] = { interfaces = "RS485_8", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RS485_8", error_msg = "CANNOT WORK", error_code = 1 } end if can_test('can0', 'can1') then ports[#ports + 1] = { interfaces = "CAN0", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "CAN0", error_msg = "CANNOT WORK", error_code = 1 } end if can_test('can1', 'can0') then ports[#ports + 1] = { interfaces = "CAN1", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "CAN1", error_msg = "CANNOT WORK", error_code = 1 } end if posix.access('/dev/rtc0') == 0 then ports[#ports + 1] = { interfaces = "RTC", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "RTC", error_msg = "RTC0 NOT FOUND", error_code = 1 } end if test_dido() then ports[#ports + 1] = { interfaces = "DI_TEST", error_code = 0, error_msg = "success" } ports[#ports + 1] = { interfaces = "DO_TEST", error_code = 0, error_msg = "success" } else ports[#ports + 1] = { interfaces = "DI_TEST", error_code = 1, error_msg = "CANNOT WORK" } ports[#ports + 1] = { interfaces = "DO_TEST", error_code = 1, error_msg = "CANNOT WORK" } end if invoker.mountpoint('/mnt/ssd') then ports[#ports + 1] = { interfaces = "SSD", error_msg = "success", error_code = 0 } else ports[#ports + 1] = { interfaces = "SSD", error_msg = "Solid State Drive Error", error_code = 1 } end summary["OnBoardPort"] = ports local netw = {} update_resolv() if ping_with('net1') or ping_with('net1', '8.8.8.8') or ping_with('net1', '114.114.114.114') then netw[#netw + 1] = { interfaces = "WAN", error_msg = "success", error_code = 0 } else netw[#netw + 1] = { interfaces = "WAN", error_msg = "NOT OKAY", error_code = 1 } end local ndev4g = get_4gnet() update_resolv() if ping_with(ndev4g) or ping_with(ndev4g, '8.8.8.8') or ping_with(ndev4g, '114.114.114.114') then netw[#netw + 1] = { interfaces = "4G-Net", error_msg = "success", error_code = 0 } else netw[#netw + 1] = { interfaces = "4G-Net", error_msg = "NOT OKAY", error_code = 1 } end local landevs, lanokay = get_lan_host(), false for _, ip in pairs(landevs) do if ping_with('br-lan', ip) then lanokay = true break end end if lanokay then netw[#netw + 1] = { interfaces = "LAN", error_msg = "success", error_code = 0 } else netw[#netw + 1] = { interfaces = "LAN", error_msg = "NOT OKAY", error_code = 1 } end summary["NetWork"] = netw finalsummary["data"] = summary io.stdout:write("\n---TEST SUMMARY---\ntestresult:") io.stdout:write(cjson.encode(finalsummary)) io.stdout:write("\n") io.stdout:flush() end posix.setenv("PATH", "/opt/lnxall_app/bin:/usr/bin:/usr/sbin:/bin:/sbin", true) maindiag() os.exit(0)