应用基础知识
应用目录结构
以新建项目为例,介绍下App工程的目录结构,以及每种文件类型的作用:
- app 源码目录
- App.js App的主界面
- AppDebug.js App的调试主界面(调试的时候启动OPK不带参数,可以在这里模拟数据)
- demo 示例代码
- DemoScreen Demo功能UI界面
- DemoViewModel Demo功能的业务逻辑
- DemoVoice Demo功能的语音指令接收器
- DemoTrigger 负责Demo功能与主流程对接(通过trigger可进行OPK间的跳转)
- extraResource 文件资源目录(例如:音视频)
- img 图片资源目录
- dist OPK存储目录(初始可能不存在,打包OPK后自动创建)
- node_modules App依赖的库(初始可能不存在,可在项目目录下执行npm install,会自动创建)
- .npmrc npm账号配置
- package.json App配置及依赖库管理
- app.json App的基本配置文件(不能修改)
应用组件
应用组件是机器人应用的基本构建模块,每一个应用组件都有自己的专属职责,所有应用组件一块构成一个完整的机器人应用。
Voice
Voice负责接收语音指令及服务端指令,需继承自BaseVoice。
onListenCallback为语音指令回调函数:
public onListenCallback(intent: string, result: any, id: number, text: string): boolean { return false; }
参数说明:
- intent 语音指令标识
- result 语音参数
- text 语音识别出的文本内容
返回值:
- true 表示该语音指令已被处理,后续不在传递。
- false 表示当前OPK不处理该指令,交由其它OPK处理。
Trigger
Trigger负责当前业务与主流程对接,可以通过trigger执行OPK间的跳转切换,需继承自Trigger,在初始化时需要一个Channel参数,该参数类型为字符串,是与ViewModel进行通信的标识,必须与ViewModel使用同一个字符串。
trigger函数是用来接收ViewModel发送过来的事件及携带的数据,接收到事件后可根据TriggerProtocol.eventId决定跳转到哪个OPK,以下示例为在接收到1001事件后跳转到机器人唤醒页:
public trigger(protocol: TriggerProtocol): void | boolean { switch (protocol.eventId) { case 1001: this._trigger('home', protocol); break; } }
this._trigger(‘home’, protocol )为真正执行OPK跳转的调用,其第一个参数为appKey,每一个OPK在注册信息的时候都会包含一个appKey,这是OPK的标识,第二个参数是我们希望给对方OPK的参数,接收方OPK可在构造函数中,使用props.navigation.state.params获取。
ViewModel
ViewModel主要用来实现当前业务逻辑,需继承自BaseViewModel,其在初始化的时候也需要一个Channel参数,该参数需与Trigger的保持一致,否则可能无法进行OPK的跳转。
ViewModel有两个生命周期函数:onStart 和 onStop , 顾名思义一个在业务开始的时候调用,一个在业务结束的时候调用。
除了两个生命周期函数,还有三个用来与Trigger通信的函数:
- _uiTrigger(eventId, data) UI事件触发的跳转
- _voiceTrigger(eventId, data) 语音事件触发的跳转
- _apiTrigger(eventId, data) Api调用触发的跳转
Screen
Screen是当前业务与用户交互的起始点,负责界面的UI展示及功能组件的加载,需继承自BaseComponent,Screen是整个业务的起始点,也是对外的唯一交互点,Screen的显示与隐藏直接决定当前业务的状态,所以其它组件的生命周期也需要与Screen进行绑定,保持与Screen同生同灭 。
需要在Screen的构造函数中调用接口绑定与其它应用组件的关系:
public constructor(props: BaseComponentProps) { super(props); this.viewModel = new DemoViewModel(); let voice = new DemoVoice(this.viewModel); //关联ViewModel及Voice的生命周期到当前界面上 this.setViewModel(this.viewModel); this.setVoice(voice); //注册trigger跳转,必须添加,否则trigger无效 triggerManager.addTrigger(new DemoTrigger()); }
生命周期函数,可用来处理业务的启动与停止事件:
//界面显示后的生命周期 public componentDidMount() { //重写界面的didMount,必须调用super super.componentDidMount(); } //界面销毁前的生命周期 public componentWillUnmount() { //重写界面的Unmount,必须调用 super super.componentWillUnmount(); }
渲染函数,用来显示UI及加载功能组件,如下所示在界面上显示一个文本,并在界面显示的时候开始导航:
public render() { let navigationParam = new NavigationParam('接待点', 0.5, 30000, 5000, 1000, 4); return ( <View> <Text style={{ fontSize: 17, color: 'red'}}> {'Hello Robot'}</Text> <NavigationComponent param={navigationParam} onFinish={this.onFinish} onStatusUpdate={this.onStatusUpdate} /> </View> );}
应用资源
图片资源
应用的图片资源需放置到img目录下,使用时通过require进行加载,参数为图片相对路径,如下示例:
<Image source={require('../../img/bg.png')} >
文件资源
文件资源统一放置在extraResource目录下,应用在编译打包时会一块打包到OPK中,随着OPK一起安装到机器人上,可通过以下接口获取到文件资源存放目录:
AppManager.getOpkExtraPath();
应用注册
机器人应用需将自身的信息注册到机器人系统中,机器人系统才能注册的信息结合外部条件(例如:语音指令)判断是否启动该机器人应用,index.js是我们整个机器人应用的入口文件,在加载后就会立即执行,所以我们的注册应该写在index.js文件中。
//上线使用 AppRegistry.registerConfig([{ appKey: 'Demo', component: () => App, intent: 'weather&get_weather', //例如:'weather&get_weather' appId: appid, priority: 1 }]); //Debug调试使用 AppRegistry.registerComponent(appName, () => AppDebug);
在示例代码里有两个注册,一个是正式注册到机器人系统,一个是Debug调试时使用,这两个可以同时存在,不会影响执行结果,系统会在不同的情况下选择对应的注册信息,Debug调试的时候不涉及语音调度及OPK切换,只需要知道启动界面就可以了,所以只需要把AppDebug调试界面传递进去就可以了。正式注册时需要的信息相对就比较多了:
- appKey OPK功能标识 ,不能与其他OPK重名,全局唯一,自己人工定义(切换OPK的时候会根据appKey查找对应的OPK)
- component OPK功能启动界面
- intent OPK的启动指令,其中weather是domain, get_weather是intent,通过&符号连接,且必须全部为小写字母,有其它OPK指定这个指令做Trigger跳转时,OPK会启动。或全局NLP解析出这个指令时,OPK会启动。(只填写启动OPK的指令就可以,OPK启动后能接收到所有指令)。OPK启动支持填写多个domain&intent,格式是[‘domain1&intent1′,’domain2&intent2’]
- appId 创建应用时使用的appId
- priority 功能优先级(OPK切换的时候会比较优先级,优先级低的不能打断优先级高的,OPK会切换失败,优先级为1-3的数组,1优先级最高,3最低)
注意1:intent在应用上线发布后,可通过OrionBase平台动态修改。
注意2:priority在应用上线发布后,可联系我们动态修改。
应用跳转
应用的跳转是通过在trigger里进行,具体可参考应用组件Trigger描述,在应用跳转时需要指定一个appKey,是应用注册时指定,官方标准OPK所包含的场景及其对应的appKey如下表所示:
应用名称 | appKey | 参数 | 描述 |
---|---|---|---|
基础应用(Portal) | wakeUp | 无 | 大表情页面 |
基础应用(Portal) | home | 无 | 唤醒首页 |
问路引领 | queryLocation | {“slots”:”{\”destination\”:[{\”value\”:\”目标点\”}]}”} 注意:slots内的参数需要额外一次json_encode。 | 问路引领 |
访客接待 | reception | 无 | 访客接待 |
导航 | navigation | {result:{destination: ‘目标点’, distance: -1}} | 导航 |
导览 | guide | 无 | 导览 |
广告 | advert | 直接trigger不可用 1:接待后台需要配置广告 2:获取广告资源数据 3:配置tigger第二个参数数据 | 广告 |
巡逻 | cruise | 无 | 巡逻 |
跳舞 | dance | 无 | 跳舞 |
拍照 | groupPhoto | 无 | 拍照 |
注意1:在开发时请避免与官方OPK中appKey重复。
注意2:在使用引领功能时,slots内的参数需要额外一次json_encode。
应用退出
OPK应用的退出原理:通过trigger跳转,到home页面,实现退出功能。
语音事件分发
语音事件会优先分发给当前正在运行的OPK,当前OPK需要处理并消耗掉该指令,可在Voice的onListenCallback返回true,不处理拦截的话请返回false。当前OPK不处理该语音指令的话,会去查询所有OPK的注册信息,如果该语音指令属于某一OPK的启动指令,则比较OPK的优先级,如果当前OPK优先级较低,则退出当前OPK,切换到新的OPK中去处理。该语音指令如果没有OPK能够处理,则检查下语音指令携带的数据里是否包含answer信息,如果包含answer信息则分发给闲聊模块进行处理。如下图所示: