예제 #1
0
    def sync_command(self, cmd):
        if self._stop_event.is_set():
            self._logger.warning(
                "Command %s sent after background processor was stopped; failing immediately",
                cmd)

            raise HardwareError(
                "Synchronous command %s failed because background processor was stopped"
                % cmd)

        done_event = threading.Event()
        results = []

        def done_callback(result):
            results.append(result)
            done_event.set()

        self._commands.put((cmd, done_callback, True, None))

        done_event.wait()

        success = results[0]['result']
        retval = results[0]['return_value']
        if not success:
            raise HardwareError("Error executing synchronous command",
                                command=cmd,
                                return_value=retval)

        return retval
예제 #2
0
    def __init__(self, address, raw_data):
        self.base_address = address

        magic1, magic2, magic3, magic4, version, flags, length = struct.unpack_from(
            "<LLLLBBH", raw_data)

        if magic1 != self.CONTROL_MAGIC_1 or magic2 != self.CONTROL_MAGIC_2 or magic3 != self.CONTROL_MAGIC_3 or magic4 != self.CONTROL_MAGIC_4:
            raise HardwareError(
                "Invalid control structure with an incorrect magic number",
                base_address=address)

        self.version = version
        self.flags = flags

        if len(raw_data) < length:
            raise HardwareError(
                "Control structure raw data is too short for encoded length",
                encoded_length=length,
                received_length=len(raw_data))
        elif len(raw_data) > length:
            raw_data = raw_data[:length]

        if version not in self.KNOWN_VERSIONS:
            raise HardwareError(
                "Unknown version embedded in control structure",
                version=version,
                known_versions=self.KNOWN_VERSIONS)

        self._parse_control_structure(raw_data)
예제 #3
0
    def connect(self, uuid_value, wait=None):
        """Connect to a specific device by its uuid

        Attempt to connect to a device that we have previously scanned using its UUID.
        If wait is not None, then it is used in the same was a scan(wait) to override
        default wait times with an explicit value.

        Args:
            uuid_value (int): The unique id of the device that we would like to connect to.
            wait (float): Optional amount of time to force the device adapter to wait before
                attempting to connect.
        """

        if self.connected:
            raise HardwareError("Cannot connect when we are already connected")

        if uuid_value not in self._scanned_devices:
            self.scan(wait=wait)

        with self._scan_lock:
            if uuid_value not in self._scanned_devices:
                raise HardwareError(
                    "Could not find device to connect to by UUID",
                    uuid=uuid_value)

            connstring = self._scanned_devices[uuid_value]['connection_string']

        self.connect_direct(connstring)
예제 #4
0
    def _try_reconnect(self):
        """Try to recover an interrupted connection."""

        try:
            if self.connection_interrupted:
                self._connect_direct(self.connection_string)
                self.connection_interrupted = False
                self.connected = True

                # Reenable streaming interface if that was open before as well
                if self._reports is not None:
                    res = self.adapter.open_interface_sync(0, 'streaming')
                    if not res['success']:
                        raise HardwareError(
                            "Could not open streaming interface to device",
                            reason=res['failure_reason'])

                # Reenable tracing interface if that was open before as well
                if self._traces is not None:
                    res = self.adapter.open_interface_sync(0, 'tracing')
                    if not res['success']:
                        raise HardwareError(
                            "Could not open tracing interface to device",
                            reason=res['failure_reason'])
        except HardwareError as exc:
            raise HardwareError(
                "Device disconnected unexpectedly and we could not reconnect",
                reconnect_error=exc)
예제 #5
0
    async def find_control_structure(self, start_address, search_length):
        """Find the control structure in RAM for this device.

        Returns:
            ControlStructure: The decoded contents of the shared memory control structure
                used for communication with this IOTile device.
        """

        words = await self.read_memory(start_address, search_length, chunk_size=4, join=False)
        found_offset = None

        for i, word in enumerate(words):
            if word == ControlStructure.CONTROL_MAGIC_1:
                if (len(words) - i) < 4:
                    continue

                if words[i + 1] == ControlStructure.CONTROL_MAGIC_2 and words[i + 2] == ControlStructure.CONTROL_MAGIC_3 and words[i + 3] == ControlStructure.CONTROL_MAGIC_4:
                    found_offset = i
                    break

        if found_offset is None:
            raise HardwareError("Could not find control structure magic value in search area")

        struct_info = words[found_offset + 4]
        _version, _flags, length = struct.unpack("<BBH", struct.pack("<L", struct_info))

        if length % 4 != 0:
            raise HardwareError("Invalid control structure length that was not a multiple of 4", length=length)

        word_length = length // 4
        control_data = struct.pack("<%dL" % word_length, *words[found_offset:found_offset + word_length])

        logger.info("Found control stucture at address 0x%08X, word_length=%d", start_address + 4*found_offset, word_length)

        return ControlStructure(start_address + 4*found_offset, control_data)
