package com.jsbogang.womp.health;
|
|
import android.content.Context;
|
import android.os.Handler;
|
import android.os.Looper;
|
import android.util.Log;
|
|
import com.jsbogang.womp.ble.AlarmPrefs;
|
import com.jsbogang.womp.ble.DevicePrefs;
|
import com.jsbogang.womp.ble.VeepooBleManager;
|
import com.veepoo.protocol.VPOperateManager;
|
import com.veepoo.protocol.listener.base.IBleWriteResponse;
|
import com.veepoo.protocol.listener.data.AbsBloodGlucoseChangeListener;
|
import com.veepoo.protocol.listener.data.IBPDetectDataListener;
|
import com.veepoo.protocol.listener.data.IHeartDataListener;
|
import com.veepoo.protocol.listener.data.ISpo2hDataListener;
|
import com.veepoo.protocol.model.datas.BpData;
|
import com.veepoo.protocol.model.datas.HeartData;
|
import com.veepoo.protocol.model.datas.Spo2hData;
|
import com.veepoo.protocol.model.enums.EBPDetectModel;
|
import com.veepoo.protocol.model.enums.EBPDetectStatus;
|
import com.veepoo.protocol.model.enums.EBloodGlucoseRiskLevel;
|
import com.veepoo.protocol.model.enums.EBloodGlucoseStatus;
|
import com.veepoo.protocol.model.enums.EHeartStatus;
|
|
/**
|
* 每 30 分钟主动触发心率/血压/血氧/血糖测量。手表端用户自行测量时设备会 busy,此时跳过本轮。
|
*/
|
public final class ActiveMeasureRunner {
|
private static final String TAG = "ActiveMeasure";
|
private static final ActiveMeasureRunner INSTANCE = new ActiveMeasureRunner();
|
private static final IBleWriteResponse WR = code -> {};
|
|
private Context app;
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
private volatile boolean measuring;
|
private int lastHr;
|
private int lastBpHigh;
|
private int lastBpLow;
|
private float lastSpo2;
|
private float lastGlucose;
|
private Runnable timeoutTask;
|
|
private ActiveMeasureRunner() {}
|
|
public static ActiveMeasureRunner get() {
|
return INSTANCE;
|
}
|
|
public void attach(Context context) {
|
app = context.getApplicationContext();
|
}
|
|
public boolean isMeasuring() {
|
return measuring;
|
}
|
|
public void startCycle() {
|
if (app == null || measuring || !VeepooBleManager.get().isReady()) {
|
return;
|
}
|
if (HealthSyncer.get().isSyncing()) {
|
handler.postDelayed(this::startCycle, 15_000);
|
return;
|
}
|
measuring = true;
|
lastHr = 0;
|
lastBpHigh = 0;
|
lastBpLow = 0;
|
lastSpo2 = 0;
|
lastGlucose = 0;
|
startHeart();
|
}
|
|
private void startHeart() {
|
HealthSyncer.get().notifyUi("正在测量心率…");
|
try {
|
VPOperateManager.getInstance().startDetectHeart(WR, new IHeartDataListener() {
|
@Override
|
public void onDataChange(HeartData heart) {
|
if (heart == null) {
|
return;
|
}
|
if (heart.getHeartStatus() == EHeartStatus.STATE_HEART_BUSY) {
|
abortBusy();
|
return;
|
}
|
int v = heart.getData();
|
if (v >= 30 && v <= 220) {
|
lastHr = v;
|
}
|
}
|
});
|
} catch (Exception e) {
|
Log.w(TAG, "start heart fail", e);
|
handler.postDelayed(this::startBp, 800);
|
return;
|
}
|
armTimeout(() -> {
|
stopHeart();
|
if (lastHr > 0) {
|
saveMetric(lastHr, 0, 0, 0, 0);
|
}
|
startBp();
|
}, 25_000);
|
}
|
|
private void stopHeart() {
|
try {
|
VPOperateManager.getInstance().stopDetectHeart(WR);
|
} catch (Exception ignored) {
|
}
|
}
|
|
private void startBp() {
|
if (!stillOn()) {
|
return;
|
}
|
HealthSyncer.get().notifyUi("正在测量血压…");
|
try {
|
VPOperateManager.getInstance().startDetectBP(WR, new IBPDetectDataListener() {
|
@Override
|
public void onDataChange(BpData bpData) {
|
if (bpData == null) {
|
return;
|
}
|
if (bpData.getStatus() == EBPDetectStatus.STATE_BP_BUSY) {
|
abortBusy();
|
return;
|
}
|
if (bpData.getHighPressure() > 0 && bpData.getLowPressure() > 0) {
|
lastBpHigh = bpData.getHighPressure();
|
lastBpLow = bpData.getLowPressure();
|
}
|
if (bpData.getProgress() >= 100 && lastBpHigh > 0) {
|
finishBp();
|
}
|
}
|
}, EBPDetectModel.DETECT_MODEL_PUBLIC);
|
} catch (Exception e) {
|
Log.w(TAG, "start bp fail", e);
|
handler.postDelayed(this::startSpo2, 800);
|
return;
|
}
|
armTimeout(this::finishBp, 55_000);
|
}
|
|
private void finishBp() {
|
clearTimeout();
|
try {
|
VPOperateManager.getInstance().stopDetectBP(WR, EBPDetectModel.DETECT_MODEL_PUBLIC);
|
} catch (Exception ignored) {
|
}
|
if (lastBpHigh > 0) {
|
saveMetric(0, lastBpHigh, lastBpLow, 0, 0);
|
}
|
handler.postDelayed(this::startSpo2, 1200);
|
}
|
|
private void startSpo2() {
|
if (!stillOn()) {
|
return;
|
}
|
HealthSyncer.get().notifyUi("正在测量血氧…");
|
try {
|
VPOperateManager.getInstance().startDetectSPO2H(WR, new ISpo2hDataListener() {
|
@Override
|
public void onSpO2HADataChange(Spo2hData data) {
|
if (data == null) {
|
return;
|
}
|
int v = data.getValue();
|
if (v >= 70 && v <= 100) {
|
lastSpo2 = v;
|
}
|
if (data.getCheckingProgress() >= 100 && lastSpo2 > 0) {
|
finishSpo2();
|
}
|
}
|
});
|
} catch (Exception e) {
|
Log.w(TAG, "start spo2 fail", e);
|
handler.postDelayed(this::startGlucose, 800);
|
return;
|
}
|
armTimeout(this::finishSpo2, 50_000);
|
}
|
|
private void finishSpo2() {
|
clearTimeout();
|
try {
|
VPOperateManager.getInstance().stopDetectSPO2H(WR, data -> {});
|
} catch (Exception ignored) {
|
}
|
if (lastSpo2 > 0) {
|
saveMetric(0, 0, 0, lastSpo2, 0);
|
}
|
handler.postDelayed(this::startGlucose, 1200);
|
}
|
|
private void startGlucose() {
|
if (!stillOn()) {
|
return;
|
}
|
HealthSyncer.get().notifyUi("正在测量血糖…");
|
try {
|
VPOperateManager.getInstance().startBloodGlucoseDetect(WR, new AbsBloodGlucoseChangeListener() {
|
@Override
|
public void onDetectError(int opt, EBloodGlucoseStatus status) {
|
finishGlucose();
|
}
|
|
@Override
|
public void onBloodGlucoseDetect(int progress, float bloodGlucose, EBloodGlucoseRiskLevel level) {
|
if (bloodGlucose > 0) {
|
lastGlucose = bloodGlucose;
|
}
|
if (progress >= 100) {
|
finishGlucose();
|
}
|
}
|
|
@Override
|
public void onBloodGlucoseStopDetect() {
|
finishGlucose();
|
}
|
});
|
} catch (Exception e) {
|
Log.w(TAG, "start glucose fail", e);
|
done();
|
return;
|
}
|
armTimeout(this::finishGlucose, 45_000);
|
}
|
|
private void finishGlucose() {
|
clearTimeout();
|
try {
|
VPOperateManager.getInstance().stopBloodGlucoseDetect(WR, new AbsBloodGlucoseChangeListener() {});
|
} catch (Exception ignored) {
|
}
|
if (lastGlucose > 0) {
|
saveMetric(0, 0, 0, 0, lastGlucose);
|
}
|
done();
|
}
|
|
private void abortBusy() {
|
clearTimeout();
|
stopHeart();
|
try {
|
VPOperateManager.getInstance().stopDetectBP(WR, EBPDetectModel.DETECT_MODEL_PUBLIC);
|
} catch (Exception ignored) {
|
}
|
try {
|
VPOperateManager.getInstance().stopDetectSPO2H(WR, data -> {});
|
} catch (Exception ignored) {
|
}
|
measuring = false;
|
HealthSyncer.get().syncSleep();
|
HealthSyncer.get().notifyUi("手表正忙,本次测量已跳过");
|
}
|
|
private void done() {
|
clearTimeout();
|
measuring = false;
|
HealthUploader.get().flush();
|
HealthSyncer.get().syncSleep();
|
HealthSyncer.get().notifyUi("测量完成");
|
}
|
|
private boolean stillOn() {
|
if (!measuring) {
|
return false;
|
}
|
if (!VeepooBleManager.get().isReady()) {
|
measuring = false;
|
return false;
|
}
|
return true;
|
}
|
|
private void armTimeout(Runnable r, long ms) {
|
clearTimeout();
|
timeoutTask = r;
|
handler.postDelayed(timeoutTask, ms);
|
}
|
|
private void clearTimeout() {
|
if (timeoutTask != null) {
|
handler.removeCallbacks(timeoutTask);
|
timeoutTask = null;
|
}
|
}
|
|
private void saveMetric(int hr, int bpHigh, int bpLow, float spo2, float glucose) {
|
if (app == null || AppDatabase.get() == null || !DevicePrefs.isBound(app)) {
|
return;
|
}
|
HealthSample s = new HealthSample();
|
s.timestamp = System.currentTimeMillis();
|
s.deviceMac = DevicePrefs.getMac(app);
|
s.dataType = "周期";
|
s.uploaded = 0;
|
s.stepValue = 0;
|
s.heartRate = hr;
|
s.bpHigh = bpHigh;
|
s.bpLow = bpLow;
|
s.spo2 = spo2;
|
s.glucose = glucose;
|
if (AlarmPrefs.isAbnormal(app, hr, bpHigh, bpLow, spo2)) {
|
s.dataType = "异常";
|
}
|
AppDatabase.dao().upsertSample(s);
|
HealthSyncer.get().notifyUi("测量数据已更新");
|
}
|
}
|