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

SpiNNakerManchester / JavaSpiNNaker / 7367

15 Dec 2025 09:24AM UTC coverage: 36.322% (+0.1%) from 36.219%
7367

push

github

web-flow
Merge pull request #1376 from SpiNNakerManchester/dependabot/maven/spring.boot.version-4.0.0

Bump spring.boot.version from 3.5.7 to 4.0.0

1915 of 5902 branches covered (32.45%)

Branch coverage included in aggregate %.

8 of 9 new or added lines in 1 file covered. (88.89%)

4 existing lines in 1 file now uncovered.

8985 of 24107 relevant lines covered (37.27%)

0.74 hits per line

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

47.0
/SpiNNaker-allocserv/src/main/java/uk/ac/manchester/spinnaker/alloc/bmp/TransceiverFactory.java
1
/*
2
 * Copyright (c) 2021 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.alloc.bmp;
17

18
import static java.util.Objects.hash;
19
import static org.slf4j.LoggerFactory.getLogger;
20
import static uk.ac.manchester.spinnaker.messages.Constants.SCP_SCAMP_PORT;
21
import static uk.ac.manchester.spinnaker.utils.InetFactory.getByName;
22
import static uk.ac.manchester.spinnaker.utils.Ping.ping;
23

24
import java.io.IOException;
25
import java.util.Collection;
26
import java.util.HashMap;
27
import java.util.List;
28
import java.util.Map;
29

30
import jakarta.annotation.PostConstruct;
31
import jakarta.annotation.PreDestroy;
32

33
import org.slf4j.Logger;
34
import org.springframework.beans.factory.annotation.Autowired;
35
import org.springframework.stereotype.Service;
36

37
import com.google.errorprone.annotations.RestrictedApi;
38
import com.google.errorprone.annotations.concurrent.GuardedBy;
39

40
import uk.ac.manchester.spinnaker.alloc.ForTestingOnly;
41
import uk.ac.manchester.spinnaker.alloc.ServiceMasterControl;
42
import uk.ac.manchester.spinnaker.alloc.SpallocProperties.TxrxProperties;
43
import uk.ac.manchester.spinnaker.alloc.allocator.SpallocAPI.Machine;
44
import uk.ac.manchester.spinnaker.connections.BMPConnection;
45
import uk.ac.manchester.spinnaker.machine.board.BMPCoords;
46
import uk.ac.manchester.spinnaker.messages.model.BMPConnectionData;
47
import uk.ac.manchester.spinnaker.messages.model.Blacklist;
48
import uk.ac.manchester.spinnaker.transceiver.BMPSendTimedOutException;
49
import uk.ac.manchester.spinnaker.transceiver.BMPTransceiverInterface;
50
import uk.ac.manchester.spinnaker.transceiver.ProcessException;
51
import uk.ac.manchester.spinnaker.transceiver.SpinnmanException;
52
import uk.ac.manchester.spinnaker.transceiver.Transceiver;
53
import uk.ac.manchester.spinnaker.utils.ValueHolder;
54

55
/**
56
 * Creates transceivers for talking to the BMPs of machines. Note that each
57
 * machine only has the one BMP that is talked to, and only ever one transceiver
58
 * that is used to do it.
59
 * <p>
60
 * Can support running with a dummy transceiver (but not in production, of
61
 * course). Set the {@code spalloc.transceiver.dummy} configuration value to
62
 * {@code true} to enable that.
63
 *
64
 * @author Donal Fellows
65
 */
