Ruby BinData:根据前两个字节定义结构

Ruby BinData:根据前两个字节定义结构,ruby,bindata,Ruby,Bindata,我正在使用Ruby并尝试从TCP接口读取二进制数据。接收到的消息包含一个报头和一个有效负载。有效负载由来自报头的id确定 这是一个例子: class TCPmessage < BinData:: Record class PayloadType_1 < BinData::Record uint8 :payloadType_1 # more payload data end class PayloadType_2 < BinData::Record

我正在使用Ruby并尝试从TCP接口读取二进制数据。接收到的消息包含一个报头和一个有效负载。有效负载由来自报头的id确定

这是一个例子:

class TCPmessage < BinData:: Record

  class PayloadType_1 < BinData::Record
    uint8 :payloadType_1
    # more payload data
  end

  class PayloadType_2 < BinData::Record
    uint8 :payloadType_2
    # more payload data
  end

  uint8 :payload_id

  array :payload, :type => <<Here I need to select "PayloadType_1" or "PayloadType_2" based on the "payload_id" from above>>, ...

end

我确信有另一种解决方案使用BinData gem中的复合类型(数组/选项),但我看不到它。

鉴于我们对您的尝试知之甚少,我看不出您的操作方式有任何真正的错误

我会写得有点不同,但在功能上不会有太大区别:

x = TCPmessage.new
y = case x.read("TCPmessage").payload_id
    when 1
      TCPmessage::PayloadType_1.new
    when 2
      TCPmessage::PayloadType_2.new
    end
y.read("TCPmessage")

我不知道当问到这个问题时,BinData gem是否已经有了这个特性,但是对于声明性方法,您应该使用它的类型

对于您的情况,可能是这样:

require 'bindata'

class PayloadType_1 < BinData::Record
  uint8 :type_one_byte
  # more payload data for type 1
end

class PayloadType_2 < BinData::Record
  uint8 :type_two_byte
  # more payload data for type 2
end

class TCPmessage < BinData::Record
  uint8 :payload_id
  choice :payload, selection: :payload_id do
    array 1, type: :payloadType_1, read_until: :eof
    array 2, type: :payloadType_2, read_until: :eof
  end
end

puts TCPmessage.read "\x01ABC" # {:payload_id=>1, :payload=>[{:type_one_byte=>65}, {:type_one_byte=>66}, {:type_one_byte=>67}]}
puts TCPmessage.read "\x02DEF" # {:payload_id=>2, :payload=>[{:type_two_byte=>68}, {:type_two_byte=>69}, {:type_two_byte=>70}]}

我试图做的是根据头值在运行时选择数组的类型。是的,我可以告诉你。我无法根据BinData的
Choice
方法中标题的值返回其他类。正如他们的文档所示,返回其中一种类型很容易。返回一个“外星人”类型没有文档记录,这是你需要的。令人遗憾的是,像这样的高质量答案没有得到更多的关注<代码>选择在这种情况下绝对是正确的选择。@amenthes这对于在问题被问了很长时间后给出的答案来说并不罕见。不过,我很高兴您发现它很有用。:-)非常好!我用力按下upvote按钮,结果应该是+20,但事实并非如此:D。当@danizgod找到这个答案时,他可能会接受这个答案!
x = TCPmessage.new
y = case x.read("TCPmessage").payload_id
    when 1
      TCPmessage::PayloadType_1.new
    when 2
      TCPmessage::PayloadType_2.new
    end
y.read("TCPmessage")
require 'bindata'

class PayloadType_1 < BinData::Record
  uint8 :type_one_byte
  # more payload data for type 1
end

class PayloadType_2 < BinData::Record
  uint8 :type_two_byte
  # more payload data for type 2
end

class TCPmessage < BinData::Record
  uint8 :payload_id
  choice :payload, selection: :payload_id do
    array 1, type: :payloadType_1, read_until: :eof
    array 2, type: :payloadType_2, read_until: :eof
  end
end

puts TCPmessage.read "\x01ABC" # {:payload_id=>1, :payload=>[{:type_one_byte=>65}, {:type_one_byte=>66}, {:type_one_byte=>67}]}
puts TCPmessage.read "\x02DEF" # {:payload_id=>2, :payload=>[{:type_two_byte=>68}, {:type_two_byte=>69}, {:type_two_byte=>70}]}
  # [...]
  choice :payload, selection: lambda { payload_id.to_s } do
    array "1", type: :payloadType_1, read_until: :eof
    array "2", type: :payloadType_2, read_until: :eof
  end
  # [...]