Example #1
0
def main(args=None):
    parser = argparse.ArgumentParser(
        description="Print a summary of basic SpiNNaker machine "
                    "and BMP information")
    parser.add_argument("--version", "-V", action="version",
                        version="%(prog)s {}".format(rig.__version__))

    parser.add_argument("hostname", type=str,
                        help="hostname or IP of SpiNNaker system or BMP")

    args = parser.parse_args(args)

    # Determine what type of machine this is and print information accordingly
    try:
        mc = MachineController(args.hostname)
        info = mc.get_software_version(255, 255)
        if "SpiNNaker" in info.version_string:
            for line in get_spinnaker_info(mc):
                print(line)
        elif "BMP" in info.version_string:
            bc = BMPController(args.hostname)
            for line in get_bmp_info(bc):
                print(line)
        else:
            sys.stderr.write("{}: error: unknown architecture '{}'\n".format(
                parser.prog, info.version_string))
            return 2
    except TimeoutError:
        sys.stderr.write("{}: error: command timed out\n".format(
            parser.prog))
        return 1

    return 0
    def __init__(self, hostname, on_thread_start=None):
        """Start a new asynchronous BMP Controller

        Parameters
        ----------
        hostname : str
            The hostname/IP of the BMP to connect to.
        on_thread_start : function() or None
            *Optional.* A function to be called by the controller's background
            thread before it starts. This can be used to ensure propper
            sequencing/handing-over between two AsyncBMPControllers connected
            to the same machine.
        """
        self._on_thread_start = on_thread_start

        self._bc = BMPController(hostname)

        self._stop = False

        # A lock which must be held when modifying the state of this object
        self._lock = threading.RLock()

        # An event fired whenever some new interaction with the BMP is
        # required.
        self._requests_pending = threading.Event()

        # A queue of power change states
        self._power_requests = deque()

        # A queue of link-enabled state changes
        self._link_requests = deque()

        self._thread = threading.Thread(
            target=self._run,
            name="<BMP control thread for {}>".format(hostname))
        self._thread.start()