66
@Service("transceiverFactory")
67
public class TransceiverFactory
2✔
68
                implements TransceiverFactoryAPI<BMPTransceiverInterface> {
69
        private static final Logger log = getLogger(TransceiverFactory.class);
2✔
70

71
        private static final class Key {
72
                private final String machine;
73

74
                private final BMPCoords bmp;
75

76
                Key(String machine, BMPCoords bmp) {
2✔
77
                        this.machine = machine;
2✔
78
                        this.bmp = bmp;
2✔
79
                }
2✔
80

81
                @Override
82
                public boolean equals(Object o) {
83
                        if (o instanceof Key) {
×
84
                                var other = (Key) o;
×
85
                                return machine.equals(other.machine) && bmp.equals(other.bmp);
×
86
                        }
87
                        return false;
×
88
                }
89

90
                @Override
91
                public int hashCode() {
92
                        return hash(machine, bmp);
2✔
93
                }
94
        }
95

96
        @GuardedBy("itself")
2✔
97
        private final Map<Key, BMPTransceiverInterface> txrxMap = new HashMap<>();
98

99
        @Autowired
100
        private ServiceMasterControl control;
101

102
        @Autowired
103
        private TxrxProperties props;
104

105
        @PostConstruct
106
        private void setup() {
107
                // Whenever the useDummyBMP property is changed, flush the cache
108
                control.addUseDummyBMPListener(e -> {
2✔
109
                        synchronized (txrxMap) {
×
110
                                try {
111
                                        closeTransceivers();
×
112
                                } catch (Exception ex) {
×
113
                                        log.warn("problem closing transceivers", ex);
×
114
                                }
×
115
                                txrxMap.clear();
×
116
                        }
×
117
                });
×
118
        }
2✔
119

120
        @Override
121
        public BMPTransceiverInterface getTransciever(Machine machineDescription,
122
                        BMPCoords bmp)
123
                        throws IOException, SpinnmanException, InterruptedException {
124
                try {
125
                        synchronized (txrxMap) {
2✔
126
                                return txrxMap
2✔
127
                                                .computeIfAbsent(
2✔
128
                                                new Key(machineDescription.getName(), bmp),
2✔
129
                                                __ -> makeTransceiver(machineDescription, bmp));
2✔
130
                        }
131
                } catch (TransceiverFactoryException e) {
×
132
                        var t = e.getCause();
×
133
                        if (t instanceof IOException) {
×
134
                                throw (IOException) t;
×
135
                        } else if (t instanceof SpinnmanException) {
×
136
                                throw (SpinnmanException) t;
×
137
                        } else if (t instanceof InterruptedException) {
×
138
                                throw (InterruptedException) t;
×
139
                        }
140
                        throw e;
×
141
                }
142
        }
143

144
        private static class TransceiverFactoryException extends RuntimeException {
145
                private static final long serialVersionUID = 2102592240724419836L;
146

147
                TransceiverFactoryException(String msg, Exception e) {
148
                        super(msg, e);
×
149
                }
×
150
        }
151

152
        private final ValueHolder<Blacklist> setBlacklist = new ValueHolder<>();
2✔
153

154
        private TestAPI.TestTransceiverFactory testFactory = null;
2✔
155

156
        private BMPTransceiverInterface makeTransceiver(Machine machineDescription,
157
                        BMPCoords bmp) {
158
                var connData = makeConnectionData(machineDescription, bmp);
2✔
159
                try {
160
                        if (testFactory != null) {
2✔
161
                                log.debug("using test transceiver factory {}", testFactory);
2✔
162
                                return testFactory.create(machineDescription.getName(),
2✔
163
                                                connData, setBlacklist);
164
                        } else {
165
                                log.debug("using real transceiver factory for {} at {}",
2✔
166
                                                machineDescription.getName(),
2✔
167
                                                connData.ipAddress);
168
                                return makeTransceiver(connData);
×
169
                        }
170
                } catch (IOException | SpinnmanException | InterruptedException e) {
×
171
                        throw new TransceiverFactoryException(
×
172
                                        "failed to build BMP transceiver", e);
173
                }
174
        }
175

176
        private BMPConnectionData makeConnectionData(Machine machine,
177
                        BMPCoords bmp) {
178
                try {
179
                        var address = machine.getBMPAddress(bmp);
2✔
180
                        var boards = machine.getBoardNumbers(bmp);
2✔
181
                        return new BMPConnectionData(0, 0, getByName(address), boards,
2✔
182
                                        SCP_SCAMP_PORT);
2✔
183
                } catch (IOException e) {
×
184
                        throw new TransceiverFactoryException(
×
185
                                        "failed to build address of BMP transceiver", e);
186
                }
187
        }
188

189
        /**
190
         * Build a transceiver connection.
191
         * <p>
192
         * The original spalloc server <em>also</em> does everything through the
193
         * root BMP; the BMPs communicate with each other if necessary. I believe
194
         * that communication is via an I<sup>2</sup>C bus, but I might be wrong.
195
         *
196
         * @param data
197
         *            The information about the BMP and the boards to manage.
198
         * @throws IOException
199
         *             If network access fails
200
         * @throws SpinnmanException
201
         *             If transceiver building fails
202
         * @throws InterruptedException
203
         *             If the communications were interrupted.
204
         */
205
        private Transceiver makeTransceiver(BMPConnectionData data)
206
                        throws IOException, SpinnmanException, InterruptedException {
207
                int count = 0;
2✔
208
                while (true) {
209
                        try {
210
                                return new Transceiver(null, List.of(new BMPConnection(data)),
×
211
                                                null, null, null, null, null);
UNCOV
212
                        } catch (ProcessException e) {
×
UNCOV
213
                                if (e.getCause() instanceof BMPSendTimedOutException
×
UNCOV
214
                                                && ++count > props.getBuildAttempts()) {
×
215
                                        log.error("completely failed to connect to BMP {}; "
×
216
                                                        + "service is unstable!", data);
217
                                        throw e;
×
218
                                }
UNCOV
219
                                log.error("failed to connect to BMP; will ping and retry", e);
×
220
                                log.debug("ping result was {}", ping(data.ipAddress));
×
221
                        }
×
222
                }
223
        }
224

225
        private Collection<BMPTransceiverInterface> transceivers() {
226
                synchronized (txrxMap) {
2✔
227
                        return List.copyOf(txrxMap.values());
2✔
228
                }
229
        }
230

231
        @PreDestroy
232
        void closeTransceivers() throws Exception {
233
                for (var txrx : transceivers()) {
2✔
234
                        txrx.close();
2✔
235
                }
2✔
236
        }
2✔
237

238
        /**
239
         * Not a public API! Operations for testing only.
240
         *
241
         * @hidden
242
         */
243
        @ForTestingOnly
244
        public interface TestAPI {
245
                /** @return The current blacklist. */
246
                Blacklist getCurrentBlacklist();
247

248
                /**
249
                 * @param factory
250
                 *            The mock transceiver factory to use.
251
                 */
252
                void setFactory(TestTransceiverFactory factory);
253

254
                /**
255
                 * Not a public API! A factory for transceivers. Use to install a
256
                 * suitable mock.
257
                 *
258
                 * @hidden
259
                 */
260
                interface TestTransceiverFactory {
261
                        /**
262
                         * Make a test transceiver.
263
                         *
264
                         * @param machineName
265
                         *            The name of the machine.
266
                         * @param data
267
                         *            The connection data.
268
                         * @param setBlacklist
269
                         *            Where to record the current blacklist
270
                         * @return Transceiver for testing.
271
                         */
272
                        BMPTransceiverInterface create(String machineName,
273
                                        BMPConnectionData data,
274
                                        ValueHolder<Blacklist> setBlacklist);
275
                }
276
        }
277

278
        /**
279
         * Not a public API! Do not call outside of test code!
280
         *
281
         * @return The test interface.
282
         * @deprecated This interface is just for testing.
283
         * @hidden
284
         */
285
        @ForTestingOnly
286
        @RestrictedApi(explanation = "just for testing", link = "index.html",
287
                        allowedOnPath = ".*/src/test/java/.*")
288
        @Deprecated
289
        public final TestAPI getTestAPI() {
290
                ForTestingOnly.Utils.checkForTestClassOnStack();
2✔
291
                return new TestAPI() {
2✔
292
                        @Override
293
                        public Blacklist getCurrentBlacklist() {
294
                                synchronized (setBlacklist) {
2✔
295
                                        return setBlacklist.getValue();
2✔
296
                                }
297
                        }
298

299
                        @Override
300
                        public void setFactory(TestTransceiverFactory factory) {
301
                                testFactory = factory;
2✔
302
                                synchronized (txrxMap) {
2✔
303
                                        txrxMap.clear();
2✔
304
                                }
2✔
305
                        }
2✔
306
                };
307
        }
308
}
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