Class: Takagi::Message::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/takagi/message/base.rb,
sig/takagi/message/base.rbs

Overview

Base class for message

Direct Known Subclasses

Inbound, Outbound, Request

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data = nil, transport: :udp) ⇒ Base

Returns a new instance of Base.

Parameters:

  • data (Object, nil) (defaults to: nil)


9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/takagi/message/base.rb', line 9

def initialize(data = nil, transport: :udp)
  @transport = transport
  if data.is_a?(String) || data.is_a?(IO)
    case transport
    when :tcp
      parse_tcp(data)
    else
      parse(data)
    end
  end
  @data = data
  @logger = Takagi.logger
end

Instance Attribute Details

#codeObject (readonly)

Returns the value of attribute code.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def code
  @code
end

#message_idObject (readonly)

Returns the value of attribute message_id.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def message_id
  @message_id
end

#optionsObject (readonly)

Returns the value of attribute options.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def options
  @options
end

#payloadObject (readonly)

Returns the value of attribute payload.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def payload
  @payload
end

#tokenObject (readonly)

Returns the value of attribute token.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def token
  @token
end

#typeObject (readonly)

Returns the value of attribute type.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def type
  @type
end

#versionObject (readonly)

Returns the value of attribute version.

Returns:

  • (Object)


7
8
9
# File 'lib/takagi/message/base.rb', line 7

def version
  @version
end

Instance Method Details

#coap_code_to_method(code) ⇒ String

Convert CoAP code number to method name using registry

Parameters:

  • code (Integer)

    CoAP code number

Returns:

  • (String)

    Method name (e.g., 'GET', 'OBSERVE')



26
27
28
29
30
31
32
33
34
# File 'lib/takagi/message/base.rb', line 26

def coap_code_to_method(code)
  # Check if it's an OBSERVE request (GET with Observe option)
  if code == CoAP::Registries::Method::GET && @options && @options[CoAP::Registries::Option::OBSERVE]
    'OBSERVE'
  else
    # Use CoAP registry to convert code to string
    CoAP::CodeHelpers.to_string(code)
  end
end

#coap_method_to_code(method) ⇒ Integer

Convert method name to CoAP code number using registry

Parameters:

  • method (String, Symbol)

    Method name (e.g., 'GET', :post)

Returns:

  • (Integer)

    CoAP code number



39
40
41
# File 'lib/takagi/message/base.rb', line 39

def coap_method_to_code(method)
  CoAP::CodeHelpers.to_numeric(method)
end

#coerce_option_value(value) ⇒ Object

Parameters:

  • value (Object)

Returns:

  • (Object)


147
148
149
150
151
152
# File 'lib/takagi/message/base.rb', line 147

def coerce_option_value(value)
  ascii = value.dup.force_encoding('ASCII-8BIT')
  return ascii.force_encoding('UTF-8') if ascii.valid_encoding?

  ascii
end

#decode_extended_value(bytes, position, raw_value) ⇒ Object

Parameters:

  • bytes (Object)
  • position (Object)
  • raw_value (Object)

Returns:

  • (Object)


118
119
120
121
122
123
124
125
126
127
128
# File 'lib/takagi/message/base.rb', line 118

def decode_extended_value(bytes, position, raw_value)
  case raw_value
  when 13
    [bytes[position] + 13, position + 1]
  when 14
    extended = bytes[position, 2].pack('C*').unpack1('n') + 269
    [extended, position + 2]
  else
    [raw_value, position]
  end
end

#extract_payload(data) ⇒ nil, untyped

Parameters:

  • data (Object)

Returns:

  • (nil, untyped)


108
109
110
111
112
113
114
115
116
# File 'lib/takagi/message/base.rb', line 108

def extract_payload(data)
  Takagi.logger.debug "Extracting payload: #{data.inspect}"
  payload_start = data.index("\xFF".b)
  return nil unless payload_start

  payload = data[(payload_start + 1)..].dup.force_encoding('ASCII-8BIT')
  utf8 = payload.dup.force_encoding('UTF-8')
  utf8.valid_encoding? ? utf8 : payload
end

#parse(data) ⇒ Object

Parameters:

  • data (Object)

Returns:

  • (Object)


68
69
70
71
72
73
74
75
76
77
78
# File 'lib/takagi/message/base.rb', line 68

def parse(data)
  bytes = data.bytes
  @version = (bytes[0] >> 6) & 0b11
  @type    = (bytes[0] >> 4) & 0b11
  token_length = bytes[0] & 0b1111
  @code = bytes[1]
  @message_id = bytes[2..3].pack('C*').unpack1('n')
  @token   = token_length.positive? ? bytes[4, token_length].pack('C*') : ''.b
  @options = parse_options(bytes[(4 + token_length)..])
  @payload = extract_payload(data)
end

#parse_options(bytes) ⇒ Object

Parameters:

  • bytes (Object)

Returns:

  • (Object)


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/takagi/message/base.rb', line 80

def parse_options(bytes)
  options = {}
  position = 0
  last_option_number = 0

  while position < bytes.length && bytes[position] != 0xFF
    byte = bytes[position]
    position += 1

    delta_raw = (byte >> 4) & 0x0F
    length_raw = byte & 0x0F

    delta, position = decode_extended_value(bytes, position, delta_raw)
    length, position = decode_extended_value(bytes, position, length_raw)

    option_number = last_option_number + delta
    value = bytes[position, length].pack('C*')
    position += length

    store_option(options, option_number, value)

    last_option_number = option_number
  end

  Takagi.logger.debug "Parsed CoAP options: #{options.inspect}"
  options
end

#store_option(options, option_number, value) ⇒ Object

Parameters:

  • options (Object)
  • option_number (Object)
  • value (Object)

Returns:

  • (Object)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/takagi/message/base.rb', line 130

def store_option(options, option_number, value)
  formatted = coerce_option_value(value)

  # Uri-Path (11) and Uri-Query (15) are always stored as arrays
  case option_number
  when CoAP::Registries::Option::URI_PATH, CoAP::Registries::Option::URI_QUERY
    options[option_number] ||= []
    options[option_number] << formatted
  else
    options[option_number] = if options.key?(option_number)
                               Array(options[option_number]) << formatted
                             else
                               formatted
                             end
  end
end