예제 #6
0
    def connect_direct(self, connection_string, no_rpc=False, force=False):
        """Directly connect to a device using its stream specific connection string.

        Normally, all connections to a device include opening the RPC
        interface to send RPCs.  However, there are certain, very specific,
        circumstances when you would not want to or be able to open the RPC
        interface (such as when you are using the debug interface on a bare
        MCU that has not been programmed yet).  In those cases you can pass
        no_rpc=True to not attempt to open the RPC interface.  If you do not
        open the RPC interface at connection time, there is no public
        interface to open it later, so you must disconnect and reconnect to
        the device in order to open the interface.

        Args:
            connection_string (str): The connection string that identifies the desired device.
            no_rpc (bool): Do not open the RPC interface on the device (default=False).
            force (bool): Whether to force another connection even if we think we are currently
                connected.  This is for internal use and not designed to be set externally.
        """

        if not force and self.connected:
            raise HardwareError(
                "Cannot connect when we are already connected to '%s'" %
                self.connection_string)

        self._loop.run_coroutine(self.adapter.connect(0, connection_string))

        try:
            if no_rpc:
                self._logger.info("Not opening RPC interface on device %s",
                                  self.connection_string)
            else:
                self._loop.run_coroutine(self.adapter.open_interface(0, 'rpc'))
        except HardwareError as exc:
            self._logger.exception("Error opening RPC interface on device %s",
                                   connection_string)
            self._loop.run_coroutine(self.adapter.disconnect(0))
            raise exc
        except Exception as exc:
            self._logger.exception("Error opening RPC interface on device %s",
                                   connection_string)
            self._loop.run_coroutine(self.adapter.disconnect(0))
            raise HardwareError(
                "Could not open RPC interface on device due to an exception: %s"
                % str(exc)) from exc

        self.connected = True
        self.connection_string = connection_string
        self.connection_interrupted = False
예제 #7
0
    def set_report_size(self, size=0xFFFFFFFF):
        """ Sets and verifies the report size for a pod
        Args:
            size (int): The maximum size of a report
        """

        error, = self._con.rpc(0x0A, 0x05, size, 0, arg_format="LB", result_format="L")

        if error:
            raise HardwareError("Error setting report size.", error_code=error, size=size)

        maxpacket, _comp1, comp2, = self._con.rpc(0x0A, 0x06, result_format="LBB")

        if maxpacket != size:
            raise HardwareError("Max Packet Size was not set as expected")
예제 #8
0
    def _enable_tracing(self):
        self._traces = queue.Queue()
        res = self.adapter.open_interface_sync(0, 'tracing')
        if not res['success']:
            raise HardwareError("Could not open tracing interface to device", reason=res['failure_reason'])

        return self._traces
예제 #9
0
    def enable_tracing(self):
        """Open the tracing interface and accumulate traces in a queue.

        This method is safe to call multiple times in a single device
        connection. There is no way to check if the tracing interface is
        opened or to close it once it is opened (apart from disconnecting from
        the device).

        The first time this method is called, it will open the tracing
        interface and return a queue that will be filled asynchronously with
        reports as they are received.  Subsequent calls will just empty the
        queue and return the same queue without interacting with the device at
        all.

        Returns:
            queue.Queue: A queue that will be filled with trace data from the device.

            The trace data will be in disjoint bytes objects in the queue
        """

        if not self.connected:
            raise HardwareError(
                "Cannot enable tracing if we are not in a connected state")

        if self._traces is not None:
            _clear_queue(self._traces)
            return self._traces

        self._traces = queue.Queue()
        self._loop.run_coroutine(self.adapter.open_interface(0, 'tracing'))

        return self._traces
