[tracking] Made tracking work

This commit is contained in:
Sergey Yershov 2016-10-21 19:54:02 +03:00
parent 8314310c0e
commit 1f1d0a5871
15 changed files with 260 additions and 48 deletions

View file

@ -6,6 +6,7 @@
#include "map/user_mark.hpp"
#include "defines.hpp"
#include "private.h"
#include "routing/online_absent_fetcher.hpp"
#include "routing/osrm_router.hpp"
@ -158,17 +159,6 @@ void CancelQuery(weak_ptr<search::ProcessorHandle> & handle)
queryHandle->Cancel();
handle.reset();
}
class StubSocket final : public platform::Socket
{
public:
// Socket overrides
bool Open(string const & host, uint16_t port) override { return false; }
void Close() override {}
bool Read(uint8_t * data, uint32_t count) override { return false; }
bool Write(uint8_t const * data, uint32_t count) override { return false; }
void SetTimeout(uint32_t milliseconds) override {}
};
} // namespace
pair<MwmSet::MwmId, MwmSet::RegResult> Framework::RegisterMap(
@ -248,9 +238,9 @@ bool Framework::IsTrackingReporterEnabled() const
if (!m_routingSession.IsOnRoute())
return false;
bool allowStat = false;
UNUSED_VALUE(settings::Get(tracking::Reporter::kEnabledSettingsKey, allowStat));
return allowStat;
bool enableTracking = false;
UNUSED_VALUE(settings::Get(tracking::Reporter::kEnableTrackingKey, enableTracking));
return enableTracking;
}
void Framework::OnUserPositionChanged(m2::PointD const & position)
@ -343,7 +333,8 @@ Framework::Framework()
, m_storage(platform::migrate::NeedMigrate() ? COUNTRIES_OBSOLETE_FILE : COUNTRIES_FILE)
, m_bmManager(*this)
, m_isRenderingEnabled(true)
, m_trackingReporter(make_unique<StubSocket>(), tracking::Reporter::kPushDelayMs)
, m_trackingReporter(platform::CreateSocket(), TRACKING_REALTIME_HOST, TRACKING_REALTIME_PORT,
tracking::Reporter::kPushDelayMs)
, m_displacementModeManager([this](bool show) {
int const mode = show ? dp::displacement::kHotelMode : dp::displacement::kDefaultMode;
CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode));

View file

@ -27,8 +27,9 @@ bool TestSocket::Read(uint8_t * data, uint32_t count)
if (!m_isConnected)
return false;
lock_guard<mutex> lg(m_inputMutex);
unique_lock<mutex> lock(m_inputMutex);
m_inputCondition.wait_for(lock, milliseconds(m_timeoutMs), [this]() { return !m_input.empty(); });
if (m_input.size() < count)
return false;
@ -51,7 +52,6 @@ bool TestSocket::Write(uint8_t const * data, uint32_t count)
}
void TestSocket::SetTimeout(uint32_t milliseconds) { m_timeoutMs = milliseconds; }
size_t TestSocket::ReadServer(vector<uint8_t> & destination)
{
unique_lock<mutex> lock(m_outputMutex);

View file

@ -28,6 +28,13 @@ public:
// Waits for some data or timeout.
// Returns size of read data.
size_t ReadServer(vector<uint8_t> & destination);
template <typename Container>
void WriteServer(Container const & answer)
{
lock_guard<mutex> lg(m_inputMutex);
m_input.insert(m_input.begin(), begin(answer), end(answer));
m_inputCondition.notify_one();
}
private:
atomic<bool> m_isConnected = {false};
@ -35,6 +42,7 @@ private:
deque<uint8_t> m_input;
mutex m_inputMutex;
condition_variable m_inputCondition;
vector<uint8_t> m_output;
mutex m_outputMutex;

View file

@ -1,5 +1,6 @@
#pragma once
#include "std/cstdint.hpp"
#include "std/string.hpp"
#include "std/unique_ptr.hpp"
@ -25,5 +26,16 @@ public:
virtual void SetTimeout(uint32_t milliseconds) = 0;
};
class StubSocket final : public Socket
{
public:
// Socket overrides:
bool Open(string const & host, uint16_t port) override { return false; }
void Close() override {}
bool Read(uint8_t * data, uint32_t count) override { return false; }
bool Write(uint8_t const * data, uint32_t count) override { return false; }
void SetTimeout(uint32_t milliseconds) override {}
};
unique_ptr<Socket> CreateSocket();
} // namespace platform

