2 功能说明
2.1 目录浏览
2.1.1 功能描述
管理设备sd、usb、flash的文件, 可以实现文件的浏览、删除。
2.1.2 示例代码
在进行目录浏览之前需要先从 RcspOperateWrapper 中获取到 OPDirectoryBrowse.IOperateDirectoryBrowse 目录浏览操作对象
const operateWrapper: RcspOperateWrapper | undefined = undefined;//此处需获取到真实的RcspOperateWrapper
//获取目录浏览操作对象
const RCSPOpDirectoryBrowse: OPDirectoryBrowse.IOperateDirectoryBrowse = operateWrapper?.getOperaterByClass(OPDirectoryBrowse.OperateDirectoryBrowse.prototype)
2.1.2.1 获取在线的存储设备
存储设备列表有:SD Card 0 ,SD Card 1,Flash,Flash2。常见手表的存储设备有SD Card 0,Flash
const storageDevs: File[] = RCSPOpDirectoryBrowse.getOnLineStorageDevs()
2.1.2.2 从存储设备的根目录开始浏览
目前浏览目录,需要从存储设备的根目录开始一层层地往下浏览。默认浏览层级上限是8层(算上存储设备文件夹和ROOT文件夹),如需修改层级上限,请在 appendBrowse 函数传递参数 maxDepth (仅当次生效)
const storageFile: OPDirectoryBrowse.File | undefined = undefined//存储设备文件夹
if (storageFile) {
RCSPOpDirectoryBrowse?.appendBrowse(storageFile).then(_devFolder => {
const rootFolder = _devFolder.listFile()?.[0]//root文件夹
if (rootFolder) {
RCSPOpDirectoryBrowse?.appendBrowse(rootFolder).then(_rootFolder => {
//根目录下的文件列表
const tempList = _rootFolder.listFile()
})
}
})
}
2.1.2.3 进入下一级文件夹
默认浏览子文件数量是10个,如需修改,请在 appendBrowse 函数传递参数 readPageSize (仅当次生效)
const file: OPDirectoryBrowse.File | undefined = undefined//目标文件夹
if (file) {
RCSPOpDirectoryBrowse?.appendBrowse(file).then(_folder => {
//子文件列表
const tempList = _folder.listFile()
//该文件夹是否加载完毕
const isLoadFinished = _folder.isLoadFinished()
})
}
2.1.2.4 加载更多文件
目标文件夹加载更多子文件,默认加载子文件数量是10个,如需修改,请在 loadMore 函数传递参数 readPageSize (仅当次生效)
const file: OPDirectoryBrowse.File | undefined = undefined//目标文件夹
if (file) {
RCSPOpDirectoryBrowse?.loadMore(file).then(_folder => {
//子文件列表
const tempList = _folder.listFile()
//该文件夹是否加载完毕
const isLoadFinished = _folder.isLoadFinished()
}).catch(error=>{
//失败
})
}
2.1.2.5 返回上一级文件夹
目标文件夹返回上一级文件夹
const file: OPDirectoryBrowse.File | undefined = undefined//目标文件夹
if (file) {
RCSPOpDirectoryBrowse?.backBrowse(file).then(_folder => {
//上一级文件夹
const parentFoldet = _folder
//子文件列表
const tempList = _folder.listFile()
//该文件夹是否加载完毕
const isLoadFinished = _folder.isLoadFinished()
}).catch(error=>{
//失败
})
}
2.1.2.6 根据路径获取文件
根据绝对路径获取文件对象,文件路径例如:SD Card 0/ROOT
const targetPath: string = "SD Card 0/ROOT";//目标文件夹路径
RCSPOpDirectoryBrowse?.getFileByPath(targetPath).then(_folder => {
//目标文件对象
const targetFolder = _folder
//子文件列表
const tempList = _folder.listFile()
//该文件夹是否加载完毕
const isLoadFinished = _folder.isLoadFinished()
}).catch(error=>{
//失败
})
2.1.2.7 删除文件
先删除设备端的文件,后删除app缓存数据。(注:目前只支持删除文件,不支持删除文件夹)
const fileList: OPDirectoryBrowse.File[];//删除文件列表
RCSPOpDirectoryBrowse?.deleteFile(fileList).then(() => {
//删除成功
}).catch((error)=>{
//删除失败
})
2.1.2.8 重读文件夹
重新读取文件夹
let targetFolder: OPDirectoryBrowse.File;//目标文件夹
RCSPOpDirectoryBrowse?.reloadFolder(targetFolder).then(_folder => {
//重读成功
targetFolder = _folder
}).catch((error)=>{
//失败
})
2.1.3 事件通知
当目录发生变化时,通过注册的回调,回调事件给上层。上层根据事件的 type 进行判断事件类型是不是自己要处理的类型。
const callback: OPDirectoryBrowse.OperaterEventCallbackDirectoryBrowse = {
onEvent: (_event: OPDirectoryBrowse.OperaterEventDirectoryBrowse) => {
switch (_event.type) {
case 'NONE':
break;
case 'onDirectoryChange'://目录发生变化
//变化的文件
const changeFolder = _event.onDirectoryChange?.folder
break;
default:
break;
}
}
}
//注册回调
RCSPOpDirectoryBrowse?.registerEventCallback(callback)
//注销回调
RCSPOpDirectoryBrowse?.unregisterEventCallback(callback)
//移除全部回调
RCSPOpDirectoryBrowse?.removeAllEventCallback()
2.1.4 File对象
File对象是目录浏览时,最多使用的操作对象。有些属性方法会影响到本地缓存的数据结构,请谨慎修改(如: path , updateLoadFinished)。
declare class File {
private _fs;
/** 路径,格式:dev/root/xxx/xxx */
path: string;
constructor(fs: FileSystem, path: string);
/**文件名**/
getName(): string | undefined;
/**列举子文件-绝对路径**/
list(): string[] | undefined;
/**列举子文件-文件对象**/
listFile(): File[] | undefined;
/**父文件-绝对路径**/
getParent(): string | undefined;
/**父文件-文件对象**/
getParentFile(): File | undefined;
/**文件路径簇号**/
getPathCluster(): number[] | undefined;
/**存储设备序号**/
getDevIndex(): number | undefined;
/**当前文件夹下的子文件是否都已加载*/
isLoadFinished(): boolean;
/**是不是文件夹**/
isDirectory(): boolean;
/**是不是unicode格式**/
isFormatUnicode(): boolean;
/**删除该文件的本地缓存*/
delete(): boolean;
/**删除该文件的全部子文件本地缓存*/
deleteAllChild(): boolean;
/**更新子文件全部加载状态**/
updateLoadFinished(isLoadFinished: boolean): void;
/**文件是否存在*/
exist(): boolean;
}
2.2 文件下载(App->设备)
该功能对应小程序中的大文件传输(App->Dev)
- 将文件传输到存储设备的默认文件夹下(SD Card设备是Root/Download目录,Flash设备是直接放在Root目录下)。
目前文件名处理规则:短文件名进行gbk编码,长文件名进行utf16le编码。长短文件名判断依据:去掉后缀名(例如.mp3)后,gbk编码字节数小于9
只能执行一个文件下载任务,不能同时进行多个文件下载任务。
当在目录浏览或者文件读取时,是不能进行文件下载任务,反之亦然。两者是互斥的。
在进行文件下载之前需要先从 RcspOperateWrapper 中获取到 OPLargerFileTrans.IOperateLargeFileTrans 大文件传输操作对象。
const operateWrapper: RcspOperateWrapper | undefined = undefined;//此处需获取到真实的RcspOperateWrapper
//获取目录浏览操作对象
const RCSPOpLargeFileTransfer: OPLargerFileTrans.IOperateLargeFileTrans = operateWrapper?.getOperaterByClass(OPLargerFileTrans.OperateLargeFileTrans.prototype)
2.2.1 传输任务回调
interface TransferTaskCallback {
/**
*传输失败
* @param code
* 1//创建文件失败
* 2//环境准备失败
* 3//读取crc失败
* 4//重命名失败
* 5//传输过程中-数据越界
* 6//传输超时
* 1001:大文件传输结束-写失败
* 1002:大文件传输结束-数据超出范围
* 1003:大文件传输结束-crc校验失败
* 1004:大文件传输结束-内存不足
*
*/
onError(code: number): void;
/**传输开始**/
onStart(): void;
/**传输进度**/
onProgress(progress: number): void;
/**传输成功**/
onSuccess(): void;
/**取消传输
* @param code 0:app取消,1:dev取消
*/
onCancel(code: number): void;
}
2.2.2 执行传输任务
执行任务前先要获取目标存储设备的SdCardBeans对象
const operateWrapper: RcspOperateWrapper | undefined = undefined;//此处需获取到真实的RcspOperateWrapper
//获取文件对象(负责设备支持的存储设备,在线的存储设备)
const RCSPOpFile: OPFile.IOperateFile = operateWrapper?.getOperaterByClass(OPFile.OperateFile.prototype)
//在线存储设备列表
const storageDevs = RCSPOpFile.getOnLineSdCardBeans()
//目标存储设备
let targetSDCardBean: OPFile.SDCardBean | undefined = undefined
storageDevs.forEach(item => {
if(item.index == OPFile.SDCardBean.INDEX_FLASH){
//flash存储设备
targetSDCardBean = item
}else if(item.index == OPFile.SDCardBean.INDEX_SD0){
//sd card 0存储设备
}
//还有其他的存储设备:sd1,flash2,usb
})
执行传输任务
const callback: OPLargerFileTrans.TransferTaskCallback = {
onError: (code) => {
//传输失败
}, onStart: () => {
//传输开始
}, onProgress: (progress) => {
//传输进度
}, onCancel: (code) => {
//传输取消,0:app取消,1:dev取消
}, onSuccess: () => {
//传输成功
}
}
const sdCardBean = targetSDCardBean//目标存储设备
const fileBuffer = new Unit8Array()//文件数据
RCSPOpLargeFileTransfer?.isFree()//当前是不是空闲状态(没有进行大文件传输)
RCSPOpLargeFileTransfer?.excuteTransferTask({
fileBuffer: fileBuffer,//文件数据
sdCardBean: sdCardBean,//目标存储设备
isSupportPackageCrc16: true,//设备是否支持crc16,公版默认支持
fileName: fileName,//文件名
lastModifyTime: tempFilePaths[0].time,//文件最新修改时间,flash文件需要根据这个进行覆盖
fileNameEncodeFormat: 0,
callback
})
2.2.3 取消传输任务
RCSPOpLargeFileTransfer?.cancelTransfer()
2.3 文件读取(设备->App)
该功能对应小程序中的大文件传输(Dev->App)
- 将设备端的文件读取到手机端
只能执行一个文件读取任务,不能同时进行多个文件读取任务。
当在目录浏览或者文件下载时,是不能进行文件读取任务,反之亦然。两者是互斥的。
在进行文件读取之前需要先从 RcspOperateWrapper 中获取到 OPLargerFileGet.IOperateLargeFileGet 文件读取操作对象。
const operateWrapper: RcspOperateWrapper | undefined = undefined;//此处需获取到真实的RcspOperateWrapper
//获取目录浏览操作对象
const RCSPOpLargeFileGet: OPLargerFileGet.IOperateLargeFileGet = operateWrapper?.getOperaterByClass(OPLargerFileGet.OperateLargeFileGet.prototype)
2.3.1 传输任务回调
interface TransferTaskCallback {
/**
*
* @param code
* 1:开始读取文件失败
* 2:总数据长度不对
* 3:传输超时
*
* 100x:对应设备端协议 结束命令的错误码 x
*/
onError(code: number): void
/**传输开始**/
onStart(): void
/**传输进度
* @param packectData 分包数据
* **/
onProgress(progress: number, packectData?: ArrayBuffer): void
/**传输成功
* @param data 总包数据
* **/
onSuccess(data: ArrayBuffer): void
/**
* @param code
* 0:主动取消
* 1:丢包
* 2:crc错误
*/
onCancel(code: number): void
}
2.3.2 执行传输任务
- 传输任务有三种接口方式。
1.以文件名方式读取
2.以文件簇号方式读取
3.以文件名方式读取指定存储设备
第一种方式目前已弃用。 第二种方式适用于绝大多数情况。 第三种方式仅使用于读取FLASH存储设备或者读取SD卡设备Download文件夹。
const callback: OPLargerFileTrans.TransferTaskCallback = {
onError: (code) => {
//传输失败
}, onStart: () => {
//传输开始
}, onProgress: (progress, packectData) => {
//传输进度
}, onCancel: (code) => {
//传输取消, 0:主动取消1:丢包 2:crc错误
}, onSuccess: (data) => {
//传输成功
}
}
const sdCardBean = targetSDCardBean//目标存储设备,(簇号方式不需要)
const isFlash = sdCardBean.index == OPFile.SDCardBean.INDEX_FLASH
if(isFlash){
//flash用文件名方式
RCSPOpLargeFileGet.excuteGetTaskByFileNameAndDevHandle({
offset: 0,
sdCardBean: sdCardBean,
fileName: fileName,
callback: callback
})
}else{
//sd卡用簇号方式。Download文件夹可用文件名方式
RCSPOpLargeFileGet.excuteGetTaskByFileCluster({
offset: 0,
sdCardBean: sdCardBean,
cluster: cluster,
callback: getFileCallback
})
}
2.3.3 取消传输任务
RCSPOpLargeFileGet?.cancelGet()
2.4 表盘操作
表盘功能管理,包括:获取手表Flash资源文件列表,获取正在使用的表盘,添加手表Flash资源文件,设置当前表盘,获取表盘版本信息,获取表盘背景,设置自定义表盘背景,删除表盘,删除自定义表盘背景等等
在进行表盘操作之前需要先从 RcspOperateWrapper 中获取到 OPWatchDial.IOperateWatchDial 表盘操作对象。
const operateWrapper: RcspOperateWrapper | undefined = undefined;//此处需获取到真实的RcspOperateWrapper
//获取目录浏览操作对象
const RCSPOpWatchDial: OPWatchDial.IOperateWatchDial = operateWrapper?.getOperaterByClass(OPWatchDial.OperateWatchDial.prototype)
2.4.1 表盘操作事件回调
表盘事件通知,如:当前使用表盘变化,手表Flash资源文件列表发生变化
const callback: OPWatchDial.OperaterEventCallbackWatchDial = {
onEvent: (_event: OPWatchDial.OperaterEventWatchDial) => {
switch (_event.type) {
case 'NONE':
break;
case 'UseDialChange'://当前表盘变化
//当前使用的表盘
const usingDail = _event.UseDialChange?.dial
break;
case 'WatchResourseFileListChange'://手表Flash资源文件列表变化
//资源文件列表
const watchResourceList = _event.WatchResourseFileListChange?.fileList
break;
default:
break;
}
}
}
//注册回调
RCSPOpWatchDial?.registerEventCallback(callback)
//注销回调
RCSPOpWatchDial?.unregisterEventCallback(callback)
//移除全部回调
RCSPOpWatchDial?.removeAllEventCallback()
2.4.2 获取手表Flash资源文件列表
获取手表Flash资源文件列表默认是过滤了部分系统资源文件(例如:JL,FONT,SIDEBAR)。
可自定义修改过滤列表覆盖原有过滤列表。
const ignoreFileList: Array<string> = ["JL","FONT","SIDEBAR"];
RCSPOpWatchDial.setIgnoreFileList(ignoreFileList)
获取手表Flash资源文件列表(除系统资源文件:JL,FONT,SIDEBAR)
RCSPOpWatchDial.getWatchResourseFileList().then((res) => {
//资源文件列表
const watchResourceList = res
//表盘文件名字过滤前缀
const watchFilt = "WATCH"
//表盘背景文件名字过滤前缀
const bgpFilt = "BGP"
for (let index = 0; index < watchResourceList.length; index++) {
const element = watchResourceList[index];
if (element.getName()?.toUpperCase().includes(watchFilt)) {
//表盘文件
} else if(element.getName()?.toUpperCase().includes(bgpFilt)) {
//表盘背景文件
}
}
}).catch( error => {
//失败
})
重新获取手表Flash资源文件列表(除系统资源文件:JL,FONT,SIDEBAR)
RCSPOpWatchDial.reloadWatchResourseFileList().then((res) => {
//资源文件列表
const watchResourceList = res
//表盘文件名字过滤前缀
const watchFilt = "WATCH"
//表盘背景文件名字过滤前缀
const bgpFilt = "BGP"
for (let index = 0; index < watchResourceList.length; index++) {
const element = watchResourceList[index];
if (element.getName()?.toUpperCase().includes(watchFilt)) {
//表盘文件
} else if(element.getName()?.toUpperCase().includes(bgpFilt)) {
//表盘背景文件
}
}
}).catch( error => {
//失败
})
2.4.3 获取正在使用的表盘
先进行获取手表Flash资源文件列表(目录浏览),才可以获取到对应的正在使用表盘。
RCSPOpWatchDial?.getUsingDial().then((res) => {
//正在使用的表盘
const usingDial = res
}).catch((error) => {
//失败
console.error("当前使用表盘,失败:", error);
})
2.4.4 设置正在使用的表盘
设置切换表盘。目标表盘有自定义背景,会一起显示。
//目标表盘
const targetDial: OPDirectoryBrowse.File;
RCSPOpWatchDial?.setUsingDial(targetDial).then((res) => {
//设置成功
}).catch((error) => {
//失败
})
2.4.5 设置自定义表盘背景
设置自定义表盘背景,仅能设置当前正在使用表盘的自定义背景。
//目标自定义背景。undefined时,恢复为默认背景
const targetCustomBackground: OPDirectoryBrowse.File | undefined;//
RCSPOpWatchDial?.setDialCustomBackground(targetCustomBackground).then((res) => {
//设置成功
}).catch((error) => {
//失败
})
2.4.6 获取表盘的自定义背景
获取指定表盘的自定义背景
//目标表盘
const targetDial: OPDirectoryBrowse.File;
RCSPOpWatchDial?.getDialCustomBackground(targetDial).then((res)=>{
const customBackground: OPDirectoryBrowse.File | null = res
//自定义背景为null时,说明用的是默认背景
}).catch((error) => {
//失败
})
2.4.7 获取表盘版本信息
获取指定表盘的版本信息。应用端从表盘文件内获取表盘的版本信息,需要借用表盘文件解析库(jl_packResFormat.js)。
//目标表盘
const targetDial: OPDirectoryBrowse.File;
RCSPOpWatchDial?.getDialVersionInfo(targetDial).then((res)=>{
//版本信息:例如:W002,ECF2E7ED-6EC7-4B75-858B-87D2ECE6CA11
const versionString: string = res
}).catch((error) => {
//失败
})
2.4.8 添加手表Flash资源文件
添加资源文件(表盘文件和自定义背景)到手表的Flash存储设备。(这里的文件传输是基于大文件传输,只能一次传输一个文件)。 如何把png或jpg图片转换成bgp格式的自定义背景文件,请参考demo;
//传输回调
const transferCallback: OPLargerFileTrans.TransferTaskCallback = {
onError: (code: number) => {
//传输失败,code
},
onStart: () => {
//开始传输
},
onProgress: (progress: number) => {
//正在传输,进度
},
onSuccess: () => {
//传输成功
},
onCancel: (_code: number) => {
//传输取消
}
}
const data: ArrayBuffer;//文件数据
const fileName: string;//文件名,英文或数字
const lastModifyTime: number;//更新时间,文件覆盖更新判断依据
const refreshDirectory = true;//添加完成后,是否刷新目录
RCSPOpWatchDial?.addWatchResourseFile(data, fileName, lastModifyTime, refreshDirectory, transferCallback).then((res) => {
if (res instanceof OPDirectoryBrowse.File) {
//传输成功且刷新目录
//传输自定义背景完成后,设置为当前使用的自定义背景
//RCSPOpWatchDial?.setDialCustomBackground(res)
//传输表盘完成后,设置为当前使用的表盘
//RCSPOpWatchDial?.setUsingDial(res)
} else if (res == undefined) {
//传输完成后,目录浏览找不到该文件
//传输完成后找不到该文件,可能是文件名不符合标准,太长或者带中文
} else {
//传输成功且不刷新目录
//res是目录浏览的相对路径
}
}).catch((_error) => {
//失败
})
Important
添加资源文件后,需要调用 RCSPOpWatchDial?.setDialCustomBackground 或 RCSPOpWatchDial?.setUsingDial 设置使用。
取消添加手表Flash资源文件
//取消添加手表Flash资源文件
RCSPOpWatchDial?.cancelAddWatchResourseFile()
2.4.9 删除表盘
const targetDial: OPDirectoryBrowse.File
RCSPOpWatchDial?.deleteDial(targetDial).then(()=>{
//删除成功
}).catch((error)=>{
//失败
})
2.4.10 删除自定义表盘背景
const targetCustomBackground: OPDirectoryBrowse.File
RCSPOpWatchDial?.deleteDialCustomBackground(targetCustomBackground).then(()=>{
//删除成功
}).catch((error)=>{
//失败
})
2.4.11 如何从表盘文件中读取表盘的版本信息
jl_packResFormat_1.0.0.js 是用来解析表盘文件信息的库。
const packResFormat = new PackResFormat()
//文件名,如:watch1
const fileName = tempFilePaths[0].name
//获取文件名.json的数据
const fileInfoData = packResFormat.getFileData(uint8, fileName + '.json')
//Uint8Array 转 String
const fileInfo = Uint8ArrayToString(fileInfoData)
2.4.12 如何用JPG文件生成自定义背景文件
jl_bmpConvert_1.0.0.js是用来将RGB数组数据或ARGB数组数据转换成bgp自定义背景格式文件的库。
Important
制作自定义背景文件前,需要 通过 RCSPOpSystemInfo?.getSystemInfo() 获取到设备的屏幕的宽高。否则会出现大小尺寸不符
制作自定义背景的步骤总共分为3步:
第一步:将jpg图片加载到canvas A上,可对图片进行放大缩小偏移进行选取裁剪范围。这一步主要是负责选取要显示的范围,canvas A的宽高是不满足设备屏幕宽高,所以无法从canvas A获取到rgba数据。
第二步:canvas B的宽高与设备屏幕的宽高一致,在canvas B上对图片进行第一步中对应的放大缩小,然后获取canvas B的rgba数据(wx.canvasGetImageData) 。这一步是要在跟设备屏幕等宽高的canvas B中获取到足够像素点的rgba数据。
第三步:调用bmpConvert将gbra数据转换成bgp文件数据。
2.5 OTA升级
OTA升级包含单备份升级和双备份升级,单备份升级需要回连设备,双备份不需要回连设备。升级成功后,会重启设备。
OTA升级流程是通过 RcspOpImpl 进行OTA数据的传输。
2.5.1 升级过程回调
单备份设备需要在 onNeedReconnect 实现回连流程,回连成功后需要把新的 RcspOpImpl 更新给 RcspOTAManager,目前 RcspOpImpl 是一个对应一次蓝牙连接。
更新 RcspOpImpl 成功后,才能在回连后继续进行OTA的数据传输。
/** 升级流程的回调 */
interface OnUpgradeCallback {
/** OTA开始*/
onStartOTA(): void;
/**需要回连的回调
* <p>
* 注意: 1.仅连接通讯通道(BLE or SPP)
* 2.用于单备份OTA</p>
*
* @param reConnectMsg 回连设备信息
*/
onNeedReconnect(reConnectMsg: ReConnectMsg): void;
/** 进度回调
*
* @param type 类型
* @param progress 进度
*/
onProgress(type: UpgradeType, progress: number): void;
/** OTA结束*/
onStopOTA(): void;
/** OTA取消*/
onCancelOTA(): void;
/** OTA失败
* @param error 错误码
* @param message 错误信息
*/
onError(error: number, message: string): void;
}
2.5.2 开始升级
单备份升级需要扫描设备回连,需要监听扫描状态。这部分根据项目实际情况进行修改。
this.scanCallback = {
onScanFinish: () => {
//扫描结束,通知回连,回连会再次调用扫描
if (_Reconnect != null) {
_Reconnect.onScanStop()
}
},
onFound: (devs) => {
//发现设备,回调给回连
if (_Reconnect != null) {
for (let index = 0; index < devs.length; index++) {
const element: any = devs[index];
_Reconnect.onDiscoveryDevice(element.device)
}
}
}
}
RCSPBluetooth.bleScan.addCallback(this.scanCallback)
RCSPBluetooth.bleScan.removeCallback(this.scanCallback)
单备份升级回连成功后,监听RCSP协议库初始化状态获取到新的 RcspOpImpl 更新给 RcspOTAManager。
this.rcspWrapperEventCallback = {
onEvent: (_res) => {
//RCSP协议库初始化成功
if (_res.type == "onRcspInit" && _res.onRcspInitEvent) {//单备份升级会切换ble地址,导致rcspOpImpl变换
if (_res.onRcspInitEvent.isInit && _res.onRcspInitEvent.device.deviceId.toUpperCase() == this.reconnectingDeviceId.toUpperCase()) {
const bluetoothDevice = RCSPManager.getBluetoothDeviceByDeviceId(_res.onRcspInitEvent.device.deviceId)
if (bluetoothDevice) {//若多设备连接时,可以在这里判断deviceId是不是回连目标的deviceId
const rcspOperateWrapper = RCSPManager.getRcspOperateWrapper(bluetoothDevice)
const rcspOpImpl = rcspOperateWrapper?.getRcspOpImpl()
if (rcspOpImpl) {
_Reconnect?.onDeviceConnected(new Device(bluetoothDevice.deviceId,bluetoothDevice.name))
this.rcspOTAManager.updateRcspOpImpl(rcspOpImpl)
}
}
}
}
}
}
RCSPManager.observe(this.rcspWrapperEventCallback)
RCSPManager.removeObserve(this.rcspWrapperEventCallback)
开始执行OTA升级
const upgradeCallback :OnUpgradeCallback= {
onStartOTA: () => {
//开始升级
},
onNeedReconnect: (reConnectMsg: ReConnectMsg) => {
//单备份:传输Loader成功后,正在回连设备
//###实现回连,这一部分可以自己实现
const op: ReconnectOp = {
startScanDevice(): any {
//开始扫描设备
RCSPBluetooth.bleScan.startScan()
},
isReconnectDevice(scanDevice: WechatMiniprogram.BlueToothDevice): boolean {
//判断是不是回连设备
//判断条件:
//旧回连方式:根据deviceId进行回连
//新回连方式:根据蓝牙广播包中的mac地址进行回连
let result = false;
//获取设备的deviceId
const oldDevice = that.rcspOTAManager.getCurrentOTADevice()
//获取设备的Mac
const oldDeviceMac = that.rcspOTAManager.getCurrentOTADeviceMac()
if (oldDeviceMac != undefined) {
//跟设备交互后,根据App和设备情况决定是否走新回连方式
if (reConnectMsg.isSupportNewReconnectADV) {//使用新回连方式-广播包广播BLE地址
const advertisStr = ab2hex(scanDevice.advertisData).toUpperCase()
const index = advertisStr.indexOf("D60541544F4C4A");
if (index != -1) {
const unit8Array = new Uint8Array(scanDevice.advertisData)
const macArray = unit8Array.slice(index + 8, index + 14).reverse()
// console.log("新回连广播包 newMAC : " + ab2hex(macArray).toUpperCase())
result = oldDeviceMac.toUpperCase() == that.hex2Mac(macArray).toUpperCase()
}
// console.log("新回连广播包 oldMAC : " + oldDeviceMac + " scanMAC: " + scanDevice.deviceId + " result: " + result + " rawData: " + ab2hex(scanDevice.advertisData));
} else {//旧方式回连-匹配BLE地址
result = oldDevice!.deviceId == scanDevice.deviceId
// console.log("旧方式回连 : oldMAC: " + oldDevice!.deviceId + " scanMAC: " + scanDevice.deviceId + " result: " + result);
}
}
return result
},
connectDevice(device: WechatMiniprogram.BlueToothDevice): any {
//判断是目标设备后,开始连接设备
const deviceTemp = new BluetoothDevice()
deviceTemp.RSSI = device.RSSI
deviceTemp.advertisData = device.advertisData
deviceTemp.advertisServiceUUIDs = device.advertisServiceUUIDs
deviceTemp.connectable = device.connectable
deviceTemp.deviceId = device.deviceId
deviceTemp.localName = device.localName
deviceTemp.serviceData = device.serviceData
console.log(" 回连,连接设备:" + device.deviceId);
that.reconnectingDeviceId = device.deviceId
RCSPBluetooth.bleConnect.connectDevice(deviceTemp)
}
}
const callback: ReconnectCallback = {
onReconnectSuccess(device: Device) {
//回连设备成功
that.rcspOTAManager.updateOTADevice(device)
_Reconnect = null;
},
onReconnectFailed() {//不用处理,库里会自动超时
//Reconnect执行回连任务失败,不是sdk库回连失败
_Reconnect = null;
}
}
_Reconnect = new Reconnect(op, callback)
_Reconnect.startReconnect(OTAImpl.RECONNECT_DEVICE_TIMEOUT);
},
onProgress: (type: UpgradeType, progress: number) => {
let msg = type == UpgradeType.UPGRADE_TYPE_FIRMWARE ? '发送sdk升级数据' : '发送uboot升级数据'
this.setData({
otaProgressText: "正在" + msg + "...,进度:" + (new Number(progress).toFixed(2))
})
},
onStopOTA: () => {
//升级成功
this.rcspOTAManager.release()
//升级完成,释放设备
const connectedDeviceIds = RCSPBluetooth.bleConnect.getConnectedDeviceIds()
if (connectedDeviceIds != null) {
RCSPBluetooth.bleConnect.disconnect(connectedDeviceIds[0])
}
},
onCancelOTA: () => {
//升级取消,释放设备
const connectedDeviceIds = RCSPBluetooth.bleConnect.getConnectedDeviceIds()
if (connectedDeviceIds != null) {
RCSPBluetooth.bleConnect.disconnect(connectedDeviceIds[0])
}
this.rcspOTAManager.release()
},
onError: (error: number, message: string) => {
if (_Reconnect != null) {
_Reconnect.stopReconnect()
}
//升级失败,释放设备
const connectedDeviceIds = RCSPBluetooth.bleConnect.getConnectedDeviceIds()
if (connectedDeviceIds != null) {
RCSPBluetooth.bleConnect.disconnect(connectedDeviceIds[0])
}
this.rcspOTAManager.release()
}
}
const otaConfig: OTAConfig = new OTAConfig()
otaConfig.isSupportNewRebootWay = true//是否支持新回连方式,默认是支持。##新回连方式,会在单备份回连时,改变设备的BLE地址,用于处理一些手机蓝牙问题。
otaConfig.updateFileData = this.otaData//升级数据
//初始化RCSPOTAManager
this.rcspOTAManager = new RcspOTAManager(rcspOpImpl)
//开始升级
this.rcspOTAManager.startOTA(otaConfig, upgradeCallback)
取消OTA升级(仅双备份支持)
const rcspOpImpl = RCSPManager.getCurrentRcspOperateWrapper()?.wrapper.getRcspOpImpl()
const currentDevice = rcspOpImpl?.getUsingDevice()
if (currentDevice != null) {
//获取设备是不是双备份升级
if (rcspOpImpl?.getDeviceInfo(currentDevice)?.isSupportDoubleBackup == true) {
//取消双备份升级
this.rcspOTAManager.cancelOTA()
} else {
wx.showToast({ title: "单备份不支持" })
}
}
2.5.3 升级回连广播包
JLOTA字符的二进制数组为 D60541544F4C4A
0~4Bytes |
5Byte |
6 ~11Bytes |
12~13Bytes |
14~15Bytes |
16Byte |
17Byte |
18~26Bytes |
|---|---|---|---|---|---|---|---|
标识 |
版本 |
||||||
JLOTA |
0 |
原BLE地址 |
|||||
JLOTA |
1 |
原BLE地址 |
UID |
PID |
Bit7-4:Type,Bit3-0:Version |
电量 |
保留位 |