# BoxBluetoothTooleAssistant **Repository Path**: pancoit_ios/BoxBluetoothTooleAssistant ## Basic Information - **Project Name**: BoxBluetoothTooleAssistant - **Description**: 磐钴智能的SDK覆盖安卓手机、苹果手机、北斗短报文一体机、安卓平板电脑、PC电脑各端。 实现北斗短报文收发各类功能。 - **Primary Language**: Swift - **License**: Not specified - **Default Branch**: master - **Homepage**: http://www.beidousdk.com - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2019-12-03 - **Last Updated**: 2024-11-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 北斗SDK使用说明 ### 一、下载SDK:BDBluetoothTools.swift demo:[https://gitee.com/pancoit_ios/BoxBluetoothTooleAssistant](https://gitee.com/pancoit_ios/BoxBluetoothTooleAssistant) ### 二、环境配置 SDK使用xcode11.1编写,使用Swift 5语言,同时支持Ojective-C语言使用,如果您是OC开发者,请先了解swift与oc混合开发。 2.1、把BDBoxBluetoothToolSwift.framework拖入自己工程并且勾选Copy items if needed选项,如图: ![MacDown Screenshot](http://pic.pancoit.com/PanGuYum/tdwt/image/1576894669107-583723070.png) ### 三、使用SDK(swift语言) SDK帮助开发者减少蓝牙开发工作,如果开发者使用SDK中涉及蓝牙底层库的类,那么请在使用的地方 `import CoreBlutooth` ##### 3.1、导入SDK ```ruby import BDBoxBluetoothToolSwift ``` ##### 3.2 设置代理 ```swift // OC语言无法使用swift全局常量bdBluetooth // 请使用 [BDBluetoothTools shared]; bdBluetooth.delegate = self bdBluetooth.dataSource = self ``` bdBluetooth.delegate:BDBluetoothDelegate 是蓝牙相关委托方法 bdBluetooth.dataSource:BDBoxManageDataSource 是北斗设备蓝牙通信相关委托方法 ##### 3.3 连接北斗设备 ```swift // OC语言无法使用swift全局常量bdBluetooth // 请使用 [BDBluetoothTools shared]; // 调用connect()方法会直接搜索蓝牙,SDK默认提供UI // 如果需要自定义UI,请实现BDBluetoothDelegate 的 func bluetoothConfiguration(_ blutooth:BDBluetoothTools)-> BDConfiguration方法 修改配置 bdBluetooth.connect() // 断开连接请调用 //bdBluetooth.disConnect() ``` ##### 3.4 配置蓝牙(可选) SDK提供默认的连接蓝牙参数, 如果不需要修改参数请跳过这部分。 ```swift func bluetoothConfiguration(_ blutooth: BDBluetoothTools) -> BDConfiguration { let configuration = BDConfiguration() // 断开自动连接 - 自主断开不重新连接 configuration.automatic = false // 使用默认UI - 默认提供连接蓝牙设备的交互UI, 用户需自定义UI,请设置为false configuration.defaultUI = true // 蓝牙设备 北斗终端自检频度(单位:秒) configuration.selfCheckingFrequency = 5 return configuration } ``` 如果需要自定义UI的开发者,请实现BDBluetoothDelegate方法 ```swift func bluetooth(_ central: BDCentralManager, didDiscover peripheral: BDPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber){ // peripheral 发现的外设 } ``` 并调用以下接口来连接北斗设备 ```swift // 连接需要选择的外设 func connect(peripheral:BDPeripheral, options:[String : Any]?) ``` ##### 3.5 监听蓝牙连接成功、连接失败、断开连接、写入数据失败 ... 建议至少实现连接成功和断开连接两个方法,其他更多方法请查看SDK的BDBluetoothDelegate协议内容 ```swift /// 已经连接设备 /// 同时发送通知 BDDidConnectTerminal func bluetooth(_ central: BDCentralManager, didConnect peripheral: BDPeripheral){ print("已连接设备") } /// 设备断开蓝牙连接 /// 同时发送通知 BDDidDisconnectTerminal func bluetooth(_ central: BDCentralManager, didDisconnectPeripheral peripheral: BDPeripheral, error: Error?){ print("已断开连接") } ``` ##### 3.6 使用北斗盒子发射短报文 上面我们已经准备好了蓝牙管理,接下来演示使用SDK 如何发射北斗短报文,发送北斗短板文时请将北斗设备的天线朝南开阔的地方,并倾斜25°~45°,具体角度开发者根据自己当前地区调整,调整至北斗设备的“报文灯”或“信号灯”至绿色。 北斗短报文通信有频度限制,SDK内部提供发送倒计时回调,开发者也可以自己控制北斗短报文频度发送控制。 北斗通信报文形式有3种,请开发者使用北斗通信之前稍微了解一下: * 汉字模式 * 代码模式 * 代码和汉字混合模式 ```swift // 该方法实际调用TXA(_,_,_,_)方法(北斗通信申请),使用代码和汉字混合模式发送 /// - Parameter address: 接收者用户地址 /// - Parameter content: 发送内容(中文) bdBluetooth.boxManager.sendMessage("北斗卡号", content: "发送内容") // 上面的方法实用的混合模式,如开发者需要自定义报文内容而使用代码模式,请使用: /// - Parameter address: 接收者用户地址 /// - Parameter type: 信息类型 1-普通通信;2-预留;3-通信查询 /// - Parameter form: 报文形式 0-汉字; 1-代码; 2-代码和汉字混合模式 /// - Parameter content: 十六进制报文内容 bdBluetooth.boxManager.TXA("北斗卡号", type: type, form: form, content: "十六进制报文内容") ``` ##### 3.7 发射信息反馈 北斗通信的过程是这样的: ![MacDown Screenshot](http://pic.pancoit.com/PanGuYum/tdwt/image/1576909383414-1383373467.png) 其中在第1过程和第4过程中发生丢包的可能性是比较高的,所以发射北斗短报文需要有良好的信号,接收短报文也需要有良好的北斗信号。 信息反馈只能反馈第1过程中北斗设备是否已经将北斗通信内容发射出去,并不能判断是否送达卫星,更不能判断第2、3、4步骤是否到达。 ``` // 发送通信申请的信息反馈(2.1协议) func bluetooth(_ boxManage:BDBaseManager,didUpdate$BDFKI BDFKI:[String]){ /// - Parameter BDFKI: 反馈信息 /// BDFKI[0] : 发送的指令名称 "TXA" , “DWA” 等 /// BDFKI[1] : 状态 Y:指令发射成功 N:指令发射失败 /// BDFKI[2] : 频度设置指示 N-频度设置错误,当填入的频度小于本用户设备的服务频度时,给出频度设置错误的提示 /// BDFKI[3] : 发射抑制指示 /// BDFKI[4] : 等待时间 /// BDFKI[5] : 发射抑制指示文本说明 } //当前我司基本已经不使用4.0协议的北斗手持设备,如果开发者的接收端还有4.0协议北斗手持设备,请同时实现4.0协议接口,如果没有可以不实现,跳过。 // 发送通信申请的信息反馈(4.0版本) // 这里指的是当前蓝牙连接的北斗设备 使用的北斗协议版本 func bluetooth(_ boxManage:BDBaseManager,didUpdate$FKXX FKXX:[String]){ /// - Parameter blutooth: /// - Parameter FKXX: 反馈信息 /// FKXX[0] : 反馈标志 /// FKXX[1] : 附加信息 /// FKXX[2] : 反馈标志文本说明 } ``` ##### 3.8 接收短报文消息 发射短报文的方法已经说明了,那么对方给北斗设备发短报文怎么接收呢? ```swift // 接收到混合模式的通信信息 // 只要接收到混合模式报文,SDK内部解析出内容信息,并回调这个方法 func bluetooth(_ boxManage: BDBaseManager, mixedModeReceive message: String, address: String) { //message : 报文内容(解析后的中文编码) // address : 发信方北斗卡号 // 没有时间,因为解析的通信时间并不准确 // SDK提供的sendMessage("北斗卡号", content: "发送内容")方法就是混合模式发送,所以可以通过这个方法直接接收并解析具体内容 } ``` ***如果没有使用代码模式,下面可以跳过,直接查看3.9*** ```swift // 如果开发者并不是使用混合模式,而是使用代码模式,可以实现更前一步的通信信息接口 // 请实现接收到通信信息的接口 // 2.1协议通信信息 func bluetooth(_ boxManage: BDBaseManager, didUpdate$BDTXR BDTXR: [String]) { print("2.1协议通信信息:\(BDTXR)") /// Parameter BDTXR: 通信信息内容 /// BDTXR[0] : 信息类型 ‘1’:普通通信 ‘2’:预留 ‘3’:通信查询 /// BDTXR[1] : 发信方用户地址ID号 /// BDTXR[2] : 报文形式 ‘0’:汉字 ‘1’:代码 ‘2’:汉字和代码混合 /// BDTXR[3] : 发信时间(UTC)(时间不准确,请不要使用) /// BDTXR[4] : 报文通信信息内容(十六进制) // 不管是 汉字模式还是代码模式或混合模式,都会调用这个方法。 } 当前我司基本已经不使用4.0协议的北斗手持设备,如果开发者的接收端还有4.0协议北斗手持设备,请同时实现4.0协议接口,如果没有可以不实现,跳过。 // 4.0协议通信信息 如果没有设备使用4.0协议可不实现 // 这里指的是当前蓝牙连接的北斗设备 使用的北斗协议版本,和发送端的北斗协议版本无关。 func bluetooth(_ boxManage: BDBaseManager, didUpdate$TXXX TXXX: [String]) { print("4.0协议通信信息:\(TXXX)") /// - Parameter TXXX: 通信信息内容 /// TXXX[0] : 本机卡号 /// TXXX[1] : 信息类别:十六进制,如果需要对信息类别解析,请转成二进制,然后拆开解析 /// 通信2bit:固定01 /// 电文l形式1bit:0-汉字;1-代码 /// 是否回执1bit:固定0 /// 通信方式1bit:0-通信;1查询 /// 密钥1bit:0-无;1-有 /// 余量2bit:固定00 /// TXXX[2] : 发信方卡号 /// TXXX[3] : 发信时间(时间不正确,请不要使用发信时间,使用当前时间) /// TXXX[4] : 电文长度 /// TXXX[5] : 电文内容 /// TXXX[6] : CRC标 } ``` ##### 3.9 终端信息 连接蓝牙的时候有配置北斗终端连接后的终端自检频度,默认5秒自检一次,并同时发送通知BDUpdateTerminalInformation,开发者也可以通过添加通知获取到终端的信息,也可以任意时刻使用`bdBluetooth.box` 获取到`BDBox.swift`类而获取到终端信息 ```swift /// 接收终端信息 (2.1协议) /// - Parameter BDZDX: 终端信息 /// BDZDX[0] : 用户地址 /// BDZDX[1] : 电池电量 /// BDZDX[2] : 功率状况 1 /// BDZDX[3] : 功率状况 2 /// BDZDX[4] : 功率状况 3 /// BDZDX[5] : 功率状况 4 /// BDZDX[6] : 功率状况 5 /// BDZDX[7] : 功率状况 6 /// BDZDX[8] : 功率状况 7 /// BDZDX[9] : 功率状况 8 /// BDZDX[10] : 功率状况 9 /// BDZDX[11] : 功率状况 10 /// BDZDX[12] : 服务频度 /// BDZDX[13] : IC卡等级 /// BDZDX[14] : 通讯长度 /// 同时发送通知 BDUpdateTerminalInformation 通知info = ["box":BDBox] func bluetooth(_ boxManage:BDBaseManager,didUpdate$BDZDX BDZDX:[String]){ print("北斗终端 自检信息:\(BDZDX)") } 当前我司基本已经不使用4.0协议的北斗手持设备,如果开发者的接收端还有4.0协议北斗手持设备,请同时实现4.0协议接口,如果没有可以不实现,跳过。 /// 接收终端信息 (4.0协议) /// - Parameter ZDXX: 终端信息 /// ZDXX[0] : 卡号 /// ZDXX[1] : 频度 /// ZDXX[2] : 最大电 文长度 本机卡所支持的最大电文长度(单位:bit) /// ZDXX[3] : 电源状态 0x00:正常,0x01:电源充电,0x02:系统放电 0xFF:电源系统异常(预留) /// ZDXX[4] : IC卡状态 0x00 正常,非0 异常 /// ZDXX[5] : 硬件状态 0x00 正常,非0 异常 /// ZDXX[6] : 电池电量 /// ZDXX[7] : 入站状态 0x02 正常入站,其他值:异常 /// ZDXX[8] : 功率状况 波束1 /// ZDXX[9] : 功率状况 波束2 /// ZDXX[10] : 功率状况 波束3 /// ZDXX[11] : 功率状况 波束4 /// ZDXX[12] : 功率状况 波束5 /// ZDXX[13] : 功率状况 波束6 /// 同时发送通知 BDUpdateTerminalInformation 通知info = ["box":BDBox] func bluetooth(_ boxManage:BDBaseManager,didUpdate$ZDXX ZDXX:[String]){ print("北斗终端 自检信息:\(ZDXX)") } ``` ##### 3.10 北斗终端 三种模式使用 北斗终端有三种模式,分别是: * SOS模式:紧急报警,长按“SOS”键3秒启动,启动后设备中间的模式灯为“红色” * 极限追踪模式:位置上报,长按“OK”+“关机键”3秒启动,启动后设备中间的模式灯为“蓝色” * OK模式:报平安,长按"OK"键3秒启动,启动后设备的模式灯为“绿色” 各个模式有默认的一些参数,开发者需要修改默认参数才能把三种模式的信息发送至开发者的平台。请调用**bdBluetooth.boxManager.方法名** **SOS模式接口** ```swift /// 获取SOS模式信息 /// - Parameter number: 中心号码 /// - Parameter frequency: SOS模式上报频度 /// - Parameter content: SOS上报内容 func getSOS(_ sosInfo:@escaping sosInfoType) /// 设置SOS /// - Parameter number: 中心号码 /// - Parameter frequency: SOS频度 /// - Parameter content: SOS 内容 -- PD01设备不支持设置SOS内容,代替值:SOS /// - Parameter reslut: 设置后返回SOS信息 func setSOS(number:String, frequency:Int, content:String, reslut:@escaping sosInfoType) /// 蓝牙指令开启SOS /// - Parameter isOn: true 打开, false 关闭 /// - Parameter sosFinish: 1成功, 0 失败 func openSOS(_ isOn:Bool,finish:@escaping ResultType) ``` 设置sos参数示例: ```swift // 配置北斗终端的SOS模式参数,配置后一直生效(重新刷机后会失效) // 报警频度不能设置低于北斗卡通信频度 bdBluetooth.boxManager.setSOS(number: "接收平台的北斗卡号", frequency: 报警频度, content: "报警内容") { (carNumber, fre, content) in // 设置完成 } ``` **极限追踪模式接口** ```swift /// 获取极限追踪信息 /// - Parameter number: 中心号码 /// - Parameter frequency: 上报频度 /// - Parameter mode: 追踪模式 func getLimitTrack(_ limitTrack:@escaping LimitTrackInfoType) /// 设置极限追踪 /// - Parameter number: 中心号码 /// - Parameter frequency: 上报频度 /// - Parameter mode: 追踪模式 /// - Parameter reslut: 设置返回追踪模式信息 func setLimitTrack(number:String, frequency:Int, mode:Int, reslut:@escaping LimitTrackInfoType) /// 蓝牙开启极限追踪模式 /// - Parameter isOn: true 打开, false 关闭 /// - Parameter sosFinish: 1成功, 0 失败 func openLimitTrack(_ isOn:Bool,finish:@escaping ResultType) ``` 设置极限追踪参数示例: ```swift bdBluetooth.boxManager.setLimitTrack(number: "接收平台的北斗卡号", frequency: 上报频度, mode: 上报模式) { (carNumber, fer, mode) in // 设置完成 } ``` **OK模式接口** ```swift /// 获取报平安信息 /// - Parameter number: 中心号码 /// - Parameter content: 报平安内容 func getOK(_ okInfo:@escaping okInfoType) /// 设置报平安信息 /// - Parameter number: 中心号码 /// - Parameter content: 报平安内容 /// - Parameter reslut: 设置返回报平安模式信息 func setOK(number:String, content:String, reslut:@escaping okInfoType) /// 蓝牙开启报平安模式 /// - Parameter isOn: true 打开, false 关闭 /// - Parameter sosFinish: 1成功, 0 失败 func openOK(_ isOn:Bool,finish:@escaping ResultType) ``` 报平安模式设置示例: ```swift bdBluetooth.boxManager.setOK(number: "接收平台的北斗卡号", content: "报平安内容") { (carNumber, content) in // 设置完成 } ``` 三种模式的北斗报文都是以代码模式发射,平台接收到这三种模式的报文内容需要对报文内容进行解析,报文内容为十六进制字符串。 解析SOS报文内容,SOS报文内容都是以“01EE”开头,但是有两种情况,第一种情况是设备在无法定位的情况下求救,那么会先发SOS标志报警报文,然后发送RD紧急定位 **SOS标志报文内容**(情况一:无法定位) 编号 | 含义 | 取值 | 备注 | ----|----------|--------|---------------------------------| 1 | 扩展指令头 | ‘01’ | 常量 | 2 | 指令类型 | “EE” | 常量 | 3 | 求救标志 | ‘534F53’ | “SOS”常量 | 4 | 发送内容 | | 发送内容为SOS接口设置参数的发送内容 | **SOS数据**(情况二:可以定位) 编号 | 含义 | 取值 | 备注 | ----|----------|--------|---------------------------------| 1 | 扩展指令头 | ‘01’ | 常量 | 2 | 指令类型 | “EE” | 常量 | 3 | 时间信息 | | 8位ASCII码(三种模式都表示十六进制的ASCII码)。采集时间,时分秒小秒各占两位ASCII码 | 4| 经度| | 8位ASCII码,结构如下: 第1位到第2位表示”度”;第3位和第4位表示”分”,第3第4位置中bit[15]为0时表示东经,为1时表示西经; 第5位至第6位表示”秒”。;第7位至第8位表示”小秒”。 | 5|纬度 | | 8位ASCII码,结构如下: 第1位,第2位表示”度”。;第3位和第4位表示”分”。 第3第4位置中bit[15]为0时表示北纬,为1时表示南纬;第5位至第6位表示”秒”。;第7位至第8位表示”小秒”。| 6|高度 | | 4位ASCII码,16进制显示,结构如下: bit[15,14]:’00’表示高度为正,01表示高度为负;bit[13,0]表示高度的绝对值;单位:米| 7|高度异常值 | |4位ASCII码,16进制显示,结构如下: bit[15,8]:00H 表示高度为正,01H 表示高度为负;bit[7,0]: 表示高度异常值的绝对值;单位:米 | 8| 发送内容| |发送内容为SOS接口设置参数的发送内容 |
解析极限追踪上报的位置数据,最多4个位置 **极限追踪位置上报** 编号 | 含义 | 取值 | 备注 | ----|----------|--------|---------------------------------| 1| 扩展指令|‘01’ | | 2|指令类型 |“E4”| 常量| 3|点个数 | | 0x01到0x04| 4| 时间信息| | 12位ASCII码(三种模式都表示十六进制的ASCII码)。采集时间,年月日时分秒各占两位ASCII码| 5| 经度| | 8位ASCII码,结构如下: 第1位到第2位表示”度”;第3位和第4位表示”分”。 第3第4位置中bit[15]为0时表示东经,为1时表示西经;第5位至第6位表示”小数点前的整秒数”;第7位至第8位表示”小数点后的小秒数”。| 6| 纬度| | 8位ASCII码,结构如下: 第1位,第2位表示”度”;第3位和第4位表示”分”。 第3第4位置中bit[15]为0时表示北纬,为1时表示南纬;第5位至第6位表示”小数点前的整秒数”;第7位至第8位表示”小数点后的小秒数”。| 7|高程 | | 4位ASCII码,16进制显示,结构如下: bit[15,14]:’00’表示高度为正,01表示高度为负;bit[13,0]表示高度的绝对值;单位:米| 8|轨迹序号 | | 4位ASCII码,范围1-65535,用区分每次追踪模式的轨迹,每次启动极限追踪模式序号递增加1| 9| | | 重复<4>-<8>项目(最多重复4个点)|
解析OK报平安内容,报平安报文内容都是以“01E1”开头,但是有两种情况,第一种情况是设备在无法定位的情况下报平安,那么会先发OK标志报警报文,然后发送RD紧急定位 **OK标志报文内容**(情况一:无法定位) 编号 | 含义 | 取值 | 备注 | ----|----------|--------|---------------------------------| 1|扩展指令头|‘01’ | 常量| 2| 指令类型|“E1” | 常量| 3| 平安标志|‘4F4B’ | 常量,‘OK’| 4| 发送内容| |发送内容为OK模式接口设置的发送内容| **OK报文数据**(情况二:可以定位) 编号 | 含义 | 取值 | 备注 | ----|----------|--------|---------------------------------| 1 | 扩展指令头 | ‘01’ | 常量 | 2 | 指令类型 | “E1” | 常量 | 3 | 时间信息 | | 8位ASCII码(三种模式都表示十六进制的ASCII码)。采集时间,时分秒小秒各占两位ASCII码 | 4| 经度| | 8位ASCII码,结构如下: 第1位到第2位表示”度”;第3位和第4位表示”分”,第3第4位置中bit[15]为0时表示东经,为1时表示西经; 第5位至第6位表示”秒”。;第7位至第8位表示”小秒”。 | 5|纬度 | | 8位ASCII码,结构如下: 第1位,第2位表示”度”。;第3位和第4位表示”分”。 第3第4位置中bit[15]为0时表示北纬,为1时表示南纬;第5位至第6位表示”秒”。;第7位至第8位表示”小秒”。| 6|高度 | | 4位ASCII码,16进制显示,结构如下: bit[15,14]:’00’表示高度为正,01表示高度为负;bit[13,0]表示高度的绝对值;单位:米| 7|高度异常值 | |4位ASCII码,16进制显示,结构如下: bit[15,8]:00H 表示高度为正,01H 表示高度为负;bit[7,0]: 表示高度异常值的绝对值;单位:米 | 8| 发送内容| |发送内容为OK模式接口设置的发送内容 | *** ### 四、SDK类 SDK接口介绍到这个位置已经基本满足开发者使用了,下面介绍SDK的几个开放类,开发者可以查看开发接口以及备注说明了解更多接口使用。 **BDBluetoothTools.swift** :蓝牙管理类 * 单利 * 搜索蓝牙设备 * 连接蓝牙设备 * 断开蓝牙设备 * 发送蓝牙指令 * 接收蓝牙指令 * 判断蓝牙设备信号是否满足发送 **BDBaseManager.swift**:北斗设备基础管理类 > BDBoxManager4_0.swift:4.0协议北斗设备管理类 > BDBoxManager21.swift:2.1协议北斗设备管理类 * 连接北斗设备发送基础数据 * 解析北斗设备蓝牙指令 * 生成北斗设备蓝牙指令 * 北斗设备操作接口 * 北斗数据类型封装 * 北斗设备发送频度控制 * 北斗设备通信接口 **BDBox.swift**:北斗设备信息类 * 基本信息 * 判断蓝牙设备信号是否满足发送 **BDConfiguration.swift**:蓝牙连接配置类 * 配置信息 **BDParameter.swift**:北斗盒子蓝牙参数 * 参数信息 **BDBluetoothDelegate**:北斗蓝牙管理委托协议 * 蓝牙配置接口 * 手机蓝牙状态接口 * 发现蓝牙设备接口 * 已连接蓝牙接口 * 连接失败接口 * 断开连接接口 * 搜索外设服务接口 * 搜索外设特征接口 * 接收蓝牙数据接口 * 写入数据失败接口 **BDBoxManageDataSource**:北斗设备管理委托协议 * 接收到完整北斗协议数据 * 设备授权结果 * 设备当前工作模式 2.1协议接口 * 接收到通信信息 * 收到终端信息 * 收到反馈信息 * 收到设备版本信息 * 收到IC信息 * 收到参数信息 * 收到内部打点信息 * 收到未读消息条数输出信息 4.0协议接口 * 收到通讯信息 * 收到IC信息 * 收到反馈信息 * 收到版本信息 * 收到参数信息 ### 五、关于我们 #### 广州磐钴智能科技有限公司 官网:[http://www.pancoit.com](http://www.pancoit.com/) 电话:(020)32379706 其他推荐:[[天地卫通官网](http://www.tiandiweitong.com/)] | [[北斗民用服务平台](http://www.bdgalaxy.com/)] ![MacDown Screenshot](http://pic.pancoit.com/PanGuYum/tdwt/image/15769221566321385978529.png)