View file

@ -1,5 +1,8 @@
#include "connection.hpp"
#include "tracking/connection.hpp"
#include "tracking/protocol.hpp"
#include "platform/platform.hpp"
#include "platform/socket.hpp"
namespace
@ -22,19 +25,27 @@ Connection::Connection(unique_ptr<platform::Socket> socket, string const & host,
bool Connection::Reconnect()
{
m_socket->Close();
return m_socket->Open(m_host, m_port);
if (!m_socket->Open(m_host, m_port))
return false;
auto packet = Protocol::CreateAuthPacket(GetPlatform().UniqueClientId());
if (!m_socket->Write(packet.data(), static_cast<uint32_t>(packet.size())))
return false;
string check(begin(Protocol::kFail), end(Protocol::kFail));
bool const isSuccess =
m_socket->Read(reinterpret_cast<uint8_t *>(&check[0]), static_cast<uint32_t>(check.size()));
if (!isSuccess || check != string(begin(Protocol::kOk), end(Protocol::kOk)))
return false;
return true;
}
// TODO: implement historical
bool Connection::Send(boost::circular_buffer<DataPoint> const & points)
{
ASSERT(m_buffer.empty(), ());
MemWriter<decltype(m_buffer)> writer(m_buffer);
using coding::TrafficGPSEncoder;
TrafficGPSEncoder::SerializeDataPoints(TrafficGPSEncoder::kLatestVersion, writer, points);
bool const isSuccess = m_socket->Write(m_buffer.data(), m_buffer.size());
m_buffer.clear();
return isSuccess;
auto packet = Protocol::CreateDataPacket(points);
return m_socket->Write(packet.data(), static_cast<uint32_t>(packet.size()));
}
} // namespace tracking

View file

@ -31,6 +31,5 @@ private:
string const m_host;
uint16_t const m_port;
bool const m_isHistorical;
vector<uint8_t> m_buffer;
};
} // namespace tracking

62
tracking/protocol.cpp Normal file
View file

@ -0,0 +1,62 @@
#include "tracking/protocol.hpp"
#include "coding/endianness.hpp"
#include "base/assert.hpp"
#include "std/cstdint.hpp"
namespace tracking
{
uint8_t const Protocol::kOk[4] = {'O', 'K', '\n', '\n'};
uint8_t const Protocol::kFail[4] = {'F', 'A', 'I', 'L'};
static_assert(sizeof(Protocol::kFail) >= sizeof(Protocol::kOk), "");
vector<uint8_t> Protocol::CreateAuthPacket(string const & clientId)
{
vector<uint8_t> packet;
InitHeader(packet, PacketType::CurrentAuth, static_cast<uint32_t>(clientId.size()));
packet.insert(packet.end(), begin(clientId), end(clientId));
return packet;
}
vector<uint8_t> Protocol::CreateDataPacket(DataElements const & points)
{
vector<uint8_t> buffer;
MemWriter<decltype(buffer)> writer(buffer);
Encoder::SerializeDataPoints(Encoder::kLatestVersion, writer, points);
vector<uint8_t> packet;
InitHeader(packet, PacketType::CurrentData, static_cast<uint32_t>(buffer.size()));
packet.insert(packet.end(), begin(buffer), end(buffer));
return packet;
}
void Protocol::InitHeader(vector<uint8_t> & packet, PacketType type, uint32_t payloadSize)
{
packet.resize(sizeof(uint32_t));
uint32_t & size = *reinterpret_cast<uint32_t *>(packet.data());
size = payloadSize;
ASSERT_LESS(size, 0x00FFFFFF, ());
if (!IsBigEndian())
size = ReverseByteOrder(size);
packet[0] = static_cast<uint8_t>(type);
}
string DebugPrint(Protocol::PacketType type)
{
switch (type)
{
case Protocol::PacketType::AuthV0: return "AuthV0";
case Protocol::PacketType::DataV0: return "DataV0";
default: return "Unknown";
}
}
} // namespace tracking

