[HAM] HRM implementation
authorRafal Galka <r.galka@samsung.com>
Thu, 14 May 2015 14:35:40 +0000 (16:35 +0200)
committerRafal Galka <r.galka@samsung.com>
Thu, 14 May 2015 14:35:40 +0000 (16:35 +0200)
[Info] Unable to verify. HRM sensor not working on emulator.

Change-Id: Icf69396ab9e89e80152543633714a2ec068ca89e
Signed-off-by: Rafal Galka <r.galka@samsung.com>
src/humanactivitymonitor/humanactivitymonitor.gyp
src/humanactivitymonitor/humanactivitymonitor_api.js
src/humanactivitymonitor/humanactivitymonitor_instance.cc
src/humanactivitymonitor/humanactivitymonitor_instance.h
src/humanactivitymonitor/humanactivitymonitor_manager.cc [new file with mode: 0644]
src/humanactivitymonitor/humanactivitymonitor_manager.h [new file with mode: 0644]

index c21c2ea95fac843f2b9055767bc43abd3c2190cc..5640779e8d49f174febc2868768256927d78c7e6 100755 (executable)
@@ -15,6 +15,8 @@
         'humanactivitymonitor_extension.h',
         'humanactivitymonitor_instance.cc',
         'humanactivitymonitor_instance.h',
+        'humanactivitymonitor_manager.cc',
+        'humanactivitymonitor_manager.h',
       ],
       'conditions': [
         ['tizen == 1', {
index 3416ddf10f2c776ed249fcff12a10cee880abf20..77677f50e40b79ceb86da126070a55a67b0e2a23 100644 (file)
@@ -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();
index b0489b67859b3373f6b36e991f6eb5bf36b3d324..fb5d77c78701ba5abc95c4c2d75ee4af44c733d0 100644 (file)
@@ -5,11 +5,13 @@
 #include "humanactivitymonitor/humanactivitymonitor_instance.h"
 
 #include <functional>
+#include <memory>
 #include <string>
 
 #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<HumanActivityMonitorManager>();
+    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<picojson::object>();
+    data_o["listenerId"] = args.get("listenerId");
+
+    PostMessage(data->serialize().c_str());
+  };
+
+  result = manager_->SetListener(args.get("type").get<std::string>(), 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<std::string>());
+  if (result) {
+    ReportSuccess(out);
+  } else {
+    ReportError(result, &out);
+  }
 }
 
 void HumanActivityMonitorInstance::HumanActivityMonitorManagerSetAccumulativePedometerListener(
index 3cf9684c213d5eb33172fbf6648b5a3b8cea24cf..1a2f8708c1eb9cc8ed62b2ef996e3700f2e687c3 100644 (file)
@@ -5,8 +5,11 @@
 #ifndef HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_INSTANCE_H_
 #define HUMANACTIVITYMONITOR_HUMANACTIVITYMONITOR_INSTANCE_H_
 
+#include <memory>
+
 #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<HumanActivityMonitorManager> manager_;
+  common::PlatformResult Init();
 };
 
 } // namespace humanactivitymonitor
diff --git a/src/humanactivitymonitor/humanactivitymonitor_manager.cc b/src/humanactivitymonitor/humanactivitymonitor_manager.cc
new file mode 100644 (file)
index 0000000..e60784f
--- /dev/null
@@ -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 <gesture_recognition.h>
+
+#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<HumanActivityMonitorManager*>(user_data);
+
+  picojson::value hrm_data = picojson::value(picojson::object());
+  picojson::object& hrm_data_o = hrm_data.get<picojson::object>();
+
+  hrm_data_o["heartRate"] = picojson::value(static_cast<double>(hr));
+  hrm_data_o["rRInterval"] = picojson::value(static_cast<double>(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 (file)
index 0000000..fbf311d
--- /dev/null
@@ -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 <functional>
+#include <sensor.h>
+#include <gesture_recognition.h>
+#include <string>
+
+#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<void(picojson::value*)> 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<std::string, bool> 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