nodes: 0, module; 1, function_definition; 2, function_name:_serial_poller; 3, parameters; 4, block; 5, identifier:self; 6, expression_statement; 7, while_statement; 8, expression_statement; 9, comment:""" Priority-sorted list of checks
  
          Highest priority is the 'halt' channel, which is used to kill the
          thread and the serial communication channel and allow everything to be
          cleaned up.
  
          Second is the lid-open interrupt, which should trigger a callback
          (typically to halt the robot).
  
          Third is an enqueued command to send to the Thermocycler.
  
          Fourth (if no other work is available) is to query the Thermocycler for
          its current temp, target temp, and time remaining in its current cycle.
          """; 10, True; 11, block; 12, call; 13, expression_statement; 14, if_statement; 15, attribute; 16, argument_list; 17, assignment; 18, comparison_operator:self._halt_read_file.fileno() in _next; 19, block; 20, elif_clause; 21, elif_clause; 22, else_clause; 23, identifier:log; 24, identifier:info; 25, call; 26, identifier:_next; 27, call; 28, call; 29, identifier:_next; 30, expression_statement; 31, expression_statement; 32, comment:# Note: this is discarded because we send a set message to halt; 33, comment:# the thread--don't currently need to parse it; 34, break_statement; 35, comparison_operator:self._connection.fileno() in _next; 36, comment:# Lid-open interrupt; 37, block; 38, comparison_operator:self._send_read_file.fileno() in _next; 39, block; 40, comment:# Nothing else to do--update device status; 41, block; 42, attribute; 43, argument_list; 44, identifier:dict; 45, argument_list; 46, attribute; 47, argument_list; 48, call; 49, call; 50, call; 51, identifier:_next; 52, expression_statement; 53, expression_statement; 54, expression_statement; 55, call; 56, identifier:_next; 57, expression_statement; 58, expression_statement; 59, expression_statement; 60, expression_statement; 61, expression_statement; 62, expression_statement; 63, expression_statement; 64, expression_statement; 65, string:"Exiting TC poller loop [{}]"; 66, identifier:format; 67, call; 68, call; 69, attribute; 70, identifier:fileno; 71, attribute; 72, argument_list; 73, attribute; 74, argument_list; 75, attribute; 76, argument_list; 77, call; 78, assignment; 79, call; 80, attribute; 81, argument_list; 82, call; 83, assignment; 84, call; 85, assignment; 86, call; 87, call; 88, assignment; 89, call; 90, identifier:hash; 91, argument_list; 92, attribute; 93, argument_list; 94, identifier:self; 95, identifier:_halt_read_file; 96, identifier:log; 97, identifier:debug; 98, call; 99, attribute; 100, identifier:read; 101, attribute; 102, identifier:fileno; 103, attribute; 104, argument_list; 105, identifier:res; 106, call; 107, attribute; 108, argument_list; 109, attribute; 110, identifier:fileno; 111, attribute; 112, argument_list; 113, pattern_list; 114, call; 115, attribute; 116, argument_list; 117, identifier:res; 118, call; 119, identifier:callback; 120, argument_list; 121, attribute; 122, argument_list; 123, identifier:res; 124, call; 125, attribute; 126, argument_list; 127, identifier:self; 128, attribute; 129, identifier:poll; 130, identifier:POLLING_FREQUENCY_MS; 131, attribute; 132, argument_list; 133, identifier:self; 134, identifier:_halt_read_file; 135, identifier:self; 136, identifier:_connection; 137, identifier:log; 138, identifier:debug; 139, call; 140, attribute; 141, argument_list; 142, identifier:self; 143, identifier:_interrupt_callback; 144, identifier:res; 145, identifier:self; 146, identifier:_send_read_file; 147, attribute; 148, identifier:read; 149, integer:1; 150, identifier:command; 151, identifier:callback; 152, attribute; 153, argument_list; 154, identifier:log; 155, identifier:debug; 156, call; 157, attribute; 158, argument_list; 159, identifier:res; 160, identifier:log; 161, identifier:debug; 162, call; 163, attribute; 164, argument_list; 165, identifier:self; 166, identifier:_status_callback; 167, identifier:res; 168, identifier:self; 169, identifier:_poller; 170, string:"Poller [{}]: halt"; 171, identifier:format; 172, call; 173, attribute; 174, argument_list; 175, attribute; 176, identifier:read_until; 177, identifier:SERIAL_ACK; 178, identifier:self; 179, identifier:_send_read_file; 180, attribute; 181, identifier:get; 182, attribute; 183, argument_list; 184, identifier:self; 185, identifier:_send_command; 186, identifier:command; 187, attribute; 188, argument_list; 189, identifier:self; 190, identifier:_send_command; 191, subscript; 192, identifier:hash; 193, argument_list; 194, string:"Poller [{}]: interrupt"; 195, identifier:format; 196, call; 197, identifier:self; 198, identifier:_connection; 199, identifier:self; 200, identifier:_command_queue; 201, string:"Poller [{}]: send {}"; 202, identifier:format; 203, call; 204, identifier:command; 205, string:"Poller [{}]: updating temp"; 206, identifier:format; 207, call; 208, identifier:GCODES; 209, string; 210, identifier:self; 211, identifier:hash; 212, argument_list; 213, identifier:hash; 214, argument_list; 215, identifier:hash; 216, argument_list; 217, string_content:GET_PLATE_TEMP; 218, identifier:self; 219, identifier:self; 220, identifier:self
code: def _serial_poller(self):
          """ Priority-sorted list of checks
  
          Highest priority is the 'halt' channel, which is used to kill the
          thread and the serial communication channel and allow everything to be
          cleaned up.
  
          Second is the lid-open interrupt, which should trigger a callback
          (typically to halt the robot).
  
          Third is an enqueued command to send to the Thermocycler.
  
          Fourth (if no other work is available) is to query the Thermocycler for
          its current temp, target temp, and time remaining in its current cycle.
          """
          while True:
              _next = dict(self._poller.poll(POLLING_FREQUENCY_MS))
              if self._halt_read_file.fileno() in _next:
                  log.debug("Poller [{}]: halt".format(hash(self)))
                  self._halt_read_file.read()
                  # Note: this is discarded because we send a set message to halt
                  # the thread--don't currently need to parse it
                  break
  
              elif self._connection.fileno() in _next:
                  # Lid-open interrupt
                  log.debug("Poller [{}]: interrupt".format(hash(self)))
                  res = self._connection.read_until(SERIAL_ACK)
                  self._interrupt_callback(res)
  
              elif self._send_read_file.fileno() in _next:
                  self._send_read_file.read(1)
                  command, callback = self._command_queue.get()
                  log.debug("Poller [{}]: send {}".format(hash(self), command))
                  res = self._send_command(command)
                  callback(res)
              else:
                  # Nothing else to do--update device status
                  log.debug("Poller [{}]: updating temp".format(hash(self)))
                  res = self._send_command(GCODES['GET_PLATE_TEMP'])
                  self._status_callback(res)
          log.info("Exiting TC poller loop [{}]".format(hash(self)))