Class: Tamashii::Agent::Networking

Inherits:
Component
  • Object
show all
Includes:
AASM
Defined in:
lib/tamashii/agent/networking.rb,
lib/tamashii/agent/networking/request_observer.rb

Defined Under Namespace

Classes: RequestObserver, RequestTimeoutError

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Component

#check_new_event, #clean_up, #default_device_name, #display_name, #get_device_class_name, #get_device_instance, #handle_new_event, #initialize_device, #load_default_device, #restart_current_component_async, #run, #run!, #run_worker_loop, #send_event, #stop, #worker_loop

Methods included from Common::Loggable

#display_name, #logger, #progname

Constructor Details

#initialize(name, master, options = {}) ⇒ Networking

Returns a new instance of Networking.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/tamashii/agent/networking.rb', line 46

def initialize(name, master, options = {})
  super

  self.reset
  @client = Tamashii::Client::Base.new

  @tag = 0

  @future_ivar_pool = Concurrent::Map.new

  @last_error_report_time = Time.now
  setup_callbacks
  setup_resolver
end

Instance Attribute Details

#masterObject (readonly)

Returns the value of attribute master.



44
45
46
# File 'lib/tamashii/agent/networking.rb', line 44

def master
  @master
end

#urlObject (readonly)

Returns the value of attribute url.



43
44
45
# File 'lib/tamashii/agent/networking.rb', line 43

def url
  @url
end

Instance Method Details

#create_request_async(id, ev_type, ev_body) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/tamashii/agent/networking.rb', line 203

def create_request_async(id, ev_type, ev_body)
  req = Concurrent::Future.new do
    # Create IVar for store result
    ivar = Concurrent::IVar.new
    @future_ivar_pool[id] = ivar
    # Schedule to get the result
    create_request_scheduler_task(id, ev_type, ev_body)
    # Wait for result
    if result = ivar.value(Config.connection_timeout)
      # IVar is already removed from pool
      result
    else
      # Manually remove IVar
      # Any fulfill at this point is useless
      logger.error "Timeout when getting IVar for #{id}"
      @future_ivar_pool.delete(id)
      raise RequestTimeoutError, "Request Timeout"
    end
  end
  req.add_observer(RequestObserver.new(self, id, ev_type, ev_body, req))
  req.execute
  req
end

#create_request_scheduler_task(id, ev_type, ev_body) ⇒ Object



199
200
201
# File 'lib/tamashii/agent/networking.rb', line 199

def create_request_scheduler_task(id, ev_type, ev_body)
  schedule_next_task(0, id, ev_type, ev_body, Time.now, 0)
end

#handle_card_result(result) ⇒ Object



79
80
81
82
83
84
85
86
87
88
# File 'lib/tamashii/agent/networking.rb', line 79

def handle_card_result(result)
  if result["auth"]
    @master.send_event(Event.new(Event::BEEP, "ok"))
  else
    @master.send_event(Event.new(Event::BEEP, "no"))
  end
  if result["message"]
    @master.send_event(Event.new(Event::LCD_MESSAGE, result["message"]))
  end
end

#handle_remote_response(ev_type, wrapped_ev_body) ⇒ Object

When data is back



238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/tamashii/agent/networking.rb', line 238

def handle_remote_response(ev_type, wrapped_ev_body)
  logger.debug "Remote packet back: #{ev_type} #{wrapped_ev_body}"
  result = JSON.parse(wrapped_ev_body)
  id = result["id"]
  ev_body = result["ev_body"]
  # fetch ivar and delete it
  if ivar = @future_ivar_pool.delete(id)
    ivar.set(ev_type: ev_type, ev_body: ev_body)
  else
    logger.warn "IVar #{id} not in pool"
  end
end

#new_remote_request(id, ev_type, ev_body) ⇒ Object



227
228
229
230
231
232
233
234
235
# File 'lib/tamashii/agent/networking.rb', line 227

def new_remote_request(id, ev_type, ev_body)
  # enqueue if not exists
  if !@future_ivar_pool[id]
    create_request_async(id, ev_type, ev_body)
    logger.debug "Request created: #{id}"
  else
    logger.warn "Duplicated id: #{id}, ignored"
  end
end

#on_request_timeout(ev_type, ev_body) ⇒ Object



75
76
77
# File 'lib/tamashii/agent/networking.rb', line 75

def on_request_timeout(ev_type, ev_body)
  @master.send_event(Event.new(Event::CONNECTION_NOT_READY, "Connection not ready for #{ev_type}:#{ev_body}"))
end

#process_event(event) ⇒ Object

override



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/tamashii/agent/networking.rb', line 162

