diff --git a/coding/traffic.hpp b/coding/traffic.hpp index c3313358a9..7b237082d5 100644 --- a/coding/traffic.hpp +++ b/coding/traffic.hpp @@ -30,6 +30,11 @@ public: // It is expected that |m_timestamp| stores time since epoch in seconds. uint64_t m_timestamp = 0; ms::LatLon m_latLon = ms::LatLon::Zero(); + + bool operator==(DataPoint const & p) const + { + return m_timestamp == p.m_timestamp && m_latLon == p.m_latLon; + } }; // Serializes |points| to |writer| by storing delta-encoded points. @@ -97,7 +102,7 @@ public: Uint32ToDouble(ReadVarUint(src), ms::LatLon::kMinLat, ms::LatLon::kMaxLat); lastLon = Uint32ToDouble(ReadVarUint(src), ms::LatLon::kMinLon, ms::LatLon::kMaxLon); - result.emplace_back(lastTimestamp, ms::LatLon(lastLat, lastLon)); + result.push_back(DataPoint(lastTimestamp, ms::LatLon(lastLat, lastLon))); first = false; } else @@ -105,7 +110,7 @@ public: lastTimestamp += ReadVarUint(src); lastLat += Uint32ToDouble(ReadVarUint(src), kMinDeltaLat, kMaxDeltaLat); lastLon += Uint32ToDouble(ReadVarUint(src), kMinDeltaLon, kMaxDeltaLon); - result.emplace_back(lastTimestamp, ms::LatLon(lastLat, lastLon)); + result.push_back(DataPoint(lastTimestamp, ms::LatLon(lastLat, lastLon))); } } } diff --git a/tracking/protocol.cpp b/tracking/protocol.cpp index ada9a49842..aea3402203 100644 --- a/tracking/protocol.cpp +++ b/tracking/protocol.cpp @@ -1,10 +1,30 @@ #include "tracking/protocol.hpp" #include "coding/endianness.hpp" +#include "coding/writer.hpp" #include "base/assert.hpp" #include "std/cstdint.hpp" +#include "std/sstream.hpp" + +namespace +{ +template +vector CreateDataPacketImpl(Container const & points) +{ + vector buffer; + MemWriter writer(buffer); + tracking::Protocol::Encoder::SerializeDataPoints(tracking::Protocol::Encoder::kLatestVersion, + writer, points); + + auto packet = tracking::Protocol::CreateHeader(tracking::Protocol::PacketType::CurrentData, + static_cast(buffer.size())); + packet.insert(packet.end(), begin(buffer), end(buffer)); + + return packet; +} +} // namespace namespace tracking { @@ -13,6 +33,15 @@ uint8_t const Protocol::kFail[4] = {'F', 'A', 'I', 'L'}; static_assert(sizeof(Protocol::kFail) >= sizeof(Protocol::kOk), ""); +// static +vector Protocol::CreateHeader(PacketType type, uint32_t payloadSize) +{ + vector header; + InitHeader(header, type, payloadSize); + return header; +} + +// static vector Protocol::CreateAuthPacket(string const & clientId) { vector packet; @@ -23,19 +52,62 @@ vector Protocol::CreateAuthPacket(string const & clientId) return packet; } -vector Protocol::CreateDataPacket(DataElements const & points) +// static +vector Protocol::CreateDataPacket(DataElementsCirc const & points) { - vector buffer; - MemWriter writer(buffer); - Encoder::SerializeDataPoints(Encoder::kLatestVersion, writer, points); - - vector packet; - InitHeader(packet, PacketType::CurrentData, static_cast(buffer.size())); - packet.insert(packet.end(), begin(buffer), end(buffer)); - - return packet; + return CreateDataPacketImpl(points); } +// static +vector Protocol::CreateDataPacket(DataElementsVec const & points) +{ + return CreateDataPacketImpl(points); +} + +// static +pair Protocol::DecodeHeader(vector const & data) +{ + ASSERT_GREATER_OR_EQUAL(data.size(), sizeof(uint32_t /* header */), ()); + + uint32_t size = (*reinterpret_cast(data.data())) & 0xFFFFFF00; + if (!IsBigEndian()) + size = ReverseByteOrder(size); + + return make_pair(PacketType(static_cast(data[0])), size); +} + +// static +string Protocol::DecodeAuthPacket(Protocol::PacketType type, vector const & data) +{ + ASSERT_GREATER_OR_EQUAL(data.size(), sizeof(uint32_t /* header */), ()); + switch (type) + { + case Protocol::PacketType::AuthV0: + return string(begin(data) + sizeof(uint32_t /* header */), end(data)); + case Protocol::PacketType::DataV0: break; + } + return string(); +} + +// static +Protocol::DataElementsVec Protocol::DecodeDataPacket(PacketType type, vector const & data) +{ + ASSERT_GREATER_OR_EQUAL(data.size(), sizeof(uint32_t /* header */), ()); + DataElementsVec points; + MemReader memReader(data.data(), data.size()); + ReaderSource src(memReader); + src.Skip(sizeof(uint32_t /* header */)); + switch (type) + { + case Protocol::PacketType::DataV0: + Encoder::DeserializeDataPoints(Encoder::kLatestVersion, src, points); + break; + case Protocol::PacketType::AuthV0: break; + } + return points; +} + +// static void Protocol::InitHeader(vector & packet, PacketType type, uint32_t payloadSize) { packet.resize(sizeof(uint32_t)); @@ -46,7 +118,7 @@ void Protocol::InitHeader(vector & packet, PacketType type, uint32_t pa if (!IsBigEndian()) size = ReverseByteOrder(size); - + packet[0] = static_cast(type); } @@ -54,9 +126,11 @@ string DebugPrint(Protocol::PacketType type) { switch (type) { - case Protocol::PacketType::AuthV0: return "AuthV0"; - case Protocol::PacketType::DataV0: return "DataV0"; - default: return "Unknown"; + case Protocol::PacketType::AuthV0: return "AuthV0"; + case Protocol::PacketType::DataV0: return "DataV0"; } + stringstream ss; + ss << "Unknown(" << static_cast(type) << ")"; + return ss.str(); } } // namespace tracking diff --git a/tracking/protocol.hpp b/tracking/protocol.hpp index 6b15b7bdeb..ed386bfffe 100644 --- a/tracking/protocol.hpp +++ b/tracking/protocol.hpp @@ -13,7 +13,8 @@ class Protocol { public: using Encoder = coding::TrafficGPSEncoder; - using DataElements = boost::circular_buffer; + using DataElementsCirc = boost::circular_buffer; + using DataElementsVec = vector; static uint8_t const kOk[4]; static uint8_t const kFail[4]; @@ -27,8 +28,14 @@ public: CurrentData = DataV0 }; + static vector CreateHeader(PacketType type, uint32_t payloadSize); static vector CreateAuthPacket(string const & clientId); - static vector CreateDataPacket(DataElements const & points); + static vector CreateDataPacket(DataElementsCirc const & points); + static vector CreateDataPacket(DataElementsVec const & points); + + static pair DecodeHeader(vector const & data); + static string DecodeAuthPacket(PacketType type, vector const & data); + static DataElementsVec DecodeDataPacket(PacketType type, vector const & data); private: static void InitHeader(vector & packet, PacketType type, uint32_t payloadSize); diff --git a/tracking/pytracking/bindings.cpp b/tracking/pytracking/bindings.cpp new file mode 100644 index 0000000000..699f0a06b3 --- /dev/null +++ b/tracking/pytracking/bindings.cpp @@ -0,0 +1,112 @@ +#include "tracking/protocol.hpp" + +#include "coding/traffic.hpp" + +#include +#include + +namespace +{ +using namespace boost::python; + +// Converts a std::pair instance to a Python tuple. +template +struct pair_to_tuple +{ + static PyObject * convert(pair const & p) + { + return incref(make_tuple(p.first, p.second).ptr()); + } + + static PyTypeObject const * get_pytype() { return &PyTuple_Type; } +}; + +template +struct pair_to_python_converter +{ + pair_to_python_converter() { to_python_converter, pair_to_tuple, true>(); } +}; + +// Converts a vector to/from Python str. +struct vector_uint8t_to_str +{ + static PyObject * convert(vector const & v) + { + str s(reinterpret_cast(v.data()), v.size()); + return incref(s.ptr()); + } +}; + +struct vector_uint8t_from_python_str +{ + vector_uint8t_from_python_str() + { + converter::registry::push_back(&convertible, &construct, type_id>()); + } + + static void * convertible(PyObject * obj_ptr) + { + if (!PyString_Check(obj_ptr)) + return nullptr; + return obj_ptr; + } + + static void construct(PyObject * obj_ptr, converter::rvalue_from_python_stage1_data * data) + { + const char * value = PyString_AsString(obj_ptr); + if (value == nullptr) + throw_error_already_set(); + void * storage = + ((converter::rvalue_from_python_storage> *)data)->storage.bytes; + new (storage) vector(value, value + PyString_Size(obj_ptr)); + data->convertible = storage; + } +}; +} // namespace + +BOOST_PYTHON_MODULE(pytracking) +{ + using namespace boost::python; + using tracking::Protocol; + + // Register the to-python converters. + pair_to_python_converter(); + to_python_converter, vector_uint8t_to_str>(); + vector_uint8t_from_python_str(); + + class_("DataElementsVec") + .def(vector_indexing_suite()); + + class_("LatLon") + .def_readwrite("lat", &ms::LatLon::lat) + .def_readwrite("lon", &ms::LatLon::lon); + + class_("DataPoint") + .def(init()) + .def_readwrite("timestamp", &coding::TrafficGPSEncoder::DataPoint::m_timestamp) + .def_readwrite("coords", &coding::TrafficGPSEncoder::DataPoint::m_latLon); + + enum_("PacketType") + .value("AuthV0", Protocol::PacketType::AuthV0) + .value("DataV0", Protocol::PacketType::DataV0) + .value("CurrentAuth", Protocol::PacketType::CurrentAuth) + .value("CurrentData", Protocol::PacketType::CurrentData); + + vector (*CreateDataPacket1)(Protocol::DataElementsCirc const &) = + &Protocol::CreateDataPacket; + vector (*CreateDataPacket2)(Protocol::DataElementsVec const &) = + &Protocol::CreateDataPacket; + + class_("Protocol") + .def("CreateAuthPacket", &Protocol::CreateAuthPacket) + .staticmethod("CreateAuthPacket") + .def("CreateDataPacket", CreateDataPacket1) + .def("CreateDataPacket", CreateDataPacket2) + .staticmethod("CreateDataPacket") + .def("CreateHeader", &Protocol::CreateHeader) + .staticmethod("CreateHeader") + .def("DecodeHeader", &Protocol::DecodeHeader) + .staticmethod("DecodeHeader") + .def("DecodeDataPacket", &Protocol::DecodeDataPacket) + .staticmethod("DecodeDataPacket"); +} diff --git a/tracking/tracking_tests/protocol_test.cpp b/tracking/tracking_tests/protocol_test.cpp index f3b72d6160..3bdbf1798d 100644 --- a/tracking/tracking_tests/protocol_test.cpp +++ b/tracking/tracking_tests/protocol_test.cpp @@ -6,22 +6,35 @@ using namespace tracking; UNIT_TEST(Protocol_CreateAuthPacket) { - auto packet = Protocol::CreateAuthPacket("AAA"); + auto packet = Protocol::CreateAuthPacket("ABC"); 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', ()); + TEST_EQUAL(packet[5], 'B', ()); + TEST_EQUAL(packet[6], 'C', ()); +} + +UNIT_TEST(Protocol_DecodeHeader) +{ + string id_str("ABC"); + auto packet = Protocol::CreateAuthPacket(id_str); + TEST_EQUAL(packet.size(), 7, ()); + TEST_EQUAL(Protocol::PacketType(packet[0]), Protocol::PacketType::CurrentAuth, ()); + + auto header = Protocol::DecodeHeader(packet); + CHECK_EQUAL(header.first, Protocol::PacketType::CurrentAuth, ()); + CHECK_EQUAL(header.second, id_str.size(), ()); } 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))); + using Container = Protocol::DataElementsCirc; + Container buffer(5); + buffer.push_back(Container::value_type(1, ms::LatLon(10, 10))); + buffer.push_back(Container::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, ()); @@ -32,3 +45,40 @@ UNIT_TEST(Protocol_CreateDataPacket) TEST_EQUAL(packet[5], 227, ()); TEST_EQUAL(packet[6], 241, ()); } + +UNIT_TEST(Protocol_DecodeAuthPacket) +{ + auto packet = Protocol::CreateAuthPacket("ABC"); + TEST_EQUAL(packet.size(), 7, ()); + TEST_EQUAL(Protocol::PacketType(packet[0]), Protocol::PacketType::CurrentAuth, ()); + + auto result = Protocol::DecodeAuthPacket(Protocol::PacketType::CurrentAuth, packet); + TEST_EQUAL(result, "ABC", ()); +} + +UNIT_TEST(Protocol_DecodeDataPacket) +{ + double const kEps = 1e-5; + + using Container = Protocol::DataElementsVec; + + Container points; + points.push_back(Container::value_type(1, ms::LatLon(10, 10))); + points.push_back(Container::value_type(2, ms::LatLon(15, 15))); + auto packet = Protocol::CreateDataPacket(points); + TEST_EQUAL(packet.size(), 26, ()); + TEST_EQUAL(Protocol::PacketType(packet[0]), Protocol::PacketType::CurrentData, ()); + + Container result = Protocol::DecodeDataPacket(Protocol::PacketType::CurrentData, packet); + + TEST_EQUAL(points.size(), result.size(), ()); + for (size_t i = 0; i < points.size(); ++i) + { + TEST_EQUAL(points[i].m_timestamp, result[i].m_timestamp, + (points[i].m_timestamp, result[i].m_timestamp)); + TEST(my::AlmostEqualAbsOrRel(points[i].m_latLon.lat, result[i].m_latLon.lat, kEps), + (points[i].m_latLon.lat, result[i].m_latLon.lat)); + TEST(my::AlmostEqualAbsOrRel(points[i].m_latLon.lon, result[i].m_latLon.lon, kEps), + (points[i].m_latLon.lon, result[i].m_latLon.lon)); + } +}