2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
23 #include "../facade/user_places_types.h"
24 #include "visit_detector.h"
25 #include "../facade/user_places_params.h"
26 #include "../visit-categer/visit_categer.h"
27 #include "../utils/similarity.h"
28 #include "../utils/median.h"
29 #include "../utils/debug_utils.h"
31 #ifdef TIZEN_ENGINEER_MODE
32 #define __VISIT_TABLE_COLUMNS \
33 VISIT_COLUMN_WIFI_APS " TEXT, "\
34 VISIT_COLUMN_START_TIME " timestamp, "\
35 VISIT_COLUMN_END_TIME " timestamp, "\
36 VISIT_COLUMN_START_TIME_HUMAN " TEXT, "\
37 VISIT_COLUMN_END_TIME_HUMAN " TEXT, "\
38 VISIT_COLUMN_LOCATION_VALID " INTEGER, "\
39 VISIT_COLUMN_LOCATION_LATITUDE " REAL, "\
40 VISIT_COLUMN_LOCATION_LONGITUDE " REAL, "\
41 VISIT_COLUMN_LOCATION_ACCURACY " REAL, "\
42 VISIT_COLUMN_CATEG_HOME " REAL, "\
43 VISIT_COLUMN_CATEG_WORK " REAL, "\
44 VISIT_COLUMN_CATEG_OTHER " REAL"
45 #else /* TIZEN_ENGINEER_MODE */
46 #define __VISIT_TABLE_COLUMNS \
47 VISIT_COLUMN_WIFI_APS " TEXT, "\
48 VISIT_COLUMN_START_TIME " timestamp, "\
49 VISIT_COLUMN_END_TIME " timestamp, "\
50 VISIT_COLUMN_LOCATION_VALID " INTEGER, "\
51 VISIT_COLUMN_LOCATION_LATITUDE " REAL, "\
52 VISIT_COLUMN_LOCATION_LONGITUDE " REAL, "\
53 VISIT_COLUMN_LOCATION_ACCURACY " REAL, "\
54 VISIT_COLUMN_CATEG_HOME " REAL, "\
55 VISIT_COLUMN_CATEG_WORK " REAL, "\
56 VISIT_COLUMN_CATEG_OTHER " REAL"
57 #endif /* TIZEN_ENGINEER_MODE */
59 #define __WIFI_APS_MAP_TABLE_COLUMNS \
60 WIFI_APS_MAP_COLUMN_MAC " TEXT NOT NULL UNIQUE, "\
61 WIFI_APS_MAP_COLUMN_NETWORK_NAME " TEXT NOT NULL, "\
62 WIFI_APS_MAP_COLUMN_INSERT_TIME " timestamp"
64 ctx::VisitDetector::VisitDetector(time_t startScan, PlaceRecogMode energyMode, bool testMode) :
66 __locationLogger(testMode ? nullptr : new LocationLogger(this)),
67 __wifiLogger(testMode ? nullptr : new WifiLogger(this, energyMode)),
68 __currentInterval(startScan, startScan + VISIT_DETECTOR_PERIOD_SECONDS_HIGH_ACCURACY),
70 __tolerance(VISIT_DETECTOR_TOLERANCE_DEPTH),
71 __entranceToPlace(false),
72 __periodSeconds(VISIT_DETECTOR_PERIOD_SECONDS_HIGH_ACCURACY),
73 __dbManager(testMode ? nullptr : new DatabaseManager()),
77 __setPeriod(energyMode);
78 __currentInterval = Interval(startScan, startScan + __periodSeconds);
79 __currentMacEvents = std::make_shared<MacEvents>();
80 __stayMacs = std::make_shared<MacSet>();
83 __detectedVisits = std::make_shared<ctx::Visits>();
87 __listeners.push_back(__locationLogger);
88 __listeners.push_back(__wifiLogger);
91 __wifiLogger->startLogging();
94 ctx::VisitDetector::~VisitDetector()
98 bool ctx::VisitDetector::__isValid(const ctx::Mac &mac)
100 return mac != "00:00:00:00:00:00";
103 void ctx::VisitDetector::onWifiScan(ctx::MacEvent e)
105 _D("timestamp=%d, current_interval.end=%d, mac=%s, network=%s",
107 __currentInterval.end,
108 std::string(e.mac).c_str(),
109 e.networkName.c_str());
110 if (__isValid(e.mac)) {
111 while (e.timestamp > __currentInterval.end) {
112 __processCurrentLogger();
113 __shiftCurrentInterval();
115 __currentMacEvents->push_back(e);
119 void ctx::VisitDetector::__processCurrentLogger()
122 std::shared_ptr<ctx::Frame> frame = __makeFrame(__currentMacEvents, __currentInterval);
123 __detectEntranceOrDeparture(frame);
124 __currentMacEvents->clear();
127 std::shared_ptr<ctx::Frame> ctx::VisitDetector::__makeFrame(std::shared_ptr<ctx::MacEvents> logger, ctx::Interval interval)
129 std::set<time_t> timestamps;
130 std::shared_ptr<Frame> frame = std::make_shared<Frame>(interval);
131 for (auto log : *logger) {
132 timestamps.insert(log.timestamp);
133 if (frame->macs2Counts.find(log.mac) == frame->macs2Counts.end()) {
134 frame->macs2Counts[log.mac] = 1;
136 frame->macs2Counts[log.mac] += 1;
139 frame->numberOfTimestamps = timestamps.size();
143 void ctx::VisitDetector::__shiftCurrentInterval()
145 __currentInterval.end += __periodSeconds;
146 __currentInterval.start += __periodSeconds;
149 void ctx::VisitDetector::__detectEntranceOrDeparture(std::shared_ptr<ctx::Frame> frame)
151 __entranceToPlace ? __detectDeparture(frame) : __detectEntrance(frame);
152 if (__entranceToPlace) {
153 for (MacEvent e : *__currentMacEvents) {
154 __wifiAPsMap.insert(std::pair<std::string, std::string>(e.mac, e.networkName));
159 bool ctx::VisitDetector::__isDisjoint(const ctx::Macs2Counts &macs2Counts, const ctx::MacSet &macSet)
161 for (auto &mac : macSet) {
162 if (macs2Counts.find(mac) != macs2Counts.end())
168 bool ctx::VisitDetector::__protrudesFrom(const ctx::Macs2Counts &macs2Counts, const ctx::MacSet &macSet)
170 for (auto &macCount : macs2Counts) {
171 if (macSet.find(macCount.first) == macSet.end())
177 void ctx::VisitDetector::__detectDeparture(std::shared_ptr<ctx::Frame> frame)
179 if (__tolerance == VISIT_DETECTOR_TOLERANCE_DEPTH) {
180 __departureTime = frame->interval.start;
181 __bufferedFrames.clear();
182 } else { // __tolerance < VISIT_DETECTOR_TOLERANCE_DEPTH
183 __bufferedFrames.push_back(frame);
185 if (__isDisjoint(frame->macs2Counts, *__representativesMacs)) {
186 if (frame->macs2Counts.empty() || __protrudesFrom(frame->macs2Counts, *__stayMacs)) {
188 } else { // no new macs
189 __bufferedFrames.clear();
191 if (__tolerance == 0) { // departure detected
192 __visitEndDetected();
193 __processBuffer(frame);
195 } else if (__tolerance < VISIT_DETECTOR_TOLERANCE_DEPTH) {
200 void ctx::VisitDetector::__visitStartDetected()
202 __entranceToPlace = true;
204 __locationEvents.clear();
206 for (IVisitListener* listener : __listeners) {
207 listener->onVisitStart();
210 __representativesMacs = __selectRepresentatives(__historyFrames);
211 __entranceTime = __historyFrames[0]->interval.start;
212 _I("Entrance detected, timestamp: %d", __entranceTime);
216 void ctx::VisitDetector::__visitEndDetected()
219 for (IVisitListener* listener : __listeners) {
220 listener->onVisitEnd();
223 _I("Departure detected, timestamp: %d", __departureTime);
225 Interval interval(__entranceTime, __departureTime);
226 Visit visit(interval, __representativesMacs);
227 VisitCateger::categorize(visit);
229 __putLocationToVisit(visit);
232 __detectedVisits->push_back(visit);
234 __dbInsertVisit(visit);
235 __dbInsertWifiAPsMap(visit);
239 __entranceToPlace = false;
240 __representativesMacs.reset();
241 __tolerance = VISIT_DETECTOR_TOLERANCE_DEPTH;
244 void ctx::VisitDetector::__putLocationToVisit(ctx::Visit &visit)
246 // TODO: filter out small accuracy locations?
247 std::vector<double> latitudes;
248 std::vector<double> longitudes;
249 std::vector<double> accuracy;
250 visit.locationValid = false;
251 for (LocationEvent &location : __locationEvents) {
252 if (location.timestamp >= __entranceTime && location.timestamp <= __departureTime) {
253 latitudes.push_back(location.coordinates.latitude);
254 longitudes.push_back(location.coordinates.longitude);
255 accuracy.push_back(location.coordinates.accuracy);
256 visit.locationValid = true;
259 if (visit.locationValid) {
260 visit.location = medianLocation(latitudes, longitudes, accuracy);
261 _D("visit location set: lat=%.8f, lon=%.8f, acc=%.8f",
262 visit.location.latitude,
263 visit.location.longitude,
264 visit.location.accuracy);
266 _D("visit location not set");
270 void ctx::VisitDetector::__processBuffer(std::shared_ptr<ctx::Frame> frame)
272 if (__bufferedFrames.empty()) {
273 __historyFrames.push_back(frame);
275 __historyFrames.push_back(__bufferedFrames[0]);
276 for (size_t i = 1; i < __bufferedFrames.size(); i++) {
277 __detectEntrance(__bufferedFrames[i]);
278 if (__entranceToPlace)
284 void ctx::VisitDetector::__detectEntrance(std::shared_ptr<ctx::Frame> currentFrame)
286 if (currentFrame->macs2Counts.empty() || __historyFrames.empty()) {
287 __resetHistory(currentFrame);
291 if (__stableCounter == 0) {
292 std::shared_ptr<Frame> oldestHistoryFrame = __historyFrames[0];
293 __stayMacs = macSetFromMacs2Counts(oldestHistoryFrame->macs2Counts);
296 std::shared_ptr<MacSet> currentBeacons = macSetFromMacs2Counts(currentFrame->macs2Counts);
298 if (similarity::overlapBiggerOverSmaller(*currentBeacons, *__stayMacs) > VISIT_DETECTOR_OVERLAP) {
300 __historyFrames.push_back(currentFrame);
301 if (__stableCounter == VISIT_DETECTOR_STABLE_DEPTH) // entrance detected
302 __visitStartDetected();
304 __resetHistory(currentFrame);
309 void ctx::VisitDetector::__resetHistory()
312 __historyFrames.clear();
315 void ctx::VisitDetector::__resetHistory(std::shared_ptr<Frame> frame)
318 __historyFrames.push_back(frame);
321 std::shared_ptr<ctx::MacSet> ctx::VisitDetector::__selectRepresentatives(const std::vector<std::shared_ptr<Frame>> &frames)
323 Macs2Counts reprs2Counts;
324 count_t allCount = 0;
326 for (auto frame : frames) {
327 allCount += frame->numberOfTimestamps;
328 for (auto &c : frame->macs2Counts) {
329 reprs2Counts[c.first] += c.second;
333 std::shared_ptr<Macs2Shares> reprs2Shares = __macSharesFromCounts(reprs2Counts, allCount);
335 share_t maxShare = __calcMaxShare(*reprs2Shares);
336 share_t threshold = maxShare < VISIT_DETECTOR_REP_THRESHOLD ? maxShare : VISIT_DETECTOR_REP_THRESHOLD;
338 std::shared_ptr<MacSet> reprsMacSet = __macSetOfGreaterOrEqualShare(*reprs2Shares, threshold);
343 ctx::share_t ctx::VisitDetector::__calcMaxShare(const ctx::Macs2Shares &macs2Shares)
345 ctx::share_t maxShare = 0.0;
346 for (auto &macShare : macs2Shares) {
347 if (macShare.second > maxShare)
348 maxShare = macShare.second;
353 std::shared_ptr<ctx::MacSet> ctx::VisitDetector::__macSetOfGreaterOrEqualShare(const ctx::Macs2Shares &macs2Shares, ctx::share_t threshold)
355 std::shared_ptr<MacSet> macSet = std::make_shared<MacSet>();
356 for (auto &macShare : macs2Shares) {
357 if (macShare.second >= threshold)
358 macSet->insert(macShare.first);
363 std::shared_ptr<ctx::Macs2Shares> ctx::VisitDetector::__macSharesFromCounts(ctx::Macs2Counts const &macs2Counts, ctx::count_t denominator)
365 std::shared_ptr<Macs2Shares> macs2Shares(std::make_shared<Macs2Shares>());
366 for (auto macCount : macs2Counts) {
367 (*macs2Shares)[macCount.first] = (share_t) macCount.second / denominator;
372 std::shared_ptr<ctx::Visits> ctx::VisitDetector::__getVisits()
374 return __detectedVisits;
377 void ctx::VisitDetector::__dbCreateTables()
379 bool ret = __dbManager->createTable(0, VISIT_TABLE, __VISIT_TABLE_COLUMNS);
380 _D("db: Visit Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
382 ret = __dbManager->createTable(0, WIFI_APS_MAP_TABLE, __WIFI_APS_MAP_TABLE_COLUMNS);
383 _D("db: Wifi AP Map Table Creation Result: %s", ret ? "SUCCESS" : "FAIL");
386 void ctx::VisitDetector::__putVisitCategToJson(const char* key, const Categs &categs, int categType, Json &data)
388 auto categ = categs.find(categType);
389 if (categ == categs.end()) {
390 _E("json_put_visit no type %d in categs", categType);
392 data.set(NULL, key, categ->second);
396 void ctx::VisitDetector::__putVisitCategsToJson(const Categs &categs, Json &data)
398 __putVisitCategToJson(VISIT_COLUMN_CATEG_HOME, categs, PLACE_CATEG_ID_HOME, data);
399 __putVisitCategToJson(VISIT_COLUMN_CATEG_WORK, categs, PLACE_CATEG_ID_WORK, data);
400 __putVisitCategToJson(VISIT_COLUMN_CATEG_OTHER, categs, PLACE_CATEG_ID_OTHER, data);
403 int ctx::VisitDetector::__dbInsertVisit(Visit visit)
405 std::stringstream ss;
409 data.set(NULL, VISIT_COLUMN_WIFI_APS, ss.str().c_str());
411 data.set(NULL, VISIT_COLUMN_LOCATION_VALID, visit.locationValid);
412 data.set(NULL, VISIT_COLUMN_LOCATION_LATITUDE, visit.location.latitude);
413 data.set(NULL, VISIT_COLUMN_LOCATION_LONGITUDE, visit.location.longitude);
414 data.set(NULL, VISIT_COLUMN_LOCATION_ACCURACY, visit.location.accuracy);
416 data.set(NULL, VISIT_COLUMN_START_TIME, static_cast<int>(visit.interval.start));
417 data.set(NULL, VISIT_COLUMN_END_TIME, static_cast<int>(visit.interval.end));
419 #ifdef TIZEN_ENGINEER_MODE
420 std::string startTimeHuman = DebugUtils::humanReadableDateTime(visit.interval.start, "%F %T", 80);
421 std::string endTimeHuman = DebugUtils::humanReadableDateTime(visit.interval.end, "%F %T", 80);
422 data.set(NULL, VISIT_COLUMN_START_TIME_HUMAN, startTimeHuman.c_str());
423 data.set(NULL, VISIT_COLUMN_END_TIME_HUMAN, endTimeHuman.c_str());
424 _D("db: visit table insert interval: (%d, %d): (%s, %s)",
425 visit.interval.start, visit.interval.end, startTimeHuman.c_str(), endTimeHuman.c_str());
427 _D("db: visit table insert interval: (%d, %d)", visit.interval.start, visit.interval.end);
428 #endif /* TIZEN_ENGINEER_MODE */
430 __putVisitCategsToJson(visit.categs, data);
433 bool ret = __dbManager->insertSync(VISIT_TABLE, data, &rowId);
434 _D("db: visit table insert result: %s", ret ? "SUCCESS" : "FAIL");
438 int ctx::VisitDetector::__dbInsertWifiAPsMap(Visit visit)
440 std::stringstream query;
441 time_t now = time(nullptr);
442 const char* separator = " ";
443 query << "BEGIN TRANSACTION; \
444 REPLACE INTO " WIFI_APS_MAP_TABLE " \
445 ( " WIFI_APS_MAP_COLUMN_MAC ", " WIFI_APS_MAP_COLUMN_NETWORK_NAME ", " WIFI_APS_MAP_COLUMN_INSERT_TIME " ) \
447 for (Mac mac : *visit.macSet) {
448 // TODO: Add protection from SQL injection in network name!!
449 query << separator << "( '" << mac << "', '" << __wifiAPsMap.find(mac)->second << "', '" << now << "' )";
452 __wifiAPsMap.clear();
455 bool ret = __dbManager->execute(0, query.str().c_str(), NULL);
456 _D("DB Wifi APs map insert request: %s", ret ? "SUCCESS" : "FAIL");
460 void ctx::VisitDetector::onNewLocation(LocationEvent locationEvent)
464 __locationEvents.push_back(locationEvent);
467 void ctx::VisitDetector::__setPeriod(PlaceRecogMode energyMode)
469 switch (energyMode) {
470 case PLACE_RECOG_LOW_POWER_MODE:
471 __periodSeconds = VISIT_DETECTOR_PERIOD_SECONDS_LOW_POWER;
473 case PLACE_RECOG_HIGH_ACCURACY_MODE:
474 __periodSeconds = VISIT_DETECTOR_PERIOD_SECONDS_HIGH_ACCURACY;
477 _E("Incorrect energy mode");
481 void ctx::VisitDetector::setMode(PlaceRecogMode energyMode)
484 __setPeriod(energyMode);
486 __wifiLogger->setMode(energyMode);