protobuf_parser.cpp
Go to the documentation of this file.
1 #include <QSettings>
2 #include <QMessageBox>
3 #include "protobuf_parser.h"
5 
6 namespace gp = google::protobuf;
7 
8 ProtobufParser::ProtobufParser(const std::string& topic_name, const std::string type_name,
9  const gp::FileDescriptorSet& descriptor_set,
11  : MessageParser(topic_name, data), _proto_pool(&_proto_database)
12 {
13  gp::FileDescriptorProto unused;
14 
15  for (int i = 0; i < descriptor_set.file_size(); ++i)
16  {
17  const auto& file = descriptor_set.file(i);
18  if (!_proto_database.FindFileByName(file.name(), &unused))
19  {
20  if (!_proto_database.Add(file))
21  {
22  throw std::runtime_error(
23  fmt::format("failed to add definition {} to protoDB", file.name()));
24  }
25  }
26  }
27 
28  _msg_descriptor = _proto_pool.FindMessageTypeByName(type_name);
29 
30  if (_msg_descriptor == nullptr)
31  {
32  throw std::runtime_error("Cannot get message descriptor");
33  }
34 }
35 
36 bool ProtobufParser::parseMessage(const MessageRef serialized_msg, double& timestamp)
37 {
38  const google::protobuf::Message* prototype_msg =
39  _msg_factory.GetPrototype(_msg_descriptor);
40 
41  google::protobuf::Message* mutable_msg = prototype_msg->New();
42  if (!mutable_msg->ParseFromArray(serialized_msg.data(), serialized_msg.size()))
43  {
44  return false;
45  }
46 
47  std::function<void(const google::protobuf::Message&, const std::string&, const bool)>
48  ParseImpl;
49 
50  ParseImpl = [&](const google::protobuf::Message& msg, const std::string& prefix,
51  const bool is_map) {
52  const gp::Reflection* reflection = msg.GetReflection();
53  const gp::Descriptor* descriptor = msg.GetDescriptor();
54  // std::vector<const FieldDescriptor*> reflection_fields;
55  // reflection->ListFields(msg, &reflection_fields);
56 
57  for (int index = 0; index < descriptor->field_count(); index++)
58  {
59  auto field = descriptor->field(index);
60 
61  std::string key =
62  prefix.empty() ? field->name() : fmt::format("{}/{}", prefix, field->name());
63  if (is_map)
64  {
65  // Map messages only have 2 fields: key and value. The key will be represented in
66  // the series name so skip it, and don't uselessly append "value" to the series
67  // name for the value.
68  if (field->name() == "key")
69  {
70  continue;
71  }
72  else
73  {
74  key = prefix;
75  }
76  }
77 
78  std::string suffix;
79 
80  if (!field)
81  {
82  continue;
83  }
84 
85  unsigned count = 1;
86  bool repeated = false;
87  if (field->is_repeated())
88  {
89  count = reflection->FieldSize(msg, field);
90  repeated = true;
91  }
92 
93  if (repeated && count > maxArraySize())
94  {
95  if (clampLargeArray())
96  {
97  count = std::max(count, maxArraySize());
98  }
99  else
100  {
101  continue;
102  }
103  }
104 
105  for (unsigned index = 0; index < count; index++)
106  {
107  if (repeated)
108  {
109  suffix = fmt::format("[{}]", index);
110  }
111 
112  bool is_double = true;
113  double value = 0;
114  switch (field->cpp_type())
115  {
116  case gp::FieldDescriptor::CPPTYPE_DOUBLE: {
117  value = !repeated ? reflection->GetDouble(msg, field) :
118  reflection->GetRepeatedDouble(msg, field, index);
119  }
120  break;
121  case gp::FieldDescriptor::CPPTYPE_FLOAT: {
122  auto tmp = !repeated ? reflection->GetFloat(msg, field) :
123  reflection->GetRepeatedFloat(msg, field, index);
124  value = static_cast<double>(tmp);
125  }
126  break;
127  case gp::FieldDescriptor::CPPTYPE_UINT32: {
128  auto tmp = !repeated ? reflection->GetUInt32(msg, field) :
129  reflection->GetRepeatedUInt32(msg, field, index);
130  value = static_cast<double>(tmp);
131  }
132  break;
133  case gp::FieldDescriptor::CPPTYPE_UINT64: {
134  auto tmp = !repeated ? reflection->GetUInt64(msg, field) :
135  reflection->GetRepeatedUInt64(msg, field, index);
136  value = static_cast<double>(tmp);
137  }
138  break;
139  case gp::FieldDescriptor::CPPTYPE_BOOL: {
140  auto tmp = !repeated ? reflection->GetBool(msg, field) :
141  reflection->GetRepeatedBool(msg, field, index);
142  value = static_cast<double>(tmp);
143  }
144  break;
145  case gp::FieldDescriptor::CPPTYPE_INT32: {
146  auto tmp = !repeated ? reflection->GetInt32(msg, field) :
147  reflection->GetRepeatedInt32(msg, field, index);
148  value = static_cast<double>(tmp);
149  }
150  break;
151  case gp::FieldDescriptor::CPPTYPE_INT64: {
152  auto tmp = !repeated ? reflection->GetInt64(msg, field) :
153  reflection->GetRepeatedInt64(msg, field, index);
154  value = static_cast<double>(tmp);
155  }
156  break;
157  case gp::FieldDescriptor::CPPTYPE_ENUM: {
158  auto tmp = !repeated ? reflection->GetEnum(msg, field) :
159  reflection->GetRepeatedEnum(msg, field, index);
160 
161  auto& series = this->getStringSeries(key + suffix);
162  series.pushBack({ timestamp, tmp->name() });
163  is_double = false;
164  }
165  break;
166  case gp::FieldDescriptor::CPPTYPE_STRING: {
167  auto tmp = !repeated ? reflection->GetString(msg, field) :
168  reflection->GetRepeatedString(msg, field, index);
169 
170  if (tmp.size() > 100)
171  {
172  // probably a blob, skip it
173  continue;
174  }
175  auto& series = this->getStringSeries(key + suffix);
176  series.pushBack({ timestamp, tmp });
177  is_double = false;
178  }
179  break;
180  case gp::FieldDescriptor::CPPTYPE_MESSAGE: {
181 // Fix macro issue in Windows
182 #pragma push_macro("GetMessage")
183 #undef GetMessage
184  const auto& new_msg = repeated ?
185  reflection->GetRepeatedMessage(msg, field, index) :
186  reflection->GetMessage(msg, field);
187 #pragma pop_macro("GetMessage")
188  if (field->is_map())
189  {
190  // A protobuf map looks just like a message but with a "key" and
191  // "value" field, extract the key so we can set a useful suffix.
192  const auto* map_descriptor = new_msg.GetDescriptor();
193  const auto* map_reflection = new_msg.GetReflection();
194  const auto* key_field = map_descriptor->FindFieldByName("key");
195  switch (key_field->cpp_type())
196  {
197  // A map's key is a scalar type (except floats and bytes) or a string
198  case gp::FieldDescriptor::CPPTYPE_STRING: {
199  suffix =
200  fmt::format("/{}", map_reflection->GetString(new_msg, key_field));
201  }
202  break;
203  case gp::FieldDescriptor::CPPTYPE_INT32: {
204  suffix =
205  fmt::format("/{}", map_reflection->GetInt32(new_msg, key_field));
206  }
207  break;
208  case gp::FieldDescriptor::CPPTYPE_INT64: {
209  suffix =
210  fmt::format("/{}", map_reflection->GetInt64(new_msg, key_field));
211  }
212  break;
213  case gp::FieldDescriptor::CPPTYPE_UINT32: {
214  suffix =
215  fmt::format("/{}", map_reflection->GetUInt32(new_msg, key_field));
216  }
217  break;
218  case gp::FieldDescriptor::CPPTYPE_UINT64: {
219  suffix =
220  fmt::format("/{}", map_reflection->GetUInt64(new_msg, key_field));
221  }
222  break;
223  }
224  }
225  ParseImpl(new_msg, key + suffix, field->is_map());
226 
227  is_double = false;
228  }
229  break;
230  }
231 
232  if (is_double)
233  {
234  auto& series = this->getSeries(key + suffix);
235  series.pushBack({ timestamp, value });
236  }
237  }
238  }
239  };
240 
241  // start recursion
242  ParseImpl(*mutable_msg, _topic_name, false);
243 
244  delete mutable_msg;
245  return true;
246 }
PJ::MessageParser::_topic_name
std::string _topic_name
Definition: messageparser_base.h:118
ProtobufParser::_msg_descriptor
const google::protobuf::Descriptor * _msg_descriptor
Definition: protobuf_parser.h:39
protobuf_parser.h
PJ::MessageRef::data
const uint8_t * data() const
Definition: messageparser_base.h:51
sol::type_name
std::string type_name(lua_State *L, type t)
Definition: sol.hpp:8079
mqtt_test_proto.msg
msg
Definition: mqtt_test_proto.py:43
PJ::MessageParser::maxArraySize
unsigned maxArraySize() const
Definition: messageparser_base.h:96
ProtobufParser::parseMessage
bool parseMessage(const MessageRef serialized_msg, double &timestamp) override
Definition: protobuf_parser.cpp:36
ProtobufParser::ProtobufParser
ProtobufParser(const std::string &topic_name, const google::protobuf::Descriptor *descriptor, PlotDataMapRef &data)
Definition: protobuf_parser.h:20
detail::count
constexpr auto count() -> size_t
Definition: core.h:1222
sol::call_status::file
@ file
PJ::MessageParser
The MessageParser is the base class used to parse a message with a specific encoding+schema.
Definition: messageparser_base.h:75
PJ::MessageParser::getSeries
PlotData & getSeries(const std::string &key)
Definition: messageparser_base.h:120
nlohmann::detail::void
j template void())
Definition: json.hpp:4061
PJ::MessageParser::getStringSeries
StringSeries & getStringSeries(const std::string &key)
Definition: messageparser_base.h:125
format
auto format(const text_style &ts, const S &format_str, const Args &... args) -> std::basic_string< Char >
Definition: color.h:543
field
static void field(LexState *ls, ConsControl *cc)
Definition: lparser.c:891
PJ::MessageRef
Definition: messageparser_base.h:28
core.h
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
PJ::MessageRef::size
size_t size() const
Definition: messageparser_base.h:61
PJ::PlotDataMapRef
Definition: plotdata.h:34
PJ::MessageParser::clampLargeArray
bool clampLargeArray() const
Definition: messageparser_base.h:101
ProtobufParser::_msg_factory
google::protobuf::DynamicMessageFactory _msg_factory
Definition: protobuf_parser.h:38


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:45