Class: Takagi::Message::Outbound
- Inherits:
-
Base
- Object
- Base
- Takagi::Message::Outbound
show all
- Defined in:
- lib/takagi/message/outbound.rb,
sig/takagi/message/outbound.rbs
Overview
Class for outbound message that is coming from server
Instance Attribute Summary
Attributes inherited from Base
#code, #message_id, #options, #payload, #token, #type, #version
Instance Method Summary
collapse
Methods inherited from Base
#coap_code_to_method, #coap_method_to_code, #coerce_option_value, #decode_extended_value, #extract_payload, #parse, #parse_options, #store_option
Constructor Details
#initialize(code:, payload:, token: nil, message_id: nil, type: CoAP::Registries::MessageType::NON, options: {}, transport: :udp) ⇒ Outbound
Returns a new instance of Outbound.
7
8
9
10
11
12
13
14
15
16
17
|
# File 'lib/takagi/message/outbound.rb', line 7
def initialize(code:, payload:, token: nil, message_id: nil, type: CoAP::Registries::MessageType::NON, options: {}, transport: :udp)
super(nil, transport: transport) @code = coap_method_to_code(code)
@token = token || ''.b
@message_id = message_id || rand(0..0xFFFF)
@type = type
@options = normalize_options(options)
@payload = serialize_payload(payload)
end
|
Instance Method Details
122
123
124
125
126
127
128
|
# File 'lib/takagi/message/outbound.rb', line 122
def
version = Takagi::CoAP::VERSION
type = @type || CoAP::Registries::MessageType::ACK token_length = @token.bytesize
version_type_token_length = (version << 6) | (type << 4) | token_length
[version_type_token_length, @code, @message_id].pack('CCn')
end
|
#build_options_section ⇒ Object
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
|
# File 'lib/takagi/message/outbound.rb', line 134
def build_options_section
return ''.b if @options.empty?
encoded = ''.b
last_option_number = 0
flattened_options.each do |number, value|
value_bytes = encode_option_value(value)
delta = number - last_option_number
delta_nibble, delta_extension = (delta)
length_nibble, length_extension = (value_bytes.bytesize)
option_byte = (delta_nibble << 4) | length_nibble
encoded << option_byte.chr
encoded << delta_extension if delta_extension
encoded << length_extension if length_extension
encoded << value_bytes
last_option_number = number
end
encoded
end
|
#build_payload_section ⇒ Object
159
160
161
162
163
|
# File 'lib/takagi/message/outbound.rb', line 159
def build_payload_section
return ''.b if @payload.nil? || @payload.empty?
"\xFF".b + @payload.b
end
|
#encode_integer_option_value(value) ⇒ Object
198
199
200
201
202
203
204
205
206
207
|
# File 'lib/takagi/message/outbound.rb', line 198
def encode_integer_option_value(value)
return ''.b if value.zero?
bytes = []
while value.positive?
bytes << (value & 0xFF)
value >>= 8
end
bytes.reverse.pack('C*')
end
|
209
210
211
212
213
214
215
216
217
218
219
220
|
# File 'lib/takagi/message/outbound.rb', line 209
def (value)
case value
when 0..12
[value, nil]
when 13..268
[13, [value - 13].pack('C')]
when 269..65_804
[14, [value - 269].pack('n')]
else
raise ArgumentError, 'Option value too large'
end
end
|
#encode_option_value(value) ⇒ Object
189
190
191
192
193
194
195
196
|
# File 'lib/takagi/message/outbound.rb', line 189
def encode_option_value(value)
case value
when Integer
encode_integer_option_value(value)
else
value.to_s.b
end
end
|
#flattened_options ⇒ Object
183
184
185
186
187
|
# File 'lib/takagi/message/outbound.rb', line 183
def flattened_options
@options.flat_map do |number, values|
values.map { |value| [number, value] }
end.sort_by.with_index { |(number, _), index| [number, index] }
end
|
#log_final_packet(packet) ⇒ Object
165
166
167
|
# File 'lib/takagi/message/outbound.rb', line 165
def log_final_packet(packet)
@logger.debug "Final CoAP packet: #{packet.inspect}"
end
|
#log_generation ⇒ Object
96
97
98
99
|
# File 'lib/takagi/message/outbound.rb', line 96
def log_generation
@logger.debug "Generating CoAP packet for code #{@code}, payload #{@payload.inspect}, " \
"message_id #{@message_id}, token #{@token.inspect}, type #{@type}"
end
|
#normalize_options(options) ⇒ Object
169
170
171
172
173
174
175
176
177
178
179
180
181
|
# File 'lib/takagi/message/outbound.rb', line 169
def normalize_options(options)
return {} unless options.is_a?(Hash)
@logger.debug "Packet options are: #{options.inspect}"
options.each_with_object({}) do |(key, value), acc|
numeric_key = Integer(key)
values = Array(value)
acc[numeric_key] = values
end
rescue ArgumentError
{}
end
|
#serialize_payload(payload) ⇒ String?
Serialize payload based on content-format option
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
# File 'lib/takagi/message/outbound.rb', line 23
def serialize_payload(payload)
return nil if payload.nil?
return payload.b if payload.is_a?(String)
content_format = @options[CoAP::Registries::Option::CONTENT_FORMAT]
content_format = content_format.first if content_format.is_a?(Array)
content_format ||= CoAP::Registries::ContentFormat::JSON
Serialization::Registry.encode(payload, content_format)
rescue Serialization::UnknownFormatError
@logger.warn "Unknown content-format #{content_format}, falling back to JSON"
payload.to_json.b
rescue Serialization::EncodeError => e
@logger.error "Serialization failed: #{e.message}, falling back to JSON"
payload.to_json.b
end
|
#to_bytes(transport: nil) ⇒ Object
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
# File 'lib/takagi/message/outbound.rb', line 46
def to_bytes(transport: nil)
return ''.b unless @code
actual_transport = transport || @transport
with_error_handling do
log_generation
packet = encode_with_transport(actual_transport)
log_final_packet(packet)
packet
end
end
|
#token_bytes ⇒ Object
130
131
132
|
# File 'lib/takagi/message/outbound.rb', line 130
def token_bytes
@token.to_s.b
end
|
#with_error_handling { ... } ⇒ Object
89
90
91
92
93
94
|
# File 'lib/takagi/message/outbound.rb', line 89
def with_error_handling
yield
rescue StandardError => e
@logger.error "To_bytes failed: #{e.message} at #{e.backtrace.first}"
''.b
end
|