예제 #10
0
    def send_highspeed(self, data, progress_callback):
        """Send a script to a device at highspeed, reporting progress.

        This method takes a binary blob and downloads it to the device as fast
        as possible, calling the passed progress_callback periodically with
        updates on how far it has gotten.

        Args:
            data (bytes): The binary blob that should be sent to the device at highspeed.
            progress_callback (callable): A function that will be called periodically to
                report progress.  The signature must be callback(done_count, total_count)
                where done_count and total_count will be passed as integers.
        """

        if not self.connected:
            raise HardwareError(
                "Cannot send a script if we are not in a connected state")

        if isinstance(data, str) and not isinstance(data, bytes):
            raise ArgumentError(
                "You must send bytes or bytearray to _send_highspeed",
                type=type(data))

        if not isinstance(data, bytes):
            data = bytes(data)

        try:
            self._on_progress = progress_callback
            self._loop.run_coroutine(self.adapter.send_script(0, data))
        finally:
            self._on_progress = None
예제 #11
0
    async def _try_connect(self, connection_string):
        """If the connecton string settings are different, try and connect to an attached device"""
        if self._parse_conn_string(connection_string):
            if self.connected is True:
                info = {"reason": "Reconnection", "expected": True}
                self.notify_event(connection_string, 'disconnection', info)
                self.connected = False
                await self.stop()

            if self._mux_func is not None:
                self._mux_func(self._channel)

            if self._device_info is None:
                raise ArgumentError(
                    "Missing device name or alias, specify using device=name in port string "
                    "or -c device=name in connect_direct or debug command",
                    known_devices=[x for x in DEVICE_ALIASES.keys()])

            try:
                await self._jlink_async.connect_jlink(
                    self._jlink_serial, self._device_info.jlink_name)
                self.connected = True
            except pylink.errors.JLinkException as exc:
                if exc.code == exc.VCC_FAILURE:
                    raise HardwareError(
                        "No target power detected",
                        code=exc.code,
                        suggestion="Check jlink connection and power wiring")

                raise
            except:
                raise
예제 #12
0
    def enable_debug(self):
        """Open the debug interface on the connected device."""

        if not self.connected:
            raise HardwareError("Cannot enable debug if we are not in a connected state")

        self._loop.run_coroutine(self.adapter.open_interface(0, 'debug'))
예제 #13
0
    def _send_rpc(self, device_info, control_info, address, rpc_id, payload, poll_interval, timeout):
        """Write and trigger an RPC."""

        write_address, write_data = control_info.format_rpc(address, rpc_id, payload)
        self._jlink.memory_write32(write_address, write_data)

        self._trigger_rpc(device_info)

        start = monotonic()
        now = start

        poll_address, poll_mask = control_info.poll_info()

        while (now - start) < timeout:
            time.sleep(poll_interval)
            value, = self._jlink.memory_read8(poll_address, 1)

            if value & poll_mask:
                break

            now = monotonic()

        if (now - start) >= timeout:
            raise HardwareError("Timeout waiting for RPC response", timeout=timeout, poll_interval=poll_interval)

        read_address, read_length = control_info.response_info()
        read_data = self._read_memory(read_address, read_length, join=True)

        return control_info.format_response(read_data)
예제 #14
0
    def disconnect_async(self, connection_id, callback):
        """Asynchronously disconnect from a device that has previously been connected

        Args:
            connection_id (int): a unique identifier for this connection on the DeviceManager
                that owns this adapter.
            callback (callable): A function called as callback(connection_id, adapter_id, success, failure_reason)
            when the disconnection finishes.  Disconnection can only either succeed or timeout.
        """

        try:
            context = self.connections.get_context(connection_id)
        except ArgumentError:
            callback(connection_id, self.id, False,
                     "Could not find connection information")
            return

        connection_string = context['connection_string']

        self.connections.begin_disconnection(
            connection_id, callback, self.get_config('default_timeout'))

        try:
            self.send_command_async(operations.DISCONNECT,
                                    connection_string=connection_string)
        except Exception as err:
            failure_reason = "Error while sending 'disconnect' command to ws server: {}".format(
                err)
            self.connections.finish_disconnection(connection_id, False,
                                                  failure_reason)
            raise HardwareError(failure_reason)
