@@ -50,7 +50,6 @@ def __new__(cls, x, y, p, cmd, arg1=0, arg2=0, arg3=0, data=b'',
50
50
class SCPConnection (object ):
51
51
"""Implements the SCP protocol for communicating with a SpiNNaker chip.
52
52
"""
53
- error_codes = {}
54
53
55
54
def __init__ (self , spinnaker_host , port = consts .SCP_PORT ,
56
55
n_tries = 5 , timeout = 0.5 ):
@@ -82,15 +81,6 @@ def __init__(self, spinnaker_host, port=consts.SCP_PORT,
82
81
# Sequence values
83
82
self .seq = seqs ()
84
83
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
-
94
84
def send_scp (self , buffer_size , x , y , p , cmd , arg1 = 0 , arg2 = 0 , arg3 = 0 ,
95
85
data = b'' , expected_args = 3 , timeout = 0.0 ):
96
86
"""Transmit a packet to the SpiNNaker machine and block until an
@@ -176,12 +166,13 @@ def send_scp_burst(self, buffer_size, window_size,
176
166
class TransmittedPacket (object ):
177
167
"""A packet which has been transmitted and still awaits a response.
178
168
"""
179
- __slots__ = ["callback" , "packet" , "n_tries" ,
169
+ __slots__ = ["callback" , "packet" , "bytestring" , " n_tries" ,
180
170
"timeout" , "timeout_time" ]
181
171
182
172
def __init__ (self , callback , packet , timeout ):
183
173
self .callback = callback
184
174
self .packet = packet
175
+ self .bytestring = packet .bytestring
185
176
self .n_tries = 1
186
177
self .timeout = timeout
187
178
self .timeout_time = time .time () + self .timeout
@@ -226,12 +217,12 @@ def __init__(self, callback, packet, timeout):
226
217
# expecting a response for it and can retransmit it if
227
218
# necessary.
228
219
outstanding_packets [seq ] = TransmittedPacket (
229
- args .callback , packet . bytestring ,
220
+ args .callback , packet ,
230
221
self .default_timeout + args .timeout
231
222
)
232
223
233
224
# Actually send the packet
234
- self .sock .send (outstanding_packets [seq ].packet )
225
+ self .sock .send (outstanding_packets [seq ].bytestring )
235
226
236
227
# Listen on the socket for an acknowledgement packet, there may not
237
228
# be one.
@@ -260,18 +251,19 @@ def __init__(self, callback, packet, timeout):
260
251
consts .SDP_HEADER_LENGTH + 2 )
261
252
262
253
# 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 :
265
256
# If the error is timeout related then treat the packet
266
257
# as though it timed out, just discard. This avoids us
267
258
# hammering the board when it's most vulnerable.
268
259
pass
269
- elif rc in self .error_codes :
270
- raise self .error_codes [rc ]
271
260
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 )
275
267
else :
276
268
# Look up the sequence index of packet in the list of
277
269
# outstanding packets. We may have already processed a
@@ -294,10 +286,13 @@ def __init__(self, callback, packet, timeout):
294
286
# the given number of times then raise a timeout error for
295
287
# it.
296
288
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 )
298
293
299
294
# Otherwise we retransmit it
300
- self .sock .send (outstanding .packet )
295
+ self .sock .send (outstanding .bytestring )
301
296
outstanding .n_tries += 1
302
297
outstanding .timeout_time = (current_time +
303
298
outstanding .timeout )
@@ -425,34 +420,46 @@ def seqs(mask=0xffff):
425
420
426
421
427
422
class SCPError (IOError ):
428
- """Base Error for SCP return codes."""
429
- pass
423
+ """Base Error for SCP return codes.
430
424
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.
434
430
"""
435
- pass
436
431
432
+ def __init__ (self , message , packet = None ):
433
+ self .packet = packet
434
+ if self .packet is not None :
435
+ message = "{} (Packet: {})" .format (message , packet )
437
436
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 )
442
438
443
439
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
+ """
447
443
pass
448
444
449
445
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.
454
449
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
+ """
455
455
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