Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Power on during init #356

Merged
merged 9 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion quick_tests/quick_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def __exit__(self, *_ignored):
def print_transceiver_tests(transceiver):

with Section("Version Information"):
version_info = transceiver.ensure_board_is_ready()
version_info = transceiver._get_scamp_version()
print(version_info)

app_id = SpiNNManDataView().get_new_id()
Expand Down
87 changes: 43 additions & 44 deletions spinnman/transceiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
UDP_BOOT_CONNECTION_DEFAULT_PORT, NO_ROUTER_DIAGNOSTIC_FILTERS,
ROUTER_REGISTER_BASE_ADDRESS, ROUTER_DEFAULT_FILTERS_MAX_POSITION,
ROUTER_FILTER_CONTROLS_OFFSET, ROUTER_DIAGNOSTIC_FILTER_SIZE, N_RETRIES,
BOOT_RETRIES)
BOOT_RETRIES, POWER_CYCLE_WAIT_TIME_IN_SECONDS)
from spinnman.data import SpiNNManDataView
from spinnman.exceptions import (
SpinnmanInvalidParameterException, SpinnmanException, SpinnmanIOException,
Expand Down Expand Up @@ -88,10 +88,27 @@
_ONE_LONG = struct.Struct("<Q")
_EXECUTABLE_ADDRESS = 0x67800000

_POWER_CYCLE_WARNING = (
"When power-cycling a board, it is recommended that you wait for 30 "
"seconds before attempting a reboot. Therefore, the tools will now "
"wait for 30 seconds. If you wish to avoid this wait, please set "
"reset_machine_on_startup = False in the [Machine] section of the "
"relevant configuration (cfg) file.")

_POWER_CYCLE_FAILURE_WARNING = (
"The end user requested the power-cycling of the board. But the "
"tools did not have the required BMP connection to facilitate a "
"power-cycling, and therefore will not do so. please set the "
"bmp_names accordingly in the [Machine] section of the relevant "
"configuration (cfg) file. Or use a machine assess process which "
"provides the BMP data (such as a spalloc system) or finally set "
"reset_machine_on_startup = False in the [Machine] section of the "
"relevant configuration (cfg) file to avoid this warning in future.")


def create_transceiver_from_hostname(
hostname, version, bmp_connection_data=None, number_of_boards=None,
auto_detect_bmp=False):
auto_detect_bmp=False, power_cycle=False):
"""
Create a Transceiver by creating a :py:class:`~.UDPConnection` to the
given hostname on port 17893 (the default SCAMP port), and a
Expand All @@ -116,6 +133,7 @@ def create_transceiver_from_hostname(
automatically determined from the board IP address
:param scamp_connections:
the list of connections used for SCAMP communications
:param bool power_cycle: If True will power cycle the machine:
:return: The created transceiver
:rtype: Transceiver
:raise SpinnmanIOException:
Expand Down Expand Up @@ -151,7 +169,8 @@ def create_transceiver_from_hostname(
# handle the boot connection
connections.append(BootConnection(remote_host=hostname))

return Transceiver(version, connections=connections)
return Transceiver(version, connections=connections,
power_cycle=power_cycle)


class Transceiver(AbstractContextManager):
Expand Down Expand Up @@ -188,12 +207,13 @@ class Transceiver(AbstractContextManager):
"_width"]

def __init__(
self, version, connections=None):
self, version, connections=None, power_cycle=False):
"""
:param int version: The version of the board being connected to
:param list(Connection) connections:
An iterable of connections to the board. If not specified, no
communication will be possible until connections are found.
:param bool power_cycle: If True will power cycle the machine:
:raise SpinnmanIOException:
If there is an error communicating with the board, or if no
connections to the board can be found (if connections is ``None``)
Expand Down Expand Up @@ -253,6 +273,10 @@ def __init__(

self._machine_off = False

if power_cycle:
self._power_off_machine()
self._ensure_board_is_ready()

def _where_is_xy(self, x, y):
"""
Attempts to get where_is_x_y info from the machine
Expand Down Expand Up @@ -641,7 +665,7 @@ def _is_scamp_version_compabible(version):
# version is irrelevant
return version[1] > _SCAMP_VERSION[1]

def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None):
def _ensure_board_is_ready(self, n_retries=5, extra_boot_values=None):
"""
Ensure that the board is ready to interact with this version of the
transceiver. Boots the board if not already booted and verifies that
Expand All @@ -653,7 +677,6 @@ def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None):
This should only be used for values which are not standard
based on the board version.
:return: The version identifier
:rtype: VersionInfo
:raise SpinnmanIOException:
* If there is a problem booting the board
* If the version of software on the board is not compatible with
Expand Down Expand Up @@ -716,8 +739,6 @@ def ensure_board_is_ready(self, n_retries=5, extra_boot_values=None):
self._scamp_connection_selector = MostDirectConnectionSelector(
self._scamp_connections)

