From 33920fe53456e50cb5a91a3a780ef2099c928292 Mon Sep 17 00:00:00 2001 From: "xjin.gao@quectel.com" Date: Mon, 5 Aug 2024 10:09:12 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=20:=20<=E6=96=B0=E5=A2=9Evoip?= =?UTF-8?q?=E7=94=B5=E8=AF=9Dapi=E6=96=87=E6=A1=A3>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 是否需要文案翻译: 是 --- docs/API_reference/zh/networklib/voip.md | 329 +++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 docs/API_reference/zh/networklib/voip.md diff --git a/docs/API_reference/zh/networklib/voip.md b/docs/API_reference/zh/networklib/voip.md new file mode 100644 index 00000000..498cba0e --- /dev/null +++ b/docs/API_reference/zh/networklib/voip.md @@ -0,0 +1,329 @@ +# class voip - voip电话 + +该类提供 voip 电话功能,是基于 sip 协议实现的网络电话客户端。 + +**示例:** + +```python +# 导入voip模块 +# -*- coding: UTF-8 -*- +#voip目前以源码的形式提供,可自行放/usr目录下 +from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent +import log +import dataCall +import utime as time + + +''' +下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 +''' +PROJECT_NAME = "QuecPython_VoipCall_example" +PROJECT_VERSION = "1.0.0" + +checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION) + +# 设置日志输出级别 +log.basicConfig(level=log.INFO) +voip_log = log.getLogger("VOIP_CALL") + +#voip状态回调函数 +def voip_event(event, call): + if event == VoIPEvent.VOIP_EVENT_REGISTERED: + print('VOIP_EVENT_REGISTERED entry') + elif event == VoIPEvent.VOIP_EVENT_RINGING: + print('VOIP_EVENT_RINGING entry') + try: + call.answer() + call.startAudioService() # pcmu/pcma + except InvalidStateError: + pass + except: + call.hangup() + elif event == VoIPEvent.VOIP_EVENT_CANCELED: + print('VOIP_EVENT_CANCELED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_BYE: + print('VOIP_EVENT_BYE entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ENDED: + print('VOIP_EVENT_ENDED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ANSWERED: + print('VOIP_EVENT_ANSWERED entry') + call.startAudioService() + else: + print('UNKNOWN {} entry'.format(event)) + +if __name__ == '__main__': + stagecode, subcode = checknet.wait_network_connected(30) + if stagecode == 3 and subcode == 1: + voip_log.info('Network connection successful!') + + # 获取拨号IP + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1: + voip_log('lte dataCall normal') + # 创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + # 注册服务器 + phone.start() + + time.sleep(3) + voip_log.info('VOIP start call 1007') + # 拨打电话:XXX + in_call = phone.call('1007') + + time.sleep(20) + voip_log.info('qpy hangup call') + # 主动挂断电话 + in_call.hangup() + + time.sleep(3) + voip_log.info('qpy deregitster voip service') + # 释放VoIPPhone对象 + phone.stop() + else: + raise ValueError("dataCall getInfo error") + else: + voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) +``` + +```python +# 导入voip模块 +# -*- coding: UTF-8 -*- +#voip目前以源码的形式提供,可自行放/usr目录下 +from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent +import log +import dataCall +import utime as time + + +''' +下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 +''' +PROJECT_NAME = "QuecPython_VoipAnswer_example" +PROJECT_VERSION = "1.0.0" + +checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION) + +# 设置日志输出级别 +log.basicConfig(level=log.INFO) +voip_log = log.getLogger("VOIP_ANSWER") + +#voip状态回调函数 +def voip_event(event, call): + if event == VoIPEvent.VOIP_EVENT_REGISTERED: + print('VOIP_EVENT_REGISTERED entry') + elif event == VoIPEvent.VOIP_EVENT_RINGING: + print('VOIP_EVENT_RINGING entry') + try: + call.answer() + call.startAudioService() # pcmu/pcma + except InvalidStateError: + pass + except: + call.hangup() + elif event == VoIPEvent.VOIP_EVENT_CANCELED: + print('VOIP_EVENT_CANCELED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_BYE: + print('VOIP_EVENT_BYE entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ENDED: + print('VOIP_EVENT_ENDED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ANSWERED: + print('VOIP_EVENT_ANSWERED entry') + call.startAudioService() + else: + print('UNKNOWN {} entry'.format(event)) + +if __name__ == '__main__': + stagecode, subcode = checknet.wait_network_connected(30) + if stagecode == 3 and subcode == 1: + voip_log.info('Network connection successful!') + + # 获取拨号IP + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1: + voip_log('lte dataCall normal') + # 创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + # 注册服务器 + phone.start() + #注册完成后,voip会一直处于监听状态,等待服务器拨打电话。 + else: + raise ValueError("dataCall getInfo error") + else: + voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) +``` + + +## 构造函数 + +### `VoIPPhone` + +```python +class VoIPPhone(server, port, username, password, myIP, callCallback, sipPort, domain, displayName, timeout) +``` +创建voip电话对象。 + +**参数描述:** + +- `server` - 服务器地址,字符串类型。 + +- `port` - 服务器端口,整型。 + +- `username` - 注册服务器使用的用户名,字符串类型。 + +- `password` - 注册服务器使用的密码,字符串类型。 + +- `myIP` - 本地使用的IP,字符串类型。 + +- `callCallback` - 注册回调,监听不同的通话状态并通过回调反馈给用户。 + +- `sipPort` - 本地使用的端口(可选参数),整型。 + +- `domain` - 注册服务器使用的域(可选参数),字符串类型。 + +- `displayName` - 注册服务器使用的显示名称(可选参数),字符串类型。 + +- `timeout` - 默认超时时间,整型,单位秒。 + +> domain参数,注册服务器的必须参数,未设置时,默认和server配置服务器地址相同。 +displayName参数,注册服务器的可选参数,未设置时,默认和username配置相同。 + +示例: +```python +#voip目前以源码的形式提供,可自行放/usr目录下 +from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent +phone = VoIPPhone(server: str, port: int, username: str, password: str, myIP: str, callCallback = None, sipPort=5060, domain=server, displayName=username) +``` + +## 方法 + +### `phone.start` + +```python +phone.start() +``` + +启动 voip 电话客户端,进行服务器注册。 + +> 对应注册参数,在申请对象时,已配置。正常无返回,返回-1或抛异常,表示注册服务器失败。需检查网络和服务器参数是否正常。 + +### `phone.stop` + +```python +phone.stop() +``` + +关闭 voip 电话客户端,断开与服务器的连接。 + +### `phone.get_answer_number` + +```python +call.get_answer_number() +``` + +该方法用于获取来电号码。 + +**返回值:** + +- `answer_number` - 来电号码,字符串类型。 + +### `phone.call` + +```python +call = phone.call(number) +``` + +该方法用于主动拨打voip电话。 + +**参数描述:** + +- `number` - 接收方电话号码,字符串类型。 + +**返回值:** + +- `call` - 通话对象,用来和接收方电话号进行通信。 + +### `call.hangup` + +```python +call.hangup() +``` + +该方法用于挂断 voip 通话中电话。 + +> 已接听的voip电话,使用 call.hangup 进行挂断电话。 + +### `call.cancel` + +```python +call.cancel() +``` + +该方法用于取消 voip 拨号中电话。 + +> 未接听的voip电话,可使用 call.cancel 进行取消拨号。 + +### `call.answer` + +```python +call.answer() +``` + +该方法用于接听 voip 呼入的电话,需配合call.startAudioService。 + +### `call.set_dtmf` + +```python +call.set_dtmf(data) +``` + +该方法用于设置voip电话DTMF音。 + +**参数描述:** + +- `data` - DTMF字符串,字符串类型,有效字符数有:0-9、A、B、C、D、*、#。 + +### `call.startAudioService` + +```python +call.startAudioService() +``` + +该方法用于voip电话接听后,开启audio。 + +## 常量 + +### `VoIPEvent.VOIP_EVENT_REGISTERED` + +voip电话服务中事件通知:服务器注册成功。 + +### `VoIPEvent.VOIP_EVENT_DEREGISTERED` + +voip电话服务中事件通知:服务器注册异常。 + +### `VoIPEvent.VOIP_EVENT_RINGING` + +voip电话服务中事件通知:电话来电通知。 + +### `VoIPEvent.VOIP_EVENT_CANCELED` + +voip电话服务中事件通知:拨号中被取消。 + +### `VoIPEvent.VOIP_EVENT_BYE` + +voip电话服务中事件通知:电话挂断。 + +### `VoIPEvent.VOIP_EVENT_ENDED` + +voip电话服务中事件通知:电话已终止。 + +### `VoIPEvent.VOIP_EVENT_ANSWERED` + +voip电话服务中事件通知:电话接听。 + + -- Gitee From 9bac79a424c89382594aec6ee1469caa4d8864c8 Mon Sep 17 00:00:00 2001 From: "xjin.gao@quectel.com" Date: Mon, 19 Aug 2024 15:12:56 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=20(umqtt):=20=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0MQTTClient.wait=5Fmsg=E9=87=8D=E8=A6=81?= =?UTF-8?q?=E4=BA=8B=E9=A1=B9=EF=BC=8C=E5=B9=B6=E6=9B=B4=E6=96=B0demo?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/API_reference/en/networklib/umqtt.md | 14 ++++++++++++++ docs/API_reference/zh/networklib/umqtt.md | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/docs/API_reference/en/networklib/umqtt.md b/docs/API_reference/en/networklib/umqtt.md index 40e42e56..9439f301 100644 --- a/docs/API_reference/en/networklib/umqtt.md +++ b/docs/API_reference/en/networklib/umqtt.md @@ -251,6 +251,8 @@ MQTTClient.wait_msg() Blocks waiting for a message response from the MQTT server. +>If this method is called in a thread, it is necessary to ensure that the thread stack created is >= 16K. For details, please refer to the mqtt reconnection sample code. + * Parameter None @@ -381,6 +383,7 @@ import net import _thread import checkNet import dataCall +import uos from umqtt import MQTTClient PROJECT_NAME = "QuecPython_MQTT_example" @@ -552,7 +555,18 @@ class MqttClient(): return -1 def loop_forever(self): + task_stacksize =_thread.stack_size() + name,platform = uos.uname()[1].split("=",1) + if platform == "EC600E" or platform == "EC800E": + _thread.stack_size(8 * 1024) + elif platform == "FCM362K": + _thread.stack_size(3 * 1024) + else: + _thread.stack_size(16 * 1024) + # Before creating a thread, modify the thread stack space according to the platform. _thread.start_new_thread(self.__listen, ()) + # After the thread is created successfully, the platform thread stack default size is restored. + _thread.stack_size(task_stacksize) if __name__ == '__main__': ''' diff --git a/docs/API_reference/zh/networklib/umqtt.md b/docs/API_reference/zh/networklib/umqtt.md index 5358e766..00a6b029 100644 --- a/docs/API_reference/zh/networklib/umqtt.md +++ b/docs/API_reference/zh/networklib/umqtt.md @@ -222,6 +222,7 @@ MQTTClient.wait_msg() 阻塞等待服务器消息响应。 +>该方法如果在线程中调用时,需保证创建线程栈空间>=16K,具体请参照mqtt重连示例代码. ### `MQTTClient.get_mqttsta` @@ -339,6 +340,7 @@ import net import _thread import checkNet import dataCall +import uos from umqtt import MQTTClient PROJECT_NAME = "QuecPython_MQTT_example" @@ -510,7 +512,18 @@ class MqttClient(): return -1 def loop_forever(self): + task_stacksize =_thread.stack_size() + name,platform = uos.uname()[1].split("=",1) + if platform == "EC600E" or platform == "EC800E": + _thread.stack_size(8 * 1024) + elif platform == "FCM362K": + _thread.stack_size(3 * 1024) + else: + _thread.stack_size(16 * 1024) + # 创建线程之前,按照平台,修改线程栈空间。 _thread.start_new_thread(self.__listen, ()) + # 线程创建成功后,恢复平台线程栈默认大小。 + _thread.stack_size(task_stacksize) if __name__ == '__main__': ''' -- Gitee From df49f188df041da6358b0cfa5fe776807e02aa24 Mon Sep 17 00:00:00 2001 From: "xjin.gao" Date: Thu, 12 Sep 2024 03:16:42 +0000 Subject: [PATCH 3/4] update docs/API_reference/zh/networklib/voip.md. Signed-off-by: xjin.gao --- docs/API_reference/zh/networklib/voip.md | 84 ++++++++++++------------ 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/docs/API_reference/zh/networklib/voip.md b/docs/API_reference/zh/networklib/voip.md index 498cba0e..02e0c256 100644 --- a/docs/API_reference/zh/networklib/voip.md +++ b/docs/API_reference/zh/networklib/voip.md @@ -12,7 +12,7 @@ from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent import log import dataCall import utime as time - +import checkNet ''' 下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 @@ -58,32 +58,31 @@ if __name__ == '__main__': stagecode, subcode = checknet.wait_network_connected(30) if stagecode == 3 and subcode == 1: voip_log.info('Network connection successful!') - - # 获取拨号IP - lte = dataCall.getInfo(1, 0) - if lte[2][0] == 1: - voip_log('lte dataCall normal') - # 创建一个VoIPPhone实例 - phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) - # 注册服务器 - phone.start() - - time.sleep(3) - voip_log.info('VOIP start call 1007') - # 拨打电话:XXX - in_call = phone.call('1007') - - time.sleep(20) - voip_log.info('qpy hangup call') - # 主动挂断电话 - in_call.hangup() - - time.sleep(3) - voip_log.info('qpy deregitster voip service') - # 释放VoIPPhone对象 - phone.stop() - else: - raise ValueError("dataCall getInfo error") + # 获取拨号IP + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1: + voip_log.info('lte dataCall normal') + # 创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + # 注册服务器 + phone.start() + + time.sleep(3) + voip_log.info('VOIP start call 1007') + # 拨打电话:XXX + in_call = phone.call('1007') + + time.sleep(20) + voip_log.info('qpy hangup call') + # 主动挂断电话 + in_call.hangup() + + time.sleep(3) + voip_log.info('qpy deregitster voip service') + # 释放VoIPPhone对象 + phone.stop() + else: + raise ValueError("dataCall getInfo error") else: voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) ``` @@ -96,7 +95,7 @@ from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent import log import dataCall import utime as time - +import checkNet ''' 下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 @@ -142,18 +141,17 @@ if __name__ == '__main__': stagecode, subcode = checknet.wait_network_connected(30) if stagecode == 3 and subcode == 1: voip_log.info('Network connection successful!') - - # 获取拨号IP - lte = dataCall.getInfo(1, 0) - if lte[2][0] == 1: - voip_log('lte dataCall normal') - # 创建一个VoIPPhone实例 - phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) - # 注册服务器 - phone.start() - #注册完成后,voip会一直处于监听状态,等待服务器拨打电话。 - else: - raise ValueError("dataCall getInfo error") + # 获取拨号IP + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1: + voip_log.info('lte dataCall normal') + # 创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + # 注册服务器 + phone.start() + #注册完成后,voip会一直处于监听状态,等待服务器拨打电话。 + else: + raise ValueError("dataCall getInfo error") else: voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) ``` @@ -184,11 +182,11 @@ class VoIPPhone(server, port, username, password, myIP, callCallback, sipPort, d - `sipPort` - 本地使用的端口(可选参数),整型。 -- `domain` - 注册服务器使用的域(可选参数),字符串类型。 +- `domain` - 注册服务器使用的域,字符串类型。 - `displayName` - 注册服务器使用的显示名称(可选参数),字符串类型。 -- `timeout` - 默认超时时间,整型,单位秒。 +- `timeout` - 超时时间,整型,单位秒,默认为20秒。 > domain参数,注册服务器的必须参数,未设置时,默认和server配置服务器地址相同。 displayName参数,注册服务器的可选参数,未设置时,默认和username配置相同。 @@ -210,7 +208,7 @@ phone.start() 启动 voip 电话客户端,进行服务器注册。 -> 对应注册参数,在申请对象时,已配置。正常无返回,返回-1或抛异常,表示注册服务器失败。需检查网络和服务器参数是否正常。 +> 对应注册参数,在申请对象时已配置。正常无返回,返回-1或抛异常,表示注册服务器失败,需检查网络和服务器参数是否正常。 ### `phone.stop` -- Gitee From 31e37da419b710fd7fd6d7b0b999509678580443 Mon Sep 17 00:00:00 2001 From: "xjin.gao@quectel.com" Date: Tue, 24 Sep 2024 14:29:00 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=20():=20=E6=96=B0=E5=A2=9Evoip?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 是否需要文案翻译: 是 --- .../net-protocols/voip/microVoip.png | Bin 0 -> 10653 bytes .../net-protocols/voip/sipInteraction.png | Bin 0 -> 29306 bytes .../net-protocols/voip/voip_answer.png | Bin 0 -> 14058 bytes .../net-protocols/voip/voip_call.png | Bin 0 -> 12909 bytes .../net-protocols/voip/voipprotocolsystem.png | Bin 0 -> 11893 bytes .../zh/network-comm/net-protocols/README.md | 1 + .../zh/network-comm/net-protocols/voip.md | 345 ++++++++++++++++++ 7 files changed, 346 insertions(+) create mode 100644 docs/Application_guide/zh/media/network-comm/net-protocols/voip/microVoip.png create mode 100644 docs/Application_guide/zh/media/network-comm/net-protocols/voip/sipInteraction.png create mode 100644 docs/Application_guide/zh/media/network-comm/net-protocols/voip/voip_answer.png create mode 100644 docs/Application_guide/zh/media/network-comm/net-protocols/voip/voip_call.png create mode 100644 docs/Application_guide/zh/media/network-comm/net-protocols/voip/voipprotocolsystem.png create mode 100644 docs/Application_guide/zh/network-comm/net-protocols/voip.md diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/voip/microVoip.png b/docs/Application_guide/zh/media/network-comm/net-protocols/voip/microVoip.png new file mode 100644 index 0000000000000000000000000000000000000000..033b650df7b02f5eea543e3d08be8d0920bee5e3 GIT binary patch literal 10653 zcmd6Nc{rQf+P9)DT145XDWO`lN(VL75JV{(HLJ0uTAQZCJcXvaDypU`s*0j&Dw>+7 z*fk_Imm);TCT2oS2_n3Y+UMQ-obP+DbFS~4>%G1|T+g*US?jsix`*HIzV8)v!&r}# zLx_WkiHY;NKFo}Xi5bSkbV!};2=Jfz)5vb%amdF^Pn)TvTVxIxu-?-$(qdx5#2wqY zeHa+Cd+FcwVPfKH-v2v<_sqY;#3Xa+I!wzV(2l|!$rl$M=Zm(pn&%%Y>=Zn6M~%JJy5J|vgb$}=l27rOIOi1)d{)r9^ZK1LhtOqq zWp@(#Ow6k@XF!SIM>mvW5jZbm&k7^o30X0yaxXK4T6-^ZL+!g4)zO1eK^UVgR%jAu z>_<;s2IPC#Ee<-w!~~^*(7@Oh!* zX-oe&P1I*?x@)*MM{hZ3b+V1|s#WBw|9yW7u_9#To_&e`$)tj%?)0nl({)A^5!VOb zsxghM7hby(CM5=k@7@np(>b|jQ++J}%xs$H_Ob2LWJ?rpnPG|?rF7ur3AFQ`?HN$l z-6~H~hH%hy>OE=-)046X4?!b>XfENBUJY@OpTRH8mike=In2)oi*H^5$=J6(%ym_d zFiU4i_%(IoWq&t3mwGa4#O?JNY958`kCA(jqbQWNFrn-!nZX-3fETVZP3_5BSH{vB zmb;_|>jIX$37m=WM%mgU6LgD?mEgV*1BzvdD?FvNjTkaENz1RI(e3+jd6+;_$>kv7 zoXR454-K(#jDE$H+*QjsCkCE@3#++CkAU`zXRwMI7zBR2obkE-ebgBf4ct_47c%fs zA9hW-zmdphVpa0h`!z(JpO%G;s(z3CmWiL-wGfNLQ`E3FGp;dhcwYM2YPP>N@8VjI z=H73xb1(1HTb7*~s%s1Tm1V zOL&!RDNk0pQA4#p7l;2iyICO{QDquZ(u)O-PCsMPO57OtN?$>|wN2_oM+3Nk!cVb7 z20yp;z5F>}lXCfi{6uZ###>qEbjQ(v*CG*>QHeG4G58l%QAoT`7Q3IPXQ_1Jd9-Ee zp$bzw^2TPE`A9W#@#=}Hw&kgx7j+o;-HD29jehHE6zhh;@>1eUYn5bOJQy#o=6E0i z`pg^M#0=B4d?}EL6=F52#oNfsy$D|MnpS*?Ca-?Gpx*zo*Pdz8)b&dxGYoyg+!dO` ztG8=ZvymzIL>Rf|OTdW|#|BlWBNrCzS={qzsix_JWcqo}%u;S%KnPb;;Y{2Ky7^U^ z&~&>TDxfL%8M1i@jT~%x)@fstto*pLx6(cbsRNmRp*${V;v5{4n21iqRK+BkNG2Dh zS0Z4;dV|CnwX&qOH z@~qj$y^|oRQ(0}zyk_=PNius&qUGyQ35-3%L#1?Kz~#a`fj5~dDs}mM$Q}`wnA={y z)FvzEAxxXO*xE*5;M*|$-815GZRF1*`LfGO*-j+nRFXB7@a7)B152<;@Gv2Ul!xhF z#%wzBja_rqrZ601Tj2E~q>d8aiWPRJb7Q5ZSC_3R$m2myV~+XhRMD!R&Jz49Rs&^! z$l2om$fMkZ4JJx%>&CI;c%yvt@vk~YZPYU88{d#Kh#1wiyJcrS;J1X`DMiD~$1;;G=jMK6g3%S6)V5U7G7SlHBGOIf|u8~SG@BVs!JKQR@*h(D%>|8i&A)&CmZZTSqnQq6Vojo zR*9jLJEb~B;fH|`^`7}(30pu!``4mf@u!4SdR~zcD=M^MkFtQh?d8s~4UfI4jLe|% zbM%S}M{;SZ6D74=ox2`y5*dpfrQ=++OA)NiHw3r4^7ewMBi6gr3=Z83Vx4wF(X81| zE?^c*ShE$ScUmsMAVT2j^NYI7raCWe{H>Wyh1p<~+ha|RlAtS(qg(?{gDOzaezE`U zpWt5Gh&Ap^{fO!2t{5ZPa}`zh8aFrSmBQu}`8c2B;NVG2O>V7`KE#~`m9JhWh*rQ) z1@#1H*eK|=bBE222VHGQp2xrT{D{}|r;Y{U`e)u+R2@5k7P>UpvO0dbrznoZ&YL#txDtoWv*zjB1C^H?zl%jKs13J- zYyA>Z6_DzynGx?@<9Dm<;?L&`*M{oDR$5bLf*mqc1IDo%;wR8VD8${w1f@UN)cOTF zxpgVUFv%lL*{P=6WzBv*s7SGKU^~hOAzLN%>%!{F;Jg7X#;7MZQU;YT?qVd1g%}6~ zSinPhlE#KA{m1E#)(_7gBfyb|aEMvwrves^aW3tQDZgbBBLr!hPPn2NNAKIPTRTQw zC1BN^s#vorkmeK4Z7s>=EWw44D#6U#bK z`IGS~*Kk{kw#RhLkU?Y7uJa_9uO21Q6!H##is6XiKvDX(X*i~PaK1kiCp<&O{Mft# zbLkk;huXE=s1dR<7DH_ox)GM%8*04Z>d2TlN(OmyIqZpfL3sm)_`!iZx&9ebUm%sqUvdGbS|H&y>;ychiOF1 z)KH3LDsAqV>fnf3db?VNa^*$2I|pttcN%oP>q-#UQ@`9G|K56wvi_8rroN%%F3-Yf za_pMB>{kteE=DfkUTQUA1Z^JF>_cfbnN4L@nVtOiEjGN-az`3(y9CwWdQ04txan)! z-{1Nc9(sGbX(K{ZCvre=BH|T+{L(u-Xp~N?EFn-!)>J+(1l`}R__8Mdt-31g20IK6 z0afJXK_^$uejE0M^o7-z)LeJ9m5YH7?oXP1CUewuw1w+YPFSt7?|2{>@#u>#ue_x22tL}Xu%TVu%H)V5 zRelYz9cbW(&@PC9oB!#g?s8@h8JbsUQ(OA(#vSmKx%lxKgN2f!7O15vc0S^q znms$r<4v9xhtBrII*1KcckaLrKxjpIl-t$QAWj&G+g9p0J9F=n!9fIM&F(UU5RKVk zEWHBxjo)Lylv2)L{~>_)GVS=fCCT4Nx#c|UaBzN+LB~Q8pbAWTR>QS_zyjb3&Giut z`VgUqVL6MdUErls&dC2`rFBiyQaVHYQ_IJ?g9G7SW3ya+1MX?1~3j*{%#9-O4 z#lcS$NJPGf0Qtv#VIUWA;{vd}k`ovz>}Me(M}XlgAQ5>8{}mW^0lCO;m0G}X8pvX9 zjX*$$pgzF-4{%`kMg^EZr1&3M&i5o*jmjSzxn=zc5I+e@65$J0xy|^ua3m<$?vsB6m z-EcV+wSA1V*^A3OK6gixzUqNS=hQB>U=1&jCT^r1&b}J3)HyOVWHpYPBA&_iBRJIg z!yWlq655@^iB1JT@aI}-KUdo6)OEQ>?Q*!pBxHSHBw*%~TcQ_A!uc3Gtg}k_+g+4? zDVMZ+Ui`$ApoMe1>1-RyKp7Nd$4j%GCy+ztZ}=5zksxeo}1`w>-SC=SfZY z+S3`fXHHn&I%@6sDxKb+H$%ZaKE}smrw?()wvNY&p`C|JZ<*E&S!KEELr$DcaTiz^ z4Ny2LfmEPQnrFUp2;J^mY+Ezw(b($GyB8ivZCVTuT`en1l~=1O6A4LquKjeV@CMy)|SULaORw*Q$I<+5AT-t z(a?(8?Lo_mSATyuDdqeB2PDc~pXDWOm@TL4E^^rATzsmw_Vd znU2%zqY{TtA@kf4_fDP?kd~;7hqi=9H;>vtPH>#!5L46`MSRC_LA3!@aVedl&h0uw zPGICJ(H3Ga1f z{qbx-CcQ35SykHp4SGnMnP=@hSHLOMY>au{-M!5WTIX187*b3l46!m{)re)#!W%VL z2W|+ONvqD5lK<`_5-TS7xSnQQw$$a3at%IY-t3!jO2)xa%)4MW2s(@Ou{{)hUUM@m z66|K~E)^HPxVV_5Cl>O?zA%$JGpN$0$`Cj(it4sysQPJDHdcvmZ%x9M3|qZn%)JkK zs^Xk>SJj3lu9DD(SbFrnDEb+mc7lAz@yYsDGV$>sGSGl(q}kt%Wm zJt^01$eMi!g#P}azw~29`1myp*F^xwC|iSI)Kq-V7qjipKf7*OQ_6H$624UgMFCL7 z%p+l}#cT>*D}MB}$yg!W~{kOOkV+(MTWMMl|ySaMucF9!?%Wqz6Fb^n%_vB30y~U}0s$2tLb;XUgnu~?n!|~8jCPM)AARj}qqnxB ze@siueI!ta1cN;FtAM^`ylBs@?D0d`ZwZ!{1|FC5VKW0tc?S6WOK|h6G+X(Bnodm? z`E#u4Sg$G61xa>Tvn1$H`LEFOBLKq^@+`!*B|lSgIoYg3c_P~UaNg%7dLj1%g!=?q zjp}y(_;q+D9z^e_YFL{gr*Dk9|oP!JFxUghh2Sp`YAZ;KZdYx66MgN1v|J)brYeB4qs>!QyJC6cmvA^s8}Ua zVW$N8C2h#_Jt0`EswcIJ>{ZiKpc5YhcU8lRD7d62yQH@p%PKLFJbBJ`WEXM;MO zaxVNT{RbyHc|<@u_B4tuhgD+QPDBpn265WN#obNu-1Q2F3;u~qDNqHF`swY_ncWAw zE5kRcp*)y~zfIxfd&2j0pGSqYx&KK@?3sICN%PIoVcT70mgCI2E&M0YFFg^}tl2DY zq+t83Y|pXxtW){)Ute*4-zPUF(R|PNkcH}>ZH=jOrAnun`^9R31#CII)>VT1hub(DUGds#c! z*dOv7(~vysbR3paLV6XM+2KT|He9+6d6Xm$65l6s$RiT!D~PS^FE9KcW=u~_UX;Jr zaDkYysUpYQS#Il}K;LpbSke@X%74r9MHN_?h?{bZMd@eV+b;{F*sPvFAMca74IXrI zglI(p@(zDh4C=E<#11009biDTC*5j{t&owW=Rj2qga+;FkU7Dk8u6tKM>-pg`mFW& zlfLWLRal+SQ#KgqAA+$;%=xSE$sBJbNk^mf-G%2iM9^=;T6cGjDJ`8#tNqJTrWWIP zalL7;M3#hPmozg75x~{Itpe~=d*D?5YB~Ahe>Jdv(4W-L5aQ1ECjkTMO@CpSK<%UH zua?32cgFSPuNG*ke1XZG3PG7QsELsCZ-`xsG$ZAA zG#XvqP{M+8V^_uGYKlby;}I#8nFS07Ln4^Q5ygXPFu`{le?F?HK@ z-7=7a@@>4!O9 zJk8JOvEVwHZ~2`IV|vFW=5xwp939?mSC>8~o~cBaPY|;wm>Ly~xyyw~)AXKoky6Fj?EMOYm;JNan7{HArDhND@?%aWOT zwqt#uHjy52l)h?yV4b~y&Sx#=ghy?HsQmsEKjo(*GJHu`&BtiJzx^-^^_lXMkciFu z!N?=oW?pUZL3Y@#vTO?ejpfI3d2OBv0Ot2CL8Qm^Q}$b8$0j1xUJA5T@N=laTMcLF zn^K_b1NaqX$spm4)aIA-hNwB9u*v;G-1+8#wcvfyp>IK1aPOcUjo4&BK1}Cy{Kp57 z=R`Sb!+R{>6A6A9avU&9@da(PK1*e@)dG|;`w9?Mqo^VZvMKI68fn8UN zZoA}eA_$7~_V6$_u?NW-?1k?eHyeSR)OwG?J=1}%24cwnWbOZk^HB44n?egjc&eQK z(S`n%MYW~)c>t#)11uIO4g4G3+JD~mux$eR9e{@;wEqYoJ=h%~3BdQE|1X07vrzK= zgIe&q+W=lZyr$ZyD^e`zzCShEDN}yI5*fPLS;k#1Y`#qU6&ze`<7o(2umabePydw+ zzIwHEt(G`R$wrL45;7cnFuS#^o9WrjyU?!2d4`g$pl72aZoDCK! zqdLWmHkG6wSuK$P?V#(M~FXnxHR%1?&SriXCWO^=QS8$iAqLe<0*n6i1=J*Jc zKx;8qY>6qIBei?q6N5xeG5*rnO0m8!P83A0ebMw<_tTm`T#TbHF16JU9x99 zRy=rA=?v+y`$moDXuf@N6=vm#W9@L8*-b0>s?kM~w1*3N+a{EST7zi7&u-_(o{Efn zUllhES9=oL86<1lJyXwz_D(ZPT@R8ud_1$xhToD-#1xTiks{H!blQRobt@Ks4{59( zb~vl=t2jV2{SGjV+i+CDef#c-k?kk1J)fx9%wDOiTEW@YiQ}*yw{!XftQd9RoAc-5 z>fXk+j%+yBPEO<#ELWxNiz&IfRUZ~$#;vVLDbD}8yjf_vrZzxBkScBS3uNVY4PS}CUU zOZWpaBBc{9V(X0*BWn%VG-QT`759|Uil?$rZo``nPM7zd!OwpmvTXpqW44E=*T{IK zD)8S?K8A6s1Ss|bXdY;@{m!(oWBc8E*9M$$(n4sn+-RU{J$o|q(`Tq0|1zuLv*g%M z#g$zju@(aOlXxi)7q$GA*VAhrYuJj{ajxd5d@CoPQ79kYPBlY0gGiSxx?!=s3!13? z!wp>Lo|NfV5V{OXG2fJANFO|u3IloHk4};~G5(kLSEp+1*t#dO8296OXh|akT=mlx zo3bVAj*7KZeD+kRqi=UZoULXqB|CT974LAYzS0UzvsBg8xVv1%_uc@1UAhS%zWeAV zmYgd*D&a@&RoprRRM8TzbI@-o5=^hmzExIc?Bo(VZs7MOxG#&LFz>!llo)9^Q z7b-56WXDx0D5j?bKGUFWiQpB5Jk&D9isG`UK8U)&9gI8y?_%Mgq14;AInhJdSQ$b- zbEMU51UAIbZ=}+n;a~Lp+5qIVEUxH<12QC*T$|7$Ja`F!o+9D+d0+FQ(ZGQ-q#eIC zGdp3=cC0u~7-KP9NR}JzG~6D_&V_%=BzU-pIDgA?DEGA6R%r>YAZ+=06)^1YXzYsk zdn0#M2A_?5-dyY2$OVztJk(if#nqJUI?+%*6dHIB)WQE9uJ*40%|DCRf1!T=^Ky@kW%mIC|DWQt{~WabuQ!wa zVcLDp6gh`iqUyoE{=khT#`Be+rJ9*(djEoi!kLPzz~!Z|HgDtC$`?sTm@2{h4G7Li z9`w?;n#-ga9d7k`jiKwwG~buf&RpAzWdzIe%`I)_dlfsAg7>Wes9%)A-@?RV(g)#~DThC{|? zI+#YrGFtCx4oA5l^m{$tQbomMw5EuSQJ)Nv2Iq@@?s&(KR-h_dCH?a3vBbmX{jCV= zDg~VURrmOr6`5$3M=UoS1HOO%dO9brgirdLG5pS=U49HTRVP+Qf6*Fl@eX-y2k|O% zw?+Oop7F~~*Fv8U1&1Y)@bCuiqbf{p`&>{HIBRnDuXnczVPqsPOQ;XR5sO&VgQa&$(QjeFez|NYLzmpfJ$2C`9+qA zd|>)x7uaFFK~9*qxK`GuL#^VHX2iVSuj1+nrfEd#$;cOiwU;>TxID}{wp_|>A$`2! zgt_W+a1-daac$tzo*PW&`=Q_bq5HZ48dXDl+Vp~% zJ_POe-b{DrHdnT~7$_<5`yS0pUwN~2DSLaQovuZJnVz1MLvKWZ)Bl(57|wLw-8*jG X7tfQRAe077VY;qs3@g#T_2|Cm`MvLZ9IxZ3j%Ggh^11KpzV7on&+C$CZE3of^B^Y! z1H<04W+pZa42%>82Bre`J-{cq%TtxWKa7DkrbY~iF0r4$e^@*XEeshLkZ`V5H&)>P z95>Cb1Trx2yrchN#P~hG#=sE3d)CD8Vu&-TaRcr_4ki4AN&T~a?4RpJSG?~DdU!N^ zXk+5=_y^INuMWnG=N!%RAy#}Y`^11|-}}=SBxZy;8t2M%5>jto#^C$6$tU#}cLnyV z_nLnd`a7m(9szqk`lgdU zn-JJEOwcP2_Z>R5r2K%-h+X=v6p~9ijxRbD8_#EyU|>cBJ~NS$g8-k&@Cl%RNBG$j zo!F)0*yGw%;`oeMj6lM`BTUl&#b!^JK(J1S;ivYXgm3Amx|7iH+4;RI7mug}9Eihi z5aa!nGWpWw?su4KH8xmV1{>F*8nWer4?#}5KWFb^oA|Y0q&2j6VrBt!fA9fT=-v|< zP05Teom>;QG%`?~$!7!XKz1i?Cyho}TdJyNd>OKyQBgho-YLT(mmtVeo`95?pVI*|4`^D1Q7Z)pv#la0?J^XUbAldbGcRRUe=;2A<`_}xqyqyXu z9Wc?#t_M%z0_sIyVDQsw)$T5yrD|W=OF98T<=j5FdNkms%^`H6!tAQj^vCH1nbb zed+(cAQo8o^LqS>AMU8igV>JalOnXagw>kPLhz%0VH|4wy01bB>Pgt3BV6rk)8ShE zYVZZJW&@Vr{BRfTX}f&X`!@NnON$(n4dStShZC&i?z$ga2D=@|(Hyipa%aUqF%AhS zSgZX4n@6AWmRNhav1ov5YjXTM?MvO*VyR+kxjM|pIY7L61a38XpnBE0$ohRljIfCpP7KX1d${q! z;6}NA_yqxJIm*Pb{#x}!_1*1T=rtf_%z!WV$DiQ#mXJ;f)Tv4OJzsjAl*92 znl400v#=9=kYd|blR2w=Wyrkm({v!c@_Nl8J1JO=KV9-ff&E<5Cg<|19foZ0^8}XM| zPM7Do`|!j?7Q(wD0rfZLPDbZe*X3|-P~DEgp(*Y}x$^QW+w1NON9r}-JKc|$LHibT zTs(3t?dYc>H}gkk3B9$5_eJzA1lBUTp+eGqz#xC@O|)ZWIj`h8`JHPQ4ZPfc8y>pV zl}?j{d#Gr+Y=2Q##fZqR z(ZO*-MZD1wz@O|YJ_Pdy{fuzUqLI3nP;ACfCvLxK@R$~e5+v6h3T}?CcVeVXB zJ!8H=y?Sla*mAp^n$Fm`ja1x9JR49Wf>b)pE7ZF3IM>;k{`H`fv-y3L0P3k4u!6Ax zVWCfQXKn`shpL^OIY&%tr=PtKKPTdWL-%250!=?_N(YTk-_%nm$r1((OCdN*IDH(^ zhVbJ4f7mIH5iw~ZK3lhRvCQ5v;pf@BZm)TlRl5y~p!XmAF8M(TFqo9e3P$opSY?L&>hHUwM_Y)l zrHnPd8iw>Cd!9$iB7>!jnAp=|=?@K8P<1>X`>~r5#S>B)jK&{33P0nM?mtA~d5%rA zu|_1@B&1FYgtJ{FnkXvtpH?*K2uf^Z{tNO{Fk||upv-uE+c2Y$_65!lK^Hjr1Drs! zQAv3UtP}ML@$7Xf$Y2AsWkw7zj!~Rr7FPQ)NdBB*oys|BBgA>4xIKiatgOsY&=f8q zPNu}#mRR=n>}fAb@eB_SKOqBcVAuXTIC$x4WEFq2zWvt}CnqOy@(DkPCe^MTw*6%5GS~4WOJ-63Dti-B7;*)a0*|r(Xm}VKfE#}6PB2spxXZKfd25N|B zgatdFTKzzKG)xyvOP}fvjf6lTXKdG`jgb3f?rcul^&3a309%m}~SC`-a*c zw~-klr`gr6*4bn@reGl~6}ZWIQvH$3wQaLp#cT6d9~C98nI{fOiq7F!d={%nRxc!R z)r7sd+QGgj-gHx`RIeBMCJGRv@!yMkM4Qp(dqqH@4@Ys&o-$WWgaxsw+qbj2@ZLg| zjJWA_v9IpvWblbZL0mQMbKD3|rnf^1hIF7tU#icq`v7)e;Dx$|hG`%TxYm!rwpFKLeH>j%_KE)Xd^6&+Ti#|EPin@ zzc+{Rvm8W+I={Zm_>Aa{lN^DAzY#qB{K}g^826DxYhql-wpkUmfhP7SI!K4M&QIli ze;eTZT*UZ*zt}TO(9HueWGv}sm{_U5a~!1E=H>=bbbo$q@A@*H@kb?&MU3E$E1<6f zL(wos>6TR-NZy2J)2>@y?~^*jb1s3D%09_2sMV@7AT6pShPo>(e)|1Hg45xX64R}E z-}I7&5UlubU1VB?3|rGkGS2r7AvtY8KV=>};d2vq^b;Zarc+bn>@HI<`3u7LsPVqw zTLH!XcoEahHQ_78o+X0B(gK_k{I^b=45uoLwCeTOy{+R^GP*hpf0@;^9v0+9t_uIY zG4r-6DNjV8yLrxrJnSBPS$}y7wwqS?n8~60^YwkGH3#GlaAWr5g1tX$CA! zMvLi15N!LNnWppwz_<6DXLU`-bV^E|vHji!1**hs^U}AgGuyslC|dP{?#lOI(?IWRZLq(6obRYWN>1~0X?xOr! z^y}AN^*Cy4aUAu%YowNuusxGKgI83RW~cv5jo!K>;gcDmA2{2`& zqW{iNX>Cm?l*6mCvWqwzt}ye(sc?$V&}~g9_1AMU;pPFIV$Gnbi{kssV1vh;keHjl ze)Nn-S}|q105`~|pjeDE)6Qn{oh7DbJ$RZ`O=2e2xa;t@>75T~YpSd~{^+3kuBT2D zYmY5~mh4_!{-@zS0bd0ooM<2AZzVC~Qbr*S6?I|*B>RuJAS~2SAIBeuxu>oZFbp?! zU5o3Ky6Mag=Wm4~cU=Y7vD@b#Vl0R>Ru>dTb8=ka59?t*m;9Ez9K?X)E>ZNpJE1OR zs%nXNd6pP?5MRsFTn-V3pu8&aHkiFc7}%y^PsQHm(d4RdJh;{q4^4=ri%}j>SH~|929~Dk>dX2ASS+m4nI!DA;!y&)+%~Tfn3ZRDAQk5vhR)U|~!B?hvo-9W_5U+s7J^e1iOCtrO%CVBNYQDFLa6X`c&|TinLV==?`m@9qiks7*Z#uTF zT#?wsboX5(W3xDl$4vKwP`&*2r7yZ)nIgG5cW)|ZBour?BfM;x;aOruj9i9OIyTyg z?JehLRX?=lL)p(?drhwdG$SLs97>tAyAaHVkJ?g9wTD$PBpIT*E$!+q+o~`_8c%8D zu3jVm3M`O(!`|bv<6GP=fbR5`Yp=WNo&gCPOYNG6G;mBGIapqrZSZ&%Mlh93#iFHl z9L(*|WN{nVQjq-hQun)UDz}Yx7WShparyqBQ2+4d#%YSF>fLCpU;XrxsWeR2UxJM( z>}bmbvCX~A&ceE^53Ecr+_?PmrKOI)>&G;d#!JAjD+xBjcRh9oD^2bk&Y<>5j+mv` zD%|GVIS)r~AkR-FDe&(&(aQbWMUl&0CHL1j(Wx>!uk*Je<@{y6iyPo$eI?f1%5snk zoC4uT{|0t5WTUbBFij{)`t+_>xJq0W)s{FEjQ%XUs}E%eL!C?5c=q~NgvhO3HlWkS zBehf`SvN0JH@$%sGEUqz`z$Quj1FM}q1~;x+j$?gLNX64t$)y#D98M8o(C!H>aGws z@}jz#x(0amFLF^2Q2$z^>^}0AbeHUWxK220oQtG2nC|(Frd)}`astV+@>(!P!$ecW!+{mS^tzB4p!eyNT zzg}v!U=fnMgU6e0A`A4O^)sfqr-H8c*2BSsQ$!){OaNHFN|DeFb1Rz|tP3nGg?{bt zH??JJ6`shCm`B;Vlju&{ZqK9`M8$$bMQ zR{eJ4sVf+Dm(utYzh&e94QE#@Vq`kpy0S2mJ%7)GC;% zBnCiuLHVQ-5ok+tRbsE^Muf(MkQ)N4wrs+_9XfQFDh0QY!v-kweFclFRZxv<2e$Uj z*bzBPBK6r3Ls)8MOh>PaOV;620wh-k?OuczXz8cJN8fLb;ICJfeY5K*k6X8HOPvWl zw9pw5ly39t)}kXxuqguyjKCo}-xB`$zQp=vJw(~!pSH;2wi)=|WiV59D1B%+7`M15 zpjq^_`IhCK-w@U2LaNp}vSl%f+~7hw?XejFJa0)()DQoC2XzdWLz=DMuiRJOHh}Npt?dfN2k^A@NpwFGhBA%9cqJ^u`IGD<+@6cQtt_+IJGCIJRJ{cQ%OGvC%YE- z6ref#W9EGbRH%I#^T;e3T=Q-bZAOyBgb)MNYxT1y7Y%AvMuvm8j7@;XzRD$r7?78? zLB@V2J}BbLG^`j5|iV%CZPuDTcJ;bJc*b_EiP0^u)Qo`@JVYz%wes59Nh z?y+qMYe%uv@a??;ur255Q`@Tv0BJjge|BA_0w|-jT^DvLRwyai80Ic~h#z2P062}X z7UN#+#Ci0W-QFJLT|-+wwZJX@sUKq^PORPHx(dv8s#eKqXV8zv#8Om*q98OGzXY3X z$s-_6&6_9(4TV}}oS{dZz*?aAj9Z44By3z(=H@q%Q144e;R!lru^9lw(=p2`X&+k= z&wp&jvMwc~H+p+jf$bAYSeqlur`xdoYY*lQY0+rY68|@Yf&Zm35{Y+LOX0XK;x7Az3nWrkZnL3Vc`s;`A=2G0z7T`wS$$vwRRyU?Z=sOBx% z<~7uLT(Kv*7^0-AeGlVr!{!{8U2-$=foqN#Qdep2FcDJ0J=xfyTPYAe^@)uVJw5!J zpqloUL2@BQHr+yk9E3A|Av+$srfGCxEDh&T)V}X7S)jV}SlP`r>H!^fpKipyMTpoE z+ZP>;wv_W{hYO59SL<7>t*?F`bZeg9%}XV3GCj8yKqX$UE;eQq^M~7NPk5B5sRVgk zHw-^V6zdLh(~SdLgdiO%1~q}dcvSsiJchXS<;dKu-t~D$!(C)l$HWd9wU@umGA+dW zN*c40qNx(tVDjb0Fws;t}#~hGitgiG{YOC#HIh zIpVlmH#6k`SP@y+Ju6AAvxxC`?x6FNgg`g=J{mg6c&f9>Xld*AI0SijtZhA3vgrbJ zQM>%!WW8bNid4o?nM2ByD`Q1bURwLzSO?@zRXrO|A=y+j;8!VimO25agi6<3u`eT| z)KTKFnbj3Wk=TkV5-&|RV$pCl^oQsUzOV55SLBkxHr{{hqzk7e+xk7zkzM9-I!WB7 zd1g75TzenQbCdk2-bjVjY$!79v+0g=`yJ`jOnl>F(Rf? zQRUhf&Wq#aAZjc-O~(MZ?W_ynZUB*|A7*Hh3kJORrP=zm9xl8TC1)n=dDteEA5Gd^Xo@rrmo*y+iSpj}HTs=sn&ZM=~} zwK7%4zqep8TYIhXERu}TxbH5M)MUDS%7S$O7U`vFP4me|qE?nnW>ZP*J5F1?_WDK# zp~(hzWaNwkll9b{*@(Zi&O8x0w>=PrCH2&JWaKQSF!KbMV{W8rshv8TUK$w>GTZ4! zG-`0(r6;MlUt8wJ{!R67} zmJ%u-bVIT4w7_;`sr~s7qtw#7{(>@5w!hPW z5t3cPLQiNMP?R==WQcm4KYCA{;pc^qVs|DKnlrG_f}u=6!F~2fz=u=2%NK*_Pr@y& ztn@JImwKZQCJ0|C~u1KhYX#^MVQ1PPEoAh zk@WlS=PDG5no6#y?#slMCS(QP;5rK5HWQ19hYxn=YT3FVaj970oN1#YNn0ub1LK8a z=MILam9LGgBeSbzichSCl{HA8L#eRPLeL+j6kCfc@C{MOy;Y$|hllQWp@sJc5H6~Q zIl998*(1QcGqi&f%~K&hQ^-oY4QamD3$dMM^}^2=NA;dZ?Q)D19=fz*^t1uX!w-@$OxyQC zAfU>Rop>x&7R|H#cXTH|%KW;{*I4Ll=zb-TL~OjDL<1JdJ+Ky9F)dq4W~zF|L{p*c zk`odx63ejhb0!~xq}%E6i^2mC_5vso(;`P)tXXCMeB@G=$O8M0q1hRknGV;K$00e6>tA z0OM{tGpv)^47j`(^}jlhTmdd;kmuJje7|(Bt0eb!GnZ80mls>tJ)`H3e^{12OraIX z?D46Y#@Efa7I<5drER8#1;Xd64z<)q0tkB^@Oh0=OCEx#j0h@4DeixE(iiN{Ai56% zsw9}>D1wf^4QBjMQ&^iMr*meh2g*$tO%nXsRZp4mkwj+8Z_~*Owab|di;e|DDSv|P z&PVqAfs$n%(Rr9cmn>4vw5($}8+ctOtSe7jfwyiaD+wBN zbU+WFRSwB@r2LWbq;!)W;w!`^vIJ1GmAKa=ufmJAF&1u^ZzL@Fsy|Inzku$a40H71 znXg{Gx*ix9c%la0)`WE|I0*=>0|Ks&%|>cP_Ggf>sS(y+89;^XjDU`(XuA+~UqT+D zeb>)fLX6%DbB@x`(h7>Dc&--2bwqdAV_F*t5${5*P8T9=JNGeK``AdMoU53^HW> zcfiO@0yQhiIlYd0_fG-qf1V?80i-r$LIK_Gk{PiYf6}>AXEJ zzO z7nG^}&Kut35+#E{abO zpkpWe3Tn;hV$bcAHJ0m}zDhD0j`e@gwAMEZzdCSX(>2V86D{abn%b>Y!mKXDbHOuF zneNtL`yBI(r{-V_Z580hv7x<&|LRmkdE#KBT#A#Hxf z2$L&sFWprTU4PH9!FtSchCiS&1c^@$uxeT!>KVNDC!^qrF;doZ)9ENHF0t1+S7Afn zF@@nng;oyTIJy}ySz2v9pF27c*^KmSEGIy#S zYcx=R+;?!(`Jigmz5uFVe7z^L0ze|~Sp~@4gP$xc|2%5-1~{?os}eB}!sF_J5&b>u zyf>P7=qsgFH4*m02>@zzw{hKNhJPxo2ZRwO@O=@GW{m52qTFvw0yym$KyL(AIngQM zGBC$J1VCob#n-b3If1gU@m|mVJjG;3Y+i_VFzyXXbOH%KeNgSVIWmXYnz&@cn{}6^ z|7gI+^xNj&^ach%fg}tdn0*DzX9{*3K%T5}zJbCp+vY(tag*nO9tp^3O5nQu`Y(VC zSn@{(A&@-oJPFvZ4Fq%hr8?x8bYb{X{+KB0e9WuKu-zu(N1$mxmRCTP)@y>D?mJyv8 zHTYf*L|9O-RZ;z$Q_}u`JNIdB&5@wci1EnDj}NIyL%08<8Otl|T0Z*X``-)W%Hvteb{ z%6vx$0@2*H%2ob3PFcc3o9^VdDJ;JG5OJ_xMU_W@&H?@YtdiC4d&Bqq$?K@;g{Kec zu#$~WTko~#5>ke$TwPu&Mc?eE=}Sia6KK#5l42ItF4}8&6}i}Xq6Ky%__B+@lu~`r zWJI=l|NM=sVEuGOlY$?{PP0jEdag+0Np^N@(vVB5aXa5B15{&74KymAc0xS-Id4xTEvy#s(qbZlJyWk~^T_4(<9^-5yB9+w;?+6Bq{Kip@7 znl4AN^vP32kwf!r(|U{hy1tQ1CPwkF-p?ov|lU>wZz+Q=ey2MDe=|=&9 z8U)RgX#4asLPE(7#G0%0xAr}-?MJ69Hu`{Go#d=$NK^glqrNF1XF%+ScEHl#K|iX4 zfcCAAhO+91Src_1V;Lu<4dZ6tg-|Zsmr%OM#d4S8sK)He1s?pHzK!0U+fpEf?n%EA z|IE8hPq6G`$&={?P}o3#lZ_p>u6?EKruQpQsudszEuFB#0X9{+?M6>(8#!fR4?(1_ z0)1XY0f@)-Qwm`DltW2QH8?@zod;1joMxE?X!qL?IB=45Pv$f|uj5()kZAlNOMLmxe(l|AU9x?$5KAsU^8*v!fqnAR76V{Yj{ z=NA;zEReB-<@IAl^e0c$4QnI`X|GqOz`vtDXELPLud|x*OBd67+>AZtO*S5?_tQ+$ zK>B|KL?@9fhODxP=j*s@FRY%3T-{^=q>!o%w0-%@hJ34sgM}5yDli9mFh|s9@j5~N zE$LR`9C5dj7`$@0;h8eyoCZYa+)UDwElvcg2&&P4cUH;=XBiF9RCR%bGq$v{@^W}F zPt5P68&AkH%K!m}fd0)D`a`Hw-=cjdB7pni>Hxs7zWF%c0lCX;0UFt24ZouSD8so1 zFd0|pD&;g7rGkfiZPmuNB6`(}MBr9oL;S<&;Tzq6Nd$zp-w6w*t0ltC)@$UE2K!e~ z>MiJLo`*1>#Rpe^EK|a>VBVq_IgZDhd-^BrBtJ{eGZgHhe8~ zCLJAF!@gD)x<5Sa9r>I4QncQT`(IH*rv7U5pr!IA^7upWQ$`w-JLuThip**q|j(R&@zq&A} zLeu?GL-WCaw6klB-e6k6D?#qiQVq_wucmKUe46X z&%LtN>CnHq5Z3k(WaZo2KN@~1YA6|fzNIz_5UeO6xkI^EgZ63T zTbc%-OKT8ln}qgze(m;UeD~VY*pDOH{ho#wKy0-j%*crJ3c|#UK{~)5f^ZlMd-zRH zHK5Aj%6)*QKK+NLF3v7VsOyHfG}eM8=N5s^3baq!1`Oq`nVWF>g0-^1n%C<62GALLcN97AzL z29^nHkFE#eGJ7d&bqu8*wxeRM4wR8Q;3}w=)?WbnM1nBQEn_blx%dSP62vmorUf&y z9tvqy1T4O#Je7|WZ>tVBd|G}sJ2eo2Wvq@4y^92yiqeQ7si%ZX{Ue)mS|CTiZqRQ2 z1fUK&Nqlr;5ViVBm!9zZ?rETI(8===1ws;XmaWwsJkZNY_FoZ7Z*;Di&4;hrqf}H~ zC79nz$kupg9V)F>0oAw%tV3%BH-UG`1{kx~r})sHj>KO-9rfWmC*oHAIoj!K^5<9$ zMzLhGvtMJYGX{}Pw4V&^{xn6##eEDYizsy%nU_d4yWTiBd-0}&$d_Mxp56N0C#^&F zcaO0FjpAm33a{HDsTv%h0y?Om-*xGqSqGfR=yMHxlNnE+B;BO!|koA zD5n}Qy`V(hZ0x+{;JU@9*JaJ;U%&eKSF7`ct5{7p^tG$Q;9;sQEV;_%@pmc_Ni&dK zQWI*KaZfZ)pH8LjmDexD9Zojv!PM(13WQ4`jjbtu5qy$#^DkDkRx;v5aQwPrXq)Yr z<&ux{Xl7f36p-wbhB*5q*lH&F?~$JkEAW-|OE8btx2G)zzkj~epMRoXnmnDNZ@&bj zaGL#kg|LcklOQGyf#Ub}g#NVky&T8T>3Ri7#{$%u)Qp?g0S&;F24vLUAwS(a&2*W_ zK)b#)w1gw`hZE&Yi6VG-a|Z2()qP;HCu?$F42O0;ihN|xozm1#7L%j2(Dj*Pm4H6u zRr6ZTf^<0vauAL$;%P8fv*HgdAT@>gg)Y zgRVZ_OZ{E%gCfq(B;D|PIUGbQ6sGQ`$r?VOr$uISr(uj6IVpG{SXxVf+Fi}9ix}LeLr=(++8w1`aMlBOWmO0s7 z5A8_(zC@R6oLhAE#Oa>#{i1T-2JF=4i^;eJBUL+Q^$HpD0+6VW?462bPW(SR8;4E- zG2V4xT7tl|UQU0J0;Wadi#~Bj<~*?kmTt|t^Yw33uPW{sPqX5q?xA$MOk|ljAW8;+ ztSZ(U5PH<2dI2a$sulqnt<$9hiXuQq7C>^9ebArmExwqO zsPj&V4%`n09X(U%3PMmpfFt+4v(sUcX;@)7`gdyM*>e4_27&ac6NlfEn`Drx2K1?d zeyDi^m}ggZmBQgqWIY*V_Z4Dl{Y>F2kuY;xqSVq$Urj}1vZ0y_= zpC*aM&GRh}3|w9FatD4+7=7Hg#dDu09%q!Pw)^HhiC=F|G5z&TDYD^0#UfA@LKf1N zKoeVZ_Xlh-->A0X1J2;^)tce8OPbV`L)xm4s8HG=9Wy7pAC(i^TzR(EcFRmir zc&obO#AAxc#U#BqzG|Ys+4D8kZ2b6Eb~_O73UdEWCusr`HMa?J-~wXZ#bE_+O}zU& z@lNFN&~9-2Q>*BazaC@xYh`63zqG(gEkRbX`o=5z(){berZWZk%O!tBs-8Z=mIxjF zNJ|=8Kb2|J)8A6gJuryo6&fqK{c2EF5OpPXHp-xj9X7&xZUA_U-1^# z#L7PD{^b3%^DeN)u#jm{Ai(fGA4LzQ;^M(s>3hHIVeN{xETw6% z8Pr%i>j1Qz7ZSFD(|<7oJv_S+Vq0~{;9m?_(ok7+w)71ppOzJWyAgnNEiOVhD5wI} zF#vTovi!b#N`87Xz477QY8Ep(wZ;i>fykUge8@EgammU>PF*{q$h$dEzjB7B>5N6T zS)8NlRpRJ-;bsS2dg}b(wLe2%9pwt*PZ~PXYC3!QBYTIqxeP>Sp)f>Sf{EVL_)k-3 z;iz?4%3Mb3V*70zGj52Ue=%2W&fRh^YyfveE<2koC8dy4r8`6+$i&N#D=Fk z^4^hLjE>%GLSF>HetNj;7svJ5=|wb9Cxe|u$?LhlIY}rF40yRG0Ka#@U^4X-V$|PQ zv_ryb;Yr^YVcmIdfSO>Wi~9>TR&vjHWbwJ3m@V=)b{!|txSeVz>Um}w-zuV(#Y^6Yf-b=-w&osHI&rz!=_4m zIv_y41@HFK|Kjbq_cm-an(fRh8dhoO^s$$Ygr;VK?(Bg##CMdlDL3|mMg|APEI5#x zgy`m0uN#JHyiZ)q&>LZYJmkV}6~L;Ka%H@J$clFf7puWQ2_e2tPXexx8z;ztGj)Xg zo(w+P_HO(TzI@q2vc~ww55lGL#e0V%4L=G~x zz2`luHm?_!m0Xk{=e{63_uG*wJA#~{grq#0)$;Azm^>v2yKc}Mz-0e48JBB;c1JW) z=W-S~Pg_Qex~%oFUU55ha#9ldtF?YZczsx3^C-Lm{~50S3rF#O3NZPd%gZ!BT3;A! zeXM+=^BT++8yGX{U((g5idg+y7jF8opcd9xKfRCla`0*o{(Tg-F3;PzeBNzOehsNS zvKjFuGq-*?`1e8SQ6*mbQ6!0`H9NUWkm~F@@Y$yki%CN;^WnG++9~(@N)X4=k>C+1 zIcxlp5&QY=4O=&q*5F7zR#+@e(l}4 zPn~(M&!j#{5WhLH;?3-)%~UkZGRGNdgJgd1G`@M#=_Q^|3#_dz-1Lhj?_*c|z!uT& z-l7`!u&po4n@&9v0O;JU6SRg-N*!J2;}j?EJKI2E^TDTRJ*5EjFnQTB@6@H{1M{sK z@}+uhQ&ZQ6;3N(5n`k81FXr~IJv;Rn!LAQET%R#V^H;9`xvMtc1F}u^-y*nnroN~B{5Ozex8)dJns?+6#&cvNKx>)@J^Q1u&m;9w>QXx+s55Ty7CLQ zx-kH(bP?hJcIT+RgWO-W0`+0S{Ss)`qXGHe*N}v)q5+1KdpINg?guWxUqVMk4>pa> z(Gp?(3mUcntX8F5aVwzt-_q3^7hU*p2Wn9gqm}e~c?T``)e+p%ex2->Hw4WBwTvxI zKXRN~*gIs%OmB~6@VAKEt#KXR&uB@DNxz!;8g#^%DCYOh`4zLOOAPc^=fI+eVSzFz zGT#8JMQEjr$p)|Tsw1%A3>64%6-$T*H;EGj3HhCF5^L-z6&*h>d;^rZGct<4MeI2a z_RXZqHGFX-G#sD`G&g|v#%*Yi1kDfO`8b;Xc|>IAndUHIBiJw6KpBkei{ z$;-6KTF;=9kSJ6W7i)0m)dO5VMa zb8o%>5@f(fG{brpPkXfLxa2|xnny{mvBFK(9M$Qzy+=xgDRRsB^Bf`-Eg`FFDeua& z7T4`bbx3HWq|eBo>L!n#evoH@tPZuIk|4CU9$FpBVdK8+NcHv+?Uo0^pQI~|dp=Jt zAvrbAS*vT&yXqCacN$s%N++bCMnnRXcrcmii|AkalBfxVlt%k3{IuJESBZvG7&hL2X%gHjnn?gz>lQ)&!D@B5cCHkawT?Ef`C z$e?dE_ozdQx7sfamNIn%VHLCKC>YRS7q5tUX(HEQGSef^3-7pu7un5m>%C1yEg`7A zEkTo`aJVb38tQmq5huyk?yjrpY>r!OufTzN2z8?ikX+T4&Zod$>X}+}Yn+|PbJ^p* zyvnC189Cr9iuHK)nY8}Nh?qVK=f#-}&0!m=mfd|~Fr9(S6-NkQU4s*OY{Zx17(Mog z3p=ygB>bSCf|T-mOVlzm%o~@F?lD5T0{#;B(k6y<#DS>D$ow&IM0s5VGuGc33M;d) zBht4^WuSE-*4Il7V;+BY4zLGi91?Sf?A90=?q!w7regfrH_M^7 zpm6(UW}jQ-QoUq165^>mD(Dm-KHc!so%bTW+W$f)@p0{|hNImTpb7zL=LlyNhQ;(G zaRcXUQ`Z4X3FQ!LI}Xf%IL-!(0BSl&^la6TGB!-IclfXf!~wb*cya5(*{lMNoZZ{k zhXJa9^}vYP^K0v2A9aA5i?Z#@SDK+M@=M;*MYs;Nv}my8eNY*`W*t4$CofT3d1{>| z58w&*omm?o?aE=uOxlwXdtIrevw`BB&QM~?)v3N3$p=`pD_z8-@BsJozccV9Xd1Aq zYYVg#r)8TVIW&*UeeGMnzD}iC!5T78p;N#`)vv67DgNdRf`Llgx!;NT^Q()Z!fh!b z)NcJ?!_I-F(}CW#x!`#qw-caG-t-oPEuPF($e`E8l^~Y# z9wz2#$aI_r(*AGn1J|WATNEc18L3ru0HT!V4Cs!Tf3U(^WO{=5bc_A)Cw9=}LW2_j zz-P8vJ(SqkLR7FWh|e5{Z;hCmG1cO*)<4Yo44A4{XHc8)`a9* zRgzt2o`NOmeS=nj2=!4zJYT*0(Q5g*EYBtP#RG4CZhZZoIQ%{e^aGfuv8yl4z2qh; zX(&nLW|IYx@|QS{(BPDhXZ(uqzvDp+)7cy@2^cO8C}AFh{Sz1<9)JORlWQR8R9X3c znq@fx`>mZJqzZJ$OI`>?D#Y?b9Jmp@u1!B{4J?^P)wc6(Y%mtrrGf?@sMKKl0$?M^ zEom+wu(A6ikTOryL11m|uoj^1I?$4OodpW=pIdO*h!By2B{p!fl9fbh0$x6GXQYQSfNYTeSVTgMb_m zbxm!*P5TKC?y-R&2V~#@3cnHr<;VW9@}3`O)X*lM&8PsBL$sc#^QBkQKcV+vLWIVG z3N(NKhn)dAyCFeC+pv9^q&5x0?d~8NFKku^0=lR+ACPmxz(?!T$TvGDe(OE?>Wm-4 zVY5QiiC2+wnxFpl@_jLuL6d$*OuftMKb?hI($sC#`1`kd{Qv#7jsN|CW7jYDFp%8W zGJGtUf0Tlji>GZuFx^bJ8zL#snX0 z(^LP0^{CW6o(JlhemHi8Mh1M&>2>=;C*bqYzOauTVhYclI2Yuw!asD9jYkAp&SCOz z04Ok5*etQrEx$Y6Nx19KfTkael(NRqiIto*#7cf?rGg7cl6KrkI7O>101_z>60x6H zIRdeDcF!GArB`5?7p-Q-(}3RDQtdweb><{Z(2H)CI->fqdlr>6+2IsJ_k!5 zI5RRuJ#mDXqpplEa@oMSYZwJSak!*p6s^;-lb#Q*RY}<65v*O$vRz4351h+Jf|V_(7;!nD#xz`_Yc` z-7I0dCS5)W8?9fiDGbpIiUjLZL%K*hff?P*-R0uRVE241p+lAJsGfY*N^UVF*rvit zC-VM)Ms?`w3ah3@WFE`3!JN*5quT)rG7o2hT?AoPz^YRg*G1`s{jor#`#_WR^7uEo z@qOk*O&Swp_9R!DD(B%03-g>*3-_a+g4~YOylboKyg69=`wcq@vO=0!B6sHZ({vc` zwuW#`h9ZbnL+WPAorKghTW)2nhJMQQhbKRCu}17S&ApafagkTTn~waz*D*%sOZ5kI z#gg9j0YRzf6v#{V1$N$RnyI3jpQ>ds*MRk~aVG|XN|4~M46fZ!PSmSvBn$4HJ=XxR zV9{Xe;Jg1Gke|*kKi*uB903@gK|&Ossrp5Y&c4{xirv%_?p6MRUuyejDn6q;;Di27>v%S zT59p?0!EXk*}046{)W&_S%FqS0m3*)tqm^&WOP;%2K4<@k}a zBhn!vfI9xJy5dvuHC8d^+6Ku2SxWWyZ-*Eu&mv*EzRn;v%`^GIQSK9=bE-Nm?(!n; z-J&fz-Q{@{Ty-Zk@MBXhD8U2l99kjaTe24x(K0LQ^+6SO7vBOQ&bvYF%SW7nFz*-( z38FPV(SZ1@ySXJL`LfBne%JErh>Jb?{dmd)S13d@{X5*s&j!Pc+*kn@q^qr!YN<^3JVVZn+oKir>UAyxjNC|@5qp7gq9X1eyf7MMw@ zG;-Qy&^0;x00JS$teQQxFRy^i`?e)P(NPfLTrjnlM21(R%8={vo80+ER~z$m2D9Rq@SzLm8>y7SXd5i z5i03uAgTY3=Nyy?{-VZ=z5g@slycb-V_cr>!Yuh(_GvmGPfszpf%~Iil`|fMS|y&o zkU$L*{b#b?A!;TmMwn+wMfr1LV2LzNi6?fntvxA+EbFt6c+|pJYejnQEWvloQ!)Z= z43bWpOdfsq(OOw*0x)ap@=U9p(wym-H|8z{btj33WwA2)u484Qn$H4^J;n3NoT0&7 z#(bUs@(iC_YaR`M6fB@}*{1dfwD3SMmQ>E|yAEo4-~=FZB%QI`nSGH6jfC?W)qhcg zDviS*9?1fV`Fd$XP{Fk{rwSSg7~dy z#5>(;7nJL5-u6V;jpm&O?oq>ZUFVwdq&yTTUd?!0ko!CHI$tWeR~3M{apKRsE~AGK zo&bPF21Dz}d!g7dSYmheOl3Llakss3UIjc5L$F@*^Tt}AMqTEW8c%(Cxn<>N6ew(| zZu2SdGGUK7kZF;7%sJX74X1ThWyk4QK~V8acT{BpQj)V!te3z~Nnm{)Yb(|~Mo)(* zzH~=9CcEDO7k?Pw?4Z%*Dtde7wz+=;(6_a3;v`EoMW|4KVI?{Ekt#@>e-w?I7DOz^l|v6jLW zs`45TO{0kaRw=2Z1Zdav=n`0Xq9NU5yOJ|O1fP?W!}$3t`w*4L2`j((fg^g_{|a1k z71DAAzlouJy*A(Sh%&ZU>k2)9h5>&Dvo?z-adeUH;fPvCak~EAEKN0|A&O{)cv6&qVzov?Fw4;vE9>7HYZO8}g_+arIo3#Jqv zPa0nfmVNwG4#nD`Y4=Zic;z(V%w1d4AA14L1duU1zqo=l$p(^0^StJkhi0Yx4s=&J zdi7?y?jpT9sB>D7zIjb?7k)CoHSmm|hCi!L4UO<>sKlTCi6Z;}jW|S=-O73EYum(m z5QdPI;Z)!ORgV)kVvk`6-x}H7lV_0j@FuE9;3c;RBcQq)A9kK2?{!A4tp}%r=2n>M zqkEGDvbWo)3{6u8$zK`dp(Ah%tzmA|k-WEaGJ{*0IdYr_^Yqli+}Wu*r(M=ER|>q( z4_L`rV2%AxA`6GygiOFCh9Wz@Q)j9W-ld|f?C~k;59FuqP4d1v@{?^bUk~Ae*Fu1v zySnvv^&_Qd%2pzy?D@{=RMmb{DE#gt03N^B$3=tN(wY(L?7zyXs`(;^?%ob*+}OO} zxhzvZI2b7dfHlD`hm!g)t{F`|4)O;x&k(Osg)`989O+P!L{j&~KHB<@b5XukF5Ba0 z3N|LCaRh*^gN(Xvx$cdnh(3|yR1XFG4rPvm54^PljblQRmunDHoWbZ|phtSOS>%%_ zhL5L%Zv0k$PH?2XwcF+x{WJM+IFJM`7Yk{8Q139@)jwXnxw>hb`?H@eT7A^YQm8ru z<&ZyS5S3@D<|rxeMEvHPo+_J#mz{LwMO@U|*!z%zbTW!!9w26YdaZ-LuXlXT9qXv($MrQRSsI7GOZOjVq@AmoH_tcM zSqLkUUHoI!+5{Nq&w&&%D1OXfDr%xKvCl?(eR)7~UW&DhOMb)gtd&^yJs$ zJhr?*?m$rV#L2#TU+<}N*3SDAg%|QrG#g^!Tn`+i>m!rL6P>@+!9@qzb2>hHKV*4$ zejP0No2&0?)@rfb&7XRfwMMvy3(UE00>j)%3SL|P=lC|8RG>zz?8^m+m7Hr*#eCA@@KM0E z5I0{6@>L%eAVf%^Ce$>?`@3IK_}f7Kn=kCY+c6CJun6u4s}F`IN=c%SH%`$3O@kk< zC+Fb&MvH%$Yz3maNAvE7jxvLM3|2H;&fc2}A|a~ax=NfF$|h@SN$_f|-LL_! z6}vV=e=m&5Jv9dq;>08F`f+aLJDSv{@P>OCQBxp19pe$NQ1CWfe5nY@yjlV?UTN@~a*xO>EUz8`wae?R+5%A3l>3m6EqpsC z&wRyVQ&ZEg&S}Il$ilTjRC)D$VrXDyLEdmVdy;6p!u_}cB(Za!=qvNKhM=TIbltbR z<)B9a6x7NK@Ambx?t6US;G0R1lxYkoN55k3a| z5N)9|C}}{Q#pv&@NdKX{l~x9oUO?0d%68ZR^llXd-qu)MRb>as3?bV{{DmOIYe;#p zs5pP#1U9>$cWMY`&=M=ysNM*$sk2`TgWTWd;) zQ$}sIi$C3ai>)rHqP*PN+@|fVdse)$JzO+<_%R3$9f^eLYQN5zrD}TQ07chj75BDK z{#vAh8jVJKyi@-2kznj-Kxtm79O~`kiJnE{9$qzpL+iR;tF&EN|A(f~nj|dSN-&MJ zp^e}Kh*$eQ(k=Yv_TU2DBRdT8OF(`)WoK;UxL}=UkfXWseyUTTm~#R`H7U$Q!!d!% zY@XSnB9HT>u{Vn>)2u&>vaS`Z6ip;414or5245UM|KY=jrhwwiN+^$ZItb$n^vbTVs*u|$Q!iOrtvNR zQ3K=_e4PUEkZ^~us|zgKv&tj65ix{u{Oo{rfKQap%>Cx7J6-0FNm=ZSWAwLze;0JZ z%U0U!(^9`kmS4HQk>@_33q};O`M%xEKWw@IDm;Ef`?;GDik9{yJ7xb{b82rFKUuux zMdrGSv|!dp!p8aG)Al!GUZyPoQ7_~{nIq5%gL(Gaz`@(3vTrjP(O*>UN^SIJAJ&jvxSeoyu2uoKP;pF9nu`c7~^ur7HSIWExUe`R+S$LIYtTyZD#Arls1!WOecswU#J9br+?s;sTMu zlkn4#?|1NR-sa#K`&fiF-OReV*SW%~z%In$t`#IlQye_)?^a%ItqUfkjJSZ^i#H2f z_-i8_p^V;+24668dhl>Q_skV}W~N8i9!{*i=!srM!F@liZt|k(1LmbZreJ)1*_3;{ zSe~%aD@Jz1MBE3Je>73}v4dWBSvTuGoph3>ndLe3k^h{h9$vlzgSUEC$wy!50Vaz` zBM_;4VxKbOb|ml7tAMtf!X~5IzD2-m&VI2njh!OTR;Ke zTeKN+Fr%mZ#M1Q6LN#*HyB@*WNBN$Yb`f8QybQje5VIK-w>99Rc{uFUfZz6BWV9aU z^fZCba?);6%$!4;-gpi8uaLO3+*H4bnaA;dw|eUiI|Na@hoAhK;IvthCRu+hp|~@M zbOvAVLBurVCtP4{q!sNb)~drjRsyN{qr}#2N~&0KRc8LcGrW}L1Sd0<2~lO`Ln2)c ziz-a{K+W|XwrwYbesdoChyF$IrQ6}}(Zh1i_saWEl_+yDjKmWkuxc z`#rxI_Jz|hP<#0w^pnt_hHt^?2o}yWzv9uS>t_wAAj`^@{~~B{_#tS)gOrd_B>M}( zuOQ}XIHV@O0|oXk;f}G#2skA&2r`F3VU=(6t+Klj(C~f-bYT{{z!UtM>sI;3utqgA z{-UB}1}GAR?t&!~#Si|IFaG-3qT$0(_Vl$Ka`yXK0VL*DGy4k3&a*>)=F~24VabT< zjmYC}*I{n{K#zcktym`5A@6;@W|zhfvYl8m?z!S>w_2}M7K3WNSq5O;h)2N_TAD5) z>P${wchFC<=g+hBwl z@Jmce`VYeh-3~VjypR&{cHLJ38gxzpX!6S2D|B@gthGYWI*I{?d2{f%X~ zh{QEI&c(UtE+Khqd^f4JBIlnEj*MK^HdA7z@s(lR>rKHM8F;CSabBJ%a)xgI!7a;v zC+HU;hR3g^b$oAAILu)od>yfQZy{Q!By@^0EaSKB-~s+rz9mzBOZovNW{}8y zovL!Qa^rwi@bBtIbbXs+6O{a_ttGTe^EvJsC+XQ!RN$~))yG!r0X5Wo4cY|eFk4gg zf)}^xLH@`nuWkrp&1d9@L&x2K2Hb{`PlZ=$94+Zdwp${U^r4NP;o@BfQ+0D=DksM- zVm^hrB$>&(-JuA)O>c;SH14;tmH=FYz#BAvtW7ts9Q3qKd-`o(tDVY z0v(c>AH5#e5ziA_uJGNt6e-0@s9`^b2r)QyyIVR zl0bvNX99DBsLWrE%N|AIewi8>OoM z{k@;m>jwY*qeQ(bJO4Mfi1?eA2LJxc?Nl%O6bof}k2_RCLEC8hhVHq})8^6EnF6#z z*5~dX>RJ_;a&$tQz zU~)>NX)BPfOX;sUey>L@7$tlxkLH2CIsCg2ZQ5W*$EU+aQMCucoYF4#S47|;A&s)( zK5n=Z#3C-6=Tq_$b8W@u=X(cPL}!$?FiPq|!M$-Ejlz}Gt8OVEzIVR%z>;4UvoQv! zzin)69%rY4+2CAMX7H~n5atcV85-#KkJ7UridJ>vqo^@ehnc(lBkwSLR%^uKk?Z4h2{3t8GD*y;lxXB_AQ%Cw_ilGXQ@#^d{-ljY{x-r2?9ChhS zO~%L*ssPbO)#YeD=oE{Kix+WUvhPrgz9n0s#)~w5P_m6JALN%fw7yG;NgDSO6R&xH z6U0e=t)GY^*b;}1v8!1V$B(7RbLA8t0+RVlC+`u5S*Uu6tc3f^ZhM!>W z6HfD;4oq}p_4`u`x}0iCr;`LmR-kY(-%zPzhlhqN+v)?!L93UTmbg+|vzQ}9tJCQr zMv1V$^efL)#%QvZ=1K0@H2T-6XW#Mi)>~*DU$|)?c=L zuHwtm$2Z!H;X`2WgVl6B(g&052{O=VFk{m>%6LL5N=8lVwL#IUf-o6iSlB+ zvot9wCM8;*d{h&VwtG~Ei`tC9LL2TKh>-m-jK4YS|Ms@IVvzzBuSJaj%*DUS3H+6g;i zRy?1UpO0-^<$baAR?Y=Xo|);)h*!WO89jcpzqH-kJ<1!O^>pW-nSQlMiJuq8k(95A zBS~8k%ucuD(XUj$7TOrb5d6u;>gnkm$d%L2r&P_V-N}@$kSypAc`p(*Ln|I?$0eBQ ze=%rFW?J3MYDl$KFGlAJ3?z=bZbF`cZeCEiNp2^R>Kdhz9dwVf)BM)e+#vg{>HJne zX}DpEMw-9m8nx`DWroFB3$rQR_vVRg*HNReM_@NXq>G#e`l|OM{WEcIp5;);c8tu1 zlaF4!N1JJGd_cj+QEpS9{VRC9?px-@IyZSya-BL@Co{J_AWbo&Dh{&3rA2Z32b47PS@Ns literal 0 HcmV?d00001 diff --git a/docs/Application_guide/zh/media/network-comm/net-protocols/voip/voip_answer.png b/docs/Application_guide/zh/media/network-comm/net-protocols/voip/voip_answer.png new file mode 100644 index 0000000000000000000000000000000000000000..4a52d5c401f6796e2908dcee6034149d55be5590 GIT binary patch literal 14058 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 以EC600M_CNLE 为例 +> - [点此下载对应固件 ](https://python.quectel.com/download)。 + +下载固件后,需确认当前固件基础功能以及硬件是否满足 VoIP 通话条件。 +> 此功能是pcm回环测试,可以通过话筒讲话并播放,确认是否正常。 + +```python +#audio pcm 回环测试 +import audio +import G711 +import _thread +import utime + +pcm = audio.Audio.PCM(2, 1, 8000, 2, 1) + +level = pcm.getVolume() +print("level:", level) + +pcm.setVolume(10) + +level = pcm.getVolume() +print("level:", level) + +g711 = G711(pcm) + +def g711_fun_test(para): + while True: + g711_buf = g711.read(1) + if(len(g711_buf) > 0): + write_len = g711.write(g711_buf, 1) + print("G711:", write_len) + +def fun_start(func): + _thread.start_new_thread(func, (1,)) + +fun_start(g711_fun_test) +``` + +### 网络状态检查 + +启动 VoIP 应用之前,首先要确保网络畅通。 + +> 如果网络连接异常,可参考网络拨号相关的应用指导,重新进行网络连接。 + +QuecPython 相关模组在上电后会自动进行蜂窝网络连接。因此,一般情况下开发者只需在应用中检测网络状态以及检查拨号即可,示例代码如下: + +```python +import checkNet +import dataCall + +if __name__ == '__main__': + stage, state = checkNet.waitNetworkReady(30) + if stage == 3 and state == 1: + print('Network connection successful.') + while True: + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1: + print('lte network normal') + break + print('wait lte network normal...') + time.sleep(3) + else: + print('Network connection failed, stage={}, state={}'.format(stage, state)) + +``` + +### VoIP 语音通话服务器注册 + +VoIP 是基于 ip 的网络通信,首先需要保证模组网络正常。[点此查看VoIP api文档 ](https://gitee.com/qpy-doc-center/teedoc_with_qpydoc/blob/df49f188df041da6358b0cfa5fe776807e02aa24/docs/API_reference/zh/networklib/voip.md)。 + +注册成功后,当前设备就可以正常和服务器进行通信,包括呼叫、接听、退出登陆等。 + +> 一般不同的服务器有不同的特性,需要按照不同服务器的要求进行参数配置。 +> 可使用PC工具:MicroSIP 进行对比&调试。 + +VoIP 注册服务器分为2步骤: +1. 创建一个VoIPPhone实例。 + > `phone=VoIPPhone( server: str, port: int, username: str, password: str, callCallback = cb, myIP="0.0.0.0", sipPort=5060, domain=server, displayName=username)` + 在对接服务器时,服务器一般会对如下参数有要求,需要匹配一致进行服务器接入。 + + - `server`: 服务器地址。 + - `port`: 服务器端口号。 + - `username`: 登录的用户名,用于标识唯一用户身份。在注册、认证以及呼叫请求中,客户端会使用这个用户名。 + - `password`: 用户密码,用于在注册过程中向服务器验证身份。客户端在与服务器交互时需提供此密码以确保合法访问。 + - `callCallback`: 回调函数,用于处理呼叫请求和呼叫响应。 + - `myIP`: 本机ip地址,用于标识本地网络地址。若未特别指定,使用本地默认网卡地址作为默认值。 + - `sipPort`: 本机端口号,用于标识本地端口号。若未特别指定,使用默认值5060。 + - `domain`: SIP域,通常与服务器地址相同。在SIP URI中用于标识用户所在的域。若未特别指定,使用服务器地址作为默认值。 + - `displayName`:显示名,默认值usrname,在呼叫中显示给对方的友好名称,不同于SIP用户名,通常更为用户友好的表述。如果没有特别指定,使用SIP用户名作为默认显示名称。 + - `phone`:返回值,VoIPPhone实例,voip电话对象。 + +2. 服务器注册。 + > `phone.start()` + + 此方法正常无返回,返回-1或抛异常,表示注册服务器失败,需检查网络和服务器参数是否正常。 + +### QuecPython VoIP 实现介绍 + +![](../../media/network-comm/net-protocols/voip/microVoip.png) + +> - [点此查看Voip api文档 ](https://gitee.com/qpy-doc-center/teedoc_with_qpydoc/blob/df49f188df041da6358b0cfa5fe776807e02aa24/docs/API_reference/zh/networklib/voip.md)。 + + +## VoIP 语音通话示例 + +QuecPython 提供 VoIP 语音通话实现源码,用于 VoIP 语音通话,本节分为 VoIP 呼叫和接听两个章节进行说明。 + +具体应用流程,如下描述: + +1. VoIP 目前以源码的形式提供,可自行放/usr目录下,再通过`from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent`方式调用对应模块。 +2. 创建一个VoIPPhone实例`phone=VoIPPhone(server: str, port: int, username: str, password: str)`,`phone`是返回可操作的句柄,Python里面称之为对象,该对象拥有voip所有的API方法,例如注册服务器,主动呼叫等。 +3. 创建对象完成后,需要与 SIP 服务端建立连接,使用`phone.start`方法完成登录请求,这里需要的用户名以及密码,在初始化时需提供。 +4. 连接登录完成后,当前设备就可以通过注册回调返回的对象`call.answer`接听来电,也可以根据需求,调用`phone.call`进行呼叫。 + + +### VoIP 呼叫 + +![](../../media/network-comm/net-protocols/voip/voip_call.png) + +> VoIP 语音通话设备仅通过网络(拨号网络或网卡网络)与服务器进行交互,即sip通话仅可呼叫服务器其他成功注册`username`,如需和运营商进行交互,需要服务器进行对应转发&处理。 + +VoIP 接入运营商呼叫具体应用流程,如下描述: +1. VoIP 目前以源码的形式提供,可自行放/usr目录下,再通过`from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent`方式调用对应模块。 +2. 创建一个VoIPPhone实例`phone=VoIPPhone(server: str, port: int, username: str, password: str)`,`phone`是返回可操作的句柄,Python里面称之为对象,该对象拥有voip所有的API方法,例如注册服务器,主动呼叫,退出登陆等。 +3. 创建对象完成后,需要与 SIP 服务端建立连接,使用`phone.start`方法完成登录请求,这里需要的用户名以及密码,在初始化时需提供。 +4. 连接登录完成后,当前设备就可以通过注册回调返回的对象`call.answer`接听来电,也可以根据需求,调用`phone.call`进行呼叫。 +5. 调用`call = phone.call(number)`进行SIP电话呼叫,SIP 通话仅可呼叫服务器其他的注册`username`(如需呼叫电话号码,需服务器进行转发)。成功呼叫后,返回可操作的句柄`call`,该对象拥有call通话所有的 API 方法,例如`call.cancel`取消 VoIP 拨号中电话,`call.hangup`挂断 VoIP 通话等。 +6. `phone.call`成功返回`call`对象后,当前设备就处于正常通话状态,用户可主动`call.cancel`取消 VoIP 拨号中电话,`call.hangup`挂断 VoIP 通话。 +7. 如成功呼叫后,通话对象取消&挂断 VoIP 通话,在创建`VoIPPhone`注册的回调函数会实时返回状态:电话挂断`VoIPEvent.VOIP_EVENT_BYE`,拨号中被取消`VoIPEvent.VOIP_EVENT_CANCELED`,电话已终止`VoIPEvent.VOIP_EVENT_ENDED`。 +8. 如成功呼叫后,通话对象接听 VoIP 通话,在创建`VoIPPhone`注册的回调函数会实时返回状态:电话接听`VoIPEvent.VOIP_EVENT_ANSWERED`。 +9. 如需退出 VoIP 通话,可调用 `phone.stop`关闭 VoIP 电话客户端,断开与服务器的连接。 + +```python +# 导入voip模块 +# -*- coding: UTF-8 -*- +#voip目前以源码的形式提供,可自行放/usr目录下 +from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent#对应流程1,导入voip相关模块 +import log +import dataCall +import utime as time +import checkNet + +''' +下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 +''' +PROJECT_NAME = "QuecPython_VoipCall_example" +PROJECT_VERSION = "1.0.0" + +checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION) + +# 设置日志输出级别 +log.basicConfig(level=log.INFO) +voip_log = log.getLogger("VOIP_CALL") + +#voip状态回调函数,具体状态含义可参考wiki。 +def voip_event(event, call): + if event == VoIPEvent.VOIP_EVENT_REGISTERED: + print('VOIP_EVENT_REGISTERED entry') + elif event == VoIPEvent.VOIP_EVENT_RINGING: + print('VOIP_EVENT_RINGING entry') + # 对应流程8,回调返回对端接听,开启voip应答以及audio。 + try: + call.answer() + call.startAudioService() # pcmu/pcma + except InvalidStateError: + pass + except: + call.hangup() + elif event == VoIPEvent.VOIP_EVENT_CANCELED: + print('VOIP_EVENT_CANCELED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_BYE: + print('VOIP_EVENT_BYE entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ENDED: + print('VOIP_EVENT_ENDED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ANSWERED: + print('VOIP_EVENT_ANSWERED entry') + call.startAudioService() + else: + print('UNKNOWN {} entry'.format(event)) + +if __name__ == '__main__': + stagecode, subcode = checknet.wait_network_connected(30) + if stagecode == 3 and subcode == 1: + voip_log.info('Network connection successful!') + # 获取拨号IP + lte = dataCall.getInfo(1, 0) + if lte[2][0] == 1:#用来确认当前拨号网络是否就绪。 + voip_log.info('lte dataCall normal') + #对应流程2,创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + #对应流程3,请求登录 SIP 服务端 + phone.start() + + time.sleep(3) + voip_log.info('VOIP start call 1007') + # 对应流程5,SIP电话呼叫 + in_call = phone.call('1007') + + time.sleep(20) + voip_log.info('qpy hangup call') + # 对应流程6,当前默认对端接听,执行挂断 VoIP 通话 + # 具体执行,需根据callback返回判断。 + in_call.hangup() + + time.sleep(3) + voip_log.info('qpy deregitster voip service') + # 对应流程9,关闭 VoIP 电话客户端,断开与服务器的连接 + phone.stop() + else: + raise ValueError("dataCall getInfo error") + else: + voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) + +``` + +### VoIP 接听 + +![](../../media/network-comm/net-protocols/voip/voip_answer.png) + +> VoIP 语音通话设备仅通过网络(拨号网络或网卡网络)与服务器进行交互,如需和运营商进行交互,需要服务器进行对应转发&处理。 + +SIP 设备内呼具体应用流程,如下描述: +1. VoIP 目前以源码的形式提供,可自行放/usr目录下,再通过`from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent`方式调用对应模块。 +2. 创建一个 VoIPPhone 实例`phone=VoIPPhone(server: str, port: int, username: str, password: str)`,`phone`是返回可操作的句柄,Python里面称之为对象,该对象拥有voip所有的API方法,例如注册服务器,主动呼叫等。 +3. 创建对象完成后,需要与SIP服务端建立连接,使用`phone.start`方法完成登录请求,这里需要的用户名以及密码,在初始化时需提供。 +4. 连接登录完成后,当前设备就可以通过注册回调返回的对象`call.answer`接听来电,也可以根据需求,调用`phone.call`进行呼叫。 +5. 成功注册服务器后,服务器其他设备就可以主动呼叫当前注册`username`,如服务器其他设备主动呼叫,创建`VoIPPhone`注册的回调函数会实时返回状态:电话来电通知`VoIPEvent.VOIP_EVENT_RINGING`,用户可主动`call.cancel`取消拨号中电话,`call.hangup`挂断通话。 +6. 如需退出VOIP通话,可调用 `phone.stop`关闭 VoIP 电话客户端,断开与服务器的连接。 + +``` +# 导入voip模块 +# -*- coding: UTF-8 -*- +#voip目前以源码的形式提供,可自行放/usr目录下 +from usr.microVoIP.VoIP import VoIPPhone, InvalidStateError, VoIPEvent#对应流程1,导入voip相关模块 +import log +import dataCall +import utime as time +import checkNet + +''' +下面两个全局变量是必须有的,用户可以根据自己的实际项目修改下面两个全局变量的值 +''' +PROJECT_NAME = "QuecPython_VoipAnswer_example" +PROJECT_VERSION = "1.0.0" + +checknet = checkNet.CheckNetwork(PROJECT_NAME, PROJECT_VERSION) + +# 设置日志输出级别 +log.basicConfig(level=log.INFO) +voip_log = log.getLogger("VOIP_ANSWER") + +#voip状态回调函数,具体状态含义可参考wiki。 +def voip_event(event, call): + if event == VoIPEvent.VOIP_EVENT_REGISTERED: + print('VOIP_EVENT_REGISTERED entry') + elif event == VoIPEvent.VOIP_EVENT_RINGING: + # 对应流程5,回调返回对端接听,开启voip应答以及audio。 + print('VOIP_EVENT_RINGING entry') + try: + call.answer() # 对应流程4,当前设备接听来电 + call.startAudioService() # 对应流程4,开启当前设备audio + except InvalidStateError: + pass + except: + call.hangup() + elif event == VoIPEvent.VOIP_EVENT_CANCELED: + print('VOIP_EVENT_CANCELED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_BYE: + print('VOIP_EVENT_BYE entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ENDED: + print('VOIP_EVENT_ENDED entry') + #call.stopAudioService() + elif event == VoIPEvent.VOIP_EVENT_ANSWERED: + print('VOIP_EVENT_ANSWERED entry') + call.startAudioService() + else: + print('UNKNOWN {} entry'.format(event)) + +if __name__ == '__main__': + stagecode, subcode = checknet.wait_network_connected(30) + if stagecode == 3 and subcode == 1: + voip_log.info('Network connection successful!') + # 获取拨号IP + lte = dataCall.getInfo(1, 0)#用来确认当前拨号网络是否就绪。 + if lte[2][0] == 1: + voip_log.info('lte dataCall normal') + #对应流程2,创建一个VoIPPhone实例 + phone = VoIPPhone('118.31.23.236', 9999, '1007', '1007', callCallback=voip_event, myIP=lte[2][2], sipPort=5061) + #对应流程3,请求登录 SIP 服务端 + phone.start() + #注册完成后,voip会一直处于监听状态,等待服务器拨打电话。 + else: + raise ValueError("dataCall getInfo error") + else: + voip_log.info('Network connection failed! stagecode = {}, subcode = {}'.format(stagecode, subcode)) + +``` + +## 常见问题 + +**Q: 存在导入模块失败** + +A:首先确保正确烧录了QuecPython固件,且该固件需包含G711&audio模块,如不包含则需更换固件。 + +**Q:设备连接Voip服务器失败。** + +A:请检查是设备能够正常找网,在确保找网成功后检查voip连接参数是否正确(可使用PC工具:MicroSIP 对比),如工具正常,需提供对应log进行排查。 + +**Q:设备使用call 拨打电话,对应号码无反应(未震铃)** + +A:可使用PC工具:MicroSIP 对比,确认当前服务器转发正常,如工具正常,需提供对应log进行排查。 + +**Q:设备成功登陆号,服务器拨打电话,无对应状态返回 ** + +A:可使用PC工具:MicroSIP 对比,确认当前服务器转发正常,如工具正常,需提供对应log进行排查。 -- Gitee