From f2b19aa40e48d0578112387e8951eb560fedb8b4 Mon Sep 17 00:00:00 2001 From: Rafal Galka Date: Thu, 14 May 2015 16:35:40 +0200 Subject: [PATCH] [HAM] HRM implementation [Info] Unable to verify. HRM sensor not working on emulator. Change-Id: Icf69396ab9e89e80152543633714a2ec068ca89e Signed-off-by: Rafal Galka --- .../humanactivitymonitor.gyp | 2 + .../humanactivitymonitor_api.js | 41 +++- .../humanactivitymonitor_instance.cc | 59 ++++- .../humanactivitymonitor_instance.h | 6 + .../humanactivitymonitor_manager.cc | 232 ++++++++++++++++++ .../humanactivitymonitor_manager.h | 56 +++++ 6 files changed, 384 insertions(+), 12 deletions(-) create mode 100644 src/humanactivitymonitor/humanactivitymonitor_manager.cc create mode 100644 src/humanactivitymonitor/humanactivitymonitor_manager.h diff --git a/src/humanactivitymonitor/humanactivitymonitor.gyp b/src/humanactivitymonitor/humanactivitymonitor.gyp index c21c2ea9..5640779e 100755 --- a/src/humanactivitymonitor/humanactivitymonitor.gyp +++ b/src/humanactivitymonitor/humanactivitymonitor.gyp @@ -15,6 +15,8 @@ 'humanactivitymonitor_extension.h', 'humanactivitymonitor_instance.cc', 'humanactivitymonitor_instance.h', + 'humanactivitymonitor_manager.cc', + 'humanactivitymonitor_manager.h', ], 'conditions': [ ['tizen == 1', { diff --git a/src/humanactivitymonitor/humanactivitymonitor_api.js b/src/humanactivitymonitor/humanactivitymonitor_api.js index 3416ddf1..77677f50 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_api.js +++ b/src/humanactivitymonitor/humanactivitymonitor_api.js @@ -29,6 +29,7 @@ var HumanActivityType = { HRM: 'HRM', GPS: 'GPS' }; + var PedometerStepStatus = { NOT_MOVING: 'NOT_MOVING', WALKING: 'WALKING', @@ -62,36 +63,56 @@ HumanActivityMonitorManager.prototype.getHumanActivityData = function(type, succ }; HumanActivityMonitorManager.prototype.start = function(type, changedCallback) { + // TODO(r.galka) check access + // HRM - http://tizen.org/privilege/healthinfo + // GPS - http://tizen.org/privilege/location + var args = validator_.validateArgs(arguments, [ {name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType)}, {name: 'changedCallback', type: types_.FUNCTION, optional: true, nullable: true} ]); + var listenerId ='HumanActivityMonitor_' + args.type; + var data = { - type: args.type + type: args.type, + listenerId: listenerId }; - var callback = function(result) { - native_.callIfPossible(args.changedCallback); - }; + if (!native_.isListenerSet(listenerId)) { + var result = native_.callSync('HumanActivityMonitorManager_start', data); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + } - native_.call('HumanActivityMonitorManager_start', data, callback); + var listener = function(result) { + native_.callIfPossible(args.changedCallback, new HumanActivityHRMData(result)); + }; + native_.addListener(listenerId, listener); }; HumanActivityMonitorManager.prototype.stop = function(type) { var args = validator_.validateArgs(arguments, [ - {name: 'type', type: types_.ENUM, values: ['PEDOMETER', 'WRIST_UP', 'HRM', 'GPS']} + {name: 'type', type: types_.ENUM, values: Object.keys(HumanActivityType)} ]); var data = { type: args.type }; - var result = native_.callSync('HumanActivityMonitorManager_stop', data); + var listenerId ='HumanActivityMonitor_' + args.type; + if (!native_.isListenerSet(listenerId)) { + return; + } + + var result = native_.callSync('HumanActivityMonitorManager_stop', data); if (native_.isFailure(result)) { throw native_.getErrorObject(result); } + + native_.removeListener(listenerId); }; HumanActivityMonitorManager.prototype.setAccumulativePedometerListener = function(changeCallback) { @@ -164,9 +185,9 @@ HumanActivityAccumulativePedometerData.prototype = new HumanActivityData(); HumanActivityAccumulativePedometerData.prototype.constructor = HumanActivityAccumulativePedometerData; -function HumanActivityHRMData() { - SetReadOnlyProperty(this, 'heartRate', null); - SetReadOnlyProperty(this, 'rRInterval', null); +function HumanActivityHRMData(data) { + SetReadOnlyProperty(this, 'heartRate', data.heartRate); + SetReadOnlyProperty(this, 'rRInterval', data.rRInterval); } HumanActivityHRMData.prototype = new HumanActivityData(); diff --git a/src/humanactivitymonitor/humanactivitymonitor_instance.cc b/src/humanactivitymonitor/humanactivitymonitor_instance.cc index b0489b67..fb5d77c7 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_instance.cc +++ b/src/humanactivitymonitor/humanactivitymonitor_instance.cc @@ -5,11 +5,13 @@ #include "humanactivitymonitor/humanactivitymonitor_instance.h" #include +#include #include #include "common/picojson.h" #include "common/logger.h" #include "common/platform_result.h" +#include "humanactivitymonitor/humanactivitymonitor_manager.h" namespace extension { namespace humanactivitymonitor { @@ -39,6 +41,21 @@ HumanActivityMonitorInstance::HumanActivityMonitorInstance() { HumanActivityMonitorInstance::~HumanActivityMonitorInstance() { } +PlatformResult HumanActivityMonitorInstance::Init() { + if (!manager_) { + + manager_ = std::make_shared(); + const PlatformResult& result = manager_->Init(); + if (!result) { + LOGGER(ERROR) << "Error initializing manager: " << result.message(); + manager_.reset(); + return result; + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + #define CHECK_EXIST(args, name, out) \ if (!args.contains(name)) { \ ReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, \ @@ -54,12 +71,50 @@ void HumanActivityMonitorInstance::HumanActivityMonitorManagerGetHumanActivityDa void HumanActivityMonitorInstance::HumanActivityMonitorManagerStart( const picojson::value& args, picojson::object& out) { - // TODO(r.galka) implement + CHECK_EXIST(args, "type", out) + + PlatformResult result = Init(); + if (!result) { + ReportError(result, &out); + return; + } + + JsonCallback cb = [this, args](picojson::value* data) -> void { + if (!data) { + LOGGER(ERROR) << "No data passed to json callback"; + return; + } + + picojson::object& data_o = data->get(); + data_o["listenerId"] = args.get("listenerId"); + + PostMessage(data->serialize().c_str()); + }; + + result = manager_->SetListener(args.get("type").get(), cb); + if (result) { + ReportSuccess(out); + } else { + ReportError(result, &out); + } } void HumanActivityMonitorInstance::HumanActivityMonitorManagerStop( const picojson::value& args, picojson::object& out) { - // TODO(r.galka) implement + CHECK_EXIST(args, "type", out) + + PlatformResult result = Init(); + if (!result) { + ReportError(result, &out); + return; + } + + result = manager_->UnsetListener(args.get("type").get()); + if (result) { + ReportSuccess(out); + } else { + ReportError(result, &out); + } } void HumanActivityMonitorInstance::HumanActivityMonitorManagerSetAccumulativePedometerListener( diff --git a/src/humanactivitymonitor/humanactivitymonitor_instance.h b/src/humanactivitymonitor/humanactivitymonitor_instance.h index 3cf9684c..1a2f8708 100644 --- a/src/humanactivitymonitor/humanactivitymonitor_instance.h +++ b/src/humanactivitymonitor/humanactivitymonitor_instance.h @@ -5,8 +5,11 @@ #ifndef HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_INSTANCE_H_ #define HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_INSTANCE_H_ +#include + #include "common/extension.h" #include "common/platform_result.h" +#include "humanactivitymonitor/humanactivitymonitor_manager.h" namespace extension { namespace humanactivitymonitor { @@ -27,6 +30,9 @@ class HumanActivityMonitorInstance : public common::ParsedInstance { const picojson::value& args, picojson::object& out); void HumanActivityMonitorManagerSetAccumulativePedometerListener( const picojson::value& args, picojson::object& out); + + std::shared_ptr manager_; + common::PlatformResult Init(); }; } // namespace humanactivitymonitor diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.cc b/src/humanactivitymonitor/humanactivitymonitor_manager.cc new file mode 100644 index 00000000..e60784fc --- /dev/null +++ b/src/humanactivitymonitor/humanactivitymonitor_manager.cc @@ -0,0 +1,232 @@ +// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "humanactivitymonitor/humanactivitymonitor_manager.h" + +#include + +#include "common/logger.h" + +namespace extension { +namespace humanactivitymonitor { + +using common::PlatformResult; +using common::ErrorCode; + +HumanActivityMonitorManager::HumanActivityMonitorManager() {} + +HumanActivityMonitorManager::~HumanActivityMonitorManager() { + UnsetHrmListener(); +} + +PlatformResult HumanActivityMonitorManager::Init() { + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult HumanActivityMonitorManager::IsSupported( + const std::string& type) { + + // check cache first + if (supported_.count(type)) { + return PlatformResult(supported_[type] + ? ErrorCode::NO_ERROR + : ErrorCode::NOT_SUPPORTED_ERR); + } + + int ret; + bool supported = false; + if (type == kActivityTypePedometer) { + // TODO(r.galka) no native api for pedometer + // so just pass it for not supported. + } else if (type == kActivityTypeWristUp) { + // TODO(r.galka) implement when available in platform + } else if (type == kActivityTypeHrm) { + ret = sensor_is_supported(SENSOR_HRM, &supported); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "sensor_is_supported(HRM), error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "HRM sensor check failed"); + } + } else if (type == kActivityTypeGps) { + // TODO(r.galka) implement when available in platform + } else { + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR); + } + + supported_[type] = supported; + + return PlatformResult(supported_[type] + ? ErrorCode::NO_ERROR + : ErrorCode::NOT_SUPPORTED_ERR); +} + +PlatformResult HumanActivityMonitorManager::SetListener( + const std::string& type, JsonCallback callback) { + + PlatformResult result = IsSupported(type); + if (!result) { + return result; + } + + int ret; + + // PEDOMETER + if (type == kActivityTypePedometer) { + // TODO(r.galka) Not Supported in current implementation. + } + + // WRIST_UP + if (type == kActivityTypeWristUp) { + // TODO(r.galka) implement + } + + // HRM + if (type == kActivityTypeHrm) { + return SetHrmListener(callback); + } + + // GPS + if (type == kActivityTypeGps) { + // TODO(r.galka) implement + } +} + +PlatformResult HumanActivityMonitorManager::UnsetListener( + const std::string& type) { + + PlatformResult result = IsSupported(type); + if (!result) { + return result; + } + + // PEDOMETER + if (type == kActivityTypePedometer) { + // TODO(r.galka) Not Supported in current implementation. + } + + // WRIST_UP + if (type == kActivityTypeWristUp) { + // TODO(r.galka) implement + } + + // HRM + if (type == kActivityTypeHrm) { + return UnsetHrmListener(); + } + + // GPS + if (type == kActivityTypeGps) { + // TODO(r.galka) implement + } +} + +// HRM +PlatformResult HumanActivityMonitorManager::SetHrmListener( + JsonCallback callback) { + sensor_h hrm_sensor; + int ret; + + ret = sensor_get_default_sensor(SENSOR_HRM, &hrm_sensor); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to get HRM sensor, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to get HRM sensor"); + } + + ret = sensor_create_listener(hrm_sensor, &hrm_sensor_listener_); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to create HRM sensor listener, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to create HRM sensor listener"); + } + + ret = sensor_listener_set_event_cb(hrm_sensor_listener_, + 0, + OnHrmSensorEvent, + this); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to set HRM sensor listener, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to set HRM sensor listener"); + } + + ret = sensor_listener_start(hrm_sensor_listener_); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to start HRM sensor listener, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to start HRM sensor listener"); + } + + hrm_event_callback_ = callback; + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult HumanActivityMonitorManager::UnsetHrmListener() { + int ret; + + if (hrm_sensor_listener_) { + ret = sensor_listener_stop(hrm_sensor_listener_); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to stop HRM sensor, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to stop HRM sensor"); + } + + ret = sensor_listener_unset_event_cb(hrm_sensor_listener_); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to unset HRM sensor listener, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to unset HRM sensor listener"); + } + + ret = sensor_destroy_listener(hrm_sensor_listener_); + if (ret != SENSOR_ERROR_NONE) { + LOGGER(ERROR) << "Failed to destroy HRM sensor listener, error: " << ret; + return PlatformResult(ErrorCode::UNKNOWN_ERR, + "Failed to destroy HRM sensor listener"); + } + } + + hrm_event_callback_ = nullptr; + + return PlatformResult(ErrorCode::NO_ERROR); +} + +void HumanActivityMonitorManager::OnHrmSensorEvent( + sensor_h sensor, sensor_event_s *event, void *user_data) { + LOGGER(DEBUG) << "Sensor event:"; + LOGGER(DEBUG) << " |- accuracy: " << event->accuracy; + LOGGER(DEBUG) << " |- timestamp: " << event->timestamp; + LOGGER(DEBUG) << " |- value_count: " << event->value_count; + + if (event->value_count < 2) { + LOGGER(ERROR) << "To few values of HRM event"; + return; + } + + LOGGER(DEBUG) << " |- values[0]: " << event->values[0]; + LOGGER(DEBUG) << " |- values[1]: " << event->values[1]; + + float hr = event->values[0]; // heart beat rate 0 ~ 220 integer (bpm) + + // there are no public native api for peak to peak interval. + // but RRI = (60 / HR) * 1000 + // or unofficially values[1] is rri (0 ~ 5000 ms) + float rri = event->values[1]; + + HumanActivityMonitorManager* manager = + static_cast(user_data); + + picojson::value hrm_data = picojson::value(picojson::object()); + picojson::object& hrm_data_o = hrm_data.get(); + + hrm_data_o["heartRate"] = picojson::value(static_cast(hr)); + hrm_data_o["rRInterval"] = picojson::value(static_cast(rri)); + + manager->hrm_event_callback_(&hrm_data); +} + +} // namespace humanactivitymonitor +} // namespace extension diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.h b/src/humanactivitymonitor/humanactivitymonitor_manager.h new file mode 100644 index 00000000..fbf311d1 --- /dev/null +++ b/src/humanactivitymonitor/humanactivitymonitor_manager.h @@ -0,0 +1,56 @@ +// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_MANAGER_H +#define HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_MANAGER_H + +#include +#include +#include +#include + +#include "common/picojson.h" +#include "common/platform_result.h" + +namespace extension { +namespace humanactivitymonitor { + +namespace { +const std::string kActivityTypePedometer = "PEDOMETER"; +const std::string kActivityTypeWristUp = "WRIST_UP"; +const std::string kActivityTypeHrm = "HRM"; +const std::string kActivityTypeGps = "GPS"; +} + +typedef std::function JsonCallback; + +class HumanActivityMonitorManager { + public: + HumanActivityMonitorManager(); + virtual ~HumanActivityMonitorManager(); + + common::PlatformResult Init(); + common::PlatformResult IsSupported(const std::string& type); + + common::PlatformResult SetListener(const std::string& type, + JsonCallback callback); + common::PlatformResult UnsetListener(const std::string& type); + + private: + std::map supported_; + + // HRM + sensor_listener_h hrm_sensor_listener_; + JsonCallback hrm_event_callback_; + common::PlatformResult SetHrmListener(JsonCallback callback); + common::PlatformResult UnsetHrmListener(); + static void OnHrmSensorEvent(sensor_h sensor, + sensor_event_s *event, + void *data); +}; + +} // namespace humanactivitymonitor +} // namespace extension + +#endif // HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_MANAGER_H -- 2.34.1