• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SpiNNakerManchester / JavaSpiNNaker / 15234311369

22 May 2025 10:25AM UTC coverage: 37.528% (-0.8%) from 38.278%
15234311369

push

github

web-flow
Merge pull request #1227 from SpiNNakerManchester/emergency_stop

Emergency stop

114 of 152 new or added lines in 10 files covered. (75.0%)

243 existing lines in 6 files now uncovered.

9062 of 24147 relevant lines covered (37.53%)

1.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/SpiNNaker-comms/src/main/java/uk/ac/manchester/spinnaker/transceiver/Transceiver.java
1
/*
2
 * Copyright (c) 2018 The University of Manchester
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package uk.ac.manchester.spinnaker.transceiver;
17

18
import static java.lang.Byte.toUnsignedInt;
19
import static java.lang.String.format;
20
import static java.lang.System.currentTimeMillis;
21
import static java.lang.Thread.sleep;
22
import static java.net.InetAddress.getByAddress;
23
import static java.nio.ByteBuffer.allocate;
24
import static java.util.Arrays.stream;
25
import static java.util.Collections.unmodifiableSet;
26
import static java.util.Objects.requireNonNull;
27
import static org.apache.commons.io.IOUtils.buffer;
28
import static org.slf4j.LoggerFactory.getLogger;
29
import static uk.ac.manchester.spinnaker.machine.MachineDefaults.NUM_ROUTER_DIAGNOSTIC_COUNTERS;
30
import static uk.ac.manchester.spinnaker.machine.SpiNNakerTriadGeometry.getSpinn5Geometry;
31
import static uk.ac.manchester.spinnaker.messages.Constants.BMP_POST_POWER_ON_SLEEP_TIME;
32
import static uk.ac.manchester.spinnaker.messages.Constants.BMP_POWER_ON_TIMEOUT;
33
import static uk.ac.manchester.spinnaker.messages.Constants.BMP_TIMEOUT;
34
import static uk.ac.manchester.spinnaker.messages.Constants.NO_ROUTER_DIAGNOSTIC_FILTERS;
35
import static uk.ac.manchester.spinnaker.messages.Constants.ROUTER_DEFAULT_FILTERS_MAX_POSITION;
36
import static uk.ac.manchester.spinnaker.messages.Constants.ROUTER_DIAGNOSTIC_FILTER_SIZE;
37
import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT;
38
import static uk.ac.manchester.spinnaker.messages.Constants.UDP_BOOT_CONNECTION_DEFAULT_PORT;
39
import static uk.ac.manchester.spinnaker.messages.Constants.WORD_SIZE;
40
import static uk.ac.manchester.spinnaker.messages.bmp.SerialVector.SERIAL_LENGTH;
41
import static uk.ac.manchester.spinnaker.messages.bmp.WriteFlashBuffer.FLASH_CHUNK_SIZE;
42
import static uk.ac.manchester.spinnaker.messages.model.IPTagTimeOutWaitTime.TIMEOUT_2560_ms;
43
import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_OFF;
44
import static uk.ac.manchester.spinnaker.messages.model.PowerCommand.POWER_ON;
45
import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.ethernet_ip_address;
46
import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.iobuf_size;
47
import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.router_table_copy_address;
48
import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.software_watchdog_count;
49
import static uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition.y_size;
50
import static uk.ac.manchester.spinnaker.messages.scp.SCPRequest.BOOT_CHIP;
51
import static uk.ac.manchester.spinnaker.transceiver.BMPCommandProcess.BMP_RETRIES;
52
import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.EXECUTABLE_ADDRESS;
53
import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.ROUTER_DIAGNOSTIC_COUNTER;
54
import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.ROUTER_FILTERS;
55
import static uk.ac.manchester.spinnaker.transceiver.CommonMemoryLocations.SYS_VARS;
56
import static uk.ac.manchester.spinnaker.transceiver.Utils.defaultBMPforMachine;
57
import static uk.ac.manchester.spinnaker.utils.ByteBufferUtils.sliceUp;
58
import static uk.ac.manchester.spinnaker.utils.UnitConstants.KILOBYTE;
59
import static uk.ac.manchester.spinnaker.utils.UnitConstants.MSEC_PER_SEC;
60

61
import java.io.File;
62
import java.io.FileInputStream;
63
import java.io.IOException;
64
import java.io.InputStream;
65
import java.net.InetAddress;
66
import java.net.SocketTimeoutException;
67
import java.nio.ByteBuffer;
68
import java.util.ArrayList;
69
import java.util.Collection;
70
import java.util.EnumSet;
71
import java.util.HashMap;
72
import java.util.HashSet;
73
import java.util.List;
74
import java.util.Map;
75
import java.util.Set;
76
import java.util.concurrent.Semaphore;
77
import java.util.concurrent.ThreadLocalRandom;
78

79
import javax.validation.Valid;
80
import javax.validation.constraints.NotNull;
81

82
import org.slf4j.Logger;
83

84
import com.google.errorprone.annotations.CheckReturnValue;
85
import com.google.errorprone.annotations.MustBeClosed;
86
import com.google.errorprone.annotations.concurrent.GuardedBy;
87

88
import uk.ac.manchester.spinnaker.connections.BMPConnection;
89
import uk.ac.manchester.spinnaker.connections.BootConnection;
90
import uk.ac.manchester.spinnaker.connections.ConnectionSelector;
91
import uk.ac.manchester.spinnaker.connections.DelegatingSCPConnection;
92
import uk.ac.manchester.spinnaker.connections.MachineAware;
93
import uk.ac.manchester.spinnaker.connections.MostDirectConnectionSelector;
94
import uk.ac.manchester.spinnaker.connections.SCPConnection;
95
import uk.ac.manchester.spinnaker.connections.SDPConnection;
96
import uk.ac.manchester.spinnaker.connections.SingletonConnectionSelector;
97
import uk.ac.manchester.spinnaker.connections.UDPConnection;
98
import uk.ac.manchester.spinnaker.connections.model.Connection;
99
import uk.ac.manchester.spinnaker.machine.ChipLocation;
100
import uk.ac.manchester.spinnaker.machine.CoreLocation;
101
import uk.ac.manchester.spinnaker.machine.CoreSubsets;
102
import uk.ac.manchester.spinnaker.machine.Direction;
103
import uk.ac.manchester.spinnaker.machine.HasChipLocation;
104
import uk.ac.manchester.spinnaker.machine.HasCoreLocation;
105
import uk.ac.manchester.spinnaker.machine.Machine;
106
import uk.ac.manchester.spinnaker.machine.MachineDimensions;
107
import uk.ac.manchester.spinnaker.machine.MachineVersion;
108
import uk.ac.manchester.spinnaker.machine.MemoryLocation;
109
import uk.ac.manchester.spinnaker.machine.MulticastRoutingEntry;
110
import uk.ac.manchester.spinnaker.machine.RoutingEntry;
111
import uk.ac.manchester.spinnaker.machine.board.BMPBoard;
112
import uk.ac.manchester.spinnaker.machine.board.BMPCoords;
113
import uk.ac.manchester.spinnaker.machine.tags.IPTag;
114
import uk.ac.manchester.spinnaker.machine.tags.ReverseIPTag;
115
import uk.ac.manchester.spinnaker.machine.tags.Tag;
116
import uk.ac.manchester.spinnaker.messages.bmp.BMPRequest;
117
import uk.ac.manchester.spinnaker.messages.bmp.BMPSetLED;
118
import uk.ac.manchester.spinnaker.messages.bmp.GetBMPVersion;
119
import uk.ac.manchester.spinnaker.messages.bmp.GetFPGAResetStatus;
120
import uk.ac.manchester.spinnaker.messages.bmp.ReadADC;
121
import uk.ac.manchester.spinnaker.messages.bmp.ReadCANStatus;
122
import uk.ac.manchester.spinnaker.messages.bmp.ReadFPGARegister;
123
import uk.ac.manchester.spinnaker.messages.bmp.ReadSerialFlashCRC;
124
import uk.ac.manchester.spinnaker.messages.bmp.ReadSerialVector;
125
import uk.ac.manchester.spinnaker.messages.bmp.ResetFPGA;
126
import uk.ac.manchester.spinnaker.messages.bmp.SetPower;
127
import uk.ac.manchester.spinnaker.messages.bmp.WriteFPGARegister;
128
import uk.ac.manchester.spinnaker.messages.bmp.WriteFlashBuffer;
129
import uk.ac.manchester.spinnaker.messages.boot.BootMessages;
130
import uk.ac.manchester.spinnaker.messages.model.ADCInfo;
131
import uk.ac.manchester.spinnaker.messages.model.AppID;
132
import uk.ac.manchester.spinnaker.messages.model.BMPConnectionData;
133
import uk.ac.manchester.spinnaker.messages.model.CPUInfo;
134
import uk.ac.manchester.spinnaker.messages.model.CPUState;
135
import uk.ac.manchester.spinnaker.messages.model.DiagnosticFilter;
136
import uk.ac.manchester.spinnaker.messages.model.FPGA;
137
import uk.ac.manchester.spinnaker.messages.model.HeapElement;
138
import uk.ac.manchester.spinnaker.messages.model.IOBuffer;
139
import uk.ac.manchester.spinnaker.messages.model.LEDAction;
140
import uk.ac.manchester.spinnaker.messages.model.PowerCommand;
141
import uk.ac.manchester.spinnaker.messages.model.ReinjectionStatus;
142
import uk.ac.manchester.spinnaker.messages.model.RouterDiagnostics;
143
import uk.ac.manchester.spinnaker.messages.model.Signal;
144
import uk.ac.manchester.spinnaker.messages.model.SystemVariableDefinition;
145
import uk.ac.manchester.spinnaker.messages.model.Version;
146
import uk.ac.manchester.spinnaker.messages.model.VersionInfo;
147
import uk.ac.manchester.spinnaker.messages.scp.ApplicationRun;
148
import uk.ac.manchester.spinnaker.messages.scp.ApplicationStop;
149
import uk.ac.manchester.spinnaker.messages.scp.CountState;
150
import uk.ac.manchester.spinnaker.messages.scp.EmptyResponse;
151
import uk.ac.manchester.spinnaker.messages.scp.GetChipInfo;
152
import uk.ac.manchester.spinnaker.messages.scp.GetVersion;
153
import uk.ac.manchester.spinnaker.messages.scp.IPTagClear;
154
import uk.ac.manchester.spinnaker.messages.scp.IPTagSet;
155
import uk.ac.manchester.spinnaker.messages.scp.IPTagSetTTO;
156
import uk.ac.manchester.spinnaker.messages.scp.PayloadedResponse;
157
import uk.ac.manchester.spinnaker.messages.scp.ReadMemory;
158
import uk.ac.manchester.spinnaker.messages.scp.ReverseIPTagSet;
159
import uk.ac.manchester.spinnaker.messages.scp.RouterClear;
160
import uk.ac.manchester.spinnaker.messages.scp.SCPRequest;
161
import uk.ac.manchester.spinnaker.messages.scp.SDRAMAlloc;
162
import uk.ac.manchester.spinnaker.messages.scp.SDRAMDeAlloc;
163
import uk.ac.manchester.spinnaker.messages.scp.SendSignal;
164
import uk.ac.manchester.spinnaker.messages.scp.SetLED;
165
import uk.ac.manchester.spinnaker.messages.sdp.SDPMessage;
166
import uk.ac.manchester.spinnaker.protocols.FastDataIn;
167
import uk.ac.manchester.spinnaker.protocols.download.Downloader;
168
import uk.ac.manchester.spinnaker.storage.BufferManagerStorage;
169
import uk.ac.manchester.spinnaker.storage.StorageException;
170
import uk.ac.manchester.spinnaker.utils.MappableIterable;
171
import uk.ac.manchester.spinnaker.utils.Ping;
172

173
/**
174
 * An encapsulation of various communications with the SpiNNaker board. Acts as
175
 * a Façade for most of the rest of this package.
176
 * <p>
177
 * The methods of this class are designed to be thread-safe; thus you can make
178
 * multiple calls to the same (or different) methods from multiple threads and
179
 * expect each call to work as if it had been called sequentially, although the
180
 * order of returns is not guaranteed. Note also that with multiple connections
181
 * to the board, using multiple threads in this way may result in an increase in
182
 * the overall speed of operation, since the multiple calls may be made
183
 * separately over the set of given connections.
184
 * <p>
185
 * For details of thread safety, see the methods annotated with
186
 * {@link ParallelSafe}, {@link ParallelSafeWithCare} and {@link ParallelUnsafe}
187
 * in {@link TransceiverInterface}. <em>Note that operations on an individual
188
 * BMP are <strong>always</strong> parallel-unsafe, other documentation in this
189
 * class notwithstanding; BMPs must only ever have one outstanding call made to
190
 * them as they do not handle asynchronous calls at all well due to known
191
 * firmware bugs.</em>
192
 */
