@@ -50,7 +50,6 @@ def __new__(cls, x, y, p, cmd, arg1=0, arg2=0, arg3=0, data=b'',
5050class SCPConnection (object ):
5151 """Implements the SCP protocol for communicating with a SpiNNaker chip.
5252 """
53- error_codes = {}
5453
5554 def __init__ (self , spinnaker_host , port = consts .SCP_PORT ,
5655 n_tries = 5 , timeout = 0.5 ):
@@ -82,15 +81,6 @@ def __init__(self, spinnaker_host, port=consts.SCP_PORT,
8281 # Sequence values
8382 self .seq = seqs ()
8483
85- @classmethod
86- def _register_error (cls , cmd_rc ):
87- """Register an Exception class as belonging to a certain CMD_RC value.
88- """
89- def err_ (err ):
90- cls .error_codes [cmd_rc ] = err
91- return err
92- return err_
93-
9484 def send_scp (self , buffer_size , x , y , p , cmd , arg1 = 0 , arg2 = 0 , arg3 = 0 ,
9585 data = b'' , expected_args = 3 , timeout = 0.0 ):
9686 """Transmit a packet to the SpiNNaker machine and block until an
@@ -176,12 +166,13 @@ def send_scp_burst(self, buffer_size, window_size,
176166 class TransmittedPacket (object ):
177167 """A packet which has been transmitted and still awaits a response.
178168 """
179- __slots__ = ["callback" , "packet" , "n_tries" ,
169+ __slots__ = ["callback" , "packet" , "bytestring" , " n_tries" ,
180170 "timeout" , "timeout_time" ]
181171
182172 def __init__ (self , callback , packet , timeout ):
183173 self .callback = callback
184174 self .packet = packet
175+ self .bytestring = packet .bytestring
185176 self .n_tries = 1
186177 self .timeout = timeout
187178 self .timeout_time = time .time () + self .timeout
@@ -226,12 +217,12 @@ def __init__(self, callback, packet, timeout):
226217 # expecting a response for it and can retransmit it if
227218 # necessary.
228219 outstanding_packets [seq ] = TransmittedPacket (
229- args .callback , packet . bytestring ,
220+ args .callback , packet ,
230221 self .default_timeout + args .timeout
231222 )
232223
233224 # Actually send the packet
234- self .sock .send (outstanding_packets [seq ].packet )
225+ self .sock .send (outstanding_packets [seq ].bytestring )
235226
236227 # Listen on the socket for an acknowledgement packet, there may not
237228 # be one.
@@ -260,18 +251,19 @@ def __init__(self, callback, packet, timeout):
260251 consts .SDP_HEADER_LENGTH + 2 )
261252
262253 # If the code is an error then we respond immediately
263- if rc != consts .SCP_RC_OK :
264- if rc in consts .SCP_RC_TIMEOUT :
254+ if rc != consts .SCPReturnCodes . ok :
255+ if rc in consts .RETRYABLE_SCP_RETURN_CODES :
265256 # If the error is timeout related then treat the packet
266257 # as though it timed out, just discard. This avoids us
267258 # hammering the board when it's most vulnerable.
268259 pass
269- elif rc in self .error_codes :
270- raise self .error_codes [rc ]
271260 else :
272- raise SCPError (
273- "Unhandled exception code {:#2x}" .format (rc )
274- )
261+ # For all other errors, we'll just fall over
262+ # immediately.
263+ packet = outstanding_packets .get (seq )
264+ if packet is not None :
265+ packet = packet .packet
266+ raise FatalReturnCodeError (rc , packet )
275267 else :
276268 # Look up the sequence index of packet in the list of
277269 # outstanding packets. We may have already processed a
@@ -294,10 +286,13 @@ def __init__(self, callback, packet, timeout):
294286 # the given number of times then raise a timeout error for
295287 # it.
296288 if outstanding .n_tries >= self .n_tries :
297- raise TimeoutError (self .n_tries )
289+ raise TimeoutError (
290+ "No response after {} attempts." .format (
291+ self .n_tries ),
292+ outstanding .packet )
298293
299294 # Otherwise we retransmit it
300- self .sock .send (outstanding .packet )
295+ self .sock .send (outstanding .bytestring )
301296 outstanding .n_tries += 1
302297 outstanding .timeout_time = (current_time +
303298 outstanding .timeout )
@@ -425,34 +420,46 @@ def seqs(mask=0xffff):
425420
426421
427422class SCPError (IOError ):
428- """Base Error for SCP return codes."""
429- pass
423+ """Base Error for SCP return codes.
430424
431-
432- class TimeoutError (SCPError ):
433- """Raised when an SCP is not acknowledged within the given period of time.
425+ Attributes
426+ ----------
427+ packet : :py:class:`rig.machine_control.packets.SCPPacket`
428+ The packet being processed when the error occurred. May be None if no
429+ specific packet was involved.
434430 """
435- pass
436431
432+ def __init__ (self , message , packet = None ):
433+ self .packet = packet
434+ if self .packet is not None :
435+ message = "{} (Packet: {})" .format (message , packet )
437436
438- @SCPConnection ._register_error (0x81 )
439- class BadPacketLengthError (SCPError ):
440- """Raised when an SCP packet is an incorrect length."""
441- pass
437+ super (SCPError , self ).__init__ (message )
442438
443439
444- @ SCPConnection . _register_error ( 0x83 )
445- class InvalidCommandError ( SCPError ):
446- """Raised when an SCP packet contains an invalid command code."""
440+ class TimeoutError ( SCPError ):
441+ """Raised when an SCP is not acknowledged within the given period of time.
442+ """
447443 pass
448444
449445
450- @SCPConnection ._register_error (0x84 )
451- class InvalidArgsError (SCPError ):
452- """Raised when an SCP packet has an invalid argument."""
453- pass
446+ class FatalReturnCodeError (SCPError ):
447+ """Raised when an SCP command returns with an error which is connsidered
448+ fatal.
454449
450+ Attributes
451+ ----------
452+ return_code : :py:class:`rig.machine_control.consts.SCPReturnCodes` or int
453+ The return code (will be a raw integer if the code is unrecognised).
454+ """
455455
456- @SCPConnection ._register_error (0x87 )
457- class NoRouteError (SCPError ):
458- """Raised when there is no route to the requested core."""
456+ def __init__ (self , return_code = None , packet = None ):
457+ try :
458+ self .return_code = consts .SCPReturnCodes (return_code )
459+ message = "RC_{}: {}" .format (
460+ self .return_code .name .upper (),
461+ consts .FATAL_SCP_RETURN_CODES [self .return_code ])
462+ except ValueError :
463+ self .return_code = return_code
464+ message = "Unrecognised return code {:#2X}."
465+ super (FatalReturnCodeError , self ).__init__ (message , packet )
0 commit comments