From 9ff8aea6b27d5e49113d500032d0cea55669bac6 Mon Sep 17 00:00:00 2001 From: Mu-Woong Lee Date: Wed, 6 Jul 2016 18:24:09 +0900 Subject: [PATCH] sensor: add HeartRate recorder Change-Id: Ib3e0ce16e34c5429715cd6095eed3de2f31ffed7 Signed-off-by: Mu-Woong Lee --- src/sensor/ClientInfo.cpp | 14 ++ src/sensor/ClientInfo.h | 2 + src/sensor/CreateProvider.cpp | 2 + src/sensor/SensorProvider.cpp | 17 ++- src/sensor/heartrate/HeartRate.cpp | 54 ++++++++ src/sensor/heartrate/HeartRate.h | 37 +++++ src/sensor/heartrate/HeartRateLogger.cpp | 157 ++++++++++++++++++++++ src/sensor/heartrate/HeartRateLogger.h | 49 +++++++ src/sensor/heartrate/HeartRateQuerier.cpp | 55 ++++++++ src/sensor/heartrate/HeartRateQuerier.h | 34 +++++ 10 files changed, 416 insertions(+), 5 deletions(-) create mode 100644 src/sensor/heartrate/HeartRate.cpp create mode 100644 src/sensor/heartrate/HeartRate.h create mode 100644 src/sensor/heartrate/HeartRateLogger.cpp create mode 100644 src/sensor/heartrate/HeartRateLogger.h create mode 100644 src/sensor/heartrate/HeartRateQuerier.cpp create mode 100644 src/sensor/heartrate/HeartRateQuerier.h diff --git a/src/sensor/ClientInfo.cpp b/src/sensor/ClientInfo.cpp index e1913f7..6c5955d 100644 --- a/src/sensor/ClientInfo.cpp +++ b/src/sensor/ClientInfo.cpp @@ -164,6 +164,20 @@ bool ClientInfo::remove(std::string subject, std::string pkgId) return ret; } +void ClientInfo::getParam(std::vector &options, const char *key, float *min, float *max) +{ + double val; + + for (Json& opt : options) { + if (!opt.get(NULL, key, &val)) + continue; + if (min) + *min = MIN(*min, static_cast(val)); + if (max) + *max = MAX(*max, static_cast(val)); + } +} + void ClientInfo::purgeClient(std::string pkgId) { IF_FAIL_VOID_TAG(__dbMgr, _W, "DB not initialized"); diff --git a/src/sensor/ClientInfo.h b/src/sensor/ClientInfo.h index a14dc3c..e3ee643 100644 --- a/src/sensor/ClientInfo.h +++ b/src/sensor/ClientInfo.h @@ -37,6 +37,8 @@ namespace ctx { bool set(std::string subject, std::string pkgId, Json option, int retentionPeriod); bool remove(std::string subject, std::string pkgId); + void getParam(std::vector& options, const char *key, float *min, float *max); + static void purgeClient(std::string pkgId); private: diff --git a/src/sensor/CreateProvider.cpp b/src/sensor/CreateProvider.cpp index 768ba81..73e9f19 100644 --- a/src/sensor/CreateProvider.cpp +++ b/src/sensor/CreateProvider.cpp @@ -19,6 +19,7 @@ #include "pedometer/Pedometer.h" #include "pressure/Pressure.h" #include "sleep/Sleep.h" +#include "heartrate/HeartRate.h" using namespace ctx; @@ -27,6 +28,7 @@ extern "C" SO_EXPORT ContextProvider* CreateProvider(const char *subject) ADD_PROVIDER(SUBJ_SENSOR_PEDOMETER, PedometerProvider); ADD_PROVIDER(SUBJ_SENSOR_PRESSURE, PressureProvider); ADD_PROVIDER(SUBJ_SENSOR_SLEEP_MONITOR, SleepProvider); + ADD_PROVIDER(SUBJ_SENSOR_HEART_RATE, HeartRateProvider); return NULL; } diff --git a/src/sensor/SensorProvider.cpp b/src/sensor/SensorProvider.cpp index 12c0eed..1e680ac 100644 --- a/src/sensor/SensorProvider.cpp +++ b/src/sensor/SensorProvider.cpp @@ -156,13 +156,20 @@ int SensorProvider::__removeClient(std::string pkgId) /* Check if there is no client anymore */ ret = __clientInfo.get(getSubject(), options); - IF_FAIL_RETURN(ret != ERR_NONE, ERR_NONE); - IF_FAIL_RETURN(ret == ERR_NO_DATA, ERR_OPERATION_FAILED); - /* Stop listening */ - sensorLogger->stop(); + if (ret == ERR_NONE) { + /* Still, one or more clients exist */ + /* If necessary, the logger restarts its logging logic with updated parameters */ + sensorLogger->start(); + return ERR_NONE; - return ERR_NONE; + } else if (ret == ERR_NO_DATA) { + /* No client */ + sensorLogger->stop(); + return ERR_NONE; + } + + return ERR_OPERATION_FAILED; } void SensorProvider::removeClient(std::string subject, std::string pkgId) diff --git a/src/sensor/heartrate/HeartRate.cpp b/src/sensor/heartrate/HeartRate.cpp new file mode 100644 index 0000000..03fae8a --- /dev/null +++ b/src/sensor/heartrate/HeartRate.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "../TypesInternal.h" +#include "HeartRateLogger.h" +#include "HeartRateQuerier.h" +#include "HeartRate.h" + +using namespace ctx; + +HeartRateProvider::HeartRateProvider() : + SensorProvider(SUBJ_SENSOR_HEART_RATE) +{ + IF_FAIL_VOID(isSupported()); + + sensorLogger = new(std::nothrow) HeartRateLogger(); + IF_FAIL_VOID_TAG(sensorLogger, _E, "Memory allocation failed"); +} + +HeartRateProvider::~HeartRateProvider() +{ +} + +void HeartRateProvider::getPrivilege(std::vector &privilege) +{ + privilege.push_back(PRIV_HEALTHINFO); +} + +bool HeartRateProvider::isSupported() +{ + return util::getSystemInfoBool("tizen.org/feature/sensor.heart_rate_monitor"); +} + +Querier* HeartRateProvider::getQuerier(Json option) +{ + HeartRateQuerier *querier = new(std::nothrow) HeartRateQuerier(this, option); + IF_FAIL_RETURN_TAG(querier, NULL, _E, "Memory allocation failed"); + return querier; +} diff --git a/src/sensor/heartrate/HeartRate.h b/src/sensor/heartrate/HeartRate.h new file mode 100644 index 0000000..c9d071b --- /dev/null +++ b/src/sensor/heartrate/HeartRate.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_HEARTRATE_PROVIDER_H__ +#define __CONTEXT_HEARTRATE_PROVIDER_H__ + +#include "../SensorProvider.h" + +namespace ctx { + + class HeartRateProvider : public SensorProvider { + public: + HeartRateProvider(); + ~HeartRateProvider(); + + bool isSupported(); + void getPrivilege(std::vector &privilege); + + protected: + Querier* getQuerier(Json option); + }; +} + +#endif /* _CONTEXT_HEARTRATE_PROVIDER_H_ */ diff --git a/src/sensor/heartrate/HeartRateLogger.cpp b/src/sensor/heartrate/HeartRateLogger.cpp new file mode 100644 index 0000000..05adbe1 --- /dev/null +++ b/src/sensor/heartrate/HeartRateLogger.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "../TypesInternal.h" +#include "../ClientInfo.h" +#include "../TimeUtil.h" +#include "HeartRateLogger.h" + +#define SAMPLING_INTERVAL 200 /* ms */ +#define VALID_HR_LB 30 /* BPM */ +#define MIN_VALID_COUNT 3 +#define MAX_TIMER_INTERVAL 1440 /* minutes */ +#define MEASURING_LIMIT 10000 /* ms */ + +using namespace ctx; + +HeartRateLogger::HeartRateLogger() : + __timerMgr(NULL), + __timerId(-1), + __timerInterval(INT_MAX), + __expiredTime(0) +{ + setSensor(HRM_SENSOR); + setPowerSave(false); + setSamplingInterval(SAMPLING_INTERVAL); + + /* Create the log table */ + executeQuery( + "CREATE TABLE IF NOT EXISTS " HEART_RATE_RECORD " (" \ + KEY_UNIV_TIME " INTEGER NOT NULL PRIMARY KEY, " \ + KEY_HEART_RATE " REAL NOT NULL" \ + ")"); + + ClientInfo clientInfo; + if (clientInfo.exist(SUBJ_SENSOR_HEART_RATE)) + start(); +} + +HeartRateLogger::~HeartRateLogger() +{ + stop(); +} + +bool HeartRateLogger::start() +{ + std::vector options; + ClientInfo clientInfo; + float interval = MAX_TIMER_INTERVAL; + + if (clientInfo.get(SUBJ_SENSOR_HEART_RATE, options) != ERR_NONE) + return false; + + clientInfo.getParam(options, KEY_INTERVAL, &interval, NULL); + + if (!__timerMgr) { + __timerMgr = new(std::nothrow) TimerManager; + IF_FAIL_RETURN_TAG(__timerMgr, false, _E, "Memory allocation failed"); + } + + if (interval == __timerInterval) + return true; + + __timerInterval = interval; + + _I(GREEN("Start to record (at every %d minutes)"), __timerInterval); + + if (__timerId > 0) + __timerMgr->remove(__timerId); + + __timerId = __timerMgr->setFor(__timerInterval, this); + + if (__timerId < 0) { + _E("Setting timer failed"); + __timerInterval = INT_MAX; + return false; + } + + return true; +} + +void HeartRateLogger::stop() +{ + _I(GREEN("Stop recording")); + + if (__timerMgr) + delete __timerMgr; + + __timerMgr = NULL; + __timerId = -1; + __timerInterval = INT_MAX; + + unlisten(); +} + +void HeartRateLogger::flushCache(bool force) +{ +} + +bool HeartRateLogger::onTimerExpired(int timerId) +{ + IF_FAIL_RETURN(!isRunning(), true); + + if (!listen()) + _W("Starting sensor failed"); + + __expiredTime = TimeUtil::getTime(); + _I("Measuring starts at %llu", __expiredTime); + return true; +} + +void HeartRateLogger::onEvent(sensor_data_t *eventData) +{ + static int validCnt = 0; + uint64_t receivedTime = TimeUtil::getTime(); + + IF_FAIL_CATCH_TAG(receivedTime - __expiredTime < MEASURING_LIMIT, _I, "Measuring failed (timeout)"); + + if (eventData->values[0] > VALID_HR_LB) + ++validCnt; + else + validCnt = 0; + + if (validCnt < MIN_VALID_COUNT) + return; + + __record(eventData->values[0], receivedTime); + +CATCH: + removeExpired(SUBJ_SENSOR_HEART_RATE, HEART_RATE_RECORD, KEY_UNIV_TIME); + unlisten(); +} + +void HeartRateLogger::__record(float heartrate, uint64_t eventTime) +{ + char *query = sqlite3_mprintf( + "INSERT INTO " HEART_RATE_RECORD \ + " (" KEY_UNIV_TIME ", " KEY_HEART_RATE ") VALUES (%llu, %.3f)", + eventTime, heartrate); + executeQuery(query); + sqlite3_free(query); +} diff --git a/src/sensor/heartrate/HeartRateLogger.h b/src/sensor/heartrate/HeartRateLogger.h new file mode 100644 index 0000000..b3a56c1 --- /dev/null +++ b/src/sensor/heartrate/HeartRateLogger.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_HEARTRATE_LOGGER_H__ +#define __CONTEXT_HEARTRATE_LOGGER_H__ + +#include +#include "../SensorLogger.h" +#include "../SensorProxy.h" + +namespace ctx { + + class HeartRateLogger : public SensorLogger, public SensorProxy, public ITimerListener { + public: + HeartRateLogger(); + ~HeartRateLogger(); + + bool start(); + void stop(); + void flushCache(bool force = false); + + protected: + bool onTimerExpired(int timerId); + void onEvent(sensor_data_t *eventData); + + private: + void __record(float heartrate, uint64_t eventTime); + + TimerManager *__timerMgr; + int __timerId; + int __timerInterval; + uint64_t __expiredTime; + }; +} + +#endif /* __CONTEXT_HEARTRATE_LOGGER_H__ */ diff --git a/src/sensor/heartrate/HeartRateQuerier.cpp b/src/sensor/heartrate/HeartRateQuerier.cpp new file mode 100644 index 0000000..1bae5a6 --- /dev/null +++ b/src/sensor/heartrate/HeartRateQuerier.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "../TypesInternal.h" +#include "HeartRateQuerier.h" + +#define PROJECTION \ + KEY_HEART_RATE ", " \ + KEY_UNIV_TIME " AS " KEY_START_TIME ", " \ + KEY_UNIV_TIME " AS " KEY_END_TIME + +using namespace ctx; + +HeartRateQuerier::HeartRateQuerier(ContextProvider *provider, Json option) : + Querier(provider, option) +{ +} + +HeartRateQuerier::~HeartRateQuerier() +{ +} + +int HeartRateQuerier::queryRaw(int startTime, int endTime) +{ + return query(startTime, endTime); +} + +int HeartRateQuerier::query(int startTime, int endTime) +{ + char *sql = sqlite3_mprintf( + "SELECT " PROJECTION \ + " FROM " HEART_RATE_RECORD \ + " WHERE " KEY_UNIV_TIME " > %llu AND " KEY_UNIV_TIME " <= %llu", + SEC_TO_MS(static_cast(startTime)), SEC_TO_MS(static_cast(endTime))); + + int ret = Querier::query(sql); + sqlite3_free(sql); + + return ret; +} diff --git a/src/sensor/heartrate/HeartRateQuerier.h b/src/sensor/heartrate/HeartRateQuerier.h new file mode 100644 index 0000000..335d902 --- /dev/null +++ b/src/sensor/heartrate/HeartRateQuerier.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CONTEXT_HEARTRATE_QUERIER_H__ +#define __CONTEXT_HEARTRATE_QUERIER_H__ + +#include "../Querier.h" + +namespace ctx { + + class HeartRateQuerier : public Querier { + public: + HeartRateQuerier(ContextProvider *provider, Json option); + ~HeartRateQuerier(); + + int queryRaw(int startTime, int endTime); + int query(int startTime, int endTime); + }; +} + +#endif /* __CONTEXT_HEARTRATE_QUERIER_H__ */ -- 2.34.1