|
|
@ -19,6 +19,8 @@ Page({
|
|
|
|
isDataComplete: false, // 判断数据是否上传完毕
|
|
|
|
isDataComplete: false, // 判断数据是否上传完毕
|
|
|
|
sleepList:[],
|
|
|
|
sleepList:[],
|
|
|
|
sleepCount:0,//获取睡眠的次数
|
|
|
|
sleepCount:0,//获取睡眠的次数
|
|
|
|
|
|
|
|
bloodOxygenCount:0,
|
|
|
|
|
|
|
|
deviceType:"Q2",// 设备型号
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 页面加载时触发的函数,用于初始化蓝牙适配器
|
|
|
|
// 页面加载时触发的函数,用于初始化蓝牙适配器
|
|
|
@ -42,7 +44,10 @@ Page({
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
handleContact (e) {
|
|
|
|
|
|
|
|
console.log(e.detail.path)
|
|
|
|
|
|
|
|
console.log(e.detail.query)
|
|
|
|
|
|
|
|
},
|
|
|
|
getLocation(){
|
|
|
|
getLocation(){
|
|
|
|
wx.getLocation({
|
|
|
|
wx.getLocation({
|
|
|
|
type: 'gcj02',
|
|
|
|
type: 'gcj02',
|
|
|
@ -72,7 +77,8 @@ Page({
|
|
|
|
getBluetoothDevices() {
|
|
|
|
getBluetoothDevices() {
|
|
|
|
wx.onBluetoothDeviceFound((res) => {
|
|
|
|
wx.onBluetoothDeviceFound((res) => {
|
|
|
|
// 过滤出名称以 "GJ-SH-01" 开头的设备
|
|
|
|
// 过滤出名称以 "GJ-SH-01" 开头的设备
|
|
|
|
const targetDevices = res.devices.filter(device => device.name && (device.name.startsWith("J2203") || device.name.startsWith("小柏")));
|
|
|
|
// const targetDevices = res.devices.filter(device => device.name && (device.name.startsWith("J2203") || device.name.startsWith("小柏") ||device.name.startsWith("T1")));
|
|
|
|
|
|
|
|
const targetDevices = res.devices.filter(device => device.name && (device.name.startsWith("2301B")));
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历并连接符合条件的设备
|
|
|
|
// 遍历并连接符合条件的设备
|
|
|
|
targetDevices.forEach(device => {
|
|
|
|
targetDevices.forEach(device => {
|
|
|
@ -83,7 +89,7 @@ Page({
|
|
|
|
// if(device.deviceId=="03:02:02:07:65:98"){
|
|
|
|
// if(device.deviceId=="03:02:02:07:65:98"){
|
|
|
|
// if(device.deviceId=="03:02:02:07:5C:B7"){
|
|
|
|
// if(device.deviceId=="03:02:02:07:5C:B7"){
|
|
|
|
// if(device.deviceId=="03:02:02:07:5E:C6"){
|
|
|
|
// if(device.deviceId=="03:02:02:07:5E:C6"){
|
|
|
|
if(device.deviceId=="03:02:02:07:62:3E"){
|
|
|
|
if(device.deviceId=="E2:B0:80:02:50:67"){
|
|
|
|
this.connectToDevice(device.deviceId);
|
|
|
|
this.connectToDevice(device.deviceId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
@ -169,8 +175,8 @@ Page({
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
wx.onBLECharacteristicValueChange((res) => {
|
|
|
|
wx.onBLECharacteristicValueChange((res) => {
|
|
|
|
// const hexStr = this.uint8ArrayToHex(new Uint8Array(res.value))
|
|
|
|
const hexStr = this.uint8ArrayToHex(new Uint8Array(res.value))
|
|
|
|
// console.log("收到手表端发送的数据>>>>>>:",hexStr);
|
|
|
|
console.log("收到手表端发送的数据>>>>>>:",hexStr);
|
|
|
|
const dataView = new DataView(res.value);
|
|
|
|
const dataView = new DataView(res.value);
|
|
|
|
// 获取第一个字节
|
|
|
|
// 获取第一个字节
|
|
|
|
let cmdType = dataView.getUint8(0);
|
|
|
|
let cmdType = dataView.getUint8(0);
|
|
|
@ -221,7 +227,7 @@ Page({
|
|
|
|
if(dataView.byteLength % 25 == 2){
|
|
|
|
if(dataView.byteLength % 25 == 2){
|
|
|
|
console.log("收到步数结束符号,最后一条指令【结束】",dataView.byteLength)
|
|
|
|
console.log("收到步数结束符号,最后一条指令【结束】",dataView.byteLength)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// console.log("步数详情数据:",stepDetailList);
|
|
|
|
console.log("步数详情数据:",stepDetailList);
|
|
|
|
// 调用后台接口 todo ....
|
|
|
|
// 调用后台接口 todo ....
|
|
|
|
// 更新缓存
|
|
|
|
// 更新缓存
|
|
|
|
HealthDataCache.updateStepData(stepDetailList[0].startTime);
|
|
|
|
HealthDataCache.updateStepData(stepDetailList[0].startTime);
|
|
|
@ -246,8 +252,14 @@ Page({
|
|
|
|
let parseBloodList = ParseDataPacket.parseBloodPressureList(dataView,0);
|
|
|
|
let parseBloodList = ParseDataPacket.parseBloodPressureList(dataView,0);
|
|
|
|
if(dataView.byteLength % 15 == 2){
|
|
|
|
if(dataView.byteLength % 15 == 2){
|
|
|
|
console.log("收到血压结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
console.log("收到血压结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
|
|
|
|
if(this.data.deviceType.includes('T1') || this.data.deviceType.includes('J2203')){
|
|
|
|
// 需要调用下一个指令(体温)
|
|
|
|
// 需要调用下一个指令(体温)
|
|
|
|
this.sendHealthCommand(0x65)
|
|
|
|
// this.sendHealthCommand(0x65)
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
// 戒指
|
|
|
|
|
|
|
|
this.sendHealthCommand(0x62)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// console.log("血压数据:",parseBloodList);
|
|
|
|
// console.log("血压数据:",parseBloodList);
|
|
|
|
// 调用后台接口 todo ....
|
|
|
|
// 调用后台接口 todo ....
|
|
|
@ -265,14 +277,27 @@ Page({
|
|
|
|
console.log("收到血氧结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
console.log("收到血氧结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
// 需要调用下一个指令(血压)
|
|
|
|
// 需要调用下一个指令(血压)
|
|
|
|
this.sendHealthCommand(0x56)
|
|
|
|
this.sendHealthCommand(0x56)
|
|
|
|
|
|
|
|
// 由于是Q2戒指,没有血压,故而这里获取下一个数据应该是体温
|
|
|
|
|
|
|
|
// 清空获取血氧的次数
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
|
|
|
bloodOxygenCount : 0
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// console.log("血氧数据:",bloodOxygenList);
|
|
|
|
// 累加血氧获取次数
|
|
|
|
|
|
|
|
this.setData({
|
|
|
|
|
|
|
|
bloodOxygenCount : this.data.bloodOxygenCount + 1
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
if(this.data.bloodOxygenCount % 50 == 0){
|
|
|
|
|
|
|
|
// 需要调用下一个指令(血压)
|
|
|
|
|
|
|
|
this.sendHealthCommand(0x56,2)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log("血氧数据:",bloodOxygenList);
|
|
|
|
// 调用后台接口 todo ....
|
|
|
|
// 调用后台接口 todo ....
|
|
|
|
|
|
|
|
|
|
|
|
// 更新缓存
|
|
|
|
// 更新缓存
|
|
|
|
HealthDataCache.updateBloodOxygenData(bloodOxygenList[0].time);
|
|
|
|
HealthDataCache.updateBloodOxygenData(bloodOxygenList[0].time);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 0x65:
|
|
|
|
case 0x65,0x62:
|
|
|
|
let tempList = ParseDataPacket.parseTempList(dataView,0);
|
|
|
|
let tempList = ParseDataPacket.parseTempList(dataView,0);
|
|
|
|
if(dataView.byteLength % 11 == 2){
|
|
|
|
if(dataView.byteLength % 11 == 2){
|
|
|
|
console.log("收到体温结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
console.log("收到体温结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
@ -281,7 +306,7 @@ Page({
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 目前返回的包括环境温度,后续固件改成只采集佩戴情况下的体温,暂时过滤掉 oneValue 小于 35.0 的数据
|
|
|
|
// 目前返回的包括环境温度,后续固件改成只采集佩戴情况下的体温,暂时过滤掉 oneValue 小于 35.0 的数据
|
|
|
|
const filteredData = tempList.filter(item => item.oneValue >= 35.0);
|
|
|
|
const filteredData = tempList.filter(item => item.oneValue >= 35.0);
|
|
|
|
// console.log("体温数据:",tempList);
|
|
|
|
console.log("体温数据:",tempList);
|
|
|
|
|
|
|
|
|
|
|
|
if(filteredData.length > 0){
|
|
|
|
if(filteredData.length > 0){
|
|
|
|
// todo 调用后端接口
|
|
|
|
// todo 调用后端接口
|
|
|
@ -295,8 +320,8 @@ Page({
|
|
|
|
// 睡眠数据结束符
|
|
|
|
// 睡眠数据结束符
|
|
|
|
if(dataView.byteLength == 2 && id == 0xff){
|
|
|
|
if(dataView.byteLength == 2 && id == 0xff){
|
|
|
|
console.log("收到睡眠结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
console.log("收到睡眠结束符号>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>",dataView.byteLength)
|
|
|
|
// 需要调用下一个指令(体温)
|
|
|
|
// 需要调用下一个指令(步数)
|
|
|
|
// this.sendHealthCommand(0x52)
|
|
|
|
this.sendHealthCommand(0x52)
|
|
|
|
|
|
|
|
|
|
|
|
let list = this.data.sleepList;
|
|
|
|
let list = this.data.sleepList;
|
|
|
|
if(list.length > 0){
|
|
|
|
if(list.length > 0){
|
|
|
@ -318,7 +343,7 @@ Page({
|
|
|
|
// let data = CommonUtil.removeBeforeDate(result,lastDate);
|
|
|
|
// let data = CommonUtil.removeBeforeDate(result,lastDate);
|
|
|
|
// console.log("data:",data)
|
|
|
|
// console.log("data:",data)
|
|
|
|
// 更新缓存
|
|
|
|
// 更新缓存
|
|
|
|
HealthDataCache.updateSleepData(result[result.length-1].date);
|
|
|
|
// HealthDataCache.updateSleepData(result[result.length-1].date);
|
|
|
|
// 调用接口传睡眠后,清空数据
|
|
|
|
// 调用接口传睡眠后,清空数据
|
|
|
|
this.setData({
|
|
|
|
this.setData({
|
|
|
|
sleepList: []
|
|
|
|
sleepList: []
|
|
|
@ -336,9 +361,9 @@ Page({
|
|
|
|
sleepList.sort((a, b) => {
|
|
|
|
sleepList.sort((a, b) => {
|
|
|
|
return Number(a.minute) - Number(b.minute); // b.minute > a.minute 排前面
|
|
|
|
return Number(a.minute) - Number(b.minute); // b.minute > a.minute 排前面
|
|
|
|
});
|
|
|
|
});
|
|
|
|
if(sleepList.length > 0){
|
|
|
|
// if(sleepList.length > 0){
|
|
|
|
console.log("日期打印:",sleepList[0].yyyyMMdd)
|
|
|
|
// console.log("日期打印:",sleepList[0].yyyyMMdd)
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
// console.log("排序后数据:",sleepList)
|
|
|
|
// console.log("排序后数据:",sleepList)
|
|
|
|
// 获取当前已存在的 sleepList(如果有)
|
|
|
|
// 获取当前已存在的 sleepList(如果有)
|
|
|
|
let currentSleepList = this.data.sleepList || [];
|
|
|
|
let currentSleepList = this.data.sleepList || [];
|
|
|
@ -361,9 +386,7 @@ Page({
|
|
|
|
// console.log("继续获取睡眠数据:",sleepPacket)
|
|
|
|
// console.log("继续获取睡眠数据:",sleepPacket)
|
|
|
|
// console.log("sleepCount:",this.data.sleepCount)
|
|
|
|
// console.log("sleepCount:",this.data.sleepCount)
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,sleepPacket);
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,sleepPacket);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
@ -385,25 +408,50 @@ Page({
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
timeTest(){
|
|
|
|
timeTest(){
|
|
|
|
let dateRange = CommonUtil.getDayTimeRange("20250119",91);
|
|
|
|
// let dateRange = CommonUtil.getDayTimeRange("20250119",91);
|
|
|
|
console.log("dateRange",dateRange);
|
|
|
|
// console.log("dateRange",dateRange);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// let time = CommonUtil.getTimestampFromDateAndMinutes("20250119",91);
|
|
|
|
|
|
|
|
// console.log("time",time);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// console.log("对比结果:",time >= dateRange.startTimestamp && time <= dateRange.endTimestamp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 准备测试数据
|
|
|
|
|
|
|
|
const hexData = [0x65, 0x9C, 0x02, 0x25, 0x06, 0x19, 0x00, 0x31, 0x00, 0x7C, 0x01];
|
|
|
|
|
|
|
|
const buffer = new ArrayBuffer(hexData.length);
|
|
|
|
|
|
|
|
const dataView = new DataView(buffer);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 将16进制数据写入DataView
|
|
|
|
|
|
|
|
hexData.forEach((value, index) => {
|
|
|
|
|
|
|
|
dataView.setUint8(index, value);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var data = ParseDataPacket.parseTempList(dataView,0);
|
|
|
|
|
|
|
|
// console.log("data:"+data)
|
|
|
|
|
|
|
|
// // 解析体温值(T1 和 T2)
|
|
|
|
|
|
|
|
// const t1 = 380; // 小端数据,保留一位小数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 计算温度值,T1T2组合成一个16位数字,并转为浮动的值
|
|
|
|
|
|
|
|
// const temperature = t1 / 10.0; // 保留1位小数
|
|
|
|
|
|
|
|
// const temperatureStr = temperature.toString();
|
|
|
|
|
|
|
|
// // 分割整数和小数部分
|
|
|
|
|
|
|
|
// const [integerPart, decimalPart] = temperatureStr.split('.');
|
|
|
|
|
|
|
|
// var oneValue = parseInt(integerPart, 10); // 获取整数部分
|
|
|
|
|
|
|
|
// var twoValue = decimalPart ? decimalPart.charAt(0) : '0';
|
|
|
|
|
|
|
|
// console.log(oneValue)//38
|
|
|
|
|
|
|
|
// console.log(twoValue)//0
|
|
|
|
|
|
|
|
|
|
|
|
let time = CommonUtil.getTimestampFromDateAndMinutes("20250119",91);
|
|
|
|
|
|
|
|
console.log("time",time);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log("对比结果:",time >= dateRange.startTimestamp && time <= dateRange.endTimestamp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
getPower(){
|
|
|
|
getPower(){
|
|
|
|
// 获取电量
|
|
|
|
// 获取电量
|
|
|
|
// const batteryPacket = DataPacket.generateBatteryCheckPacket();
|
|
|
|
const batteryPacket = DataPacket.generateBatteryCheckPacket();
|
|
|
|
// SendCommandUtil.sendPackage(this.data.deviceId,batteryPacket);
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,batteryPacket);
|
|
|
|
|
|
|
|
},
|
|
|
|
// const HRPacket = DataPacket.generateReadHRDataPacket();
|
|
|
|
getSleep(){
|
|
|
|
// SendCommandUtil.sendPackage(this.data.deviceId,HRPacket);
|
|
|
|
// 睡眠数据
|
|
|
|
|
|
|
|
|
|
|
|
this.sendHealthCommand(0x53)
|
|
|
|
this.sendHealthCommand(0x53)
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
getStep(){
|
|
|
|
getStep(){
|
|
|
@ -412,7 +460,12 @@ Page({
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,stepPacket);
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,stepPacket);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
sendHealthCommand(cmd){
|
|
|
|
/**
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param {*} cmd
|
|
|
|
|
|
|
|
* @param {获取数据标识,1:读取历史数据,2:继续从上一次的位置读取数据} flag
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
sendHealthCommand(cmd,flag = 1){
|
|
|
|
// 如果不传则默认发送心率
|
|
|
|
// 如果不传则默认发送心率
|
|
|
|
if(cmd == null){
|
|
|
|
if(cmd == null){
|
|
|
|
cmd = 0x55;
|
|
|
|
cmd = 0x55;
|
|
|
@ -442,7 +495,7 @@ Page({
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let packet = DataPacket.generateReadDataPacket(cmd);
|
|
|
|
let packet = DataPacket.generateReadDataPacket(cmd,flag);
|
|
|
|
console.log("发送指令到设备端>>>:", this.uint8ArrayToHex(packet));
|
|
|
|
console.log("发送指令到设备端>>>:", this.uint8ArrayToHex(packet));
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId, packet);
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId, packet);
|
|
|
|
},
|
|
|
|
},
|
|
|
@ -474,33 +527,32 @@ Page({
|
|
|
|
getHealthData(){
|
|
|
|
getHealthData(){
|
|
|
|
|
|
|
|
|
|
|
|
// 睡眠数据
|
|
|
|
// 睡眠数据
|
|
|
|
const sleepPacket = DataPacket.generateReadSleepDataPacket();
|
|
|
|
// const sleepPacket = DataPacket.generateReadSleepDataPacket();
|
|
|
|
SendCommandUtil.sendPackage(this.data.deviceId,sleepPacket);
|
|
|
|
// SendCommandUtil.sendPackage(this.data.deviceId,sleepPacket);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前时间
|
|
|
|
// 获取当前时间
|
|
|
|
// let currentTime = Math.floor(new Date().getTime() / 1000);
|
|
|
|
let currentTime = Math.floor(new Date().getTime() / 1000);
|
|
|
|
// // 如果是第一次调用,则不判断时间,如果是第二次调用,要判断本次调用与上次是否间隔18秒(因手表机制问题,不能频繁发送指令给手表端)
|
|
|
|
// 如果是第一次调用,则不判断时间,如果是第二次调用,要判断本次调用与上次是否间隔18秒(因手表机制问题,不能频繁发送指令给手表端)
|
|
|
|
// console.log(this.data.initTime,this.data.initIndex)
|
|
|
|
console.log(this.data.initTime,this.data.initIndex)
|
|
|
|
// if(this.data.initIndex === 0){
|
|
|
|
if(this.data.initIndex === 0){
|
|
|
|
// this.sendHealthCommand();
|
|
|
|
this.sendHealthCommand();
|
|
|
|
// this.setData({
|
|
|
|
this.setData({
|
|
|
|
// initIndex: this.data.initIndex + 1,
|
|
|
|
initIndex: this.data.initIndex + 1,
|
|
|
|
// initTime: currentTime
|
|
|
|
initTime: currentTime
|
|
|
|
// })
|
|
|
|
})
|
|
|
|
// } else {
|
|
|
|
} else {
|
|
|
|
// // 初始时间
|
|
|
|
// 初始时间
|
|
|
|
// let initTime = this.data.initTime
|
|
|
|
let initTime = this.data.initTime
|
|
|
|
// let timeDifference = currentTime - initTime;
|
|
|
|
let timeDifference = currentTime - initTime;
|
|
|
|
// if(this.data.initIndex > 0 && timeDifference > 18){
|
|
|
|
if(this.data.initIndex > 0 && timeDifference > 18){
|
|
|
|
// console.log(timeDifference,this.data.initIndex)
|
|
|
|
console.log(timeDifference,this.data.initIndex)
|
|
|
|
// this.setData({
|
|
|
|
this.setData({
|
|
|
|
// initIndex: this.data.initIndex + 1,
|
|
|
|
initIndex: this.data.initIndex + 1,
|
|
|
|
// initTime: currentTime
|
|
|
|
initTime: currentTime
|
|
|
|
// })
|
|
|
|
})
|
|
|
|
// this.sendHealthCommand();
|
|
|
|
this.sendHealthCommand();
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|