为和后台任务作比较,本文的前台任务指在桌面主动运行小程序。
- 一旦要运行前台任务,会结束正运行的后台任务,而且前台任务运行期间,不能再运行新后台任务。代码中存在权限相关的,目的是看能否让前台、后台可同时运行,目前没作用。
- 执行前台任务时,所在小程序如果有libroseaplt.so,这些so是以driver方式加载,类型apltsotype_fgaplt。后台任务运行时,libroseaplt.so不是以driver方式,而是create_task_api/unload_deps机制。
- 每个后台任务都会绑定一个小程序。对这只移动,建议绑定到内置(fake_aplt)小程序的“空/用于只移动”任务,任务id:fake_task_id_empty。fake_aplt是个伪小程序,用这个不会让产生读、写文件这种开销。
- 后台任务限制:不能弹出界面。有重要信息放到日志,或声音提示。
- 存在2位置、且单任务允许一次失败。如果第一次start_aplt_task时,单任务处理结果失败,那此次不会移动到位置2,接下执行第二次start_aplt_task。在第二次start_aplt_task,不会移动到position1,不论此次单任务处理结果是成功还是失败,都会执行移动到position2。
- tbg_task::modify_task修改aplt_task字段时,不能影响xxx_tasks_排序,以及销毁/重创建该aplt_task。为达到这要求,主要表现在两个方面。1)用std::map<std::taplt_task_key, taplt_task>作为xxx_tasks_变量类型,而不是std::set<taplt_task>。2)当有涉及到排序、但过程中会修改字段时,像zerotz_t,不要把它放进taplt_task_key,而是改放进aux_key_id。在xxx_tasks_,aux_key_id会确保唯一。至于排序,那是tklink中的ttask_key去实现。
除前台运行小程序外,打开以下窗口也会结束、并且不能运行的新的后台任务。
enum | 描述 |
reason_map | 正在显示“地图”窗口,并且当前不在导航中。可能会修改当前地图信息 |
reason_settings | 正在显示“设置”窗口。可能会修改驱动要使用哪个小程序 |
reason_trigger | 正在显示“触发”窗口。修改语音语命令,可能会修改运行任务后触发配置 |
reason_task | 正在显示“任务”窗口。修改task_cpp任务配置,可能会修改欲运行任务的配置 |
reason_dnn | 正在显示“DNN”窗口 |
reason_moveit | 正在显示“机械臂”窗口 |
reason_camera | 正在显示“相机”窗口,并且当前没使用的机械臂的后台任务在运行 |
reason_netxmit | 正在用ihttp_cswamp,包括下载小程序,升级launcher、拼音包等 |
有出现以上情况时,tdisable_new_aplt_lock::disabled()会返回true,表示当前禁止执行后台任务。
一、tbg_task、tbg_task2
tbg_task封装了后台任务相关操作。其中tbg_task2负责处理某个后台任务的执行过程,虽然bg_task2_一直有效,但只有存在后台任务时,才可使用它。因为这样,调用tbg_task::bg_task2()获取bg_task2_时,首先要诊断is_ing_必须是true。
对task_cpp任务,只会处于两种状态:纯task_cpp和执行单任务。不是只有一开始时会处于纯task_cpp,只要结束一个单任务就会转入纯task_cpp,像正处于语音状态。但是,因为结束其它纯task_cpp是放在了start_sys2,如果stop2遇到纯task_cpp,那就表示要求结束此个task_cpp。
可用tbg_task2::in_task_cpp()判断是否处于纯task_cpp状态,为双向验证,tbg_task还用running_.task_cpp_fake_started表示当前是否处于这状态。在纯task_cpp,没有运行单任务,但还在用着start_task_cpp_fake()/nullptr_task_cpp_fake(),这就好像在运行/结束一个“假任务”。
结束纯task_cpp或单任务,都会把is_ing_置为false,但接下会立即进入单任务或纯task_cpp,那里会把is_ing_置为true,而且结束、进入的中间不会运行用户层代码。在用户层代码看来,可用is_ing_来判断是否正有个后台任务在运行。