38
tracking/protocol.hpp Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include "coding/traffic.hpp"
#include "std/string.hpp"
#include "std/vector.hpp"
#include "boost/circular_buffer.hpp"
namespace tracking
{
class Protocol
{
public:
using Encoder = coding::TrafficGPSEncoder;
using DataElements = boost::circular_buffer<Encoder::DataPoint>;
static uint8_t const kOk[4];
static uint8_t const kFail[4];
enum class PacketType
{
AuthV0 = 0x81,
DataV0 = 0x82,
CurrentAuth = AuthV0,
CurrentData = DataV0
};
static vector<uint8_t> CreateAuthPacket(string const & clientId);
static vector<uint8_t> CreateDataPacket(DataElements const & points);
private:
static void InitHeader(vector<uint8_t> & packet, PacketType type, uint32_t payloadSize);
};
string DebugPrint(Protocol::PacketType type);
} // namespace tracking

View file

@ -8,8 +8,6 @@
#include "std/target_os.hpp"
#include "private.h"
namespace
{
double constexpr kRequiredHorizontalAccuracy = 10.0;
@ -25,21 +23,22 @@ namespace tracking
// Keys saved for existing users, so can' fix it easy, need migration.
// Use this hack until change to special traffic key.
#if defined(OMIM_OS_IPHONE)
const char Reporter::kEnabledSettingsKey[] = "StatisticsEnabled";
const char Reporter::kEnableTrackingKey[] = "StatisticsEnabled";
#elif defined(OMIM_OS_ANDROID)
const char Reporter::kEnabledSettingsKey[] = "AllowStat";
const char Reporter::kEnableTrackingKey[] = "AllowStat";
#else
const char Reporter::kEnabledSettingsKey[] = "AllowStat";
const char Reporter::kEnableTrackingKey[] = "AllowStat";
#endif
// static
milliseconds const Reporter::kPushDelayMs = milliseconds(10000);
Reporter::Reporter(unique_ptr<platform::Socket> socket, milliseconds pushDelay)
: m_realtimeSender(move(socket), TRACKING_REALTIME_HOST, TRACKING_REALTIME_PORT, false)
Reporter::Reporter(unique_ptr<platform::Socket> socket, string const & host, uint16_t port,
milliseconds pushDelay)
: m_realtimeSender(move(socket), host, port, false)
, m_pushDelay(pushDelay)
, m_points(kRealTimeBufferSize)
, m_thread([this]{Run();})
, m_thread([this] { Run(); })
{
}
@ -82,8 +81,15 @@ void Reporter::Run()
m_input.clear();
lock.unlock();
if (SendPoints())
m_points.clear();
if (m_points.empty() && m_idleFn)
{
m_idleFn();
}
else
{
if (SendPoints())
m_points.clear();
}
lock.lock();
auto const passedMs = duration_cast<milliseconds>(steady_clock::now() - startTime);

View file

@ -7,6 +7,7 @@
#include "std/chrono.hpp"
#include "std/condition_variable.hpp"
#include "std/mutex.hpp"
#include "std/string.hpp"
#include "std/unique_ptr.hpp"
#include "std/vector.hpp"
@ -28,13 +29,16 @@ class Reporter final
{
public:
static milliseconds const kPushDelayMs;
static const char kEnabledSettingsKey[];
static const char kEnableTrackingKey[];
Reporter(unique_ptr<platform::Socket> socket, milliseconds pushDelay);
Reporter(unique_ptr<platform::Socket> socket, string const & host, uint16_t port,
milliseconds pushDelay);
~Reporter();
void AddLocation(location::GpsInfo const & info);
inline void SetIdleFunc(function<void()> fn) { m_idleFn = fn; }
private:
void Run();
bool SendPoints();
@ -43,6 +47,9 @@ private:
milliseconds m_pushDelay;
bool m_wasConnected = false;
double m_lastConnectionAttempt = 0.0;
// Function to be called every |kPushDelayMs| in
// case no points were sent.
function<void()> m_idleFn;
// Input buffer for incoming points. Worker thread steals it contents.
vector<DataPoint> m_input;
// Last collected points, sends periodically to server.

View file

@ -8,8 +8,10 @@ include($$ROOT_DIR/common.pri)
SOURCES += \
connection.cpp \
protocol.cpp \
reporter.cpp \
HEADERS += \
connection.hpp \
protocol.hpp \
reporter.hpp \

View file

@ -0,0 +1,34 @@
#include "testing/testing.hpp"
#include "tracking/protocol.hpp"
using namespace tracking;
UNIT_TEST(Protocol_CreateAuthPacket)
{
auto packet = Protocol::CreateAuthPacket("AAA");
TEST_EQUAL(packet.size(), 7, ());
TEST_EQUAL(Protocol::PacketType(packet[0]), Protocol::PacketType::CurrentAuth, ());
TEST_EQUAL(packet[1], 0x00, ());
TEST_EQUAL(packet[2], 0x00, ());
TEST_EQUAL(packet[3], 0x03, ());
TEST_EQUAL(packet[4], 'A', ());
TEST_EQUAL(packet[5], 'A', ());
TEST_EQUAL(packet[6], 'A', ());
}
UNIT_TEST(Protocol_CreateDataPacket)
{
Protocol::DataElements buffer(5);
buffer.push_back(Protocol::DataElements::value_type(1, ms::LatLon(10, 10)));
buffer.push_back(Protocol::DataElements::value_type(2, ms::LatLon(15, 15)));
auto packet = Protocol::CreateDataPacket(buffer);
TEST_EQUAL(packet.size(), 26, ());
TEST_EQUAL(Protocol::PacketType(packet[0]), Protocol::PacketType::CurrentData, ());
TEST_EQUAL(packet[1], 0x00, ());
TEST_EQUAL(packet[2], 0x00, ());
TEST_EQUAL(packet[3], 22, ());
TEST_EQUAL(packet[4], 1, ());
TEST_EQUAL(packet[5], 227, ());
TEST_EQUAL(packet[6], 241, ());
}

View file

@ -1,4 +1,7 @@
#include "testing/testing.hpp"
#include "tracking/reporter.hpp"
#include "tracking/protocol.hpp"
#include "coding/traffic.hpp"
@ -6,8 +9,6 @@
#include "platform/platform_tests_support/test_socket.hpp"
#include "platform/socket.hpp"
#include "testing/testing.hpp"
#include "base/math.hpp"
#include "base/thread.hpp"
@ -28,13 +29,36 @@ void TransferLocation(Reporter & reporter, TestSocket & testSocket, double times
gpsInfo.m_horizontalAccuracy = 1.0;
reporter.AddLocation(gpsInfo);
using Packet = tracking::Protocol::PacketType;
vector<uint8_t> buffer;
size_t const readSize = testSocket.ReadServer(buffer);
TEST_GREATER(readSize, 0, ());
size_t readSize = 0;
size_t attempts = 3;
do
{
readSize = testSocket.ReadServer(buffer);
if (attempts-- && readSize == 0)
continue;
switch (Packet(buffer[0]))
{
case Packet::CurrentAuth:
{
buffer.clear();
testSocket.WriteServer(tracking::Protocol::kOk);
break;
}
case Packet::CurrentData:
{
readSize = 0;
break;
}
}
} while (readSize);
TEST(!buffer.empty(), ());
vector<coding::TrafficGPSEncoder::DataPoint> points;
MemReader memReader(buffer.data(), buffer.size());
ReaderSource<MemReader> src(memReader);
src.Skip(sizeof(uint32_t /* header */));
coding::TrafficGPSEncoder::DeserializeDataPoints(coding::TrafficGPSEncoder::kLatestVersion, src,
points);
@ -51,7 +75,7 @@ UNIT_TEST(Reporter_TransferLocations)
auto socket = make_unique<TestSocket>();
TestSocket & testSocket = *socket.get();
Reporter reporter(move(socket), milliseconds(10) /* pushDelay */);
Reporter reporter(move(socket), "localhost", 0, milliseconds(10) /* pushDelay */);
TransferLocation(reporter, testSocket, 1.0, 2.0, 3.0);
TransferLocation(reporter, testSocket, 4.0, 5.0, 6.0);
TransferLocation(reporter, testSocket, 7.0, 8.0, 9.0);

View file

@ -26,4 +26,5 @@ win*|linux* {
SOURCES += \
$$ROOT_DIR/testing/testingmain.cpp \
reporter_test.cpp
protocol_test.cpp \
reporter_test.cpp \

View file

@ -19,7 +19,10 @@
675E88981DB7B08500F8EBDA /* libplatform_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 675E88961DB7B08500F8EBDA /* libplatform_tests_support.a */; };
675E88991DB7B08500F8EBDA /* libtomcrypt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 675E88971DB7B08500F8EBDA /* libtomcrypt.a */; };
675E88A21DB7B10300F8EBDA /* libtracking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 675E88501DB7AC0300F8EBDA /* libtracking.a */; };
67B6E7B81DB8BBA900745F8A /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67B6E7B71DB8BBA900745F8A /* testingmain.cpp */; };
6772590D1DB90F8F00BF54EE /* testingmain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67B6E7B71DB8BBA900745F8A /* testingmain.cpp */; };
6772592D1DBA26FD00BF54EE /* protocol.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6772592B1DBA26FD00BF54EE /* protocol.cpp */; };
6772592E1DBA26FD00BF54EE /* protocol.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6772592C1DBA26FD00BF54EE /* protocol.hpp */; };
67EC0CB71DBE4A320021F484 /* protocol_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 67EC0CB61DBE4A320021F484 /* protocol_test.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -37,7 +40,10 @@
675E88911DB7B03400F8EBDA /* libplatform.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform.a; path = "/Users/darkserj/mapsme/omim/xcode/platform/../../../omim-xcode-build/Debug/libplatform.a"; sourceTree = "<absolute>"; };
675E88961DB7B08500F8EBDA /* libplatform_tests_support.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libplatform_tests_support.a; path = "/Users/darkserj/mapsme/omim/xcode/platform/../../../omim-xcode-build/Debug/libplatform_tests_support.a"; sourceTree = "<absolute>"; };
675E88971DB7B08500F8EBDA /* libtomcrypt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libtomcrypt.a; path = "/Users/darkserj/mapsme/omim/xcode/tomcrypt/../../../omim-xcode-build/Debug/libtomcrypt.a"; sourceTree = "<absolute>"; };
6772592B1DBA26FD00BF54EE /* protocol.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protocol.cpp; sourceTree = "<group>"; };
6772592C1DBA26FD00BF54EE /* protocol.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = protocol.hpp; sourceTree = "<group>"; };
67B6E7B71DB8BBA900745F8A /* testingmain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = testingmain.cpp; path = ../../testing/testingmain.cpp; sourceTree = "<group>"; };
67EC0CB61DBE4A320021F484 /* protocol_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protocol_test.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -92,6 +98,8 @@
675E88611DB7AC9000F8EBDA /* connection.hpp */,
675E88621DB7AC9000F8EBDA /* reporter.cpp */,
675E88631DB7AC9000F8EBDA /* reporter.hpp */,
6772592B1DBA26FD00BF54EE /* protocol.cpp */,
6772592C1DBA26FD00BF54EE /* protocol.hpp */,
);
name = tracking;
path = ../../tracking;
@ -102,6 +110,7 @@
children = (
67B6E7B71DB8BBA900745F8A /* testingmain.cpp */,
675E88841DB7AD1000F8EBDA /* reporter_test.cpp */,
67EC0CB61DBE4A320021F484 /* protocol_test.cpp */,
);
name = tracking_tests;
path = ../../tracking/tracking_tests;
@ -127,6 +136,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6772592E1DBA26FD00BF54EE /* protocol.hpp in Headers */,
675E88651DB7AC9000F8EBDA /* connection.hpp in Headers */,
675E88671DB7AC9000F8EBDA /* reporter.hpp in Headers */,
);
@ -223,8 +233,8 @@
buildActionMask = 2147483647;
files = (
675E88661DB7AC9000F8EBDA /* reporter.cpp in Sources */,
67B6E7B81DB8BBA900745F8A /* testingmain.cpp in Sources */,
675E88641DB7AC9000F8EBDA /* connection.cpp in Sources */,
6772592D1DBA26FD00BF54EE /* protocol.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -232,7 +242,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
67EC0CB71DBE4A320021F484 /* protocol_test.cpp in Sources */,
675E88891DB7AD3100F8EBDA /* reporter_test.cpp in Sources */,
6772590D1DB90F8F00BF54EE /* testingmain.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -352,6 +364,10 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"OMIM_UNIT_TEST_WITH_QT_EVENT_LOOP=1",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = "$(SRCROOT)/../../iphone/Maps/MAPSME.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
@ -369,6 +385,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
GCC_PREPROCESSOR_DEFINITIONS = "OMIM_UNIT_TEST_WITH_QT_EVENT_LOOP=1";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = "$(SRCROOT)/../../iphone/Maps/MAPSME.plist";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;