nvm.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. --- 模块功能:参数管理
  2. -- @module nvm
  3. -- @author openLuat
  4. -- @license MIT
  5. -- @copyright openLuat
  6. -- @release 2017.11.9
  7. require "log"
  8. module(..., package.seeall)
  9. --实时参数配置存储在paraname文件中
  10. --默认参数配置存储在configname文件中
  11. --para:实时参数表
  12. --config:默认参数表
  13. paraname, paranamebak = "/nvm_para.lua", "/nvm_para_bak.lua"
  14. para = {}
  15. local libdftconfig, configname, cconfigname, econfigname
  16. local sBurnSave
  17. --[[
  18. 函数名:serialize
  19. 功能 :根据不同的数据类型,按照不同的格式,写格式化后的数据到文件中
  20. 参数 :
  21. pout:文件句柄
  22. o:数据
  23. 返回值:无
  24. ]]
  25. local function serialize(pout, o)
  26. if type(o) == "number" then
  27. --number类型,直接写原始数据
  28. pout:write(o)
  29. elseif type(o) == "string" then
  30. --string类型,原始数据左右各加上双引号写入
  31. pout:write(string.format("%q", o))
  32. elseif type(o) == "boolean" then
  33. --boolean类型,转化为string写入
  34. pout:write(tostring(o))
  35. elseif type(o) == "table" then
  36. --table类型,加换行,大括号,中括号,双引号写入
  37. pout:write("{\n")
  38. for k, v in pairs(o) do
  39. -- if type(k) == "number" then
  40. -- pout:write(" [" .. k .. "] = ")
  41. -- elseif type(k) == "string" then
  42. -- pout:write(" [\"" .. k .."\"] = ")
  43. -- else
  44. -- error("cannot serialize table key " .. type(o))
  45. -- end
  46. pout:write(" [")
  47. serialize(pout, k)
  48. pout:write("] = ")
  49. serialize(pout, v)
  50. pout:write(",\n")
  51. end
  52. pout:write("}\n")
  53. else
  54. error("cannot serialize a " .. type(o))
  55. end
  56. end
  57. --[[
  58. 函数名:upd
  59. 功能 :更新实时参数表
  60. 参数 :
  61. overide:是否用默认参数强制更新实时参数
  62. 返回值:无
  63. ]]
  64. function upd(overide)
  65. for k, v in pairs(libdftconfig) do
  66. if k ~= "_M" and k ~= "_NAME" and k ~= "_PACKAGE" then
  67. if overide or para[k] == nil then
  68. para[k] = v
  69. end
  70. end
  71. end
  72. end
  73. local function safePcall(file)
  74. local oldPath = package.path
  75. package.path = "/?.lua;".."/?.luae;".."/#lua_user#/?.lua;".."/#lua_user#/?.luae;"..package.path
  76. local result, para = pcall(require, file)
  77. package.path = oldPath
  78. return result, para
  79. end
  80. --[[
  81. 函数名:load
  82. 功能 :初始化参数
  83. 参数 :无
  84. 返回值:如果参数文件异常,会恢复出厂设置,此时返回false;其余情况返回nil
  85. ]]
  86. local function load()
  87. local f, fBak, fExist, fBakExist
  88. f = io.open(paraname, "rb")
  89. fBak = io.open(paranamebak, "rb")
  90. if f then
  91. fExist = f:read("*a") ~= ""
  92. f:close()
  93. end
  94. if fBak then
  95. fBakExist = fBak:read("*a") ~= ""
  96. fBak:close()
  97. end
  98. print("load fExist fBakExist", fExist, fBakExist)
  99. local fResult, fBakResult
  100. if fExist then
  101. if sBurnSave then
  102. fResult, para = safePcall(paraname:match("/#lua_user#/(.+)%.lua"))
  103. else
  104. fResult, para = safePcall(paraname:match("/(.+)%.lua"))
  105. end
  106. end
  107. print("load fResult,type(para)", fResult,type(para))
  108. local fError,fBakError
  109. if fResult then
  110. if type(para)=="table" then
  111. os.remove(paranamebak)
  112. upd()
  113. return
  114. else
  115. fError = true
  116. end
  117. end
  118. if fBakExist then
  119. if sBurnSave then
  120. fBakResult, para = safePcall(paranamebak:match("/#lua_user#/(.+)%.lua"))
  121. else
  122. fBakResult, para = safePcall(paranamebak:match("/(.+)%.lua"))
  123. end
  124. end
  125. print("load fBakResult,type(para)", fBakResult,type(para))
  126. if fBakResult then
  127. if type(para)=="table" then
  128. os.remove(paraname)
  129. upd()
  130. return
  131. else
  132. fBakError = true
  133. end
  134. else
  135. para = {}
  136. restore()
  137. end
  138. --两种参数文件都不存在,不算异常,以下算是异常情况:
  139. --1、参数文件存在,备份参数文件不存在
  140. -- (1)、参数文件require异常
  141. --2、参数文件不存在,备份参数文件存在
  142. -- (1)、备份参数文件require异常
  143. --3、参数文件存在,备份参数文件存在
  144. -- (1)、参数文件和备份参数文件require都异常
  145. if fExist and not fBakExist then
  146. if not fResult or fError then return false end
  147. end
  148. if not fExist and fBakExist then
  149. if not fBakResult or fBakError then return false end
  150. end
  151. if fExist and fBakExist then
  152. if (not fResult or fError) and (not fBakResult or fBakError) then return false end
  153. end
  154. end
  155. --[[
  156. 函数名:save
  157. 功能 :保存参数文件
  158. 参数 :
  159. s:是否真正保存,true保存,false或者nil不保存
  160. 返回值:无
  161. ]]
  162. local function save(s)
  163. if not s then return end
  164. local f = {}
  165. f.write = function(self, s)table.insert(self, s) end
  166. f:write("module(...)\n")
  167. for k, v in pairs(para) do
  168. if k ~= "_M" and k ~= "_NAME" and k ~= "_PACKAGE" then
  169. f:write(k .. " = ")
  170. serialize(f, v)
  171. f:write("\n")
  172. end
  173. end
  174. local fparabak = io.open(paranamebak, 'wb')
  175. fparabak:write(table.concat(f))
  176. fparabak:close()
  177. os.remove(paraname)
  178. os.rename(paranamebak, paraname)
  179. end
  180. --- 初始化参数存储管理模块
  181. -- @string defaultCfgFile 默认参数文件名
  182. -- @bool burnSave 本地烧录时是否保留已有参数,true为保留,false或者nil为清除
  183. -- 注意:在同一个项目,不同版本中,此参数必须保持前后版本一致
  184. -- @return boolean result,如果初始化异常,会恢复出厂设置,此时返回false;其余情况返回nil
  185. -- @usage
  186. -- 初始化参数存储管理模块,默认参数文件名为config.lua,本地烧录时清除已有的参数:
  187. -- nvm.init("config.lua")
  188. -- 初始化参数存储管理模块,默认参数文件名为config.lua,本地烧录时保留已有的参数:
  189. -- nvm.init("config.lua",true)
  190. function init(defaultCfgFile,burnSave)
  191. local f
  192. f, libdftconfig = safePcall(defaultCfgFile:match("(.+)%.lua"))
  193. configname, cconfigname, econfigname = "/lua/" .. defaultCfgFile, "/lua/" .. defaultCfgFile .. "c", "/lua/" .. defaultCfgFile .. "e"
  194. sBurnSave = burnSave
  195. if burnSave then
  196. rtos.make_dir("/#lua_user#")
  197. paraname, paranamebak = "/#lua_user#/nvm_para.lua", "/#lua_user#/nvm_para_bak.lua"
  198. end
  199. --初始化配置文件,从文件中把参数读取到内存中
  200. return load()
  201. end
  202. --- 设置某个参数的值
  203. -- @string k 参数名
  204. -- @param v 参数的新值,仅支持number、string、boolean、table类型
  205. -- @param r 设置原因,如果传入了非nil的有效参数,并且v值和旧值相比发生了改变,
  206. -- 会产生一个PARA_CHANGED_IND内部消息,携带 k,v,r 3个参数
  207. -- @param s 是否立即写入到文件系统中,false不写入,其余的都写入
  208. -- @return bool或者nil,成功返回true,失败返回nil
  209. -- @usage
  210. -- 参数name赋值为Luat,立即写入文件系统:
  211. -- nvm.set("name","Luat")
  212. --
  213. -- 参数age赋值为12,立即写入文件系统:
  214. -- 如果旧值不是12,会产生一个PARA_CHANGED_IND消息,携带 "age",12,"SVR" 3个参数:
  215. -- nvm.set("age",12,"SVR")
  216. --
  217. -- 参数class赋值为Class2,不写入文件系统:
  218. -- nvm.set("class","Class2",nil,false)
  219. --
  220. -- 参数score赋值为{chinese=100,math=99,english=98},立即写入文件系统:
  221. -- nvm.set("score",{chinese=100,math=99,english=98})
  222. --
  223. -- 连续写入4个参数,前3个不保存到文件系统中,写第4个时,一次性全部保存到文件系统中:
  224. -- nvm.set("para1",1,nil,false)
  225. -- nvm.set("para2",2,nil,false)
  226. -- nvm.set("para3",3,nil,false)
  227. -- nvm.set("para4",4)
  228. function set(k, v, r, s)
  229. local bchg = true
  230. if type(v) ~= "table" then
  231. bchg = (para[k] ~= v)
  232. end
  233. log.info("nvm.set", bchg, k, v, r, s)
  234. if bchg then
  235. para[k] = v
  236. save(s or s == nil)
  237. if r then sys.publish("PARA_CHANGED_IND", k, v, r) end
  238. end
  239. return true
  240. end
  241. --- 设置某个table类型参数的某一个索引的值
  242. -- @string k table类型的参数名
  243. -- @param kk table类型参数的某一个索引名
  244. -- @param v table类型参数的某一个索引的新值
  245. -- @param r 设置原因,如果传入了非nil的有效参数,并且v值和旧值相比发生了改变,会产生一个TPARA_CHANGED_IND消息,携带k,kk,v,r4个参数
  246. -- @param s 是否立即写入到文件系统中,false不写入,其余的都写入
  247. -- @return bool或者nil,成功返回true,失败返回nil
  248. -- @usage nvm.sett("score","chinese",100),参数score["chinese"]赋值为100,立即写入文件系统
  249. -- @usage nvm.sett("score","chinese",100,"SVR"),参数score["chinese"]赋值为100,立即写入文件系统,
  250. -- 如果旧值不是100,会产生一个TPARA_CHANGED_IND消息,携带 "score","chinese",100,"SVR" 4个参数
  251. -- @usage nvm.sett("score","chinese",100,nil,false),参数class赋值为Class2,不写入文件系统
  252. function sett(k, kk, v, r, s)
  253. local bchg = true
  254. if type(v) ~= "table" then
  255. bchg = (para[k][kk] ~= v)
  256. end
  257. if bchg then
  258. para[k][kk] = v
  259. save(s or s == nil)
  260. if r then sys.publish("TPARA_CHANGED_IND", k, kk, v, r) end
  261. end
  262. return true
  263. end
  264. --- 所有参数立即写入文件系统
  265. -- @return nil
  266. -- @usage nvm.flush()
  267. function flush()
  268. save(true)
  269. end
  270. --- 读取某个参数的值
  271. -- @string k 参数名
  272. -- @return 参数值
  273. -- @usage
  274. -- 读取参数名为name的参数值:
  275. -- nameValue = nvm.get("name")
  276. function get(k)
  277. return para[k]
  278. end
  279. --- 读取某个table类型参数的键名对应的值
  280. -- @string k table类型的参数名
  281. -- @param kk table类型参数的键名
  282. -- @usage
  283. -- 有一个table参数为score,数据如下:
  284. -- score = {chinese=100, math=100, english=95}
  285. -- 读取score中chinese对应的值:
  286. -- nvm.gett("score","chinese")
  287. function gett(k, kk)
  288. return para[k][kk]
  289. end
  290. --- 参数恢复出厂设置
  291. -- @return nil
  292. -- @usage nvm.restore()
  293. function restore()
  294. os.remove(paraname)
  295. os.remove(paranamebak)
  296. local fpara, fconfig = io.open(paraname, "wb"), io.open(configname, "rb")
  297. if not fconfig then fconfig = io.open(cconfigname, "rb") end
  298. if not fconfig then fconfig = io.open(econfigname, "rb") end
  299. fpara:write(fconfig:read("*a"))
  300. fpara:close()
  301. fconfig:close()
  302. upd(true)
  303. end
  304. --- 请求删除参数文件.
  305. -- 此接口一般用在远程升级时,需要用新的config.lua覆盖原来的参数文件的场景,在此场景下,远程升级包下载成功后,在确定要重启前调用此接口
  306. -- 下次开机执行nvm.init("config.lua")时,会用新的config.lua文件自动覆盖参数文件;以后再开机就不会自动覆盖了
  307. -- 也就是说"nvm.remove()->重启->nvm.init("config.lua")"是一个仅执行一次的完整操作
  308. -- @return nil
  309. -- @usage nvm.remove()
  310. function remove()
  311. os.remove(paraname)
  312. os.remove(paranamebak)
  313. end