公众号推荐
微信公众号搜"智元新知" 关注 微信扫一扫可直接关注哦!
有关lua,luci的介绍
时间:2020-08-15 分类:Lua 作者:编程之家
一颗璀璨的月光宝石——Lua
Lua语言简介
1993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro in Brazil)诞生了一门编程语言,发明者是该校的三位研究人员,他们给这门语言取了个浪漫的名字——Lua,在葡萄牙语里代表美丽的月亮。事实证明她没有糟蹋这个优美的单词,Lua语言正如它名字所预示的那样成长为一门简洁、优雅且富有乐趣的语言。
Lua从一开始就是作为一门方便嵌入(其它应用程序)并可扩展的轻量级脚本语言来设计的,因此她一直遵从着简单、小巧、可移植、快速 的原则,官方实现完全采用ANSI C编写,能以C程序库的形式嵌入到宿主程序中。Lua的每个版本都保持着开放源码的传统,不过各版采用的许可协议并不相同,自5.0版(最新版是5.1)开始她采用的是著名的MIT许可协议。正由于上述特点,所以Lua在游戏开发、机器人控制、分布式应用、图像处理、生物信息学等各种各样的领域中得到了越来越广泛的应用。其中尤以游戏开发为最,许多著名的游戏,比如Escape from Monkey Island、World of Warcraft、大话西游,都采用了Lua来配合引擎完成数据描述、配置管理和逻辑控制等任务。
作为一门过程型动态语言,Lua有着如下的特性:
1、变量名没有类型,值才有类型,变量名在运行时可与任何类型的值绑定;
2、语言只提供唯一一种数据结构,称为表(table),它类似key-value关联数组,可以用任何类型的值作为key和value。提供了一致且富有表达力的表构造语法,使得Lua很适合描述复杂的数据;
3、函数 是一等类型,支持 匿名函数 和正则尾递归(proper tail recursion);
4、支持 词法定界(lexical scoping )和闭包(closure);
5、提供thread类型和结构化 的协程(coroutine)机制,在此基础上可方便实现协作式多任务; 6、运行期能编译字符串形式的程序文本并载入虚拟机执行;
7、通过元表(Meta table)和元方法 (Meta method)提供动态元机制 (dynamic Meta -mechanism),从而允许程序运行时根据需要改变或扩充语法设施的内定语义;
8、能方便地利用表和动态元机制实现基于原型(prototype-based)的面向对象模型;
9、从5.1版开始提供了完善的模块机制,从而更好地支持 开发大型的应用程序;
Lua的语法类似PASCAL和Modula但更加简洁,所有的语法产生式规则(EBNF)不过才60几个。熟悉C和PASCAL的程序员一般只需半个小时便可 将其完全掌握。而在语义上Lua则与Scheme极为相似,她们完全共享上述的1、3、4、6点特性,Scheme的continuation与协程也基本相同只是自由度更高。最引人注目的是,两种语言都只提供唯一一种数据结构:Lua的表和Scheme的列表(list)。正因为如此,有人甚至称Lua为“只用表的Scheme”。
库和工具相比Java、Python、Perl,Lua的开源工具和库可能并不算多,但其中不乏优秀之作。以下介绍的资源均可在http://luausers.org/wiki/LuaAddons上找到,而且绝大多数都遵循着与Lua相同的许可协议。
一、Kepler
Kepler 是一个 简单且轻量的Web开发平台(但这并不意味着只能用它来开发简单的应用),支持 用Lua撰写Web程序,因此相当易学易用,并且能较方便地应用在一些资源受限的系统中。由于使用ANSI C和Lua进行开发,所以它能移植到任何支持 ANSI C的平台上。
Kepler由多个Lua扩展库组成,包括 CGILua、LuaSocket、LuaFileSystem 、cop as、Luasql 、LuaLDAP、LuaExpat、LuaXMLRPC、LuaSOAP、LuaZip、Xavante等,它们可大致分为核心库和功能 支撑库两部分。其中核心是CGILua和LuaSocket,后者负责TCP/UDP sockets的操作,前者则可以创建动态页面 并处理web表单上的输入数据。Kepler通过CGILua起动器(launcher)使得Web服务器能执行CGILua和Web程序并与之通信。目前的版本已经包括 适合CGI、FastCGI、Apache、IIS、Tomcat、Zope的CGILua起动器,因此用Lua开发的Web程序可以在这 些种类的服务器中自由迁移,只要同时安装上对应的CGILua起动器。
LuaFileSystem 是对标准Lua库中文件 读写功能 的补充,它提供了一种可移植的方法 来访问系统的目录结构和文件 属性 。cop as则是一个 基于协程的服务调度器。 Xavante是一个 用Lua开发的支持 HTTP 1.1的Web服务器,它直接支持 CGILua而无需起动器。
其它的组件提供了sql 数据库 访问、XML解析、LDAP、SOAP、XMLRPC、ZIP文件 操作等功能 ,用户 如果只需要其中的某些功能 ,可以抽出相关组件(及其所依赖的组件)来使用。
二、wxLua
GUI 是开发人员花费气力比较大的一个 领域,因此简化GUI程序的编写一直是广大程序员的努力方向。随着脚本语言的兴起,将动态、灵活、易用的脚本语言引入到GUI开发中是一种非常有价值的尝试。由于复杂的GUI布局需要大量的描述信息,所以比起其它脚本来,既适合编程又适合描述数据的Lua语言在构建GUI上就具有独特的优势。
wxWidgets是一个 著名的跨平台C++ GUI库,wxLua在Lua与wxWidgets之间架起了一座桥梁,通过它Lua代码 几乎可以调用 wxWidgets的所有功能 。wxLua基本将wxWidgets的类体系映射到了Lua(基于原型)的对象模型中,这使得程序员能以基于对象或面向对象的风格来开发wxLua程序。一个 Lua脚本的撰写、运行、测试和修改 可以非常快速 ,这无疑大大提高了GUI程序的开发效率,因此wxLua非常适合快速 原型的构造。另外,Lua本身以及 wxWidgets良好的可移植性使得相应的Lua GUI程序在许多平台上都能顺畅地运行。
三、Pluto
虽然Lua中的表能通过表构造器以Lua代码 的形式保存到文件 中从而实现持久化,但当数据之间有着复杂的引用关系,并且存在循环引用、共享引用等特殊情况时,这个任务就变得相当困难与繁琐了。Pluto持久化库能够为用户 解决 这个难题。在它的帮助下程序员可以将任意复杂的表数据保存到特殊格式的二进制文件 中以待将来恢复,库会自动 处理循环引用之类的情况。除表之外,Pluto还支持 函数 (确切地说是闭包)、thread的持久化,这种能力非常有意义。大家都知道程序调试中的一个 基本动作就是复现bug,但很多时候bug产生的条件是非常复杂的,依赖很多因素,开发者很难精确地构建出完全一致的运行环境。而利用Pluto对函数 和thread的持久化能力,我们可以把bug发生时程序的完整运行环境保存下来,今后就可凭此方便地复现bug。另外一个 重要应用是游戏进度的保存。实现游戏逻辑的Lua脚本的运行状态能随时写入到文件 中留待将来恢复,这使得在任何时间点保存游戏成了一件非常容易的事情。
四、LuaCOM
LuaCOM是一个 支持 Lua与符合COM规范的组件对象(确切一点说是自动 化对象)进行交互的扩展库。所谓交互包括 了两个方面,首先是允许Lua程序使用COM对象。LuaCOM支持 注册 在系统注册 表中的COM对象的动态实例化,也支持 动态访问运行中的对象。在LuaCOM的帮助下,调用 COM对象方法 就象调用 普通Lua函数 一样,存取属性 也与存取表的字段类似,同时它还负责Automation数据类型与Lua数据类型的自动 转换。有了这些特性,Lua程序操作COM对象就变得容易多了,再加上 Lua天生的动态性,这无疑使其成了一门非常灵活的组件装配语言。交互的另外一个 方面就是支持 用Lua
来实现(自动 化)组件对象并提供给外部客户使用。LuaCOM同时支持 进程内和进程外组件,它提供了一些辅助函数 来处理注册 、对象实例化这类事情,从而简化了相关工作。由于LuaCOM实际上是根据Lua的表来构造一个 COM对象,所以我们可以做一些非常有趣的事情:在userdata数据类型(代表不属于Lua世界的数据结构)和动态元机制的支持 下,Lua能通过表访问各种各样的外部数据,包括 C++对象、C结构或者CORBA对象等等;LuaCOM可 以很方便地将代表这些数据的表包装成一个 COM对象给外部使用,从而使得那些老迈的应用程序和库无需太多的努力便能跻身于COM世界。
五、tolua
直接用C实现某些功能 ,然后将相应的函数 导入到Lua中是很常见的做法。不过尽管Lua提供了与C语言交互的API,但用户 必需手工进行繁琐的Lua栈(用于与C交换数据)操作,而且还需注意两种语言数据类型的正确转换。难怪有人说使用Lua的C API就象在使用汇编语言一样。为了减轻程序员的负担,一些C/C++ Wrapper应运而生。
tolua本身不是一个 Wrapper,但它是一个 Wrapper代码 自动 生成 器。它使用一种称为包(package)的文件 来描述要导入到Lua环境中的常量、变量、函数 、类和方法 ,这种文件 按照简化了的C++头文件 格式编写。使用时首先让tolua解析包文件 以生成 含有相应胶水代码 的C/C++源文件 。然后将生成 的源文件 编译并与那些具体实现功能 的目标模块链接 起来。tolua虽然自动 产生胶水代码 ,但需另外撰写描述文件 ,所以仍然不够方便。其它一些Wrapper库则利用C++模板元编程技术来自动 生成 合适的连接代码 ,从而避免了额外的描述文件 ,比如使用boost库的luabind。
六、LuaJIT
Lua非常高效,它运行得比许多其它脚本(如Perl、Python、Ruby)都快,这点在第三方的独立测评中得到了证实。尽管如此,仍然会有人不满足,他们
总觉得“嗯,还不够快!”。LuaJIT就是一个 为了再榨出一点速度的尝试,它利用JIT编译技术把Lua代码 编译成本地机器码后交由cpu 直接执行。 LuaJIT测评报告表明,在浮点运算、循环和协程的切换等方面它的加速效果 比较显著,但如果程序大量依赖C编写的函数 ,那么运行速度便不会有什么改进。目前LuaJIT只支持 X86 cpu 。LuaJIT中包括 一个 名为Coco的库,用户 可以单独使用它。Coco为C函数 提供了真正的协程能力,用户 能在C函数 内部的任何一点将协程挂起,然后在将来用协程恢复操作返回到那一点。在标准Lua中,协程的挂起与恢复是不允许跨越C函数 调用 边界的。Coco使用了一些依赖于特定系统的特性,因此在移植程序时要特别注意。
七、ChunkSpy
Lua的虚拟机字节码指令集并非语言定义的一部分,因此官方没有提供相应的文档。用户 当然可以通过查看相关源代码 来获取 信息,但这毕竟不方便。
ChunkSpy是一个 Lua虚拟机字节码反汇编器,可以将一个 二进制Lua代码 块输出 为非常易读的各种格式(详细或简略,带或不带源程序)的字节码汇编文件 。而且它还支持 交互式的反汇编,用户 在敲入一行代码 后立刻就能看到对应的字节码指令。ChunkSpy的作者写过一篇详细的介绍Lua5虚拟机指令的文章 ,名为《ANo-Frills Introduction to Lua 5 VM Instructions》,你在项目主页上能找到它。这篇文章 现在已经针对最新的Lua5.1做了更正。另外,他还是Yueliang项目的开发者,这个项目采用Lua语言本身来实现Lua。从项目名来看,作者应该是个华人。
八、其它
另外一些库与工具还包括 LuaEDIT、LuaEclipse、VS’05LuaLangPack(它们都是IDE或IDE插件 ),LuaWrapper、CaLua、CPB、CppLua(Wrapper库),LuaProfiler(性能 测量工具)等,读者可以在lua-user.org与luaforge.net网站上找到它们以及其它有用资源。
luci框架的简介
LUCI 这个在百度 上搜索 除了一篇我的百度 文库 luci 的介绍文章 之外,前三页都是些不知所云的名词(足见百度 在专业领域的搜索 之烂),我却在大学毕业的大半年的大部分时间里与它纠结,由于开始的发懵到后来逐渐感觉到这家伙还很好玩的,现在就把我对 luci 的浅显认识介绍给大家。
官网: http://luci.subsignal.org/
有关luci 的各个方面,你几乎都可以从这里获得,当然,只是浅显的获得, luci 的文档写的还算比较全,但是写的稍显简略,开始看的时候会有一点不知所措。
UCI 熟悉 openwrt 的人都会有所了解,就是 Uni fied Con figuration Interface 的简称,而 luci 这个 openwrt 上的默 认 web 系统,是一个 独立的由严谨的德国人开发的 web 框架,是 Lua Con figuration Interface 的简称,如果在您的应用里, luci 是对 openwrt 的服务,我们就有必要做 一下 uci 的简介,我这里就不说了,见链接 :
http://www.google.com.hk/url?sa=t&source=web&cd=5&ved=0CEMQFjAE&url=http%3A%2F%2Fnbd.name%2Fopenwrt-fosdem-09.pdf&ei=h52iTcXvOcrMcJ-xxOwD&usg=AFQjCNGFhumCIgS5tK_mDJ2dDFU4qsskfQ
有的时候,我们开发的luci 是在自己的 Linux PC 上开发,在普通的 linux 上,一般是没有 uci 命令的,为了开发方便,可以手动编译一下,方法 见链接 :
https://forum.openwrt.org/viewtopic.php?id=15243
OK ,之前罗里罗嗦的说了很多,现在就进入正题,进入正题的前提是你已经 make install 正确的安装了 lua , uci ,以及编译好链接 了相关的 so (如果你需要,比如 uci.so nixio.so ),以及 make install 正确 web server ,(我用的 web server 是 thttpd ,也编译过 mongoose , lighttpd ,在这 三个之中, lighttpd 是功能 最完 善的, mongoose 是最小巧的)。
进入正题:
一:luci 的启动
在web server 中的 cgi-bin 目录下,运行 luci 文件 (权限一般是 755 ), luci 的代码 如下:
#!/usr/bin/lua --cgi的执行命令的路径require"luci.cacheloader" --导入cacheloader包require"luci.sgi .cgi" --导入sgi .cgi包 luci.dis patcher.indexcache = "/tmp/luci-indexcache" --cache缓存路径地址
luci.sgi .cgi.run() --执行run方法 ,此方法 位于*/luci/sgi /cgi.lua中
run方法 的主要任务就是在安全的环境中打开开始页面 (登录 页面 ),在 run 中,最主要的功能 还是在dis patch.lua 中完成。
运行luci 之后,就会出现登录 界面:
-bash-4.0# pwd
/var/www/cgi-bin
-bash-4.0# ./luci
Status: 200 OK
Content-Type: text/html;
charset = utf -8
Cache-Control: no-cache
Expires: 0
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
< html class = " ext-strict" >
/*some html code*/
</ html >
如果你成功的运行了luci 就说明你的 luci 框架成功的跑了起来。
二:LUCI 的 MVC
1:用户 管理:
在luci 的官方网站说明了 luci 是一个 MVC 架构的框架,这个 MVC 做的可扩展性很好,可以完全的统一的写自己的 html 网页,而且他对 shell 的支持 相当的到位,(因为 luci 是 lua 写的,lua 是 C 的儿子嘛,与 shell 是兄弟)。在登录 界面用户名 的选择很重要,luci 是一个 单用户 框架,公用的模块放置在 */luci/controller/ 下面,各个用户 的模块放置在 */luci/controller/ 下面对应的文件 夹里面,比如 admin 登录 ,最终的页面 只显示 /luci/controller/admin 下面的菜单 。这样既有效的管理了不同管理员 的权限。
2: controller 文件 夹下的 lua 文件 说明:(以 mini 用户 为例)
在mini 目录下面,会有一个 index.lua 文件 ,简略的代码 如下:
module( "luci.controller.mini.index" , package.seeall)
17
18 function index()
19 luci.i18n.loadc( "admin-core" )
20 local i18n = luci.i18n.translate
21
22 local root = node()
23 if not root.lock then
24 root.target = alias( "mini" )
25 root.index = true
26 end
27
28 entry({ "about" }, template( "about" )).i18n = "admin-core"
29
30 local page = entry({ "mini" }, alias( "mini" , "index" ), i18n( "essentials" , "Essentials" ), 10 )
31 page.i18n = "admin-core"
32 page.sysauth = "root"
33 page.sysauth_authenticator = "htmlauth"
34 page.index = true
35
36 entry({ "mini" , "index" }, "index" , i18n( "overview" ), 10 ).index = true
37 entry({ "mini" , form( "mini/index" ), i18n( "general" ), 1 ).ignor eindex = true
38 entry({ "mini" , "luci" }, cbi( "mini/luci" , {autoapply=true}), i18n( "settings" ),248); line-height:18px"> 39 entry({ "mini" , "logo ut" }, call( "action_logo ut" ), i18n( "logo ut" ))
40 end
41
42 function action_logo ut()
43 luci.http.header( "Set-Cookie" , "sysauth=; path=/" )
44 luci.http.redirect(luci.dis patcher.build_url())
45 end
这个文件 定义了node ,最外面的节点,最上层菜单 的显示 等等。在其他的 lua 文件 里,定义了其他菜单 的显示 和html 以及业务处理路径。每个文件 对应一个 菜单 相。
例如 system .lua 文件
function index()
19 luci.i18n.loadc("admin-core" )
20 local i18n = luci.i18n.translate
21
22 entry({"mini" , "system " }, "system " , i18n( "system " ), 40).index = true
23 entry({"mini" , cbi( "mini/system " , {autoapply= true }), 1)
24 entry({"mini" , "passwd" }, form( "mini/passwd" ), i18n( "a_s_changepw" ), 10)
25 entry({"mini" , "backup" }, call( "action_backup" ), i18n( "a_s_backup" ), 80)
26 entry({"mini" , "upgrade" }, call( "action_upgrade" ), i18n( "a_s_flash" ), 90)
27 entry({"mini" , "reboot" }, call( "action_reboot" ), i18n( "reboot" ), 100)
28 end
mudel是对应文件 的, function index 定义了菜单 ,比如这一句entry({"mini", "system ", "reboot"}, call("action_reboot"), i18n("reboot"), 100)
第1 项为菜单 入口:
{"mini", "reboot"}, mini 是最上层的菜单 ,即为用户 项, system 为一个 具体的菜单 , reboot 为这个菜单 的子菜单 ,如果 reboot 还需要加上 子菜单 的话,可以这样写:
entry({"mini", "reboot", "chreboot"}, call("action_chreboot"), i18n("chreboot"), 1), 这样就会在reboot 上产生一个 新的子菜单 ,以此类推,可以产生 N 层菜单 。
第二项为菜单 对应的页面 ,可以是lua 的源代码 文件 ,也可以是 html 页面 。
alias cgi form call 等定义了此菜单 相应的处理方式, form 和 cgi 对应到 model/cbi 相应的目录下面,那里面是对应的定制好的 html 和 lua 业务处理。
alias是等同于别的链接 , call 调用 了相应的 action_function 。还有一种调用 ,是 template ,是直接链接 到view 相应目录下面的 htm 页面 。(说明: luci 框架下面的 htm 都是可以嵌入 lua 语句的,做业务处理,相当于 jsp 页面 的内部的 Java 语句)。
问价查找对应简介:
ottom:0pt; padding-top:0px; padding-bott om:0px; font-family:Arial; font-size:14px; line-height:26px; color:rgb(51, 100) :对应此文件 的action_reboot function
ottom:0pt; padding-top:0px; padding-bott om:0px; font-family:Arial; font-size:14px; line-height:26px; color:rgb(51, "index"}, cbi("mini/system ", i18n("general"), 1):对应*/model/cbi/mini/system .lua {autoapply=true} 这个失传的参数。
。。。。。
第三项为i18n 显示 ,比如 entry({"mini", 100),菜单 的名字为admin-core 文件 内的对应显示 。此处也可以这样写, i18n("reboot"," 重启 ") ,即直接做了国际化。菜单 上显示 的就是“重启”。
第四项为现实的顺序,这个数字越小,显示 越靠前,靠上。
现在说一下这些文件 的解析是怎么解析的呢?你当然是说dis patch.lua中,你说对了,但是真正解析成菜单 的递归算法确实在header.htm中 位置:*/view/themes/openwrt/
<%
require ( "luci.sys" )
local load1, load5, load15 = luci.sys.loadavg()
local request = require ( "luci.dis patcher" ).context.path
local category = request[1]
local tree = luci.dis patcher.node()
local cattree = category and luci.dis patcher.node(category)
local node = luci.dis patcher.context.dis patched
local hostname = luci.sys.hostname()
local c = tree
for i,r in ipairs (request) do
if c.nodes and c.nodes[r] then
c = c.nodes[r]
c._menu_selected = true
end
end
require ( "luci.i18n" ).loadc( "default" )
require( "luci.http" ).prepare_content( "application/xhtml+xml" )
-%>
<?xml version="1.0" encoding= "utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang= "<%=luci.i18n.context.lang%>" lang= "<%=luci.i18n.context.lang%>" >
<head>
<Meta http-equiv="Content-Type" content= "text/html; charset=utf-8" />
<Meta http-equiv="Content-Script-Type" content= "text/javascript" />
<link rel="stylesheet" type= "text/css" media= "screen" href= "<%=media%>/cascade.css" />
<!--[if lt IE 7]><link rel= "stylesheet" type= "text/css" media= "screen" href= "<%=media%>/ie6.css" /><![ endif ]-->
<!--[if IE 7]><link rel= "stylesheet" type= "text/css" media= "screen" href= "<%=media%>/ie7.css" /><![ endif ]-->
<% if node and node.css then %><link rel= "stylesheet" type= "text/css" media= "screen" href= "<%=resource%>/<%=node.css%>" />
<% end -%>
<mce:script type="text/javascript" src="<%=resource%><!--
/VarType.js">
<mce:script type="text/javascript" src="<%=resource%><!--
/XHTML1.js">
<mce:script type="text/javascript" src="<%=resource%><!--
/Dropdowns.js">
</head>
<body class = "lang_<%=luci.i18n.context.lang%>" >
<p class = "skiplink" >
<span id="skiplink1" ><a href= "#navigation" mce_href= "#navigation" ><%:skiplink1 Skip to navigation%></a></span>
<span id="skiplink2" ><a href= "#content" mce_href= "#content" ><%:skiplink2 Skip to content%></a></span>
</p>
<div id="header" >
<h1><%=luci.version.dis tname%></h1>
<p>
<%=luci.version.dis tversion%><br />
<%:load%>: <%="%.2f" % load1%> <%= "%.2f" % load5%> <%= "%.2f" % load15%><br />
<%:hostname%>: <%=hostname%>
</p>
</div>
<div id="menubar" >
<h2 class = "navigation" ><a id= "navigation" name= "navigation" ><%:navigation Navigation%></a></h2>
<ul id="mainmenu" class = "dropdowns" >
<%-
local function submenu(prefix, node)
if not node.nodes or node.hidden then
return false
end
local index = {}
local count = 0
for k, n in pairs (node.nodes) if n.title and n.target then
table.insert(index, {name=k, order=n.order or 100})
count = count + 1
end
end
table.so rt(index, function (a, b) return a.order < b.order end )
if count > 0 then
%>
<ul id="submenu_<%=string.gsub(string.gsub(prefix, " / ", " _ "), " ^_(.-)_$ ", " %1 ")%>" >
<%-
for j, v in pairs (index) do
if #v.name > 0 then
local nnode = node.nodes[v.name]
local href = controller .. prefix .. v.name .. "/"
href = (nnode.query) and href .. luci.http.build_querystring(nnode.query) or href
if nnode.nodes then
for k1, n1 in pairs (nnode.nodes) href = "#"
end
end
%>
<li><a<% if nnode._menu_selected then %> class = "active" <% end %> href= "<%=luci.util.pcdata(href)%>" ><%=nnode.title%></a><%-
submenu(prefix .. v.name .. "/" , nnode)
%></li>
<%-
end
end
%>
</ul>
<%
end
end
if cattree and cattree.nodes then
local index = {}
irs(cattree.nodes) do
table.insert(index, order=node.order irs(index) do
node = cattree.nodes[k.name]
if node.title and node.target and not node.hidden then
local href = controller.."/" ..category.. "/" ..k.name.. "/"
href = (k.query) and href .. luci.http.build_querystring(k.query) or href
irs(node.nodes) do
if n1.title and n1.target then
href = " #"
end
end
if node._menu_selected then %> class = "preactive" <% end %> href= "<%=href%>" ><%=node.title%></a><%
submenu("/" .. category .. "/" .. k.name .. "/" , node)
%></li><% end
%>
</ul>
<ul id="modemenu" ><%
irs(tree.nodes) and not node.hidden then %>
<li><a<% if request[1] == k then %> class = "active" <% end %> href= "<%=controller%>/<%=k%>/" ><%=node.title%></a></li><%
<%
if tree.nodes[category] and tree.nodes[category].ucidata then
local ucic = 0
irs( require ( "luci.model.uci" ).cursor():changes()) irs(j) for m, n in pairs (l) ucic = ucic + 1;
end
end
end
-%>
<ul id="savemenu" <li><% if ucic > 0 then %><a class = "warning" href= "<%=controller%>/<%=category%>/uci/changes/" ><%:unsavedchanges%>: <%=ucic%></a><%
submenu("/" .. category .. "/uci/" , tree.nodes[category].nodes[ "uci" ])
else -%>
<a href="#" mce_href= "#" ><%:changes%>: 0</a><% end -%>
</li>
</ul><% end %>
<div class = "clear" ></div>
</div>
<div id="maincontent" >
3: model 业务处理和页面 生成 简介
我认为model 的业务处理和 html 生成 ,是 luci 框架的精华,但是想用好它,最终扩展定义自己的页面 也是最难的,但是一旦定义好了,后面的工作就会轻松高效简介统一,不失为一种好的解决方 案。但是它又有缺点,就是写页面 虽然统一,但是不够灵活。
下面以 SimpleForm为例,讲解一下。
具体文件 */luci/model/cbi/passwd.lua
f = SimpleForm("password", translate("a_s_changepw"), translate("a_s_changepw1")) --调用 SimpleForm 页面 当然还是 I18N 从中捣乱,看上去没那么直观,不理他 pw1=f:field(Value,"pw1",translate("password")) -- SimpleForm 里面加一个 field 至于 SimpleForm 和 fiemd 是什么,一会去看 SimpleForm 页面 去 pw1.rmempty=false -- 把 SimpleForm的 rmempty 为不显示 后面就不做注释了 应该看得懂了 pw2 = f:field(Value, "pw2", translate("confirmation")) pw2.rmempty = false function pw2.validate(self, value, section) return pw1:formvalue(section) == value and value end function f.handle(self, state, data) if state == FORM_VALID then --这个就是业务处理了 你懂得 呵呵 local stat = luci.sys.user.setpasswd("admin", data.pw1) == 0 -- root --> admin if stat then f.message = translate("a_s_changepw_changed") else f.errmessage = translate("unkNow nerror") end data.pw1 = nil data.pw2 = nil end return true end return f
说明:( simpleForm 位于 view/cbi 下面,可以研究一下,各个元素是如何定义的 )
现在在给一个 小例子:
以.*/luci/model /cbi/admin_system /version_manage.lua 为例,介绍一下 luci 中 web 页面 lua 代码 6 local h = loadfile("/usr/local/luci/help.lua") 7 if h then 8 h() 9 end 10 local help_txt = help_info and help_info.version 加载帮助帮助文件 help.lua, 关于 loadfile() 的用法 可以查看 lua 的手册 ( 我还没完全弄明白,先用了 ) help_txt 是一个 全局变量 12 appadmin_path = "/usr/local/appadmin/bin/" 定义一个 全局变量 ,其实跟功能 跟宏一样,定义appadmin 的绝对路径 14 versionlist = {} 15 16 function getline (s) ......... 32 end 33 34 function get_versionlist() ......... 68 end 69 70 versionlist = get_versionlist() 定义一个 全局变量 和两个函数 ,并初始化此变量 接下来就是和最终展现的Web 页面 直接相关的代码 了,大部分都是对 luci 封装好的一些 html 控件(代码 )的使用和扩展。 luci 封装好的 html 控件 类可以在以下文件 查看:./host/usr/lib/lua/luci/cbi.lua 71 m = SimpleForm("version", translate("版本管理 ")) 72 m.submit = false 73 m.reset = false 74 m.help = help_txt and true or false 75 m.helptxt = help_txt or "" 使用了一个 SimpleForm 的控件, SimpleForm 实际上对应一个 html 表单,是整个页面 最大的 " 容器 " ,本页面 内的绝大部分控件都处于 SimpleForm 内 ,是它的子控件 。我知道的可以做> 页面 最大 " 容器 " 的控件还有一个 Map, 但它需要 ./host/etc/config / 目录下的一个 配置文件 ,我没有使用。 submit reset是 luci 默 认就封装好的一个 属性 ,分别控制 html 表单的 " 提交 "" 重置 " 按钮 ;help helptxt 是我扩充的表单属性 ,分别控制 web 页面 的 "帮助 " 功能 和帮助内容 。关于控件属 性的意义、实现和扩充可以按以下步骤进行: 在文件 ./host/usr/lib/lua/luci/cbi.lua 中查找控件名 SimpleForm, 然后可以找到以下行 664 self.template = "cbi/simpleform" 这 表明SimpleForm 的 html 模版文件 为 ./host/usr/lib/lua/luci/view/cbi /simpleform.htm ,通过研究 simpleform.htm 文件 内容 可以知道各属性 的 功能 以及模版中使用lua 代码 的方法 ,然后可以按类似的方法 添加 自定义 的 属性 。 77 s = m:section(Table, versionlist) 新建了一个 section,section 内定义了一个 表格类, versionlist 是与其相关的变量( lua 的所有变量都可归类于 table 类型 ) 与Table 关联的 table 变量应该是这种结构的: t = { row1 = {column1 = "xxx", column2 = "xxx", .... }, row2 = {column1 = "xxx", row3 = {column1 = "xxx", row4 = {column1 = "xxx", } 然后定义Table 的列控件 79 enable = s:option(DummyValue, "_enabled", translate("软件状态 ")) 83 appid = s:option(DummyValue, "_appid", translate("软件版本 ")) 84 appname = s:option(DummyValue, "_appname", translate("软件名称 ")) DummyValue是只读的文本框,只输出 不输入。 Value 是单行文本框,可输出 也可输入。 Flag 是一个 checkBox , 值为 "1" 时被选中,为 "0" 时未选中。 ListValue是列表框 ... 具体的用法 可 以看./host/usr/lib/lua/luci /model/cbi/ 下的文件 ( find ./host/usr/lib/lua/luci/model/cbi/ -name "*.lua" |xargs grep "ListValue") 对于table 内普通的字符串类的值,只需要把列控件的 id (括号内第二个值,如 "_appid" )定义为 table 内对应的变量名(比如 column1 ) 对于非变通字符串类的值,或者为字符串但需要进行一定的处理然后再显示 的值,可以按以下方法 显示 :定义该控件的cfgvalue 函数 属性 127 newinfo = up_s:option(TextValue, "_newifo", translate("新版本信息 ")) 128 newinfo.readonly = true 129 newinfo.rows = 11 130 newinfo.cfgvalue = function(self, section) 131 local t = string.gsub(info, "Archive:[^/n]*", "") 132 return t 133 end 定义cfgvalue 后, luci 的处理函数 会调用 此函数 为此控件赋值,(传入的 section 参数值为 row1/row2/row3 等,当处理到 row 几时值就为 row 几 ) 对于DummyValue 等只输出 不输入的类,还有一种赋值方法 : 控件实例名(如 enable).value = xxx 对于有输入的控件Value 等, .value 方法 赋值在处理输入里会有一些问题,有什么问题以及如何解决 可以做实验试试 , 也许是我使用方法 不对造 成的 对有输入控件的处理有两种方法 : 1 定义控件的 .write 属性 这种方法 对处理比较独立的输入(与其它控件输入关系不大)比较适用 88 up_s = m:section(SimpleSection) 89 up_version = up_s:option(Button, "_up_version", translate("上传 新版本 ")) 90 up_version.onlybutton = true 91 up_version.align = "right" 92 up_version.inputstyle = "save" 93 up_version.write = function(self, section) 94 luci.http.redirect(luci.dis patcher.build_url("admin", "version_manage", "upload")) 95 end ps:只有当 Value 的 rmempty == false 时, Value 输入为空也会触发 write 函数 , 需要对 rmemtpy 显示 赋值为false ( xx.rmempty = false)
4: view 下面的 html 简介
这个是最好理解的 例:passwd.htm
<%+header%>
<h2><a id="content" name="content"><%:system %></a></h2>
<h3><%:reboot%></h3>
<p><%:a_s_reboot1%></p>
<%-
local c = require("luci.model.uci").cursor():changes()
if c and next(c) then
-%>
<p class="warning"><%:a_s_reboot_u%></p>
end
if not reboot then
<p><a href="<%=controller%>/admin/system /reboot?reboot=1"><%:a_s_reboot_do%></a></p>
<%- else -%>
<p><%:a_s_reboot_running%></p>
<script type="text/javascript">setTimeout("location='<%=controller%>/admin'", 60000)</script>
<%- end -%>
<%+footer%>
<%+header%> <%+footer%> 加载公用的头部和尾部
<% lua code%>
<%:i18n%>
<%lua code%>
<%=lua 变量 %>
From: fhttp://www.voidcn.com/article/p-kfqbscrd-ur.html
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。