예제 #15
0
    def get(self, address, basic=False):
        """Create a proxy object for a tile by address.

        The correct proxy object is determined by asking the tile for its
        status information and looking up the appropriate proxy in our list of
        installed proxy objects.  If you want to send raw RPCs, you can get a
        basic TileBusProxyObject by passing basic=True.
        """

        tile = self._create_proxy('TileBusProxyObject', address)

        if basic:
            return tile

        name = tile.tile_name()
        version = tile.tile_version()

        # Now create the appropriate proxy object based on the name and version of the tile
        tile_type = self.get_proxy(name)
        if tile_type is None:
            raise HardwareError("Could not find proxy object for tile",
                                name="'{}'".format(name),
                                known_names=self._name_map.keys())

        tile = tile_type(self.stream, address)
        tile._hwmanager = self

        return tile
예제 #16
0
    async def _poll_queue_status(self, control_info):
        """ Read next frame from queue

            Returns:
                bool: true if queue is empty
        """

        read_address, write_address, queue_size_address = control_info.queue_info()

        if read_address != (write_address - 1) and write_address != (queue_size_address - 1):
            raise HardwareError("Read/Write/Queue Size addresses are not algined.")

        queue_info = await self.read_memory(read_address, 4, chunk_size=1)
        read_index = queue_info[0]
        write_index = queue_info[1]
        queue_size = queue_info[2] + (queue_info[3] << 8)

        if read_index == write_index:
            return True

        try:
            await self._read_queue_frames(control_info, read_index, write_index, queue_size)
        except (HardwareError, pylink.errors.JLinkException):
            logger.debug("Queue poll exception.", exc_info=True)
        except:
            logger.exception("Unexpected queue poll exception!")

        if queue_size != 0:
            read_index = write_index
        await self.write_memory(read_address, [read_index], chunk_size=1)

        return read_index == write_index
예제 #17
0
    def _send_rpc(self, address, feature, cmd, payload, **kwargs):
        timeout = 3.0
        if 'timeout' in kwargs:
            timeout = float(kwargs['timeout'])

        # If our connection was interrupted before this RPC, try to recover it
        if self.connection_interrupted:
            self._try_reconnect()

        result = self.adapter.send_rpc_sync(0, address, (feature << 8) | cmd,
                                            payload, timeout)
        success = result['success']
        status = result['status']
        payload = result['payload']

        # Sometimes RPCs can cause the device to go offline, so try to reconnect to it.
        # For example, the RPC could cause the device to reset itself.
        if self.connection_interrupted:
            self._try_reconnect()

        if not success:
            raise HardwareError("Could not send RPC",
                                reason=result['failure_reason'])

        return status, payload
예제 #18
0
파일: jlink.py 프로젝트: palagap/coretools
    def _try_connect(self, connection_string):
        """If the connecton string settings are different, try and connect to an attached device"""
        if self._parse_conn_string(connection_string):
            self._trigger_callback('on_disconnect', self.id, self._connection_id)

            self.stop_sync()

            if self._mux_func is not None:
                self._mux_func(self._channel)

            if self._device_info is None:
                raise ArgumentError("Missing device name or alias, specify using device=name in port string or -c device=name in connect_direct or debug command", known_devices=[x for x in viewkeys(DEVICE_ALIASES)])

            try:
                self.jlink = pylink.JLink()
                self.jlink.open(serial_no=self._jlink_serial)
                self.jlink.set_tif(pylink.enums.JLinkInterfaces.SWD)
                self.jlink.connect(self._device_info.jlink_name)
                self.jlink.set_little_endian()
            except pylink.errors.JLinkException as exc:
                if exc.code == exc.VCC_FAILURE:
                    raise HardwareError("No target power detected", code=exc.code, suggestion="Check jlink connection and power wiring")

                raise
            except:
                raise

            self._control_thread = JLinkControlThread(self.jlink)
            self._control_thread.start()

            self.set_config('probe_required', True)
            self.set_config('probe_supported', True)
