说实话,openwrt 这东西的开发我都快丢下一年时间了,但是学了很多东西,但是感觉也用不到了吧,当时很想写博客记下来,可是没有,现在再去回想有点难了,尽力吧,下面根据之前学过的过程再总结一边吧。
了解 Luci
openwrt 中的 Luci 是用来编写网页界面的,比如我们通过网关访问的路由器控制界面就是用它写的。它还有一个功能就是设置参数,比如网页上修改的 IP、网关等。如果我们需要在 的网页上做一些我们自己的东西,那就得了解 Luci 的使用了。
这里主要就是要有一个 MVC 模型的概念,Luci 通过这三部分构建页面。
-
M(Model): 模型层
路径:/usr/lib/lua/luci/model/。
这个是数据处理的具体代码的层。该层中有一个cbi文件夹,那里面是预定义的一些的逻辑文件。 -
V(View): 视图层
路径:/usr/lib/lua/luci/view/。
这个是存放视图文件的地方。LUCI里的视图文件是.htm文件。 -
C(Controller): 控制层
路径:/usr/lib/lua/luci/controller/admin/。
这个层最主要的功能是设定路由;它还有一个功能跟模型层一致——处理数据。
要做什么
讲这么多用处不大,我们首先要确定的是我们要做些什么,下面就讲讲我们要编写的东西。
配置文件
如果我们的页面需要修改参数,首先就要创建一个配置文件(Uci),里面存放我们的数据。配置文件的路径一般是在/etc/config/,我们可以打开这个目录看看,实际上我们配置的 IP、WiFi密码等都在这里面,可以看到他们都以一定格式储存的,我们要写的配置文件模仿他们就可以了。
config interface 'loopback'
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'
config interface 'lan'
option type 'bridge'
option ifname 'eth1'
option proto 'static'
option ipaddr '192.168.1.1'
option netmask '255.255.255.0'
option ip6assign '60'
这里应该有很多文档的,可惜我这没保留下来,如果要详细研究的话,还是自行找点资料吧。
-- 配置文件格式
config <section type> <section name>
option <option name> <option value>
option <option name> <option value>
option <option name> <option value>
这里我们在 /etc/config/ 目录下新建一个文件,命名为 test_config,填入下面内容
config interface test
option ifname 'test'
option type 'test'
option ipaddr '192.168.1.1'
option net '192.168.251.1'
导航页面入口(控制层)
这里的导航页面入口就是上面说的 MVC 模型中的控制层,实际上就是定义在路由器网页顶部或者侧边的导航栏,是新增一个导航标签,还是在某个已有标签中添加新标签页。
我们新开一个标签页吧,进入 /usr/lib/lua/luci/controller/admin/ 目录,在里面创建 test 目录,并在该目录下创建一个new_tab.lua 文件,写入下面内容:
-- .../controller/admin/test/new_tab.lua
module("luci.controller.admin.test.new_tab", package.seeall)
function index()
entry({"admin", "new_tab"}, firstchild(),translate("导航标签"), 1).dependent=false
entry({"admin", "new_tab", "helloworld"}, cbi("test/helloworld"), translate("子标签"), 2)
end
下面简单讲下代码
- 文件路径
module("luci.controller.admin.test.new_tab", package.seeall)
这里路径就是我们文件的路径,使用点号分隔开来。
- 页面信息
idnex 函数里面定义了页面的信息,系统会调用 index 函数去创建页面。entry 表示添加一个新的选项入口,我们这定义了两个 entry。
entry(path, target, title=nil, order=nil)
上面是 entry 的用法,下面通过例子说明。
entry({"admin", "new_tab"}, firstchild(),translate("导航标签"), 1).dependent=false
第一个 entry 里面是导航栏中标签的信息,这里只有两层参数,其中的 “admin” 表示为管理员添加脚本,“new_tab” 为一级菜单名,下面一个 entry 的 “hellworld”为菜单项名。
entry({"admin", "new_tab", "helloworld"}, cbi("test/helloworld"), translate("子标签"), 2)
第二个 entry 里面是展开后的标签页,translate里面是标题,末尾的数字表示其顺序,序号以1开始,不填序号或者填 nil 则不显示该标签页。
- 页面路径
展开后的标签页就是可以点击的了,具体显示的页面看下面介绍
- **cbi("…"):**调用/luci/model/cbi/文件夹中的指定lua文件,这里指向的文件相当于指向一个处理函数。使用的是LUCI自带逻辑的处理方法,本文就是使用这种方法来生成的页面。
- **template("…"):**调用/luci/view/文件夹里指定的.htm视图文件。这个方法是直接调用视图。在视图里我们也可以嵌入代码读取配置。
- alias("…"): 这个是重定向函数, 一般用于*菜单上,使其重定向到指定的子菜单。
- call("…"): 这个函数的作用是将该路由指向控制下的某个函数,一般用于处理数据,作用与指向模型层的cbi("…")函数类似,只是一个指向到其他文件,一个仍是存在于控制层文件内。
- post("…"): 这个函数在openwrt里的其他模块有使用过,本人研究了一下,其作用于call方法类似,但在使用的时候似乎没有成功,若是有成功使用过的猿友,欢迎交流探讨。
定义模型页面(模型层)
这里页面前面加了模型两个字,意思就是页面是按特定规则编写的(CBI组件),当从上面的标签入口点击进入的时候,系统会把这 lua 写成的代码转成 htm 网页,并显示出来。
进入 /usr/lib/lua/luci/model/cbi/ 目录,创建一个我们的新目录 test,里面创建一个文件,命名为 helloworld.lua
-- /model/cbi/test/helloworld.lua
-- 连接配置文件,显示页面标题
m = Map("test_config", translate("Test Page"))
-- 获取模块数据,后为模块标题
s = m:section(NamedSection, "test", "interface", translate("Net Configuration")
-- 打开/关闭添加删除按钮
s.addremove = true
-- 不显示section的名字在页面上
s.anonymouse = true
-- 下面是我们几个数据,都是文本数据,可编辑,提交生效
ifname = s:option(Value, "ifname", translate("Ifname: "))
ifname.datatype = 'string'
itype = s:option(Value, "type", translate("Type: "))
itype.datatype = 'string'
ipaddr = s:option(Value, "ipaddr", translate("Ipaddr: "))
ipaddr.datatype = 'ipaddr'
net = s:option(Value, "net", translate("Net:"))
net.datatype = 'ipaddr'
return m
这是最简单的页面,只有文本框,下面有个图可以看看,后面简单介绍
- 获取模块数据
s = m:section(NamedSection, "test", "interface", translate("Net Configuration")
-
第一个参数
是指定存取section的方法,本文用的是以section的name名进行查找的方式NamedSecton
。其他还有几种方式:- TypedName: 根据section的type来进行存取
- SimpleSection:(这个没用过,就不多说了)
- Table:以表格的形式体现section
- Tab:以标签的形式体现section
-
第二个参数
是section的名字 -
第三个参数
是section的类型 -
第四个参数
是模块标题的名称
- 组件显示
net = s:option(Value, "net", translate("Net:"))
net.datatype = 'ipaddr'
这里的 Value 是一种文本框类型组件,“net” 是模块数据里面的属性名,translate 是文本框前面的提示文字,datatype 是数据类型。
组件类型有下面这些
Value(普通文本框)、ListValue(下拉列表)、Flag(复选框)、MultiValue(文本域)、DummyValue(纯文本)、TextValue(多行input)、Button(按钮)、StaticList(静态列表)、DynamicList(动态列表)
- lua 库调用
实际在这个模型页面中是可以调用一些 lua 通用的库的,之前同事就在这调用了网络通信的库,在这里调通了 UDP\TCP 的服务端及客户端,当然我是不推荐这样用的,页面生命有限,做出来也就进行一次通信,作用不大吧。
- 官方文档
组件这块我写的不多,具体看官方文档吧
https://github.com/openwrt/luci/wiki/CBI
视图层
视图层就是自己定义一些 htm 页面,而不使用模型层,我们这里用不到,但是也简单讲一下吧。自定义的就htm 页面写在 /usr/lib/lua/luci/view/ 目录下,好像不支持 html ,不太懂。
语法如下
1、 包含Lua代码:
<% code %>
2、 输出变量和函数值:
<% write(value) %>
<%=value%>
3、 包含模板:
<% include(templatesName) %>
<%+templatesName%>
4、 转换:
<%= translate(“Text to translate”) %>
<%:Text to translate%>
5、 注释:
<%# comment %>
其他语法跟html和JavaScript一样。
总结
讲了这么多,我自己都头晕了,下面做一个简单的小结,实际我们就编写了三个文件。
- 配置文件,/etc/config/test_config
- 导航栏入口文件,/usr/lib/lua/luci/controller/admin/test/new_tab.lua
- 模型页面文件,/usr/lib/lua/luci/model/cbi/test/helloword.lua
至于上面具体的用法,我讲的很简单,大致能用,说复杂了就不是一篇博客能解决的。
进阶
和之前写的自启动功能类似,我们也可以在应用的源码中编写好这些文件,安装的时候通过上层 的 Makefile,将其复制到上面提到的位置,注意下复制的格式就好,安装好软件我们的页面就能用了,如果不能用,可能需要重启一下板子。
如果还想了解更多 Luci 的用法的话,可以按着下面的图学习学习,搞懂 CBI 和 UCI这几个东西。
结语
这篇博文不是一个字典型的博文,更多的是初级的用法,入个门而已,毕竟我再也不想搞这东西了,但是觉得全部忘了又可惜,大致把重要的东西写写,细节就不管了。
end
完美撒花