{%- if msg_proto_utils_header | length -%} #include "{{ msg_proto_utils_header }}" {%- endif %} #include #include {%- if cpp_namespace is defined and cpp_namespace is not none %} namespace {{ cpp_namespace }} { {%- endif %} // Populate the protobuf Header (field 1) shared by every typed message. static void updateHeader(messages::Header *header, const QDateTime ×tamp, const QString &deviceSerialNum, quint16 msgId, qint16 sequence) { if (header) { const auto msecs = timestamp.toMSecsSinceEpoch(); header->set_deviceserialnum(deviceSerialNum.toStdString()); auto proto_timestamp = header->mutable_timestamp(); if (proto_timestamp) { proto_timestamp->set_seconds(msecs / 1000); proto_timestamp->set_nanos((msecs % 1000) * 1000000); } header->set_msgid(msgId); header->set_sequence(sequence); } } {%- for (msg_id_value, msg) in msg_cpp.data.items() %} {%- set has_union = msg['payload'] | selectattr('type', 'equalto', 'union') | list | length > 0 %} // {{ msg['msg_id'] }} ({{ msg['msg_id_hex_string'] }}) // payload: {{ msg_cpp.field_list(msg_id_value) | join(", ") }} // serializeProto: msg struct -> QByteArray of serialized protobuf data (header populated from params) QByteArray serializeProto([[maybe_unused]] const {{ msg['msg_name'] }}Payload &src, const QDateTime ×tamp, const QString &deviceSerialNum, quint16 msgId, qint16 sequence) { {%- if has_union %} qDebug().noquote() << "WARNING: MsgId={{ msg['msg_name'] }} contains union/oneof field(s); protobuf serialization is partial"; {%- endif %} messages::{{ msg['msg_name'] }} proto; {%- for field in msg['payload'] %} {%- if field['type'] != "union" %} // {{ field['type'] ~ "-" ~ field['name'] }} proto.set_{{ field['name'].lower() }}(src.{{ field['name'] }}.value); {%- else %} // TODO: {{ field['type'] ~ "-" ~ field['name'] }} {%- endif %} {%- endfor %} updateHeader(proto.mutable_header(), timestamp, deviceSerialNum, msgId, sequence); std::string out; (void)proto.SerializeToString(&out); return QByteArray(out.data(), static_cast(out.size())); } // {{ msg['msg_id'] }} ({{ msg['msg_id_hex_string'] }}) // payload: {{ msg_cpp.field_list(msg_id_value) | join(", ") }} // deserializeProto: QByteArray of serialized protobuf data -> msg struct (false on parse failure) bool deserializeProto(const QByteArray &bytes, [[maybe_unused]] {{ msg['msg_name'] }}Payload &dst) { {%- if has_union %} qDebug().noquote() << "WARNING: MsgId={{ msg['msg_name'] }} contains union/oneof field(s); protobuf deserialization is partial"; {%- endif %} messages::{{ msg['msg_name'] }} proto; if (proto.ParseFromArray(bytes.constData(), bytes.size()) == false) { qDebug().noquote() << "ERROR: could not parse protobuf for MsgId={{ msg['msg_name'] }}"; return false; } {%- for field in msg['payload'] %} {%- if field['type'] != "union" %} // {{ field['type'] ~ "-" ~ field['name'] }} dst.{{ field['name'] }}.value = proto.{{ field['name'].lower() }}(); {%- else %} // TODO: {{ field['type'] ~ "-" ~ field['name'] }} {%- endif %} {%- endfor %} return true; } {%- endfor %} QByteArray canMessageToProtobufByteArray(const QDateTime ×tamp, const QString &deviceSerialNum, const Can::Message &msg) { switch (msg.msgId) { {%- for (msg_id_value, msg) in msg_cpp.data.items() %} case {{ msg['msg_id'] }}: { {{ msg['msg_name'] }}Payload payload; if (payload.fromQByteArray(msg.data) == false) { qDebug().noquote() << "ERROR: could not convert CAN message with MsgId={{ msg['msg_name'] }} to struct"; } payload.dump(); return serializeProto(payload, timestamp, deviceSerialNum, msg.msgId, msg.sequence); } {%- endfor %} default: qDebug().noquote() << QString("WARNING: MsgId=0x%1 not handled").arg(msg.msgId, 4, 16, QChar('0')); break; } return QByteArray(); } // Maps a msgId to its fully-qualified protobuf message name for descriptor-pool lookup. const std::string &msgIdToProtoName(quint16 msgId) { static const std::unordered_map names = { {%- for (msg_id_value, msg) in msg_cpp.data.items() %} {%- if cpp_namespace is defined and cpp_namespace is not none %} { {{ msg['msg_id'] }}, "{{ cpp_namespace }}.messages.{{ msg['msg_name'] }}" }, {%- else %} { {{ msg['msg_id'] }}, "messages.{{ msg['msg_name'] }}" }, {%- endif %} {%- endfor %} }; static const std::string empty; const auto it = names.find(msgId); return it == names.end() ? empty : it->second; } {%- if cpp_namespace is defined and cpp_namespace is not none %} } // namespace {{ cpp_namespace }} {%- endif %}