return version_info

def __is_default_destination(self, version_info):
return (version_info.x == AbstractSCPRequest.DEFAULT_DEST_X_COORD
and version_info.y == AbstractSCPRequest.DEFAULT_DEST_Y_COORD)
Expand Down Expand Up @@ -1048,66 +1069,44 @@ def _power_on_machine(self):
"""
Power on the whole machine.

:rtype bool
:return success of failure to power on machine
"""
if self._bmp_connection is None:
logger.warning("No BMP connections, so can't power on")
return False
self.power_on(self._bmp_connection)
return True

def power_on(self, boards=0):
"""
Power on a set of boards in the machine.
self._power(PowerCommand.POWER_ON)
self._machine_off = True
# Sleep for 5 seconds as the machine has just been powered on
time.sleep(BMP_POST_POWER_ON_SLEEP_TIME)

:param int boards: The board or boards to power on
"""
self._power(PowerCommand.POWER_ON, boards)

def power_off_machine(self):
def _power_off_machine(self):
"""
Power off the whole machine.

:rtype bool
:return success or failure to power off the machine
"""
if self._bmp_connection is None:
logger.warning("No BMP connections, so can't power off")
return False
logger.info("Turning off machine")
self.power_off(self._bmp_connection)
return True

def power_off(self, boards=0):
"""
Power off a set of boards in the machine.
logger.warning(_POWER_CYCLE_FAILURE_WARNING)
self._power(PowerCommand.POWER_OFF)
logger.warning(_POWER_CYCLE_WARNING)
time.sleep(POWER_CYCLE_WAIT_TIME_IN_SECONDS)
logger.warning("Power cycle wait complete")

:param int boards: The board or boards to power off
"""
self._power(PowerCommand.POWER_OFF, boards)

def _power(self, power_command, boards=0):
def _power(self, power_command):
"""
Send a power request to the machine.

:param PowerCommand power_command: The power command to send
:param boards: The board or boards to send the command to
"""
connection_selector = self._bmp_selector
timeout = (
BMP_POWER_ON_TIMEOUT
if power_command == PowerCommand.POWER_ON
else BMP_TIMEOUT)
process = SendSingleCommandProcess(
connection_selector, timeout=timeout, n_retries=0)
process.execute(SetPower(power_command, boards))
self._bmp_selector, timeout=timeout, n_retries=0)
process.execute(SetPower(power_command, self._bmp_connection.boards))
self._machine_off = power_command == PowerCommand.POWER_OFF

# Sleep for 5 seconds if the machine has just been powered on
if not self._machine_off:
time.sleep(BMP_POST_POWER_ON_SLEEP_TIME)

def read_fpga_register(
self, fpga_num, register, board=0):
"""
Expand Down Expand Up @@ -1937,7 +1936,7 @@ def close(self):
"""
if self._bmp_connection is not None:
if get_config_bool("Machine", "turn_off_machine"):
self.power_off_machine()
self._power_off_machine()

for connection in self._all_connections:
connection.close()
Expand Down
3 changes: 3 additions & 0 deletions unittests/test_transceiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def write_memory(
def close(self):
pass

def _ensure_board_is_ready(self, n_retries=5, extra_boot_values=None):
pass


class MockExtendedTransceiver(MockWriteTransceiver, ExtendedTransceiver):
pass
Expand Down