Skip to content

Commit 7a88487

Browse files
crschardtsamfreund
andauthored
Wait for NetworkManager on first call to getAllInterfaces (#2240)
## Description On the RubikPi3, PhotonVision starts before NetworkManager has fully initialized. This causes it to fail to identify the network interfaces available on the board, which leads to problems with configuring and controling networking from the UI. The failure can be detected by the call to `nmcli` returning an [exit status of 8](https://networkmanager.dev/docs/api/latest/nmcli.html#exit_status), which means "NetworkManager is not running." This PR retries the call to nmcli every 0.5 seconds until the exit status does not equal 8, or a maximum of 10 attempts have been made. The retry only occurs the first time `getAllInterfaces()` is called. Subsequent calls to this method will only make one attempt to avoid locking up the program if networking isn't responding as expected. In my testing on the RubikPi3, the code has to retry for less than 2 seconds in order to get a valid response from NetworkManager. The need for this is greatly reduced by PhotonVision/photon-image-modifier#114, but this code adds an additional layer of robustness against slow network startup. Closes #2212 ## Meta Merge checklist: - [x] Pull Request title is [short, imperative summary](https://cbea.ms/git-commit/) of proposed changes - [x] The description documents the _what_ and _why_ - [ ] If this PR changes behavior or adds a feature, user documentation is updated - [ ] If this PR touches photon-serde, all messages have been regenerated and hashes have not changed unexpectedly - [ ] If this PR touches configuration, this is backwards compatible with settings back to v2025.3.2 - [ ] If this PR touches pipeline settings or anything related to data exchange, the frontend typing is updated - [ ] If this PR addresses a bug, a regression test for it is added --------- Co-authored-by: samfreund <samf.236@proton.me>
1 parent 0e33aef commit 7a88487

File tree

2 files changed

+39
-12
lines changed
  • photon-core/src/main/java/org/photonvision/common/networking
  • photon-server/src/main/java/org/photonvision

2 files changed

+39
-12
lines changed

photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,48 @@ public static boolean nmcliIsInstalled() {
8282

8383
private static List<NMDeviceInfo> allInterfaces = null;
8484
private static long lastReadTimestamp = 0;
85+
private static long timeout = 5000; // milliseconds
86+
private static long retry = 500; // milliseconds
8587

86-
public static List<NMDeviceInfo> getAllInterfaces() {
87-
long now = System.currentTimeMillis();
88-
if (now - lastReadTimestamp < 5000) return allInterfaces;
89-
else lastReadTimestamp = now;
90-
88+
public static synchronized List<NMDeviceInfo> getAllInterfaces() {
89+
var start = System.currentTimeMillis();
90+
if (start - lastReadTimestamp < 5000) {
91+
return allInterfaces;
92+
}
9193
var ret = new ArrayList<NMDeviceInfo>();
9294

9395
if (Platform.isLinux()) {
9496
String out = null;
9597
try {
9698
var shell = new ShellExec(true, false);
97-
shell.executeBashCommand(
98-
"nmcli -t -f GENERAL.CONNECTION,GENERAL.DEVICE,GENERAL.TYPE device show", true, false);
99-
out = shell.getOutput();
99+
boolean networkManagerRunning = false;
100+
boolean tryagain = true;
101+
102+
do {
103+
shell.executeBashCommand(
104+
"nmcli -t -f GENERAL.CONNECTION,GENERAL.DEVICE,GENERAL.TYPE device show", true, true);
105+
// nmcli returns an error of 8 if NetworkManager isn't running
106+
networkManagerRunning = shell.getExitCode() != 8;
107+
tryagain = System.currentTimeMillis() - start < timeout;
108+
if (!networkManagerRunning && tryagain) {
109+
logger.debug("NetworkManager not running, retrying in " + (retry) + " milliseconds");
110+
Thread.sleep(retry);
111+
}
112+
} while (!networkManagerRunning && tryagain);
113+
114+
timeout = 0; // only try once after the first time
115+
116+
if (networkManagerRunning) {
117+
out = shell.getOutput();
118+
} else {
119+
logger.error(
120+
"Timed out trying to reach NetworkManager, may not be able to configure networking");
121+
}
122+
100123
} catch (IOException e) {
101124
logger.error("IO Exception occured when calling nmcli to get network interfaces!", e);
125+
} catch (InterruptedException e) {
126+
logger.error("Interrupted while waiting for NetworkManager", e);
102127
}
103128
if (out != null) {
104129
Pattern pattern =
@@ -120,6 +145,8 @@ public static List<NMDeviceInfo> getAllInterfaces() {
120145
}
121146
allInterfaces = ret;
122147
}
148+
lastReadTimestamp = System.currentTimeMillis();
149+
123150
return ret;
124151
}
125152

photon-server/src/main/java/org/photonvision/Main.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,6 @@ public static void main(String[] args) {
276276
ConfigManager.getInstance().load(); // init config manager
277277
ConfigManager.getInstance().requestSave();
278278

279-
logger.debug("Loading HardwareManager...");
280-
// Force load the hardware manager
281-
HardwareManager.getInstance();
282-
283279
logger.info("Loading ML models...");
284280
var modelManager = NeuralNetworkModelManager.getInstance();
285281
modelManager.extractModels();
@@ -293,6 +289,10 @@ public static void main(String[] args) {
293289
.setConfig(ConfigManager.getInstance().getConfig().getNetworkConfig());
294290
NetworkTablesManager.getInstance().registerTimedTasks();
295291

292+
logger.debug("Loading HardwareManager...");
293+
// Force load the hardware manager
294+
HardwareManager.getInstance();
295+
296296
if (isSmoketest) {
297297
logger.info("PhotonVision base functionality loaded -- smoketest complete");
298298
System.exit(0);

0 commit comments

Comments
 (0)