diff --git a/jdm-core/src/main/java/jdiskmark/BenchmarkDriveInfo.java b/jdm-core/src/main/java/jdiskmark/BenchmarkDriveInfo.java index 2dd0b76..7ad1b77 100644 --- a/jdm-core/src/main/java/jdiskmark/BenchmarkDriveInfo.java +++ b/jdm-core/src/main/java/jdiskmark/BenchmarkDriveInfo.java @@ -23,6 +23,9 @@ public class BenchmarkDriveInfo { double totalGb; @JsonSerialize(using = RoundingSerializer.class) public double getTotalGb() { return totalGb; } + @Column + String driveInterface = null; + public String getDriveInterface() { return driveInterface; } public BenchmarkDriveInfo() {} } diff --git a/jdm-core/src/main/java/jdiskmark/BenchmarkRunner.java b/jdm-core/src/main/java/jdiskmark/BenchmarkRunner.java index 1043555..09055b4 100644 --- a/jdm-core/src/main/java/jdiskmark/BenchmarkRunner.java +++ b/jdm-core/src/main/java/jdiskmark/BenchmarkRunner.java @@ -120,11 +120,12 @@ public Benchmark execute() throws Exception { String driveModel = Util.getDriveModel(configDir); String partitionId = Util.getPartitionId(configDir.toPath()); DiskUsageInfo usageInfo = Util.getDiskUsage(configDir.getAbsolutePath()); + String driveInterface = Util.getDriveInterface(configDir); // Initialize Benchmark Benchmark benchmark = new Benchmark(config); - mapEnvironment(benchmark, driveModel, partitionId, usageInfo); + mapEnvironment(benchmark, driveModel, partitionId, usageInfo, driveInterface); // capture the render mode chosen at the time this run starts benchmark.setRenderMode(App.rmOption); @@ -380,7 +381,7 @@ private BenchmarkOperation createOp(Benchmark b, IOMode mode) { return op; } - private void mapEnvironment(Benchmark b, String model, String partId, DiskUsageInfo u) { + private void mapEnvironment(Benchmark b, String model, String partId, DiskUsageInfo u, String driveIface) { b.systemId = (App.systemId != null) ? App.systemId : ""; b.systemInfo.processorName = App.processorName; @@ -395,6 +396,7 @@ private void mapEnvironment(Benchmark b, String model, String partId, DiskUsageI b.driveInfo.percentUsed = u.percentUsed; b.driveInfo.usedGb = u.usedGb; b.driveInfo.totalGb = u.totalGb; + b.driveInfo.driveInterface = driveIface; } /** diff --git a/jdm-core/src/main/java/jdiskmark/Gui.java b/jdm-core/src/main/java/jdiskmark/Gui.java index 4c70c3c..f80e9e0 100644 --- a/jdm-core/src/main/java/jdiskmark/Gui.java +++ b/jdm-core/src/main/java/jdiskmark/Gui.java @@ -566,10 +566,18 @@ public static void showAboutDialog() { // Build an HTML panel so the website URL is a clickable hyperlink. String url = "https://www.jdiskmark.net"; + boolean hasSystemId = App.systemId != null && !App.systemId.isBlank(); + String systemIdDisplay = hasSystemId ? App.systemId : "(unavailable)"; String html = "
" + "" + App.APP_NAME + " " + App.VERSION + "" + systemIdDisplay + ""
+ + " \u29C9"
+ : "" + systemIdDisplay + "")
+ + "Returns the BusType string from {@code Get-Disk}, e.g. + * {@code NVMe}, {@code SATA}, {@code USB}, {@code RAID}, {@code SAS}. + * + * @param driveLetter single drive letter (e.g. "C") + * @return the bus type string, or null if unavailable + */ + public static String getDriveInterfaceWindows(String driveLetter) { + driveLetter = driveLetter.toUpperCase(); + try { + // Get-Partition maps a drive letter to a DiskNumber, + // then Get-Disk gives us the BusType. + String script = String.format( + "Get-Partition -DriveLetter %s | Get-Disk | Select-Object -ExpandProperty BusType", + driveLetter); + ProcessBuilder pb = new ProcessBuilder("powershell", "-NoProfile", + "-ExecutionPolicy", "Bypass", "-Command", script); + pb.redirectErrorStream(true); + Process process = pb.start(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (!line.isEmpty()) { + return line; + } + } + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Could not detect drive interface on Windows", e); + } + return null; + } + + /** + * Get the storage transport for a Linux device using {@code lsblk --output TRAN}. + * + *
Returns values like {@code sata}, {@code nvme}, {@code usb}, or null.
+ *
+ * @param devicePath the device path (e.g. "/dev/sda", "/dev/nvme0n1")
+ * @return the transport string, or null if unavailable
+ */
+ public static String getDriveInterfaceLinux(String devicePath) {
+ try {
+ ProcessBuilder pb = new ProcessBuilder(
+ "lsblk", devicePath, "--nodeps", "--noheadings", "--output", "TRAN");
+ Map Common values: {@code PCI-Express} (NVMe), {@code SATA},
+ * {@code USB}, {@code Apple Fabric}.
+ *
+ * @param devicePath the device path (e.g. {@code /dev/disk4s1})
+ * @return the protocol string, or null if unavailable
+ */
+ public static String getDriveInterfaceMacOs(String devicePath) {
+ if (devicePath == null || devicePath.isEmpty()) {
+ return null;
+ }
+ try {
+ ProcessBuilder pb = new ProcessBuilder("diskutil", "info", devicePath);
+ Map