class AsyncBMPController(object):
    """An object which provides an asynchronous interface to a power and link
    control commands of a SpiNNaker BMP.

    Since BMP commands, particularly power-on commands, take some time to
    complete, it is desirable for them to be executed asynchronously. This
    object uses a Rig :py:class:`~rig.machine_control.BMPController` object to
    communicate with a BMP controlling a single frame of boards.

    Power and link configuration commands are queued and executed in a
    background thread. When a command completes, a user-supplied callback is
    called.

    Sequential power commands of the same type (on/off) are coalesced into a
    single power on command. When a power command is sent, all previous link
    configuration commands queued for that board are skipped. Additionally, all
    power commands are completed before link configuration commands are carried
    out.
    """

    def __init__(self, hostname, on_thread_start=None):
        """Start a new asynchronous BMP Controller

        Parameters
        ----------
        hostname : str
            The hostname/IP of the BMP to connect to.
        on_thread_start : function() or None
            *Optional.* A function to be called by the controller's background
            thread before it starts. This can be used to ensure propper
            sequencing/handing-over between two AsyncBMPControllers connected
            to the same machine.
        """
        self._on_thread_start = on_thread_start

        self._bc = BMPController(hostname)

        self._stop = False

        # A lock which must be held when modifying the state of this object
        self._lock = threading.RLock()

        # An event fired whenever some new interaction with the BMP is
        # required.
        self._requests_pending = threading.Event()

        # A queue of power change states
        self._power_requests = deque()

        # A queue of link-enabled state changes
        self._link_requests = deque()

        self._thread = threading.Thread(
            target=self._run,
            name="<BMP control thread for {}>".format(hostname))
        self._thread.start()

    def __enter__(self):
        """When used as a context manager, make requests 'atomic'."""
        self._lock.acquire()

    def __exit__(self, type=None, value=None, traceback=None):
        self._lock.release()

    def set_power(self, board, state, on_done):
        """Set the power state of a single board.

        Parameters
        ----------
        board : int
            The board to control.
        state : bool
            True = on, False = off.
        on_done : function(success)
            Function to call when the command completes. May be called from
            another thread. Success is a bool which is True if the command
            completed successfully and False if it did not (or was cancelled).
        """
        with self._lock:
            assert not self._stop

            # Enqueue the request
            self._power_requests.append(_PowerRequest(state, board, on_done))
            self._requests_pending.set()

            # Cancel any existing link enable commands for this board
            cancelled = []
            for request in list(self._link_requests):
                if request.board == board:
                    self._link_requests.remove(request)
                    cancelled.append(request)

        for request in cancelled:
            request.on_done(False)

    def set_link_enable(self, board, link, enable, on_done):
        """Enable or disable a link.

        Parameters
        ----------
        board : int
            The board on which the link resides.
        link : :py:class:`rig.links.Links`
            The link to configure.
        enable : bool
            True = link enabled, False = link disabled.
        on_done : function(success)
            Function to call when the command completes. May be called from
            another thread. Success is a bool which is True if the command
            completed successfully and False if it did not (or was cancelled).
        """
        with self._lock:
            assert not self._stop

            # Enqueue the request
            self._link_requests.append(
                _LinkRequest(board, link, enable, on_done))
            self._requests_pending.set()

    def stop(self):
        """Stop the background thread, as soon as possible after completing all
        queued actions.
        """
        with self._lock:
            self._stop = True
            self._requests_pending.set()

    def join(self):
        """Wait for the thread to actually stop."""
        self._thread.join()

    def _run(self):
        """The background thread for interacting with the BMP.
        """
        try:
            if self._on_thread_start is not None:
                self._on_thread_start()

            while True:
                self._requests_pending.wait()

                # Priority 0: Power commands
                power_request = self._get_atomic_power_request()
                if power_request:
                    # Send the power command
                    try:
                        self._bc.set_power(state=power_request.state,
                                           board=power_request.board)
                        success = True
                    except IOError:
                        # Communication issue with the machine, log it but not
                        # much we can do for the end-user.
                        logging.exception("Failed to set board power.")
                        success = False

                    # Alert all waiting threads
                    for on_done in power_request.on_done:
                        on_done(success)

                    continue

                # Priority 1: Link enable/disable commands
                link_request = self._get_atomic_link_request()
                if link_request:
                    # Set the link state, as required
                    try:
                        fpga, addr \
                            = FPGA_LINK_STOP_REGISTERS[link_request.link]
                        self._bc.write_fpga_reg(fpga, addr,
                                                not link_request.enable,
                                                board=link_request.board)
                        success = True
                    except IOError:
                        # Communication issue with the machine, log it but not
                        # much we can do for the end-user.
                        logging.exception("Failed to set link state.")
                        success = False

                    # Alert waiting thread
                    link_request.on_done(success)

                    continue

                # If nothing left in the queues, clear the request flag and
                # break out of queue-processing loop.
                with self._lock:
                    if (not self._power_requests and  # pragma: no branch
                            not self._link_requests):
                        self._requests_pending.clear()

                        # If we've been told to stop, actually stop the thread
                        # now
                        if self._stop:  # pragma: no branch
                            return
        except:  # pragma: no cover
            # If the thread crashes something has gone wrong with this program
            # (not the machine), setting _stop will cause set_power and
            # set_link_enable to fail, hopefully propogating news of this
            # crash..
            with self._lock:
                self._stop = True
            raise

    def _get_atomic_power_request(self):
        """If any power requests are outstanding, return a (boards, state)
        tuple which combines as many of the requests at the head of the queue
        as possible.

        Returns
        -------
        :py:class:`._PowerRequest` or None
        """
        with self._lock:
            # Special case: no requests
            if not self._power_requests:
                return None

            # Otherwise, accumulate as many boards as possible
            state = self._power_requests[0].state
            boards = set()
            on_done = []
            while (self._power_requests and
                   self._power_requests[0].state == state):
                request = self._power_requests.popleft()
                boards.add(request.board)
                on_done.append(request.on_done)
            return _PowerRequest(state, boards, on_done)

    def _get_atomic_link_request(self):
        """Pop the next link state change request, if one exists.

        Returns
        -------
        :py:class:`._LinkRequest` or None
        """
        with self._lock:
            if not self._link_requests:
                return None
            else:
                return self._link_requests.popleft()
