[place-recognition] PlacesDetector and PlaceCateger classes and associates. 01/53001/3
authorMarcin Masternak <m.masternak@samsung.com>
Wed, 2 Dec 2015 17:04:27 +0000 (18:04 +0100)
committerMarcin Masternak <m.masternak@samsung.com>
Wed, 2 Dec 2015 17:04:27 +0000 (18:04 +0100)
Change-Id: I77cfffee8764e553444d811c2ea7af28146cc6aa
Signed-off-by: Marcin Masternak <m.masternak@samsung.com>
18 files changed:
src/place_recognition/place_recognition.cpp
src/place_recognition/place_recognition_types.h
src/place_recognition/user_places/gmap.cpp [new file with mode: 0644]
src/place_recognition/user_places/gmap.h [new file with mode: 0644]
src/place_recognition/user_places/graph.cpp [new file with mode: 0644]
src/place_recognition/user_places/graph.h [new file with mode: 0644]
src/place_recognition/user_places/location_logger.cpp
src/place_recognition/user_places/place_categer.cpp [new file with mode: 0644]
src/place_recognition/user_places/place_categer.h [new file with mode: 0644]
src/place_recognition/user_places/places_detector.cpp [new file with mode: 0644]
src/place_recognition/user_places/places_detector.h [new file with mode: 0644]
src/place_recognition/user_places/user_places.cpp
src/place_recognition/user_places/user_places.h
src/place_recognition/user_places/user_places_params.h
src/place_recognition/user_places/user_places_types.cpp
src/place_recognition/user_places/user_places_types.h
src/place_recognition/user_places/visit_detector.cpp
src/place_recognition/user_places/wifi_logger.cpp

index 625d022..137d3b6 100644 (file)
@@ -53,7 +53,8 @@ int ctx::place_recognition_provider::read(const char *subject, ctx::json option,
        _I(BLUE("Read"));
        _J("Option", option);
 
-       json data_read; // @to_be_added get detected places
+       std::vector<std::shared_ptr<ctx::Place>> places = engine.get_places();
+       json data_read = engine.compose_json(places);
 
        // The below function needs to be called once.
        // It does not need to be called within this read() function.
index 50b445c..f11f946 100644 (file)
 #define VISIT_COLUMN_END_TIME                  "end_time"
 #define VISIT_COLUMN_WIFI_APS                  "wifi_aps"
 #define VISIT_COLUMN_CATEGORY                  "category"
+#ifdef TIZEN_ENGINEER_MODE
 #define VISIT_COLUMN_START_TIME_HUMAN          "start_time_human" // only for debug: human readable time data:
 #define VISIT_COLUMN_END_TIME_HUMAN            "end_time_human" // only for debug: human readable time data:
+#endif /* TIZEN_ENGINEER_MODE */
 #define VISIT_COLUMN_LOCATION_VALID            "geo_valid"
 #define VISIT_COLUMN_LOCATION_LATITUDE         "geo_latitude"
 #define VISIT_COLUMN_LOCATION_LONGITUDE        "geo_longitude"
 #define VISIT_COLUMN_CATEG_WORK                "categ_work"
 #define VISIT_COLUMN_CATEG_OTHER               "categ_other"
 
+#define PLACE_TABLE                            "place_status_user_place"
+#define PLACE_COLUMN_CATEG_ID                  "type_id" // Name inconsistency: "cated_id" vs "type_id" TODO make it consistent
+#define PLACE_COLUMN_CATEG_CONFIDENCE          "type_confidence"
+#define PLACE_COLUMN_NAME                      "name"
+#define PLACE_COLUMN_LOCATION_VALID            "geo_valid"
+#define PLACE_COLUMN_LOCATION_LATITUDE         "geo_latitude"
+#define PLACE_COLUMN_LOCATION_LONGITUDE        "geo_longitude"
+#define PLACE_COLUMN_WIFI_APS                  "wifi_aps"
+#define PLACE_COLUMN_CREATE_DATE               "create_date"
+
 #define WIFI_TABLE_NAME                        "place_status_user_place_wifi"
 #define WIFI_COLUMN_TIMESTAMP                  "timestamp"
 #define WIFI_COLUMN_BSSID                      "bssid"
 #define LOCATION_COLUMN_LONGITUDE              "geo_longitude"
 #define LOCATION_COLUMN_ACCURACY               "accuracy"
 #define LOCATION_COLUMN_TIMESTAMP              "timestamp"
+#ifdef TIZEN_ENGINEER_MODE
 #define LOCATION_COLUMN_TIMESTAMP_HUMAN        "time_human" // only for debug: human readable time data:
 #define LOCATION_COLUMN_METHOD                 "method"
+#endif /* TIZEN_ENGINEER_MODE */
+
+// Data Key
+#define DATA_READ                              "PlacesList"
+#define PLACE_CATEG_ID                         "TypeId" // Name inconsistency: "cated_id" vs "type_id" TODO make it consistent
+#define PLACE_CATEG_CONFIDENCE                 "TypeConfidence"
+#define PLACE_NAME                             "Name"
+#define PLACE_GEO_LATITUDE                     "GeoLatitude"
+#define PLACE_GEO_LONGITUDE                    "GeoLongitude"
+#define PLACE_WIFI_APS                         "WifiAPs"
+#define PLACE_CREATE_DATE                      "CreateDate"
 
 // Data values
 typedef enum {
diff --git a/src/place_recognition/user_places/gmap.cpp b/src/place_recognition/user_places/gmap.cpp
new file mode 100644 (file)
index 0000000..6ad114d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 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 "gmap.h"
+#include <iostream>
+#include <fstream>
+
+const std::string ctx::Gmap::html_header = R"(
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
+    <meta charset="utf-8">
+    <title>Simple markers</title>
+    <style>
+      html, body, #map-canvas {
+        height: 100%;
+        margin: 0px;
+        padding: 0px
+      }
+    </style>
+    <script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
+    <script>
+function initialize() {
+  var mapOptions = {
+    zoom: 2,
+    center: new google.maps.LatLng(20,60)
+  }
+  var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
+)";
+
+const std::string ctx::Gmap::html_footer = R"(
+}
+
+google.maps.event.addDomListener(window, 'load', initialize);
+
+    </script>
+  </head>
+  <body>
+    <div id="map-canvas"></div>
+  </body>
+</html>
+)";
+
+std::string ctx::Gmap::icon_for_categ_id(place_categ_id_e categ_id)
+{
+       switch (categ_id) {
+       case PLACE_CATEG_ID_HOME:  return "markerH.png";
+       case PLACE_CATEG_ID_WORK:  return "markerW.png";
+       case PLACE_CATEG_ID_OTHER: return "markerO.png";
+       case PLACE_CATEG_ID_NONE:  return "markerN.png";
+       default: return "markerD.png";
+       }
+}
+
+void ctx::Gmap::place_marker_to_stream(const ctx::Place& place, std::ostream& out)
+{
+       if (place.location_valid) {
+               out << "new google.maps.Marker({" << std::endl;
+               out << "    position: new google.maps.LatLng(" << place.location.latitude << "," << place.location.longitude << ")," << std::endl;
+               out << "    map: map," << std::endl;
+               out << "    icon: \"http://maps.google.com/mapfiles/" << icon_for_categ_id(place.categ_id)<< "\"" << std::endl;
+               out << "});" << std::endl;
+       }
+}
+
+void ctx::Gmap::html_to_stream(const std::vector<std::shared_ptr<ctx::Place>>& places, std::ostream& out)
+{
+       out << html_header;
+       for (std::shared_ptr<ctx::Place> place : places) {
+               place_marker_to_stream(*place, out);
+       }
+       out << html_footer;
+}
+
+void ctx::Gmap::write_map(const std::vector<std::shared_ptr<ctx::Place>>& places)
+{
+       std::ofstream out(GMAP_FILE);
+       html_to_stream(places, out);
+}
diff --git a/src/place_recognition/user_places/gmap.h b/src/place_recognition/user_places/gmap.h
new file mode 100644 (file)
index 0000000..aca182d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 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_PLACE_STATUS_GMAP_H__
+#define __CONTEXT_PLACE_STATUS_GMAP_H__
+
+#include "user_places_types.h"
+#include "../place_recognition_types.h"
+
+#define GMAP_FILE "/opt/usr/media/Others/user_places_map.html"
+
+namespace ctx {
+
+       /*
+        * Class for generating a HTML page with GoogleMaps with all user places.
+        * This class is for test/demo purposes only. TODO: Remove this class from final solution.
+        */
+       class Gmap {
+
+       private:
+               const static std::string html_header;
+               const static std::string html_footer;
+               static std::string icon_for_categ_id(place_categ_id_e categ_id);
+               static void place_marker_to_stream(const Place& place, std::ostream& out);
+               static void html_to_stream(const std::vector<std::shared_ptr<Place>>& places, std::ostream& out);
+
+       public:
+               static void write_map(const std::vector<std::shared_ptr<Place>>& places);
+
+       };      /* class Gmap */
+
+}      /* namespace ctx */
+
+#endif /* __CONTEXT_PLACE_STATUS_VISIT_GMAP_H__ */
diff --git a/src/place_recognition/user_places/graph.cpp b/src/place_recognition/user_places/graph.cpp
new file mode 100644 (file)
index 0000000..e33ca2c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 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 <memory>
+#include <queue>
+#include "graph.h"
+
+std::shared_ptr<ctx::components_t> ctx::connected_components(graph_t &graph)
+{
+       std::shared_ptr<components_t> ccs = std::make_shared<components_t>();
+       std::set<int> fringe;
+
+       for (node_t i = 0; i < static_cast<node_t>(graph.size()); i++) {
+               if (!graph[i]) {
+                       continue;
+               }
+               // neighbourhood of node i exists (was not removed)
+               std::shared_ptr<component_t> c = std::make_shared<component_t>();
+               ccs->push_back(c);
+               fringe.insert(i);
+               while (!fringe.empty()) {
+                       node_t curr_node = *fringe.begin();
+                       fringe.erase(fringe.begin());
+                       c->insert(curr_node);
+
+                       std::shared_ptr<nhood_t> curr_nhood = graph[curr_node];
+                       for (node_t nhood_node : *curr_nhood) {
+                               if (graph[nhood_node] && fringe.find(nhood_node) == fringe.end()) {
+                                       fringe.insert(nhood_node);
+                               }
+                       }
+                       graph[curr_node].reset();  // removing current node
+               }
+       }
+       return ccs;
+}
diff --git a/src/place_recognition/user_places/graph.h b/src/place_recognition/user_places/graph.h
new file mode 100644 (file)
index 0000000..7310e11
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015 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_PLACE_STATUS_GRAPH_H__
+#define __CONTEXT_PLACE_STATUS_GRAPH_H__
+
+#include <memory>
+#include <vector>
+#include <set>
+
+namespace ctx {
+
+       typedef int node_t;
+       typedef std::set<node_t> nhood_t; // neighbouring nodes
+       typedef std::vector<std::shared_ptr<nhood_t>> graph_t;
+       typedef std::set<node_t> component_t;
+       typedef std::vector<std::shared_ptr<component_t>> components_t;
+
+       /*
+        * make connected components of a given graph
+        * caution: the graph will be changed (its nodes will be cleared)
+        */
+       std::shared_ptr<components_t> connected_components(graph_t &graph);
+
+}      /* namespace ctx */
+
+#endif /* __CONTEXT_PLACE_STATUS_GRAPH_H__ */
index 94057a0..4adc07c 100644 (file)
@@ -23,6 +23,7 @@
 #include "user_places_params.h"
 #include "debug_utils.h"
 
