001/* 002 * Copyright (C) Photon Vision. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package org.photonvision.common.hardware.metrics; 019 020import java.io.PrintWriter; 021import java.io.StringWriter; 022import java.util.HashMap; 023import org.photonvision.common.configuration.ConfigManager; 024import org.photonvision.common.configuration.HardwareConfig; 025import org.photonvision.common.dataflow.DataChangeService; 026import org.photonvision.common.dataflow.events.OutgoingUIEvent; 027import org.photonvision.common.hardware.Platform; 028import org.photonvision.common.hardware.metrics.cmds.CmdBase; 029import org.photonvision.common.hardware.metrics.cmds.FileCmds; 030import org.photonvision.common.hardware.metrics.cmds.LinuxCmds; 031import org.photonvision.common.hardware.metrics.cmds.PiCmds; 032import org.photonvision.common.hardware.metrics.cmds.RK3588Cmds; 033import org.photonvision.common.logging.LogGroup; 034import org.photonvision.common.logging.Logger; 035import org.photonvision.common.networking.NetworkUtils; 036import org.photonvision.common.util.ShellExec; 037 038public class MetricsManager { 039 final Logger logger = new Logger(MetricsManager.class, LogGroup.General); 040 041 CmdBase cmds; 042 043 private final ShellExec runCommand = new ShellExec(true, true); 044 045 public void setConfig(HardwareConfig config) { 046 if (config.hasCommandsConfigured()) { 047 cmds = new FileCmds(); 048 } else if (Platform.isRaspberryPi()) { 049 cmds = new PiCmds(); // Pi's can use a hardcoded command set 050 } else if (Platform.isRK3588()) { 051 cmds = new RK3588Cmds(); // RK3588 chipset hardcoded command set 052 } else if (Platform.isLinux()) { 053 cmds = new LinuxCmds(); // Linux/Unix platforms assume a nominal command set 054 } else { 055 cmds = new CmdBase(); // default - base has no commands 056 } 057 058 cmds.initCmds(config); 059 } 060 061 public String safeExecute(String str) { 062 if (str.isEmpty()) return ""; 063 try { 064 return execute(str); 065 } catch (Exception e) { 066 return "****"; 067 } 068 } 069 070 private String cpuMemSave = null; 071 072 public String getMemory() { 073 if (cmds.cpuMemoryCommand.isEmpty()) return ""; 074 if (cpuMemSave == null) { 075 // save the value and only run it once 076 cpuMemSave = execute(cmds.cpuMemoryCommand); 077 } 078 return cpuMemSave; 079 } 080 081 public String getTemp() { 082 return safeExecute(cmds.cpuTemperatureCommand); 083 } 084 085 public String getUtilization() { 086 return safeExecute(cmds.cpuUtilizationCommand); 087 } 088 089 public String getUptime() { 090 return safeExecute(cmds.cpuUptimeCommand); 091 } 092 093 public String getThrottleReason() { 094 return safeExecute(cmds.cpuThrottleReasonCmd); 095 } 096 097 public String getNpuUsage() { 098 return safeExecute(cmds.npuUsageCommand); 099 } 100 101 private String gpuMemSave = null; 102 103 public String getGPUMemorySplit() { 104 if (gpuMemSave == null) { 105 // only needs to run once 106 gpuMemSave = safeExecute(cmds.gpuMemoryCommand); 107 } 108 return gpuMemSave; 109 } 110 111 public String getMallocedMemory() { 112 return safeExecute(cmds.gpuMemUsageCommand); 113 } 114 115 public String getUsedDiskPct() { 116 return safeExecute(cmds.diskUsageCommand); 117 } 118 119 // TODO: Output in MBs for consistency 120 public String getUsedRam() { 121 return safeExecute(cmds.ramUsageCommand); 122 } 123 124 public String getIpAddress() { 125 String dev = ConfigManager.getInstance().getConfig().getNetworkConfig().networkManagerIface; 126 logger.debug("Requesting IP addresses for \"" + dev + "\""); 127 String addr = NetworkUtils.getIPAddresses(dev); 128 logger.debug("Got value \"" + addr + "\""); 129 return addr; 130 } 131 132 public void publishMetrics() { 133 logger.debug("Publishing Metrics..."); 134 final var metrics = new HashMap<String, String>(); 135 136 metrics.put("cpuTemp", this.getTemp()); 137 metrics.put("cpuUtil", this.getUtilization()); 138 metrics.put("cpuMem", this.getMemory()); 139 metrics.put("cpuThr", this.getThrottleReason()); 140 metrics.put("cpuUptime", this.getUptime()); 141 metrics.put("gpuMem", this.getGPUMemorySplit()); 142 metrics.put("ramUtil", this.getUsedRam()); 143 metrics.put("gpuMemUtil", this.getMallocedMemory()); 144 metrics.put("diskUtilPct", this.getUsedDiskPct()); 145 metrics.put("npuUsage", this.getNpuUsage()); 146 metrics.put("ipAddress", this.getIpAddress()); 147 148 DataChangeService.getInstance().publishEvent(OutgoingUIEvent.wrappedOf("metrics", metrics)); 149 } 150 151 public synchronized String execute(String command) { 152 try { 153 runCommand.executeBashCommand(command); 154 return runCommand.getOutput(); 155 } catch (Exception e) { 156 StringWriter sw = new StringWriter(); 157 PrintWriter pw = new PrintWriter(sw); 158 e.printStackTrace(pw); 159 160 logger.error( 161 "Command: \"" 162 + command 163 + "\" returned an error!" 164 + "\nOutput Received: " 165 + runCommand.getOutput() 166 + "\nStandard Error: " 167 + runCommand.getError() 168 + "\nCommand completed: " 169 + runCommand.isOutputCompleted() 170 + "\nError completed: " 171 + runCommand.isErrorCompleted() 172 + "\nExit code: " 173 + runCommand.getExitCode() 174 + "\n Exception: " 175 + e 176 + sw); 177 return ""; 178 } 179 } 180}