后台任务:定时任务、timing任务

定时让用户可以规划机器人哪时间执行哪任务,像6:30打开豆浆机,17:00关窗帘。

图1 launcher中的定时界面

 

  1. 任务总是归属哪个小程序。小程序中被调函数都是lua脚本函数,即实现任务的小程序可以全是lua代码。
  2.  一个小程序能包含多个任务。
  3. 一天内可以多次执行同一任务。
  4. 每个任务会有个触发时间t,任务会在[t, t+5分钟]内的某个时间点开始执行。为什么不是准点t?一来app不能保证恰好在t时刻检测开始哪任务,二来在t时刻因为一些原因该任务可能无法执行。
  5. 小程序优先级比任务高。这表现在两个方面。1)当前有任务正在执行,一旦用户运行任务所在小程序,或和该任务有权限冲突的小程序,任务会立即被结束。2)该任务所在小程序正在运行,或和该任务有权限冲突的小程序正在运行,虽然时间点上该任务是可执行了,但也得等。如果(t+5分钟)内用户结束小程序,那会开始该任务。
  6. 引入权限,是因为有独占资源,目前两种权限:蓝牙、导航。和手机编程的权限不同,这里权限总是被允许的,又要写上,是为保证把这独占资源赋给优先级更高的使用者。举个例子,正运行的任务A需要蓝牙,用户此刻要运行小程序b,小程序b也需要蓝牙,那只能结束掉优先级低的任务A。
  7. 任务执行时,不能主动在界面弹出什么提示,即任务不能有界面操作。要想告知错误、重要提示,等等,通过写timing.pb日志。
  8. 需要蓝牙权限时,考虑到蓝牙会出现偶然错误,第一次执行失败后,如果时间没过[t, t+5分钟],会给第二次机会。
标识描述
navigation导航
ble蓝牙

 

一、编写任务

以下用了“BLE Smart”小程序中代码,其bundleid是aplt.leagor.blesmart,任务id是query_ip。

图2 任务执行逻辑

运行任务时,app只加载两个lua文件:main.lua、timing.lua。默认必须存在两个表:aplt_leagor_blesmart、aplt_leagor_blesmart__timing,后者是任务特有,要没意外,写在timing.lua。

1.1 settings.cfg

[timing]
	id = "query_ip"
	permissions = "navigation, ble"
[/timing]

通过写在settings.cfg的[timing]块,小程序向外告知它含有哪些任务。一个[timing]表示一个任务,可以出现多个。

在[timing]块内,id用于标识任务,不同小程序可重复,此小程序内唯一就行。permissions表示该任务要使用的权限。

 

1.2 aplt_leagor_blesmart__timing.start(id)

到点了,app要执行某个任务,就会调用xxx__timing.start。参数id(string)指示要执行的任务,像quiery_ip。

function aplt_leagor_blesmart__timing.start(id)
	local aplt = aplt_leagor_blesmart;
	local this = aplt_leagor_blesmart__timing;

	if id == "query_ip" then
		aplt.create_ble("timing");

		this.start_scan();
		return true, 500;
	else
		return false, "unknown timing id:" .. id;
	end
end

启动任务。只是启动,并不是一定要在这函数内执行完任务。在示例,创建一个vble对象,并开始蓝牙扫描。

填两个返回值,这分两种情况。

  • 任务能执行。第一个true(bool),表示成功。第二个是定时器时间间隔(integer),单位毫秒,最小400毫秒。0表示不用定时器。示例想使用一个500毫秒定时器。
  • 任务不能执行。第一个false(bool),表示失败。第二个是错误描述(string)。

一旦start成功,进入任务执行阶段。

 

1.3 aplt_leagor_blesmart__timing.timer_handler(now)

如果在start时返回了非0的定时器间隔,到时间会调用xxx__timing.timer_handler。参数now(integer)等于此刻用rose.SDL_GetTicks()计算出的值。

function aplt_leagor_blesmart__timing.timer_handler(now)
	local aplt = aplt_leagor_blesmart;
	local this = aplt_leagor_blesmart__timing;
	local _ = aplt_leagor_blesmart._
	local vgettext2 = aplt_leagor_blesmart.vgettext2;
	rose.log(now .. " aplt_leagor_blesmart__timing.timer_handler called");

	if this.stop_scan_ticks_ ~= 0 then
		assert(this.scaning_);
		if now >= this.stop_scan_ticks_ then
			this.stop_scan();

			local max_times = 2;
			if this.scan_times_ <= max_times then
				rose.timing_log(vgettext2("$times|th scan fail, try next scan",
				    {times = this.scan_times_}));
				this.start_scan();
			else 
				rose.timing_finished(_("No scan to device, task failed"));
			end
		end
	elseif this.connect_ticks_ ~= 0 then
		local threshold = 5000; -- 5 second
		if now >= this.connect_ticks_ + threshold then
			rose.timing_finished(_("Connected, but can not complete task within a limited time"));
		end
	end
end

示例用定时器控制扫描溢出时间。另外,如果连接成功后,5秒内不能完成查询ip,认为任务执行失败。

 

1.4 rose.timing_log(log)

输出任务日志,log(string)是日志内容

任务在后台无声执行,但用户得有地方知道今天设定的定时任务是什么个执行情况。作为中介,系统使用一个timing.pb文件,任务执行过程中,调用rose.timing_log向timing.pb写日志,用户则通过查看timing.pb,知道任务执行结果。

 

1.5 rose.timing_finished(log)

调用rose.timing_finished,小程序告知app,完成任务了。考虑到完成时往往会输出一条日志,这里就给个log(string)参数,免得额外调用rose.timing_log。

 

1.6 aplt_leagor_blesmart__timing.stop()

app发现ttiming.running_.finished是true,会调用xxx__timing.stop。在这里,小程序把此次任务是成功还是失败反馈给app。

function aplt_leagor_blesmart__timing.stop()
	local aplt = aplt_leagor_blesmart;
	local this = aplt_leagor_blesmart__timing;

	if this.my_peripheral_ ~= nil then
		aplt.ble_:disconnect_peripheral();
	end

	return this.result;
end

任务只要进入执行阶段了,那xxx__timing.stop一定会被调用,而且是此次执行的最后一个函数。在这里可以放一些释放操作。示例是断开连接。

填写bool返回值,true表示此次任务执行成功、false则失败。示例中this.reult是个bool变量,初始false,查到一个ip后,置为true,于是它就指示了此次任务的执行成败。

如果任务需要一条日志来描述执行结果,那处理方法是在之前、以它为传参数调用rose.timing_finished。

全部评论: 0

    写评论: