_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.
#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 {
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
#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) { \
_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();
}
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");
manager_create();
- if (test_mode) return;
-
+ if (test_mode) {
+ return;
+ }
if (LOCATION_LOGGER_DATABASE) {
create_table();
}
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;
_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);
}
}
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
--- /dev/null
+/*
+ * 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 "";
+ }
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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(¤t_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;
+}
--- /dev/null
+/*
+ * 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__ */
#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);
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()
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)
{
#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>
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 */
*/
#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__ */
#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 ','
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");
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;
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_)
_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;
+}
#include <map>
#include <unordered_map>
#include <unordered_set>
+#include "graph.h"
#include "../place_recognition_types.h"
#include <string>
#include <ctime>
* mac address
*/
class Mac {
+
public:
const static size_t MAC_SIZE = 6; // number of bytes for mac address.
unsigned char c[MAC_SIZE];
}; /* 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
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 */
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__*/
#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_)
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) {
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());
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;
, running(false)
{
_D("CONSTRUCTOR");
- if (test_mode) return;
-
+ if (test_mode) {
+ return;
+ }
set_interval(energy_mode);
if (WIFI_LOGGER_DATABASE) {
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)
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);
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);