mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Merge various information into systemstat (#2764)
* Merge various information to systemstat
This commit is contained in:
@@ -35,7 +35,8 @@ Source: <a href="http://snippets.luacode.org/snippets/String_splitting_130">http
|
||||
----@string str string to split
|
||||
----@param pattern the pattern to split against
|
||||
----@bool capture
|
||||
function util.gsplit(str, pattern, capture)
|
||||
----@bool capture_empty_entity
|
||||
function util.gsplit(str, pattern, capture, capture_empty_entity)
|
||||
pattern = pattern and tostring(pattern) or '%s+'
|
||||
if (''):find(pattern) then
|
||||
error('pattern matches empty string!', 2)
|
||||
@@ -45,7 +46,7 @@ function util.gsplit(str, pattern, capture)
|
||||
repeat
|
||||
local first, last = str:find(pattern, index)
|
||||
if first and last then
|
||||
if index < first then
|
||||
if index < first or (index == first and capture_empty_entity) then
|
||||
coroutine.yield(str:sub(index, first - 1))
|
||||
end
|
||||
if capture then
|
||||
@@ -372,4 +373,18 @@ function util.fixUtf8(str, replacement)
|
||||
return str
|
||||
end
|
||||
|
||||
--- Splits input string with the splitter into a table. This function ignores the last empty entity.
|
||||
--
|
||||
--- @string str the string to be split
|
||||
--- @string splitter
|
||||
--- @bool capture_empty_entity
|
||||
--- @treturn an array-like table
|
||||
function util.splitToArray(str, splitter, capture_empty_entity)
|
||||
local result = {}
|
||||
for word in util.gsplit(str, splitter, false, capture_empty_entity) do
|
||||
table.insert(result, word)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
return util
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
|
||||
local Device = require("device")
|
||||
|
||||
local filter
|
||||
|
||||
-- TODO(Hzj_jie): Find the right filter for PocketBook
|
||||
if Device:isKobo() or Device:isPocketBook() then
|
||||
filter = "mmcblk"
|
||||
elseif Device:isKindle() then
|
||||
filter = "' /mnt/us$'"
|
||||
elseif Device:isSDL() then
|
||||
filter = "/dev/sd"
|
||||
else
|
||||
return { disabled = true, }
|
||||
end
|
||||
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local _ = require("gettext")
|
||||
|
||||
local StorageStat = WidgetContainer:new{
|
||||
name = "storagestat",
|
||||
menuItem = {
|
||||
text = _("Storage statistics"),
|
||||
callback = function()
|
||||
local std_out = io.popen(
|
||||
"df -h | sed -r 's/ +/ /g' | grep " .. filter ..
|
||||
" | cut -d ' ' -f 2,3,4,5,6 | " ..
|
||||
"awk '{print $5\": \\n Available: \" $3\"/\" $1 \"\\n Used: \" $4}'"
|
||||
)
|
||||
local msg
|
||||
if std_out then
|
||||
msg = std_out:read("*all")
|
||||
std_out:close()
|
||||
end
|
||||
if msg == nil or msg == "" then
|
||||
msg = _("Failed to retrieve storage information.")
|
||||
end
|
||||
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = msg,
|
||||
})
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
function StorageStat:init()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
function StorageStat:addToMainMenu(menu_items)
|
||||
menu_items.storage_stat = self.menuItem
|
||||
end
|
||||
|
||||
return StorageStat
|
||||
@@ -1,6 +1,8 @@
|
||||
local Device = require("device")
|
||||
local KeyValuePage = require("ui/widget/keyvaluepage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
|
||||
local SystemStat = {
|
||||
@@ -13,6 +15,197 @@ local SystemStat = {
|
||||
discharge_count = 0,
|
||||
}
|
||||
|
||||
function SystemStat:init()
|
||||
if Device:isKobo() or Device:isPocketBook() then
|
||||
self.storage_filter = "mmcblk"
|
||||
elseif Device:isKindle() then
|
||||
self.storage_filter = "' /mnt/us$'"
|
||||
elseif Device:isSDL() then
|
||||
self.storage_filter = "/dev/sd"
|
||||
end
|
||||
end
|
||||
|
||||
function SystemStat:put(p)
|
||||
table.insert(self.kv_pairs, p)
|
||||
end
|
||||
|
||||
function SystemStat:appendCounters()
|
||||
self:put({_("KOReader Started at"), os.date("%c", self.start_sec)})
|
||||
if self.suspend_sec then
|
||||
self:put({_(" Last suspend time"), os.date("%c", self.suspend_sec)})
|
||||
end
|
||||
if self.resume_sec then
|
||||
self:put({_(" Last resume time"), os.date("%c", self.resume_sec)})
|
||||
end
|
||||
self:put({_(" Up hours"),
|
||||
string.format("%.2f", os.difftime(os.time(), self.start_sec) / 60 / 60)})
|
||||
self:put({_("Counters"), ""})
|
||||
self:put({_(" wake-ups"), self.wakeup_count})
|
||||
self:put({_(" sleeps"), self.sleep_count})
|
||||
self:put({_(" charge cycles"), self.charge_count})
|
||||
self:put({_(" discharge cycles"), self.discharge_count})
|
||||
end
|
||||
|
||||
local function systemInfo()
|
||||
local result = {}
|
||||
do
|
||||
local stat = io.open("/proc/stat", "r")
|
||||
if stat ~= nil then
|
||||
for line in util.gsplit(stat:read("*all"), "\n", false) do
|
||||
local t = util.splitToArray(line, " ")
|
||||
if #t >= 5 and string.lower(t[1]) == "cpu" then
|
||||
local n1, n2, n3, n4
|
||||
n1 = tonumber(t[2])
|
||||
n2 = tonumber(t[3])
|
||||
n3 = tonumber(t[4])
|
||||
n4 = tonumber(t[5])
|
||||
if n1 ~= nil and n2 ~= nil and n3 ~= nil and n4 ~= nil then
|
||||
result.cpu = {
|
||||
user = n1,
|
||||
nice = n2,
|
||||
system = n3,
|
||||
idle = n4,
|
||||
total = n1 + n2 + n3 + n4
|
||||
}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
stat:close()
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local meminfo = io.open("/proc/meminfo", "r")
|
||||
if meminfo ~= nil then
|
||||
result.memory = {}
|
||||
for line in util.gsplit(meminfo:read("*all"), "\n", false) do
|
||||
local t = util.splitToArray(line, " ")
|
||||
if #t >= 2 then
|
||||
if string.lower(t[1]) == "memtotal:" then
|
||||
local n = tonumber(t[2])
|
||||
if n ~= nil then
|
||||
result.memory.total = n
|
||||
end
|
||||
elseif string.lower(t[1]) == "memfree:" then
|
||||
local n = tonumber(t[2])
|
||||
if n ~= nil then
|
||||
result.memory.free = n
|
||||
end
|
||||
elseif string.lower(t[1]) == "memavailable:" then
|
||||
local n = tonumber(t[2])
|
||||
if n ~= nil then
|
||||
result.memory.available = n
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
meminfo:close()
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function SystemStat:appendSystemInfo()
|
||||
local stat = systemInfo()
|
||||
if stat.cpu ~= nil then
|
||||
self:put({_("System information"), ""})
|
||||
self:put({_(" Total ticks (million)"),
|
||||
string.format("%.2f", stat.cpu.total / 1000000)})
|
||||
self:put({_(" Idle ticks (million)"),
|
||||
string.format("%.2f", stat.cpu.idle / 1000000)})
|
||||
self:put({_(" Processor usage %"),
|
||||
string.format("%.2f", (1 - stat.cpu.idle / stat.cpu.total) * 100)})
|
||||
end
|
||||
if stat.memory ~= nil then
|
||||
if stat.memory.total ~= nil then
|
||||
self:put({_(" Total memory (MB)"),
|
||||
string.format("%.2f", stat.memory.total / 1024)})
|
||||
end
|
||||
if stat.memory.free ~= nil then
|
||||
self:put({_(" Free memory (MB)"),
|
||||
string.format("%.2f", stat.memory.free / 1024)})
|
||||
end
|
||||
if stat.memory.available ~= nil then
|
||||
self:put({_(" Available memory (MB)"),
|
||||
string.format("%.2f", stat.memory.available / 1024)})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SystemStat:appendProcessInfo()
|
||||
local stat = io.open("/proc/self/stat", "r")
|
||||
if stat == nil then return end
|
||||
|
||||
local t = util.splitToArray(stat:read("*all"), " ")
|
||||
stat:close()
|
||||
|
||||
local n1, n2
|
||||
|
||||
if #t == 0 then return end
|
||||
self:put({_("Process"), ""})
|
||||
|
||||
self:put({_(" ID"), t[1]})
|
||||
|
||||
if #t < 14 then return end
|
||||
n1 = tonumber(t[14])
|
||||
n2 = tonumber(t[15])
|
||||
if n1 ~= nil then
|
||||
if n2 ~= nil then
|
||||
n1 = n1 + n2
|
||||
end
|
||||
local sys_stat = systemInfo()
|
||||
if sys_stat.cpu ~= nil and sys_stat.cpu.total ~= nil then
|
||||
self:put({_(" Processor usage %"),
|
||||
string.format("%.2f", n1 / sys_stat.cpu.total * 100)})
|
||||
else
|
||||
self:put({_(" Processor usage ticks (million)"), n1 / 1000000})
|
||||
end
|
||||
end
|
||||
|
||||
if #t < 20 then return end
|
||||
n1 = tonumber(t[20])
|
||||
if n1 ~= nil then
|
||||
self:put({_(" Threads"), tostring(n1)})
|
||||
end
|
||||
|
||||
if #t < 23 then return end
|
||||
n1 = tonumber(t[23])
|
||||
if n1 ~= nil then
|
||||
self:put({_(" Virtual memory (MB)"), string.format("%.2f", n1 / 1024 / 1024)})
|
||||
end
|
||||
|
||||
if #t < 24 then return end
|
||||
n1 = tonumber(t[24])
|
||||
if n1 ~= nil then
|
||||
self:put({_(" RAM usage (MB)"), string.format("%.2f", n1 / 256)})
|
||||
end
|
||||
end
|
||||
|
||||
function SystemStat:appendStorageInfo()
|
||||
if self.storage_filter == nil then return end
|
||||
|
||||
local std_out = io.popen(
|
||||
"df -h | sed -r 's/ +/ /g' | grep " .. self.storage_filter ..
|
||||
" | sed 's/ /\\t/g' | cut -f 2,4,5,6"
|
||||
)
|
||||
if not std_out then return end
|
||||
|
||||
self:put({_("Storage information"), ""})
|
||||
for line in util.gsplit(std_out:read("*all"), "\n", false) do
|
||||
local t = util.splitToArray(line, "\t")
|
||||
if #t ~= 4 then
|
||||
self:put({_(" Unexpected"), line})
|
||||
else
|
||||
self:put({_(" Mount point"), t[4]})
|
||||
self:put({_(" Available"), t[2]})
|
||||
self:put({_(" Total"), t[1]})
|
||||
self:put({_(" Used percentage"), t[3]})
|
||||
end
|
||||
end
|
||||
std_out:close()
|
||||
end
|
||||
|
||||
function SystemStat:onSuspend()
|
||||
self.suspend_sec = os.time()
|
||||
self.sleep_count = self.sleep_count + 1
|
||||
@@ -32,28 +225,19 @@ function SystemStat:onNotCharging()
|
||||
end
|
||||
|
||||
function SystemStat:showStatistics()
|
||||
local kv_pairs = {
|
||||
{_("KOReader Started at"), os.date("%c", self.start_sec)},
|
||||
{_("Up hours"), string.format("%.2f", os.difftime(os.time(), self.start_sec) / 60 / 60)},
|
||||
{_("Number of wake-ups"), self.wakeup_count},
|
||||
{_("Number of sleeps"), self.sleep_count},
|
||||
{_("Number of charge cycles"), self.charge_count},
|
||||
{_("Number of discharge cycles"), self.discharge_count},
|
||||
}
|
||||
if self.suspend_sec then
|
||||
local kv_pairs_suspend = {_("Last suspend time"), os.date("%c", self.suspend_sec)}
|
||||
table.insert(kv_pairs, kv_pairs_suspend)
|
||||
end
|
||||
if self.resume_sec then
|
||||
local kv_pairs_resume = {_("Last resume time"), os.date("%c", self.resume_sec)}
|
||||
table.insert(kv_pairs, kv_pairs_resume)
|
||||
end
|
||||
self.kv_pairs = {}
|
||||
self:appendCounters()
|
||||
self:appendProcessInfo()
|
||||
self:appendStorageInfo()
|
||||
self:appendSystemInfo()
|
||||
UIManager:show(KeyValuePage:new{
|
||||
title = _("System statistics"),
|
||||
kv_pairs = kv_pairs,
|
||||
kv_pairs = self.kv_pairs,
|
||||
})
|
||||
end
|
||||
|
||||
SystemStat:init()
|
||||
|
||||
local SystemStatWidget = WidgetContainer:new{
|
||||
name = "systemstat",
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ local Screen = require("device").screen
|
||||
local Terminal = WidgetContainer:new{
|
||||
name = "terminal",
|
||||
dump_file = util.realpath(DataStorage:getDataDir()) .. "/terminal_output.txt",
|
||||
command = "",
|
||||
}
|
||||
|
||||
function Terminal:init()
|
||||
@@ -22,6 +23,7 @@ end
|
||||
function Terminal:start()
|
||||
self.input = InputDialog:new{
|
||||
title = _("Enter a command and press \"Execute\""),
|
||||
input = self.command,
|
||||
text_height = Screen:getHeight() * 0.4,
|
||||
input_type = "string",
|
||||
buttons = {{{
|
||||
@@ -43,14 +45,14 @@ function Terminal:start()
|
||||
end
|
||||
|
||||
function Terminal:execute()
|
||||
local command = self.input:getInputText()
|
||||
self.command = self.input:getInputText()
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = _("Executing…"),
|
||||
timeout = 0.1,
|
||||
})
|
||||
UIManager:forceRePaint()
|
||||
local std_out = io.popen(command)
|
||||
local entries = { command }
|
||||
local std_out = io.popen(self.command)
|
||||
local entries = { self.command }
|
||||
if std_out then
|
||||
while true do
|
||||
local line = std_out:read()
|
||||
|
||||
@@ -38,6 +38,22 @@ describe("util module", function()
|
||||
assert.are_same(argv, {"./sdcv", "-nj", "words", "a lot", "more or less", "--data-dir=dict"})
|
||||
end)
|
||||
|
||||
it("should split with splitter", function()
|
||||
local words = {}
|
||||
for word in util.gsplit("a-b-c-d", "-", false) do
|
||||
table.insert(words, word)
|
||||
end
|
||||
assert.are_same(words, {"a", "b", "c", "d"})
|
||||
end)
|
||||
|
||||
it("should also split with splitter", function()
|
||||
local words = {}
|
||||
for word in util.gsplit("a-b-c-d-", "-", false) do
|
||||
table.insert(words, word)
|
||||
end
|
||||
assert.are_same(words, {"a", "b", "c", "d"})
|
||||
end)
|
||||
|
||||
it("should split line into words", function()
|
||||
local words = util.splitToWords("one two,three four . five")
|
||||
assert.are_same(words, {
|
||||
@@ -251,4 +267,18 @@ describe("util module", function()
|
||||
assert.is_equal(util.fixUtf8("glück schließen", "_"), "glück schließen")
|
||||
end)
|
||||
|
||||
it("should split input to array", function()
|
||||
assert.are_same(util.splitToArray("100\tabc\t\tdef\tghi200\t", "\t", true),
|
||||
{"100", "abc", "", "def", "ghi200"})
|
||||
end)
|
||||
|
||||
it("should also split input to array", function()
|
||||
assert.are_same(util.splitToArray("abcabcabcabca", "a", true),
|
||||
{"", "bc", "bc", "bc", "bc"})
|
||||
end)
|
||||
|
||||
it("should split input to array without empty entities", function()
|
||||
assert.are_same(util.splitToArray("100 abc def ghi200 ", " ", false),
|
||||
{"100", "abc", "def", "ghi200"})
|
||||
end)
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user