You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

280 lines
8.4 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import DayConverter from './DayConverter';
import CommonUtil from './CommonUtil.js';
const ET620ParseDataUtil = {
/**
* @returns {Array} data 心率或血氧数据
* @param {Array} data 手表SDK数据
* @param {Number} dataType 数据类型 1-心率 3-血氧
*/
parseHeartRateOrBloodOxygenData(data,dataType){
if (!Array.isArray(data)) {
console.error('输入数据必须是数组');
return [];
}
return ET620ParseDataUtil.get10MinuteAverage(data,dataType);
},
/**
*
* @param {Array} data 手表SDK数据
*/
parseBodyTemperatureData(data){
if (!Array.isArray(data)) {
console.error('输入数据必须是数组');
return [];
}
return data
.filter(item => this.isValidBodyTemperature(item.bodyTemperature))
.map(item => this.formatBodyTemperatureItem(item));
},
parseBloodPressureData(data){
if (!Array.isArray(data)) {
console.error('输入数据必须是数组');
return [];
}
return data
.filter(item => this.isValidBloodPressure(item.bloodPressure))
.map(item => this.formatBloodPressureItem(item));
},
/**
* @returns {Object} 返回血压数据
* @param {Number} bp
*/
isValidBloodPressure(bp) {
return bp &&
bp.bloodPressureHigh > 0 &&
bp.bloodPressureLow > 0;
},
/**
* @returns {Object} 返回体温数据
* @param {} bt
*/
isValidBodyTemperature(bt) {
return bt &&
bt.bodyTemperature > 0.0 ;
},
/**
* @returns {Object} 返回体温数据
* @param {Number} bt
*/
isValidStep(step){
return step &&
step.stepCount > 0 ;
},
/**
* 组装体温数据
* @param {Object} item
*/
formatBodyTemperatureItem(item) {
let timestamp = this.getTimestamp(item.date)
// const tempStr = item.bodyTemperature.bodyTemperature.toFixed(1); // "36.0"
const [integerPart, decimalPart] = item.bodyTemperature.bodyTemperature.split('.');
return {
type: 4, // 4.体温
oneValue: integerPart, // 体温整数部分
twoValue: decimalPart, // 体温小数部分
time: timestamp, // 11位时间戳
stringTime: item.date
};
},
/**
* 组装步数数据
* @param {Object} item
*/
formatStepItem(item,name,userId) {
let timestamp = this.getTimestamp(item.date)
return {
calories: item.step.calorie,
distance: item.step.distance,
step: item.step.stepCount,
startTime: timestamp - (5 * 60), // 开始时间
endTime: timestamp, // 结束时间
createBy: name, // 体温整数部分
createTime: timestamp * 1000, // 体温小数部分
updateTime: timestamp * 1000, // 11位时间戳
userId: userId
};
},
/**
* 组装血压数据
* @param {Object} item
*/
formatBloodPressureItem(item) {
let timestamp = this.getTimestamp(item.date)
return {
type: 2, // 2.血压
oneValue: item.bloodPressure.bloodPressureHigh, // 高压
twoValue: item.bloodPressure.bloodPressureLow, // 低压
time: timestamp, // 11位时间戳
stringTime: item.date // 直接使用原始date字段
};
},
/**
* 解析心率、血氧数据心率和血氧开启科学睡眠后是1分钟采集1次按每10分钟分组并计算平均值传给后端
* @param {Array} data - 心率数据数组,格式为[{date: "YYYY-MM-DD-HH-mm", pulseReat: [number...]}, ...]
* @returns {Array} - 分组后的结果数组
*/
get10MinuteAverage(data,dataType) {
// 1. 创建用于分组的Map
const groupMap = new Map();
// 2. 遍历每个数据点
for (const item of data) {
// 解析日期和时间
const [year, month, day, hour, minute] = item.date.split('-').map(Number);
// 计算分组标识 (每10分钟一组)
const groupMinute = Math.floor(minute / 10) * 10;
const groupKey = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}-${hour.toString().padStart(2, '0')}-${groupMinute.toString().padStart(2, '0')}`;
// 提取有效值(大于0) SDK文档有心率字段实际没有心率字段与厂家确认使用脉率 pulseReat
const validPulseRates = dataType== 1 ? item.pulseReat.filter(rate => rate > 0) : item.bloodOxygen.oxygens.filter(rate => rate > 0);
// 添加到分组
if (!groupMap.has(groupKey)) {
groupMap.set(groupKey, []);
}
groupMap.get(groupKey).push(...validPulseRates);
}
// 3. 计算每个分组的平均值
const result = [];
groupMap.forEach((values, groupKey) => {
let average = 0;
if (values.length > 0) {
// 计算平均值
const sum = values.reduce((total, rate) => total + rate, 0);
average = Math.round(sum / values.length);// 四舍五入
}
let timestamp = this.getTimestamp(groupKey)
if(average > 0){
result.push({
type: dataType, // 1.心率 , 3.血氧
oneValue: average,
time: timestamp, // 11位时间戳
stringTime: groupKey // 直接使用原始date字段
});
}
});
// 4. 按时间排序(可选)
result.sort((a, b) => a.stringTime.localeCompare(b.stringTime));
return result;
},
/**
* 步数详情
* @param {Array} data 手表SDK数据
* @param {string} name 用户姓名
* @param {Number} userId
*/
parseStepData(data,name,userId){
if (!Array.isArray(data)) {
console.error('输入数据必须是数组');
return [];
}
return data
.filter(item => this.isValidStep(item.step))
.map(item => this.formatStepItem(item,name,userId));
},
/**
* 获取11位的时间戳
* @param {Long} date
*/
getTimestamp(date){
const dateParts = date.split('-');
const year = dateParts[0];
const month = dateParts[1];
const day = dateParts[2];
const hour = dateParts[3];
const minute = dateParts[4];
// 创建Date对象并获取时间戳
const dateObj = new Date(
`${year}-${month}-${day}T${hour}:${minute}:00`
);
// 单独 +5 分钟:使数据的时间更接近现在的时刻
const timestamp = (dateObj.getTime() / 1000) + (5 * 60);
return timestamp;
},
/**
* 解析睡眠数据
* @param {*} data 睡眠数据
* @param {*} sleepFlagBitType 1-精准睡眠, 2-无睡眠 4-精准睡眠数据结构与1一致曲线修改为2bit表示其他-普通睡眠
*/
parseSleepData(data,sleepFlagBitType){
let sleepList = [];
console.log("data.content ===============================================",data.content)
data.content.forEach(item=>{
// 睡眠片段的入睡时间13位时间戳
let startTimestamp = DayConverter.formatFullDateTime(item.fallAsleepTime);
// 计算睡眠曲线
const timeline = item.sleepCurve.map((state, index) => {
const currentTimestamp = startTimestamp + index * 60000; // 每分钟 +60,000ms
/**
* 睡眠一个字符代表1分钟时长0深睡1浅睡2快速眼动3失眠4苏醒
*/
// console.log(`minute: ${DayConverter.getMinuteOfDay(currentTimestamp)}, status: ${state}, yyyyMMdd: ${DayConverter.formatDate(currentTimestamp)}`);
return {
yyyyMMdd: DayConverter.formatDate(currentTimestamp),
timestamp: currentTimestamp,
minute: DayConverter.getMinuteOfDay(currentTimestamp),
status: this.et620SleepStatus(state)
};
});
sleepList.push(...timeline);
})
console.log("解析后格式:",sleepList.length)
let completeList = CommonUtil.completeMissingData(sleepList);
console.log("完整的数据:",completeList.length)
// 组装数据
let arr = CommonUtil.assembleSleepData(completeList);
arr[0].date = DayConverter.getDateByDayName(data.readDay);
console.log("睡眠日期:",DayConverter.getDateByDayName(data.readDay))
return arr;
},
/**
* 普通睡眠
*/
calculateNormalSleep(){
},
/**
* 精准睡眠
*/
calculatePreciseSleep(){
},
/**
* 0深睡1浅睡2快速眼动3失眠4苏醒
* @param {*} status
*/
et620SleepStatus(status) {
switch (status) {
case 0:
return 2; // 深睡
case 1:
return 1; // 浅睡
case 2:
return 4; // 快速眼动
default:
return 3; // 清醒
}
},
}
// 导出模块
module.exports = ET620ParseDataUtil;