cgminer.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. --[[
  2. LuCI - Lua Configuration Interface
  3. Copyright 2013 Xiangfu
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. $Id$
  9. ]]--
  10. module("luci.controller.cgminer", package.seeall)
  11. function index()
  12. entry({"admin", "status", "cgminer"}, cbi("cgminer/cgminer"), _("Cgminer Configuration"), 90)
  13. entry({"admin", "status", "cgminerapi"}, call("action_cgminerapi"), _("Cgminer API Log"), 91)
  14. entry({"admin", "status", "cgminerstatus"}, cbi("cgminer/cgminerstatus"), _("Cgminer Status"), 92)
  15. entry({"admin", "status", "checkupgrade"}, call("action_checkupgrade"), nil).leaf = true
  16. entry({"admin", "status", "cgminerstatus", "restart"}, call("action_cgminerrestart"), nil).leaf = true
  17. entry({"admin", "status", "set_miningmode"}, call("action_setminingmode"), nil).leaf = true
  18. entry({"admin", "status", "cgminerdebug"}, call("action_cgminerdebug"), nil).leaf = true
  19. end
  20. function action_cgminerrestart()
  21. luci.util.exec("/etc/init.d/cgminer restart")
  22. luci.http.redirect(
  23. luci.dispatcher.build_url("admin", "status", "cgminerstatus")
  24. )
  25. end
  26. function action_cgminerapi()
  27. local pp = io.popen("echo -n \"[Firmware Version] => \"; cat /etc/avalon_version; /usr/bin/cgminer-api stats;")
  28. local data = pp:read("*a")
  29. pp:close()
  30. luci.template.render("cgminerapi", {api=data})
  31. end
  32. function num_commas(n)
  33. return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,"):gsub(",(%-?)$","%1"):reverse()
  34. end
  35. function valuetodate(elapsed)
  36. if elapsed then
  37. local str
  38. local days
  39. local h
  40. local m
  41. local s = elapsed % 60;
  42. elapsed = elapsed - s
  43. elapsed = elapsed / 60
  44. if elapsed == 0 then
  45. str = string.format("%ds", s)
  46. else
  47. m = elapsed % 60;
  48. elapsed = elapsed - m
  49. elapsed = elapsed / 60
  50. if elapsed == 0 then
  51. str = string.format("%dm %ds", m, s);
  52. else
  53. h = elapsed % 24;
  54. elapsed = elapsed - h
  55. elapsed = elapsed / 24
  56. if elapsed == 0 then
  57. str = string.format("%dh %dm %ds", h, m, s)
  58. else
  59. str = string.format("%dd %dh %dm %ds", elapsed, h, m, s);
  60. end
  61. end
  62. end
  63. return str
  64. end
  65. return "date invalid"
  66. end
  67. function summary()
  68. local data = {}
  69. local summary = luci.util.execi("/usr/bin/cgminer-api -o summary | sed \"s/|/\\n/g\" ")
  70. if not summary then
  71. return
  72. end
  73. for line in summary do
  74. local elapsed, mhsav, foundblocks, getworks, accepted,
  75. rejected, hw, utility, stale, getfailures,
  76. remotefailures, networkblocks, totalmh,
  77. diffaccepted, diffrejected, diffstale, bestshare =
  78. line:match(".*," ..
  79. "Elapsed=(-?%d+)," ..
  80. "MHS av=(-?[%d%.]+)," ..
  81. ".*," ..
  82. "Found Blocks=(-?%d+)," ..
  83. "Getworks=(-?%d+)," ..
  84. "Accepted=(-?%d+)," ..
  85. "Rejected=(-?%d+)," ..
  86. "Hardware Errors=(-?%d+)," ..
  87. "Utility=([-?%d%.]+)," ..
  88. ".*," ..
  89. "Stale=(-?%d+)," ..
  90. "Get Failures=(-?%d+)," ..
  91. ".-" ..
  92. "Remote Failures=(-?%d+)," ..
  93. "Network Blocks=(-?%d+)," ..
  94. "Total MH=(-?[%d%.]+)," ..
  95. ".-" ..
  96. "Difficulty Accepted=(-?[%d]+)%.%d+," ..
  97. "Difficulty Rejected=(-?[%d]+)%.%d+," ..
  98. "Difficulty Stale=(-?[%d]+)%.%d+," ..
  99. "Best Share=(-?%d+)")
  100. if elapsed then
  101. data[#data+1] = {
  102. ['elapsed'] = valuetodate(elapsed),
  103. ['mhsav'] = num_commas(mhsav),
  104. ['foundblocks'] = foundblocks,
  105. ['getworks'] = num_commas(getworks),
  106. ['accepted'] = num_commas(accepted),
  107. ['rejected'] = num_commas(rejected),
  108. ['hw'] = num_commas(hw),
  109. ['utility'] = num_commas(utility),
  110. ['stale'] = stale,
  111. ['getfailures'] = getfailures,
  112. ['remotefailures'] = remotefailures,
  113. ['networkblocks'] = networkblocks,
  114. ['totalmh'] = string.format("%e",totalmh),
  115. ['diffaccepted'] = num_commas(diffaccepted),
  116. ['diffrejected'] = num_commas(diffrejected),
  117. ['diffstale'] = diffstale,
  118. ['bestshare'] = num_commas(bestshare)
  119. }
  120. end
  121. end
  122. return data
  123. end
  124. function pools()
  125. local data = {}
  126. local pools = luci.util.execi("/usr/bin/cgminer-api -o pools | sed \"s/|/\\n/g\" ")
  127. if not pools then
  128. return
  129. end
  130. for line in pools do
  131. local pi, url, st, pri, quo, lp, gw, a, r, sta, gf,
  132. rf, user, lst, ds, da, dr, dsta, lsd, hs, sa, sd, hg =
  133. line:match("POOL=(-?%d+)," ..
  134. "URL=(.*)," ..
  135. "Status=(%a+)," ..
  136. "Priority=(-?%d+)," ..
  137. "Quota=(-?%d+)," ..
  138. "Long Poll=(%a+)," ..
  139. "Getworks=(-?%d+)," ..
  140. "Accepted=(-?%d+)," ..
  141. "Rejected=(-?%d+)," ..
  142. ".*," ..
  143. "Stale=(-?%d+)," ..
  144. "Get Failures=(-?%d+)," ..
  145. "Remote Failures=(-?%d+)," ..
  146. "User=(.*)," ..
  147. "Last Share Time=(-?%d+)," ..
  148. "Diff1 Shares=(-?%d+)," ..
  149. ".*," ..
  150. "Difficulty Accepted=(-?%d+)[%.%d]+," ..
  151. "Difficulty Rejected=(-?%d+)[%.%d]+," ..
  152. "Difficulty Stale=(-?%d+)[%.%d]+," ..
  153. "Last Share Difficulty=(-?%d+)[%.%d]+," ..
  154. ".-," ..
  155. "Has Stratum=(%a+)," ..
  156. "Stratum Active=(%a+)," ..
  157. ".-," ..
  158. "Stratum Difficulty=(-?%d+)[%.%d]+," ..
  159. "Has GBT=(%a+)")
  160. if pi then
  161. if lst == "0" then
  162. lst_date = "Never"
  163. else
  164. lst_date = os.date("%c", lst)
  165. end
  166. data[#data+1] = {
  167. ['pool'] = pi,
  168. ['url'] = url,
  169. ['status'] = st,
  170. ['priority'] = pri,
  171. ['quota'] = quo,
  172. ['longpoll'] = lp,
  173. ['getworks'] = gw,
  174. ['accepted'] = a,
  175. ['rejected'] = r,
  176. ['stale'] = sta,
  177. ['getfailures'] = gf,
  178. ['remotefailures'] = rf,
  179. ['user'] = user,
  180. ['lastsharetime'] = lst_date,
  181. ['diff1shares'] = ds,
  182. ['diffaccepted'] = da,
  183. ['diffrejected'] = dr,
  184. ['diffstale'] = dsta,
  185. ['lastsharedifficulty'] = lsd,
  186. ['hasstratum'] = hs,
  187. ['stratumactive'] = sa,
  188. ['stratumdifficulty'] = sd,
  189. ['hasgbt'] = hg
  190. }
  191. end
  192. end
  193. return data
  194. end
  195. function devs()
  196. local data = {}
  197. local devs = luci.util.execi("/usr/bin/cgminer-api -o edevs | sed \"s/|/\\n/g\" ")
  198. if not devs then
  199. return
  200. end
  201. for line in devs do
  202. local asc, name, id, enabled, status, temp, mhsav, mhs5s, mhs1m, mhs5m, mhs15m, lvw, dh =
  203. line:match("ASC=(%d+)," ..
  204. "Name=([%a%d]+)," ..
  205. "ID=(%d+)," ..
  206. "Enabled=(%a+)," ..
  207. "Status=(%a+)," ..
  208. "Temperature=(-?[%d]+).%d+," ..
  209. "MHS av=(-?[%.%d]+)," ..
  210. "MHS 5s=(-?[%.%d]+)," ..
  211. "MHS 1m=(-?[%.%d]+)," ..
  212. "MHS 5m=(-?[%.%d]+)," ..
  213. "MHS 15m=(-?[%.%d]+)," ..
  214. ".*," ..
  215. "Last Valid Work=(-?%d+)," ..
  216. "Device Hardware%%=(-?[%.%d]+)")
  217. if lvw == "0" then
  218. lvw_date = "Never"
  219. else
  220. lvw_date = os.date("%c", lst)
  221. end
  222. if asc then
  223. data[#data+1] = {
  224. ['name'] = "ASC" .. asc .. "-" .. name .. "-" .. id,
  225. ['enable'] = enabled,
  226. ['status'] = status,
  227. ['temp'] = temp,
  228. ['mhsav'] = mhsav,
  229. ['mhs5s'] = mhs5s,
  230. ['mhs1m'] = mhs1m,
  231. ['mhs5m'] = mhs5m,
  232. ['mhs15m'] = mhs15m,
  233. ['lvw'] = lvw_date
  234. }
  235. end
  236. end
  237. return data
  238. end
  239. function stats()
  240. local data = {}
  241. local stats = luci.util.execi("/usr/bin/cgminer-api -o estats | sed \"s/|/\\n/g\" | grep AV4 ")
  242. if not stats then
  243. return
  244. end
  245. for line in stats do
  246. local id =
  247. line:match(".*" ..
  248. "ID=AV4([%d]+),")
  249. if id then
  250. local istart, iend = line:find("MM ID")
  251. while (istart) do
  252. local istr = line:sub(istart)
  253. local idname
  254. local index, idn, dnan, elapsedn, lwn, tempn, temp0n, temp1n, fann, voln, ghsmm, pgn, ledn, ecn =
  255. istr:match("MM ID(%d+)=" ..
  256. "Ver%[([%+%-%d%a]+)%]" ..
  257. ".-" ..
  258. "DNA%[(%x+)%]" ..
  259. ".-" ..
  260. "Elapsed%[(-?%d+)%]" ..
  261. ".-" ..
  262. "LW%[(-?%d+)%]" ..
  263. ".-" ..
  264. "Temp%[(-?%d+)%]" ..
  265. ".-" ..
  266. "Temp0%[(-?%d+)%]" ..
  267. ".-" ..
  268. "Temp1%[(-?%d+)%]" ..
  269. ".-" ..
  270. "Fan%[(-?%d+)%]" ..
  271. ".-" ..
  272. "Vol%[(-?[%.%d]+)%]" ..
  273. ".-" ..
  274. "GHSmm%[(-?[%.%d]+)%]" ..
  275. ".-" ..
  276. "PG%[(%d+)%]" ..
  277. ".-" ..
  278. "Led%[(%d)%]" ..
  279. ".-" ..
  280. "EC%[(%d+)%]")
  281. if idn ~= nil then
  282. if string.sub(idn, 1, 2) == '60' then
  283. idname = 'A60S-'
  284. else
  285. idname = 'AV4-'
  286. end
  287. data[#data+1] = {
  288. ['devid'] = id,
  289. ['moduleid'] = tostring(index),
  290. ['id'] = idname .. id .. '-' .. tostring(index),
  291. ['mm'] = idn,
  292. ['dna'] = string.sub(dnan, -4, -1),
  293. ['elapsed'] = valuetodate(elapsedn),
  294. ['lw'] = lwn or '0',
  295. ['temp'] = (tempn or '0') .. ' ' .. (temp0n or '0') .. ' ' .. (temp1n or '0'),
  296. ['fan'] = fann or '0',
  297. ['voltage'] = voln or '0',
  298. ['ss'] = 'Enable',
  299. ['ghsmm'] = ghsmm or '0',
  300. ['pg'] = pgn or '0',
  301. ['led'] = ledn or '0',
  302. ['ec'] = ecn or '0'
  303. }
  304. end
  305. istart, iend = line:find("MM ID", iend + 1)
  306. end
  307. end
  308. end
  309. return data
  310. end
  311. function action_setminingmode()
  312. local uci = luci.model.uci.cursor()
  313. local mmode = luci.http.formvalue("mining_mode")
  314. local modetab = {
  315. customs = " ",
  316. normal = "-c /etc/config/a4.normal",
  317. eco = "-c /etc/config/a4.eco",
  318. turbo = "-c /etc/config/a4.turbo"
  319. }
  320. if modetab[mmode] then
  321. uci:set("cgminer", "default", "mining_mode", modetab[mmode])
  322. uci:save("cgminer")
  323. uci:commit("cgminer")
  324. if mmode == "customs" then
  325. luci.http.redirect(
  326. luci.dispatcher.build_url("admin", "status", "cgminer")
  327. )
  328. else
  329. action_cgminerrestart()
  330. end
  331. end
  332. end
  333. function action_mmupgrade()
  334. local mm_tmp = "/tmp/mm.mcs"
  335. local finish_flag = "/tmp/mm_finish"
  336. local function mm_upgrade_avail()
  337. if nixio.fs.access("/usr/bin/mm-tools") then
  338. return true
  339. end
  340. return nil
  341. end
  342. local function mm_supported()
  343. local mm_tmp = "/tmp/mm.mcs"
  344. if not nixio.fs.access(mm_tmp) then
  345. return false
  346. end
  347. local filesize = nixio.fs.stat(mm_tmp).size
  348. -- TODO: Check mm.mcs format
  349. if filesize == 0 then
  350. return false
  351. end
  352. return true
  353. end
  354. local function mm_checksum()
  355. return (luci.sys.exec("md5sum %q" % mm_tmp):match("^([^%s]+)"))
  356. end
  357. local function storage_size()
  358. local size = 0
  359. if nixio.fs.access("/proc/mtd") then
  360. for l in io.lines("/proc/mtd") do
  361. local d, s, e, n = l:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)%s+"([^%s]+)"')
  362. if n == "linux" or n == "firmware" then
  363. size = tonumber(s, 16)
  364. break
  365. end
  366. end
  367. elseif nixio.fs.access("/proc/partitions") then
  368. for l in io.lines("/proc/partitions") do
  369. local x, y, b, n = l:match('^%s*(%d+)%s+(%d+)%s+([^%s]+)%s+([^%s]+)')
  370. if b and n and not n:match('[0-9]') then
  371. size = tonumber(b) * 1024
  372. break
  373. end
  374. end
  375. end
  376. return size
  377. end
  378. local fp
  379. luci.http.setfilehandler(
  380. function(meta, chunk, eof)
  381. if not fp then
  382. if meta and meta.name == "image" then
  383. fp = io.open(mm_tmp, "w")
  384. end
  385. end
  386. if chunk then
  387. fp:write(chunk)
  388. end
  389. if eof and fp then
  390. fp:close()
  391. end
  392. end
  393. )
  394. if luci.http.formvalue("image") or luci.http.formvalue("step") then
  395. --
  396. -- Check firmware
  397. --
  398. local step = tonumber(luci.http.formvalue("step") or 1)
  399. if step == 1 then
  400. if mm_supported() == true then
  401. luci.template.render("mmupgrade", {
  402. checksum = mm_checksum(),
  403. storage = storage_size(),
  404. size = nixio.fs.stat(mm_tmp).size,
  405. })
  406. else
  407. nixio.fs.unlink(mm_tmp)
  408. luci.template.render("mmupload", {
  409. mm_upgrade_avail = mm_upgrade_avail(),
  410. mm_image_invalid = true
  411. })
  412. end
  413. --
  414. -- Upgrade firmware
  415. --
  416. elseif step == 2 then
  417. luci.template.render("mmapply")
  418. fork_exec("insmod i2c-dev.ko;sleep 1;mmupgrade;touch %q;rmmod i2c-dev" %{ finish_flag })
  419. elseif step == 3 then
  420. nixio.fs.unlink(finish_flag)
  421. luci.template.render("mmapply", {
  422. finish = 1
  423. })
  424. end
  425. else
  426. luci.template.render("mmupload", {
  427. mm_upgrade_avail = mm_upgrade_avail()
  428. })
  429. end
  430. end
  431. function action_checkupgrade()
  432. local status = {}
  433. local finish_flag = "/tmp/mm_finish"
  434. if not nixio.fs.access(finish_flag) then
  435. status.finish = 0
  436. else
  437. status.finish = 1
  438. end
  439. luci.http.prepare_content("application/json")
  440. luci.http.write_json(status)
  441. end
  442. function fork_exec(command)
  443. local pid = nixio.fork()
  444. if pid > 0 then
  445. return
  446. elseif pid == 0 then
  447. -- change to root dir
  448. nixio.chdir("/")
  449. -- patch stdin, out, err to /dev/null
  450. local null = nixio.open("/dev/null", "w+")
  451. if null then
  452. nixio.dup(null, nixio.stderr)
  453. nixio.dup(null, nixio.stdout)
  454. nixio.dup(null, nixio.stdin)
  455. if null:fileno() > 2 then
  456. null:close()
  457. end
  458. end
  459. -- replace with target command
  460. nixio.exec("/bin/sh", "-c", command)
  461. end
  462. end
  463. function action_cgminerdebug()
  464. luci.util.exec("cgminer-api \"debug|D\"")
  465. luci.http.redirect(
  466. luci.dispatcher.build_url("admin", "status", "cgminerapi")
  467. )
  468. end