def process_event(event)
  case event.type
  when Event::CARD_DATA
    if self.ready?
      id = event.body
      wrapped_body = {
        id: id,
        ev_body: event.body
      }.to_json
      new_remote_request(id, Type::RFID_NUMBER, wrapped_body)
    else
      @master.send_event(Event.new(Event::CONNECTION_NOT_READY, "Connection not ready for #{event.type}:#{event.body}"))
    end
  end
end

#process_packet(pkt) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/tamashii/agent/networking.rb', line 138

def process_packet(pkt)
  if self.auth_pending?
    if pkt.type == Type::AUTH_RESPONSE
      if pkt.body == Packet::STRING_TRUE
        @tag = pkt.tag
        self.auth_success
      else
        logger.error "Authentication failed. Delay for 3 seconds"
        @master.send_event(Event.new(Event::LCD_MESSAGE, "Fatal Error\nAuth Failed"))
        sleep 3
      end
    else
      logger.error "Authentication error: Not an authentication result packet"
    end
  else
    if pkt.tag == @tag || pkt.tag == 0
      Resolver.resolve(pkt)
    else
      logger.debug "Tag mismatch packet: tag: #{pkt.tag}, type: #{pkt.type}"
    end
  end
end

#schedule_next_task(interval, id, ev_type, ev_body, start_time, times) ⇒ Object



195
196
197
# File 'lib/tamashii/agent/networking.rb', line 195

def schedule_next_task(interval, id, ev_type, ev_body, start_time, times)
  Concurrent::ScheduledTask.execute(interval, args: [id, ev_type, ev_body, start_time, times], &method(:schedule_task_runner))
end

#schedule_task_runner(id, ev_type, ev_body, start_time, times) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/tamashii/agent/networking.rb', line 178

def schedule_task_runner(id, ev_type, ev_body, start_time, times)
  logger.debug "Schedule send attemp #{id} : #{times + 1} time(s)"
  if try_send_request(ev_type, ev_body)
    # Request sent, do nothing
    logger.debug "Request sent for id = #{id}"
  else
    if Time.now - start_time < Config.connection_timeout
      # Re-schedule self
      logger.warn "Reschedule #{id} after 1 sec"
      schedule_next_task(1, id,  ev_type, ev_body, start_time, times + 1)
    else
      # This job is expired. Do nothing
      logger.warn "Abort scheduling #{id}"
    end
  end
end

#send_auth_requestObject



104
105
106
107
108
109
110
111
# File 'lib/tamashii/agent/networking.rb', line 104

def send_auth_request
  # TODO: other types of auth
  if @client.transmit(Packet.new(Type::AUTH_TOKEN, 0, [Type::CLIENT[:agent], @master.serial_number,Config.token].join(",")).dump)
    logger.debug "Auth sent!"	
  else
    logger.error "Cannot sent auth request!"
	end
end

#setup_callbacksObject



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/tamashii/agent/networking.rb', line 113

def setup_callbacks
  @client.on :open, proc {
    logger.info "Server opened"
    self.auth_request
    send_auth_request
  }
  @client.on :close, proc {
    # Note: this only called when normally receive the WS close message
    logger.info "Server closed normally"
  }
  @client.on :socket_closed, proc {
    # Note: called when low-level IO is closed
    logger.info "Server socket closed"
    self.reset
  }
  @client.on :message, proc { |data| 
    pkt = Packet.load(data)
    process_packet(pkt) if pkt
  }
  @client.on :error, proc { |e|
    logger.error("#{e.message}")
  }
end

#setup_resolverObject



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/tamashii/agent/networking.rb', line 61

def setup_resolver
  env_data = {networking: self, master: @master}
  Resolver.config do
    [Type::REBOOT, Type::POWEROFF, Type::RESTART, Type::UPDATE].each do |type|
      handle type,  Handler::System, env_data
    end
    [Type::LCD_MESSAGE, Type::LCD_SET_IDLE_TEXT].each do |type|
      handle type,  Handler::Lcd, env_data
    end
    handle Type::BUZZER_SOUND,  Handler::Buzzer, env_data
    handle Type::RFID_RESPONSE_JSON,  Handler::RemoteResponse, env_data
  end
end

#stop_threadsObject



99
100
101
102
# File 'lib/tamashii/agent/networking.rb', line 99

def stop_threads
  super
  @client.close
end

#try_send_request(ev_type, ev_body) ⇒ Object



90
91
92
93
94
95
96
97
# File 'lib/tamashii/agent/networking.rb', line 90

def try_send_request(ev_type, ev_body)
  if self.ready?
    @client.transmit(Packet.new(ev_type, @tag, ev_body).dump)
    true
  else
    false
  end
end