0

我正在使用 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 中的复合类型(数组/选择)的解决方案,但我看不到它。

4

2 回答 2

2

我不知道在问这个问题时 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
  # [...]
于 2015-02-20T23:24:10.737 回答
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")
于 2012-11-02T16:33:28.223 回答