对一次task_cpp,shedule()被调用次数是1+N+r。1是开始时纯task_cpp,N是内中单任务调用次数,r是N次调用中因为执行失败而重试的次数。N是由tros_cpp_api的request_single_task启动,r启动则由request_retry_aplt_task。
对一次task_cpp,task_finished被调用次数是N+r+1。N是内中单任务调用次数,r是N次调用中因为执行失败而重试的次数,1是最后一次的结束纯task_cpp。N+r由tros_cpp_api调用set_task_finished()或tbg_task2::slice()调用set_luafunc_finished触发,最后一次肯定由tros_cpp_api调用set_task_finished()触发。
task_cpp时,第一次的shedule()(tbg_bask::start_task_cpp_fake())没有对应的ttemp_task::task_finished。那它是怎么调起内中的第一个单任务?——在tros_cpp_api,第一次async_request_state_存储着“启动时状态(startup_state)”。执行到tros_cpp_api::slice(),发现async_request_state_不是nposm,就调用request_single_task去启动第一个单任务。
以IoT触发一次task_cpp类型的巡检任务为例,包括去门口抓拍、去沙发控制蓝牙灯、去窗台抓拍。看shedule()、task_finished()的调用次序。
- (tbg_task::request_klink_aplt_task)参数new_aplt_task指向iot_tasks_队列中的某个aplt_task。函数会调用tbg_task2::request_single_aplt_task,后者调用shedule。
- (tbg_task::request_klink_aplt_task中shedule)第一次shedule,
- (tbg_task::request_klink_aplt_task中start_sys2)调用start_task_cpp_fake,进入纯task_cpp状态。至此tbg_task::request_klink_aplt_task结束。
- (tros_cpp_api::slice中request_single_aplt_task)tros_cpp_api执行时间片,发现async_request_state_存储着“启动时状态(startup_state)”,即去门口抓拍。调用request_single_aplt_task。
- (tbg_task::request_single_aplt_task中shedule)第二次shedule,
- (tbg_task::start_sys2中start_aplt_task)start_sys2先是调用nullptr_task_cpp_fake结束纯task_cpp状态,再调用start_aplt_task,进入“去门口中抓拍”这个单任务。至此tbg_task::request_single_aplt_task结束。
- (tbg_task::stop2中stop_aplt_task)执行完“去门口中抓拍”单任务,tcpp_api小程序会调用ros.set_task_finished(),告知任务结束,并把finished2置为finished_luafunc。在tbg_task::pump,发现finished2不是nposm,于是调用tbg_task::stop2。由于当前正处于单任务状态,调用的是stop_aplt_task。
- (tbg_task::stop_aplt_task中task_finished)stop_aplt_task会调用到task_finished。这是第一次task_finished,它发现当前是单任务状态,调用task_cpp_api_->single_task_finished。后者会把async_request_state_存储着要执行的下一个单任务,即“去沙发控制蓝牙灯”。
- (tbg_task::stop_aplt_task中start_task_cpp_fake)start_task_cpp_fake让任务再度进入纯task_cpp状态。至此tbg_task::stop2结束。
- (tros_cpp_api::slice中request_single_aplt_task)tros_cpp_api执行时间片,发现async_request_state_存储着“去沙发控制蓝牙灯”。调用request_single_aplt_task。
- (tbg_task::request_single_aplt_task中shedule)第三次shedule,
- (tbg_task::start_sys2中start_aplt_task)start_sys2先是调用nullptr_task_cpp_fake结束纯task_cpp状态,再调用start_aplt_task,进入“去沙发控制蓝牙灯”这个单任务。至此tbg_task::request_single_aplt_task结束。
- (tbg_task::stop2中stop_aplt_task)执行完“去沙发控制蓝牙灯”单任务,tcpp_api小程序会调用ros.set_task_finished(),告知任务结束。和7)一样,在tbg_task::pump会调用tbg_task::stop2。由于当前正处于单任务状态,调用的是stop_aplt_task。
- (tbg_task::stop_aplt_task中task_finished)类似8)。stop_aplt_task会调用第二次task_finished,它发现当前是单任务状态,调用task_cpp_api_->single_task_finished。后者发现task_cpp两个单任务都执行了,要结束此个“巡检”,调用set_end_request。
- (tbg_task::stop_aplt_task中start_task_cpp_fake)start_task_cpp_fake让任务再度进入纯task_cpp状态。至此tbg_task::stop2结束。
- (tbg_task::stop2中stop_non_aplt_task)在下一个tros_cpp_api::slice,发现“end_flag_ == end_request”,tcpp_api小程序会调用ros.set_task_finished(),告知任务结束。和7)、13)一样,在tbg_task::pump会调用tbg_task::stop2。由于当前正处于纯task_cpp状态,调用的是stop_non_aplt_task。
- (tbg_task::stop_non_aplt_task中task_finished)stop_non_aplt_task会调用第三次task_finished,它发现当前是纯task_cpp状态,调用task_cpp_api_->task_finished。实现的task_finished小程序在那里做此个task_cpp任务结束的清理工作。至此tbg_task::stop2结束。同时此个task_cpp结束。
蓝牙单任务可使用失败再试机制。任务配置中有个字段叫max_fails,指示该任务允许失败的最大次数。只有蓝牙单任务是2,其它都是1。
- 在stop_aplt_task()返回true,意味着retry=true,表示要再试。
- stop2()发现retry是true,调用instance->app_request_retry_aplt_task()。后者由tbg_task2实现,它发现非零的aplt_task->fails值,再次shedule()这个aplt_task。
- request_retry_aplt_task()继续调用start_sys2,于是此单任务再次被运行。相比上一次,它的task2->fails多了1,意味着此次要是又失败,fails+1就能到最大失败次数(max_fails),那这次就不可能再试了。
当不是再试时,aplt_task->fails值是0,而不是像max_fails。
三、tros_instance::ttask
ttask让当前环境具有ros操作能力。具体说,会确保存在能获得tf树的tf2_ros::Buffer,能让回调到接收导航结果的did_move_base_result,等等。和ros相关的两个主要操作是导航和机械臂,在它们执行期间,须确保系统中至少存在一个ttask。
导航、机械臂,往往是后台任务中做的一部分操作,尤其导航,好多后台任务的第一阶段就是它。导航会创建ttask,不免就会把ttask和后台任务联系在一起。但无论无何,ttask功能是让当前环境具有ros操作能力。如果一个前台任务须要导航、机械臂,照样会创建ttask,后面“lua代码中导航”会说到这个。
开始导航阶段的入口函数是base_instance::lua_did_navigation_start,在执行这函数期间,会创建ttask。如果之前有一个,会先销毁,再重新创建。
因为存在lua_did_navigation_start时创建的ttask,即使地图界面这些都销毁了,还有它在确保证能执行ros操作。那什么时候销毁ttask。
(后台任务或前台小程序)导航结束,app会收到did_move_base_result。如果luafunc指定的任务不是机械臂,是timing任务、lua函数这些,会erase_task。为什么机械臂时不销毁task?——操作机械臂须要ros环境,免得重新创建task。
(后台任务)如果luafunc指定任务是机械臂,那要等到操作完机械臂,结束此个后台任务,执行app_post_stop_bg_task()时。另外,如果提前中止后台任务中导航,也是在这里erase_task。
(前台小程序)想提前中止导航,方法是关闭当前窗口,关闭窗口(APLT_MSG_DIDDLGCLOSE)时会erase_task。退出小程序一定会关闭窗口,所以退出也能中止导航。
四、lua代码中导航
lua代码中出现导航,意味着要运行前台小程序,而前台小程序会停掉、以及运行其它后台任务。这限制了使用,不排除将来会放弃这功能。要通过按按钮直接导航、执行小程序的,换用去“中心”去执行。
前台小程序会须到导航,形式是“导航+lua代码”。让想像下空调小程序,要求把空调调到25度。那它执行步骤是先移到空调位置,再调25度温。为可灵活设置温度值,调温不是作为一个timing任务,而是段lua代码。
1、(lua,小程序)navigation_goal_:set_did_left_click("click_navigation_goal");
set_did_left_click用于挂接当用户单击navigation_goal_按钮后,会执行什么操作。一旦单击按钮,将执行个lua函数:click_navigation_goal。
2、(lua,小程序)widget:navigation_start(uuid, "did_navigation_goal");
在click_navigation_goal中执行这条语句,也就是说,当用户单击navigation_goal_按钮后,会执行它。
widget是一个从tcontrol派生的控件类型,像此刻正单击的按钮。参数uuid是要移动到位置的uuid。did_navigation_goal是导航结束后要执行的lua函数。
navigation_start是个C实现函数,它会调用lua_did_navigation_start启动导航,后者会调用ros_instance_.moveto_guid。
导航结束,会调用did_move_base_result。在那里会调用lua代码写的did_navigation_goal。并会erase_task。如果想提前中止导航,那关闭当前窗口,或退出小程序。这整个过程有创建、销毁ttask,但没有后台任务。