--[[ LuCI - Lua Configuration Interface Copyright 2013 Xiangfu Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 $Id$ ]]-- module("luci.controller.cgminer", package.seeall) function index() entry({"admin", "status", "cgminer"}, cbi("cgminer/cgminer"), _("Cgminer Configuration"), 90) entry({"admin", "status", "cgminerapi"}, call("action_cgminerapi"), _("Cgminer API Log"), 91) entry({"admin", "status", "cgminerstatus"}, cbi("cgminer/cgminerstatus"), _("Cgminer Status"), 92) entry({"admin", "status", "checkupgrade"}, call("action_checkupgrade"), nil).leaf = true entry({"admin", "status", "cgminerstatus", "restart"}, call("action_cgminerrestart"), nil).leaf = true entry({"admin", "status", "set_miningmode"}, call("action_setminingmode"), nil).leaf = true entry({"admin", "status", "cgminerdebug"}, call("action_cgminerdebug"), nil).leaf = true end function action_cgminerrestart() luci.util.exec("/etc/init.d/cgminer restart") luci.http.redirect( luci.dispatcher.build_url("admin", "status", "cgminerstatus") ) end function action_cgminerapi() local pp = io.popen("echo -n \"[Firmware Version] => \"; cat /etc/avalon_version; /usr/bin/cgminer-api stats;") local data = pp:read("*a") pp:close() luci.template.render("cgminerapi", {api=data}) end function num_commas(n) return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,"):gsub(",(%-?)$","%1"):reverse() end function valuetodate(elapsed) if elapsed then local str local days local h local m local s = elapsed % 60; elapsed = elapsed - s elapsed = elapsed / 60 if elapsed == 0 then str = string.format("%ds", s) else m = elapsed % 60; elapsed = elapsed - m elapsed = elapsed / 60 if elapsed == 0 then str = string.format("%dm %ds", m, s); else h = elapsed % 24; elapsed = elapsed - h elapsed = elapsed / 24 if elapsed == 0 then str = string.format("%dh %dm %ds", h, m, s) else str = string.format("%dd %dh %dm %ds", elapsed, h, m, s); end end end return str end return "date invalid" end function summary() local data = {} local summary = luci.util.execi("/usr/bin/cgminer-api -o summary | sed \"s/|/\\n/g\" ") if not summary then return end for line in summary do local elapsed, mhsav, foundblocks, getworks, accepted, rejected, hw, utility, stale, getfailures, remotefailures, networkblocks, totalmh, diffaccepted, diffrejected, diffstale, bestshare = line:match(".*," .. "Elapsed=(-?%d+)," .. "MHS av=(-?[%d%.]+)," .. ".*," .. "Found Blocks=(-?%d+)," .. "Getworks=(-?%d+)," .. "Accepted=(-?%d+)," .. "Rejected=(-?%d+)," .. "Hardware Errors=(-?%d+)," .. "Utility=([-?%d%.]+)," .. ".*," .. "Stale=(-?%d+)," .. "Get Failures=(-?%d+)," .. ".-" .. "Remote Failures=(-?%d+)," .. "Network Blocks=(-?%d+)," .. "Total MH=(-?[%d%.]+)," .. ".-" .. "Difficulty Accepted=(-?[%d]+)%.%d+," .. "Difficulty Rejected=(-?[%d]+)%.%d+," .. "Difficulty Stale=(-?[%d]+)%.%d+," .. "Best Share=(-?%d+)") if elapsed then data[#data+1] = { ['elapsed'] = valuetodate(elapsed), ['mhsav'] = num_commas(mhsav), ['foundblocks'] = foundblocks, ['getworks'] = num_commas(getworks), ['accepted'] = num_commas(accepted), ['rejected'] = num_commas(rejected), ['hw'] = num_commas(hw), ['utility'] = num_commas(utility), ['stale'] = stale, ['getfailures'] = getfailures, ['remotefailures'] = remotefailures, ['networkblocks'] = networkblocks, ['totalmh'] = string.format("%e",totalmh), ['diffaccepted'] = num_commas(diffaccepted), ['diffrejected'] = num_commas(diffrejected), ['diffstale'] = diffstale, ['bestshare'] = num_commas(bestshare) } end end return data end function pools() local data = {} local pools = luci.util.execi("/usr/bin/cgminer-api -o pools | sed \"s/|/\\n/g\" ") if not pools then return end for line in pools do local pi, url, st, pri, quo, lp, gw, a, r, sta, gf, rf, user, lst, ds, da, dr, dsta, lsd, hs, sa, sd, hg = line:match("POOL=(-?%d+)," .. "URL=(.*)," .. "Status=(%a+)," .. "Priority=(-?%d+)," .. "Quota=(-?%d+)," .. "Long Poll=(%a+)," .. "Getworks=(-?%d+)," .. "Accepted=(-?%d+)," .. "Rejected=(-?%d+)," .. ".*," .. "Stale=(-?%d+)," .. "Get Failures=(-?%d+)," .. "Remote Failures=(-?%d+)," .. "User=(.*)," .. "Last Share Time=(-?%d+)," .. "Diff1 Shares=(-?%d+)," .. ".*," .. "Difficulty Accepted=(-?%d+)[%.%d]+," .. "Difficulty Rejected=(-?%d+)[%.%d]+," .. "Difficulty Stale=(-?%d+)[%.%d]+," .. "Last Share Difficulty=(-?%d+)[%.%d]+," .. ".-," .. "Has Stratum=(%a+)," .. "Stratum Active=(%a+)," .. ".-," .. "Stratum Difficulty=(-?%d+)[%.%d]+," .. "Has GBT=(%a+)") if pi then if lst == "0" then lst_date = "Never" else lst_date = os.date("%c", lst) end data[#data+1] = { ['pool'] = pi, ['url'] = url, ['status'] = st, ['priority'] = pri, ['quota'] = quo, ['longpoll'] = lp, ['getworks'] = gw, ['accepted'] = a, ['rejected'] = r, ['stale'] = sta, ['getfailures'] = gf, ['remotefailures'] = rf, ['user'] = user, ['lastsharetime'] = lst_date, ['diff1shares'] = ds, ['diffaccepted'] = da, ['diffrejected'] = dr, ['diffstale'] = dsta, ['lastsharedifficulty'] = lsd, ['hasstratum'] = hs, ['stratumactive'] = sa, ['stratumdifficulty'] = sd, ['hasgbt'] = hg } end end return data end function devs() local data = {} local devs = luci.util.execi("/usr/bin/cgminer-api -o edevs | sed \"s/|/\\n/g\" ") if not devs then return end for line in devs do local asc, name, id, enabled, status, temp, mhsav, mhs5s, mhs1m, mhs5m, mhs15m, lvw, dh = line:match("ASC=(%d+)," .. "Name=([%a%d]+)," .. "ID=(%d+)," .. "Enabled=(%a+)," .. "Status=(%a+)," .. "Temperature=(-?[%d]+).%d+," .. "MHS av=(-?[%.%d]+)," .. "MHS 5s=(-?[%.%d]+)," .. "MHS 1m=(-?[%.%d]+)," .. "MHS 5m=(-?[%.%d]+)," .. "MHS 15m=(-?[%.%d]+)," .. ".*," .. "Last Valid Work=(-?%d+)," .. "Device Hardware%%=(-?[%.%d]+)") if lvw == "0" then lvw_date = "Never" else lvw_date = os.date("%c", lst) end if asc then data[#data+1] = { ['name'] = "ASC" .. asc .. "-" .. name .. "-" .. id, ['enable'] = enabled, ['status'] = status, ['temp'] = temp, ['mhsav'] = mhsav, ['mhs5s'] = mhs5s, ['mhs1m'] = mhs1m, ['mhs5m'] = mhs5m, ['mhs15m'] = mhs15m, ['lvw'] = lvw_date } end end return data end function stats() local data = {} local stats = luci.util.execi("/usr/bin/cgminer-api -o estats | sed \"s/|/\\n/g\" | grep AV4 ") if not stats then return end for line in stats do local id = line:match(".*" .. "ID=AV4([%d]+),") if id then local istart, iend = line:find("MM ID") while (istart) do local istr = line:sub(istart) local idname local index, idn, dnan, elapsedn, lwn, tempn, temp0n, temp1n, fann, voln, ghsmm, pgn, ledn, ecn = istr:match("MM ID(%d+)=" .. "Ver%[([%+%-%d%a]+)%]" .. ".-" .. "DNA%[(%x+)%]" .. ".-" .. "Elapsed%[(-?%d+)%]" .. ".-" .. "LW%[(-?%d+)%]" .. ".-" .. "Temp%[(-?%d+)%]" .. ".-" .. "Temp0%[(-?%d+)%]" .. ".-" .. "Temp1%[(-?%d+)%]" .. ".-" .. "Fan%[(-?%d+)%]" .. ".-" .. "Vol%[(-?[%.%d]+)%]" .. ".-" .. "GHSmm%[(-?[%.%d]+)%]" .. ".-" .. "PG%[(%d+)%]" .. ".-" .. "Led%[(%d)%]" .. ".-" .. "EC%[(%d+)%]") if idn ~= nil then if string.sub(idn, 1, 2) == '60' then idname = 'A60S-' else idname = 'AV4-' end data[#data+1] = { ['devid'] = id, ['moduleid'] = tostring(index), ['id'] = idname .. id .. '-' .. tostring(index), ['mm'] = idn, ['dna'] = string.sub(dnan, -4, -1), ['elapsed'] = valuetodate(elapsedn), ['lw'] = lwn or '0', ['temp'] = (tempn or '0') .. ' ' .. (temp0n or '0') .. ' ' .. (temp1n or '0'), ['fan'] = fann or '0', ['voltage'] = voln or '0', ['ss'] = 'Enable', ['ghsmm'] = ghsmm or '0', ['pg'] = pgn or '0', ['led'] = ledn or '0', ['ec'] = ecn or '0' } end istart, iend = line:find("MM ID", iend + 1) end end end return data end function action_setminingmode() local uci = luci.model.uci.cursor() local mmode = luci.http.formvalue("mining_mode") local modetab = { customs = " ", normal = "-c /etc/config/a4.normal", eco = "-c /etc/config/a4.eco", turbo = "-c /etc/config/a4.turbo" } if modetab[mmode] then uci:set("cgminer", "default", "mining_mode", modetab[mmode]) uci:save("cgminer") uci:commit("cgminer") if mmode == "customs" then luci.http.redirect( luci.dispatcher.build_url("admin", "status", "cgminer") ) else action_cgminerrestart() end end end function action_mmupgrade() local mm_tmp = "/tmp/mm.mcs" local finish_flag = "/tmp/mm_finish" local function mm_upgrade_avail() if nixio.fs.access("/usr/bin/mm-tools") then return true end return nil end local function mm_supported() local mm_tmp = "/tmp/mm.mcs" if not nixio.fs.access(mm_tmp) then return false end local filesize = nixio.fs.stat(mm_tmp).size -- TODO: Check mm.mcs format if filesize == 0 then return false end return true end local function mm_checksum() return (luci.sys.exec("md5sum %q" % mm_tmp):match("^([^%s]+)")) end local function storage_size() local size = 0 if nixio.fs.access("/proc/mtd") then for l in io.lines("/proc/mtd") do local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"') if n == "linux" or n == "firmware" then size = tonumber(s, 16) break end end elseif nixio.fs.access("/proc/partitions") then for l in io.lines("/proc/partitions") do local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)') if b and n and not n:match('[0-9]') then size = tonumber(b) * 1024 break end end end return size end local fp luci.http.setfilehandler( function(meta, chunk, eof) if not fp then if meta and meta.name == "image" then fp = io.open(mm_tmp, "w") end end if chunk then fp:write(chunk) end if eof and fp then fp:close() end end ) if luci.http.formvalue("image") or luci.http.formvalue("step") then -- -- Check firmware -- local step = tonumber(luci.http.formvalue("step") or 1) if step == 1 then if mm_supported() == true then luci.template.render("mmupgrade", { checksum = mm_checksum(), storage = storage_size(), size = nixio.fs.stat(mm_tmp).size, }) else nixio.fs.unlink(mm_tmp) luci.template.render("mmupload", { mm_upgrade_avail = mm_upgrade_avail(), mm_image_invalid = true }) end -- -- Upgrade firmware -- elseif step == 2 then luci.template.render("mmapply") fork_exec("insmod i2c-dev.ko;sleep 1;mmupgrade;touch %q;rmmod i2c-dev" %{ finish_flag }) elseif step == 3 then nixio.fs.unlink(finish_flag) luci.template.render("mmapply", { finish = 1 }) end else luci.template.render("mmupload", { mm_upgrade_avail = mm_upgrade_avail() }) end end function action_checkupgrade() local status = {} local finish_flag = "/tmp/mm_finish" if not nixio.fs.access(finish_flag) then status.finish = 0 else status.finish = 1 end luci.http.prepare_content("application/json") luci.http.write_json(status) end function fork_exec(command) local pid = nixio.fork() if pid > 0 then return elseif pid == 0 then -- change to root dir nixio.chdir("/") -- patch stdin, out, err to /dev/null local null = nixio.open("/dev/null", "w+") if null then nixio.dup(null, nixio.stderr) nixio.dup(null, nixio.stdout) nixio.dup(null, nixio.stdin) if null:fileno() > 2 then null:close() end end -- replace with target command nixio.exec("/bin/sh", "-c", command) end end function action_cgminerdebug() luci.util.exec("cgminer-api \"debug|D\"") luci.http.redirect( luci.dispatcher.build_url("admin", "status", "cgminerapi") ) end