193
public class Transceiver extends UDPTransceiver
194
                implements TransceiverInterface, RetryTracker {
195
        private static final Logger log = getLogger(Transceiver.class);
×
196

197
        private static final String SCAMP_NAME = "SC&MP";
198

199
        private static final Version SCAMP_VERSION = new Version(3, 0, 1);
×
200

201
        private static final String BMP_NAME = "BC&MP";
202

203
        private static final Set<Integer> BMP_MAJOR_VERSIONS = Set.of(1, 2);
×
204

205
        /** How many times do we try to find SCAMP? */
206
        private static final int INITIAL_FIND_SCAMP_RETRIES_COUNT = 3;
207

208
        private static final int CONNECTION_CHECK_RETRY_COUNT = 3;
209

210
        private static final int CONNECTION_CHECK_DELAY = 100;
211

212
        private static final int NNID_MAX = 0x7F;
213

214
        private static final int POST_BOOT_DELAY = 2000;
215

216
        /**
217
         * The number of milliseconds after powering on the machine to wait before
218
         * attempting to boot SCAMP on its chips. This is time to allow the code on
219
         * each chip's ROM to figure out what the state of the hardware is enough
220
         * for booting to be viable.
221
         */
222
        private static final int POST_POWER_ON_DELAY = 2000;
223

224
        private static final int ENABLE_SHIFT = 16;
225

226
        /**
227
         * How much data to pile into SCAMP before reducing the number of messages
228
         * in flight at a time.
229
         */
230
        private static final int LARGE_DATA_WRITE_THRESHOLD = 16 * KILOBYTE;
231

232
        /**
233
         * The maximum number of SCP messages to have in flight in a large data
234
         * write.
235
         */
236
        private static final int LARGE_WRITE_PARALLEL_MESSAGE_COUNT = 4;
237

238
        /** The version of the board being connected to. */
239
        private MachineVersion version;
240

241
        /** The discovered machine model. */
242
        private Machine machine;
243

244
        private MachineDimensions dimensions;
245

246
        /**
247
         * A set of chips to ignore in the machine. Requests for a "machine" will
248
         * have these chips excluded, as if they never existed. The processor IDs of
249
         * the specified chips are ignored.
250
         */
251
        private final Set<ChipLocation> ignoreChips = new HashSet<>();
×
252

253
        /**
254
         * A set of cores to ignore in the machine. Requests for a "machine" will
255
         * have these cores excluded, as if they never existed.
256
         */
257
        private final Map<ChipLocation, Set<Integer>> ignoreCores = new HashMap<>();
×
258

259
        /**
260
         * A set of links to ignore in the machine. Requests for a "machine" will
261
         * have these links excluded, as if they never existed.
262
         */
263
        private final Map<ChipLocation, EnumSet<Direction>> ignoreLinks =
×
264
                        new HashMap<>();
265

266
        /**
267
         * The max size each chip can say it has for SDRAM. (This is mainly used for
268
         * debugging purposes.)
269
         */
270
        private final Integer maxSDRAMSize;
271

272
        private Integer iobufSize;
273

274
        private AppIdTracker appIDTracker;
275

276
        /**
277
         * A set of the original connections. Used to determine what can be closed.
278
         */
279
        private final Set<Connection> originalConnections = new HashSet<>();
×
280

281
        /** A set of all connections. Used for closing. */
282
        private final Set<Connection> allConnections = new HashSet<>();
×
283

284
        /**
285
         * A boot send connection. There can only be one in the current system, or
286
         * otherwise bad things can happen!
287
         */
288
        private BootConnection bootConnection;
289

290
        /** A list of all connections that can be used to send SDP messages. */
291
        private final List<SDPConnection> sdpConnections = new ArrayList<>();
×
292

293
        /**
294
         * A map of IP address &rarr; SCAMP connection. These are those that can be
295
         * used for setting up IP Tags.
296
         */
297
        private final Map<InetAddress, SCPConnection> udpScpConnections =
×
298
                        new HashMap<>();
299

300
        /**
301
         * A list of all connections that can be used to send and receive SCP
302
         * messages for SCAMP interaction.
303
         */
304
        private final List<SCPConnection> scpConnections = new ArrayList<>();
×
305

306
        /** The BMP connections. */
307
        private final List<BMPConnection> bmpConnections = new ArrayList<>();
×
308

309
        /** Connection selectors for the BMP processes. */
310
        private final Map<BMPCoords,
×
311
                        ConnectionSelector<BMPConnection>> bmpSelectors = new HashMap<>();
312

313
        /** Connection selectors for the SCP processes. */
314
        private final ConnectionSelector<SCPConnection> scpSelector;
315

316
        /** The nearest neighbour start ID. */
317
        @GuardedBy("nearestNeighbourLock")
×
318
        private int nearestNeighbourID = 1;
319

320
        /** The nearest neighbour lock. */
321
        private final Object nearestNeighbourLock = new Object();
×
322

323
        /**
324
         * A lock against multiple flood fill writes. This is needed as SCAMP cannot
325
         * cope with this.
326
         */
327
        private final Object floodWriteLock = new Object();
×
328

329
        /**
330
         * Lock against single chip executions. The condition should be acquired
331
         * before the locks are checked or updated.
332
         * <p>
333
         * The write lock condition should also be acquired to avoid a flood fill
334
         * during an individual chip execute.
335
         */
336
        private final Map<ChipLocation, Semaphore> chipExecuteLocks =
×
337
                        new HashMap<>();
338

339
        @GuardedBy("itself")
×
340
        private final FloodLock executeFloodLock = new FloodLock();
341

342
        private boolean machineOff = false;
×
343

344
        private long retryCount = 0L;
×
345

346
        private BMPCoords boundBMP = new BMPCoords(0, 0);
×
347

348
        @GuardedBy("itself")
×
349
        private Map<ChipLocation, FastDataIn> cachedDataIn = new HashMap<>();
350

351
        @GuardedBy("itself")
×
352
        private Map<ChipLocation, Downloader> cachedDownloaders = new HashMap<>();
353

354
        /**
355
         * Create a Transceiver by creating a UDPConnection to the given hostname on
356
         * port 17893 (the default SCAMP port), and a BootConnection on port 54321
357
         * (the default boot port), optionally discovering any additional links
358
         * using the UDPConnection, and then returning the transceiver created with
359
         * the conjunction of the created UDPConnection and the discovered
360
         * connections.
361
         *
362
         * @param host
363
         *            The host IP address of the board
364
         * @param version
365
         *            The type of SpiNNaker board used within the SpiNNaker machine
366
         *            being used. May be {@code null} if the board in question can
367
         *            be assumed to be always already booted.
368
         * @param numberOfBoards
369
         *            a number of boards expected to be supported, or {@code null},
370
         *            which defaults to a single board
371
         * @param ignoredChips
372
         *            An optional set of chips to ignore in the machine. Requests
373
         *            for a "machine" will have these chips excluded, as if they
374
         *            never existed. The processors of the specified chips are
375
         *            ignored.
376
         * @param ignoredCores
377
         *            An optional map of cores to ignore in the machine. Requests
378
         *            for a "machine" will have these cores excluded, as if they
379
         *            never existed.
380
         * @param ignoredLinks
381
         *            An optional set of links to ignore in the machine. Requests
382
         *            for a "machine" will have these links excluded, as if they
383
         *            never existed.
384
         * @param bmpConnectionData
385
         *            the details of the BMP connections used to boot multi-board
386
         *            systems
387
         * @param autodetectBMP
388
         *            True if the BMP of version 4 or 5 boards should be
389
         *            automatically determined from the board IP address
390
         * @param bootPortNumber
391
         *            the port number used to boot the machine
392
         * @param scampConnections
393
         *            the list of connections used for SCAMP communications
394
         * @param maxSDRAMSize
395
         *            the max size each chip can say it has for SDRAM (mainly used
396
         *            in debugging purposes)
397
         * @throws IOException
398
         *             if networking fails
399
         * @throws SpinnmanException
400
         *             If a BMP is uncontactable or SpiNNaker rejects a message.
401
         * @throws InterruptedException
402
         *             If the communications were interrupted.
403
         */
404
        @MustBeClosed
405
        @SuppressWarnings("MustBeClosed")
406
        public Transceiver(InetAddress host, MachineVersion version,
407
                        Collection<BMPConnectionData> bmpConnectionData,
408
                        Integer numberOfBoards, Set<ChipLocation> ignoredChips,
409
                        Map<ChipLocation, Set<Integer>> ignoredCores,
410
                        Map<ChipLocation, EnumSet<Direction>> ignoredLinks,
411
                        boolean autodetectBMP, List<ConnectionDescriptor> scampConnections,
412
                        Integer bootPortNumber, Integer maxSDRAMSize)
413
                        throws IOException, SpinnmanException, InterruptedException {
×
414
                log.info("Creating transceiver for {}", requireNonNull(host,
×
415
                                "SpiNNaker machine host name must be not null"));
416
                var connections = new ArrayList<Connection>();
×
417

418
                /*
419
                 * if no BMP has been supplied, but the board is a spinn4 or a spinn5
420
                 * machine, then an assumption can be made that the BMP is at -1 on the
421
                 * final value of the IP address
422
                 */
423
                if (version != null && !version.isFourChip && autodetectBMP
×
424
                                && (bmpConnectionData == null || bmpConnectionData.isEmpty())) {
×
425
                        bmpConnectionData =
×
426
                                        List.of(defaultBMPforMachine(host, numberOfBoards));
×
427
                }
428

429
                // handle BMP connections
430
                if (bmpConnectionData != null) {
×
431
                        var bmpIPs = new ArrayList<>();
×
432
                        for (var connData : bmpConnectionData) {
×
433
                                var connection = new BMPConnection(connData);
×
434
                                connections.add(connection);
×
435
                                bmpIPs.add(connection.getRemoteIPAddress());
×
436
                        }
×
437
                        log.info("Transceiver using BMPs: {}", bmpIPs);
×
438
                }
439

440
                // handle the SpiNNaker connection
441
                if (scampConnections == null) {
×
442
                        scampConnections = List.of();
×
443
                }
444
                if (scampConnections.isEmpty()) {
×
445
                        connections.add(createScpConnection(BOOT_CHIP, host));
×
446
                }
447

448
                // handle the boot connection
449
                connections.add(new BootConnection(null, null, host, bootPortNumber));
×
450

451
                this.version = version;
×
452
                if (ignoredChips != null) {
×
453
                        ignoreChips.addAll(ignoredChips);
×
454
                }
455
                if (ignoredCores != null) {
×
456
                        ignoreCores.putAll(ignoredCores);
×
457
                }
458
                if (ignoredLinks != null) {
×
459
                        ignoreLinks.putAll(ignoredLinks);
×
460
                }
461
                this.maxSDRAMSize = maxSDRAMSize;
×
462

463
                originalConnections.addAll(connections);
×
464
                allConnections.addAll(connections);
×
465
                // if there has been SCAMP connections given, build them
466
                for (var desc : scampConnections) {
×
467
                        if (desc.portNumber != null
×
468
                                        && desc.portNumber != SCP_SCAMP_PORT) {
×
469
                                log.warn("ignoring unexpected SCAMP port: {}",
×
470
                                                desc.portNumber);
471
                        }
472
                        connections.add(createScpConnection(desc.chip, desc.hostname));
×
473
                }
×
474
                for (Connection conn : connections) {
×
475
                        identifyConnection(conn);
×
476
                }
×
477
                scpSelector = makeConnectionSelector();
×
478
                checkBMPConnections();
×
479
        }
×
480

481
        /**
482
         * Create a Transceiver by creating a UDPConnection to the given hostname on
483
         * port 17893 (the default SCAMP port), and a BootConnection on port 54321
484
         * (the default boot port), discovering any additional links using the
485
         * UDPConnection, and then returning the transceiver created with the
486
         * conjunction of the created UDPConnection and the discovered connections.
487
         *
488
         * @param hostname
489
         *            The hostname or IP address of the board
490
         * @param version
491
         *            The type of SpiNNaker board used within the SpiNNaker machine
492
         *            being used. May be {@code null} if the board in question can
493
         *            be assumed to be always already booted.
494
         * @throws IOException
495
         *             if networking fails
496
         * @throws SpinnmanException
497
         *             If a BMP is uncontactable or SpiNNaker rejects a message.
498
         * @throws InterruptedException
499
         *             If the communications were interrupted.
500
         */
501
        @MustBeClosed
502
        public Transceiver(InetAddress hostname, MachineVersion version)
503
                        throws IOException, SpinnmanException, InterruptedException {
504
                this(hostname, version, null, 0, Set.of(), Map.of(), Map.of(), false,
×
505
                                null, null, null);
506
        }
×
507

508
        /**
509
         * Create a transceiver.
510
         *
511
         * @param version
512
         *            The type of SpiNNaker board used within the SpiNNaker machine
513
         *            being used. May be {@code null} if the board in question can
514
         *            be assumed to be always already booted.
515
         * @param connections
516
         *            The connections to use in the transceiver. Note that the
517
         *            transceiver may make additional connections.
518
         * @throws IOException
519
         *             if networking fails
520
         * @throws SpinnmanException
521
         *             If a BMP is uncontactable or SpiNNaker rejects a message.
522
         * @throws InterruptedException
523
         *             If the communications were interrupted.
524
         */
525
        @MustBeClosed
526
        public Transceiver(MachineVersion version,
527
                        Collection<Connection> connections)
528
                        throws IOException, SpinnmanException, InterruptedException {
529
                this(version, connections, null, null, null, null,        null);
×
530
        }
×
531

532
        /**
533
         * Create a transceiver.
534
         *
535
         * @param version
536
         *            The type of SpiNNaker board used within the SpiNNaker machine
537
         *            being used. May be {@code null} if the board in question can
538
         *            be assumed to be always already booted.
539
         * @param scampConnections
540
         *            Descriptions of SCP Connections to make.
541
         * @return The created transceiver.
542
         * @throws IOException
543
         *             if networking fails
544
         * @throws SpinnmanException
545
         *             If a BMP is uncontactable or SpiNNaker rejects a message.
546
         * @throws InterruptedException
547
         *             If the communications were interrupted.
548
         */
549
        @MustBeClosed
550
        public static Transceiver makeWithDescriptors(MachineVersion version,
551
                        Collection<ConnectionDescriptor> scampConnections)
552
                        throws IOException, SpinnmanException, InterruptedException {
553
                return new Transceiver(version, null, null, null, null,
×
554
                                scampConnections, null);
555
        }
556

557
        /**
558
         * Create a transceiver.
559
         *
560
         * @param version
561
         *            The type of SpiNNaker board used within the SpiNNaker machine
562
         *            being used. May be {@code null} if the board in question can
563
         *            be assumed to be always already booted.
564
         * @param connections
565
         *            The connections to use in the transceiver. Note that the
566
         *            transceiver may make additional connections. <em>This should
567
         *            be modifiable (or {@code null}) if {@code scampConnections}
568
         *            supplied and not empty.</em>
569
         * @param ignoredChips
570
         *            Blacklisted chips.
571
         * @param ignoredCores
572
         *            Blacklisted cores.
573
         * @param ignoredLinks
574
         *            Blacklisted links.
575
         * @param scampConnections
576
         *            Descriptions of SCP connections to create.
577
         * @param maxSDRAMSize
578
         *            If not {@code null}, the maximum SDRAM size to allow.
579
         * @throws IOException
580
         *             if networking fails
581
         * @throws SpinnmanException
582
         *             If a BMP is uncontactable or SpiNNaker rejects a message.
583
         * @throws InterruptedException
584
         *             If the communications were interrupted.
585
         */
586
        @SuppressWarnings("MustBeClosed")
587
        public Transceiver(MachineVersion version,
588
                        Collection<Connection> connections,
589
                        Collection<ChipLocation> ignoredChips,
590
                        Map<ChipLocation, Set<Integer>> ignoredCores,
591
                        Map<ChipLocation, EnumSet<Direction>> ignoredLinks,
592
                        Collection<ConnectionDescriptor> scampConnections,
593
                        Integer maxSDRAMSize)
594
                        throws IOException, SpinnmanException, InterruptedException {
×
595
                this.version = version;
×
596
                if (ignoredChips != null) {
×
597
                        ignoreChips.addAll(ignoredChips);
×
598
                }
599
                if (ignoredCores != null) {
×
600
                        ignoreCores.putAll(ignoredCores);
×
601
                }
602
                if (ignoredLinks != null) {
×
603
                        ignoreLinks.putAll(ignoredLinks);
×
604
                }
605
                this.maxSDRAMSize = maxSDRAMSize;
×
606

607
                if (connections == null) {
×
608
                        // Needs to be modifiable
609
                        connections = new ArrayList<>();
×
610
                }
611
                originalConnections.addAll(connections);
×
612
                allConnections.addAll(connections);
×
613
                // if there has been SCAMP connections given, build them
614
                if (scampConnections != null) {
×
615
                        for (ConnectionDescriptor desc : scampConnections) {
×
616
                                if (desc.portNumber != null
×
617
                                                && desc.portNumber != SCP_SCAMP_PORT) {
×
618
                                        log.warn("ignoring unexpected SCAMP port: {}",
×
619
                                                        desc.portNumber);
620
                                }
621
                                connections.add(createScpConnection(desc.chip, desc.hostname));
×
622
                        }
×
623
                }
624
                for (Connection conn : connections) {
×
625
                        identifyConnection(conn);
×
626
                }
×
627
                scpSelector = makeConnectionSelector();
×
628
                checkBMPConnections();
×
629
        }
×
630

631
        @Override
632
        public SCPConnection createScpConnection(ChipLocation chip,
633
                        InetAddress addr) throws IOException {
634
                return new SCPConnection(chip, null, null, addr);
×
635
        }
636

637
        private ConnectionSelector<SCPConnection> makeConnectionSelector() {
638
                return new MostDirectConnectionSelector<SCPConnection>(machine,
×
639
                                scpConnections);
640
        }
641

642
        /**
643
         * Work out what is going on with a connection. There are many types of
644
         * connections, and some connections are several different things at once.
645
         *
646
         * @param conn
647
         *            The connection to be handled.
648
         */
649
        private void identifyConnection(Connection conn) {
650
                // locate the only boot send conn
651
                if (conn instanceof BootConnection) {
×
652
                        if (bootConnection != null) {
×
653
                                throw new IllegalArgumentException(
×
654
                                                "Only a single BootSender can be specified");
655
                        }
656
                        bootConnection = (BootConnection) conn;
×
657
                }
658

659
                // Locate any connections listening on a UDP port
660
                if (conn instanceof UDPConnection) {
×
661
                        registerConnection((UDPConnection<?>) conn);
×
662
                }
663

664
                // Locate any connections that can send SDP
665
                if (conn instanceof SDPConnection) {
×
666
                        sdpConnections.add((SDPConnection) conn);
×
667
                }
668

669
                // Locate any connections that can send and receive SCP
670
                // If it is a BMP connection, add it here
671
                if (conn instanceof BMPConnection) {
×
672
                        var bmpc = (BMPConnection) conn;
×
673
                        bmpConnections.add(bmpc);
×
674
                        bmpSelectors.put(bmpc.getCoords(),
×
675
                                        new SingletonConnectionSelector<>(bmpc));
676
                } else if (conn instanceof SCPConnection) {
×
677
                        var scpc = (SCPConnection) conn;
×
678
                        scpConnections.add(scpc);
×
679
                        udpScpConnections.put(scpc.getRemoteIPAddress(), scpc);
×
680
                }
681
        }
×
682

683
        /**
684
         * Get the connections for talking to a board.
685
         *
686
         * @param boardAddress
687
         *            The address of the board to talk to. May be {@code null} to
688
         *            use all connections.
689
         * @return All the connections that could reach the board.
690
         */
691
        private Collection<SCPConnection> getConnectionList(
692
                        InetAddress boardAddress) {
693
                if (boardAddress == null) {
×
694
                        return scpConnections;
×
695
                }
696
                var connection = locateSpinnakerConnection(boardAddress);
×
697
                if (connection == null) {
×
698
                        return List.of();
×
699
                }
700
                return List.of(connection);
×
701
        }
702

703
        /**
704
         * Get the connections for talking to a board.
705
         *
706
         * @param connection
707
         *            Directly gives the connection to use. May be {@code null} to
708
         *            use defaults.
709
         * @return List of connections that could reach a board.
710
         */
711
        private Collection<SCPConnection> getConnectionList(
712
                        SCPConnection connection) {
713
                if (connection == null) {
×
714
                        return scpConnections;
×
715
                }
716
                return List.of(connection);
×
717
        }
718

719
        @CheckReturnValue
720
        private Object getSystemVariable(HasChipLocation chip,
721
                        SystemVariableDefinition dataItem)
722
                        throws IOException, ProcessException, InterruptedException {
723
                var buffer = readMemory(chip, SYS_VARS.add(dataItem.offset),
×
724
                                dataItem.type.value);
725
                switch (dataItem.type) {
×
726
                case BYTE:
727
                        return Byte.toUnsignedInt(buffer.get());
×
728
                case SHORT:
729
                        return Short.toUnsignedInt(buffer.getShort());
×
730
                case INT:
731
                        return buffer.getInt();
×
732
                case LONG:
733
                        return buffer.getLong();
×
734
                case BYTE_ARRAY:
735
                        byte[] dst = (byte[]) dataItem.getDefault();
×
736
                        buffer.get(dst);
×
737
                        return dst;
×
738
                case ADDRESS:
739
                        return new MemoryLocation(buffer.getInt());
×
740
                default:
741
                        // Unreachable
742
                        throw new IllegalStateException();
×
743
                }
744
        }
745

746
        private ConnectionSelector<BMPConnection> bmpConnection(BMPCoords bmp) {
747
                if (!bmpSelectors.containsKey(bmp)) {
×
748
                        throw new IllegalArgumentException(
×
749
                                        "Unknown combination of cabinet (" + bmp.getCabinet()
×
750
                                                        + ") and frame (" + bmp.getFrame() + ")");
×
751
                }
752
                return bmpSelectors.get(bmp);
×
753
        }
754

755
        private byte getNextNearestNeighbourID() {
756
                synchronized (nearestNeighbourLock) {
×
757
                        int next = (nearestNeighbourID + 1) & NNID_MAX;
×
758
                        nearestNeighbourID = next;
×
759
                        return (byte) next;
×
760
                }
761
        }
762

763
        @Override
764
        public ConnectionSelector<SCPConnection> getScampConnectionSelector() {
765
                return scpSelector;
×
766
        }
767

768
        /**
769
         * Returns the given connection, or else picks one at random.
770
         *
771
         * @param <C>
772
         *            the connection type
773
         * @param conn
774
         *            the connection to use if it is not {@code null}
775
         * @param connections
776
         *            the list of connections to locate a random one from
777
         * @return a connection object
778
         */
779
        private static <C> C useOrRandomConnection(C conn, List<C> connections) {
780
                if (conn != null) {
×
781
                        return conn;
×
782
                }
783
                if (connections.isEmpty()) {
×
784
                        return null;
×
785
                }
786
                int idx = ThreadLocalRandom.current().nextInt(0, connections.size());
×
787
                return connections.get(idx);
×
788
        }
789

790
        /** Check that the BMP connections are actually connected to valid BMPs. */
791
        private void checkBMPConnections()
792
                        throws IOException, SpinnmanException, InterruptedException {
793
                /*
794
                 * Check that the UDP BMP conn is actually connected to a BMP via the
795
                 * SVER command
796
                 */
797
                for (var conn : bmpConnections) {
×
798
                        // try to send a BMP SVER to check if it responds as expected
799
                        try {
800
                                var versionInfo = readBMPVersion(conn.getCoords(), conn.boards);
×
801
                                if (!BMP_NAME.equals(versionInfo.name) || !BMP_MAJOR_VERSIONS
×
802
                                                .contains(versionInfo.versionNumber.majorVersion)) {
×
803
                                        throw new IOException(format(
×
804
                                                        "The BMP at %s is running %s %s which is "
805
                                                                        + "incompatible with this transceiver, "
806
                                                                        + "required version is %s %s",
807
                                                        conn.getRemoteIPAddress(), versionInfo.name,
×
808
                                                        versionInfo.versionString, BMP_NAME,
809
                                                        BMP_MAJOR_VERSIONS));
810
                                }
811

812
                                log.info("Using BMP at {} with version {} {}",
×
813
                                                conn.getRemoteIPAddress(), versionInfo.name,
×
814
                                                versionInfo.versionString);
815
                        } catch (SocketTimeoutException e) {
×
816
                                /*
817
                                 * If it fails to respond due to timeout, maybe that the
818
                                 * connection isn't valid.
819
                                 */
820
                                throw new SpinnmanException(
×
821
                                                format("BMP connection to %s is not responding",
×
822
                                                                conn.getRemoteIPAddress()),
×
823
                                                e);
824
                        } catch (ProcessException e) {
×
825
                                log.error("Failed to speak to BMP at {}",
×
826
                                                conn.getRemoteIPAddress(), e);
×
827
                                throw e;
×
828
                        }
×
829
                }
×
830
        }
×
831

832
        @Override
833
        public void retryNeeded() {
834
                retryCount++;
×
835
        }
×
836

837
        /**
838
         * Check that the given connection to the given chip works.
839
         *
840
         * @param connection
841
         *            the connection to use when doing the check
842
         * @param chip
843
         *            the chip coordinates to try to talk to
844
         * @return True if a valid response is received, False otherwise
845
         * @throws InterruptedException
846
         *             If interrupted while waiting for a response.
847
         */
848
        @CheckReturnValue
849
        private boolean checkConnection(SCPConnection connection,
850
                        HasChipLocation chip) throws InterruptedException {
851
                for (int r = 0; r < CONNECTION_CHECK_RETRY_COUNT; r++) {
×
852
                        try {
853
                                var chipInfo = simpleProcess(connection)
×
854
                                                .retrieve(new GetChipInfo(chip));
×
855
                                if (chipInfo.isEthernetAvailable) {
×
856
                                        return true;
×
857
                                }
858
                                sleep(CONNECTION_CHECK_DELAY);
×
859
                        } catch (SocketTimeoutException | ProcessException e) {
×
860
                                // do nothing
861
                        } catch (IOException e) {
×
862
                                break;
×
863
                        }
×
864
                }
865
                return false;
×
866
        }
867

868
        @Override
869
        public void sendSCPMessage(SCPRequest<?> message, SCPConnection connection)
870
                        throws IOException {
871
                if (message instanceof BMPRequest) {
×
872
                        throw new ClassCastException(
×
873
                                        "BMP messages should not be sent on SCP connections");
874
                }
875
                useOrRandomConnection(connection, scpConnections).send(message);
×
876
        }
×
877

878
        @Override
879
        public void sendSDPMessage(SDPMessage message, SDPConnection connection)
880
                        throws IOException {
881
                useOrRandomConnection(connection, sdpConnections).send(message);
×
882
        }
×
883

884
        /**
885
         * Get the current machine status and store it.
886
         *
887
         * @throws IOException
888
         *             if the OS has networking troubles
889
         * @throws ProcessException
890
         *             if SpiNNaker rejects a message
891
         * @throws InterruptedException
892
         *             If the communications were interrupted.
893
         */
894
        void updateMachine()
895
                        throws IOException, ProcessException, InterruptedException {
896
                // Get the width and height of the machine
897
                getMachineDimensions();
×
898

899
                // Get the coordinates of the boot chip
900
                var versionInfo = getScampVersion();
×
901

902
                // Get the details of all the chips
903
                machine = new GetMachineProcess(scpSelector, ignoreChips, ignoreCores,
×
904
                                ignoreLinks, maxSDRAMSize, this)
905
                                                .getMachineDetails(versionInfo.location, dimensions);
×
906

907
                /*
908
                 * Ask the machine to check itself and if required to rebuild itself
909
                 * with out invalid links or chips etc.
910
                 */
911
                machine = machine.rebuild();
×
912

913
                // update the SCAMP selector with the machine
914
                if (scpSelector instanceof MachineAware) {
×
915
                        ((MachineAware) scpSelector).setMachine(machine);
×
916
                }
917

918
                /*
919
                 * update the SCAMP connections replacing any x and y with the default
920
                 * SCP request params with the boot chip coordinates
921
                 */
922
                for (var sc : scpConnections) {
×
923
                        if (sc.getChip().equals(BOOT_CHIP)) {
×
924
                                sc.setChip(machine.boot);
×
925
                        }
926
                }
×
927

928
                // Work out and add the SpiNNaker links and FPGA links
929
                machine.addSpinnakerLinks();
×
930
                machine.addFpgaLinks();
×
931

932
                // TODO: Actually get the existing APP_IDs in use
933
                appIDTracker = new AppIdTracker();
×
934

935
                if (bootConnection != null) {
×
936
                        log.info("Detected a machine on IP address {} which has {}",
×
937
                                        bootConnection.getRemoteIPAddress(),
×
938
                                        machine.coresAndLinkOutputString());
×
939
                }
940
        }
×
941

942
        /**
943
         * Find connections to the board and store these for future use. Note that
944
         * connections can be empty, in which case another local discovery mechanism
945
         * will be used. Note that an exception will be thrown if no initial
946
         * connections can be found to the board.
947
         *
948
         * @return An iterable of discovered connections, not including the
949
         *         initially given connections in the constructor
950
         * @throws IOException
951
         *             if networking fails
952
         * @throws ProcessException
953
         *             If SpiNNaker rejects a message.
954
         * @throws InterruptedException
955
         *             If the communications were interrupted.
956
         */
957
        @CheckReturnValue
958
        @SuppressWarnings("MustBeClosed")
959
        public List<SCPConnection> discoverScampConnections()
960
                        throws IOException, ProcessException, InterruptedException {
961
                /*
962
                 * Currently, this only finds other UDP connections given a connection
963
                 * that supports SCP - this is done via the machine
964
                 */
965
                if (scpConnections.isEmpty()) {
×
966
                        return List.of();
×
967
                }
968

969
                // Get the machine dimensions
970
                var dims = getMachineDimensions();
×
971

972
                // Find all the new connections via the machine Ethernet-connected chips
973
                var newConnections = new ArrayList<SCPConnection>();
×
974
                for (var chip : getSpinn5Geometry().getPotentialRootChips(dims)) {
×
975
                        var ipAddress = getByAddress(
×
976
                                        (byte[]) getSystemVariable(chip, ethernet_ip_address));
×
977
                        if (udpScpConnections.containsKey(ipAddress)) {
×
978
                                continue;
×
979
                        }
980
                        var conn = searchForProxies(chip);
×
981

982
                        // if no data, no proxy
983
                        if (conn == null) {
×
984
                                conn = createScpConnection(chip, ipAddress);
×
985
                        } else {
986
                                // proxy, needs an adjustment
987
                                udpScpConnections.remove(conn.getRemoteIPAddress());
×
988
                        }
989

990
                        // check if it works
991
                        if (checkConnection(conn, chip)) {
×
992
                                allConnections.add(conn);
×
993
                                udpScpConnections.put(ipAddress, conn);
×
994
                                scpConnections.add(conn);
×
995
                                newConnections.add(conn);
×
996
                        } else {
997
                                log.warn(
×
998
                                                "Additional Ethernet connection on {} at "
999
                                                                + "chip {},{} cannot be contacted",
1000
                                                ipAddress, chip.getX(), chip.getY());
×
1001
                        }
1002
                }
×
1003

1004
                // Update the connection queues after finding new connections
1005
                return newConnections;
×
1006
        }
1007

1008
        /**
1009
         * Looks for an entry within the UDP SCAMP connections which is linked to a
1010
         * given chip.
1011
         *
1012
         * @param chip
1013
         *            The coordinates of the chip
1014
         * @return connection or {@code null} if there is no such connection
1015
         */
1016
        private SCPConnection searchForProxies(ChipLocation chip) {
1017
                for (var connection : scpConnections) {
×
1018
                        if (connection.getChip().equals(chip)) {
×
1019
                                return connection;
×
1020
                        }
1021
                }
×
1022
                return null;
×
1023
        }
1024

1025
        /**
1026
         * Get the currently known connections to the board, made up of those passed
1027
         * in to the transceiver and those that are discovered during calls to
1028
         * {@link #discoverScampConnections()}. No further discovery is done here.
1029
         *
1030
         * @return The connections known to the transceiver
1031
         */
1032
        public Set<Connection> getConnections() {
1033
                return unmodifiableSet(allConnections);
×
1034
        }
1035

1036
        @Override
1037
        public MachineDimensions getMachineDimensions()
1038
                        throws IOException, ProcessException, InterruptedException {
1039
                if (dimensions == null) {
×
1040
                        var data = readMemory(BOOT_CHIP, SYS_VARS.add(y_size.offset), 2);
×
1041
                        int height = toUnsignedInt(data.get());
×
1042
                        int width = toUnsignedInt(data.get());
×
1043
                        dimensions = new MachineDimensions(width, height);
×
1044
                }
1045
                return dimensions;
×
1046
        }
1047

1048
        @Override
1049
        public Machine getMachineDetails()
1050
                        throws IOException, ProcessException, InterruptedException {
1051
                if (machine == null) {
×
1052
                        updateMachine();
×
1053
                }
1054
                return machine;
×
1055
        }
1056

1057
        /**
1058
         * @return the application ID tracker for this transceiver.
1059
         * @throws IOException
1060
         *             If anything goes wrong with networking.
1061
         * @throws ProcessException
1062
         *             If SpiNNaker rejects a message.
1063
         * @throws InterruptedException
1064
         *             If the communications were interrupted.
1065
         */
1066
        public AppIdTracker getAppIdTracker()
1067
                        throws IOException, ProcessException, InterruptedException {
1068
                if (appIDTracker == null) {
×
1069
                        updateMachine();
×
1070
                }
1071
                return appIDTracker;
×
1072
        }
1073

1074
        @Override
1075
        public boolean isConnected(Connection connection) {
1076
                if (connection != null) {
×
1077
                        return connectedTest(connection);
×
1078
                }
1079
                return scpConnections.stream().anyMatch(this::connectedTest);
×
1080
        }
1081

1082
        private boolean connectedTest(Connection c) {
1083
                try {
1084
                        return c.isConnected();
×
1085
                } catch (IOException e) {
×
1086
                        return false;
×
1087
                }
1088
        }
1089

1090
        @Override
1091
        @CheckReturnValue
1092
        public VersionInfo getScampVersion(HasChipLocation chip,
1093
                        ConnectionSelector<SCPConnection> connectionSelector)
1094
                        throws IOException, ProcessException, InterruptedException {
1095
                if (connectionSelector == null) {
×
1096
                        connectionSelector = scpSelector;
×
1097
                }
1098
                return simpleProcess(connectionSelector)
×
1099
                                .retrieve(new GetVersion(chip.getScampCore()));
×
1100
        }
1101

1102
        @Override
1103
        @ParallelUnsafe
1104
        public void bootBoard(Map<SystemVariableDefinition, Object> extraBootValues)
1105
                        throws InterruptedException, IOException {
1106
                var bootMessages = new BootMessages(version, extraBootValues);
×
1107
                var msgs = bootMessages.getMessages().iterator();
×
1108
                while (msgs.hasNext()) {
×
1109
                        bootConnection.sendBootMessage(msgs.next());
×
1110
                }
1111
                sleep(POST_BOOT_DELAY);
×
1112
        }
×
1113

1114
        /**
1115
         * Determine if the version of SCAMP is compatible with this transceiver.
1116
         *
1117
         * @param version
1118
         *            The version to test
1119
         * @return true exactly when they are compatible
1120
         */
1121
        public static boolean isScampVersionCompatible(Version version) {
1122
                // The major version must match exactly
1123
                if (version.majorVersion != SCAMP_VERSION.majorVersion) {
×
1124
                        return false;
×
1125
                }
1126

1127
                /*
1128
                 * If the minor version matches, the patch version must be >= the
1129
                 * required version
1130
                 */
1131
                if (version.minorVersion == SCAMP_VERSION.minorVersion) {
×
1132
                        return version.revision >= SCAMP_VERSION.revision;
×
1133
                }
1134

1135
                /*
1136
                 * If the minor version is > than the required version, the patch
1137
                 * version is irrelevant
1138
                 */
1139
                return version.minorVersion > SCAMP_VERSION.minorVersion;
×
1140
        }
1141

1142
        /**
1143
         * A neater way of getting a process for running simple SCP requests.
1144
         *
1145
         * @return The SCP runner process
1146
         */
1147
        private TxrxProcess simpleProcess() {
1148
                return new TxrxProcess(scpSelector, this);
×
1149
        }
1150

1151
        /**
1152
         * A neater way of getting a process for running simple SCP requests.
1153
         *
1154
         * @param selector
1155
         *            The connection selector to use.
1156
         * @return The SCP runner process.
1157
         */
1158
        private TxrxProcess simpleProcess(
1159
                        ConnectionSelector<SCPConnection> selector) {
1160
                return new TxrxProcess(selector, this);
×
1161
        }
1162

1163
        /**
1164
         * A neater way of getting a process for running simple SCP requests against
1165
         * a specific SDP connection. Note that the connection is just SDP, not
1166
         * guaranteed to be SCP; that matters because it is used to set up
1167
         * cross-firewall/NAT routing.
1168
         *
1169
         * @param connector
1170
         *            The specific connector to talk to the board along.
1171
         * @return The SCP runner process
1172
         * @throws IOException
1173
         *             If anything fails (unexpected).
1174
         */
1175
        private TxrxProcess simpleProcess(SDPConnection connector)
1176
                        throws IOException {
1177
                // Avoid delegation of the connection if not needed
1178
                if (connector instanceof SCPConnection) {
×
1179
                        return new TxrxProcess(new SingletonConnectionSelector<>(
×
1180
                                        (SCPConnection) connector), this);
1181
                }
1182
                return new TxrxProcess(new SingletonConnectionSelector<>(
×
1183
                                new DelegatingSCPConnection(connector)), this);
1184
        }
1185

1186
        /**
1187
         * Do a synchronous call of an SCP operation using the default connection
1188
         * for a request, sending the given message and completely processing the
1189
         * interaction. There is no payload in the response, and no result from this
1190
         * method. This can only properly handle those calls that involve a single
1191
         * request and a single reply; fortunately, that's many of them!
1192
         *
1193
         * @param request
1194
         *            The request to make.
1195
         * @throws ProcessException
1196
         *             If SpiNNaker rejects a request.
1197
         * @throws IOException
1198
         *             If anything fails with networking.
1199
         * @throws InterruptedException
1200
         *             If the communications were interrupted.
1201
         */
1202
        protected void call(SCPRequest<EmptyResponse> request)
1203
                        throws ProcessException, IOException, InterruptedException {
1204
                new TxrxProcess(scpSelector, this).call(request);
×
1205
        }
×
1206

1207
        /**
1208
         * Do a synchronous call of an SCP operation using the default connection
1209
         * for a request, sending the given message and completely processing the
1210
         * interaction before returning its response. This can only properly handle
1211
         * those calls that involve a single request and a single reply;
1212
         * fortunately, that's many of them!
1213
         *
1214
         * @param <T>
1215
         *            The type of the payload.
1216
         * @param <R>
1217
         *            The type of the response that the payload is extracted from.
1218
         * @param request
1219
         *            The request to make.
1220
         * @return The successful response to the request.
1221
         * @throws ProcessException
1222
         *             If SpiNNaker rejects a request.
1223
         * @throws IOException
1224
         *             If anything fails with networking.
1225
         * @throws InterruptedException
1226
         *             If the communications were interrupted.
1227
         */
1228
        protected <T, R extends PayloadedResponse<T, ?>> T get(
1229
                        SCPRequest<R> request)
1230
                        throws ProcessException, IOException, InterruptedException {
1231
                return new TxrxProcess(scpSelector, this).retrieve(request);
×
1232
        }
1233

1234
        @Override
1235
        @ParallelUnsafe
1236
        public VersionInfo ensureBoardIsReady(int numRetries,
1237
                        Map<SystemVariableDefinition, Object> extraBootValues)
1238
                        throws IOException, ProcessException, InterruptedException {
1239
                // try to get a SCAMP version once
1240
                log.info("Working out if machine is booted");
×
1241
                VersionInfo versionInfo;
1242
                if (machineOff) {
×
1243
                        versionInfo = null;
×
1244
                } else {
1245
                        versionInfo = findScampAndBoot(INITIAL_FIND_SCAMP_RETRIES_COUNT,
×
1246
                                        extraBootValues);
1247
                }
1248

1249
                // If we fail to get a SCAMP version this time, try other things
1250
                if (versionInfo == null && !bmpConnections.isEmpty()) {
×
1251
                        // start by powering up each BMP connection
1252
                        log.info("Attempting to power on machine");
×
1253
                        powerOnMachine();
×
1254

1255
                        // Sleep a bit to let things get going
1256
                        sleep(POST_POWER_ON_DELAY);
×
1257
                        log.info("Attempting to boot machine");
×
1258

1259
                        // retry to get a SCAMP version, this time trying multiple times
1260
                        versionInfo = findScampAndBoot(numRetries, extraBootValues);
×
1261
                }
1262

1263
                // verify that the version is the expected one for this transceiver
1264
                if (versionInfo == null) {
×
1265
                        throw new IOException("Failed to communicate with the machine");
×
1266
                }
1267
                if (!versionInfo.name.equals(SCAMP_NAME)
×
1268
                                || !isScampVersionCompatible(versionInfo.versionNumber)) {
×
1269
                        throw new IOException(format(
×
1270
                                        "The machine is currently booted with %s %s "
1271
                                                        + "which is incompatible with this transceiver, "
1272
                                                        + "required version is %s %s",
1273
                                        versionInfo.name, versionInfo.versionNumber, SCAMP_NAME,
1274
                                        SCAMP_VERSION));
1275
                }
1276

1277
                log.info("Machine communication successful");
×
1278

1279
                /*
1280
                 * Change the default SCP timeout on the machine, keeping the old one to
1281
                 * revert at close
1282
                 */
1283
                var process = simpleProcess();
×
1284
                for (var connection : scpConnections) {
×
1285
                        // we apparently don't save the timeout...
1286
                        process.retrieve(
×
1287
                                        new IPTagSetTTO(connection.getChip(), TIMEOUT_2560_ms));
×
1288
                }
×
1289

1290
                return versionInfo;
×
1291
        }
1292

1293
        /**
1294
         * Try to detect if SCAMP is running, and if not, boot the machine.
1295
         *
1296
         * @param numAttempts
1297
         *            how many attempts should be supported
1298
         * @param extraBootValues
1299
         *            Any additional values to set during boot
1300
         * @return version info for SCAMP on the booted system
1301
         * @throws IOException
1302
         *             if networking fails
1303
         * @throws ProcessException
1304
         *             If SpiNNaker rejects a message.
1305
         */
1306
        @CheckReturnValue
1307
        private VersionInfo findScampAndBoot(int numAttempts,
1308
                        Map<SystemVariableDefinition, Object> extraBootValues)
1309
                        throws InterruptedException, IOException, ProcessException {
1310
                VersionInfo versionInfo = null;
×
1311
                int triesLeft = numAttempts;
×
1312
                while (versionInfo == null && triesLeft > 0) {
×
1313
                        try {
1314
                                versionInfo = getScampVersion();
×
1315
                                if (versionInfo.location.asChipLocation().equals(BOOT_CHIP)) {
×
1316
                                        versionInfo = null;
×
1317
                                        sleep(CONNECTION_CHECK_DELAY);
×
1318
                                }
1319
                        } catch (ProcessException e) {
×
1320
                                if (e.getCause() instanceof SocketTimeoutException) {
×
1321
                                        log.info("Attempting to boot machine");
×
1322
                                        bootBoard(extraBootValues);
×
1323
                                        triesLeft--;
×
1324
                                } else if (e.getCause() instanceof IOException) {
×
1325
                                        throw new IOException(
×
1326
                                                        "Failed to communicate with the machine", e);
1327
                                } else {
1328
                                        throw e;
×
1329
                                }
1330
                        } catch (SocketTimeoutException e) {
×
1331
                                log.info("Attempting to boot machine");
×
1332
                                bootBoard(extraBootValues);
×
1333
                                triesLeft--;
×
1334
                        } catch (IOException e) {
×
1335
                                throw new IOException("Failed to communicate with the machine",
×
1336
                                                e);
1337
                        }
×
1338
                }
1339

1340
                // The last thing we tried was booting, so try again to get the version
1341
                if (versionInfo == null) {
×
1342
                        versionInfo = getScampVersion();
×
1343
                        if (versionInfo.location.asChipLocation().equals(BOOT_CHIP)) {
×
1344
                                versionInfo = null;
×
1345
                        }
1346
                }
1347
                if (versionInfo != null) {
×
1348
                        log.info("Found board with hardware {} firmware {} version {}",
×
1349
                                        versionInfo.hardware, versionInfo.name,
1350
                                        versionInfo.versionNumber);
1351
                }
1352
                return versionInfo;
×
1353
        }
1354

1355
        private CoreSubsets getAllCores()
1356
                        throws IOException, ProcessException, InterruptedException {
1357
                if (machine == null) {
×
1358
                        updateMachine();
×
1359
                }
1360
                var coreSubsets = new CoreSubsets();
×
1361
                for (var chip : machine.chips()) {
×
1362
                        for (var processor : chip.allProcessors()) {
×
1363
                                coreSubsets.addCore(new CoreLocation(chip.getX(), chip.getY(),
×
1364
                                                processor.processorId));
1365
                        }
×
1366
                }
×
1367
                return coreSubsets;
×
1368
        }
1369

1370
        @Override
1371
        @CheckReturnValue
1372
        @ParallelSafeWithCare
1373
        public MappableIterable<CPUInfo> getCPUInformation(CoreSubsets coreSubsets)
1374
                        throws IOException, ProcessException, InterruptedException {
1375
                // Get all the cores if the subsets are not given
1376
                if (coreSubsets == null) {
×
1377
                        coreSubsets = getAllCores();
×
1378
                }
1379

1380
                return new GetCPUInfoProcess(scpSelector, this).getCPUInfo(coreSubsets);
×
1381
        }
1382

1383
        @Override
1384
        @CheckReturnValue
1385
        @ParallelSafeWithCare
1386
        public MappableIterable<IOBuffer> getIobuf(CoreSubsets coreSubsets)
1387
                        throws IOException, ProcessException, InterruptedException {
1388
                // making the assumption that all chips have the same iobuf size.
1389
                if (iobufSize == null) {
×
1390
                        iobufSize = (Integer) getSystemVariable(BOOT_CHIP, iobuf_size);
×
1391
                }
1392

1393
                // Get all the cores if the subsets are not given
1394
                if (coreSubsets == null) {
×
1395
                        coreSubsets = getAllCores();
×
1396
                }
1397

1398
                // read iobuf from machine
1399
                return new RuntimeControlProcess(scpSelector, this).readIOBuf(iobufSize,
×
1400
                                coreSubsets);
1401
        }
1402

1403
        @Override
1404
        @ParallelSafeWithCare
1405
        public void clearIobuf(CoreSubsets coreSubsets)
1406
                        throws IOException, ProcessException, InterruptedException {
1407
                // Get all the cores if the subsets are not given
1408
                if (coreSubsets == null) {
×
1409
                        coreSubsets = getAllCores();
×
1410
                }
1411

1412
                // read iobuf from machine
1413
                new RuntimeControlProcess(scpSelector, this).clearIOBUF(coreSubsets);
×
1414
        }
×
1415

1416
        @Override
1417
        @ParallelSafeWithCare
1418
        public void updateRuntime(Integer runTimesteps, int currentTime,
1419
                        int syncTimesteps, CoreSubsets coreSubsets)
1420
                        throws IOException, ProcessException, InterruptedException {
1421
                // Get all the cores if the subsets are not given
1422
                if (coreSubsets == null) {
×
1423
                        coreSubsets = getAllCores();
×
1424
                }
1425

1426
                // set the information
1427
                new RuntimeControlProcess(scpSelector, this).updateRuntime(runTimesteps,
×
1428
                                currentTime, syncTimesteps, coreSubsets);
1429
        }
×
1430

1431
        @Override
1432
        @ParallelSafeWithCare
1433
        public void updateProvenanceAndExit(CoreSubsets coreSubsets)
1434
                        throws IOException, ProcessException, InterruptedException {
1435
                // Get all the cores if the subsets are not given
1436
                if (coreSubsets == null) {
×
1437
                        coreSubsets = getAllCores();
×
1438
                }
1439

1440
                // set the information
1441
                new RuntimeControlProcess(scpSelector, this)
×
1442
                                .updateProvenanceAndExit(coreSubsets);
×
1443
        }
×
1444

1445
        private static ByteBuffer oneByte(int value) {
1446
                var data = allocate(1);
×
1447
                data.put((byte) value).flip();
×
1448
                return data;
×
1449
        }
1450

1451
        @Override
1452
        @ParallelSafe
1453
        public void setWatchDogTimeoutOnChip(HasChipLocation chip, int watchdog)
1454
                        throws IOException, ProcessException, InterruptedException {
1455
                // write data
1456
                writeMemory(chip, SYS_VARS.add(software_watchdog_count.offset),
×
1457
                                oneByte(watchdog));
×
1458
        }
×
1459

1460
        @Override
1461
        @ParallelSafe
1462
        public void enableWatchDogTimerOnChip(HasChipLocation chip,
1463
                        boolean watchdog)
1464
                        throws IOException, ProcessException, InterruptedException {
1465
                // write data
1466
                writeMemory(chip, SYS_VARS.add(software_watchdog_count.offset), oneByte(
×
1467
                                watchdog ? (Integer) software_watchdog_count.getDefault() : 0));
×
1468
        }
×
1469

1470
        @Override
1471
        @CheckReturnValue
1472
        @ParallelUnsafe
1473
        public int getCoreStateCount(AppID appID, CPUState state)
1474
                        throws IOException, ProcessException, InterruptedException {
1475
                return get(new CountState(appID, state));
×
1476
        }
1477

1478
        /**
1479
         * The guardian of the flood lock. Also the lock around that piece of global
1480
         * state.
1481
         *
1482
         * @see ExecuteLock
1483
         * @author Donal Fellows
1484
         */
1485
        private static final class FloodLock {
×
1486
                private int count = 0;
×
1487

1488
                /**
1489
                 * Wait for the system to be ready to perform a flood fill. Must only
1490
                 * ever be called with this object already locked.
1491
                 *
1492
                 * @throws InterruptedException
1493
                 *             If the wait is interrupted.
1494
                 */
1495
                void waitForReady() throws InterruptedException {
1496
                        while (count > 0) {
×
1497
                                wait();
×
1498
                        }
1499
                }
×
1500

1501
                /**
1502
                 * Increment the lock counter. Must only ever be called with this object
1503
                 * already locked.
1504
                 */
1505
                void increment() {
1506
                        count++;
×
1507
                }
×
1508

1509
                /**
1510
                 * Decrement the lock counter. Must only ever be called with this object
1511
                 * already locked.
1512
                 */
1513
                void decrement() {
1514
                        count--;
×
1515
                        notifyAll();
×
1516
                }
×
1517
        }
1518

1519
        /**
1520
         * Helper class that makes lock management for application launch a lot
1521
         * easier.
1522
         *
1523
         * @see FloodLock
1524
         * @author Donal Fellows
1525
         */
1526
        private final class ExecuteLock implements AutoCloseable {
1527
                private final Semaphore lock;
1528

1529
                /**
1530
                 * Acquire the lock associated with a particular chip.
1531
                 *
1532
                 * @param chip
1533
                 *            The chip we're talking about.
1534
                 * @throws InterruptedException
1535
                 *             If any waits to acquire locks are interrupted.
1536
                 */
1537
                @MustBeClosed
1538
                ExecuteLock(HasChipLocation chip) throws InterruptedException {
×
1539
                        var key = chip.asChipLocation();
×
1540
                        synchronized (executeFloodLock) {
×
1541
                                lock = chipExecuteLocks.computeIfAbsent(key,
×
1542
                                                __ -> new Semaphore(1));
×
1543
                        }
×
1544
                        lock.acquire();
×
1545
                        synchronized (executeFloodLock) {
×
1546
                                executeFloodLock.increment();
×
1547
                        }
×
1548
                }
×
1549

1550
                /**
1551
                 * Release the lock associated with a particular chip.
1552
                 */
1553
                @Override
1554
                public void close() {
1555
                        synchronized (executeFloodLock) {
×
1556
                                lock.release();
×
1557
                                executeFloodLock.decrement();
×
1558
                        }
×
1559
                }
×
1560
        }
1561

1562
        @Override
1563
        @ParallelSafe
1564
        public void execute(HasChipLocation chip, Collection<Integer> processors,
1565
                        InputStream executable, int numBytes, AppID appID, boolean wait)
1566
                        throws IOException, ProcessException, InterruptedException {
1567
                // Lock against updates
1568
                try (var lock = new ExecuteLock(chip)) {
×
1569
                        // Write the executable
1570
                        writeMemory(chip, EXECUTABLE_ADDRESS, executable, numBytes);
×
1571

1572
                        // Request the start of the executable
1573
                        call(new ApplicationRun(appID, chip, processors, wait));
×
1574
                }
1575
        }
×
1576

1577
        @Override
1578
        @ParallelSafe
1579
        public final void execute(HasChipLocation chip,
1580
                        Collection<Integer> processors, File executable, AppID appID,
1581
                        boolean wait)
1582
                        throws IOException, ProcessException, InterruptedException {
1583
                // Lock against updates
1584
                try (var lock = new ExecuteLock(chip)) {
×
1585
                        // Write the executable
1586
                        writeMemory(chip, EXECUTABLE_ADDRESS, executable);
×
1587

1588
                        // Request the start of the executable
1589
                        call(new ApplicationRun(appID, chip, processors, wait));
×
1590
                }
1591
        }
×
1592

1593
        @Override
1594
        @ParallelSafe
1595
        public void execute(HasChipLocation chip, Collection<Integer> processors,
1596
                        ByteBuffer executable, AppID appID, boolean wait)
1597
                        throws IOException, ProcessException, InterruptedException {
1598
                // Lock against updates
1599
                try (var lock = new ExecuteLock(chip)) {
×
1600
                        // Write the executable
1601
                        writeMemory(chip, EXECUTABLE_ADDRESS, executable);
×
1602

1603
                        // Request the start of the executable
1604
                        call(new ApplicationRun(appID, chip, processors, wait));
×
1605
                }
1606
        }
×
1607

1608
        @Override
1609
        @ParallelSafeWithCare
1610
        public void executeFlood(CoreSubsets coreSubsets, InputStream executable,
1611
                        int numBytes, AppID appID, boolean wait)
1612
                        throws IOException, ProcessException, InterruptedException {
1613
                // Lock against other executables
1614
                synchronized (executeFloodLock) {
×
1615
                        executeFloodLock.waitForReady();
×
1616

1617
                        // Flood fill the system with the binary
1618
                        writeMemoryFlood(EXECUTABLE_ADDRESS, executable, numBytes);
×
1619

1620
                        // Execute the binary on the cores on the chips where required
1621
                        new ApplicationRunProcess(scpSelector, this).run(appID, coreSubsets,
×
1622
                                        wait);
1623
                }
×
1624
        }
×
1625

1626
        @Override
1627
        @ParallelSafeWithCare
1628
        public void executeFlood(CoreSubsets coreSubsets, File executable,
1629
                        AppID appID, boolean wait)
1630
                        throws IOException, ProcessException, InterruptedException {
1631
                // Lock against other executables
1632
                synchronized (executeFloodLock) {
×
1633
                        executeFloodLock.waitForReady();
×
1634

1635
                        // Flood fill the system with the binary
1636
                        writeMemoryFlood(EXECUTABLE_ADDRESS, executable);
×
1637

1638
                        // Execute the binary on the cores on the chips where required
1639
                        new ApplicationRunProcess(scpSelector, this).run(appID, coreSubsets,
×
1640
                                        wait);
1641
                }
×
1642
        }
×
1643

1644
        @Override
1645
        @ParallelSafeWithCare
1646
        public void executeFlood(CoreSubsets coreSubsets, ByteBuffer executable,
1647
                        AppID appID, boolean wait)
1648
                        throws IOException, ProcessException, InterruptedException {
1649
                // Lock against other executables
1650
                synchronized (executeFloodLock) {
×
1651
                        executeFloodLock.waitForReady();
×
1652

1653
                        // Flood fill the system with the binary
1654
                        writeMemoryFlood(EXECUTABLE_ADDRESS, executable);
×
1655

1656
                        // Execute the binary on the cores on the chips where required
1657
                        new ApplicationRunProcess(scpSelector, this).run(appID, coreSubsets,
×
1658
                                        wait);
1659
                }
×
1660
        }
×
1661

1662
        /**
1663
         * Call a BMP operation on a BMP.
1664
         *
1665
         * @param <T>
1666
         *            The type of the response.
1667
         * @param bmp
1668
         *            The BMP to call.
1669
         * @param request
1670
         *            The request to make.
1671
         * @return The response from the request.
1672
         * @throws IOException
1673
         *             If networking fails.
1674
         * @throws ProcessException
1675
         *             If the BMP rejects the message.
1676
         * @throws InterruptedException
1677
         *             If the thread is interrupted.
1678
         */
1679
        private <T extends BMPRequest.BMPResponse> T call(BMPCoords bmp,
1680
                        BMPRequest<T> request)
1681
                        throws IOException, ProcessException, InterruptedException {
1682
                return new BMPCommandProcess(bmpConnection(bmp), this).execute(request);
×
1683
        }
1684

1685
        /**
1686
         * Call a BMP operation on a BMP.
1687
         *
1688
         * @param <T>
1689
         *            The type of the response.
1690
         * @param bmp
1691
         *            The BMP to call.
1692
         * @param timeout
1693
         *            The timeout, in milliseconds.
1694
         * @param retries
1695
         *            The number of times to retry the call on a transient failure.
1696
         * @param request
1697
         *            The request to make.
1698
         * @return The response from the request.
1699
         * @throws IOException
1700
         *             If networking fails.
1701
         * @throws ProcessException
1702
         *             If the BMP rejects the message.
1703
         * @throws InterruptedException
1704
         *             If the thread is interrupted.
1705
         */
1706
        private <T extends BMPRequest.BMPResponse> T call(BMPCoords bmp,
1707
                        int timeout, int retries, BMPRequest<T> request)
1708
                                        throws IOException, ProcessException, InterruptedException {
1709
                return new BMPCommandProcess(bmpConnection(bmp), timeout, this)
×
1710
                                .execute(request, retries);
×
1711
        }
1712

1713
        /**
1714
         * Call a BMP operation on a BMP and return the parsed payload of the
1715
         * response.
1716
         *
1717
         * @param <T>
1718
         *            The type of the parsed payload.
1719
         * @param <R>
1720
         *            The type of the response.
1721
         * @param bmp
1722
         *            The BMP to call.
1723
         * @param request
1724
         *            The request to make.
1725
         * @return The response from the request.
1726
         * @throws IOException
1727
         *             If networking fails.
1728
         * @throws ProcessException
1729
         *             If the BMP rejects the message.
1730
         * @throws InterruptedException
1731
         *             If the thread is interrupted.
1732
         */
1733
        private <T, R extends BMPRequest.PayloadedResponse<T>> T get(BMPCoords bmp,
1734
                        BMPRequest<R> request)
1735
                        throws IOException, ProcessException, InterruptedException {
1736
                return new BMPCommandProcess(bmpConnection(bmp), this).call(request);
×
1737
        }
1738

1739
        /**
1740
         * Call a BMP operation on a BMP and return the parsed payload of the
1741
         * response.
1742
         *
1743
         * @param <T>
1744
         *            The type of the parsed payload.
1745
         * @param <R>
1746
         *            The type of the response.
1747
         * @param bmp
1748
         *            The BMP to call.
1749
         * @param timeout
1750
         *            The timeout, in milliseconds.
1751
         * @param retries
1752
         *            The number of times to retry the call on a transient failure.
1753
         * @param request
1754
         *            The request to make.
1755
         * @return The response from the request.
1756
         * @throws IOException
1757
         *             If networking fails.
1758
         * @throws ProcessException
1759
         *             If the BMP rejects the message.
1760
         * @throws InterruptedException
1761
         *             If the thread is interrupted.
1762
         */
1763
        private <T, R extends BMPRequest.PayloadedResponse<T>> T get(BMPCoords bmp,
1764
                        int timeout, int retries, BMPRequest<R> request)
1765
                        throws IOException, ProcessException, InterruptedException {
1766
                return new BMPCommandProcess(bmpConnection(bmp), timeout, this)
×
1767
                                .execute(request, retries).get();
×
1768
        }
1769

1770
        @Override
1771
        @ParallelUnsafe
1772
        public void powerOnMachine()
1773
                        throws InterruptedException, IOException, ProcessException {
1774
                if (bmpConnections.isEmpty()) {
×
1775
                        log.warn("No BMP connections, so can't power on");
×
1776
                }
1777
                for (var connection : bmpConnections) {
×
1778
                        power(POWER_ON, connection.getCoords(), connection.boards);
×
1779
                }
×
1780
        }
×
1781

1782
        @Override
1783
        @ParallelUnsafe
1784
        public void powerOffMachine()
1785
                        throws InterruptedException, IOException, ProcessException {
1786
                if (bmpConnections.isEmpty()) {
×
1787
                        log.warn("No BMP connections, so can't power off");
×
1788
                }
1789
                for (var connection : bmpConnections) {
×
1790
                        power(POWER_OFF, connection.getCoords(), connection.boards);
×
1791
                }
×
1792
        }
×
1793

1794
        @Override
1795
        @ParallelUnsafe
1796
        public void power(PowerCommand powerCommand, BMPCoords bmp,
1797
                        Collection<BMPBoard> boards)
1798
                        throws InterruptedException, IOException, ProcessException {
1799
                int timeout = (int) (MSEC_PER_SEC
×
1800
                                * (powerCommand == POWER_ON ? BMP_POWER_ON_TIMEOUT
×
1801
                                                : BMP_TIMEOUT));
×
1802
                requireNonNull(call(bmp, timeout, 0,
×
1803
                                new SetPower(powerCommand, boards, 0.0)));
1804
                machineOff = powerCommand == POWER_OFF;
×
1805

1806
                // Sleep for 5 seconds if the machine has just been powered on
1807
                if (!machineOff) {
×
1808
                        sleep((int) (BMP_POST_POWER_ON_SLEEP_TIME * MSEC_PER_SEC));
×
1809
                }
1810
        }
×
1811

1812
        @Override
1813
        @ParallelUnsafe
1814
        public void setLED(Collection<Integer> leds, LEDAction action,
1815
                        BMPCoords bmp, Collection<BMPBoard> board)
1816
                        throws IOException, ProcessException, InterruptedException {
1817
                call(bmp, new BMPSetLED(leds, action, board));
×
1818
        }
×
1819

1820
        @Override
1821
        @CheckReturnValue
1822
        @ParallelUnsafe
1823
        public int readFPGARegister(FPGA fpga, MemoryLocation register,
1824
                        BMPCoords bmp, BMPBoard board)
1825
                        throws IOException, ProcessException, InterruptedException {
1826
                return get(bmp, new ReadFPGARegister(fpga, register, board));
×
1827
        }
1828

1829
        @Override
1830
        @ParallelUnsafe
1831
        public void writeFPGARegister(FPGA fpga, MemoryLocation register, int value,
1832
                        BMPCoords bmp, BMPBoard board)
1833
                        throws IOException, ProcessException, InterruptedException {
1834
                call(bmp, new WriteFPGARegister(fpga, register, value, board));
×
1835
        }
×
1836

1837
        @Override
1838
        @CheckReturnValue
1839
        @ParallelUnsafe
1840
        public ADCInfo readADCData(BMPCoords bmp, BMPBoard board)
1841
                        throws IOException, ProcessException, InterruptedException {
1842
                return get(bmp, new ReadADC(board));
×
1843
        }
1844

1845
        @Override
1846
        @CheckReturnValue
1847
        @ParallelUnsafe
1848
        public VersionInfo readBMPVersion(BMPCoords bmp, BMPBoard board)
1849
                        throws IOException, ProcessException, InterruptedException {
1850
                return get(bmp, new GetBMPVersion(board));
×
1851
        }
1852

1853
        @Override
1854
        @CheckReturnValue
1855
        @ParallelSafeWithCare
1856
        public ByteBuffer readBMPMemory(BMPCoords bmp, BMPBoard board,
1857
                        MemoryLocation baseAddress, int length)
1858
                        throws ProcessException, IOException, InterruptedException {
1859
                return new BMPReadMemoryProcess(bmpConnection(bmp), this).read(board,
×
1860
                                baseAddress, length);
1861
        }
1862

1863
        @Override
1864
        public void writeBMPMemory(BMPCoords bmp, BMPBoard board,
1865
                        MemoryLocation baseAddress, ByteBuffer data)
1866
                        throws IOException, ProcessException, InterruptedException {
1867
                new BMPWriteMemoryProcess(bmpConnection(bmp), this).writeMemory(board,
×
1868
                                baseAddress, data);
1869
        }
×
1870

1871
        @Override
1872
        public void writeBMPMemory(BMPCoords bmp, BMPBoard board,
1873
                        MemoryLocation baseAddress, File file)
1874
                        throws IOException, ProcessException, InterruptedException {
1875
                var wmp = new BMPWriteMemoryProcess(bmpConnection(bmp), this);
×
1876
                try (var f = buffer(new FileInputStream(file))) {
×
1877
                        // The file had better fit...
1878
                        wmp.writeMemory(board, baseAddress, f, (int) file.length());
×
1879
                }
1880
        }
×
1881

1882
        @Override
1883
        public MemoryLocation getSerialFlashBuffer(BMPCoords bmp, BMPBoard board)
1884
                        throws IOException, ProcessException, InterruptedException {
1885
                return get(bmp, new ReadSerialVector(board)).getFlashBuffer();
×
1886
        }
1887

1888
        @Override
1889
        public String readBoardSerialNumber(BMPCoords bmp, BMPBoard board)
1890
                        throws IOException, ProcessException, InterruptedException {
1891
                var serialNumber = new int[SERIAL_LENGTH];
×
1892
                get(bmp, new ReadSerialVector(board)).getSerialNumber()
×
1893
                                .get(serialNumber);
×
1894
                return format("%08x-%08x-%08x-%08x",
×
1895
                                stream(serialNumber).mapToObj(Integer::valueOf).toArray());
×
1896
        }
1897

1898
        @Override
1899
        @CheckReturnValue
1900
        public ByteBuffer readSerialFlash(BMPCoords bmp, BMPBoard board,
1901
                        MemoryLocation baseAddress, int length)
1902
                        throws IOException, ProcessException, InterruptedException {
1903
                return new BMPReadSerialFlashProcess(bmpConnection(bmp), this)
×
1904
                                .read(board, baseAddress, length);
×
1905
        }
1906

1907
        // CRC calculations of megabytes can take a bit
1908
        private static final int CRC_TIMEOUT = 2000;
1909

1910
        @Override
1911
        @CheckReturnValue
1912
        public int readSerialFlashCRC(BMPCoords bmp, BMPBoard board,
1913
                        MemoryLocation address, int length)
1914
                        throws IOException, ProcessException, InterruptedException {
1915
                return get(bmp, CRC_TIMEOUT, BMP_RETRIES /* =default */,
×
1916
                                new ReadSerialFlashCRC(board, address, length));
1917
        }
1918

1919
        @Override
1920
        public void writeSerialFlash(BMPCoords bmp, BMPBoard board,
1921
                        MemoryLocation baseAddress, ByteBuffer data)
1922
                        throws ProcessException, IOException, InterruptedException {
1923
                new BMPWriteSerialFlashProcess(bmpConnection(bmp), this).write(board,
×
1924
                                baseAddress, data);
1925
        }
×
1926

1927
        @Override
1928
        public void writeSerialFlash(BMPCoords bmp, BMPBoard board,
1929
                        MemoryLocation baseAddress, int size, InputStream stream)
1930
                        throws ProcessException, IOException, InterruptedException {
1931
                new BMPWriteSerialFlashProcess(bmpConnection(bmp), this).write(board,
×
1932
                                baseAddress, stream, size);
1933
        }
×
1934

1935
        @Override
1936
        public void writeSerialFlash(BMPCoords bmp, BMPBoard board,
1937
                        MemoryLocation baseAddress, File file)
1938
                        throws ProcessException, IOException, InterruptedException {
1939
                try (var f = buffer(new FileInputStream(file))) {
×
1940
                        // The file had better fit...
1941
                        new BMPWriteSerialFlashProcess(bmpConnection(bmp), this)
×
1942
                                        .write(board, baseAddress, f, (int) file.length());
×
1943
                }
1944
        }
×
1945

1946
        @Override
1947
        public void writeBMPFlash(BMPCoords bmp, BMPBoard board,
1948
                        MemoryLocation address)
1949
                        throws IOException, ProcessException, InterruptedException {
1950
                call(bmp, new WriteFlashBuffer(board, address, true));
×
1951
        }
×
1952

1953
        @Override
1954
        public void writeFlash(@Valid BMPCoords bmp, @Valid BMPBoard board,
1955
                        @NotNull MemoryLocation baseAddress, @NotNull ByteBuffer data)
1956
                        throws ProcessException, IOException, InterruptedException {
1957
                if (!data.hasRemaining()) {
×
1958
                        // Zero length write?
1959
                        log.warn("zero length write to flash ignored");
×
1960
                        return;
×
1961
                }
1962

1963
                var serialVector = get(bmp, new ReadSerialVector(board));
×
1964
                var workingBuffer = serialVector.getFlashBuffer();
×
1965
                var targetAddr = baseAddress;
×
1966
                for (var buf : sliceUp(data, FLASH_CHUNK_SIZE)) {
×
1967
                        writeBMPMemory(bmp, board, workingBuffer, buf);
×
1968
                        call(bmp, new WriteFlashBuffer(board, targetAddr, true));
×
1969
                        targetAddr = targetAddr.add(FLASH_CHUNK_SIZE);
×
1970
                }
×
1971
        }
×
1972

1973
        @Override
1974
        @ParallelSafe
1975
        public boolean getResetStatus(BMPCoords bmp, BMPBoard board)
1976
                        throws IOException, ProcessException, InterruptedException {
1977
                return get(bmp, new GetFPGAResetStatus(board));
×
1978
        }
1979

1980
        @Override
1981
        @ParallelSafe
1982
        public void resetFPGA(BMPCoords bmp, BMPBoard board,
1983
                        FPGAResetType resetType)
1984
                        throws IOException, ProcessException, InterruptedException {
1985
                call(bmp, new ResetFPGA(board, resetType));
×
1986
        }
×
1987

1988
        @Override
1989
        @CheckReturnValue
1990
        public MappableIterable<BMPBoard> availableBoards(BMPCoords bmp)
1991
                        throws IOException, ProcessException, InterruptedException {
1992
                return get(bmp, new ReadCANStatus());
×
1993
        }
1994

1995
        private WriteMemoryProcess writeProcess(long size) {
1996
                if (size > LARGE_DATA_WRITE_THRESHOLD) {
×
1997
                        /*
1998
                         * If there's more than a (tunable) threshold of data to move, we
1999
                         * limit the number of messages in flight when doing uploads so that
2000
                         * we don't overload SCAMP. Overloading SCAMP *really* slows things
2001
                         * down!
2002
                         */
2003
                        return new WriteMemoryProcess(scpSelector,
×
2004
                                        LARGE_WRITE_PARALLEL_MESSAGE_COUNT, this);
2005
                }
2006
                return new WriteMemoryProcess(scpSelector, this);
×
2007
        }
2008

2009
        @Override
2010
        @ParallelSafe
2011
        public void writeMemory(HasCoreLocation core, MemoryLocation baseAddress,
2012
                        InputStream dataStream, int numBytes)
2013
                        throws IOException, ProcessException, InterruptedException {
2014
                writeProcess(numBytes).writeMemory(core, baseAddress, dataStream,
×
2015
                                numBytes);
2016
        }
×
2017

2018
        @Override
2019
        @ParallelSafe
2020
        public void writeMemory(HasCoreLocation core, MemoryLocation baseAddress,
2021
                        File dataFile)
2022
                        throws IOException, ProcessException, InterruptedException {
2023
                writeProcess(dataFile.length()).writeMemory(core, baseAddress,
×
2024
                                dataFile);
2025
        }
×
2026

2027
        @Override
2028
        @ParallelSafe
2029
        public void writeMemory(HasCoreLocation core, MemoryLocation baseAddress,
2030
                        ByteBuffer data)
2031
                        throws IOException, ProcessException, InterruptedException {
2032
                writeProcess(data.remaining()).writeMemory(core, baseAddress, data);
×
2033
        }
×
2034

2035
        @Override
2036
        @ParallelUnsafe
2037
        public void writeNeighbourMemory(HasCoreLocation core, Direction link,
2038
                        MemoryLocation baseAddress, InputStream dataStream, int numBytes)
2039
                        throws IOException, ProcessException, InterruptedException {
2040
                writeProcess(numBytes).writeLink(core, link, baseAddress, dataStream,
×
2041
                                numBytes);
2042
        }
×
2043

2044
        @Override
2045
        @ParallelUnsafe
2046
        public void writeNeighbourMemory(HasCoreLocation core, Direction link,
2047
                        MemoryLocation baseAddress, File dataFile)
2048
                        throws IOException, ProcessException, InterruptedException {
2049
                writeProcess(dataFile.length()).writeLink(core, link, baseAddress,
×
2050
                                dataFile);
2051
        }
×
2052

2053
        @Override
2054
        @ParallelUnsafe
2055
        public void writeNeighbourMemory(HasCoreLocation core, Direction link,
2056
                        MemoryLocation baseAddress, ByteBuffer data)
2057
                        throws IOException, ProcessException, InterruptedException {
2058
                writeProcess(data.remaining()).writeLink(core, link, baseAddress, data);
×
2059
        }
×
2060

2061
        @Override
2062
        @ParallelUnsafe
2063
        public void writeMemoryFlood(MemoryLocation baseAddress,
2064
                        InputStream dataStream, int numBytes)
2065
                        throws IOException, ProcessException, InterruptedException {
2066
                var process = new WriteMemoryFloodProcess(scpSelector, this);
×
2067
                // Ensure only one flood fill occurs at any one time
2068
                synchronized (floodWriteLock) {
×
2069
                        // Start the flood fill
2070
                        process.writeMemory(getNextNearestNeighbourID(), baseAddress,
×
2071
                                        dataStream, numBytes);
2072
                }
×
2073
        }
×
2074

2075
        @Override
2076
        @ParallelUnsafe
2077
        public void writeMemoryFlood(MemoryLocation baseAddress, File dataFile)
2078
                        throws IOException, ProcessException, InterruptedException {
2079
                var process = new WriteMemoryFloodProcess(scpSelector, this);
×
2080
                // Ensure only one flood fill occurs at any one time
2081
                synchronized (floodWriteLock) {
×
2082
                        // Start the flood fill
2083
                        process.writeMemory(getNextNearestNeighbourID(), baseAddress,
×
2084
                                        dataFile);
2085
                }
×
2086
        }
×
2087

2088
        @Override
2089
        @ParallelUnsafe
2090
        public void writeMemoryFlood(MemoryLocation baseAddress, ByteBuffer data)
2091
                        throws IOException, ProcessException, InterruptedException {
2092
                var process = new WriteMemoryFloodProcess(scpSelector, this);
×
2093
                // Ensure only one flood fill occurs at any one time
2094
                synchronized (floodWriteLock) {
×
2095
                        // Start the flood fill
2096
                        process.writeMemory(getNextNearestNeighbourID(), baseAddress, data);
×
2097
                }
×
2098
        }
×
2099

2100
        @Override
2101
        @CheckReturnValue
2102
        @ParallelSafe
2103
        public ByteBuffer readMemory(HasCoreLocation core,
2104
                        MemoryLocation baseAddress, int length)
2105
                        throws IOException, ProcessException, InterruptedException {
2106
                return new ReadMemoryProcess(scpSelector, this).readMemory(core,
×
2107
                                baseAddress, length);
2108
        }
2109

2110
        @Override
2111
        @ParallelSafe
2112
        public void readRegion(BufferManagerStorage.Region region,
2113
                        BufferManagerStorage storage) throws IOException, ProcessException,
2114
                        StorageException, InterruptedException {
2115
                new ReadMemoryProcess(scpSelector, this).readMemory(region, storage);
×
2116
        }
×
2117

2118
        @Override
2119
        @CheckReturnValue
2120
        @ParallelUnsafe
2121
        public ByteBuffer readNeighbourMemory(HasCoreLocation core, Direction link,
2122
                        MemoryLocation baseAddress, int length)
2123
                        throws IOException, ProcessException, InterruptedException {
2124
                return new ReadMemoryProcess(scpSelector, this).readLink(core, link,
×
2125
                                baseAddress, length);
2126
        }
2127

2128
        @Override
2129
        @ParallelUnsafe
2130
        public void stopApplication(AppID appID)
2131
                        throws IOException, ProcessException, InterruptedException {
2132
                if (machineOff) {
×
2133
                        log.warn("You are calling a app stop on a turned off machine. "
×
2134
                                        + "Please fix and try again");
2135
                        return;
×
2136
                }
2137
                call(new ApplicationStop(appID));
×
2138
        }
×
2139

2140
        @CheckReturnValue
2141
        private boolean inErrorStates(AppID appID, EnumSet<CPUState> errorStates)
2142
                        throws IOException, ProcessException, InterruptedException {
2143
                for (var state : errorStates) {
×
2144
                        if (getCoreStateCount(appID, state) > 0) {
×
2145
                                return true;
×
2146
                        }
2147
                }
×
2148
                return false;
×
2149
        }
2150

2151
        @Override
2152
        @ParallelSafeWithCare
2153
        public void waitForCoresToBeInState(CoreSubsets allCoreSubsets, AppID appID,
2154
                        EnumSet<CPUState> cpuStates, Integer timeout, int timeBetweenPolls,
2155
                        EnumSet<CPUState> errorStates, int countBetweenFullChecks)
2156
                        throws IOException, InterruptedException, SpinnmanException {
2157
                // check that the right number of processors are in the states
2158
                int processorsReady = 0;
×
2159
                long timeoutTime =
2160
                                currentTimeMillis() + (timeout == null ? 0 : timeout);
×
2161
                int tries = 0;
×
2162
                while (processorsReady < allCoreSubsets.size()
×
2163
                                && (timeout == null || currentTimeMillis() < timeoutTime)) {
×
2164
                        // Get the number of processors in the ready states
2165
                        processorsReady = 0;
×
2166
                        for (var state : cpuStates) {
×
2167
                                processorsReady += getCoreStateCount(appID, state);
×
2168
                        }
×
2169

2170
                        // If the count is too small, check for error states
2171
                        if (processorsReady < allCoreSubsets.size()) {
×
2172
                                if (inErrorStates(appID, errorStates)) {
×
2173
                                        // Small chance that inErrorStates() is wrong
2174
                                        var errorCores =
×
2175
                                                        getCoresInState(allCoreSubsets, errorStates);
×
2176
                                        if (!errorCores.isEmpty()) {
×
2177
                                                throw new CoresNotInStateException(timeout, cpuStates,
×
2178
                                                                errorCores);
2179
                                        }
2180
                                }
2181

2182
                                /*
2183
                                 * If we haven't seen an error, increase the tries, and do a
2184
                                 * full check if required
2185
                                 */
2186
                                if (++tries >= countBetweenFullChecks) {
×
2187
                                        var coresInState =
×
2188
                                                        getCoresInState(allCoreSubsets, cpuStates);
×
2189
                                        processorsReady = coresInState.size();
×
2190
                                        tries = 0;
×
2191
                                }
2192

2193
                                // If we're still not in the correct state, wait a bit
2194
                                if (processorsReady < allCoreSubsets.size()) {
×
2195
                                        sleep(timeBetweenPolls);
×
2196
                                }
2197
                        }
2198
                }
2199

2200
                // If we haven't reached the final state, do a final full check
2201
                if (processorsReady < allCoreSubsets.size()) {
×
2202
                        var coresInState = getCoresInState(allCoreSubsets, cpuStates);
×
2203

2204
                        /*
2205
                         * If we are sure we haven't reached the final state, report a
2206
                         * timeout error
2207
                         */
2208
                        if (coresInState.size() != allCoreSubsets.size()) {
×
2209
                                throw new SocketTimeoutException(
×
2210
                                                format("waiting for cores %s to reach one of %s",
×
2211
                                                                allCoreSubsets, cpuStates));
2212
                        }
2213
                }
2214
        }
×
2215

2216
        @Override
2217
        @ParallelUnsafe
2218
        public void sendSignal(AppID appID, Signal signal)
2219
                        throws IOException, ProcessException, InterruptedException {
2220
                call(new SendSignal(appID, signal));
×
2221
        }
×
2222

2223
        @Override
2224
        @ParallelSafe
2225
        public void setLEDs(HasCoreLocation core, Map<Integer, LEDAction> ledStates)
2226
                        throws IOException, ProcessException, InterruptedException {
2227
                call(new SetLED(core, ledStates));
×
2228
        }
×
2229

2230
        @Override
2231
        @ParallelSafe
2232
        public SCPConnection locateSpinnakerConnection(InetAddress boardAddress) {
2233
                return udpScpConnections.get(boardAddress);
×
2234
        }
2235

2236
        @Override
2237
        @ParallelSafeWithCare
2238
        public void setIPTag(IPTag tag)
2239
                        throws IOException, ProcessException, InterruptedException {
2240
                // Check that the tag has a port assigned
2241
                if (tag.getPort() == null) {
×
2242
                        throw new IllegalArgumentException(
×
2243
                                        "The tag port must have been set");
2244
                }
2245

2246
                /*
2247
                 * Get the connections. If the tag specifies a connection, use that,
2248
                 * otherwise apply the tag to all connections
2249
                 */
2250
                var connections = getConnectionList(tag.getBoardAddress());
×
2251
                if (connections == null || connections.isEmpty()) {
×
2252
                        throw new IllegalArgumentException(
×
2253
                                        "The given board address is not recognised");
2254
                }
2255

2256
                var process = simpleProcess();
×
2257
                for (var connection : connections) {
×
2258
                        // Convert the host string
2259
                        var host = tag.getIPAddress();
×
2260
                        if (host == null || host.isAnyLocalAddress()
×
2261
                                        || host.isLoopbackAddress()) {
×
2262
                                host = connection.getLocalIPAddress();
×
2263
                        }
2264
                        process.call(new IPTagSet(connection.getChip(),
×
2265
                                        host.getAddress(), tag.getPort(), tag.getTag(),
×
2266
                                        tag.isStripSDP(), false));
×
2267
                }
×
2268
        }
×
2269

2270
        @Override
2271
        @ParallelSafeWithCare
2272
        public void setIPTag(IPTag tag, SDPConnection connection)
2273
                        throws IOException, ProcessException, InterruptedException {
2274
                /*
2275
                 * Check that the connection is actually pointing to somewhere we know.
2276
                 */
2277
                var connections = getConnectionList(connection.getRemoteIPAddress());
×
2278
                if (connections == null || connections.isEmpty()) {
×
2279
                        throw new IllegalArgumentException(
×
2280
                                        "The given board address is not recognised");
2281
                }
2282

2283
                var process = simpleProcess(connection);
×
2284
                process.call(new IPTagSet(connection.getChip(), null, 0,
×
2285
                                        tag.getTag(), tag.isStripSDP(), true));
×
2286
        }
×
2287

2288
        @Override
2289
        @ParallelSafeWithCare
2290
        public void setReverseIPTag(ReverseIPTag tag)
2291
                        throws IOException, ProcessException, InterruptedException {
2292
                if (requireNonNull(tag).getPort() == SCP_SCAMP_PORT
×
2293
                                || tag.getPort() == UDP_BOOT_CONNECTION_DEFAULT_PORT) {
×
2294
                        throw new IllegalArgumentException(format(
×
2295
                                        "The port number for the reverse IP tag conflicts with"
2296
                                                        + " the SpiNNaker system ports (%d and %d)",
2297
                                        SCP_SCAMP_PORT, UDP_BOOT_CONNECTION_DEFAULT_PORT));
×
2298
                }
2299

2300
                /*
2301
                 * Get the connections. If the tag specifies a connection, use that,
2302
                 * otherwise apply the tag to all connections
2303
                 */
2304
                var connections = getConnectionList(tag.getBoardAddress());
×
2305
                if (connections == null || connections.isEmpty()) {
×
2306
                        throw new IllegalArgumentException(
×
2307
                                        "The given board address is not recognised");
2308
                }
2309

2310
                var process = simpleProcess();
×
2311
                for (var connection : connections) {
×
2312
                        process.call(new ReverseIPTagSet(connection.getChip(),
×
2313
                                        tag.getDestination(), tag.getPort(), tag.getTag(),
×
2314
                                        tag.getPort()));
×
2315
                }
×
2316
        }
×
2317

2318
        @Override
2319
        @ParallelSafeWithCare
2320
        public void clearIPTag(int tag, InetAddress boardAddress)
2321
                        throws IOException, ProcessException, InterruptedException {
2322
                var process = simpleProcess();
×
2323
                for (var conn : getConnectionList(boardAddress)) {
×
2324
                        process.call(new IPTagClear(conn.getChip(), tag));
×
2325
                }
×
2326
        }
×
2327

2328
        @Override
2329
        @CheckReturnValue
2330
        @ParallelSafeWithCare
2331
        public List<Tag> getTags(SCPConnection connection)
2332
                        throws IOException, ProcessException, InterruptedException {
2333
                var allTags = new ArrayList<Tag>();
×
2334
                var process = new GetTagsProcess(scpSelector, this);
×
2335
                for (var conn : getConnectionList(connection)) {
×
2336
                        allTags.addAll(process.getTags(conn));
×
2337
                }
×
2338
                return allTags;
×
2339
        }
2340

2341
        @Override
2342
        @CheckReturnValue
2343
        @ParallelSafeWithCare
2344
        public Map<Tag, Integer> getTagUsage(SCPConnection connection)
2345
                        throws IOException, ProcessException, InterruptedException {
2346
                var allUsage = new HashMap<Tag, Integer>();
×
2347
                var process = new GetTagsProcess(scpSelector, this);
×
2348
                for (var conn : getConnectionList(connection)) {
×
2349
                        allUsage.putAll(process.getTagUsage(conn));
×
2350
                }
×
2351
                return allUsage;
×
2352
        }
2353

2354
        @Override
2355
        @CheckReturnValue
2356
        @ParallelSafe
2357
        public MemoryLocation mallocSDRAM(HasChipLocation chip, int size,
2358
                        AppID appID, int tag)
2359
                        throws IOException, ProcessException, InterruptedException {
2360
                return get(new SDRAMAlloc(chip, appID, size, tag));
×
2361
        }
2362

2363
        @Override
2364
        @ParallelSafe
2365
        public void freeSDRAM(HasChipLocation chip, MemoryLocation baseAddress)
2366
                        throws IOException, ProcessException, InterruptedException {
2367
                // Ignore the result of this
2368
                get(new SDRAMDeAlloc(chip, baseAddress));
×
2369
        }
×
2370

2371
        @Override
2372
        @ParallelSafe
2373
        public int freeSDRAM(HasChipLocation chip, AppID appID)
2374
                        throws IOException, ProcessException, InterruptedException {
2375
                return get(new SDRAMDeAlloc(chip, appID));
×
2376
        }
2377

2378
        @Override
2379
        @ParallelSafe
2380
        public void loadMulticastRoutes(HasChipLocation chip,
2381
                        Collection<MulticastRoutingEntry> routes, AppID appID)
2382
                        throws IOException, ProcessException, InterruptedException {
2383
                new MulticastRoutesControlProcess(scpSelector, this, this)
×
2384
                                .setRoutes(chip, routes, appID);
×
2385
        }
×
2386

2387
        @Override
2388
        @ParallelSafe
2389
        public void loadFixedRoute(HasChipLocation chip, RoutingEntry fixedRoute,
2390
                        AppID appID)
2391
                        throws IOException, ProcessException, InterruptedException {
2392
                new FixedRouteControlProcess(scpSelector, this).loadFixedRoute(chip,
×
2393
                                fixedRoute, appID);
2394
        }
×
2395

2396
        @Override
2397
        @CheckReturnValue
2398
        @ParallelSafe
2399
        public RoutingEntry readFixedRoute(HasChipLocation chip, AppID appID)
2400
                        throws IOException, ProcessException, InterruptedException {
2401
                return new FixedRouteControlProcess(scpSelector, this)
×
2402
                                .readFixedRoute(chip, appID);
×
2403
        }
2404

2405
        @Override
2406
        @CheckReturnValue
2407
        @ParallelSafe
2408
        public List<MulticastRoutingEntry> getMulticastRoutes(HasChipLocation chip,
2409
                        AppID appID)
2410
                        throws IOException, ProcessException, InterruptedException {
2411
                var address = (MemoryLocation) getSystemVariable(chip,
×
2412
                                router_table_copy_address);
2413
                return new MulticastRoutesControlProcess(scpSelector, this, this)
×
2414
                                .getRoutes(chip, address, appID);
×
2415
        }
2416

2417
        @Override
2418
        @ParallelSafe
2419
        public void clearMulticastRoutes(HasChipLocation chip)
2420
                        throws IOException, ProcessException, InterruptedException {
2421
                call(new RouterClear(chip));
×
2422
        }
×
2423

2424
        @Override
2425
        @CheckReturnValue
2426
        @ParallelSafe
2427
        public RouterDiagnostics getRouterDiagnostics(HasChipLocation chip)
2428
                        throws IOException, ProcessException, InterruptedException {
2429
                return new RouterControlProcess(scpSelector, this)
×
2430
                                .getRouterDiagnostics(chip);
×
2431
        }
2432

2433
        @Override
2434
        @ParallelSafe
2435
        public void setRouterDiagnosticFilter(HasChipLocation chip, int position,
2436
                        DiagnosticFilter diagnosticFilter)
2437
                        throws IOException, ProcessException, InterruptedException {
2438
                if (position < 0 || position > NO_ROUTER_DIAGNOSTIC_FILTERS) {
×
2439
                        throw new IllegalArgumentException(
×
2440
                                        "router filter positions must be between 0 and "
2441
                                                        + NO_ROUTER_DIAGNOSTIC_FILTERS);
2442
                }
2443
                if (position <= ROUTER_DEFAULT_FILTERS_MAX_POSITION) {
×
2444
                        log.warn("You are planning to change a filter which is set by "
×
2445
                                        + "default. By doing this, other runs occurring on this "
2446
                                        + "machine will be forced to use this new configuration "
2447
                                        + "until the machine is reset. Please also note that "
2448
                                        + "these changes will make the the reports from ybug not "
2449
                                        + "correct. This has been executed and is trusted that "
2450
                                        + "the end user knows what they are doing.");
2451
                }
2452

2453
                var address =
×
2454
                                ROUTER_FILTERS.add(position * ROUTER_DIAGNOSTIC_FILTER_SIZE);
×
2455
                writeMemory(chip, address, diagnosticFilter.getFilterWord());
×
2456
        }
×
2457

2458
        @Override
2459
        @ParallelSafe
2460
        public DiagnosticFilter getRouterDiagnosticFilter(HasChipLocation chip,
2461
                        int position)
2462
                        throws IOException, ProcessException, InterruptedException {
2463
                if (position < 0 || position > NO_ROUTER_DIAGNOSTIC_FILTERS) {
×
2464
                        throw new IllegalArgumentException(
×
2465
                                        "router filter positions must be between 0 and "
2466
                                                        + NO_ROUTER_DIAGNOSTIC_FILTERS);
2467
                }
2468
                var address =
×
2469
                                ROUTER_FILTERS.add(position * ROUTER_DIAGNOSTIC_FILTER_SIZE);
×
2470
                var response = get(new ReadMemory(chip, address, WORD_SIZE));
×
2471
                return new DiagnosticFilter(response.getInt());
×
2472
        }
2473

2474
        @Override
2475
        @ParallelSafe
2476
        public void clearRouterDiagnosticCounters(HasChipLocation chip,
2477
                        boolean enable, Iterable<Integer> counterIDs)
2478
                        throws IOException, ProcessException, InterruptedException {
2479
                int clearData = 0;
×
2480
                for (int counterID : requireNonNull(counterIDs)) {
×
2481
                        if (counterID < 0 || counterID >= NUM_ROUTER_DIAGNOSTIC_COUNTERS) {
×
2482
                                throw new IllegalArgumentException(
×
2483
                                                "Diagnostic counter IDs must be between 0 and 15");
2484
                        }
2485
                        clearData |= 1 << counterID;
×
2486
                }
×
2487
                if (enable) {
×
2488
                        for (int counterID : counterIDs) {
×
2489
                                clearData |= 1 << (counterID + ENABLE_SHIFT);
×
2490
                        }
×
2491
                }
2492
                writeMemory(chip, ROUTER_DIAGNOSTIC_COUNTER, clearData);
×
2493
        }
×
2494

2495
        @Override
2496
        @ParallelSafe
2497
        public void clearReinjectionQueues(HasCoreLocation monitorCore)
2498
                        throws IOException, ProcessException, InterruptedException {
2499
                new RouterControlProcess(scpSelector, this)
×
2500
                                .clearQueue(monitorCore.asCoreLocation());
×
2501
        }
×
2502

2503
        @Override
2504
        @ParallelSafe
2505
        public void clearReinjectionQueues(CoreSubsets monitorCores)
2506
                        throws IOException, ProcessException, InterruptedException {
2507
                new RouterControlProcess(scpSelector, this).clearQueue(monitorCores);
×
2508
        }
×
2509

2510
        @Override
2511
        @ParallelSafe
2512
        public ReinjectionStatus getReinjectionStatus(HasCoreLocation monitorCore)
2513
                        throws IOException, ProcessException, InterruptedException {
2514
                return new RouterControlProcess(scpSelector, this)
×
2515
                                .getReinjectionStatus(monitorCore.asCoreLocation());
×
2516
        }
2517

2518
        @Override
2519
        @ParallelSafe
2520
        public Map<CoreLocation, ReinjectionStatus> getReinjectionStatus(
2521
                        CoreSubsets monitorCores)
2522
                        throws IOException, ProcessException, InterruptedException {
2523
                return new RouterControlProcess(scpSelector, this)
×
2524
                                .getReinjectionStatus(monitorCores);
×
2525
        }
2526

2527
        @Override
2528
        @ParallelSafe
2529
        public void resetReinjectionCounters(HasCoreLocation monitorCore)
2530
                        throws IOException, ProcessException, InterruptedException {
2531
                new RouterControlProcess(scpSelector, this)
×
2532
                                .resetCounters(monitorCore.asCoreLocation());
×
2533
        }
×
2534

2535
        @Override
2536
        @ParallelSafeWithCare
2537
        public void resetReinjectionCounters(CoreSubsets monitorCores)
2538
                        throws IOException, ProcessException, InterruptedException {
2539
                new RouterControlProcess(scpSelector, this).resetCounters(monitorCores);
×
2540
        }
×
2541

2542
        @Override
2543
        @ParallelSafe
2544
        public void setReinjectionTypes(HasCoreLocation monitorCore,
2545
                        boolean multicast, boolean pointToPoint, boolean fixedRoute,
2546
                        boolean nearestNeighbour)
2547
                        throws IOException, ProcessException, InterruptedException {
2548
                new RouterControlProcess(scpSelector, this).setPacketTypes(
×
2549
                                monitorCore.asCoreLocation(), multicast, pointToPoint,
×
2550
                                fixedRoute, nearestNeighbour);
2551
        }
×
2552

2553
        @Override
2554
        @ParallelSafeWithCare
2555
        public void setReinjectionTypes(CoreSubsets monitorCores, boolean multicast,
2556
                        boolean pointToPoint, boolean fixedRoute, boolean nearestNeighbour)
2557
                        throws IOException, ProcessException, InterruptedException {
2558
                new RouterControlProcess(scpSelector, this).setPacketTypes(monitorCores,
×
2559
                                multicast, pointToPoint, fixedRoute, nearestNeighbour);
2560
        }
×
2561

2562
        @Override
2563
        @ParallelSafe
2564
        public void setReinjectionEmergencyTimeout(HasCoreLocation monitorCore,
2565
                        int timeoutMantissa, int timeoutExponent)
2566
                        throws IOException, ProcessException, InterruptedException {
2567
                new RouterControlProcess(scpSelector, this).setEmergencyTimeout(
×
2568
                                monitorCore.asCoreLocation(), timeoutMantissa, timeoutExponent);
×
2569
        }
×
2570

2571
        @Override
2572
        @ParallelSafeWithCare
2573
        public void setReinjectionEmergencyTimeout(CoreSubsets monitorCores,
2574
                        int timeoutMantissa, int timeoutExponent)
2575
                        throws IOException, ProcessException, InterruptedException {
2576
                new RouterControlProcess(scpSelector, this).setEmergencyTimeout(
×
2577
                                monitorCores, timeoutMantissa, timeoutExponent);
2578
        }
×
2579

2580
        @Override
2581
        @ParallelSafe
2582
        public void setReinjectionTimeout(HasCoreLocation monitorCore,
2583
                        int timeoutMantissa, int timeoutExponent)
2584
                        throws IOException, ProcessException, InterruptedException {
2585
                new RouterControlProcess(scpSelector, this).setTimeout(
×
2586
                                monitorCore.asCoreLocation(), timeoutMantissa, timeoutExponent);
×
2587
        }
×
2588

2589
        @Override
2590
        @ParallelSafeWithCare
2591
        public void setReinjectionTimeout(CoreSubsets monitorCores,
2592
                        int timeoutMantissa, int timeoutExponent)
2593
                        throws IOException, ProcessException, InterruptedException {
2594
                new RouterControlProcess(scpSelector, this).setTimeout(monitorCores,
×
2595
                                timeoutMantissa, timeoutExponent);
2596
        }
×
2597

2598
        @Override
2599
        @CheckReturnValue
2600
        @ParallelSafe
2601
        public List<HeapElement> getHeap(HasChipLocation chip,
2602
                        SystemVariableDefinition heap)
2603
                        throws IOException, ProcessException, InterruptedException {
2604
                return new GetHeapProcess(scpSelector, this).getBlocks(chip, heap);
×
2605
        }
2606

2607
        @Override
2608
        @ParallelSafe
2609
        public void fillMemory(HasChipLocation chip, MemoryLocation baseAddress,
2610
                        int repeatValue, int size, FillDataType dataType)
2611
                        throws ProcessException, IOException, InterruptedException {
2612
                if (repeatValue < 1) {
×
2613
                        throw new IllegalArgumentException("the repeat must be at least 1");
×
2614
                }
2615
                new FillProcess(scpSelector, this).fillMemory(chip, baseAddress,
×
2616
                                repeatValue, size, dataType);
2617
        }
×
2618

2619
        @Override
2620
        @ParallelSafeWithCare
2621
        public void saveApplicationRouterTables(CoreSubsets monitorCores)
2622
                        throws IOException, ProcessException, InterruptedException {
2623
                new RouterControlProcess(scpSelector, this)
×
2624
                                .saveApplicationRouterTable(monitorCores);
×
2625
        }
×
2626

2627
        @Override
2628
        @ParallelSafeWithCare
2629
        public void loadApplicationRouterTables(CoreSubsets monitorCores)
2630
                        throws IOException, ProcessException, InterruptedException {
2631
                new RouterControlProcess(scpSelector, this)
×
2632
                                .loadApplicationRouterTable(monitorCores);
×
2633
        }
×
2634

2635
        @Override
2636
        @ParallelSafeWithCare
2637
        public void loadSystemRouterTables(CoreSubsets monitorCores)
2638
                        throws IOException, ProcessException, InterruptedException {
2639
                new RouterControlProcess(scpSelector, this)
×
2640
                                .loadSystemRouterTable(monitorCores);
×
2641
        }
×
2642

2643
        @Override
2644
        public void resetRouting(Map<Integer, DiagnosticFilter> customFilters)
2645
                        throws ProcessException, IOException, InterruptedException {
2646
                var machine = this.getMachineDetails();
×
2647
                for (var chip : machine.chipCoordinates()) {
×
2648
                        this.clearMulticastRoutes(chip);
×
2649
                        this.clearRouterDiagnosticCounters(chip);
×
2650
                        if (customFilters != null) {
×
2651
                                for (var item : customFilters.entrySet()) {
×
2652
                                        this.setRouterDiagnosticFilter(chip, item.getKey(),
×
2653
                                                        item.getValue());
×
2654
                                }
×
2655
                        }
2656
                }
×
2657
        }
×
2658

2659
        /**
2660
         * Close the transceiver and any threads that are running.
2661
         *
2662
         * @throws IOException
2663
         *             If anything goes wrong
2664
         */
2665
        @Override
2666
        public void close() throws IOException {
2667
                try {
2668
                        synchronized (cachedDataIn) {
×
2669
                                for (var dataIn : cachedDataIn.values()) {
×
2670
                                        dataIn.close();
×
2671
                                }
×
2672
                                cachedDataIn.clear();
×
2673
                        }
×
2674
                        synchronized (cachedDownloaders) {
×
2675
                                cachedDownloaders.clear();
×
2676
                        }
×
2677
                        close(true, false);
×
2678
                } catch (InterruptedException e) {
×
2679
                        log.warn("unexpected interruption", e);
×
2680
                }
×
2681
        }
×
2682

2683
        /**
2684
         * Close the transceiver and any threads that are running.
2685
         *
2686
         * @param closeOriginalConnections
2687
         *            If True, the original connections passed to the transceiver in
2688
         *            the constructor are also closed. If False, only newly
2689
         *            discovered connections are closed.
2690
         * @param powerOffMachine
2691
         *            if true, the machine is sent a power down command via its BMP
2692
         *            (if it has one)
2693
         * @throws IOException
2694
         *             If anything goes wrong with networking
2695
         * @throws InterruptedException
2696
         *             If interrupted while waiting for the machine to power down
2697
         *             (only if that is requested).
2698
         */
2699
        public void close(boolean closeOriginalConnections, boolean powerOffMachine)
2700
                        throws IOException, InterruptedException {
2701
                if (powerOffMachine && !bmpConnections.isEmpty()) {
×
2702
                        try {
2703
                                powerOffMachine();
×
2704
                        } catch (ProcessException e) {
×
2705
                                log.warn("failed to power off machine", e);
×
2706
                        }
×
2707
                }
2708

2709
                super.close();
×
2710

2711
                for (var connection : allConnections) {
×
2712
                        if (closeOriginalConnections
×
2713
                                        || !originalConnections.contains(connection)) {
×
2714
                                connection.close();
×
2715
                        }
2716
                }
×
2717

2718
                log.info("total retries used: {}", retryCount);
×
2719
        }
×
2720

2721
        /** @return The connection selectors used for BMP connections. */
2722
        public Map<BMPCoords,
2723
                        ConnectionSelector<BMPConnection>> getBMPConnection() {
2724
                return bmpSelectors;
×
2725
        }
2726

2727
        /** A simple description of a connection to create. */
2728
        public static final class ConnectionDescriptor {
2729
                /** What host to talk to. */
2730
                private InetAddress hostname;
2731

2732
                /** What port to talk to, or {@code null} for default. */
2733
                private Integer portNumber;
2734

2735
                /** What chip to talk to. */
2736
                private ChipLocation chip;
2737

2738
                /**
2739
                 * Create a connection descriptor.
2740
                 *
2741
                 * @param hostname
2742
                 *            The host to talk to. The default UDP port will be used.
2743
                 * @param chip
2744
                 *            The chip to talk to.
2745
                 */
2746
                public ConnectionDescriptor(InetAddress hostname,
2747
                                HasChipLocation chip) {
×
2748
                        this.hostname = requireNonNull(hostname);
×
2749
                        this.chip = chip.asChipLocation();
×
2750
                        this.portNumber = null;
×
2751
                }
×
2752

2753
                /**
2754
                 * Create a connection descriptor.
2755
                 *
2756
                 * @param host
2757
                 *            The host to talk to.
2758
                 * @param port
2759
                 *            The UDP port to talk to.
2760
                 * @param chip
2761
                 *            The chip to talk to.
2762
                 */
2763
                public ConnectionDescriptor(InetAddress host, int port,
2764
                                HasChipLocation chip) {
×
2765
                        this.hostname = requireNonNull(host);
×
2766
                        this.chip = chip.asChipLocation();
×
2767
                        this.portNumber = port;
×
2768
                }
×
2769
        }
2770

2771
        @Override
2772
        protected void addConnection(Connection connection) {
2773
                this.allConnections.add(connection);
×
2774
        }
×
2775

2776
        @Override
2777
        public void bind(BMPCoords bmp) {
2778
                boundBMP = bmp;
×
2779
        }
×
2780

2781
        @Override
2782
        public BMPCoords getBoundBMP() {
2783
                return boundBMP;
×
2784
        }
2785

2786
        @Override
2787
        public int pingBoard(String address) {
NEW
2788
                return Ping.ping(address);
×
2789
        }
2790
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc