Skip to content

Commit 6610b21

Browse files
Gold856samfreund
andauthored
Refactor MAC address detection (#1991)
Co-authored-by: Sam Freund <[email protected]>
1 parent ef5e646 commit 6610b21

File tree

4 files changed

+81
-69
lines changed

4 files changed

+81
-69
lines changed

photon-core/src/main/java/org/photonvision/common/dataflow/networktables/NetworkTablesManager.java

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import org.photonvision.common.logging.LogGroup;
4444
import org.photonvision.common.logging.LogLevel;
4545
import org.photonvision.common.logging.Logger;
46-
import org.photonvision.common.networking.NetworkManager;
46+
import org.photonvision.common.networking.NetworkUtils;
4747
import org.photonvision.common.scripting.ScriptEventType;
4848
import org.photonvision.common.scripting.ScriptManager;
4949
import org.photonvision.common.util.TimedTaskManager;
@@ -58,6 +58,7 @@ public class NetworkTablesManager {
5858
public final String kCoprocTableName = "coprocessors";
5959
private final String kFieldLayoutName = "apriltag_field_layout";
6060
public final NetworkTable kRootTable = ntInstance.getTable(kRootTableName);
61+
public final NetworkTable kCoprocTable = kRootTable.getSubTable(kCoprocTableName);
6162

6263
// This is used to subscribe to all coprocessor tables, so we can detect conflicts
6364
@SuppressWarnings("unused")
@@ -69,6 +70,7 @@ public class NetworkTablesManager {
6970

7071
public boolean conflictingHostname = false;
7172
public String conflictingCameras = "";
73+
private String currentMacAddress;
7274

7375
private boolean m_isRetryingConnection = false;
7476

@@ -235,8 +237,12 @@ private void broadcastVersion() {
235237
* this table.
236238
*/
237239
private void checkHostnameAndCameraNames() {
238-
String MAC = NetworkManager.getInstance().getMACAddress();
239-
if (MAC == null || MAC.isEmpty()) {
240+
String mac = NetworkUtils.getMacAddress();
241+
if (!mac.equals(currentMacAddress)) {
242+
logger.debug("MAC address changed! New MAC address is " + mac + ", was " + currentMacAddress);
243+
currentMacAddress = mac;
244+
}
245+
if (mac.isEmpty()) {
240246
logger.error("Cannot check hostname and camera names, MAC address is not set!");
241247
return;
242248
}
@@ -254,62 +260,51 @@ private void checkHostnameAndCameraNames() {
254260
.map(entry -> entry.getValue().nickname)
255261
.toArray(String[]::new);
256262

257-
// Create a subtable under the photonvision root table
258-
NetworkTable coprocTable = kRootTable.getSubTable(kCoprocTableName);
259-
260263
// Create a subtable for this coprocessor using its MAC address
261-
NetworkTable macTable = coprocTable.getSubTable(MAC);
264+
NetworkTable macTable = kCoprocTable.getSubTable(mac);
262265

263266
// Publish the hostname and camera names
264267
macTable.getEntry("hostname").setString(hostname);
265268
macTable.getEntry("cameraNames").setStringArray(cameraNames);
266-
logger.debug("Published hostname and camera names to NT under MAC: " + MAC);
267269

268270
boolean conflictingHostname = false;
269271
StringBuilder conflictingCameras = new StringBuilder();
270272

271273
// Check for conflicts with other coprocessors
272-
for (String key : coprocTable.getSubTables()) {
274+
for (String key : kCoprocTable.getSubTables()) {
273275
// Check that key is formatted like a MAC address
274276
if (!key.matches("([0-9A-F]{2}-){5}[0-9A-F]{2}")) {
275277
logger.warn("Skipping non-MAC key in conflict detection: " + key);
276278
continue;
277279
}
280+
if (key.equals(mac)) { // Skip our own entry
281+
continue;
282+
}
283+
NetworkTable otherCoprocTable = kCoprocTable.getSubTable(key);
284+
String otherHostname = otherCoprocTable.getEntry("hostname").getString("");
285+
String[] otherCameraNames =
286+
otherCoprocTable.getEntry("cameraNames").getStringArray(new String[0]);
287+
// Check for hostname conflicts
288+
if (otherHostname.equals(hostname)) {
289+
logger.warn("Hostname conflict detected with coprocessor " + key + ": " + hostname);
290+
conflictingHostname = true;
291+
}
278292

279-
if (!key.equals(MAC)) { // Skip our own entry
280-
NetworkTable otherCoprocTable = coprocTable.getSubTable(key);
281-
String otherHostname = otherCoprocTable.getEntry("hostname").getString("");
282-
String[] otherCameraNames =
283-
otherCoprocTable.getEntry("cameraNames").getStringArray(new String[0]);
284-
// Check for hostname conflicts
285-
if (otherHostname.equals(hostname)) {
286-
logger.warn("Hostname conflict detected with coprocessor " + key + ": " + hostname);
287-
conflictingHostname = true;
288-
}
289-
290-
// Check for camera name conflicts
291-
for (String cameraName : cameraNames) {
292-
if (Arrays.stream(otherCameraNames).anyMatch(otherName -> otherName.equals(cameraName))) {
293-
logger.warn("Camera name conflict detected: " + cameraName);
294-
conflictingCameras.append(
295-
conflictingCameras.isEmpty() ? cameraName : ", " + cameraName);
296-
}
293+
// Check for camera name conflicts
294+
for (String cameraName : cameraNames) {
295+
if (Arrays.stream(otherCameraNames).anyMatch(otherName -> otherName.equals(cameraName))) {
296+
logger.warn("Camera name conflict detected: " + cameraName);
297+
conflictingCameras.append(conflictingCameras.isEmpty() ? cameraName : ", " + cameraName);
297298
}
298299
}
299300
}
300301

301-
boolean hasChanged =
302-
this.conflictingHostname != conflictingHostname
303-
|| !this.conflictingCameras.equals(conflictingCameras.toString());
304-
305302
// Publish the conflict status
306-
if (hasChanged) {
307-
DataChangeService.getInstance()
308-
.publishEvent(
309-
new OutgoingUIEvent<>(
310-
"fullsettings",
311-
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
312-
}
303+
DataChangeService.getInstance()
304+
.publishEvent(
305+
new OutgoingUIEvent<>(
306+
"fullsettings",
307+
UIPhotonConfiguration.programStateToUi(ConfigManager.getInstance().getConfig())));
313308

314309
conflictAlert.setText(
315310
conflictingHostname

photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public class MetricsManager {
4545

4646
ProtobufPublisher<DeviceMetrics> metricPublisher =
4747
NetworkTablesManager.getInstance()
48-
.kRootTable
49-
.getSubTable("/" + NetworkTablesManager.getInstance().kCoprocTableName + "/metrics")
48+
.kCoprocTable
49+
.getSubTable("/metrics")
5050
.getProtobufTopic(CameraServerJNI.getHostname(), DeviceMetrics.proto)
5151
.publish();
5252

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

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -179,35 +179,6 @@ private void setHostname(String hostname) {
179179
}
180180
}
181181

182-
public String getMACAddress() {
183-
var config = ConfigManager.getInstance().getConfig().getNetworkConfig();
184-
if (config.networkManagerIface == null || config.networkManagerIface.isBlank()) {
185-
logger.error("No network interface configured, cannot get MAC address!");
186-
return "";
187-
}
188-
try {
189-
NetworkInterface iFace = NetworkInterface.getByName(config.networkManagerIface);
190-
if (iFace == null) {
191-
logger.error("Network interface " + config.networkManagerIface + " not found!");
192-
return "";
193-
}
194-
byte[] mac = iFace.getHardwareAddress();
195-
if (mac == null) {
196-
logger.error("No MAC address found for " + config.networkManagerIface);
197-
return "";
198-
}
199-
StringBuilder sb = new StringBuilder(17);
200-
for (byte b : mac) {
201-
sb.append(String.format("%02X-", b));
202-
}
203-
sb.setLength(sb.length() - 1);
204-
return sb.toString();
205-
} catch (Exception e) {
206-
logger.error("Error getting MAC address for " + config.networkManagerIface, e);
207-
return "";
208-
}
209-
}
210-
211182
private void setConnectionDHCP(NetworkConfig config) {
212183
String connName = "dhcp-" + config.networkManagerIface;
213184

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.List;
2424
import java.util.regex.Matcher;
2525
import java.util.regex.Pattern;
26+
import org.photonvision.common.configuration.ConfigManager;
2627
import org.photonvision.common.hardware.Platform;
2728
import org.photonvision.common.logging.LogGroup;
2829
import org.photonvision.common.logging.Logger;
@@ -203,4 +204,49 @@ public static String getIPAddresses(String iFaceName) {
203204
}
204205
return String.join(", ", addresses);
205206
}
207+
208+
public static String getMacAddress() {
209+
var config = ConfigManager.getInstance().getConfig().getNetworkConfig();
210+
if (config.networkManagerIface == null || config.networkManagerIface.isBlank()) {
211+
// This is a silly heuristic to find a network interface that PV might be using. It looks like
212+
// it works pretty well, but Hyper-V adapters still show up in the list. But we're using MAC
213+
// address as a semi-unique identifier, not as a source of truth, so this should be fine.
214+
// Hyper-V adapters seem to show up near the end of the list anyways, so it's super likely
215+
// we'll find the right adapter anyways
216+
try {
217+
for (var iface : NetworkInterface.networkInterfaces().toList()) {
218+
if (iface.isUp() && !iface.isVirtual() && !iface.isLoopback()) {
219+
byte[] mac = iface.getHardwareAddress();
220+
if (mac == null) {
221+
logger.error("No MAC address found for " + iface.getDisplayName());
222+
}
223+
return formatMacAddress(mac);
224+
}
225+
}
226+
} catch (Exception e) {
227+
logger.error("Error getting MAC address:", e);
228+
}
229+
return "";
230+
}
231+
try {
232+
byte[] mac = NetworkInterface.getByName(config.networkManagerIface).getHardwareAddress();
233+
if (mac == null) {
234+
logger.error("No MAC address found for " + config.networkManagerIface);
235+
return "";
236+
}
237+
return formatMacAddress(mac);
238+
} catch (Exception e) {
239+
logger.error("Error getting MAC address for " + config.networkManagerIface, e);
240+
return "";
241+
}
242+
}
243+
244+
private static String formatMacAddress(byte[] mac) {
245+
StringBuilder sb = new StringBuilder(17);
246+
sb.append(String.format("%02X", mac[0]));
247+
for (int i = 1; i < mac.length; i++) {
248+
sb.append(String.format("-%02X", mac[i]));
249+
}
250+
return sb.toString();
251+
}
206252
}

0 commit comments

Comments
 (0)