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

4 weeks ago
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;