diff options
author | Sergey Yershov <syershov@maps.me> | 2016-10-31 17:02:56 +0300 |
---|---|---|
committer | Sergey Yershov <syershov@maps.me> | 2016-11-01 15:30:27 +0300 |
commit | 35e1a79cc5e69457a78113baafc4ae0f2f102848 (patch) | |
tree | 6746d29262541810b7087a5063fe4f6b4453271a /tracking | |
parent | ba32a88ce2041928d61702fc38bc00c4a1a0d823 (diff) |
Bindings to Python for tracking protocol
Diffstat (limited to 'tracking')
-rw-r--r-- | tracking/protocol.cpp | 98 | ||||
-rw-r--r-- | tracking/protocol.hpp | 11 | ||||
-rw-r--r-- | tracking/pytracking/bindings.cpp | 112 | ||||
-rw-r--r-- | tracking/tracking_tests/protocol_test.cpp | 62 |
4 files changed, 263 insertions, 20 deletions
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 <typename Container> +vector<uint8_t> CreateDataPacketImpl(Container const & points) +{ + vector<uint8_t> buffer; + MemWriter<decltype(buffer)> writer(buffer); + tracking::Protocol::Encoder::SerializeDataPoints(tracking::Protocol::Encoder::kLatestVersion, + writer, points); + + auto packet = tracking::Protocol::CreateHeader(tracking::Protocol::PacketType::CurrentData, + static_cast<uint32_t>(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<uint8_t> Protocol::CreateHeader(PacketType type, uint32_t payloadSize) +{ + vector<uint8_t> header; + InitHeader(header, type, payloadSize); + return header; +} + +// static vector<uint8_t> Protocol::CreateAuthPacket(string const & clientId) { vector<uint8_t> packet; @@ -23,19 +52,62 @@ vector<uint8_t> Protocol::CreateAuthPacket(string const & clientId) return packet; } -vector<uint8_t> Protocol::CreateDataPacket(DataElements const & points) +// static +vector<uint8_t> Protocol::CreateDataPacket(DataElementsCirc const & points) { - vector<uint8_t> buffer; - MemWriter<decltype(buffer)> writer(buffer); - Encoder::SerializeDataPoints(Encoder::kLatestVersion, writer, points); + return CreateDataPacketImpl(points); +} - vector<uint8_t> packet; - InitHeader(packet, PacketType::CurrentData, static_cast<uint32_t>(buffer.size())); - packet.insert(packet.end(), begin(buffer), end(buffer)); +// static +vector<uint8_t> Protocol::CreateDataPacket(DataElementsVec const & points) +{ + return CreateDataPacketImpl(points); +} - return packet; +// static +pair<Protocol::PacketType, size_t> Protocol::DecodeHeader(vector<uint8_t> const & data) +{ + ASSERT_GREATER_OR_EQUAL(data.size(), sizeof(uint32_t /* header */), ()); + + uint32_t size = (*reinterpret_cast<uint32_t const *>(data.data())) & 0xFFFFFF00; + if (!IsBigEndian()) + size = ReverseByteOrder(size); + + return make_pair(PacketType(static_cast<uint8_t>(data[0])), size); } +// static +string Protocol::DecodeAuthPacket(Protocol::PacketType type, vector<uint8_t> 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<uint8_t> const & data) +{ + ASSERT_GREATER_OR_EQUAL(data.size(), sizeof(uint32_t /* header */), ()); + DataElementsVec points; + MemReader memReader(data.data(), data.size()); + ReaderSource<MemReader> 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<uint8_t> & packet, PacketType type, uint32_t payloadSize) { packet.resize(sizeof(uint32_t)); @@ -46,7 +118,7 @@ void Protocol::InitHeader(vector<uint8_t> & packet, PacketType type, uint32_t pa if (!IsBigEndian()) size = ReverseByteOrder(size); - + packet[0] = static_cast<uint8_t>(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<uint32_t>(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<Encoder::DataPoint>; + using DataElementsCirc = boost::circular_buffer<Encoder::DataPoint>; + using DataElementsVec = vector<Encoder::DataPoint>; static uint8_t const kOk[4]; static uint8_t const kFail[4]; @@ -27,8 +28,14 @@ public: CurrentData = DataV0 }; + static vector<uint8_t> CreateHeader(PacketType type, uint32_t payloadSize); static vector<uint8_t> CreateAuthPacket(string const & clientId); - static vector<uint8_t> CreateDataPacket(DataElements const & points); + static vector<uint8_t> CreateDataPacket(DataElementsCirc const & points); + static vector<uint8_t> CreateDataPacket(DataElementsVec const & points); + + static pair<PacketType, size_t> DecodeHeader(vector<uint8_t> const & data); + static string DecodeAuthPacket(PacketType type, vector<uint8_t> const & data); + static DataElementsVec DecodeDataPacket(PacketType type, vector<uint8_t> const & data); private: static void InitHeader(vector<uint8_t> & 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 <boost/python.hpp> +#include <boost/python/suite/indexing/vector_indexing_suite.hpp> + +namespace +{ +using namespace boost::python; + +// Converts a std::pair instance to a Python tuple. +template <typename T1, typename T2> +struct pair_to_tuple +{ + static PyObject * convert(pair<T1, T2> const & p) + { + return incref(make_tuple(p.first, p.second).ptr()); + } + + static PyTypeObject const * get_pytype() { return &PyTuple_Type; } +}; + +template <typename T1, typename T2> +struct pair_to_python_converter +{ + pair_to_python_converter() { to_python_converter<pair<T1, T2>, pair_to_tuple<T1, T2>, true>(); } +}; + +// Converts a vector<uint8_t> to/from Python str. +struct vector_uint8t_to_str +{ + static PyObject * convert(vector<uint8_t> const & v) + { + str s(reinterpret_cast<char const *>(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<vector<uint8_t>>()); + } + + 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<vector<uint8_t>> *)data)->storage.bytes; + new (storage) vector<uint8_t>(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<Protocol::PacketType, size_t>(); + to_python_converter<vector<uint8_t>, vector_uint8t_to_str>(); + vector_uint8t_from_python_str(); + + class_<Protocol::DataElementsVec>("DataElementsVec") + .def(vector_indexing_suite<Protocol::DataElementsVec>()); + + class_<ms::LatLon>("LatLon") + .def_readwrite("lat", &ms::LatLon::lat) + .def_readwrite("lon", &ms::LatLon::lon); + + class_<coding::TrafficGPSEncoder::DataPoint>("DataPoint") + .def(init<uint64_t, ms::LatLon const &>()) + .def_readwrite("timestamp", &coding::TrafficGPSEncoder::DataPoint::m_timestamp) + .def_readwrite("coords", &coding::TrafficGPSEncoder::DataPoint::m_latLon); + + enum_<Protocol::PacketType>("PacketType") + .value("AuthV0", Protocol::PacketType::AuthV0) + .value("DataV0", Protocol::PacketType::DataV0) + .value("CurrentAuth", Protocol::PacketType::CurrentAuth) + .value("CurrentData", Protocol::PacketType::CurrentData); + + vector<uint8_t> (*CreateDataPacket1)(Protocol::DataElementsCirc const &) = + &Protocol::CreateDataPacket; + vector<uint8_t> (*CreateDataPacket2)(Protocol::DataElementsVec const &) = + &Protocol::CreateDataPacket; + + class_<Protocol>("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)); + } +} |