예제 #19
0
    def connect_async(self, connection_id, connection_string, callback):
        """Asynchronously connect to a device by its connection_string

        Args:
            connection_id (int): A unique integer set by the caller for referring to this connection
                once created
            connection_string (string): A DeviceAdapter specific string that can be used to connect to
                a device using this DeviceAdapter.
            callback (callable): A callback function called when the connection has succeeded or
                failed
        """

        context = {'connection_string': connection_string}
        self.connections.begin_connection(connection_id, connection_string,
                                          callback, context,
                                          self.get_config('default_timeout'))

        try:
            self.send_command_async(operations.CONNECT,
                                    connection_string=connection_string)
        except Exception as err:
            failure_reason = "Error while sending 'connect' command to ws server: {}".format(
                err)
            self.connections.finish_connection(connection_id, False,
                                               failure_reason)
            raise HardwareError(failure_reason)
예제 #20
0
    def _close_interface(self, connection_id, interface, callback):
        """Asynchronously close an interface on the device

        Args:
            connection_id (int): the unique identifier for the connection
            interface (string): the interface name to open
            callback (callback): Callback to be called when this command finishes
                callback(connection_id, adapter_id, success, failure_reason)
        """

        try:
            context = self.connections.get_context(connection_id)
        except ArgumentError:
            callback(connection_id, self.id, False,
                     "Could not find connection information")
            return

        connection_string = context['connection_string']

        self.connections.begin_operation(connection_id, 'close_interface',
                                         callback,
                                         self.get_config('default_timeout'))

        try:
            self.send_command_async(operations.CLOSE_INTERFACE,
                                    connection_string=connection_string,
                                    interface=interface)
        except Exception as err:
            failure_reason = "Error while sending 'close_interface' command to ws server: {}".format(
                err)
            self.connections.finish_operation(connection_id, False,
                                              failure_reason)
            raise HardwareError(failure_reason)
예제 #21
0
    def _connect_direct(self, connection_string):
        res = self.adapter.connect_sync(0, connection_string)
        if not res['success']:
            self.adapter.periodic_callback()
            raise HardwareError("Could not connect to device", reason=res['failure_reason'], connection_string=connection_string)

        try:
            res = self.adapter.open_interface_sync(0, 'rpc')
        except Exception as exc:
            self.adapter.disconnect_sync(0)
            self.adapter.periodic_callback()
            raise HardwareError("Could not open RPC interface on device due to an exception", exception=str(exc))

        if not res['success']:
            self.adapter.disconnect_sync(0)
            self.adapter.periodic_callback()
            raise HardwareError("Could not open RPC interface on device", reason=res['failure_reason'], connection_string=connection_string)
예제 #22
0
    def _trigger_rpc(self, device_info):
        """Trigger an RPC in a device specific way."""

        method = device_info.rpc_trigger
        if isinstance(method, devices.RPCTriggerViaSWI):
            self._jlink.memory_write32(method.register, [1 << method.bit])
        else:
            raise HardwareError("Unknown RPC trigger method", method=method)
예제 #23
0
    def _trigger_streamer(self, index):
        error, = self._con.rpc(0x20, 0x10, index, result_format="L")

        # This error code means that the streamer did not have any new data
        if error == 0x8003801f:
            self.logger.info("We manually triggered streamer %d but it reported that there were no new readings", index)
        elif error != 0:
            raise HardwareError("Error triggering streamer", code=error, index=index)
예제 #24
0
    def send_rpc(self, address, rpc_id, call_payload, timeout=3.0):
        """Send an rpc to our connected device.

        The device must already be connected and the rpc interface open.  This
        method will synchronously send an RPC and wait for the response.  Any
        RPC errors will be raised as exceptions and if there were no errors, the
        RPC's response payload will be returned as a binary bytearray.

        See :meth:`AbstractDeviceAdapter.send_rpc` for documentation of the possible
        exceptions that can be raised here.

        Args:
            address (int): The tile address containing the RPC
            rpc_id (int): The ID of the RPC that we wish to call.
            call_payload (bytes): The payload containing encoded arguments for the
                RPC.
            timeout (float): The maximum number of seconds to wait for the RPC to
                finish.  Defaults to 3s.

        Returns:
            bytearray: The RPC's response payload.
        """

        if not self.connected:
            raise HardwareError(
                "Cannot send an RPC if we are not in a connected state")

        if timeout is None:
            timeout = 3.0

        status = -1
        payload = b''
        recording = None

        if self.connection_interrupted:
            self._try_reconnect()

        if self._record is not None:
            recording = _RecordedRPC(self.connection_string, address, rpc_id,
                                     call_payload)
            recording.start()

        try:
            payload = self._loop.run_coroutine(
                self.adapter.send_rpc(0, address, rpc_id, call_payload,
                                      timeout))
            status, payload = pack_rpc_response(payload, None)
        except VALID_RPC_EXCEPTIONS as exc:
            status, payload = pack_rpc_response(payload, exc)

        if self._record is not None:
            recording.finish(status, payload)
            self._recording.append(recording)

        if self.connection_interrupted:
            self._try_reconnect()

        return unpack_rpc_response(status, payload, rpc_id, address)
