mirror of
https://github.com/vxcontrol/soldr-modules.git
synced 2026-07-21 08:55:22 -04:00
284 lines
8.7 KiB
Lua
284 lines
8.7 KiB
Lua
require("yaci")
|
|
local lfs = require("lfs")
|
|
local time = require("time")
|
|
|
|
CFileReader = newclass("CFileReader")
|
|
|
|
function CFileReader:init(is_debug, is_block, timer_ms, is_skip_empty)
|
|
self.is_debug = false
|
|
self.is_block = true
|
|
self.timer_ms = 50
|
|
self.is_skip_empty = true
|
|
self.co = nil
|
|
self.file_handle = nil
|
|
self.last_size = 0
|
|
self.last_modification = 0
|
|
|
|
if type(is_debug) == "boolean" then
|
|
self.is_debug = is_debug
|
|
end
|
|
if type(is_block) == "boolean" then
|
|
self.is_block = is_block
|
|
end
|
|
if type(timer_ms) == "number" then
|
|
self.timer_ms = timer_ms
|
|
end
|
|
if type(is_skip_empty) == "boolean" then
|
|
self.is_skip_empty = is_skip_empty
|
|
end
|
|
end
|
|
|
|
function CFileReader:print(...)
|
|
if self.is_debug then
|
|
print(...)
|
|
end
|
|
end
|
|
|
|
function CFileReader:is_modification()
|
|
local attr, err = lfs.attributes(self.file_path)
|
|
if not attr then
|
|
return false, err
|
|
elseif self.last_size > attr["size"] then
|
|
return true, "truncate"
|
|
elseif self.last_size < attr["size"] then
|
|
return true, "change size"
|
|
elseif self.last_modification ~= attr["modification"] then
|
|
return true, "updated"
|
|
end
|
|
return false, "nothing"
|
|
end
|
|
|
|
function CFileReader:get_modification()
|
|
local attr, err = lfs.attributes(self.file_path)
|
|
if not attr then
|
|
return nil, err
|
|
end
|
|
return attr["modification"]
|
|
end
|
|
|
|
function CFileReader:check_modification(await, is_close)
|
|
repeat
|
|
if is_close() then
|
|
return false, "closed"
|
|
end
|
|
if not self.is_follow then
|
|
return true, "not follow"
|
|
end
|
|
local is_mod, msg = self:is_modification()
|
|
if msg == "nothing" then
|
|
if self.is_block then
|
|
return true, msg
|
|
else
|
|
await(self.timer_ms)
|
|
end
|
|
elseif msg == "updated" then
|
|
self.last_modification = self:get_modification()
|
|
elseif msg == "truncate" and not self:open() then
|
|
return false, "error"
|
|
elseif is_mod == false then
|
|
return false, msg
|
|
else
|
|
return true, "changed"
|
|
end
|
|
until not self.is_block
|
|
end
|
|
|
|
function CFileReader:get_size()
|
|
local attr, err = lfs.attributes(self.file_path)
|
|
if not attr then
|
|
return nil, err
|
|
end
|
|
return attr["size"]
|
|
end
|
|
|
|
function CFileReader:open(file_path, file_op, is_follow, limit, step)
|
|
if self.file_handle and io.type(self.file_handle) == "file" then
|
|
self:print("Reader already initialized and it will be closed")
|
|
self.file_handle:close()
|
|
else
|
|
self.file_path = nil
|
|
self.file_op = nil
|
|
self.is_follow = false
|
|
self.limit = -1
|
|
self.step = 1
|
|
|
|
if type(file_path) == "string" then
|
|
self.file_path = file_path
|
|
end
|
|
if type(file_op) == "string" then
|
|
self.file_op = file_op
|
|
end
|
|
if type(is_follow) == "boolean" then
|
|
self.is_follow = is_follow
|
|
end
|
|
if type(limit) == "number" and limit >= 0 then
|
|
self.limit = limit
|
|
end
|
|
if type(step) == "number" and step > 0 then
|
|
self.step = step
|
|
end
|
|
end
|
|
|
|
if self.limit ~= -1 and self.step > self.limit then
|
|
return false, "limit should be greater than step"
|
|
end
|
|
if self.limit < -1 then
|
|
return false, "limit should be greater or equal than -1"
|
|
end
|
|
if self.step <= 0 then
|
|
return false, "step should be greater than 0"
|
|
end
|
|
|
|
if self.file_path then
|
|
self.dir, self.file_name, self.file_ext =
|
|
string.match(self.file_path, "(.-)([^\\/]-%.?([^%.\\/]*))$")
|
|
else
|
|
return false, "File path doesn't set"
|
|
end
|
|
|
|
self.file_handle = io.open(self.file_path, "rb")
|
|
if not self.file_handle then
|
|
return false, "Can't open file: " .. file_path
|
|
end
|
|
|
|
if self.file_op == "tail" and self.is_follow then
|
|
self.file_handle:seek("end")
|
|
elseif self.file_op == "tail" and not self.is_follow then
|
|
self.file_handle:seek("set")
|
|
if self.limit ~= -1 then
|
|
local ofsets = { 0 }
|
|
for line in self.file_handle:lines() do
|
|
local is_empty = string.match(line, "([^\r\n]*)")
|
|
if is_empty and #is_empty > 0 then
|
|
table.insert(ofsets, self.file_handle:seek())
|
|
end
|
|
end
|
|
if #ofsets <= self.limit then
|
|
self.file_handle:seek("set")
|
|
else
|
|
self.file_handle:seek("set", ofsets[#ofsets - self.limit])
|
|
end
|
|
end
|
|
elseif self.file_op == "head" then
|
|
self.file_handle:seek("set")
|
|
else
|
|
self:print("File operation doesn't set")
|
|
end
|
|
|
|
self.last_size = self.file_handle:seek()
|
|
self.last_modification = self:get_modification()
|
|
return true
|
|
end
|
|
|
|
function CFileReader:get_lines(await, is_close)
|
|
local nline = 0
|
|
while self.limit == -1 or nline < self.limit do
|
|
local lines = {}
|
|
local res, msg = self:check_modification(await, is_close)
|
|
if not res then
|
|
self:print("Check modifications failed: ", msg)
|
|
return res, msg
|
|
end
|
|
|
|
local date_marker = os.date("%Y-%m-%d %H:%M:%S ", os.time())
|
|
for line in self.file_handle:lines() do
|
|
self:print("<" .. date_marker .. "> " .. line)
|
|
local is_empty = string.match(line, "([^\r\n]*)")
|
|
if is_empty and #is_empty > 0 then
|
|
nline = nline + 1
|
|
table.insert(lines, line)
|
|
if #lines == self.step or nline == self.limit then
|
|
self.last_size = self.file_handle:seek()
|
|
coroutine.yield(lines)
|
|
lines = {}
|
|
end
|
|
if nline == self.limit then
|
|
self:print("Lines limit exceeded: ", nline, self.limit)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not self.is_follow or nline == self.limit or not self.is_block then
|
|
if #lines ~= 0 then
|
|
self.last_size = self.file_handle:seek()
|
|
coroutine.yield(lines)
|
|
end
|
|
self:print("Get lines function was skipped: ",
|
|
self.is_follow, nline, self.limit, self.is_block)
|
|
break
|
|
end
|
|
end
|
|
self:print("Get lines function was done")
|
|
return true
|
|
end
|
|
|
|
function CFileReader:close()
|
|
if io.type(self.file_handle) == "file" then
|
|
self.file_handle:close()
|
|
end
|
|
self.co = nil
|
|
self.file_handle = nil
|
|
self.last_size = 0
|
|
self.last_modification = 0
|
|
end
|
|
|
|
function CFileReader:get_sync_func(await, is_close)
|
|
if type(await) ~= "function" and type(await) ~= "userdata" then
|
|
self:print("Replace await function to default: ", await)
|
|
await = function(delay) time.sleep(delay / 1000.) end
|
|
end
|
|
if type(is_close) ~= "function" and type(is_close) ~= "userdata" then
|
|
self:print("Replace is_close function to default: ", is_close)
|
|
is_close = function()
|
|
if not self.file_handle then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
end
|
|
return await, is_close
|
|
end
|
|
|
|
function CFileReader:read_line(await, is_close)
|
|
await, is_close = self:get_sync_func(await, is_close)
|
|
if not self.co or type(self.co) ~= "thread" then
|
|
self.co = coroutine.create(self.get_lines)
|
|
end
|
|
while coroutine.status(self.co) == "suspended" do
|
|
local _, lines = coroutine.resume(self.co, self, await, is_close)
|
|
if coroutine.status(self.co) ~= "dead" then
|
|
return lines
|
|
end
|
|
end
|
|
self:print("Read line function was done")
|
|
if self.is_block then
|
|
self:close()
|
|
else
|
|
self.co = nil
|
|
end
|
|
end
|
|
|
|
function CFileReader:read_line_cb(callback, await, is_close)
|
|
if type(callback) ~= "function" then
|
|
return false, "Callback doesn't set"
|
|
end
|
|
await, is_close = self:get_sync_func(await, is_close)
|
|
if not self.co or type(self.co) ~= "thread" then
|
|
self.co = coroutine.create(self.get_lines)
|
|
end
|
|
while coroutine.status(self.co) == "suspended" do
|
|
local _, lines = coroutine.resume(self.co, self, await, is_close)
|
|
if coroutine.status(self.co) ~= "dead" then
|
|
callback(lines)
|
|
end
|
|
end
|
|
self:print("Read line function callback was done")
|
|
if self.is_block then
|
|
self:close()
|
|
else
|
|
self.co = nil
|
|
end
|
|
return true
|
|
end
|