diff --git a/photon-client/src/components/cameras/CameraCalibrationCard.vue b/photon-client/src/components/cameras/CameraCalibrationCard.vue
index 47271283f2..b3f033cc9b 100644
--- a/photon-client/src/components/cameras/CameraCalibrationCard.vue
+++ b/photon-client/src/components/cameras/CameraCalibrationCard.vue
@@ -242,7 +242,7 @@ const setSelectedVideoFormat = (format: VideoFormat) => {
{{ value.verticalFOV !== undefined ? value.verticalFOV.toFixed(2) + "°" : "-" }} |
{{ value.diagonalFOV !== undefined ? value.diagonalFOV.toFixed(2) + "°" : "-" }} |
-
+
mdi-information
|
diff --git a/photon-client/src/components/settings/MetricsCard.vue b/photon-client/src/components/settings/MetricsCard.vue
index 53e14d22d5..013c916722 100644
--- a/photon-client/src/components/settings/MetricsCard.vue
+++ b/photon-client/src/components/settings/MetricsCard.vue
@@ -9,24 +9,36 @@ interface MetricItem {
value?: string;
}
-const generalMetrics = computed(() => [
- {
- header: "Version",
- value: useSettingsStore().general.version || "Unknown"
- },
- {
- header: "Hardware Model",
- value: useSettingsStore().general.hardwareModel || "Unknown"
- },
- {
- header: "Platform",
- value: useSettingsStore().general.hardwarePlatform || "Unknown"
- },
- {
- header: "GPU Acceleration",
- value: useSettingsStore().general.gpuAcceleration || "Unknown"
+const generalMetrics = computed(() => {
+ const stats = [
+ {
+ header: "Version",
+ value: useSettingsStore().general.version || "Unknown"
+ },
+ {
+ header: "Hardware Model",
+ value: useSettingsStore().general.hardwareModel || "Unknown"
+ },
+ {
+ header: "Platform",
+ value: useSettingsStore().general.hardwarePlatform || "Unknown"
+ },
+
+ {
+ header: "GPU Acceleration",
+ value: useSettingsStore().general.gpuAcceleration || "Unknown"
+ }
+ ];
+
+ if (!useSettingsStore().network.networkingDisabled) {
+ stats.push({
+ header: "IP Address",
+ value: useSettingsStore().metrics.ipAddress || "Unknown"
+ });
}
-]);
+
+ return stats;
+});
const platformMetrics = computed(() => {
const stats = [
diff --git a/photon-client/src/stores/settings/GeneralSettingsStore.ts b/photon-client/src/stores/settings/GeneralSettingsStore.ts
index ae0a41461c..a36de9e9e8 100644
--- a/photon-client/src/stores/settings/GeneralSettingsStore.ts
+++ b/photon-client/src/stores/settings/GeneralSettingsStore.ts
@@ -62,7 +62,8 @@ export const useSettingsStore = defineStore("settings", {
cpuThr: undefined,
cpuUptime: undefined,
diskUtilPct: undefined,
- npuUsage: undefined
+ npuUsage: undefined,
+ ipAddress: undefined
},
currentFieldLayout: {
field: {
@@ -95,7 +96,8 @@ export const useSettingsStore = defineStore("settings", {
cpuThr: data.cpuThr || undefined,
cpuUptime: data.cpuUptime || undefined,
diskUtilPct: data.diskUtilPct || undefined,
- npuUsage: data.npuUsage || undefined
+ npuUsage: data.npuUsage || undefined,
+ ipAddress: data.ipAddress || undefined
};
},
updateGeneralSettingsFromWebsocket(data: WebsocketSettingsUpdate) {
diff --git a/photon-client/src/types/SettingTypes.ts b/photon-client/src/types/SettingTypes.ts
index 9b7e7372d2..ef3a81d94a 100644
--- a/photon-client/src/types/SettingTypes.ts
+++ b/photon-client/src/types/SettingTypes.ts
@@ -23,6 +23,7 @@ export interface MetricData {
cpuUptime?: string;
diskUtilPct?: string;
npuUsage?: string;
+ ipAddress?: string;
}
export enum NetworkConnectionType {
diff --git a/photon-client/src/views/CameraMatchingView.vue b/photon-client/src/views/CameraMatchingView.vue
index 0eca83a7c0..3815555061 100644
--- a/photon-client/src/views/CameraMatchingView.vue
+++ b/photon-client/src/views/CameraMatchingView.vue
@@ -167,11 +167,11 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
{{ module.nickname }}
@@ -223,8 +223,8 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
Details
Deactivate
@@ -250,9 +250,9 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
mdi-trash-can-outline
@@ -263,7 +263,7 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
{{ module.nickname }}
Status: Deactivated
@@ -299,16 +299,16 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
Details
Activate
@@ -316,9 +316,9 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
mdi-trash-can-outline
@@ -329,7 +329,7 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
USB Camera:
@@ -345,12 +345,12 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
Details
-
+
Activate
@@ -377,7 +377,7 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
-
+
{{ cameraInfoFor(viewingCamera)?.name ?? cameraInfoFor(viewingCamera)?.baseName }}
@@ -398,10 +398,10 @@ const setCameraView = (camera: PVCameraInfo | null, showCurrent: boolean = false
Saved camera
-
+
Current camera
-
+
diff --git a/photon-client/src/views/DashboardView.vue b/photon-client/src/views/DashboardView.vue
index adc9732e4b..e55fc2c4d1 100644
--- a/photon-client/src/views/DashboardView.vue
+++ b/photon-client/src/views/DashboardView.vue
@@ -62,8 +62,8 @@ const arducamWarningShown = computed
(() => {
(() => {
-
+
Setup some cameras to get started!
diff --git a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java
index c51729a086..cfddf8f829 100644
--- a/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java
+++ b/photon-core/src/main/java/org/photonvision/common/hardware/metrics/MetricsManager.java
@@ -20,6 +20,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
+import org.photonvision.common.configuration.ConfigManager;
import org.photonvision.common.configuration.HardwareConfig;
import org.photonvision.common.dataflow.DataChangeService;
import org.photonvision.common.dataflow.events.OutgoingUIEvent;
@@ -31,6 +32,7 @@
import org.photonvision.common.hardware.metrics.cmds.RK3588Cmds;
import org.photonvision.common.logging.LogGroup;
import org.photonvision.common.logging.Logger;
+import org.photonvision.common.networking.NetworkUtils;
import org.photonvision.common.util.ShellExec;
public class MetricsManager {
@@ -119,6 +121,14 @@ public String getUsedRam() {
return safeExecute(cmds.ramUsageCommand);
}
+ public String getIpAddress() {
+ String dev = ConfigManager.getInstance().getConfig().getNetworkConfig().networkManagerIface;
+ logger.debug("Requesting IP addresses for \"" + dev + "\"");
+ String addr = NetworkUtils.getIPAddresses(dev);
+ logger.debug("Got value \"" + addr + "\"");
+ return addr;
+ }
+
public void publishMetrics() {
logger.debug("Publishing Metrics...");
final var metrics = new HashMap();
@@ -133,6 +143,7 @@ public void publishMetrics() {
metrics.put("gpuMemUtil", this.getMallocedMemory());
metrics.put("diskUtilPct", this.getUsedDiskPct());
metrics.put("npuUsage", this.getNpuUsage());
+ metrics.put("ipAddress", this.getIpAddress());
DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics));
}
diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkInterface.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkInterface.java
deleted file mode 100644
index 8130dccb62..0000000000
--- a/photon-core/src/main/java/org/photonvision/common/networking/NetworkInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) Photon Vision.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-package org.photonvision.common.networking;
-
-import java.net.InterfaceAddress;
-import org.photonvision.common.logging.LogGroup;
-import org.photonvision.common.logging.Logger;
-
-@SuppressWarnings("WeakerAccess")
-public class NetworkInterface {
- private static final Logger logger = new Logger(NetworkInterface.class, LogGroup.General);
-
- public final String name;
- public final String displayName;
- public final String ipAddress;
- public final String netmask;
- public final String broadcast;
-
- public NetworkInterface(java.net.NetworkInterface inetface, InterfaceAddress ifaceAddress) {
- name = inetface.getName();
- displayName = inetface.getDisplayName();
-
- var inetAddress = ifaceAddress.getAddress();
- ipAddress = inetAddress.getHostAddress();
- netmask = getIPv4LocalNetMask(ifaceAddress);
-
- // TODO: (low) hack to "get" gateway, this is gross and bad, pls fix
- var splitIPAddr = ipAddress.split("\\.");
- splitIPAddr[3] = "1";
- splitIPAddr[3] = "255";
- broadcast = String.join(".", splitIPAddr);
- }
-
- private static String getIPv4LocalNetMask(InterfaceAddress interfaceAddress) {
- var netPrefix = interfaceAddress.getNetworkPrefixLength();
- try {
- // Since this is for IPv4, it's 32 bits, so set the sign value of
- // the int to "negative"...
- int shiftby = (1 << 31);
- // For the number of bits of the prefix -1 (we already set the sign bit)
- for (int i = netPrefix - 1; i > 0; i--) {
- // Shift the sign right... Java makes the sign bit sticky on a shift...
- // So no need to "set it back up"...
- shiftby = (shiftby >> 1);
- }
- // Transform the resulting value in xxx.xxx.xxx.xxx format, like if
- /// it was a standard address...
- // Return the address thus created...
- return ((shiftby >> 24) & 255)
- + "."
- + ((shiftby >> 16) & 255)
- + "."
- + ((shiftby >> 8) & 255)
- + "."
- + (shiftby & 255);
- // return InetAddress.getByName(maskString);
- } catch (Exception e) {
- logger.error("Failed to get netmask!", e);
- }
- // Something went wrong here...
- return null;
- }
-}
diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java
index 9a543c4962..c948c70ec8 100644
--- a/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java
+++ b/photon-core/src/main/java/org/photonvision/common/networking/NetworkManager.java
@@ -17,11 +17,12 @@
package org.photonvision.common.networking;
+import java.net.NetworkInterface;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.HashMap;
import java.util.NoSuchElementException;
-
import org.photonvision.common.configuration.ConfigManager;
import org.photonvision.common.configuration.NetworkConfig;
import org.photonvision.common.dataflow.DataChangeDestination;
@@ -38,6 +39,7 @@
public class NetworkManager {
private static final Logger logger = new Logger(NetworkManager.class, LogGroup.General);
+ private HashMap activeConnections = new HashMap();
private NetworkManager() {}
@@ -61,17 +63,21 @@ public void initialize(boolean shouldManage) {
if (!Platform.isLinux()) {
logger.info("Not managing network on non-Linux platforms.");
+ this.networkingIsDisabled = true;
return;
}
if (!PlatformUtils.isRoot()) {
logger.error("Cannot manage network without root!");
+ this.networkingIsDisabled = true;
return;
}
// Start tasks to monitor the network interface(s)
var ethernetDevices = NetworkUtils.getAllWiredInterfaces();
for (NMDeviceInfo deviceInfo : ethernetDevices) {
+ activeConnections.put(
+ deviceInfo.devName, NetworkUtils.getActiveConnection(deviceInfo.devName));
monitorDevice(deviceInfo.devName, 5000);
}
@@ -81,7 +87,9 @@ public void initialize(boolean shouldManage) {
try {
// if the configured interface isn't in the list of available ones, select one that is
var iFace = physicalDevices.stream().findFirst().orElseThrow();
- logger.warn("The configured interface doesn't match any available interface. Applying configuration to " + iFace.devName);
+ logger.warn(
+ "The configured interface doesn't match any available interface. Applying configuration to "
+ + iFace.devName);
// update NetworkConfig with found interface
config.networkManagerIface = iFace.devName;
ConfigManager.getInstance().requestSave();
@@ -96,7 +104,13 @@ public void initialize(boolean shouldManage) {
}
}
- logger.info("Setting " + config.connectionType + " with team " + config.ntServerAddress + " on " + config.networkManagerIface);
+ logger.info(
+ "Setting "
+ + config.connectionType
+ + " with team "
+ + config.ntServerAddress
+ + " on "
+ + config.networkManagerIface);
// always set hostname (unless it's blank)
if (!config.hostname.isBlank()) {
@@ -129,15 +143,14 @@ private void setHostname(String hostname) {
var shell = new ShellExec(true, false);
shell.executeBashCommand("cat /etc/hostname | tr -d \" \\t\\n\\r\"");
var oldHostname = shell.getOutput().replace("\n", "");
- logger.debug("Old host name: >" + oldHostname +"<");
- logger.debug("New host name: >" + hostname +"<");
+ logger.debug("Old host name: \"" + oldHostname + "\"");
+ logger.debug("New host name: \"" + hostname + "\"");
if (!oldHostname.equals(hostname)) {
var setHostnameRetCode =
shell.executeBashCommand(
"echo $NEW_HOSTNAME > /etc/hostname".replace("$NEW_HOSTNAME", hostname));
- setHostnameRetCode =
- shell.executeBashCommand("hostnamectl set-hostname " + hostname);
+ setHostnameRetCode = shell.executeBashCommand("hostnamectl set-hostname " + hostname);
// Add to /etc/hosts
var addHostRetCode =
@@ -168,31 +181,23 @@ private void setHostname(String hostname) {
private void setConnectionDHCP(NetworkConfig config) {
String connName = "dhcp-" + config.networkManagerIface;
- String addDHCPcommand = """
- nmcli connection add
- con-name "${connection}"
- ifname "${interface}"
- type ethernet
- autoconnect no
- ipv4.method auto
- ipv6.method disabled
- """;
- addDHCPcommand = addDHCPcommand.replaceAll("[\\n]", " ");
-
var shell = new ShellExec();
try {
if (NetworkUtils.connDoesNotExist(connName)) {
- // create connection
- logger.info("Creating the DHCP connection " + connName );
+ logger.info("Creating DHCP connection " + connName);
shell.executeBashCommand(
- addDHCPcommand
- .replace("${connection}", connName)
- .replace("${interface}", config.networkManagerIface)
- );
+ NetworkingCommands.addConnectionCommand
+ .replace("${connection}", connName)
+ .replace("${interface}", config.networkManagerIface));
}
+ logger.info("Updating the DHCP connection " + connName);
+ shell.executeBashCommand(
+ NetworkingCommands.modDHCPCommand.replace("${connection}", connName));
// activate it
- logger.info("Activating the DHCP connection " + connName );
- shell.executeBashCommand("nmcli connection up \"${connection}\"".replace("${connection}", connName), false);
+ logger.info("Activating DHCP connection " + connName);
+ shell.executeBashCommand(
+ "nmcli connection up \"${connection}\"".replace("${connection}", connName), false);
+ activeConnections.put(config.networkManagerIface, connName);
} catch (Exception e) {
logger.error("Exception while setting DHCP!", e);
}
@@ -200,20 +205,6 @@ private void setConnectionDHCP(NetworkConfig config) {
private void setConnectionStatic(NetworkConfig config) {
String connName = "static-" + config.networkManagerIface;
- String addStaticCommand = """
- nmcli connection add
- con-name "${connection}"
- ifname "${interface}"
- type ethernet
- autoconnect no
- ipv4.addresses ${ipaddr}/8
- ipv4.gateway ${gateway}
- ipv4.method "manual"
- ipv6.method "disabled"
- """;
- addStaticCommand = addStaticCommand.replaceAll("[\\n]", " ");
-
- String modStaticCommand = "nmcli connection mod \"${connection}\" ipv4.addresses ${ipaddr}/8 ipv4.gateway ${gateway}";
if (config.staticIp.isBlank()) {
logger.warn("Got empty static IP?");
@@ -222,34 +213,31 @@ private void setConnectionStatic(NetworkConfig config) {
// guess at the gateway from the staticIp
String[] parts = config.staticIp.split("\\.");
- parts[parts.length-1] = "1";
+ parts[parts.length - 1] = "1";
String gateway = String.join(".", parts);
var shell = new ShellExec();
try {
if (NetworkUtils.connDoesNotExist(connName)) {
// create connection
- logger.info("Creating the Static connection " + connName );
- shell.executeBashCommand(
- addStaticCommand
- .replace("${connection}", connName)
- .replace("${interface}", config.networkManagerIface)
- .replace("${ipaddr}", config.staticIp)
- .replace("${gateway}", gateway)
- );
- } else {
- // modify it in case the static IP address is different
- logger.info("Modifying the Static connection " + connName );
+ logger.info("Creating Static connection " + connName);
shell.executeBashCommand(
- modStaticCommand
- .replace("${connection}", connName)
- .replace("${ipaddr}", config.staticIp)
- .replace("${gateway}", gateway)
- );
+ NetworkingCommands.addConnectionCommand
+ .replace("${connection}", connName)
+ .replace("${interface}", config.networkManagerIface));
}
+ // modify it in case the static IP address is different
+ logger.info("Updating the Static connection " + connName);
+ shell.executeBashCommand(
+ NetworkingCommands.modStaticCommand
+ .replace("${connection}", connName)
+ .replace("${ipaddr}", config.staticIp)
+ .replace("${gateway}", gateway));
// activate it
- logger.info("Activating the Static connection " + connName );
- shell.executeBashCommand("nmcli connection up \"${connection}\"".replace("${connection}", connName), false);
+ logger.info("Activating the Static connection " + connName);
+ shell.executeBashCommand(
+ "nmcli connection up \"${connection}\"".replace("${connection}", connName), false);
+ activeConnections.put(config.networkManagerIface, connName);
} catch (Exception e) {
logger.error("Error while setting static IP!", e);
}
@@ -267,31 +255,60 @@ private void monitorDevice(String devName, int millisInterval) {
logger.error("Can't find " + path + ", so can't monitor " + devName);
return;
}
- logger.debug("Watching network interface at path: " + path);
- var last = new Object() {boolean carrier = true; boolean exceptionLogged = false;};
- Runnable task = () -> {
- try {
- boolean carrier = Files.readString(path).trim().equals("1");
- if (carrier != last.carrier) {
- if (carrier) {
- // carrier came back
- logger.info("Interface " + devName + " has re-connected, reinitializing");
- reinitialize();
- } else {
- logger.warn("Interface " + devName + " is disconnected, check Ethernet!");
- }
- }
- last.carrier = carrier;
- last.exceptionLogged = false;
- } catch (Exception e) {
- if (!last.exceptionLogged) {
- // Log the exception only once, but keep trying
- logger.error("Could not check network status for " + devName, e);
- last.exceptionLogged = true;
+ var last =
+ new Object() {
+ boolean carrier = true;
+ boolean exceptionLogged = false;
+ String addresses = "";
+ };
+ Runnable task =
+ () -> {
+ try {
+ boolean carrier = Files.readString(path).trim().equals("1");
+ if (carrier != last.carrier) {
+ if (carrier) {
+ // carrier came back
+ logger.info("Interface " + devName + " has re-connected, reinitializing");
+ reinitialize();
+ } else {
+ logger.warn("Interface " + devName + " is disconnected, check Ethernet!");
+ }
+ }
+ NetworkInterface iFace;
+ iFace = NetworkInterface.getByName(devName);
+ if (iFace.isUp()) {
+ String tmpAddresses = "";
+ tmpAddresses = iFace.getInterfaceAddresses().toString();
+ if (!last.addresses.equals(tmpAddresses)) {
+ // addresses have changed, log the difference
+ last.addresses = tmpAddresses;
+ logger.info("Interface " + devName + " has address(es): " + last.addresses);
+ }
+ var conn = NetworkUtils.getActiveConnection(devName);
+ if (!conn.equals(activeConnections.get(devName))) {
+ logger.warn(
+ "Unexpected connection "
+ + conn
+ + " active on "
+ + devName
+ + ". Expected "
+ + activeConnections.get(devName));
+ logger.info("Reinitializing");
+ reinitialize();
+ }
+ }
+ last.carrier = carrier;
+ last.exceptionLogged = false;
+ } catch (Exception e) {
+ if (!last.exceptionLogged) {
+ // Log the exception only once, but keep trying
+ logger.error("Could not check network status for " + devName, e);
+ last.exceptionLogged = true;
+ }
}
- }
- };
+ };
TimedTaskManager.getInstance().addTask(taskName, task, millisInterval);
+ logger.debug("Watching network interface at path: " + path);
}
}
diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java
index 0f9a282ec7..569b27ee4f 100644
--- a/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java
+++ b/photon-core/src/main/java/org/photonvision/common/networking/NetworkUtils.java
@@ -18,6 +18,7 @@
package org.photonvision.common.networking;
import java.io.IOException;
+import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
@@ -156,15 +157,50 @@ public static NMDeviceInfo getNMinfoForDevName(String devName) {
return null;
}
+ public static String getActiveConnection(String devName) {
+ var shell = new ShellExec(true, true);
+ try {
+ shell.executeBashCommand(
+ "nmcli -g GENERAL.CONNECTION dev show \"" + devName + "\"", true, false);
+ return shell.getOutput().strip();
+ } catch (Exception e) {
+ logger.error("Exception from nmcli!");
+ }
+ return "";
+ }
+
public static boolean connDoesNotExist(String connName) {
var shell = new ShellExec(true, true);
try {
- // set nmcli back to DHCP, and re-run dhclient -- this ought to grab a new IP address
- shell.executeBashCommand("nmcli -f GENERAL.STATE connection show \"" + connName + "\"");
+ shell.executeBashCommand(
+ "nmcli -g GENERAL.STATE connection show \"" + connName + "\"", true, false);
return (shell.getExitCode() == 10);
} catch (Exception e) {
logger.error("Exception from nmcli!");
}
return false;
}
+
+ public static String getIPAddresses(String iFaceName) {
+ if (iFaceName == null || iFaceName.isBlank()) {
+ return "";
+ }
+ List addresses = new ArrayList();
+ try {
+ var iFace = NetworkInterface.getByName(iFaceName);
+ for (var addr : iFace.getInterfaceAddresses()) {
+ var addrStr = addr.getAddress().toString();
+ if (addrStr.startsWith("/")) {
+ addrStr = addrStr.substring(1);
+ }
+ addrStr = addrStr + "/" + addr.getNetworkPrefixLength();
+ addresses.add(addrStr);
+ }
+ // addresses = iFace.inetAddresses().map(a ->
+ // a.getAddress().toString()).collect(Collectors.joining(","));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return String.join(", ", addresses);
+ }
}
diff --git a/photon-core/src/main/java/org/photonvision/common/networking/NetworkingCommands.java b/photon-core/src/main/java/org/photonvision/common/networking/NetworkingCommands.java
new file mode 100644
index 0000000000..d985f98d2c
--- /dev/null
+++ b/photon-core/src/main/java/org/photonvision/common/networking/NetworkingCommands.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) Photon Vision.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.photonvision.common.networking;
+
+// using a separate class because Spotless fails on text blocks
+// spotless:off
+public class NetworkingCommands {
+ public static final String addConnectionCommand = """
+ nmcli connection add
+ con-name "${connection}"
+ ifname "${interface}"
+ type ethernet
+ """.replaceAll("[\\n]", " ");
+
+ public static final String modStaticCommand = """
+ nmcli connection modify ${connection}
+ autoconnect yes
+ ipv4.method manual
+ ipv6.method disabled
+ ipv4.addresses ${ipaddr}/8
+ ipv4.gateway ${gateway}
+ """.replaceAll("[\\n]", " ");
+
+ public static final String modDHCPCommand = """
+ nmcli connection modify "${connection}"
+ autoconnect yes
+ ipv4.method auto
+ ipv6.method disabled
+ """.replaceAll("[\\n]", " ");
+}
+//spotless:on
diff --git a/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java b/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java
index c8433ec158..06ea32f146 100644
--- a/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java
+++ b/photon-core/src/main/java/org/photonvision/common/util/ShellExec.java
@@ -40,8 +40,15 @@ public ShellExec(boolean readOutput, boolean readError) {
this.readError = readError;
}
+ /**
+ * Execute a bash command. We can handle complex bash commands including multiple executions (; |
+ * and ||), quotes, expansions ($), escapes (\), e.g.: "cd /abc/def; mv ghi 'older ghi '$(whoami)"
+ *
+ * @param command Bash command to execute
+ * @return process exit code
+ */
public int executeBashCommand(String command) throws IOException {
- return executeBashCommand(command, true);
+ return executeBashCommand(command, true, true);
}
/**
@@ -49,10 +56,25 @@ public int executeBashCommand(String command) throws IOException {
* and ||), quotes, expansions ($), escapes (\), e.g.: "cd /abc/def; mv ghi 'older ghi '$(whoami)"
*
* @param command Bash command to execute
- * @return true if bash got started, but your command may have failed.
+ * @param wait true if the command should wait for the proccess to complete
+ * @return process exit code
*/
public int executeBashCommand(String command, boolean wait) throws IOException {
- logger.debug("Executing \"" + command + "\"");
+ return executeBashCommand(command, true, true);
+ }
+
+ /**
+ * Execute a bash command. We can handle complex bash commands including multiple executions (; |
+ * and ||), quotes, expansions ($), escapes (\), e.g.: "cd /abc/def; mv ghi 'older ghi '$(whoami)"
+ * This runs the commands with the default logging.
+ *
+ * @param command Bash command to execute
+ * @param wait true if the command should wait for the proccess to complete
+ * @param debug true if the command and return value should be logged
+ * @return process exit code
+ */
+ public int executeBashCommand(String command, boolean wait, boolean debug) throws IOException {
+ if (debug) logger.debug("Executing \"" + command + "\"");
boolean success = false;
Runtime r = Runtime.getRuntime();
@@ -68,7 +90,7 @@ public int executeBashCommand(String command, boolean wait) throws IOException {
// Consume streams, older jvm's had a memory leak if streams were not read,
// some other jvm+OS combinations may block unless streams are consumed.
int retcode = doProcess(wait, process);
- logger.debug("Got exit code " + retcode);
+ if (debug) logger.debug("Got exit code " + retcode);
return retcode;
}