예제 #25
0
    async def _trigger_rpc(self, device_info):
        """Trigger an RPC in a device specific way."""
        self.rpc_response_event.clear()

        method = device_info.rpc_trigger
        if isinstance(method, devices.RPCTriggerViaSWI):
            await self.write_memory(method.register, [1 << method.bit], chunk_size=4)
        else:
            raise HardwareError("Unknown RPC trigger method", method=method)
예제 #26
0
    def enable_tracing(self):
        if not self.connected:
            raise HardwareError(
                "Cannot enable tracing if we are not in a connected state")

        if not hasattr(self, '_enable_tracing'):
            raise StreamOperationNotSupportedError(command="enable_tracing")

        return self._enable_tracing()
예제 #27
0
    def _debug_command(self, cmd, args, progress_callback=None):
        def _progress_callback(_finished, _total):
            pass

        res = self.adapter.debug_sync(0, cmd, args, progress_callback)
        if not res['success']:
            raise HardwareError("Could not execute debug command %s on device" % cmd, reason=res['failure_reason'])

        return res.get('return_value')
예제 #28
0
    def send_script_async(self, connection_id, data, progress_callback,
                          callback):
        """Asynchronously send a a script to this IOTile device

        Args:
            connection_id (int): A unique identifier that will refer to this connection
            data (string): the script to send to the device
            progress_callback (callable): A function to be called with status on our progress, called as:
                progress_callback(done_count, total_count)
            callback (callable): A callback for when we have finished sending the script. The callback will be called as
                callback(connection_id, adapter_id, success, failure_reason)
                'connection_id': the connection id
                'adapter_id': this adapter's id
                'success': a bool indicating whether we received a response to our attempted RPC
                'failure_reason': a string with the reason for the failure if success == False
        """

        try:
            context = self.connections.get_context(connection_id)
        except ArgumentError:
            callback(connection_id, self.id, False,
                     "Could not find connection information")
            return

        connection_string = context['connection_string']
        context['progress_callback'] = progress_callback

        self.connections.begin_operation(connection_id, 'script', callback,
                                         self.get_config('default_timeout'))

        mtu = int(self.get_config(
            'mtu', 60 * 1024))  # Split script payloads larger than this

        # Count number of chunks to send
        nb_chunks = 1
        if len(data) > mtu:
            nb_chunks = len(data) // mtu
            if len(data) % mtu != 0:
                nb_chunks += 1

        # Send the script out possibly in multiple chunks if it's larger than our maximum transmit unit
        for i in range(0, nb_chunks):
            start = i * mtu
            chunk = data[start:start + mtu]

            try:
                self.send_command_async(operations.SEND_SCRIPT,
                                        connection_string=connection_string,
                                        script=base64.b64encode(chunk),
                                        fragment_count=nb_chunks,
                                        fragment_index=i)
            except Exception as err:
                failure_reason = "Error while sending 'send_script' command to ws server: {}".format(
                    err)
                self.connections.finish_operation(connection_id, False,
                                                  failure_reason)
                raise HardwareError(failure_reason)
예제 #29
0
    def send_highspeed(self, data, progress_callback=None):
        if not self.connected:
            raise HardwareError(
                "Cannot send highspeed data if we are not in a connected state"
            )

        if not hasattr(self, '_send_highspeed'):
            raise StreamOperationNotSupportedError(command="send_highspeed")

        return self._send_highspeed(data, progress_callback)
예제 #30
0
        def _done_callback(result):
            retval = result['return_value']

            if not result['result']:
                future.set_exception(
                    HardwareError("Error executing synchronous command",
                                  command=cmd,
                                  return_value=retval))
            else:
                future.set_result(retval)