Example #4
0
def main(args=None):
    parser = argparse.ArgumentParser(
        description=
        "Interactively guide the user through the process of wiring up a "
        "SpiNNaker machine.")
    arguments.add_version_args(parser)

    parser.add_argument("--no-tts",
                        action="store_true",
                        default=False,
                        help="disable text-to-speech announcements of wiring "
                        "steps")

    parser.add_argument("--no-auto-advance",
                        action="store_true",
                        default=False,
                        help="disable auto-advancing through wiring steps")

    parser.add_argument("--fix",
                        action="store_true",
                        default=False,
                        help="detect errors in existing wiring and just show "
                        "corrective steps")

    parser.add_argument(
        "--log",
        type=str,
        metavar="LOGFILE",
        help="record the times at which each cable is installed")

    arguments.add_topology_args(parser)
    arguments.add_cabinet_args(parser)
    arguments.add_wire_length_args(parser)
    arguments.add_bmp_args(parser)
    arguments.add_proxy_args(parser)
    arguments.add_subset_args(parser)

    # Process command-line arguments
    args = parser.parse_args(args)
    (w, h), transformation, uncrinkle_direction, folds =\
     arguments.get_topology_from_args(parser, args)

    cabinet, num_frames = arguments.get_cabinets_from_args(parser, args)

    wire_lengths, min_slack = arguments.get_wire_lengths_from_args(
        parser, args, mandatory=True)

    bmp_ips = arguments.get_bmps_from_args(parser, args, cabinet.num_cabinets,
                                           num_frames)

    proxy_host_port = arguments.get_proxy_from_args(parser, args)

    wire_filter = arguments.get_subset_from_args(parser, args)

    if cabinet.num_cabinets == num_frames == 1:
        num_boards = 3 * w * h
    else:
        num_boards = cabinet.boards_per_frame

    # Generate folded system
    hex_boards, folded_boards = folded_torus(w, h, transformation,
                                             uncrinkle_direction, folds)

    # Divide into cabinets
    cabinetised_boards = transforms.cabinetise(folded_boards,
                                               cabinet.num_cabinets,
                                               num_frames,
                                               cabinet.boards_per_frame)
    cabinetised_boards = transforms.remove_gaps(cabinetised_boards)
    physical_boards = transforms.cabinet_to_physical(cabinetised_boards,
                                                     cabinet)

    # Focus on only the boards which are part of the system
    if cabinet.num_cabinets > 1:
        focus = [slice(0, cabinet.num_cabinets)]
    elif num_frames > 1:
        focus = [0, slice(0, num_frames)]
    else:
        focus = [0, 0, slice(0, w * h * 3)]

    # Generate wiring plan
    wires_between_boards, wires_between_frames, wires_between_cabinets =\
     generate_wiring_plan(cabinetised_boards, physical_boards,
                          cabinet.board_wire_offset, wire_lengths, min_slack)
    flat_wiring_plan = flatten_wiring_plan(wires_between_boards,
                                           wires_between_frames,
                                           wires_between_cabinets,
                                           cabinet.board_wire_offset)

    # Create a BMP connection/wiring probe or connect to a proxy
    if proxy_host_port is None:
        if len(bmp_ips) == 0:
            if args.fix:
                parser.error(
                    "--fix requires that all BMPs be listed with --bmp")
            bmp_controller = None
            wiring_probe = None
        else:
            bmp_controller = BMPController(bmp_ips)

        # Create a wiring probe
        if bmp_controller is not None and (not args.no_auto_advance
                                           or args.fix):
            wiring_probe = WiringProbe(bmp_controller, cabinet.num_cabinets,
                                       num_frames, num_boards)
    else:
        # Fix is not supported since the proxy client does not recreate the
        # discover_wires method of WiringProbe.
        if args.fix:
            parser.error("--fix cannot be used with --proxy")

        # The proxy object provides a get_link_target and set_led method compatible
        # with those provided by bmp_controller and wiring_probe. Since these are
        # the only methods used, we use the proxy client object in place of
        # bmp_controller and wiring_probe.
        bmp_controller = wiring_probe = ProxyClient(*proxy_host_port)

    # Create a TimingLogger if required
    if args.log:
        if os.path.isfile(args.log):
            logfile = open(args.log, "a")
            add_header = False
        else:
            logfile = open(args.log, "w")
            add_header = True
        timing_logger = TimingLogger(logfile, add_header)
    else:
        logfile = None
        timing_logger = None

    # Convert wiring plan into cabinet coordinates
    b2c = dict(cabinetised_boards)
    wires = []
    for ((src_board, src_direction), (dst_board, dst_direction), wire_length) \
        in flat_wiring_plan:

        sc, sf, sb = b2c[src_board]
        dc, df, db = b2c[dst_board]
        wires.append(((sc, sf, sb, src_direction), (dc, df, db, dst_direction),
                      wire_length))

    # Filter wires according to user-specified rules
    wires = list(filter(wire_filter, wires))
    if len(wires) == 0:
        parser.error("--subset selects no wires")

    if not args.fix:
        # If running normally, just run through the full set of wires
        wiring_plan = wires
    else:
        # If running in fix mode, generate a list of fixes to make
        correct_wires = set((src, dst) for src, dst, length in wires)
        actual_wires = set(wiring_probe.discover_wires())

        to_remove = actual_wires - correct_wires
        to_add = correct_wires - actual_wires

        # Remove all bad wires first, then re-add good ones (note ordering now is
        # just reset to cabinets right-to-left, frames top-to-bottom and boards
        # left-to-right).
        wiring_plan = [(src, dst, None) for src, dst in sorted(to_remove)]
        for src, dst, length in wires:
            if (src, dst) in to_add:
                wiring_plan.append((src, dst, length))

        if len(wiring_plan) == 0:
            print("No corrections required.")
            return 0

    # Intialise the GUI and launch the mainloop
    ui = InteractiveWiringGuide(cabinet=cabinet,
                                wire_lengths=wire_lengths,
                                wires=wiring_plan,
                                bmp_controller=bmp_controller,
                                use_tts=not args.no_tts,
                                focus=focus,
                                wiring_probe=wiring_probe,
                                auto_advance=not args.no_auto_advance,
                                timing_logger=timing_logger)
    ui.mainloop()

    if logfile is not None:
        logfile.close()

    return 0
