Esempio n. 1
0
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker, name="ant.easy")
        self._worker_thread.start()
Esempio n. 2
0
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()
        self._main_thread = None

        self._datas = queue.Queue()

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.easy")
        self._worker_thread.start()

        self.capabilities = self.get_capabilities()

        self.channels = [None] * self.capabilities["max_channels"]
Esempio n. 3
0
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker, name="ant.easy")
        self._worker_thread.start()
Esempio n. 4
0
class Node():
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.easy")
        self._worker_thread.start()

    def new_channel(self, ctype, network_number=0x00, ext_assign=None):
        size = len(self.channels)
        channel = Channel(size, self, self.ant)
        self.channels[size] = channel
        channel._assign(ctype, network_number, ext_assign)
        return channel

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses,
                                 self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses,
                                self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(('burst', channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(('broadcast', channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        while self._running:
            try:
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == 'broadcast':
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == 'burst':
                    self.channels[channel].on_burst_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type,
                                    data)
            except queue.Empty as e:
                pass

    def start(self):
        self._main()

    def stop(self):
        if self._running:
            _logger.debug("Stoping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()
Esempio n. 5
0
class Node:
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()
        self._main_thread = None

        self._datas = queue.Queue()

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.easy")
        self._worker_thread.start()

        self.capabilities = self.get_capabilities()

        self.channels = [None] * self.capabilities["max_channels"]

    def new_channel(self, ctype, network_number=0x00, ext_assign=None):
        for i in range(len(self.channels)):
            if self.channels[i] is None:
                channel = Channel(i, self, self.ant)
                self.channels[i] = channel
                channel._assign(ctype, network_number, ext_assign)
                return channel
        _logger.debug("No free channel available")
        return None

    def remove_channel(self, channel_id):
        for i in range(len(self.channels)):
            try:
                if self.channels[i].id == channel_id:
                    self.channels[i].close()
                    self.channels[i]._unassign()
                    self.channels[i] = None
            except:
                pass

    def get_capabilities(self):
        data = self.request_message(Message.ID.RESPONSE_CAPABILITIES)
        if data[1] == Message.ID.RESPONSE_CAPABILITIES:
            #The Standard Options bit field is encoded as follows:
            #   Bit 0 - CAPABILITIES_NO_RECEIVE_CHANNELS
            #   Bit 1 - CAPABILITIES_NO_TRANSMIT_CHANNELS
            #   Bit 2 - CAPABILITIES_NO_RECEIVE_MESSAGES
            #   Bit 3 - CAPABILITIES_NO_TRANSMIT_MESSAGES
            #   Bit 4 - CAPABILITIES_NO_ACKD_MESSAGES
            #   Bit 5 - CAPABILITIES_NO_BURST_MESSAGES
            #   Other bits are reserved
            #The Advanced Options bit field is encoded as follows:
            #   Bit 1 - CAPABILITIES_NETWORK_ENABLED
            #   Bit 3 - CAPABILITIES_SERIAL_NUMBER_ENABLED
            #   Bit 4 - CAPABILITIES_PER_CHANNEL_TX_POWER_ENABLED
            #   Bit 5 - CAPABILITIES_LOW_PRIORITY_SEARCH_ENABLED
            #   Bit 6 - CAPABILITIES_SCRIPT_ENABLED
            #   Bit 7 - CAPABILITIES_SEARCH_LIST_ENABLED
            #   Other bits are reserved
            #The Advanced Options 2 bit field is encoded as follows:
            #   Bit 0 - CAPABILITIES_LED_ENABLED
            #   Bit 1 - CAPABILITIES_EXT_MESSAGE_ENABLED
            #   Bit 2 - CAPABILITIES_SCAN_MODE_ENABLED
            #   Bit 3 - Reserved
            #   Bit 4 - CAPABILITIES_PROX_SEARCH_ENABLED
            #   Bit 5 - CAPABILITIES_EXT_ASSIGN_ENABLED
            #   Bit 6 - CAPABILITIES_FS_ANTFS_ENABLED
            #   Bit 7 - CAPABILITIES_FIT1_ENABLED
            #   Other bits are reserved
            #The Advanced Options 3 bit field is encoded as follows:
            #   Bit 0 - CAPABILITIES_ADVANCED_BURST_ENABLED
            #   Bit 1 - CAPABILITIES_EVENT_BUFFERING_ENABLED
            #   Bit 2 - CAPABILITIES_EVENT_FILTERING_ENABLED
            #   Bit 3 - CAPABILITIES_HIGH_DUTY_SEARCH_ENABLED
            #   Bit 4 - CAPABILITIES_SEARCH_SHARING_ENABLED
            #   Bit 5 - Reserved.
            #   Bit 6 - CAPABILITIES_SELECTIVE_DATA_UPDATES_ENABLED
            #   Bit 7 - CAPABILITIES_ENCRYPTED_CHANNEL_ENABLED
            #The Advanced Options 4 bit field is encoded as follows:
            #   Bit 0 - CAPABILITIES_RFACTIVE_NOTIFICATION_ENABLED
            #   Other bits are reserved
            result = {
                "max_channels": data[2][0],
                "max_networks": data[2][1],
                "standard_options": data[2][2],
                "advanced_options": data[2][3],
                "advanced_options2": data[2][4],
                "max_sensrcore_channels": data[2][5],
            }
            if len(data[2]) >= 7:
                result["advanced_options3"] = data[2][6]
            if len(data[2]) >= 8:
                result["advanced_options4"] = data[2][7]
            return result
        else:
            _logger.debug(
                "capabilities requested and not received (message id 0x{:02x} , but should be 0x{:02x})"
                .format(data[2][4], Message.ID.RESPONSE_CAPABILITIES))
            return

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses,
                                 self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses,
                                self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(("burst", channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(("broadcast", channel, data))
        elif event == Message.Code.EVENT_TX:
            self._datas.put(("broadcast_tx", channel, data))
        elif event == Message.Code.EVENT_RX_ACKNOWLEDGED:
            self._datas.put(("acknowledge", channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        while self._running:
            try:
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == "broadcast":
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == "burst":
                    self.channels[channel].on_burst_data(data)
                elif data_type == "broadcast_tx":
                    self.channels[channel].on_broadcast_tx_data(data)
                elif data_type == "acknowledge":
                    self.channels[channel].on_acknowledge_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type,
                                    data)
            except queue.Empty as e:
                pass

    def start(self):
        self._main_thread = threading.Thread(target=self._main, name="_main")
        self._main_thread.start()

    def stop(self):
        if self._running:
            _logger.debug("Stoping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()
            self._main_thread.join()
            self._main_thread = None
Esempio n. 6
0
class Node():
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker, name="ant.easy")
        self._worker_thread.start()

    def new_channel(self, ctype):
        size = len(self.channels)
        channel = Channel(size, self, self.ant)
        self.channels[size] = channel
        channel._assign(ctype, 0x00)
        return channel

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses, self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses, self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(('burst', channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(('broadcast', channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        while self._running:
            try:
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == 'broadcast':
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == 'burst':
                    self.channels[channel].on_burst_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type, data)
            except queue.Empty as e:
                pass

    def start(self):
        self._main()

    def stop(self):
        if self._running:
            _logger.debug("Stoping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()
Esempio n. 7
0
class Node:
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker, name="ant.easy")
        self._worker_thread.start()

    def new_channel(self, ctype, network_number=0x00, ext_assign=None):
        size = len(self.channels)
        channel = Channel(size, self, self.ant)
        self.channels[size] = channel
        channel._assign(ctype, network_number, ext_assign)
        return channel

    def delete_channel(self):
        if len(self.channels) > 0:
            c = self.channels.pop(len(self.channels)-1)
            self.ant.unassign_channel(c.id)
            c.get_channel_status()
            del c

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    def set_lib_config(self, value):
      # 0: Disabled
      # 0x20: Enables Rx Timestamp Output
      # 0x40: Enables RSSI Output
      # 0x80: Enables Channel ID Output
      self.ant.set_lib_config(value)
      return self.wait_for_response(Message.ID.LIB_CONFIG)

    def set_tx_power(self, value):
      # 0: low, 1...2, 3: High(0dBm), (4: N/A or High(4dbm))
      self.ant.set_tx_power(value)
      return self.wait_for_response(Message.ID.SET_TRANSMIT_POWER)

    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses, self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses, self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(("burst", channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(("broadcast", channel, data))
        elif event == Message.Code.EVENT_TX:
            self._datas.put(("broadcast_tx", channel, data))
        elif event == Message.Code.EVENT_RX_ACKNOWLEDGED:
            self._datas.put(("acknowledge", channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        while self._running:
            try:
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == "broadcast":
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == "burst":
                    self.channels[channel].on_burst_data(data)
                elif data_type == "broadcast_tx":
                    self.channels[channel].on_broadcast_tx_data(data)
                elif data_type == "acknowledge":
                    self.channels[channel].on_acknowledge_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type, data)
            except queue.Empty as e:
                pass

    def start(self):
        self._main()

    def stop(self):
        if self._running:
            _logger.debug("Stoping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()
Esempio n. 8
0
class Node:
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.easy")
        self._worker_thread.start()

    def new_channel(self, ctype, network_number=0x00, ext_assign=None):
        size = len(self.channels)
        channel = Channel(size, self, self.ant)
        self.channels[size] = channel
        channel._assign(ctype, network_number, ext_assign)
        return channel

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    # TODO: SDS wie gebruikt dit?? enkel channel.wait_for_event wordt blijkbaar gebruikt
    # en heb ik aangepast met extra chnum
    # dus deze call zal niet werken!!!
    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses,
                                 self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses,
                                self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(("burst", channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(("broadcast", channel, data))
        elif event == Message.Code.EVENT_TX:
            self._datas.put(("broadcast_tx", channel, data))
        elif event == Message.Code.EVENT_RX_ACKNOWLEDGED:
            self._datas.put(("acknowledge", channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        _logger.debug("node._main started")

        while self._running:
            # SDS TODO : hier de self._events opkuisen/afhandelen
            # dan heeft die timeout op datas.get() nog zin
            # beter om dit per channel te doen ipv de volledige node?
            # SDS : voorlopige workaround
            # remove RX_FAILED, COLLISION events that keep piling up
            # TODO : nog niet ok, want als het [0] niet wordt gecleaned of gebruikt, stapelen de vuile events zich erachter op
            # hier moet een algo komen die systematisch alle events afhandelt!
            _logger.debug("SDS - cleaning node._events queue %r", self._events)

            # sds de .acquire() is niet echt nodig als je events.remove(message) gebruikt ipv popleft
            # zonder de .acquire() zou de node._events queue kunnen wijzigen tussen message=... en node._events.popleft()
            # om dezelfde reden kan je ook geen for message in node._events loop doen zonder lock acquire
            # deze cleaning vermijdt nog niet dat wait_for_event wordt genotified bij elke inkomend event (ook die van <> channel)
            # want deze cleaning routine krijgt niet noodzakelijk eerst toegang tot de gewijzigde node._events
            # dit is sowieso tijdelijk, maar eigenlijk kan je ook beter een lijst hebben van events die moeten blijven ipv op te sommen wat weg moet
            # ofwel moeten we hier die error events callbacken naar de applicatie
            self._event_cond.acquire()
            cleaning_done = False
            while cleaning_done == False and len(self._events) != 0:
                message = self._events[0]
                if message[1] == 1 and message[2][0] in [
                        Message.Code.EVENT_RX_FAIL,
                        Message.Code.EVENT_CHANNEL_COLLISION,
                        Message.Code.EVENT_QUE_OVERFLOW,
                        Message.Code.EVENT_SERIAL_QUE_OVERFLOW,
                ]:
                    self._events.popleft()
                elif message[1] == 1 and message[2][0] in [
                        Message.Code.EVENT_TRANSFER_TX_FAILED
                ]:
                    _logger.warning(
                        f"event cleanup(temp): TX_FAILED event on channel {message[0]}"
                    )
                    _logger.warning(message)
                    self._events.popleft()
                elif message[1] == 1 and message[2][0] in [
                        Message.Code.EVENT_RX_FAIL_GO_TO_SEARCH
                ]:
                    _logger.warning(
                        f"event cleanup(temp):EVENT_RX_FAIL_GO_TO_SEARCH event on channel {message[0]}"
                    )
                    _logger.warning(message)
                    self._events.popleft()
                else:
                    # no more events to clean from the left of the self._events
                    cleaning_done = True
            self._event_cond.release()

            try:
                # SDS info : timeout 1.0s enkel nodig om deze thread te kunnen terminate door self._running = False te zetten
                # zie ook ant.py
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == "broadcast":
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == "burst":
                    self.channels[channel].on_burst_data(data)
                elif data_type == "broadcast_tx":
                    self.channels[channel].on_broadcast_tx_data(data)
                elif data_type == "acknowledge":
                    self.channels[channel].on_acknowledge_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type,
                                    data)
            except queue.Empty as e:
                pass
        _logger.debug("node._main stopped")

    def start(self):
        self._main()

    def stop(self):
        if self._running:
            _logger.debug("Stopping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()
Esempio n. 9
0
class Node():
    def __init__(self):

        self._responses_cond = threading.Condition()
        self._responses = collections.deque()
        self._event_cond = threading.Condition()
        self._events = collections.deque()

        self._datas = queue.Queue()

        #self.channels = {}

        self.ant = Ant()

        self._running = True

        self._worker_thread = threading.Thread(target=self._worker,
                                               name="ant.easy")
        self._worker_thread.start()

        self.cap = self.get_capabilities()

        self.channels = [None] * self.cap["max_channels"]

    def new_channel(self, ctype, network_number=0x00, ext_assign=None):
        for i in range(len(self.channels)):
            if self.channels[i] is None:
                channel = Channel(i, self, self.ant)
                self.channels[i] = channel
                channel._assign(ctype, network_number, ext_assign)
                return channel
        _logger.debug("No free channel available")
        return None

    def remove_channel(self, channel_id):
        for i in range(len(self.channels)):
            try:
                if self.channels[i].id == channel_id:
                    self.channels[i].close()
                    self.channels[i]._unassign()
                    self.channels[i] = None
            except:
                pass

    def get_capabilities(self):
        data = self.request_message(Message.ID.RESPONSE_CAPABILITIES)
        if data[1] == 84:
            return {
                "max_channels": data[2][0],
                "max_networks": data[2][1],
                "options": data[2][2]
            }
        else:
            _logger.debug(
                "capabilities requested and not received (message id 0x{:02x} , but should be 0x{:02x})"
                .format(data[2][4], Message.ID.RESPONSE_CAPABILITIES))
            return

    def request_message(self, messageId):
        _logger.debug("requesting message %#02x", messageId)
        self.ant.request_message(0, messageId)
        _logger.debug("done requesting message %#02x", messageId)
        return self.wait_for_special(messageId)

    def set_network_key(self, network, key):
        self.ant.set_network_key(network, key)
        return self.wait_for_response(Message.ID.SET_NETWORK_KEY)

    def wait_for_event(self, ok_codes):
        return wait_for_event(ok_codes, self._events, self._event_cond)

    def wait_for_response(self, event_id):
        return wait_for_response(event_id, self._responses,
                                 self._responses_cond)

    def wait_for_special(self, event_id):
        return wait_for_special(event_id, self._responses,
                                self._responses_cond)

    def _worker_response(self, channel, event, data):
        self._responses_cond.acquire()
        self._responses.append((channel, event, data))
        self._responses_cond.notify()
        self._responses_cond.release()

    def _worker_event(self, channel, event, data):
        if event == Message.Code.EVENT_RX_BURST_PACKET:
            self._datas.put(('burst', channel, data))
        elif event == Message.Code.EVENT_RX_BROADCAST:
            self._datas.put(('broadcast', channel, data))
        else:
            self._event_cond.acquire()
            self._events.append((channel, event, data))
            self._event_cond.notify()
            self._event_cond.release()

    def _worker(self):
        self.ant.response_function = self._worker_response
        self.ant.channel_event_function = self._worker_event

        # TODO: check capabilities
        self.ant.start()

    def _main(self):
        while self._running:
            try:
                (data_type, channel, data) = self._datas.get(True, 1.0)
                self._datas.task_done()

                if data_type == 'broadcast':
                    self.channels[channel].on_broadcast_data(data)
                elif data_type == 'burst':
                    self.channels[channel].on_burst_data(data)
                else:
                    _logger.warning("Unknown data type '%s': %r", data_type,
                                    data)
            except queue.Empty as e:
                pass

    def start(self):
        self._main()

    def stop(self):
        if self._running:
            _logger.debug("Stoping ant.easy")
            self._running = False
            self.ant.stop()
            self._worker_thread.join()