This translation is older than the original page and might be outdated. See what has changed.

LuCI2 (OpenWrt web 管理界面)

一直以来OpenWrt都是采用Lua写的web管理界面LuCI,(开机速度慢不说,居然比不过腾达等弱路由器开机速度)。 LuCI需要使用多个Lua扩展(如 ubus, luci.model.uci, nixio.fs, 等等)去存取系统信息和设置. 不幸的是这种解决方案在慢CPU和低内存的低配机器设备上是个灾难,此方案相当消耗资源且并不能很好的工作。

这导致开发了LuCI2, 一个不同架构的新的web管理节目。它不再使用Lua,而是使用静态HTML页面加JavaScript XHR方法。 这意味着从OpenWrt设备中下载后在客户端(浏览器)中构建HTML页面, 通过ubus存取各种系统数据(通过uhttpd-mod-ubus提供基于HTTP的接口API).

如上所述, LuCI2通过ubus和OpenWrt子系统通信(包括如networkservice以及其它)。遗憾的是并非每个主要的OpenWrt工具都自己注册了ubus,如LuCI2不能使用opkg(安装包管理)。 LuCI2通过提供在附加ubus名称空间的rpcd插件解决了这个问题。前面说的opkg它在ubus中注册了一个新的luci2.opkg路径来访问。

综上所述, LuCI2包括两个方面: 打包的HTML/CSS/JS文件 (htdocs) 和一些在OpenWrt环境下运行的附加小工具。

在接下来的章节中,你会找到各种关于LuCI2开发帮助的细节。

首先需要知道的是浏览器接收到的关于LuCI2菜单并不固定写死在任何文件。代替的是通过ubus使用luci.ui路径和menu方法。可以通过使用以下命令来查看:

ubus call luci2.ui menu '{ "ubus_rpc_session": "invalid" }'

内部的rpcd插件分析在目录/usr/share/rpcd/menu.d的所有文件,当前用户(基于传递的ubus_rpc_session)不允许增加或者删除条目。这将导致在一个二级菜单限制当前有权限访问的条目。

顶层菜单项用下面的JSON定义:

"foo": {
	"title": "Foo",
	"index": 12
}

(并且通过index值排序)。

第二层菜单项通过同样的方法定义:

"foo/bar": {
	"title": "Bar",
	"acls": [ "baz", "qux" ],
	"view": "foo/bar",
	"index": 5
}

注意,第二层菜单项可以在独立的文件中定义,这样就可以方便的添加新的菜单项定义而不用修改原有的文件。

每一个LuCI2子页面必须有一个存放在/www/luci2/template/目录下的模板。它们提供了非常简单的内容替换区的HTML文件。需要注意的是它们不包含任何变量的引用,这是JavaScript的功劳:用JavaScript去读取并写入内容。在为本地化系统(i18n)这些文件中唯一特定的语法类似下面的标记:

<p><%:Hello world%></p>

从模板中分离,每个LuCI2子页面也需要一个定义并存放在/www/luci2/view/目录下的一个视图,视图是使用了子页面特定对象的L.ui.view扩展的JavaScript文件。Javascript中需要提供execute方法的实现,该方法将在膜拜加载后被执行。可选的也可以提供子页面的titledescription属性。

有一点需要注意的是需要提供execute方法,创建视图失败有几种原因,特别是当它需要ubus加载额外的数据使用的时候。所以在execute方法中提供成功或者失败的信息是有意义的。不幸的是在异步方法加载特定数据的时候,不能简单的返回truefalse,解决的方法是返回一个Promise对象来运行推迟提供是否成功的信息。

最简单的视图可以如下所示:

L.ui.view.extend({
	title: L.tr('Foo'),			/* 可选的标题 */
	description: 'Bar',			/* 可选的描述 */

	execute: function() {
		var deferred = $.Deferred();	/* 创建一个延迟对象 */
		deferred.resolve();		/* 立即Resolve,它不做任何事可以返回失败 */
		return deferred.promise();	/* 返回Promise对象 (延迟对象的子集) */
	}
});

在开始之前,需要知道的是LuCI2提供了一些存取uci系统的一些帮助。如果写一个简单的管理/etc/config/下配置文件不需要完全知道ubus的调用方法,则可以跳过该部分。

构建一些更复杂的LuCI2视图前最好先弄明白使用到的ubus调用。完整的对象和方法列表可以通过运行ubus -v list命令来得到。

下面这个简单的例子调用了log对象和write方法,它需要提供一个event参数传递进去。使用ubus命令行国内根据是,需要如下所示命令:

ubus call log write '{ "event": "Foo" }'

LuCI2为ubus通讯提供了一个叫做L.rpc.declare的工具,它可以如魔法般的帮助JavaScript访问ubus方法。注意,定义声明方法的时候方法并不被执行,参数也未传递,这是为以后调用准备的一个方法。下面这个示例的方法调用了log对象的write方法:

var writeToLog = L.rpc.declare({
	object: 'log',
	method: 'write',
	params: [ 'event' ]
})

定义了一次这个方法后,可以在任意时间通过如下示例简单调用:

writeToLog('Foo');

在上面的例子中执行结果被忽略了,如果视图需要处理ubus返回的数据时不能忽略执行结果。下面例子将通过访问system对象的info方法来描述返回结果的处理。在命令行下通过以下命令访问:

# ubus call system info
{
	"uptime": 123,
	"localtime": 1234567890,
	"load": [
		1,
		2,
		3
	],
	"memory": {
		"total": 67108864,
		"free": 33554432,
		"shared": 0,
		"buffered": 16777216
	},
	"swap": {
		"total": 0,
		"free": 0
	}
}

在LuCI2(JavaScript)定义上面的访问如下所示:

var readSystemInfo = L.rpc.declare({
	object: 'system',
	method: 'info',
	expect: { memory: { } }			/* 可选, 只提取结果的一部分memory */
})

通过简单的调用.then方法可访问结果数据

readSystemInfo().then(function(memory) {
	console.log(memory);
});

在“feeds.conf”中添加一个feed:

src-git luci2 git://git.openwrt.org/project/luci2/ui.git

安装luci2包:

./scripts/feeds update
./scripts/feeds install luci2

在menuconfig中勾选Luci2包并编译新固件。

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2018/06/06 19:57
  • by tmomas