def main(args=None):
	parser = argparse.ArgumentParser(
		description="Validate the wiring of a SpiNNaker system.")
	arguments.add_version_args(parser)
	
	parser.add_argument("--verbose", "-v", action="store_true", default=False,
	                    help="list all incorrect and missing wires")
	
	arguments.add_topology_args(parser)
	arguments.add_cabinet_args(parser)
	arguments.add_bmp_args(parser)
	
	# Process command-line arguments
	args = parser.parse_args(args)
	(w, h), transformation, uncrinkle_direction, folds =\
		arguments.get_topology_from_args(parser, args)
	
	cabinet, num_frames = arguments.get_cabinets_from_args(parser, args)
	
	bmp_ips = arguments.get_bmps_from_args(parser, args,
	                                       cabinet.num_cabinets,
	                                       num_frames)
	
	if len(bmp_ips) == 0:
		parser.error("BMP host names must be provided for every frame.")
	
	# Generate folded system
	hex_boards, folded_boards = folded_torus(w, h,
	                                         transformation,
	                                         uncrinkle_direction,
	                                         folds)
	
	# Divide into cabinets
	cabinetised_boards = transforms.cabinetise(folded_boards,
	                                           cabinet.num_cabinets,
	                                           num_frames,
	                                           cabinet.boards_per_frame)
	cabinetised_boards = transforms.remove_gaps(cabinetised_boards)
	
	# Generate list of wires
	wires = plan.enumerate_wires(cabinetised_boards)
	
	# Set up the wiring probe
	bmp_controller = BMPController(bmp_ips)
	if cabinet.num_cabinets == 1 and num_frames == 1:
		num_boards = 3 * w * h
	else:
		num_boards = cabinet.boards_per_frame
	wiring_probe = probe.WiringProbe(bmp_controller,
	                                 cabinet.num_cabinets,
	                                 num_frames,
	                                 num_boards)
	
	
	# Check for the presence of every wire
	missing = []
	b2c = dict(cabinetised_boards)
	for ((src_board, src_direction), (dst_board, dst_direction)) in wires:
		src = tuple(list(b2c[src_board]) + [src_direction])
		dst = tuple(list(b2c[dst_board]) + [dst_direction])
		actual_dst = wiring_probe.get_link_target(*src)
		actual_src = wiring_probe.get_link_target(*dst)
		
		if actual_dst != dst or actual_src != src:
			missing.append((src, dst))
	
	if missing:
		sys.stderr.write("{} wires missing or erroneously connected.\n".format(
			len(missing), len(wires)))
		
		if args.verbose:
			for src, dst in missing:
				print("C:{} F:{} B:{} {} <--> C:{} F:{} B:{} {}".format(
					src[0], src[1], src[2], src[3].name.replace("_", " "),
					dst[0], dst[1], dst[2], dst[3].name.replace("_", " ")))
		else:
			print("Add --verbose for a complete list.")
		
		return -1
	else:
		sys.stderr.write("All {} wires correctly connected.\n".format(len(wires)))
		return 0