+#ifdef TIZEN_ENGINEER_MODE
 #define LOCATION_CREATE_TABLE_COLUMNS \
        LOCATION_COLUMN_LATITUDE " REAL NOT NULL, "\
        LOCATION_COLUMN_LONGITUDE " REAL NOT NULL, "\
        LOCATION_COLUMN_TIMESTAMP " timestamp NOT NULL, "\
        LOCATION_COLUMN_TIMESTAMP_HUMAN " TEXT, "\
        LOCATION_COLUMN_METHOD " INTEGER "
+#else /* TIZEN_ENGINEER_MODE */
+#define LOCATION_CREATE_TABLE_COLUMNS \
+       LOCATION_COLUMN_LATITUDE " REAL NOT NULL, "\
+       LOCATION_COLUMN_LONGITUDE " REAL NOT NULL, "\
+       LOCATION_COLUMN_ACCURACY " REAL, "\
+       LOCATION_COLUMN_TIMESTAMP " timestamp NOT NULL "
+#endif /* TIZEN_ENGINEER_MODE */
 
 #define _LOCATION_ERROR_LOG(error) { \
        if (error != LOCATIONS_ERROR_NONE) { \
@@ -103,7 +111,11 @@ void ctx::LocationLogger::position_updated_cb(double latitude, double longitude,
        _D("");
        ctx::LocationLogger* location_logger_p = (ctx::LocationLogger *)user_data;
        double horizontal = location_logger_p->manager_get_horizontal_accuracy();
+#ifdef TIZEN_ENGINEER_MODE
        ctx::location_event_s location(latitude, longitude, horizontal, timestamp, LOCATION_METHOD_REQUEST);
+#else /* TIZEN_ENGINEER_MODE */
+       ctx::location_event_s location(latitude, longitude, horizontal, timestamp);
+#endif /* TIZEN_ENGINEER_MODE */
        location_logger_p->broadcast(location);
        location_logger_p->on_active_request_succeeded();
 }
@@ -175,9 +187,11 @@ int ctx::LocationLogger::db_insert_log(location_event_s location_event)
        data.set(NULL, LOCATION_COLUMN_LONGITUDE, location_event.coordinates.longitude, GEO_LOCATION_PRECISION);
        data.set(NULL, LOCATION_COLUMN_ACCURACY, location_event.coordinates.accuracy, GEO_LOCATION_PRECISION);
        data.set(NULL, LOCATION_COLUMN_TIMESTAMP, static_cast<int>(location_event.timestamp));
+#ifdef TIZEN_ENGINEER_MODE
        std::string time_human = DebugUtils::human_readable_date_time(location_event.timestamp, "%F %T", 80);
        data.set(NULL, LOCATION_COLUMN_TIMESTAMP_HUMAN, time_human);
        data.set(NULL, LOCATION_COLUMN_METHOD, static_cast<int>(location_event.method));
+#endif /* TIZEN_ENGINEER_MODE */
 
        bool ret = ctx::db_manager::insert(0, LOCATION_TABLE_NAME, data);
        _D("%s -> DB: location table insert result", ret ? "SUCCESS" : "FAIL");
@@ -202,8 +216,9 @@ ctx::LocationLogger::LocationLogger(ILocationListener *listener_, bool test_mode
 
        manager_create();
 
-       if (test_mode) return;
-
+       if (test_mode) {
+               return;
+       }
        if (LOCATION_LOGGER_DATABASE) {
                create_table();
        }
@@ -336,7 +351,11 @@ bool ctx::LocationLogger::manager_get_location()
        active_attempts++;
        all_attempts++;
        if (ret == LOCATIONS_ERROR_NONE) {
+#ifdef TIZEN_ENGINEER_MODE
                ctx::location_event_s location(latitude, longitude, horizontal, timestamp, LOCATION_METHOD_GET_LOCATION);
+#else /* TIZEN_ENGINEER_MODE */
+               ctx::location_event_s location(latitude, longitude, horizontal, timestamp);
+#endif /* TIZEN_ENGINEER_MODE */
                broadcast(location);
                on_active_location_succeeded();
                return true;
@@ -365,7 +384,11 @@ void ctx::LocationLogger::manager_get_last_location()
        _LOCATION_ERROR_LOG(ret);
        all_attempts++;
        if (ret == LOCATIONS_ERROR_NONE) {
+#ifdef TIZEN_ENGINEER_MODE
                ctx::location_event_s location(latitude, longitude, horizontal, timestamp, LOCATION_METHOD_GET_LAST_LOCATION);
+#else /* TIZEN_ENGINEER_MODE */
+               ctx::location_event_s location(latitude, longitude, horizontal, timestamp);
+#endif /* TIZEN_ENGINEER_MODE */
                broadcast(location);
        }
 }
@@ -573,13 +596,17 @@ void ctx::LocationLogger::stop_logging()
 void ctx::LocationLogger::on_visit_start()
 {
        _D("");
-       if (!test_mode) start_logging();
+       if (!test_mode) {
+               start_logging();
+       }
 }
 
 void ctx::LocationLogger::on_visit_end()
 {
        _D("");
-       if (!test_mode) stop_logging();
+       if (!test_mode) {
+               stop_logging();
+       }
 }
 
 #undef _LOCATION_ERROR_LOG
diff --git a/src/place_recognition/user_places/place_categer.cpp b/src/place_recognition/user_places/place_categer.cpp
new file mode 100644 (file)
index 0000000..b1656e3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015 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 "place_categer.h"
+#include "median.h"
+#include "../place_recognition_types.h"
+#include "user_places_params.h"
+#include "user_places_types.h"
+#include <algorithm>
+#include <types_internal.h>
+
+void ctx::PlaceCateger::reduce_outliers(ctx::visits_t &visits)
+{
+       int size = visits.size();
+       visits.erase(std::remove_if(
+                                       visits.begin(),
+                                       visits.end(),
+                                       [](visit_s v) {
+                                               return v.categs[PLACE_CATEG_ID_HOME] < PLACES_CATEGER_MIN_VISITS_SCORE
+                                                               && v.categs[PLACE_CATEG_ID_WORK] < PLACES_CATEGER_MIN_VISITS_SCORE
+                                                               && v.categs[PLACE_CATEG_ID_OTHER] < PLACES_CATEGER_MIN_VISITS_SCORE;
+                                       }),
+                               visits.end());
+       int new_size = visits.size();
+       if (size != new_size) {
+               _D("Visits number from %d to %d (visits min scores checking)", size, new_size);
+       }
+}
+
+/*
+ * Change category if home or work has to few visits
+ */
+bool ctx::PlaceCateger::reduce_category(const place_categ_id_e &categ, const ctx::visits_t &visits)
+{
+       return (categ == PLACE_CATEG_ID_HOME && visits.size() < PLACES_CATEGER_MIN_VISITS_PER_HOME)
+               || (categ == PLACE_CATEG_ID_WORK && visits.size() < PLACES_CATEGER_MIN_VISITS_PER_WORK);
+}
+
+void ctx::PlaceCateger::categorize(ctx::visits_t &visits, ctx::Place &place)
+{
+       reduce_outliers(visits);
+
+       place.categ_id = PLACE_CATEG_ID_NONE;
+       place.categ_confidence = 0.0;
+
+       if (!visits.empty()) {
+               const std::vector<place_categ_id_e> categ_ids = {
+                       PLACE_CATEG_ID_HOME,
+                       PLACE_CATEG_ID_WORK,
+                       PLACE_CATEG_ID_OTHER
+               };
+               num_t sum_score = 0.0;
+               num_t max_score = 0.0;
+               for (place_categ_id_e categ_id : categ_ids) {
+                       std::vector<num_t> categ_vector = categ_vector_from_visits(visits, categ_id);
+                       num_t score = median(categ_vector);
+                       sum_score += score;
+                       if (score > max_score) {
+                               max_score = score;
+                               place.categ_id = categ_id;
+                       }
+               }
+               if (sum_score > 0) {
+                       place.categ_confidence = max_score / sum_score;
+               }
+               if (reduce_category(place.categ_id, visits)) {
+                       place.categ_id = PLACE_CATEG_ID_OTHER;
+                       place.categ_confidence = 0.0;
+               }
+       }
+
+       place.name = categ_id_to_name(place.categ_id);
+}
+
+std::vector<ctx::num_t> ctx::PlaceCateger::categ_vector_from_visits(const ctx::visits_t &visits, place_categ_id_e categ_id)
+{
+       std::vector<ctx::num_t> vec;
+       for (auto &visit : visits) {
+               auto search = visit.categs.find(categ_id);
+               if (search != visit.categs.end()) {
+                       vec.push_back(search->second);
+               }
+       }
+       return vec;
+}
+
+std::string ctx::PlaceCateger::categ_id_to_name(place_categ_id_e categ_id) {
+       switch (categ_id) {
+       case PLACE_CATEG_ID_HOME:  return "home";
+       case PLACE_CATEG_ID_WORK:  return "work";
+       case PLACE_CATEG_ID_OTHER: return "other";
+       case PLACE_CATEG_ID_NONE:  return "none";
+       default: return "";
+       }
+}
diff --git a/src/place_recognition/user_places/place_categer.h b/src/place_recognition/user_places/place_categer.h
new file mode 100644 (file)
index 0000000..cbab192
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 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_PLACE_STATUS_PLACE_CATEGER__
+#define __CONTEXT_PLACE_STATUS_PLACE_CATEGER__
+
+#include "user_places_types.h"
+#include <utility>
+#include <vector>
+#include <string>
+#include "../place_recognition_types.h"
+
+namespace ctx {
+
+       class PlaceCateger {
+
+       private:
+               static bool reduce_category(const place_categ_id_e &categ, const ctx::visits_t &visits);
+
+       public:
+               static void reduce_outliers(visits_t &visits);
+               static std::vector<ctx::num_t> categ_vector_from_visits(const ctx::visits_t &visits, place_categ_id_e categ_id);
+               static void categorize(ctx::visits_t &visits, ctx::Place &place);
+               static std::string categ_id_to_name(place_categ_id_e categ_id);
+
+       };      /* class PlaceCateger */
+
+}      /* namespace ctx */
+
+#endif /* __CONTEXT_PLACE_STATUS_PLACE_CATEGER__ */
diff --git a/src/place_recognition/user_places/places_detector.cpp b/src/place_recognition/user_places/places_detector.cpp
new file mode 100644 (file)
index 0000000..974fd7b
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2015 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 <sstream>
+#include <types_internal.h>
+#include <db_mgr.h>
+#include <json.h>
+#include "similar.h"
+#include "places_detector.h"
+#include "place_categer.h"
+#include "graph.h"
+#include "median.h"
+#ifdef TIZEN_ENGINEER_MODE
+#include "gmap.h"
+#endif /* TIZEN_ENGINEER_MODE */
+#include "../place_recognition_types.h"
+#include <fstream>
+#include <algorithm>
+#include "user_places_params.h"
+
+#define DELETE_PLACES_QUERY "DELETE FROM " PLACE_TABLE
+
+#ifdef TIZEN_ENGINEER_MODE
+#define USER_PLACES_FILE "/opt/usr/media/Others/user_places.txt" // TODO: Only for debug purposes -> Remove in final solution
+#endif /* TIZEN_ENGINEER_MODE */
+
+#define GET_VISITS_QUERY "SELECT "\
+       VISIT_COLUMN_END_TIME ", "\
+       VISIT_COLUMN_START_TIME ", "\
+       VISIT_COLUMN_WIFI_APS ", "\
+       VISIT_COLUMN_LOCATION_VALID ", "\
+       VISIT_COLUMN_LOCATION_LATITUDE ", "\
+       VISIT_COLUMN_LOCATION_LONGITUDE ", " \
+       VISIT_COLUMN_CATEG_HOME ", "\
+       VISIT_COLUMN_CATEG_WORK ", "\
+       VISIT_COLUMN_CATEG_OTHER \
+       " FROM " VISIT_TABLE
+
+#define GET_PLACES_QUERY "SELECT "\
+       PLACE_COLUMN_CATEG_ID ", "\
+       PLACE_COLUMN_CATEG_CONFIDENCE ", "\
+       PLACE_COLUMN_NAME ", "\
+       PLACE_COLUMN_LOCATION_VALID ", "\
+       PLACE_COLUMN_LOCATION_LATITUDE ", "\
+       PLACE_COLUMN_LOCATION_LONGITUDE ", " \
+       PLACE_COLUMN_WIFI_APS ", "\
+       PLACE_COLUMN_CREATE_DATE \
+       " FROM " PLACE_TABLE
+
+#define PLACE_TABLE_COLUMNS \
+       PLACE_COLUMN_CATEG_ID " INTEGER, "\
+       PLACE_COLUMN_CATEG_CONFIDENCE " REAL, "\
+       PLACE_COLUMN_NAME " TEXT, "\
+       PLACE_COLUMN_LOCATION_VALID " INTEGER, "\
+       PLACE_COLUMN_LOCATION_LATITUDE " REAL, "\
+       PLACE_COLUMN_LOCATION_LONGITUDE " REAL, "\
+       PLACE_COLUMN_WIFI_APS " STRING, "\
+       PLACE_COLUMN_CREATE_DATE " timestamp"
+
+bool ctx::PlacesDetector::on_timer_expired(int timer_id, void* user_data)
+{
+       _D("");
+       db_delete_places();
+       return true;
+}
+
+void ctx::PlacesDetector::on_query_result_received(unsigned int query_id, int error, std::vector<json>& records)
+{
+       // TODO:
+       // The below "state machine" approach was choosen because it is not possible to use synchronized database queries in the main thread.
+       // Probably the more elegant approach would be to use event_driven_thread class where synchronized queries are allowed.
+       // Consider refactoring.
+       if (error != ERR_NONE) {
+               _E("on_query_result_received query_id:%d, error:%d", query_id, error);
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_DELETE_PLACES) {
+               db_delete_old_visits();
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_DELETE_OLD_VISITS) {
+               db_get_visits();
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_GET_VISITS) {
+               visits_t visits = visits_from_jsons(records);
+               process_visits(visits);
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_INSERT_VISIT) {
+               // Don't do anything. It is fine.
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_INSERT_PLACE) {
+               // Don't do anything. It is fine.
+       }
+       else if (query_id == PLACES_DETECTOR_QUERY_ID_GET_PLACES) {
+               std::vector<std::shared_ptr<Place>> db_places = places_from_jsons(records);
+               detected_places_update(db_places);
+       }
+       else {
+               _E("on_query_result_received unknown query_id:%d", query_id);
+       }
+}
+
+void ctx::PlacesDetector::db_get_visits()
+{
+       bool ret = db_manager::execute(PLACES_DETECTOR_QUERY_ID_GET_VISITS, GET_VISITS_QUERY, this);
+       _D("load visits execute query result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+void ctx::PlacesDetector::db_get_places()
+{
+       bool ret = db_manager::execute(PLACES_DETECTOR_QUERY_ID_GET_PLACES, GET_PLACES_QUERY, this);
+       _D("load places execute query result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+double ctx::PlacesDetector::double_value_from_json(json &row, const char* key)
+{
+       double value;
+       row.get(NULL, key, &value);
+       _D("double_value_from_json, key:%s, value: %lf", key, value);
+       return value;
+}
+
+ctx::categs_t ctx::PlacesDetector::visit_categs_from_json(json &row)
+{
+       categs_t categs;
+       categs[PLACE_CATEG_ID_HOME] = double_value_from_json(row, VISIT_COLUMN_CATEG_HOME);
+       categs[PLACE_CATEG_ID_WORK] = double_value_from_json(row, VISIT_COLUMN_CATEG_WORK);
+       categs[PLACE_CATEG_ID_OTHER] = double_value_from_json(row, VISIT_COLUMN_CATEG_OTHER);
+       return categs;
+}
+
+ctx::visit_s ctx::PlacesDetector::visit_from_json(json &row)
+{
+       int start_time;
+       int end_time;
+       std::string mac_set_string;
+       row.get(NULL, VISIT_COLUMN_START_TIME, &start_time);
+       row.get(NULL, VISIT_COLUMN_END_TIME, &end_time);
+       row.get(NULL, VISIT_COLUMN_WIFI_APS, &mac_set_string);
+
+       std::stringstream mac_set_ss;
+       mac_set_ss << mac_set_string;
+       std::shared_ptr<mac_set_t> mac_set = std::make_shared<mac_set_t>();
+       mac_set_ss >> *mac_set;
+
+       interval_s interval(start_time, end_time);
+       categs_t categs = visit_categs_from_json(row);
+
+       visit_s visit(interval, mac_set, categs);
+
+       { // location
+               int location_valid_int;
+               row.get(NULL, VISIT_COLUMN_LOCATION_VALID, &location_valid_int);
+               visit.location_valid = (bool) location_valid_int;
+               row.get(NULL, VISIT_COLUMN_LOCATION_LATITUDE, &(visit.location.latitude));
+               row.get(NULL, VISIT_COLUMN_LOCATION_LONGITUDE, &(visit.location.longitude));
+       }
+
+       return visit;
+}
+
+ctx::visits_t ctx::PlacesDetector::visits_from_jsons(std::vector<json>& records)
+{
+       visits_t visits;
+       _D("db_result: number of all visits: %d", records.size());
+
+       for (json &row : records) {
+               visit_s visit = visit_from_json(row);
+               visits.push_back(visit);
+       }
+       _D("number of all visits in vector: %d", visits.size());
+       return visits;
+}
+
+std::shared_ptr<ctx::Place> ctx::PlacesDetector::place_from_json(json &row)
+{
+       std::shared_ptr<Place> place = std::make_shared<Place>();
+       { // category
+               int categ_id;
+               row.get(NULL, PLACE_COLUMN_CATEG_ID, &categ_id);
+               // This is due to the fact the JSON module API interface doesn't handle enum
+               place->categ_id = static_cast<place_categ_id_e>(categ_id);
+       }
+       row.get(NULL, PLACE_COLUMN_NAME, &(place->name));
+       row.get(NULL, PLACE_COLUMN_WIFI_APS, &(place->wifi_aps));
+       { // location
+               int location_valid_int;
+               row.get(NULL, PLACE_COLUMN_LOCATION_VALID, &location_valid_int);
+               place->location_valid = (bool) location_valid_int;
+               row.get(NULL, PLACE_COLUMN_LOCATION_LATITUDE, &(place->location.latitude));
+               row.get(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, &(place->location.longitude));
+       }
+       { // create_date
+               int create_date;
+               row.get(NULL, PLACE_COLUMN_CREATE_DATE, &(create_date));
+               // This is due to the fact the JSON module API interface doesn't handle time_t
+               place->create_date = static_cast<time_t>(create_date);
+       }
+       _D("db_result: categ_id: %d; place: name: %s; wifi_aps: %s; location_valid: %d; latitude: %lf, longitude: %lf, create_date: %d", place->categ_id, place->name.c_str(), place->wifi_aps.c_str(), place->location_valid, place->location.latitude, place->location.longitude, place->create_date);
+       return place;
+}
+
+std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::places_from_jsons(std::vector<json>& records)
+{
+       std::vector<std::shared_ptr<Place>> places;
+       _D("db_result: number of all places: %d", records.size());
+
+       for (json &row : records) {
+               std::shared_ptr<Place> place = place_from_json(row);
+               places.push_back(place);
+       }
+       _D("number of all places in vector: %d", places.size());
+       return places;
+}
+
+void ctx::PlacesDetector::reduce_outliers(ctx::visits_t &visits)
+{
+       int size = visits.size();
+       visits.erase(std::remove_if(
+                                       visits.begin(),
+                                       visits.end(),
+                                       [](visit_s v) {
+                                               int minutes = (v.interval.end - v.interval.start) / 60;
+                                               return (minutes < PLACES_DETECTOR_MIN_VISIT_DURATION_MINUTES)
+                                                               || (minutes > PLACES_DETECTOR_MAX_VISIT_DURATION_MINUTES);
+                                       }),
+                               visits.end());
+       int new_size = visits.size();
+       if (size != new_size) {
+               _D("Visits number from %d to %d (to short and to long reduction)", size, new_size);
+       }
+}
+
+void ctx::PlacesDetector::process_visits(ctx::visits_t &visits)
+{
+       reduce_outliers(visits);
+
+       _D("test_mode = %d", test_mode);
+       auto components = merge_visits(visits);
+       std::vector<std::shared_ptr<Place>> new_detected_places;
+#ifdef TIZEN_ENGINEER_MODE
+       std::vector<visits_t> places_visits; // TODO: remove from final solution.
+#endif /* TIZEN_ENGINEER_MODE */
+       for (std::shared_ptr<component_t> component : *components) {
+               // Small places outliers reduction
+               if (!test_mode && component->size() < PLACES_DETECTOR_MIN_VISITS_PER_BIG_PLACE) {
+                       continue;
+               }
+
+               std::shared_ptr<visits_t> merged = std::make_shared<visits_t>();
+               for (node_t i : *component) {
+                       merged->push_back(visits[i]);
+               }
+               std::shared_ptr<Place> place = place_from_merged(*merged);
+               if (place->categ_id == PLACE_CATEG_ID_NONE) {
+                       continue;
+               }
+               new_detected_places.push_back(place);
+               if (!test_mode) {
+                       db_insert_place(*place);
+               }
+
+#ifdef TIZEN_ENGINEER_MODE
+               { // TODO: Only for debug -> remove in final solution
+                       visits_t place_visits;
+                       for (node_t i : *component) {
+                               place_visits.push_back(visits[i]);
+                       }
+                       places_visits.push_back(place_visits);
+               }
+#endif /* TIZEN_ENGINEER_MODE */
+       }
+
+       detected_places_update(new_detected_places);
+
+#ifdef TIZEN_ENGINEER_MODE
+       { // Print to file TODO: Only for debug -> remove in final solution
+               std::ofstream out(USER_PLACES_FILE);
+               for (size_t i = 0; i < new_detected_places.size(); i++) {
+                       new_detected_places[i]->print_to_stream(out);
+                       visits_t place_visits = places_visits[i];
+                       for (visit_s visit : place_visits) {
+                               visit.print_short_to_stream(out);
+                       }
+               }
+               out.close();
+               Gmap::write_map(detected_places);
+       }
+#endif /* TIZEN_ENGINEER_MODE */
+}
+
+/*
+ * Pseudo-atomic operation of old places replacement by new ones.
+ */
+void ctx::PlacesDetector::detected_places_update(std::vector<std::shared_ptr<Place>> &new_places)
+{
+       _D("");
+       detected_places_access_mutex.lock();
+       detected_places = new_places;
+       new_places.clear();
+       detected_places_access_mutex.unlock();
+}
+
+void ctx::PlacesDetector::merge_location(const visits_t &visits, Place &place)
+{
+       place.location_valid = false;
+       std::vector<double> latitudes;
+       std::vector<double> longitudes;
+       for (const visit_s& visit : visits) {
+               if (visit.location_valid) {
+                       latitudes.push_back(visit.location.latitude);
+                       longitudes.push_back(visit.location.longitude);
+                       place.location_valid = true;
+               }
+       }
+       if (place.location_valid) {
+               place.location.latitude = median(latitudes);
+               place.location.longitude = median(longitudes);
+       }
+}
+
+std::shared_ptr<ctx::Place> ctx::PlacesDetector::place_from_merged(visits_t &merged_visits)
+{
+       std::shared_ptr<Place> place = std::make_shared<Place>();
+       place->create_date = std::time(nullptr);
+       std::vector<std::shared_ptr<mac_set_t>> mac_sets;
+       for (const visit_s &visit : merged_visits) {
+               mac_sets.push_back(visit.mac_set);
+       }
+       std::shared_ptr<mac_set_t> all_macs = mac_sets_union(mac_sets);
+       std::stringstream all_macs_ss;
+       all_macs_ss << *all_macs;
+       place->wifi_aps = all_macs_ss.str();
+
+       merge_location(merged_visits, *place);
+
+       PlaceCateger::categorize(merged_visits, *place);
+
+       return place;
+}
+
+void ctx::PlacesDetector::reduce_outliers(std::shared_ptr<ctx::components_t> &cc)
+{
+       int size = cc->size();
+       cc->erase(std::remove_if(cc->begin(),
+                                                        cc->end(),
+                                                        [](std::shared_ptr<component_t> &c) {
+                                                                return c->size() < PLACES_DETECTOR_MIN_VISITS_PER_PLACE;
+                                                        }),
+                         cc->end());
+       int new_size = cc->size();
+       if (size != new_size) {
+               _D("Connected components from %d to %d (min visit per place)", size, new_size);
+       }
+}
+
+std::shared_ptr<ctx::components_t> ctx::PlacesDetector::merge_visits(const std::vector<visit_s> &visits)
+{
+       auto graph = graph_from_visits(visits);
+       auto cc = connected_components(*graph);
+       reduce_outliers(cc);
+       return cc;
+}
+
+std::shared_ptr<ctx::graph_t> ctx::PlacesDetector::graph_from_visits(const std::vector<visit_s> &visits)
+{
+       std::shared_ptr<graph_t> graph = std::make_shared<graph_t>();
+       graph->resize(visits.size());
+       for (size_t i = 0; i < visits.size(); i++) {
+               (*graph)[i] = std::make_shared<nhood_t>();
+               for (size_t j = 0; j < i; j++) {
+                       if (is_joint(*visits[i].mac_set, *visits[j].mac_set)) {
+                               (*graph)[i]->insert(j);
+                               (*graph)[j]->insert(i);
+                       }
+               }
+       }
+       return graph;
+}
+
+void ctx::PlacesDetector::db_delete_places()
+{
+       bool ret = db_manager::execute(PLACES_DETECTOR_QUERY_ID_DELETE_PLACES, DELETE_PLACES_QUERY, this);
+       _D("delete places execute query result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+void ctx::PlacesDetector::db_delete_old_visits()
+{
+       time_t current_time;
+       time(&current_time);
+       time_t threshold_time = current_time - PLACES_DETECTOR_RETENTION_SECONDS;
+       db_delete_older_visits(threshold_time);
+}
+
+void ctx::PlacesDetector::db_delete_older_visits(time_t threshold)
+{
+       _D("deleting vistits older than: %d", threshold);
+       std::stringstream query;
+       query << "DELETE FROM " << VISIT_TABLE;
+       query << " WHERE " << VISIT_COLUMN_END_TIME << " < " << threshold;
+       // query << " AND 0"; // Always false condition. Uncomment it for not deleting any visit during development.
+       bool ret = db_manager::execute(PLACES_DETECTOR_QUERY_ID_DELETE_OLD_VISITS, query.str().c_str(), this);
+       _D("delete old visits execute query result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+ctx::PlacesDetector::PlacesDetector(bool test_mode_)
+       : test_mode(test_mode_)
+{
+       if (test_mode) {
+               return;
+       }
+       db_create_table();
+       db_get_places();
+}
+
+void ctx::PlacesDetector::db_create_table()
+{
+       bool ret = db_manager::create_table(0, PLACE_TABLE, PLACE_TABLE_COLUMNS);
+       _D("db: place Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+void ctx::PlacesDetector::db_insert_place(const Place &place)
+{
+       json data;
+       data.set(NULL, PLACE_COLUMN_CATEG_ID, place.categ_id);
+       data.set(NULL, PLACE_COLUMN_CATEG_CONFIDENCE, place.categ_confidence);
+       data.set(NULL, PLACE_COLUMN_NAME, place.name);
+
+       data.set(NULL, PLACE_COLUMN_LOCATION_VALID, place.location_valid);
+       data.set(NULL, PLACE_COLUMN_LOCATION_LATITUDE, place.location.latitude, GEO_LOCATION_PRECISION);
+       data.set(NULL, PLACE_COLUMN_LOCATION_LONGITUDE, place.location.longitude, GEO_LOCATION_PRECISION);
+
+       data.set(NULL, PLACE_COLUMN_WIFI_APS, place.wifi_aps);
+       data.set(NULL, PLACE_COLUMN_CREATE_DATE, static_cast<int>(place.create_date));
+
+       bool ret = db_manager::insert(PLACES_DETECTOR_QUERY_ID_INSERT_PLACE, PLACE_TABLE, data);
+       _D("insert place execute query result: %s", ret ? "SUCCESS" : "FAIL");
+}
+
+std::vector<std::shared_ptr<ctx::Place>> ctx::PlacesDetector::get_places()
+{
+       detected_places_access_mutex.lock();
+       // indirect ret vector usage due to limit the scope of a mutex to only this single file / class
+       std::vector<std::shared_ptr<Place>> ret = detected_places;
+       detected_places_access_mutex.unlock();
+       return ret;
+}
diff --git a/src/place_recognition/user_places/places_detector.h b/src/place_recognition/user_places/places_detector.h
new file mode 100644 (file)
index 0000000..72767c4
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 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_PLACE_STATUS_PLACES_DETECTOR__
+#define __CONTEXT_PLACE_STATUS_PLACES_DETECTOR__
+
+#include "visit_detector.h"
+#include "timer_listener_iface.h"
+#include <cstdint>
+#include "db_listener_iface.h"
+#include "user_places_types.h"
+#include <vector>
+#include <mutex>
+
+namespace ctx {
+
+       enum {
+               PLACES_DETECTOR_QUERY_ID_DELETE_PLACES = 1,
+               PLACES_DETECTOR_QUERY_ID_DELETE_OLD_VISITS = 2,
+               PLACES_DETECTOR_QUERY_ID_GET_VISITS = 3,
+               PLACES_DETECTOR_QUERY_ID_INSERT_VISIT = 4,
+               PLACES_DETECTOR_QUERY_ID_INSERT_PLACE = 5,
+               PLACES_DETECTOR_QUERY_ID_GET_PLACES = 6
+       };
+
+       class PlacesDetector : public timer_listener_iface, public db_listener_iface {
+
+       private:
+               bool test_mode;
+               double double_value_from_json(json &row, const char* key);
+               categs_t visit_categs_from_json(json &row);
+               visit_s visit_from_json(json &row);
+               visits_t visits_from_jsons(std::vector<json>& records);
+               std::shared_ptr<ctx::Place> place_from_json(json &row);
+               std::vector<std::shared_ptr<Place>> places_from_jsons(std::vector<json>& records);
+               std::shared_ptr<graph_t> graph_from_visits(const std::vector<visit_s> &visits);
+               void db_create_table();
+               void db_delete_places();
+               void db_delete_old_visits();
+               void db_delete_older_visits(time_t threshold);
+               void db_get_visits();
+               void db_get_places();
+               void db_insert_place(const Place &place);
+               std::shared_ptr<Place> place_from_merged(visits_t &merged_visits);
+               std::vector<std::shared_ptr<Place>> detected_places;
+               std::mutex detected_places_access_mutex;
+               void detected_places_update(std::vector<std::shared_ptr<Place>> &new_places);
+
+       public:
+               static void reduce_outliers(visits_t &visits);
+               static void reduce_outliers(std::shared_ptr<components_t> &cc);
+               void process_visits(visits_t &visits);
+               static void merge_location(const visits_t &merged_visits, Place &place);
+               PlacesDetector(bool test_mode_ = false);
+               bool on_timer_expired(int timer_id, void* user_data);
+               void on_creation_result_received(unsigned int query_id, int error) {}
+               void on_insertion_result_received(unsigned int query_id, int error, int64_t row_id) {}
+               void on_query_result_received(unsigned int query_id, int error, std::vector<json>& records);
+               std::shared_ptr<components_t> merge_visits(const std::vector<visit_s> &visits);
+               std::vector<std::shared_ptr<Place>> get_places();
+
+       };  /* class PlacesDetector */
+
+}      /* namespace ctx */
+
+#endif /* __CONTEXT_PLACE_STATUS_PLACES_DETECTOR__ */
index 56fb189..d6152ff 100644 (file)
 #include <memory>
 #include <types_internal.h>
 #include "user_places.h"
+#include "places_detector.h"
 #include "timer_mgr.h"
 #include "../place_recognition_types.h"
 #include "db_mgr.h"
 
 ctx::UserPlaces::UserPlaces(place_recog_mode_e energy_mode)
        : visit_detector(nullptr)
+       , places_detector(nullptr)
+       , places_detector_timer_id(-1)
 {
        time_t now = std::time(nullptr);
        visit_detector = new(std::nothrow) VisitDetector(now, energy_mode);
@@ -32,6 +35,23 @@ ctx::UserPlaces::UserPlaces(place_recog_mode_e energy_mode)
                return;
        }
 
+       places_detector = new(std::nothrow) PlacesDetector();
+       if (places_detector == nullptr) {
+               _E("Cannot initialize places_detector");
+               return;
+       }
+
+       places_detector_timer_id = timer_manager::set_at( // execute once every night
+                       PLACES_DETECTOR_TASK_START_HOUR,
+                       PLACES_DETECTOR_TASK_START_MINUTE,
+                       timer_manager::EVERYDAY,
+                       places_detector);
+       if (places_detector_timer_id < 0) {
+               _E("PlacesDetector timer set FAIL");
+               return;
+       } else {
+               _D("PlacesDetector timer set SUCCESS");
+       }
 }
 
 ctx::UserPlaces::~UserPlaces()
@@ -45,8 +65,70 @@ ctx::UserPlaces::~UserPlaces()
                delete visit_detector;
        }
 
+       if (places_detector) {
+               delete places_detector;
+       }
+};
+
+std::vector<std::shared_ptr<ctx::Place>> ctx::UserPlaces::get_places()
+{
+       if (places_detector) {
+               return places_detector->get_places();
+       } else {
+               return std::vector<std::shared_ptr<ctx::Place>>();
+       }
 }
 
+/*
+ * Example JSON output:
+ * ------------------------------------------------
+ * {
+ *       "PlacesList": [
+ *             {
+ *               "TypeId": 2,
+ *               "Name": "Work",
+ *               "GeoLatitude": 10.94433,
+ *               "GeoLongitude": 50.85504,
+ *               "WifiAPs": "00:1f:f3:5b:2b:1f,15:34:56:78:9a:ba,13:34:56:78:9a:ba",
+ *               "CreateDate": 12132567
+ *             },
+ *             {
+ *               "TypeId": 1,
+ *               "Name": "Home",
+ *               "GeoLatitude": 10.96233,
+ *               "GeoLongitude": 50.84304,
+ *               "WifiAPs": "aa:bb:cc:dd:ee:ff,10:34:56:78:9a:bc",
+ *               "CreateDate": 12132889
+ *             },
+ *             {
+ *               "TypeId": 3,
+ *               "Name": "Other",
+ *               "GeoLatitude": 10.96553,
+ *               "GeoLongitude": 50.80404,
+ *               "WifiAPs": "12:34:56:78:9a:ba",
+ *               "CreateDate": 12132346
+ *             }
+ *       ]
+ * }
+ */
+ctx::json ctx::UserPlaces::compose_json(std::vector<std::shared_ptr<Place>> places)
+{
+       ctx::json data;
+       for (std::shared_ptr<ctx::Place> place : places) {
+               ctx::json place_j;
+               place_j.set(NULL, PLACE_CATEG_ID, place->categ_id);
+               place_j.set(NULL, PLACE_CATEG_CONFIDENCE, place->categ_confidence);
+               place_j.set(NULL, PLACE_NAME, place->name);
+               if (place->location_valid) {
+                       place_j.set(NULL, PLACE_GEO_LATITUDE, place->location.latitude, GEO_LOCATION_PRECISION);
+                       place_j.set(NULL, PLACE_GEO_LONGITUDE, place->location.longitude, GEO_LOCATION_PRECISION);
+               }
+               place_j.set(NULL, PLACE_WIFI_APS, place->wifi_aps);
+               place_j.set(NULL, PLACE_CREATE_DATE, static_cast<int>(place->create_date));
+               data.array_append(NULL, DATA_READ, place_j);
+       }
+       return data;
+}
 
 void ctx::UserPlaces::set_mode(place_recog_mode_e energy_mode)
 {
index 6c11930..2ad10ce 100644 (file)
@@ -18,6 +18,7 @@
 #define __CONTEXT_PLACE_STATUS_USER_PLACES_ENGINE_H__
 
 #include "visit_detector.h"
+#include "places_detector.h"
 #include <vector>
 #include "user_places_types.h"
 #include <json.h>
@@ -28,12 +29,16 @@ namespace ctx {
 
        private:
                VisitDetector *visit_detector;
+               PlacesDetector *places_detector;
                int places_detector_timer_id;
+
        public:
                UserPlaces(place_recog_mode_e energy_mode = PLACE_RECOG_HIGH_ACCURACY_MODE);
                ~UserPlaces();
 
                void set_mode(place_recog_mode_e energy_mode);
+               std::vector<std::shared_ptr<Place>> get_places();
+               static json compose_json(std::vector<std::shared_ptr<Place>> places);
 
        };      /* class UserPlaces */
 
index f392799..fc041c0 100644 (file)
  */
 #define VISIT_DETECTOR_TOLERANCE_DEPTH 3
 
+#define PLACES_DETECTOR_TASK_START_HOUR 3
+#define PLACES_DETECTOR_TASK_START_MINUTE 11
+#define PLACES_DETECTOR_RETENTION_DAYS 30
+#define PLACES_DETECTOR_RETENTION_SECONDS 24 * 60 * 60 * PLACES_DETECTOR_RETENTION_DAYS
 
 /*
  * Number of digits after decimal point used in geo coordinates.
  */
 #define GEO_LOCATION_PRECISION 7
 
+/*
+ * Minimal duration of visit (in minutes) taking into account for place detection
+ */
+#define PLACES_DETECTOR_MIN_VISIT_DURATION_MINUTES 15
+
+/*
+ * Maximum duration of visit (in minutes) taking into account for place detection
+ */
+#define PLACES_DETECTOR_MAX_VISIT_DURATION_MINUTES 5 * 24 * 60
+
+/*
+ * Minimum visits number per place
+ */
+#define PLACES_DETECTOR_MIN_VISITS_PER_PLACE 1
+
+/*
+ * Minimum visits number per big place
+ */
+#define PLACES_DETECTOR_MIN_VISITS_PER_BIG_PLACE 4
+
+/*
+ * Minimal visit category score for taking this visit into consideration during
+ * place categorization
+ */
+#define PLACES_CATEGER_MIN_VISITS_SCORE 0.1
+
+/*
+ * Minimum visits number per home
+ */
+#define PLACES_CATEGER_MIN_VISITS_PER_HOME 3
+
+/*
+ * Minimum visits number per work
+ */
+#define PLACES_CATEGER_MIN_VISITS_PER_WORK 2
 
 #endif /* __CONTEXT_PLACE_STATUS_USER_PLACES_PARAMS_H__ */
index 38cae34..3d72852 100644 (file)
@@ -21,6 +21,7 @@
 #include <algorithm>
 #include <types_internal.h>
 #include "user_places_types.h"
+#include "user_places_params.h"
 #include "debug_utils.h"
 
 #define MAC_SET_STRING_DELIMITER ','
@@ -47,8 +48,9 @@ std::istream& ctx::operator>>(std::istream &input, ctx::Mac &mac)
                input >> h;
                mac.c[i] = h;
                i++;
-               if (i >= ctx::Mac::MAC_SIZE)
+               if (i >= ctx::Mac::MAC_SIZE) {
                        break;
+               }
                input >> colon;
                if (colon != ':') {
                        throw std::runtime_error("Invalid mac format");
@@ -65,8 +67,9 @@ std::ostream& ctx::operator<<(std::ostream &output, const ctx::Mac &mac)
                output << std::hex << std::setfill('0') << std::setw(2);
                output << static_cast<int>(mac.c[i]);
                i++;
-               if (i >= Mac::MAC_SIZE)
+               if (i >= Mac::MAC_SIZE) {
                        break;
+               }
                output << ":";
        }
        output << std::dec;
@@ -151,12 +154,20 @@ std::ostream& ctx::operator<<(std::ostream &output, const ctx::mac_set_t &mac_se
 void ctx::location_event_s::log()
 {
        std::string time_str = DebugUtils::human_readable_date_time(timestamp, "%T", 9);
+#ifdef TIZEN_ENGINEER_MODE
        _D("location lat=%.8f, lon=%.8f, acc=%.2f[m], time=%s, method=%d",
                        coordinates.latitude,
                        coordinates.longitude,
                        coordinates.accuracy,
                        time_str.c_str(),
                        method);
+#else /* TIZEN_ENGINEER_MODE */
+       _D("location lat=%.8f, lon=%.8f, acc=%.2f[m], time=%s",
+                       coordinates.latitude,
+                       coordinates.longitude,
+                       coordinates.accuracy,
+                       time_str.c_str());
+#endif /* TIZEN_ENGINEER_MODE */
 }
 
 void ctx::visit_s::set_location(location_s location_)
@@ -225,3 +236,15 @@ ctx::interval_s::interval_s(time_t start_, time_t end_) : start(start_), end(end
                _E("Negative interval, start=%d, end=%d", start_, end_);
        }
 }
+
+void ctx::Place::print_to_stream(std::ostream &out) const
+{
+       out << "PLACE:" << std::endl;
+       out << "__CATEGORY: " << name << std::endl;
+       if (location_valid) {
+               out << "__LOCATION: lat=" << std::setprecision(GEO_LOCATION_PRECISION + 2) << location.latitude;
+               out << ", lon=" << location.longitude << std::setprecision(5) << std::endl;
+       }
+       out << "__WIFI:" << wifi_aps << std::endl;
+       out << "__CREATE_DATE: " << DebugUtils::human_readable_date_time(create_date, "%F %T", 80) << std::endl;
+}
index 251170d..8eed322 100644 (file)
@@ -22,6 +22,7 @@
 #include <map>
 #include <unordered_map>
 #include <unordered_set>
+#include "graph.h"
 #include "../place_recognition_types.h"
 #include <string>
 #include <ctime>
@@ -37,6 +38,7 @@ namespace ctx {
         * mac address
         */
        class Mac {
+
        public:
                const static size_t MAC_SIZE = 6;  // number of bytes for mac address.
                unsigned char c[MAC_SIZE];
@@ -141,11 +143,13 @@ namespace ctx {
 
        };      /* struct location_s */
 
+#ifdef TIZEN_ENGINEER_MODE
        typedef enum {
                LOCATION_METHOD_REQUEST = 0,
                LOCATION_METHOD_GET_LOCATION = 1,
                LOCATION_METHOD_GET_LAST_LOCATION = 2
        } location_source_e;
+#endif /* TIZEN_ENGINEER_MODE */
 
        /*
         * location + timestamp + method
@@ -153,10 +157,17 @@ namespace ctx {
        struct location_event_s {
                location_s coordinates;
                time_t timestamp;
+
+#ifdef TIZEN_ENGINEER_MODE
                location_source_e method;
 
                location_event_s(double latitude_, double longitude_, double accuracy_, time_t timestamp_, location_source_e method_)
                        : coordinates(latitude_, longitude_, accuracy_), timestamp(timestamp_), method(method_) {}
+#else /* TIZEN_ENGINEER_MODE */
+               location_event_s(double latitude_, double longitude_, double accuracy_, time_t timestamp_)
+                       : coordinates(latitude_, longitude_, accuracy_), timestamp(timestamp_) {}
+#endif /* TIZEN_ENGINEER_MODE */
+
                void log();
 
        };      /* struct location_event_s */
@@ -184,6 +195,23 @@ namespace ctx {
 
        std::shared_ptr<mac_set_t> mac_set_from_mac_counts(const mac_counts_t &mac_counts);
 
+       typedef float confidence_t;
+
+       class Place {
+
+       public:
+               place_categ_id_e categ_id; // category of a place (work/home/other)
+               confidence_t categ_confidence; // confidence of the above category - between [0,1]
+               std::string name; // for now: "work"/"home"/"other"
+               bool location_valid;
+               location_s location; // makes sense if location_valid == true;
+               std::string wifi_aps; // WiFi APs MAC addresses separated by ","
+               time_t create_date; // The last update time of this place
+
+               void print_to_stream(std::ostream &out) const;
+
+       };      /* class Place */
+
 }      /* namespace ctx */
 
 #endif /*__CONTEXT_PLACE_STATUS_USER_PLACES_TYPES_H__*/
index c16df29..0123b93 100644 (file)
@@ -29,6 +29,7 @@
 #include "median.h"
 #include "debug_utils.h"
 
+#ifdef TIZEN_ENGINEER_MODE
 #define VISIT_TABLE_COLUMNS \
        VISIT_COLUMN_WIFI_APS " TEXT, "\
        VISIT_COLUMN_START_TIME " timestamp, "\
        VISIT_COLUMN_CATEG_HOME " REAL, "\
        VISIT_COLUMN_CATEG_WORK " REAL, "\
        VISIT_COLUMN_CATEG_OTHER " REAL"
+#else /* TIZEN_ENGINEER_MODE */
+#define VISIT_TABLE_COLUMNS \
+       VISIT_COLUMN_WIFI_APS " TEXT, "\
+       VISIT_COLUMN_START_TIME " timestamp, "\
+       VISIT_COLUMN_END_TIME " timestamp, "\
+       VISIT_COLUMN_LOCATION_VALID " INTEGER, "\
+       VISIT_COLUMN_LOCATION_LATITUDE " REAL, "\
+       VISIT_COLUMN_LOCATION_LONGITUDE " REAL, "\
+       VISIT_COLUMN_CATEG_HOME " REAL, "\
+       VISIT_COLUMN_CATEG_WORK " REAL, "\
+       VISIT_COLUMN_CATEG_OTHER " REAL"
+#endif /* TIZEN_ENGINEER_MODE */
+
 
 ctx::VisitDetector::VisitDetector(time_t t_start_scan, place_recog_mode_e energy_mode, bool test_mode_)
        : test_mode(test_mode_)
@@ -298,11 +312,6 @@ std::shared_ptr<ctx::mac_set_t> ctx::VisitDetector::select_representatives(const
        mac_counts_t repr_counts;
        count_t all_count = 0;
 
-       /* in python there were also:
-       if (frames.empty()) {
-               return make_shared<mac_set_t>();
-       } */
-
        for (auto frame : frames) {
                all_count += frame->no_timestamps;
                for (auto &c : frame->mac_counts) {
@@ -385,9 +394,6 @@ int ctx::VisitDetector::db_insert_visit(visit_s visit)
        std::stringstream macs_ss;
        macs_ss << *visit.mac_set;
 
-       std::string start_time_human = DebugUtils::human_readable_date_time(visit.interval.start, "%F %T", 80);
-       std::string end_time_human = DebugUtils::human_readable_date_time(visit.interval.end, "%F %T", 80);
-
        json data;
        data.set(NULL, VISIT_COLUMN_WIFI_APS, macs_ss.str().c_str());
 
@@ -397,15 +403,23 @@ int ctx::VisitDetector::db_insert_visit(visit_s visit)
 
        data.set(NULL, VISIT_COLUMN_START_TIME, static_cast<int>(visit.interval.start));
        data.set(NULL, VISIT_COLUMN_END_TIME, static_cast<int>(visit.interval.end));
-       // for debug:
+
+#ifdef TIZEN_ENGINEER_MODE
+       std::string start_time_human = DebugUtils::human_readable_date_time(visit.interval.start, "%F %T", 80);
+       std::string end_time_human = DebugUtils::human_readable_date_time(visit.interval.end, "%F %T", 80);
        data.set(NULL, VISIT_COLUMN_START_TIME_HUMAN, start_time_human.c_str());
        data.set(NULL, VISIT_COLUMN_END_TIME_HUMAN, end_time_human.c_str());
 
        json_put_visit_categs(data, visit.categs);
 
        _D("db: visit table insert interval: (%d, %d): (%s, %s)",
-                       visit.interval.start, visit.interval.end,
-                       start_time_human.c_str(), end_time_human.c_str());
+                       visit.interval.start, visit.interval.end, start_time_human.c_str(), end_time_human.c_str());
+#else
+       json_put_visit_categs(data, visit.categs);
+
+       _D("db: visit table insert interval: (%d, %d)", visit.interval.start, visit.interval.end);
+#endif /* TIZEN_ENGINEER_MODE */
+
        bool ret = db_manager::insert(0, VISIT_TABLE, data);
        _D("db: visit table insert result: %s", ret ? "SUCCESS" : "FAIL");
        return ret;
index 936e3a3..92bcdd4 100644 (file)
@@ -76,8 +76,9 @@ ctx::WifiLogger::WifiLogger(IWifiListener * listener_, place_recog_mode_e energy
        , running(false)
 {
        _D("CONSTRUCTOR");
-       if (test_mode) return;
-
+       if (test_mode) {
+               return;
+       }
        set_interval(energy_mode);
 
        if (WIFI_LOGGER_DATABASE) {
@@ -140,7 +141,7 @@ void ctx::WifiLogger::wifi_connection_state_changed_cb(wifi_connection_state_e s
                wifi_logger_p->last_scans_pool.clear();
                break;
        }
-       // TODO: Check if AP bssid (MAC Address) will be helpful somehow
+       // TODO: Check if AP bssid (MAC Address) will be helpful somehow in LOW_POWER mode
 }
 
 bool ctx::WifiLogger::wifi_found_ap_cb(wifi_ap_h ap, void *user_data)
@@ -218,6 +219,7 @@ void ctx::WifiLogger::wifi_scan_finished_cb(wifi_error_e error_code, void *user_
        ctx::WifiLogger* wifi_logger_p = (ctx::WifiLogger *)user_data;
 
        time_t now = time(nullptr);
+#ifdef TIZEN_ENGINEER_MODE
        double seconds = 0;
        if (wifi_logger_p->last_scan_time > 0) {
                seconds = difftime(now, wifi_logger_p->last_scan_time);
@@ -229,6 +231,7 @@ void ctx::WifiLogger::wifi_scan_finished_cb(wifi_error_e error_code, void *user_
                        wifi_logger_p->last_scans_pool.size(),
                        time_str.c_str(),
                        seconds);
+#endif /* TIZEN_ENGINEER_MODE */
        wifi_logger_p->last_scan_time = now;
 
        int ret = wifi_foreach_found_aps_request(user_data);