使用 deserializeBinary 函数反序列化 protobuf 的 Uint8Array 消息

Deserialize Uint8Array memssage for protobuf with deserializeBinary function

正在尝试读取通过 BLE 从 ESP32 芯片发送的 protobuf 消息的结果,但无法反序列化传入消息。在 Android 设备的 Ionic 应用程序中将 protobuf 用于 javascript。这是我的代码:

import * as goog from 'google-protobuf';
export class PrepareMessageService {
  protoMessages = require('../assets/proto-js/wifi_config_pb.js');
  cmdSetConfig = new this.protoMessages.CmdSetConfig();
  respSetConfig = new this.protoMessages.RespSetConfig();
  wiFiConfigPayload = new this.protoMessages.WiFiConfigPayload();

  deserializeToBinary(message: any){
    const binMsg = new Uint8Array(message);
    const deserializedMsg = this.protoMessages.RespSetConfig.deserializeBinary(binMsg);
    console.log(JSON.stringify(deserializedMsg));
  }

console.log 的输出是:{"wrappers_":null,"arrayIndexOffset_":-1,"array":[],"pivot_":1.7976931348623157e+308,"convertedPrimitiveFields_":{}

我期待在“数组”中看到一些内容,但它是空的。相反,如果我使用以下代码通过 BLE 打印从 EPS32 芯片接收到的消息的每个元素(函数 deserializeToBinary 的参数):

for (let i = 0; i < message.byteLength; i++){
      console.log('message[', i, ']: ', message.getUint8(i));
}

这是输出:

message[0]: 8
message[1]: 3
message[2]: 106
message[3]: 2
message[4]: 8
message[5]: 6

现在我知道消息 [5] 正确表示 RespSetConfig 的状态(显示在下面我的主要 protobuf 文件中)因为每次我在 ESP32 芯片端更改它时,我都会在消息中得到正确的代码[5].下面显示的 constants.proto 文件显示了 RespSetConfig 的各种状态代码。那么,为什么我在 deserializeToBinary 中的代码没有为 message 参数的所有元素提供正确的表示形式?尽管 ESP32 向我发送了不同的 RespSetConfig 状态值,但 deserializeToBinary 的控制台日志打印出完全相同的内容,但没有明确指示 RespSetConfig 的状态值是什么。在这种情况下,我可以使用 message 参数的不同元素,但在我需要正确反序列化传入的 protobuf 响应的其他情况下,这不一定能解决我的问题。

这是主要的 protobuf 文件:

syntax = "proto3";
package espressif;

import "constants.proto";
import "wifi_constants.proto";

message CmdGetStatus {}

message RespGetStatus {
    Status status = 1;
    WifiStationState sta_state = 2;
    oneof state {
        WifiConnectFailedReason fail_reason = 10;
        WifiConnectedState connected = 11;
    }
}

message CmdSetConfig {
    string ssid = 1;
    string passphrase = 2;
    bytes bssid = 3;
    int32 channel = 4;
}

message RespSetConfig {
    Status status = 1;
}

message CmdApplyConfig {}

message RespApplyConfig {
    Status status = 1;
}

enum WiFiConfigMsgType {
    TypeCmdGetStatus = 0;
    TypeRespGetStatus = 1;
    TypeCmdSetConfig = 2;
    TypeRespSetConfig = 3;
    TypeCmdApplyConfig = 4;
    TypeRespApplyConfig = 5;
}

message WiFiConfigPayload {
    WiFiConfigMsgType msg = 1;
    oneof payload {
        CmdGetStatus cmd_get_status = 10;
        RespGetStatus resp_get_status = 11;
        CmdSetConfig cmd_set_config = 12;
        RespSetConfig resp_set_config = 13;
        CmdApplyConfig cmd_apply_config = 14;
        RespApplyConfig resp_apply_config = 15;
    }
}

这是在主 proto 文件中导入的 constants.proto 文件:

syntax = "proto3";
package espressif;

/* Allowed values for the status
 * of a protocomm instance */
enum Status {
    Success = 0;
    InvalidSecScheme = 1;
    InvalidProto = 2;
    TooManySessions = 3;
    InvalidArgument = 4;
    InternalError = 5;
    CryptoError = 6;
    InvalidSession = 7;
}

弄清楚为什么这不起作用。我的 deserializeToBinary 函数中有不少错误。这有效:

deserializeToBinary(message: any){
  dataViewToNumbers(message) //dataViewToNumbers can be imported from @capacitor-community/bluetooth-le
  console.log(message); //this prints protobuf response elements stored in the array array.  Output is as expected in question.
      };

然后,我们可以在消息上使用deserializeBinary 来反序列化它,然后在反序列化的消息上使用protobuf 生成的函数来获取我们需要的数据。例如,下面的代码将通过先用deserializeBinary反序列化消息,然后用getRestSetConfig().getStatus()获取消息状态,来提供WifiConfigPayload对象中respSetConfig消息类型的状态,两者都是由[=生成的27=] protobuf 编译器:this.messages.WiFiConfigPayload.deserializeBinary(message).getRespSetConfig().getStatus()

要使用 protobuf 消息,必须了解 encoding/decoding 进出 protobuf 消息。 Google 网站上的以下 link 可以让您开始了解如何 code/decode protobuf 消息:https://developers.google.com/protocol-buffers/docs/encoding

也就是说,Google 自己的文档对于 protobufs 的新手来说是不够的。以下 link 在解释 protobuf 消息的 coding/decoding 方面做得更好:https://medium.com/nerd-for-tech/protobuf-what-why-fcb324a64564