def main(args=None):
    parser = argparse.ArgumentParser(
        description="Start a proxy server to enable multiple interactive wiring "
        "sessions to interact with the same SpiNNaker machine.")
    arguments.add_version_args(parser)

    parser.add_argument("--host",
                        "-H",
                        type=str,
                        default="",
                        help="Host interface to listen on (default: any)")

    parser.add_argument("--port",
                        "-p",
                        type=int,
                        default=DEFAULT_PORT,
                        help="Port listen on (default: %(default)d)")

    parser.add_argument("--verbose",
                        "-v",
                        action="count",
                        default=0,
                        help="Increase verbosity.")

    arguments.add_topology_args(parser)
    arguments.add_cabinet_args(parser)
    arguments.add_bmp_args(parser)

    # Process command-line arguments
    args = parser.parse_args(args)
    (w, h), transformation, uncrinkle_direction, folds =\
     arguments.get_topology_from_args(parser, args)

    cabinet, num_frames = arguments.get_cabinets_from_args(parser, args)

    bmp_ips = arguments.get_bmps_from_args(parser, args, cabinet.num_cabinets,
                                           num_frames)

    if cabinet.num_cabinets == num_frames == 1:
        num_boards = 3 * w * h
    else:
        num_boards = cabinet.boards_per_frame

    # Set verbosity level
    if args.verbose == 1:
        logging.basicConfig(level=logging.INFO)
    elif args.verbose >= 2:
        logging.basicConfig(level=logging.DEBUG)

    # Create a BMP connection
    if len(bmp_ips) == 0:
        parser.error("All BMPs must be supplied using --bmp")
    bmp_controller = BMPController(bmp_ips)

    # Create a wiring probe
    wiring_probe = WiringProbe(bmp_controller, cabinet.num_cabinets,
                               num_frames, num_boards)

    proxy_server = ProxyServer(bmp_controller, wiring_probe, args.host,
                               args.port)

    print("Proxy server starting...")
    proxy_server.main()

    return 0
Example #7
0
def main(args=None):
    parser = argparse.ArgumentParser(
        description="Control SpiNNaker board power (via a BMP)")
    parser.add_argument("--version", "-V", action="version",
                        version="%(prog)s {}".format(rig.__version__))

    parser.add_argument("hostname", type=str,
                        help="hostname or IP of a SpiNNaker board BMP")

    parser.add_argument("state", type=str, default=ON_CHOICES[0], nargs="?",
                        choices=ON_CHOICES + OFF_CHOICES)

    parser.add_argument("-b", "--board", type=str, default="0-23",
                        help="board number (e.g. 0) "
                             "or range of boards (e.g.  1,3,4-6)")

    parser.add_argument("-d", "--power-on-delay", type=float, default=None,
                        help="specify delay (seconds) after power on "
                             "command completes")

    args = parser.parse_args(args)

    # To power on, or to power off, that is the question
    if args.state in ON_CHOICES:
        state = True
    elif args.state in OFF_CHOICES:  # pragma: no branch
        state = False

    # Check power-on-delay range
    if args.power_on_delay is not None and args.power_on_delay < 0.0:
        parser.error("power on delay must be positive")

    # Parse the board number range
    boards = set()
    range_specs = args.board.split(",")
    for range_spec in range_specs:
        left, sep, right = map(str.strip, range_spec.partition("-"))
        try:
            if sep:
                if right.startswith("-"):
                    raise ValueError()
                left = int(left)
                right = int(right)
                if left > right or left < 0 or right < 0:
                    raise ValueError()
                boards.update(range(int(left), int(right) + 1))
            else:
                boards.add(int(left))
        except ValueError:
            parser.error("'{}' is not a valid board/range".format(
                range_spec))

    bc = BMPController(args.hostname)
    try:
        # Check that the device is a actually BMP
        info = bc.get_software_version(board=next(iter(boards)))
        if "BMP" not in info.version_string:
            sys.stderr.write("{}: error: device is not a BMP\n".format(
                parser.prog))
            return 2

        # Actually send the command
        if args.power_on_delay is None:
            bc.set_power(state=state, board=boards)
        else:
            bc.set_power(state=state, board=boards,
                         post_power_on_delay=args.power_on_delay)
    except TimeoutError:
        sys.stderr.write("{}: error: bmp did not respond to command\n".format(
            parser.prog))
        return 1

    return 0