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

时间:2012-11-02 15:06:01

标签: 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
    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

我尝试了一些变体,但只提出了以下解决方案:

class TCPmessage < BinData:: Record

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

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

  uint8 :payload_id
end

在主程序中,我首先阅读payload_id,然后使用case语句选择下一个要实例化的类:

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

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

2 个答案:

答案 0 :(得分:1)

我不知道当问到这个问题时BinData gem是否已经有了这个功能,但对于声明式方法,你想要使用它的Choice类型。

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

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}]}

请注意,selection: :payload_idselection: lambda { payload_id }的快捷方式。因此,如果您确实需要出于某种原因按字符串索引选项,则可以

  # [...]
  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
  # [...]

答案 1 :(得分:0)

我没有看到你做这件事的方式有什么问题,因为我们对你要做的事情知之甚少。

我的写法有点不同,但它在功能上并没有太大的区别:

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")