diff --git a/.gitignore b/.gitignore index e8bd5bb..099398f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -/bdm.properties /dist/ /nbproject/private/ /build/ /jdm.properties /tmp *.zip -/build.properties +*.patch +build.properties /derbyDB/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..774616b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# How to contribute to JDiskMark + +First of all, thank you for considering a contribution to JDiskMark. We really appreciate the time and effort you want to spend improving the project, and we can use the help + +## What we are looking for + +- documentation (Getting Started guide, Examples, etc...) +- bug reports +- bug fixes +- code quality improvements +- features (both ideas and code are welcome) + +## Making your Changes + +Before you start, we recommend you open an issue for new features or bug fixes. This helps us coordinate and avoids duplicate work. + +1. **Fork and Clone:** Start by forking the repository on GitHub and then clone your fork locally. + +2. **Create a New Branch:** From the `dev` branch create a new, descriptively named branch for your changes: + `git checkout -b feature/your-awesome-feature` + +3. **Code & Test:** + - Make your changes. + - Add test cases for new features or bug fixes. + - Test your build to make sure everything is still working. + - Please ensure your code is formatted correctly. If you're using NetBeans, you can use `Alt` + `Shift` + `F`. + +4. **Commit Your Changes:** + - Write a clear and concise commit message. For example, "Fix: Corrected null pointer exception in DiskLogger" or "Feat: Added support for XFS filesystem." + - If you're fixing an issue, include the issue number and include `Closes#123` in your commit message to automatically close the issue when the PR is merged. + - If you have multiple commits, please squash them into a single one before submitting your pull request. + +## Submitting the Changes + +Submit a pull request via the normal GitHub UI. + +## After Submitting + +Do not use your branch for any other development, otherwise further changes that you make will be visible in the PR. \ No newline at end of file diff --git a/EmptyStandbyList.exe b/EmptyStandbyList.exe new file mode 100644 index 0000000..a5e8747 Binary files /dev/null and b/EmptyStandbyList.exe differ diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b98e3d9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,48 @@ +JDiskMark 0.6.0 License Agreement +NOTICE: This is a legal agreement. By using this software, you agree to be bound by the terms of this license. + +1. SCOPE OF LICENSE + +This software, JDiskMark (the "Software"), is licensed, not sold. The copyright holders reserve all rights not expressly granted to you. This license grants you a non-exclusive, worldwide, royalty-free license to use, copy, modify, and distribute the Software, subject to the terms and conditions outlined below. + +2. PERMISSIONS + +You are granted permission, free of charge, to any person obtaining a copy of this Software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to: + +Use the Software for personal and commercial purposes. + +Copy, modify, and merge the Software. + +Publish, distribute, and sublicense the Software. + +Sell copies of the Software. + +3. CONDITIONS + +The above permissions are subject to the following conditions: + +The original copyright notice and this permission notice must be included in all copies or substantial portions of the Software. + +You are not permitted to remove, minimize, block, or modify any notices or attributions of the original authors or copyright holders within the Software. + +You may not use the Software in any way that is unlawful or to create or propagate malware. + +4. DISCLAIMER OF WARRANTY + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +5. LIMITATION OF LIABILITY + +In no event shall the authors, copyright holders, or any other party involved in the creation or distribution of the Software be liable for any damages, including but not limited to direct, indirect, incidental, special, or consequential damages, arising out of the use or inability to use the Software. + +6. SUPPORT SERVICES + +The copyright holders are not obligated to provide any support services for the Software. Any support provided is "as is," "with all faults," and without warranty of any kind. + +7. TRANSFER TO ANOTHER DEVICE + +You may uninstall the Software and install it on another device for your use. You may not share this license on multiple devices. + +8. GOVERNING LAW + +This license agreement is governed by and construed in accordance with the laws of State of California, without regard to its conflict of law principles. \ No newline at end of file diff --git a/README.md b/README.md index 53a24a7..0cdb973 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,181 @@ -jDiskMark is a disk io utility written in java. +# JDiskMark v0.6.2 beta (Windows/Mac/Linux) +Java Disk Benchmark Utility -== Builds == +## Features + +- Java cross platform solution +- Benchmark IO read/write performance +- Graphs: sample bw, max, min, cum bw, latency (access time) +- Adjustable block size, block qty and sample qty +- Single or multi file option +- Sequential or random option +- Detect drive model, capacity and processor +- Save and load benchmark +- Auto clear disk cache (when sudo or admin) +- multi threaded benchmarks + +## Releases https://sourceforge.net/projects/jdiskmark/ +## Installation + +Java 21 needs to be installed to run jdiskmark. + +1. Download and install [java 21](https://www.oracle.com/java/technologies/downloads/) from Oracle. + +2. Verify java 21 is installed: + ``` + C:\Users\username>java --version + java 21.0.1 2023-10-17 LTS + Java(TM) SE Runtime Environment (build 21.0.1+12-LTS-29) + Java HotSpot(TM) 64-Bit Server VM (build 21.0.1+12-LTS-29, mixed mode, sharing) + ``` + +3. Extract release zip archive into desired location. + ``` + Examples: + /Users/username/jdiskmark-v0.6.0 + /opt/jdiskmark-v0.6.0 + ``` + +## Launching as normal process + +Note: Running without sudo or a windows administrator will require manually +clearing the disk write cache before performing read benchmarks. + +1. Open a terminal or shell in the extracted directory. + +2. run command: + ``` + $ java -jar jdiskmark.jar + ``` + In windows double click executable jar file. + +3. Drop cache manually: + - Linux: `sudo sh -c "sync; 1 > /proc/sys/vm/drop_caches"` + - Mac OS: `sudo sh -c "sync; purge"` + - Windows: Run included EmptyStandbyList.exe or [RAMMap64.exe](https://learn.microsoft.com/en-us/sysinternals/downloads/rammap) + - With RAMMap64 invalidate disk cache with Empty > Empty Standby List + +## Launching with elevated privileges + +Note: Take advantage of automatic clearing of the disk cache for write read +benchmarks start with sudo or an administrator windows shell. + +- Linux: `sudo java -jar jdiskmark.jar` +- Mac OS: `sudo java -jar jdiskmark.jar` +- Windows: start powershell as administrator then `java -jar jdiskmark.jar` + +## Development Environment + +jdiskmark client is developed with [NetBeans 21](https://netbeans.apache.org/front/main/download/) and [Java 21](https://www.oracle.com/java/technologies/downloads/) + +## Source + +Source code is available on our [github repo](https://github.com/JDiskMark/jdm-java/) + +## Release Notes + +### v1.0.0 planned +- TODO: #16 MacOS installer - tyler +- TODO: #15 Ubuntu installer - jeff +- TODO: #33 maven build - lane +- TODO: #40 gui presentation issues - james +- #84 processor info resolved for (sp) installs +- #73 refactor benchmark data model, keyboard op sel + +### v0.6.2 linux optimized ui +- #64 persist IOPS, write sync - val +- control panel on left +- allow concurrent version runs +- event tab swapped w disk location + +### v0.6.1 ms app store release +- JDiskMark in title and msi vendor name +- Remove "Average" from "Access Time" label + +### v0.6.0 +- #13 Detect drive info on startup +- #12 update look and feel (windows) +- #22 foreign capacity reporting +- #23 delete selected benchmarks +- #10 IOPS reporting +- #25 linux crash, capacity w terabytes and exabytes +- write sync default off +- #26 lowercase project and jar +- #20 threading and queue depth +- #36 I/O Mode dropdown uses enum values for type safety + +### v0.5.1 +- resolve #17 invalid disk usage reported win 10 +- msi installer available + +### v0.5 +- update for java 21 LTS w NetBeans 20 environment: eclipselink 4.0, jpa 3.1, + modelgen 5.6, annotations 3.1, xml.bind 4.0 +- increased drive information default col width to 170 +- time format updated to `yyyy-MM-dd HH:mm:ss` +- default to 200 marks +- replace Date w LocalDateTime to avoid deprecated @Temporal +- disk access time (ms) - plotting disabled by default +- replace display of transfer size with access time in run panel +- GH-2 auto clear disk cache for combined write read benchmarks +- GH-6 save and load benchmarks and graph series +- break out actions into seperate menu +- admin or root indicator, architecture indicator +- GH-8 used capacity and total capacity +- initial color palette options +- report processor name + +### v0.4 +- updated eclipselink to 2.6 allows auto schema update +- improved gui initialization +- platform disk model info: + - windows: via powershell query + - linux: via `df /data/path` & `lsblk /dev/path --output MODEL` + - osx: via `df /data/path` & `diskutil info /dev/disk1` + +### v0.3 +- persist recent run with embedded derby db +- remove "transfer mark number" from graph +- changed graph background to dark gray +- resizing main frame stretches tabbed pane instead of empty panel -== Usage == +### v0.2 +- auto generate zip release ie. `jdiskmark-v0.2.zip` +- added tabbed pane near bottom to organize new controls +- format excessive decimal places +- show recent runs (not persisted) +- default to nimbus look and feel -1. Requires java 8. +### v0.1 +- initial release -2. to run: - - $ java -jar jDiskMark.jar +### Proposed Features +- upload benchmarks to jdiskmark.net portal (anonymous/w login) +- local app log for remote diagnostics +- selecting a drive location displays detected drive information below +- speed curves w rw at different tx sizes +- response time histogram > distribution of IO +- IOPS charts, review potential charts +- help that describes features and controls - On windows double click executable jar file. +## issues +- read&write not consistant with order caps +- bottom margins between table to bar to window edge should be the same +## Windows Paths Examples for Building -== Release Notes == +For ant builds -v0.1 - - initial release +`C:\apache-ant-1.10.15\bin` -v0.2 - - auto generate zip release ie. jdiskmark-v0.2.zip - - added tabbed pane near bottom to organize new controls - - format excessive decimal places - - show recent runs (not persisted) - - default to nimbus look and feel +For maven builds -v0.3 - - persist recent run with embedded derby db - - remove "transfer mark number" from graph - - changed graph background to dark gray - - resizing main frame stretches tabbed pane instead of empty panel +`C:\apache-maven-3.9.10\bin` -v0.4 - - updated eclipselink to 2.6 allows auto schema update - - improved gui initialization - - platform disk model info: - windows: via powershell query - linux: via "df /data/path" & "lsblk /dev/path --output MODEL" - osx: via "df /data/path" & "diskutil info /dev/disk1" +For code signing -desired features - - update windows drive model parsing script to adapt to differing script output - - disk access time (ms) - - disk capacity and drive letter (available on windows) - - auto clear disk cache windows, linux, osx - linux: sync && echo 1 > /proc/sys/vm/drop_caches - windows: ??? - osx: ??? \ No newline at end of file +`C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x86\` diff --git a/archive/package-msi.bat b/archive/package-msi.bat new file mode 100644 index 0000000..b292d09 --- /dev/null +++ b/archive/package-msi.bat @@ -0,0 +1,16 @@ +:: GH-14 This script uses jpackage w wix 3.14 to create a msi installer +:: wix toolset 3.14 is available here: +:: https://github.com/wixtoolset/wix3/releases/tag/wix3141rtm + +:: make sure to set the version number +set VERSION=0.6.0-dev.g +set MSI_VER=0.6.0 + +set JPKG_EXE="C:\Program Files\java\jdk-21\bin\jpackage" + +del jdiskmark-*.msi +%JPKG_EXE% --type msi --input jdiskmark-%VERSION% --main-jar jdiskmark.jar ^ + --name jdiskmark-%VERSION% --app-version %MSI_VER% ^ + --vendor "jdiskmark.net" --win-console --win-menu + +ren jdiskmark-%VERSION%-%MSI_VER%.msi jdiskmark-%VERSION%.msi \ No newline at end of file diff --git a/archive/sign-msi.bat b/archive/sign-msi.bat new file mode 100644 index 0000000..cec5cf0 --- /dev/null +++ b/archive/sign-msi.bat @@ -0,0 +1,4 @@ +:: script to sign msi file +:: + +"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe" sign /tr http://timestamp.sectigo.com /td sha256 /fd sha256 /a "jdiskmark-0.6.0-dev.h.msi" \ No newline at end of file diff --git a/build.xml b/build.xml index f243a40..3514652 100644 --- a/build.xml +++ b/build.xml @@ -7,8 +7,8 @@ - - Builds, tests, and runs the project jDiskMark. + + Builds, tests, and runs the project JDiskMark. - - - + + + + + + + + + + + + + + + + + + + + + + + - + + - + + + + + - - + + + - - - + + + @@ -112,4 +138,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/capacity.ps1 b/capacity.ps1 new file mode 100644 index 0000000..eb5196d --- /dev/null +++ b/capacity.ps1 @@ -0,0 +1,35 @@ + +# Drive letter is passed in +$driveLetter = $args[0] + +if ($driveLetter -eq $null) { + Write-Output "WARNING: drive not provided - defaulting to c: for test" + $driveLetter = "C:" +} + +# Add ":" to the drive letter if it's missing +if (-not $driveLetter.EndsWith(':')) { + $driveLetter += ':' +} + +# Get disk information using Get-CimInstance +$disk = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceID='$driveLetter'" + +# Calculate disk space values in GB +$totalSpaceGb = [Math]::Round($disk.Size / 1GB, 2) +$freeSpaceGb = [Math]::Round($disk.FreeSpace / 1GB, 2) +$usedSpaceGb = [Math]::Round(($disk.Size - $disk.FreeSpace) / 1GB, 2) + +# Create a hashtable with the results +$diskUsage = @{ + Drive = $driveLetter + TotalSpaceGb = $totalSpaceGb + FreeSpaceGb = $freeSpaceGb + UsedSpaceGb = $usedSpaceGb +} + +# Convert the hashtable to JSON +$jsonOutput = $diskUsage | ConvertTo-Json + +# Output the JSON +Write-Output $jsonOutput \ No newline at end of file diff --git a/flushmem.exe b/flushmem.exe deleted file mode 100644 index 1536b8c..0000000 Binary files a/flushmem.exe and /dev/null differ diff --git a/libs/derbyshared.jar b/libs/derbyshared.jar new file mode 100644 index 0000000..ca71ed0 Binary files /dev/null and b/libs/derbyshared.jar differ diff --git a/libs/derbytools.jar b/libs/derbytools.jar new file mode 100644 index 0000000..cf104e5 Binary files /dev/null and b/libs/derbytools.jar differ diff --git a/libs/eclipselink.jar b/libs/eclipselink.jar index a829994..4dd0e4e 100644 Binary files a/libs/eclipselink.jar and b/libs/eclipselink.jar differ diff --git a/libs/flatlaf-3.3.jar b/libs/flatlaf-3.3.jar new file mode 100644 index 0000000..001beaf Binary files /dev/null and b/libs/flatlaf-3.3.jar differ diff --git a/libs/hibernate-jpamodelgen-jakarta-5.6.15.Final.jar b/libs/hibernate-jpamodelgen-jakarta-5.6.15.Final.jar new file mode 100644 index 0000000..c470cd8 Binary files /dev/null and b/libs/hibernate-jpamodelgen-jakarta-5.6.15.Final.jar differ diff --git a/libs/jackson-annotations-2.16.1.jar b/libs/jackson-annotations-2.16.1.jar new file mode 100644 index 0000000..b9c48e6 Binary files /dev/null and b/libs/jackson-annotations-2.16.1.jar differ diff --git a/libs/jackson-core-2.16.1.jar b/libs/jackson-core-2.16.1.jar new file mode 100644 index 0000000..a8ff5e3 Binary files /dev/null and b/libs/jackson-core-2.16.1.jar differ diff --git a/libs/jackson-databind-2.16.1.jar b/libs/jackson-databind-2.16.1.jar new file mode 100644 index 0000000..5171b7a Binary files /dev/null and b/libs/jackson-databind-2.16.1.jar differ diff --git a/libs/jakarta.persistence-api-3.2.0-M1.jar b/libs/jakarta.persistence-api-3.2.0-M1.jar new file mode 100644 index 0000000..ce61ca5 Binary files /dev/null and b/libs/jakarta.persistence-api-3.2.0-M1.jar differ diff --git a/libs/jakarta.xml.bind-api-4.0.1.jar b/libs/jakarta.xml.bind-api-4.0.1.jar new file mode 100644 index 0000000..c79fdfd Binary files /dev/null and b/libs/jakarta.xml.bind-api-4.0.1.jar differ diff --git a/libs/javax.annotation-3.1.2.2.jar b/libs/javax.annotation-3.1.2.2.jar new file mode 100644 index 0000000..098671c Binary files /dev/null and b/libs/javax.annotation-3.1.2.2.jar differ diff --git a/libs/javax.persistence_2.1.1.v201509150925.jar b/libs/javax.persistence_2.1.1.v201509150925.jar deleted file mode 100644 index 63375d1..0000000 Binary files a/libs/javax.persistence_2.1.1.v201509150925.jar and /dev/null differ diff --git a/libs/org.eclipse.persistence.jpa.modelgen_2.6.4.v20160829-44060b6.jar b/libs/org.eclipse.persistence.jpa.modelgen_2.6.4.v20160829-44060b6.jar deleted file mode 100644 index 83f7a62..0000000 Binary files a/libs/org.eclipse.persistence.jpa.modelgen_2.6.4.v20160829-44060b6.jar and /dev/null differ diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index ad7fa89..32bf14f 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -19,7 +19,7 @@ is divided into following sections: - cleanup --> - + @@ -46,14 +46,79 @@ is divided into following sections: - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -76,7 +141,9 @@ is divided into following sections: - + + + @@ -87,15 +154,6 @@ is divided into following sections: - - - - - - - - - @@ -250,11 +308,80 @@ is divided into following sections: - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -291,11 +418,13 @@ is divided into following sections: - + + + @@ -324,7 +453,7 @@ is divided into following sections: - + @@ -379,59 +508,99 @@ is divided into following sections: - - + + - - - + - + + + + + + + - + + - - + + - - - + - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -450,12 +619,16 @@ is divided into following sections: - + + + + + @@ -510,10 +683,6 @@ is divided into following sections: - - - - @@ -521,74 +690,20 @@ is divided into following sections: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - + + + + + + @@ -601,20 +716,20 @@ is divided into following sections: - + - + - + @@ -641,14 +756,10 @@ is divided into following sections: - - - - - + - + @@ -728,10 +839,14 @@ is divided into following sections: + + + + @@ -750,18 +865,6 @@ is divided into following sections: - - - - - - - - - - - - @@ -771,21 +874,79 @@ is divided into following sections: + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -795,9 +956,11 @@ is divided into following sections: - + + + @@ -820,6 +983,7 @@ is divided into following sections: + @@ -893,7 +1057,7 @@ is divided into following sections: - + @@ -909,7 +1073,9 @@ is divided into following sections: - + + + @@ -950,7 +1116,7 @@ is divided into following sections: Must select some files in the IDE or set javac.includes - + @@ -970,6 +1136,25 @@ is divided into following sections: + + + + + + + + + + + + + + + + + + + @@ -996,21 +1181,61 @@ is divided into following sections: - + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: java -jar "${dist.jar.resolved}" - + + + + + + + + + - + + + + + + + + + + + + + + + + + + + @@ -1029,8 +1254,73 @@ is divided into following sections: - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + @@ -1268,10 +1613,14 @@ is divided into following sections: - + Must select some files in the IDE or set javac.includes - + + + + + @@ -1289,7 +1638,7 @@ is divided into following sections: - + @@ -1301,14 +1650,14 @@ is divided into following sections: - + Must select some files in the IDE or set test.includes Some tests failed; see details above. - + Must select some files in the IDE or set test.class Must select some method in the IDE or set test.method @@ -1317,7 +1666,7 @@ is divided into following sections: Some tests failed; see details above. - + + + + + diff --git a/src/jdiskmark/AdvancedOptionsFrame.form b/src/jdiskmark/AdvancedOptionsFrame.form new file mode 100644 index 0000000..6e6412c --- /dev/null +++ b/src/jdiskmark/AdvancedOptionsFrame.form @@ -0,0 +1,85 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/jdiskmark/AdvancedOptionsFrame.java b/src/jdiskmark/AdvancedOptionsFrame.java new file mode 100644 index 0000000..5274b41 --- /dev/null +++ b/src/jdiskmark/AdvancedOptionsFrame.java @@ -0,0 +1,112 @@ + +package jdiskmark; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + + +/** + * + * @author james + */ +public class AdvancedOptionsFrame extends javax.swing.JFrame { + + /** + * Creates new form AdvancedOptionsFrame + */ + @SuppressWarnings("unchecked") + public AdvancedOptionsFrame() { + getContentPane().setLayout(new java.awt.BorderLayout()); + initComponents(); + + // --- Existing combo setup --- + setLocationRelativeTo(Gui.mainFrame); + DefaultComboBoxModel rModeModel = + new DefaultComboBoxModel<>(RenderFrequencyMode.values()); + renderModeCombo.setModel(rModeModel); + } + + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jLabel1 = new javax.swing.JLabel(); + renderModeCombo = new javax.swing.JComboBox(); + jButton1 = new javax.swing.JButton(); + + setTitle("Advanced Options"); + + jLabel1.setText("Render Mode"); + + renderModeCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "" })); + renderModeCombo.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + renderModeComboActionPerformed(evt); + } + }); + + jButton1.setLabel("Close"); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addGap(18, 18, 18) + .addComponent(renderModeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 128, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(177, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jButton1) + .addGap(26, 26, 26)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(renderModeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 227, Short.MAX_VALUE) + .addComponent(jButton1) + .addGap(18, 18, 18)) + ); + + pack(); + }// //GEN-END:initComponents + + private void renderModeComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_renderModeComboActionPerformed + try { + App.rmOption = (RenderFrequencyMode) renderModeCombo.getSelectedItem(); + } catch (RuntimeException re) { + System.err.print(" error casting item object" + re.getMessage()); + } + }//GEN-LAST:event_renderModeComboActionPerformed + + private void closeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeButtonActionPerformed + setVisible(false); + }//GEN-LAST:event_closeButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JLabel jLabel1; + private javax.swing.JComboBox renderModeCombo; + // End of variables declaration//GEN-END:variables + +} diff --git a/src/jdiskmark/App.java b/src/jdiskmark/App.java index c0c3bcc..c681aa8 100644 --- a/src/jdiskmark/App.java +++ b/src/jdiskmark/App.java @@ -1,37 +1,42 @@ package jdiskmark; -import java.beans.PropertyChangeEvent; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingWorker.StateValue; import static javax.swing.SwingWorker.StateValue.STARTED; -import javax.swing.UIManager; -import javax.swing.UIManager.LookAndFeelInfo; -import javax.swing.UnsupportedLookAndFeelException; /** * Primary class for global variables. */ public class App { - public static final String APP_CACHE_DIR = System.getProperty("user.home") + File.separator + ".jDiskMark"; - public static final String PROPERTIESFILE = "jdm.properties"; - public static final String DATADIRNAME = "jDiskMarkData"; + public static final String VERSION = App.getVersion(); + public static final String APP_CACHE_DIR_NAME = System.getProperty("user.home") + File.separator + ".jdm" + File.separator + VERSION; + public static final File APP_CACHE_DIR = new File(APP_CACHE_DIR_NAME); + public static final String PROPERTIES_FILENAME = "jdm.properties"; + public static final File PROPERTIES_FILE = new File(APP_CACHE_DIR_NAME + File.separator + PROPERTIES_FILENAME); + public static final String BUILD_TOKEN_FILENAME = "build.properties"; + public static final String DATADIRNAME = "JDiskMarkData"; + public static final String ESBL_EXE = "EmptyStandbyList.exe"; + public static final int MEGABYTE = 1024 * 1024; public static final int KILOBYTE = 1024; public static final int IDLE_STATE = 0; public static final int DISK_TEST_STATE = 1; - public static enum State {IDLE_STATE, DISK_TEST_STATE}; + public static enum State { IDLE_STATE, DISK_TEST_STATE }; public static State state = State.IDLE_STATE; public static Properties p; @@ -39,51 +44,50 @@ public static enum State {IDLE_STATE, DISK_TEST_STATE}; public static File dataDir = null; public static File testFile = null; + // system info + public static String os; + public static String arch; + public static String processorName; + + // elevated priviledges + public static boolean isRoot = false; + public static boolean isAdmin = false; + // options public static boolean multiFile = true; public static boolean autoRemoveData = false; public static boolean autoReset = true; public static boolean showMaxMin = true; - public static boolean writeSyncEnable = true; + public static boolean showDriveAccess = true; + public static boolean writeSyncEnable = false; // run configuration - public static boolean readTest = false; - public static boolean writeTest = true; - public static DiskRun.BlockSequence blockSequence = DiskRun.BlockSequence.SEQUENTIAL; - public static int numOfMarks = 25; // desired number of marks + public static BenchmarkOperation.BlockSequence blockSequence = BenchmarkOperation.BlockSequence.SEQUENTIAL; + public static int numOfSamples = 200; // desired number of samples public static int numOfBlocks = 32; // desired number of blocks public static int blockSizeKb = 512; // size of a block in KBs + public static int numOfThreads = 1; // number of threads - public static DiskWorker worker = null; - public static int nextMarkNumber = 1; // number of the next mark - public static double wMax = -1, wMin = -1, wAvg = -1; - public static double rMax = -1, rMin = -1, rAvg = -1; + // advanced options + public static RenderFrequencyMode rmOption = RenderFrequencyMode.PER_SAMPLE; + + public static BenchmarkWorker worker = null; + public static int nextSampleNumber = 1; // number of the next sample + public static double wMax = -1, wMin = -1, wAvg = -1, wAcc = -1; + public static double rMax = -1, rMin = -1, rAvg = -1, rAcc = -1; + public static long wIops = -1; + public static long rIops = -1; + + public static HashMap benchmarks = new HashMap<>(); + public static HashMap operations = new HashMap<>(); + public static Benchmark.BenchmarkType benchmarkType = Benchmark.BenchmarkType.WRITE; + public static BenchmarkOperation.IOMode ioMode = BenchmarkOperation.IOMode.WRITE; + /** * @param args the command line arguments */ public static void main(String args[]) { - - /* Set the Nimbus look and feel */ - try { - for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { - if ("Nimbus".equals(info.getName())) { - UIManager.setLookAndFeel(info.getClassName()); - break; - } - } - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { - // - /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. - * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html - */ - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { - java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); - } - // - } /* Create and display the form */ java.awt.EventQueue.invokeLater(App::init); } @@ -95,14 +99,30 @@ public static void main(String args[]) { public static String getVersion() { Properties bp = new Properties(); String version = "0.0"; - try { - bp.load(new FileInputStream("build.properties")); - version = bp.getProperty("version"); - } catch (FileNotFoundException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); + InputStream input = App.class.getResourceAsStream("/META-INF/build.properties"); + if (input != null) { + try (input) { + bp.load(input); + } catch (IOException e) { + Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, e); + } + } else if (Files.exists(Paths.get(BUILD_TOKEN_FILENAME))) { // ide and zip release + try { + bp.load(new FileInputStream(BUILD_TOKEN_FILENAME)); + } catch (IOException ex) { + System.err.println("If in NetBeans please do a " + + "Clean and Build Project from the Run Menu or press F11"); + Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); + } + } else { + // GH-14 jpackage windows environment + try { + bp.load(new FileInputStream("app/" + BUILD_TOKEN_FILENAME)); + } catch (IOException ex) { + Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); + } } + version = bp.getProperty("version", version); return version; } @@ -110,18 +130,47 @@ public static String getVersion() { * Initialize the GUI Application. */ public static void init() { - Gui.mainFrame = new MainFrame(); - Gui.selFrame = new SelectFrame(); - p = new Properties(); + + App.os = System.getProperty("os.name"); + App.arch = System.getProperty("os.arch"); + if (App.os.startsWith("Windows")) { + App.processorName = UtilOs.getProcessorNameWindows(); + } else if (App.os.startsWith("Mac OS")) { + App.processorName = UtilOs.getProcessorNameMacOS(); + } else if (App.os.contains("Linux")) { + App.processorName = UtilOs.getProcessorNameLinux(); + } + + checkPermission(); + if (!APP_CACHE_DIR.exists()) { + APP_CACHE_DIR.mkdirs(); + } loadConfig(); + + // initialize data dir if necessary + if (App.locationDir == null) { + App.locationDir = new File(System.getProperty("user.home")); + App.dataDir = new File(App.locationDir.getAbsolutePath() + + File.separator + App.DATADIRNAME); + } + + Gui.configureLaf(); + + Gui.mainFrame = new MainFrame(); + Gui.runPanel.hideFirstColumn(); + Gui.selFrame = new SelectDriveFrame(); + Gui.advancedFrame = new AdvancedOptionsFrame(); System.out.println(App.getConfigString()); - Gui.mainFrame.refreshConfig(); + Gui.mainFrame.loadConfig(); Gui.mainFrame.setLocationRelativeTo(null); Gui.progressBar = Gui.mainFrame.getProgressBar(); - // configure the embedded DB in .jDiskMark - System.setProperty("derby.system.home", APP_CACHE_DIR); - loadSavedRuns(); + // configure the embedded DB in .jdm + System.setProperty("derby.system.home", APP_CACHE_DIR_NAME); + loadBenchmarks(); + + // load current drive + Gui.updateDiskInfo(); Gui.mainFrame.setVisible(true); @@ -132,99 +181,166 @@ public static void init() { }); } + public static void checkPermission() { + String osName = System.getProperty("os.name"); + if (osName.contains("Linux")) { + isRoot = UtilOs.isRunningAsRootLinux(); + } else if (osName.contains("Mac OS")) { + isRoot = UtilOs.isRunningAsRootMacOs(); + } else if (osName.contains("Windows")) { + isAdmin = UtilOs.isRunningAsAdminWindows(); + } + if (isRoot || isAdmin) { + System.out.println("Running w elevated priviledges"); + } + } + public static void loadConfig() { - File pFile = new File(PROPERTIESFILE); - if (!pFile.exists()) { return; } + if (PROPERTIES_FILE.exists()) { + System.out.println("loading: " + PROPERTIES_FILE.getAbsolutePath()); + } else { + // generate default properties file if it does not exist + System.out.println(PROPERTIES_FILE + " does not exist generating..."); + saveConfig(); + } + + // read properties file + if (p == null) { p = new Properties(); } try { - InputStream is = new FileInputStream(pFile); - p.load(is); - } catch (FileNotFoundException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); + InputStream in = new FileInputStream(PROPERTIES_FILE); + p.load(in); } catch (IOException ex) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); } + + // configure settings from properties String value; - value = p.getProperty("locationDir", System.getProperty("user.home")); - locationDir = new File(value); + + value = p.getProperty("benchmarkType", String.valueOf(benchmarkType)); + benchmarkType = Benchmark.BenchmarkType.valueOf(value.toUpperCase()); + value = p.getProperty("multiFile", String.valueOf(multiFile)); - multiFile = Boolean.valueOf(value); + multiFile = Boolean.parseBoolean(value); + value = p.getProperty("autoRemoveData", String.valueOf(autoRemoveData)); - autoRemoveData = Boolean.valueOf(value); + autoRemoveData = Boolean.parseBoolean(value); + value = p.getProperty("autoReset", String.valueOf(autoReset)); - autoReset = Boolean.valueOf(value); + autoReset = Boolean.parseBoolean(value); + value = p.getProperty("blockSequence", String.valueOf(blockSequence)); - blockSequence = DiskRun.BlockSequence.valueOf(value); + blockSequence = BenchmarkOperation.BlockSequence.valueOf(value.toUpperCase()); + value = p.getProperty("showMaxMin", String.valueOf(showMaxMin)); - showMaxMin = Boolean.valueOf(value); - value = p.getProperty("numOfFiles", String.valueOf(numOfMarks)); - numOfMarks = Integer.valueOf(value); + showMaxMin = Boolean.parseBoolean(value); + + value = p.getProperty("showDriveAccess", String.valueOf(showDriveAccess)); + showDriveAccess = Boolean.parseBoolean(value); + + value = p.getProperty("numOfSamples", String.valueOf(numOfSamples)); + numOfSamples = Integer.parseInt(value); + value = p.getProperty("numOfBlocks", String.valueOf(numOfBlocks)); - numOfBlocks = Integer.valueOf(value); + numOfBlocks = Integer.parseInt(value); + value = p.getProperty("blockSizeKb", String.valueOf(blockSizeKb)); - blockSizeKb = Integer.valueOf(value); - value = p.getProperty("writeTest", String.valueOf(writeTest)); - writeTest = Boolean.valueOf(value); - value = p.getProperty("readTest", String.valueOf(readTest)); - readTest = Boolean.valueOf(value); + blockSizeKb = Integer.parseInt(value); + + value = p.getProperty("numOfThreads", String.valueOf(numOfThreads)); + numOfThreads = Integer.parseInt(value); + value = p.getProperty("writeSyncEnable", String.valueOf(writeSyncEnable)); - writeSyncEnable = Boolean.valueOf(value); - } + writeSyncEnable = Boolean.parseBoolean(value); + + value = p.getProperty("palette", String.valueOf(Gui.palette)); + Gui.palette = Gui.Palette.valueOf(value); + + value = p.getProperty("renderMode", String.valueOf(rmOption)); + rmOption = RenderFrequencyMode.valueOf(value.toUpperCase()); + + } public static void saveConfig() { - p.setProperty("locationDir", App.locationDir.getAbsolutePath()); + if (p == null) { p = new Properties(); } + + // configure properties + p.setProperty("benchmarkType", benchmarkType.name()); p.setProperty("multiFile", String.valueOf(multiFile)); p.setProperty("autoRemoveData", String.valueOf(autoRemoveData)); p.setProperty("autoReset", String.valueOf(autoReset)); - p.setProperty("blockSequence", String.valueOf(blockSequence)); + p.setProperty("blockSequence", blockSequence.name()); p.setProperty("showMaxMin", String.valueOf(showMaxMin)); - p.setProperty("numOfFiles", String.valueOf(numOfMarks)); + p.setProperty("showDriveAccess", String.valueOf(showDriveAccess)); + p.setProperty("numOfSamples", String.valueOf(numOfSamples)); p.setProperty("numOfBlocks", String.valueOf(numOfBlocks)); p.setProperty("blockSizeKb", String.valueOf(blockSizeKb)); - p.setProperty("writeTest", String.valueOf(writeTest)); - p.setProperty("readTest", String.valueOf(readTest)); + p.setProperty("numOfThreads", String.valueOf(numOfThreads)); p.setProperty("writeSyncEnable", String.valueOf(writeSyncEnable)); - + p.setProperty("palette", Gui.palette.name()); + p.setProperty("renderMode", rmOption.name()); + + // write properties file try { - OutputStream out = new FileOutputStream(new File(PROPERTIESFILE)); - p.store(out, "jDiskMark Properties File"); - } catch (FileNotFoundException ex) { - Logger.getLogger(SelectFrame.class.getName()).log(Level.SEVERE, null, ex); + OutputStream out = new FileOutputStream(PROPERTIES_FILE); + p.store(out, "JDiskMark Properties File"); } catch (IOException ex) { - Logger.getLogger(SelectFrame.class.getName()).log(Level.SEVERE, null, ex); + Logger.getLogger(SelectDriveFrame.class.getName()).log(Level.SEVERE, null, ex); } } + public static boolean isReadEnabled() { + return benchmarkType == Benchmark.BenchmarkType.READ || benchmarkType == Benchmark.BenchmarkType.READ_WRITE; + } + + public static boolean isWriteEnabled() { + return benchmarkType == Benchmark.BenchmarkType.WRITE || benchmarkType == Benchmark.BenchmarkType.READ_WRITE; + } + public static String getConfigString() { StringBuilder sb = new StringBuilder(); - sb.append("Config for Java Disk Mark ").append(getVersion()).append('\n'); - sb.append("readTest: ").append(readTest).append('\n'); - sb.append("writeTest: ").append(writeTest).append('\n'); + sb.append("Config for JDiskMark ").append(App.VERSION).append('\n'); + sb.append("readTest: ").append(isReadEnabled()).append('\n'); + sb.append("writeTest: ").append(isWriteEnabled()).append('\n'); sb.append("locationDir: ").append(locationDir).append('\n'); sb.append("multiFile: ").append(multiFile).append('\n'); sb.append("autoRemoveData: ").append(autoRemoveData).append('\n'); sb.append("autoReset: ").append(autoReset).append('\n'); sb.append("blockSequence: ").append(blockSequence).append('\n'); sb.append("showMaxMin: ").append(showMaxMin).append('\n'); - sb.append("numOfFiles: ").append(numOfMarks).append('\n'); + sb.append("numOfFiles: ").append(numOfSamples).append('\n'); sb.append("numOfBlocks: ").append(numOfBlocks).append('\n'); sb.append("blockSizeKb: ").append(blockSizeKb).append('\n'); + sb.append("numOfThreads: ").append(numOfThreads).append('\n'); + sb.append("palette: ").append(Gui.palette).append('\n'); + sb.append("ioMode: ").append(benchmarkType).append('\n'); return sb.toString(); } - public static void loadSavedRuns() { + public static void loadBenchmarks() { Gui.runPanel.clearTable(); // populate run table with saved runs from db - System.out.println("loading stored run data"); - DiskRun.findAll().stream().forEach((DiskRun run) -> { + System.out.println("loading benchmarks"); + Benchmark.findAll().stream().forEach((Benchmark run) -> { + benchmarks.put(run.getStartTimeString(), run); Gui.runPanel.addRun(run); + for (BenchmarkOperation o : run.getOperations()) { + operations.put(o.getStartTimeString(), o); + } }); } - public static void clearSavedRuns() { - DiskRun.deleteAll(); - - loadSavedRuns(); + public static void deleteAllBenchmarks() { + Benchmark.deleteAll(); + App.benchmarks.clear(); + loadBenchmarks(); + } + + // only tested for single delete but should work + public static void deleteBenchmarks(List benchmarkIds) { + Benchmark.delete(benchmarkIds); + App.benchmarks.clear(); // clear the cache + loadBenchmarks(); } public static void msg(String message) { @@ -241,7 +357,7 @@ public static void cancelBenchmark() { public static void startBenchmark() { - //1. check that there isn't already a worker in progress + // 1. check that there isn't already a worker in progress if (state == State.DISK_TEST_STATE) { //if (!worker.isCancelled() && !worker.isDone()) { msg("Test in progress, aborting..."); @@ -249,20 +365,20 @@ public static void startBenchmark() { //} } - //2. check can write to location + // 2. check can write to location if (locationDir.canWrite() == false) { msg("Selected directory can not be written to... aborting"); return; } - //3. update state + // 3. update state state = State.DISK_TEST_STATE; Gui.mainFrame.adjustSensitivity(); - //4. create data dir reference - dataDir = new File (locationDir.getAbsolutePath()+File.separator+DATADIRNAME); + // 4. create data dir reference + dataDir = new File (locationDir.getAbsolutePath() + File.separator + DATADIRNAME); - //5. remove existing test data if exist + // 5. remove existing test data if exist if (App.autoRemoveData && dataDir.exists()) { if (dataDir.delete()) { msg("removed existing data dir"); @@ -271,28 +387,25 @@ public static void startBenchmark() { } } - //6. create data dir if not already present + // 6. create data dir if not already present if (dataDir.exists() == false) { dataDir.mkdirs(); } - //7. start disk worker thread - worker = new DiskWorker(); - worker.addPropertyChangeListener((final PropertyChangeEvent event) -> { + // 7. start disk worker thread + worker = new BenchmarkWorker(); + worker.addPropertyChangeListener((final var event) -> { switch (event.getPropertyName()) { - case "progress": + case "progress" -> { int value = (Integer)event.getNewValue(); Gui.progressBar.setValue(value); - long kbProcessed = (value) * App.targetTxSizeKb() / 100; - Gui.progressBar.setString(String.valueOf(kbProcessed)+" / "+String.valueOf(App.targetTxSizeKb())); - break; - case "state": - switch ((StateValue) event.getNewValue()) { - case STARTED: - Gui.progressBar.setString("0 / "+String.valueOf(App.targetTxSizeKb())); - break; - case DONE: - break; + long kbProcessed = value * App.targetTxSizeKb() / 100; + Gui.progressBar.setString(String.valueOf(kbProcessed) + " / " + String.valueOf(App.targetTxSizeKb())); + } + case "state" -> { + switch ((StateValue)event.getNewValue()) { + case STARTED -> Gui.progressBar.setString("0 / " + String.valueOf(App.targetTxSizeKb())); + case DONE -> {} } // end inner switch - break; + } } }); worker.execute(); @@ -303,56 +416,113 @@ public static long targetMarkSizeKb() { } public static long targetTxSizeKb() { - return blockSizeKb * numOfBlocks * numOfMarks; + return blockSizeKb * numOfBlocks * numOfSamples; } - public static void updateMetrics(DiskMark mark) { - if (mark.type==DiskMark.MarkType.WRITE) { - if (wMax==-1 || wMax < mark.bwMbSec) { - wMax = mark.bwMbSec; + public static void updateMetrics(Sample s) { + if (s.type == Sample.Type.WRITE) { + if (wMax == -1 || wMax < s.bwMbSec) { + wMax = s.bwMbSec; } - if (wMin==-1 || wMin > mark.bwMbSec) { - wMin = mark.bwMbSec; + if (wMin == -1 || wMin > s.bwMbSec) { + wMin = s.bwMbSec; } - if (wAvg==-1) { - wAvg = mark.bwMbSec; + + // cumulative average bw + if (wAvg == -1) { + wAvg = s.bwMbSec; } else { - int n = mark.markNum; - wAvg = (((double)(n-1)*wAvg)+mark.bwMbSec)/(double)n; + int n = s.sampleNum; + wAvg = (((double)(n - 1) * wAvg) + s.bwMbSec) / (double)n; } - mark.cumAvg = wAvg; - mark.cumMax = wMax; - mark.cumMin = wMin; + + // cumulative access time + if (wAcc == -1) { + wAcc = s.accessTimeMs; + } else { + int n = s.sampleNum; + wAcc = (((double)(n - 1) * wAcc) + s.accessTimeMs) / (double)n; + } + + s.cumAvg = wAvg; + s.cumMax = wMax; + s.cumMin = wMin; + s.cumAccTimeMs = wAcc; } else { - if (rMax==-1 || rMax < mark.bwMbSec) { - rMax = mark.bwMbSec; + if (rMax == -1 || rMax < s.bwMbSec) { + rMax = s.bwMbSec; } - if (rMin==-1 || rMin > mark.bwMbSec) { - rMin = mark.bwMbSec; + if (rMin == -1 || rMin > s.bwMbSec) { + rMin = s.bwMbSec; } - if (rAvg==-1) { - rAvg = mark.bwMbSec; + + // cumulative bw + if (rAvg == -1) { + rAvg = s.bwMbSec; } else { - int n = mark.markNum; - rAvg = (((double)(n-1)*rAvg)+mark.bwMbSec)/(double)n; + int n = s.sampleNum; + rAvg = (((double)(n-1) * rAvg) + s.bwMbSec) / (double)n; } - mark.cumAvg = rAvg; - mark.cumMax = rMax; - mark.cumMin = rMin; + + // cumulative access time + if (rAcc == -1) { + rAcc = s.accessTimeMs; + } else { + int n = s.sampleNum; + rAcc = (((double)(n - 1) * rAcc) + s.accessTimeMs) / (double)n; + } + + s.cumAvg = rAvg; + s.cumMax = rMax; + s.cumMin = rMin; + s.cumAccTimeMs = rAcc; } } static public void resetSequence() { - nextMarkNumber = 1; + nextSampleNumber = 1; } static public void resetTestData() { - nextMarkNumber = 1; + nextSampleNumber = 1; wAvg = -1; wMax = -1; wMin = -1; + wAcc = -1; + wIops = -1; rAvg = -1; rMax = -1; rMin = -1; + rAcc = -1; + rIops = -1; + } + + /** + * Get a string summary of current drive capacity info + * @return String summarizing the drive information. + */ + static public String getDriveInfo() { + if (locationDir == null) { + return "Location has not been selected"; + } + + String driveModel = Util.getDriveModel(locationDir); + String partitionId = Util.getPartitionId(locationDir.toPath()); + DiskUsageInfo usageInfo = new DiskUsageInfo(); // init to prevent null ref + try { + usageInfo = Util.getDiskUsage(locationDir.toString()); + } catch (IOException | InterruptedException ex) { + Logger.getLogger(BenchmarkWorker.class.getName()).log(Level.SEVERE, null, ex); + } + return driveModel + " - " + partitionId + ": " + usageInfo.getUsageTitleDisplay(); + } + + /** + * This sets the location directory and configures the data directory within it. + * @param directory the dir to store + */ + static public void setLocationDir(File directory) { + locationDir = directory; + dataDir = new File (locationDir.getAbsolutePath() + File.separator + DATADIRNAME); } } diff --git a/src/jdiskmark/Benchmark.java b/src/jdiskmark/Benchmark.java new file mode 100644 index 0000000..9701c5f --- /dev/null +++ b/src/jdiskmark/Benchmark.java @@ -0,0 +1,204 @@ + +package jdiskmark; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.io.Serializable; +import java.text.DecimalFormat; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; + +/** + * A read or write benchmark + */ +@Entity +@Table(name="Benchmark") +@NamedQueries({ +@NamedQuery(name="Benchmark.findAll", + query="SELECT b FROM Benchmark b JOIN FETCH b.operations") +}) +public class Benchmark implements Serializable { + + static final DecimalFormat DF = new DecimalFormat("###.##"); + static final DecimalFormat DFT = new DecimalFormat("###"); + static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public enum BenchmarkType { + READ { + @Override + public String toString() { return "Read"; } + }, + WRITE { + @Override + public String toString() { return "Write"; } + }, + READ_WRITE { + @Override + public String toString() { return "Read & Write"; } + } + } + + // surrogate key + @Column + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + long id; + + // system data + @Column + String os; + @Column + String arch; + @Column + String processorName; + + // drive info + @Column + String driveModel = null; + @Column + String partitionId; // on windows the drive letter + @Column + long percentUsed; + @Column + double usedGb; + @Column + double totalGb; + + // benchmark parameters + @Column + BenchmarkType benchmarkType; + + @Enumerated(EnumType.STRING) + @Column + private RenderFrequencyMode renderMode = RenderFrequencyMode.PER_SAMPLE; + + // timestamps + @Convert(converter = LocalDateTimeAttributeConverter.class) + @Column(name = "startTime", columnDefinition = "TIMESTAMP") + LocalDateTime startTime; + @Convert(converter = LocalDateTimeAttributeConverter.class) + @Column + LocalDateTime endTime = null; + + + @OneToMany(mappedBy = "benchmark", cascade = CascadeType.ALL, orphanRemoval = true) + private List operations = new ArrayList<>(); + + public List getOperations() { + return operations; + } + + // get the first operation of that type + public BenchmarkOperation getOperation(BenchmarkOperation.IOMode mode) { + for (BenchmarkOperation operation : operations) { + if (operation.ioMode == mode) { + return operation; + } + } + return null; + } + + @Override + public String toString() { + return "Benchmark(" + benchmarkType + ") start=" + startTime + "numOps=" + operations.size(); + } + + public Benchmark() { + startTime = LocalDateTime.now(); + } + + Benchmark(BenchmarkType type) { + startTime = LocalDateTime.now(); + benchmarkType = type; + } + + // basic getters and setters + + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + public RenderFrequencyMode getRenderMode() { + return renderMode != null ? renderMode : RenderFrequencyMode.PER_SAMPLE; + } + + public void setRenderMode(RenderFrequencyMode renderMode) { + this.renderMode = renderMode; + } + + public String getDriveInfo() { + return driveModel + " - " + partitionId + ": " + getUsageTitleDisplay(); + } + public String getUsageTitleDisplay() { + return percentUsed + "% (" + DFT.format(usedGb) + "/" + DFT.format(totalGb) + " GB)"; + } + public String getUsageColumnDisplay() { + return percentUsed + "%"; + } + + public String getStartTimeString() { + return startTime.format(DATE_FORMAT); + } + + public String getDuration() { + if (endTime == null) { + return "unknown"; + } + long diffMs = Duration.between(startTime, endTime).toMillis(); + return String.valueOf(diffMs); + } + + // utility methods for collection + + static List findAll() { + EntityManager em = EM.getEntityManager(); + return em.createNamedQuery("Benchmark.findAll", Benchmark.class).getResultList(); + } + + static int deleteAll() { + EntityManager em = EM.getEntityManager(); + em.getTransaction().begin(); + int deletedOperationsCount = em.createQuery("DELETE FROM BenchmarkOperation").executeUpdate(); + int deletedBenchmarksCount = em.createQuery("DELETE FROM Benchmark").executeUpdate(); + em.getTransaction().commit(); + return deletedBenchmarksCount; + } + + static int delete(List benchmarkIds) { + EntityManager em = EM.getEntityManager(); + em.getTransaction().begin(); + + // delete the child BenchmarkOperation records. + String deleteOperationsJpql = "DELETE FROM BenchmarkOperation bo WHERE bo.benchmark.id IN :benchmarkIds"; + int deletedOpsCount = em.createQuery(deleteOperationsJpql) + .setParameter("benchmarkIds", benchmarkIds) + .executeUpdate(); + + // delete the parent BenchmarkOperation records + String deleteBenchmarksJpql = "DELETE FROM Benchmark b WHERE b.id IN :benchmarkIds"; + int deletedCount = em.createQuery(deleteBenchmarksJpql) + .setParameter("benchmarkIds", benchmarkIds) + .executeUpdate(); + + em.getTransaction().commit(); + return deletedCount; + } +} \ No newline at end of file diff --git a/src/jdiskmark/BenchmarkOperation.java b/src/jdiskmark/BenchmarkOperation.java new file mode 100644 index 0000000..9fb1616 --- /dev/null +++ b/src/jdiskmark/BenchmarkOperation.java @@ -0,0 +1,272 @@ + +package jdiskmark; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.Table; +import java.io.Serializable; +import java.text.DecimalFormat; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import jakarta.persistence.Enumerated; +import jakarta.persistence.EnumType; + + +/** + * A read or write benchmark + */ +@Entity +@Table(name="BenchmarkOperation") +@NamedQueries({ +@NamedQuery(name="BenchmarkOperation.findAll", + query="SELECT d FROM BenchmarkOperation d") +}) +public class BenchmarkOperation implements Serializable { + + static final DecimalFormat DF = new DecimalFormat("###.##"); + static final DecimalFormat DFT = new DecimalFormat("###"); + static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + public enum IOMode { + READ { + @Override + public String toString() { return "Read"; } + }, + WRITE { + @Override + public String toString() { return "Write"; } + } + } + + public enum BlockSequence { + SEQUENTIAL { + @Override + public String toString() { return "Sequential"; } + }, + RANDOM { + @Override + public String toString() { return "Random"; } + } + } + + // surrogate key + @Column + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + long id; + + // This is the foreign key relationship + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "benchmark_id") // Specifies the foreign key column + private Benchmark benchmark; + + // benchmark parameters + @Column + IOMode ioMode; + @Column + BlockSequence blockOrder; + @Column + int numBlocks = 0; + @Column + int blockSize = 0; + @Column + int numSamples = 0; + @Column + long txSize = 0; + @Column + int numThreads = 1; + // NEW: whether write-sync was enabled for this run (only meaningful for WRITE; may be null for READ) + @Column + Boolean writeSyncEnabled; + + // timestamps + @Convert(converter = LocalDateTimeAttributeConverter.class) + @Column(name = "startTime", columnDefinition = "TIMESTAMP") + LocalDateTime startTime; + @Convert(converter = LocalDateTimeAttributeConverter.class) + @Column + LocalDateTime endTime = null; + + // sample data + @Column + @Convert(converter = SampleAttributeConverter.class) + ArrayList samples = new ArrayList<>(); + + // results + @Column + double bwAvg = 0; + @Column + double bwMax = 0; + @Column + double bwMin = 0; + @Column + double accAvg = 0; + @Column + long iops = 0; + + @Override + public String toString() { + return "BenchmarkOp(" + ioMode + "," + blockOrder + "): " + numSamples + " bw avg: " + bwAvg; + } + + public BenchmarkOperation() { + startTime = LocalDateTime.now(); + } + + BenchmarkOperation(IOMode type, BlockSequence order) { + startTime = LocalDateTime.now(); + ioMode = type; + blockOrder = order; + } + + // basic getters and setters + public Benchmark getBenchmark() { + return benchmark; + } + public void setBenchmark(Benchmark benchmark) { + this.benchmark = benchmark; + } + public Long getId() { + return id; + } + public void setId(Long id) { + this.id = id; + } + + public String getModeDisplay() { + // Show "Write*" when write-sync was enabled for a WRITE run + if (ioMode == IOMode.WRITE && Boolean.TRUE.equals(getWriteSyncEnabled())) { + return "Write*"; + } + return (ioMode == null) ? "" : ioMode.toString(); // "Read", "Write", "Read & Write" + } + + + // GH-20 TODO: review should this be synchronized or redone to not be blocking? + public synchronized void add(Sample s) { + samples.add(s); + } + + public synchronized ArrayList getSamples() { + return samples; + } + + // display friendly methods + + public String getBlocksDisplay() { + return numBlocks + " (" + blockSize + ")"; + } + + public String getStartTimeString() { + return startTime.format(DATE_FORMAT); + } + + public String getAccTimeDisplay() { + return accAvg == -1? "- -" : DF.format(accAvg); + } + + public String getBwMinDisplay() { + return bwMin == -1 ? "- -" : DF.format(bwMin); + } + + public String getBwMaxDisplay() { + return bwMax == -1 ? "- -" : DF.format(bwMax); + } + + public String getBwMinMaxDisplay() { + return bwMax == -1 ? "- -" : DFT.format(bwMin) + "/" + DFT.format(bwMax); + } + + public String getBwAvgDisplay() { + return bwAvg == -1 ? "- -" : DF.format(bwAvg); + } + + public String getDuration() { + if (endTime == null) { + return "unknown"; + } + long diffMs = Duration.between(startTime, endTime).toMillis(); + return String.valueOf(diffMs); + } + + public void setTotalOps(long totalOps) { + // iops = operations / sec = ops / (elapsed ms / 1,000ms) + // Multiply by 1_000_000 to convert milliseconds to seconds + System.err.println("startTime=" + startTime); + System.err.println("endTime=" + endTime); + long diffMs = Duration.between(startTime, endTime).toMillis(); + if (diffMs != 0) { + double iopsDouble = (double) (totalOps * 1_000_000) / (double) diffMs; + iops = Math.round(iopsDouble); + } + } + + // NEW: getters/setters for writeSyncEnabled and iops (expose iops too if needed) + + public Boolean getWriteSyncEnabled() { + return writeSyncEnabled; + } + + public void setWriteSyncEnabled(Boolean writeSyncEnabled) { + this.writeSyncEnabled = writeSyncEnabled; + } + + public long getIops() { + return iops; + } + + public void setIops(long iops) { + this.iops = iops; + } + +// @Enumerated(EnumType.STRING) +// @Column +// private RenderFrequencyMode renderMode; + +// public void setRenderMode(RenderFrequencyMode renderMode) { +// this.renderMode = renderMode; +// } + +// public RenderFrequencyMode getRenderMode() { +// // Gracefully default to PER_SAMPLE if null or missing +// return renderMode != null ? renderMode : RenderFrequencyMode.PER_SAMPLE; +// } + // utility methods for collection + + static List findAll() { + EntityManager em = EM.getEntityManager(); + return em.createNamedQuery("Benchmark.findAll", BenchmarkOperation.class).getResultList(); + } + + static int deleteAll() { + EntityManager em = EM.getEntityManager(); + em.getTransaction().begin(); + int deletedCount = em.createQuery("DELETE FROM Benchmark").executeUpdate(); + em.getTransaction().commit(); + return deletedCount; + } + + static int delete(List benchmarkIds) { + EntityManager em = EM.getEntityManager(); + em.getTransaction().begin(); + String jpql = "DELETE FROM Benchmark b WHERE b.id IN :benchmarkIds"; + int deletedCount = em.createQuery(jpql) + .setParameter("benchmarkIds", benchmarkIds) + .executeUpdate(); + em.getTransaction().commit(); + return deletedCount; + } +} \ No newline at end of file diff --git a/src/jdiskmark/RunPanel.form b/src/jdiskmark/BenchmarkPanel.form similarity index 71% rename from src/jdiskmark/RunPanel.form rename to src/jdiskmark/BenchmarkPanel.form index 563cb27..1106ca9 100644 --- a/src/jdiskmark/RunPanel.form +++ b/src/jdiskmark/BenchmarkPanel.form @@ -1,6 +1,6 @@ -
+ @@ -44,89 +44,101 @@ - - - - - - - - +
+ + + + + + + + - - - - + + + +
- + <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="25" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="135" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="50" resizable="true"> + <Column maxWidth="45" minWidth="-1" prefWidth="46" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="7" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="6" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="10" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="40" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="10" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="6" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="10" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="35" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="100" resizable="true"> + <Column maxWidth="45" minWidth="-1" prefWidth="45" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="20" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="80" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="32" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="15" resizable="true"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="32" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="10" resizable="true"> + <Title/> + <Editor/> + <Renderer/> + </Column> + <Column maxWidth="-1" minWidth="-1" prefWidth="50" resizable="false"> <Title/> <Editor/> <Renderer/> </Column> - <Column maxWidth="-1" minWidth="-1" prefWidth="32" resizable="true"> + <Column maxWidth="-1" minWidth="-1" prefWidth="32" resizable="false"> <Title/> <Editor/> <Renderer/> </Column> </TableColumnModel> </Property> + <Property name="selectionModel" type="javax.swing.ListSelectionModel" editor="org.netbeans.modules.form.editors2.JTableSelectionModelEditor"> + <JTableSelectionModel selectionMode="0"/> + </Property> <Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor"> <TableHeader reorderingAllowed="true" resizingAllowed="true"/> </Property> </Properties> + <AuxValues> + <AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="BenchmarkPanel_runTable"/> + </AuxValues> </Component> </SubComponents> </Container> diff --git a/src/jdiskmark/BenchmarkPanel.java b/src/jdiskmark/BenchmarkPanel.java new file mode 100644 index 0000000..a24b791 --- /dev/null +++ b/src/jdiskmark/BenchmarkPanel.java @@ -0,0 +1,184 @@ + +package jdiskmark; + +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.util.LinkedList; +import java.util.List; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +public class BenchmarkPanel extends javax.swing.JPanel { + + /** + * Creates new form TestPanel + */ + public BenchmarkPanel() { + initComponents(); + Gui.runPanel = BenchmarkPanel.this; + + // Dynamically add Render Mode column + DefaultTableModel model = (DefaultTableModel) runTable.getModel(); + model.addColumn("Render Mode"); + + // Tooltip only – keep it simple + runTable.setToolTipText("Mode: Write* means Write Sync was enabled"); + + // #73 load new selected operation + OperationTableSelectionListener selectionListener = new OperationTableSelectionListener(runTable); + runTable.getSelectionModel().addListSelectionListener(selectionListener); + + // center align cells 2 - 11 + for (int i = 2; i <= 11; i++) { + TableColumn c = runTable.getColumnModel().getColumn(i); + c.setCellRenderer(new CenterTableCellRenderer()); + } + + // right align cell 12 (avg bw) + TableColumn c = runTable.getColumnModel().getColumn(12); + c.setCellRenderer(new RightTableCellRenderer()); + + // auto scroll to bottom when a new record is added + runTable.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + runTable.scrollRectToVisible( + runTable.getCellRect(runTable.getRowCount() - 1, 0, true) + ); + } + }); +} + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + runTable = new javax.swing.JTable(); + + runTable.setModel(new javax.swing.table.DefaultTableModel( + new Object [][] { + + }, + new String [] { + "ID", "Drive Model", "Usage", "Type", "Order", "Samples", "Blocks (Size)", "Thread", "Start Time", "Time (ms)", "Acc (ms)", "Min/Max (MB/s)", "IO (MB/s)" + } + ) { + boolean[] canEdit = new boolean [] { + false, false, false, false, false, false, false, false, false, false, false, false, false + }; + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return canEdit [columnIndex]; + } + }); + runTable.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + jScrollPane1.setViewportView(runTable); + if (runTable.getColumnModel().getColumnCount() > 0) { + runTable.getColumnModel().getColumn(0).setPreferredWidth(10); + runTable.getColumnModel().getColumn(1).setPreferredWidth(135); + runTable.getColumnModel().getColumn(2).setPreferredWidth(46); + runTable.getColumnModel().getColumn(2).setMaxWidth(45); + runTable.getColumnModel().getColumn(3).setPreferredWidth(6); + runTable.getColumnModel().getColumn(4).setPreferredWidth(40); + runTable.getColumnModel().getColumn(5).setPreferredWidth(6); + runTable.getColumnModel().getColumn(6).setPreferredWidth(35); + runTable.getColumnModel().getColumn(7).setPreferredWidth(45); + runTable.getColumnModel().getColumn(7).setMaxWidth(45); + runTable.getColumnModel().getColumn(8).setPreferredWidth(80); + runTable.getColumnModel().getColumn(9).setPreferredWidth(15); + runTable.getColumnModel().getColumn(10).setPreferredWidth(10); + runTable.getColumnModel().getColumn(11).setResizable(false); + runTable.getColumnModel().getColumn(11).setPreferredWidth(50); + runTable.getColumnModel().getColumn(12).setResizable(false); + runTable.getColumnModel().getColumn(12).setPreferredWidth(32); + } + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 789, Short.MAX_VALUE) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 118, Short.MAX_VALUE) + .addContainerGap()) + ); + }// </editor-fold>//GEN-END:initComponents + + static final int START_TIME_COLUMN = 7; + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JTable runTable; + // End of variables declaration//GEN-END:variables + + + public void addRun(Benchmark run) { + List<BenchmarkOperation> operations = run.getOperations(); + DefaultTableModel model = (DefaultTableModel) this.runTable.getModel(); + for (BenchmarkOperation o : operations) { + model.addRow( + new Object[] { + run.id, + run.driveModel, + run.getUsageColumnDisplay(), + o.getModeDisplay(), + o.blockOrder, + o.numSamples, + o.getBlocksDisplay(), + o.numThreads, + o.getStartTimeString(), + o.getDuration(), + o.getAccTimeDisplay(), + o.getBwMinMaxDisplay(), + o.getBwAvgDisplay(), + (o.getRenderMode() != null ? o.getRenderMode().toString() : "-") + }); + } + } + + public void hideFirstColumn() { + TableColumnModel columnModel = runTable.getColumnModel(); + columnModel.removeColumn(columnModel.getColumn(0)); + } + + public void clearTable() { + DefaultTableModel model = (DefaultTableModel) this.runTable.getModel(); + while (model.getRowCount() > 0) { + model.removeRow(0); + } + } + + public void deselect() { + runTable.getSelectionModel().clearSelection(); + } + + // Get the selected benchmarks + public List<Long> getSelectedIds() { + LinkedList<Long> ids = new LinkedList<>(); + int[] selectedRows = runTable.getSelectedRows(); + // for each row get the benchmark id in the hidden column + for (int r : selectedRows) { + try { + long benchmarkId = (long) runTable.getModel().getValueAt(r, 0); + ids.add(benchmarkId); + } catch (RuntimeException re) { + System.err.println("Error retrieving id at row=" + r); + } + } + return ids; + } +} diff --git a/src/jdiskmark/BenchmarkWorker.java b/src/jdiskmark/BenchmarkWorker.java new file mode 100644 index 0000000..086482a --- /dev/null +++ b/src/jdiskmark/BenchmarkWorker.java @@ -0,0 +1,448 @@ +package jdiskmark; + +import jakarta.persistence.EntityManager; +import static jdiskmark.App.KILOBYTE; +import static jdiskmark.App.MEGABYTE; +import static jdiskmark.App.blockSizeKb; +import static jdiskmark.App.msg; +import static jdiskmark.App.numOfBlocks; +import static jdiskmark.App.testFile; +import static jdiskmark.App.dataDir; +import static jdiskmark.Sample.Type.READ; +import static jdiskmark.Sample.Type.WRITE; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.SwingWorker; +import static jdiskmark.App.locationDir; +import static jdiskmark.App.numOfSamples; + + +/** + * Thread running the disk benchmarking. only one of these threads can run at + * once. + */ +public class BenchmarkWorker extends SwingWorker<Boolean, Sample> { + + // Temporary buffer for timed render modes (100/500/1000 ms) + private final List<Sample> intervalBuffer = new ArrayList<>(); + private long lastPublishTime = System.currentTimeMillis(); + private long nextPublishTime = lastPublishTime; + + + + public static int[][] divideIntoRanges(int startIndex, int endIndex, int numThreads) { + if (numThreads <= 0 || endIndex < startIndex) { + return new int[0][0]; // Handle invalid input + } + + int numElements = endIndex - startIndex + 1; // Calculate the total number of elements + int[][] ranges = new int[numThreads][2]; + int rangeSize = numElements / numThreads; + int remainder = numElements % numThreads; + int start = startIndex; + + for (int i = 0; i < numThreads; i++) { + int end = start + rangeSize - 1; + if (remainder > 0) { + end++; // Distribute the remainder + remainder--; + } + ranges[i][0] = start; + ranges[i][1] = end; + start = end + 1; + } + return ranges; + } + + @Override + protected Boolean doInBackground() throws Exception { + + System.out.println("*** starting new worker thread"); + msg("Running readTest " + App.isReadEnabled() + " writeTest " + App.isWriteEnabled()); + msg("num samples: " + App.numOfSamples + ", num blks: " + App.numOfBlocks + + ", blk size (kb): " + App.blockSizeKb + ", blockSequence: " + + App.blockSequence); + + // GH-20 final to aid w lambda usage + final int[] wUnitsComplete = {0}; + final int[] rUnitsComplete = {0}; + final int[] unitsComplete = {0}; + + int wUnitsTotal = App.isWriteEnabled() ? numOfBlocks * numOfSamples : 0; + int rUnitsTotal = App.isReadEnabled() ? numOfBlocks * numOfSamples : 0; + + int unitsTotal = wUnitsTotal + rUnitsTotal; + + int blockSize = blockSizeKb * KILOBYTE; + byte[] blockArr = new byte[blockSize]; + for (int b = 0; b < blockArr.length; b++) { + if (b % 2 == 0) { + blockArr[b] = (byte) 0xFF; + } + } + + Gui.updateLegendAndAxis(); + + + + if (App.autoReset == true) { + App.resetTestData(); + Gui.resetBenchmarkData(); + Gui.updateLegendAndAxis(); + } + + String driveModel = Util.getDriveModel(locationDir); + String partitionId = Util.getPartitionId(locationDir.toPath()); + DiskUsageInfo usageInfo = new DiskUsageInfo(); // init to prevent null ref + try { + usageInfo = Util.getDiskUsage(locationDir.toString()); + } catch (IOException | InterruptedException ex) { + Logger.getLogger(BenchmarkWorker.class.getName()).log(Level.SEVERE, null, ex); + } + msg("drive model=" + driveModel + " partitionId=" + partitionId + + " usage=" + usageInfo.toDisplayString()); + + // GH-20 calculate ranges for concurrent thread IO + int sIndex = App.nextSampleNumber; + int eIndex = sIndex + numOfSamples; + int[][] tRanges = divideIntoRanges(sIndex, eIndex, App.numOfThreads); + + List<java.util.concurrent.Future<?>> futures = new ArrayList<>(); + + Benchmark benchmark = new Benchmark(App.benchmarkType); + benchmark.setRenderMode(App.rmOption); + RenderFrequencyMode renderMode = benchmark.getRenderMode(); + + // system info + benchmark.processorName = App.processorName; + benchmark.os = App.os; + benchmark.arch = App.arch; + // drive information + benchmark.driveModel = driveModel; + benchmark.partitionId = partitionId; + benchmark.percentUsed = usageInfo.percentUsed; + benchmark.usedGb = usageInfo.usedGb; + benchmark.totalGb = usageInfo.totalGb; + + Gui.chart.getTitle().setText(benchmark.getDriveInfo()); + Gui.chart.getTitle().setVisible(true); + + if (App.isWriteEnabled()) { + BenchmarkOperation wOperation = new BenchmarkOperation(); + wOperation.setBenchmark(benchmark); + wOperation.ioMode = BenchmarkOperation.IOMode.WRITE; + wOperation.blockOrder = App.blockSequence; + wOperation.numSamples = App.numOfSamples; + wOperation.numBlocks = App.numOfBlocks; + wOperation.blockSize = App.blockSizeKb; + wOperation.txSize = App.targetTxSizeKb(); + wOperation.numThreads = App.numOfThreads; + // persist whether write sync was enabled for this run + wOperation.setWriteSyncEnabled(App.writeSyncEnable); + benchmark.getOperations().add(wOperation); + + if (App.multiFile == false) { + testFile = new File(dataDir.getAbsolutePath() + File.separator + "testdata.jdm"); + } + + if (renderMode == RenderFrequencyMode.PER_100MS + || renderMode == RenderFrequencyMode.PER_500MS + || renderMode == RenderFrequencyMode.PER_1000MS) { + synchronized (intervalBuffer) { + intervalBuffer.clear(); + } + long now = System.currentTimeMillis(); + lastPublishTime = now; + nextPublishTime = now + renderMode.getIntervalMillis(); + } + + // GH-20 instantiate threads to operate on each range + ExecutorService executorService = Executors.newFixedThreadPool(App.numOfThreads); + + for (int[] range : tRanges) { + final int startSample = range[0]; + final int endSample = range[1]; + + futures.add(executorService.submit(() -> { + + for (int s = startSample; s <= endSample && !isCancelled(); s++) { + + if (App.multiFile == true) { + testFile = new File(dataDir.getAbsolutePath() + + File.separator + "testdata" + s + ".jdm"); + } + Sample sample = new Sample(WRITE, s); + long startTime = System.nanoTime(); + long totalBytesWrittenInSample = 0; + String mode = (App.writeSyncEnable) ? "rwd" : "rw"; + + try { + try (RandomAccessFile rAccFile = new RandomAccessFile(testFile, mode)) { + for (int b = 0; b < numOfBlocks; b++) { + if (App.blockSequence == BenchmarkOperation.BlockSequence.RANDOM) { + int rLoc = Util.randInt(0, numOfBlocks - 1); + rAccFile.seek(rLoc * blockSize); + } else { + rAccFile.seek(b * blockSize); + } + rAccFile.write(blockArr, 0, blockSize); + totalBytesWrittenInSample += blockSize; + synchronized (BenchmarkWorker.this) { + wUnitsComplete[0]++; + unitsComplete[0] = rUnitsComplete[0] + wUnitsComplete[0]; + float percentComplete = (float)unitsComplete[0] / (float) unitsTotal * 100f; + setProgress((int) percentComplete); + } + } + } + } catch (IOException ex) { + Logger.getLogger(BenchmarkWorker.class.getName()).log(Level.SEVERE, null, ex); + } + long endTime = System.nanoTime(); + long elapsedTimeNs = endTime - startTime; + sample.accessTimeMs = (elapsedTimeNs / 1_000_000f) / numOfBlocks; + double sec = (double) elapsedTimeNs / 1_000_000_000d; + double mbWritten = (double) totalBytesWrittenInSample / (double) MEGABYTE; + sample.bwMbSec = mbWritten / sec; +// msg("s:" + s + " write IO is " + sample.getBwMbSecDisplay() + " MB/s " +// + "(" + Util.displayString(mbWritten) + "MB written in " +// + Util.displayString(sec) + " sec) elapsedNs: " + elapsedTimeNs); + // Determine when to publish based on render mode + + switch (renderMode) { + case PER_SAMPLE -> { + App.updateMetrics(sample); + publish(sample); + wOperation.add(sample); + } + case PER_OPERATION -> { + wOperation.add(sample); + } + case PER_100MS, PER_500MS, PER_1000MS -> { + long interval = renderMode.getIntervalMillis(); + long now = System.currentTimeMillis(); + + // collect samples into buffer + synchronized (intervalBuffer) { + intervalBuffer.add(sample); + } + + // check if it’s time to flush + if (now >= nextPublishTime) { + synchronized (intervalBuffer) { + for (Sample sm : intervalBuffer) { + App.updateMetrics(sm); + publish(sm); + } + intervalBuffer.clear(); + nextPublishTime = now + interval; + } + lastPublishTime = now; + } + + wOperation.add(sample); + } + + } + } + })); + } + + executorService.shutdown(); + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + + if (!wOperation.getSamples().isEmpty()) { + Sample last = wOperation.getSamples().get(wOperation.getSamples().size() - 1); + wOperation.bwMax = last.cumMax; + wOperation.bwMin = last.cumMin; + wOperation.bwAvg = last.cumAvg; + wOperation.accAvg = last.cumAccTimeMs; + } + + if (renderMode == RenderFrequencyMode.PER_OPERATION) { + for (Sample s : wOperation.getSamples()) { + App.updateMetrics(s); + publish(s); + } + } + + // GH-10 file IOPS processing + wOperation.endTime = LocalDateTime.now(); + wOperation.setTotalOps(wUnitsComplete[0]); + App.wIops = wOperation.iops; + Gui.mainFrame.refreshWriteMetrics(); + } + + // try renaming all files to clear catch + if (App.isReadEnabled() && App.isWriteEnabled() && !isCancelled()) { + Gui.dropCache(); + } + + if (App.isReadEnabled()) { + BenchmarkOperation rOperation = new BenchmarkOperation(); + rOperation.setBenchmark(benchmark); + // operation parameters + rOperation.ioMode = BenchmarkOperation.IOMode.READ; + rOperation.blockOrder = App.blockSequence; + rOperation.numSamples = App.numOfSamples; + rOperation.numBlocks = App.numOfBlocks; + rOperation.blockSize = App.blockSizeKb; + rOperation.txSize = App.targetTxSizeKb(); + rOperation.numThreads = App.numOfThreads; + // write sync does not apply to pure read benchmarks + rOperation.setWriteSyncEnabled(null); + benchmark.getOperations().add(rOperation); + + ExecutorService executorService = Executors.newFixedThreadPool(App.numOfThreads); + + for (int[] range : tRanges) { + final int startSample = range[0]; + final int endSample = range[1]; + + futures.add(executorService.submit(() -> { + for (int s = startSample; s <= endSample && !isCancelled(); s++) { + + if (App.multiFile == true) { + testFile = new File(dataDir.getAbsolutePath() + + File.separator + "testdata" + s + ".jdm"); + } + Sample sample = new Sample(READ, s); + long startTime = System.nanoTime(); + long totalBytesReadInMark = 0; + + try { + try (RandomAccessFile rAccFile = new RandomAccessFile(testFile, "r")) { + for (int b = 0; b < numOfBlocks; b++) { + if (App.blockSequence == BenchmarkOperation.BlockSequence.RANDOM) { + int rLoc = Util.randInt(0, numOfBlocks - 1); + rAccFile.seek(rLoc * blockSize); + } else { + rAccFile.seek(b * blockSize); + } + rAccFile.readFully(blockArr, 0, blockSize); + totalBytesReadInMark += blockSize; + synchronized (BenchmarkWorker.this) { + rUnitsComplete[0]++; + unitsComplete[0] = rUnitsComplete[0] + wUnitsComplete[0]; + float percentComplete = (float)unitsComplete[0] / (float) unitsTotal * 100f; + setProgress((int) percentComplete); + } + } + } + } catch (IOException ex) { + Logger.getLogger(BenchmarkWorker.class.getName()).log(Level.SEVERE, null, ex); + } + long endTime = System.nanoTime(); + long elapsedTimeNs = endTime - startTime; + sample.accessTimeMs = (elapsedTimeNs / 1_000_000f) / (float) numOfBlocks; + double sec = (double) elapsedTimeNs / 1_000_000_000d; + double mbRead = (double) totalBytesReadInMark / (double) MEGABYTE; + sample.bwMbSec = mbRead / sec; +// msg("s:" + s + " read IO is " + sample.getBwMbSecDisplay() + " MB/s " +// + "(" + Util.displayString(mbRead) + "MB read in " +// + Util.displayString(sec) + " sec) elapsedNs: " + elapsedTimeNs); + switch (renderMode) { + case PER_SAMPLE -> { + App.updateMetrics(sample); + publish(sample); + rOperation.add(sample); + } + case PER_OPERATION -> { + rOperation.add(sample); + } + case PER_100MS, PER_500MS, PER_1000MS -> { + long interval = renderMode.getIntervalMillis(); + long now = System.currentTimeMillis(); + + synchronized (intervalBuffer) { + intervalBuffer.add(sample); + } + + if (now >= nextPublishTime) { + synchronized (intervalBuffer) { + for (Sample sm : intervalBuffer) { + App.updateMetrics(sm); + publish(sm); + } + intervalBuffer.clear(); + nextPublishTime = now + interval; + } + lastPublishTime = now; + } + + rOperation.add(sample); + } + } + + if (!rOperation.getSamples().isEmpty()) { + Sample last = rOperation.getSamples().get(rOperation.getSamples().size() - 1); + rOperation.bwMax = last.cumMax; + rOperation.bwMin = last.cumMin; + rOperation.bwAvg = last.cumAvg; + rOperation.accAvg = last.cumAccTimeMs; + } + } + })); + } + + executorService.shutdown(); + executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); + + if (renderMode == RenderFrequencyMode.PER_OPERATION) { + for (Sample s : rOperation.getSamples()) { + App.updateMetrics(s); + publish(s); + } + } + + // GH-10 file IOPS processing + rOperation.endTime = LocalDateTime.now(); + rOperation.setTotalOps(rUnitsComplete[0]); + App.rIops = rOperation.iops; + Gui.mainFrame.refreshReadMetrics(); + } + benchmark.endTime = LocalDateTime.now(); + EntityManager em = EM.getEntityManager(); + em.getTransaction().begin(); + em.persist(benchmark); + em.getTransaction().commit(); + App.benchmarks.put(benchmark.getStartTimeString(), benchmark); + for (BenchmarkOperation o : benchmark.getOperations()) { + App.operations.put(o.getStartTimeString(), o); + } + Gui.runPanel.addRun(benchmark); + + App.nextSampleNumber += App.numOfSamples; + return true; + } + + @Override + protected void process(List<Sample> sampleList) { + sampleList.stream().forEach((Sample s) -> { + switch (s.type) { + case Sample.Type.WRITE -> + Gui.addWriteSample(s); + case Sample.Type.READ -> + Gui.addReadSample(s); + } + }); + } + + @Override + protected void done() { + if (App.autoRemoveData) { + Util.deleteDirectory(dataDir); + } + App.state = App.State.IDLE_STATE; + Gui.mainFrame.adjustSensitivity(); + } +} diff --git a/src/jdiskmark/CenterTableCellRenderer.java b/src/jdiskmark/CenterTableCellRenderer.java new file mode 100644 index 0000000..380a6f4 --- /dev/null +++ b/src/jdiskmark/CenterTableCellRenderer.java @@ -0,0 +1,18 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package jdiskmark; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; + +public class CenterTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setHorizontalAlignment(SwingConstants.CENTER); + return c; + } +} \ No newline at end of file diff --git a/src/jdiskmark/DiskMark.java b/src/jdiskmark/DiskMark.java deleted file mode 100644 index 2936035..0000000 --- a/src/jdiskmark/DiskMark.java +++ /dev/null @@ -1,46 +0,0 @@ - -package jdiskmark; - -import java.text.DecimalFormat; - -/** - * - */ -public class DiskMark { - - static DecimalFormat df = new DecimalFormat("###.###"); - - public enum MarkType { READ,WRITE; } - - DiskMark(MarkType type) { - this.type=type; - } - - MarkType type; - int markNum = 0; // x-axis - double bwMbSec = 0; // y-axis - double cumMin = 0; - double cumMax = 0; - double cumAvg = 0; - - @Override - public String toString() { - return "Mark("+type+"): "+markNum+" bwMbSec: "+getBwMbSec()+" avg: "+getAvg(); - } - - String getBwMbSec() { - return df.format(bwMbSec); - } - - String getMin() { - return df.format(cumMin); - } - - String getMax() { - return df.format(cumMax); - } - - String getAvg() { - return df.format(cumAvg); - } -} diff --git a/src/jdiskmark/DiskRun.java b/src/jdiskmark/DiskRun.java deleted file mode 100644 index 2efde14..0000000 --- a/src/jdiskmark/DiskRun.java +++ /dev/null @@ -1,157 +0,0 @@ - -package jdiskmark; - -import java.io.Serializable; -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EntityManager; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; - -/** - * - */ -@Entity -@Table(name="DiskRun") -@NamedQueries({ -@NamedQuery(name="DiskRun.findAll", - query="SELECT d FROM DiskRun d") -}) -public class DiskRun implements Serializable { - - static final DecimalFormat DF = new DecimalFormat("###.##"); - static final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, MMM d HH:mm:ss"); - - static public enum IOMode { READ, WRITE, READ_WRITE; } - static public enum BlockSequence {SEQUENTIAL, RANDOM; } - - @Column - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - long id; - - // configuration - @Column - String diskInfo = null; - @Column - IOMode ioMode; - @Column - BlockSequence blockOrder; - @Column - int numMarks = 0; - @Column - int numBlocks = 0; - @Column - int blockSize = 0; - @Column - long txSize = 0; - @Temporal(TemporalType.TIMESTAMP) - @Column - Date startTime; - @Temporal(TemporalType.TIMESTAMP) - @Column - Date endTime = null; - @Column - int totalMarks = 0; - @Column - double runMin = 0; - @Column - double runMax = 0; - @Column - double runAvg = 0; - - @Override - public String toString() { - return "Run("+ioMode+","+blockOrder+"): "+totalMarks+" run avg: "+runAvg; - } - - public DiskRun() { - this.startTime = new Date(); - } - - DiskRun(IOMode type, BlockSequence order) { - this.startTime = new Date(); - ioMode = type; - blockOrder = order; - } - - // display friendly methods - - public String getStartTimeString() { - return DATE_FORMAT.format(startTime); - } - - public String getMin() { - return runMin == -1 ? "- -" : DF.format(runMin); - } - - public void setMin(double min) { - runMin = min; - } - - public String getMax() { - return runMax == -1 ? "- -" : DF.format(runMax); - } - - public void setMax(double max) { - runMax = max; - } - - public String getAvg() { - return runAvg == -1 ? "- -" : DF.format(runAvg); - } - - public void SetAvg(double avg) { - runAvg = avg; - } - - public String getDuration() { - if (endTime == null) { - return "unknown"; - } - long duration = endTime.getTime() - startTime.getTime(); - long diffSeconds = duration / 1000 % 60; - return String.valueOf(diffSeconds) + "s"; - } - - // basic getters and setters - - public Long getId() { - return id; - } - public void setId(Long id) { - this.id = id; - } - public String getDiskInfo() { - return diskInfo; - } - public void setDiskInfo(String info) { - diskInfo = info; - } - - // utility methods for collection - - static List<DiskRun> findAll() { - EntityManager em = EM.getEntityManager(); - return em.createNamedQuery("DiskRun.findAll", DiskRun.class).getResultList(); - } - - static int deleteAll() { - EntityManager em = EM.getEntityManager(); - em.getTransaction().begin(); - int deletedCount = em.createQuery("DELETE FROM DiskRun").executeUpdate(); - em.getTransaction().commit(); - return deletedCount; - } -} diff --git a/src/jdiskmark/DiskUsageInfo.java b/src/jdiskmark/DiskUsageInfo.java new file mode 100644 index 0000000..0a7e85f --- /dev/null +++ b/src/jdiskmark/DiskUsageInfo.java @@ -0,0 +1,52 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package jdiskmark; + +import java.text.DecimalFormat; + +public class DiskUsageInfo { + static final DecimalFormat DFT = new DecimalFormat("#"); + + public long percentUsed; + public double freeGb; + public double usedGb; + public double totalGb; + + public DiskUsageInfo() {} + + public DiskUsageInfo(double percentUsed, double usedGB, double totalGB) { + this.percentUsed = Math.round(percentUsed); + this.usedGb = usedGB; + this.totalGb = totalGB; + } + + public DiskUsageInfo(double percentUsed, double freeGB, double usedGB, double totalGB) { + this.percentUsed = Math.round(percentUsed); + this.freeGb = freeGB; + this.usedGb = usedGB; + this.totalGb = totalGB; + } + + public long calcPercentageUsed() { + if (totalGb != 0) { + percentUsed = Math.round(100 * usedGb / totalGb); + } + return percentUsed; + } + + /** + * Format as: + * option a: [23%] (52/228 GB) + * option b: 23% 52/228GB + * @return + */ + public String toDisplayString() { + return percentUsed + "% " + DFT.format(usedGb) + "/" + DFT.format(totalGb) + " GB"; + } + + public String getUsageTitleDisplay() { + return percentUsed + "% (" + DFT.format(usedGb) + "/" + DFT.format(totalGb) + " GB)"; + } +} \ No newline at end of file diff --git a/src/jdiskmark/DiskWorker.java b/src/jdiskmark/DiskWorker.java deleted file mode 100644 index dae7659..0000000 --- a/src/jdiskmark/DiskWorker.java +++ /dev/null @@ -1,249 +0,0 @@ - -package jdiskmark; - -import static jdiskmark.App.KILOBYTE; -import static jdiskmark.App.MEGABYTE; -import static jdiskmark.App.blockSizeKb; -import static jdiskmark.App.msg; -import static jdiskmark.App.numOfBlocks; -import static jdiskmark.App.testFile; -import static jdiskmark.App.dataDir; -import static jdiskmark.App.numOfMarks; -import static jdiskmark.DiskMark.MarkType.READ; -import static jdiskmark.DiskMark.MarkType.WRITE; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Date; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.persistence.EntityManager; -import javax.swing.JOptionPane; -import javax.swing.SwingWorker; - -/** - * Thread running the disk benchmarking. only one of these threads can run at - * once. - */ -public class DiskWorker extends SwingWorker <Boolean, DiskMark> { - - @Override - protected Boolean doInBackground() throws Exception { - - System.out.println("*** starting new worker thread"); - msg("Running readTest "+App.readTest+" writeTest "+App.writeTest); - msg("num files: "+App.numOfMarks+", num blks: "+App.numOfBlocks - +", blk size (kb): "+App.blockSizeKb+", blockSequence: "+App.blockSequence); - - int wUnitsComplete = 0, - rUnitsComplete = 0, - unitsComplete; - - int wUnitsTotal = App.writeTest ? numOfBlocks * numOfMarks : 0; - int rUnitsTotal = App.readTest ? numOfBlocks * numOfMarks : 0; - int unitsTotal = wUnitsTotal + rUnitsTotal; - float percentComplete; - - int blockSize = blockSizeKb*KILOBYTE; - byte [] blockArr = new byte [blockSize]; - for (int b=0; b<blockArr.length; b++) { - if (b%2==0) { - blockArr[b]=(byte)0xFF; - } - } - - DiskMark wMark, rMark; - - Gui.updateLegend(); - - if (App.autoReset == true) { - App.resetTestData(); - Gui.resetTestData(); - } - - int startFileNum = App.nextMarkNumber; - - if(App.writeTest) { - DiskRun run = new DiskRun(DiskRun.IOMode.WRITE, App.blockSequence); - run.numMarks = App.numOfMarks; - run.numBlocks = App.numOfBlocks; - run.blockSize = App.blockSizeKb; - run.txSize = App.targetTxSizeKb(); - run.setDiskInfo(Util.getDiskInfo(dataDir)); - - msg("disk info: ("+ run.getDiskInfo()+")"); - - Gui.chartPanel.getChart().getTitle().setVisible(true); - Gui.chartPanel.getChart().getTitle().setText(run.getDiskInfo()); - - if (App.multiFile == false) { - testFile = new File(dataDir.getAbsolutePath()+File.separator+"testdata.jdm"); - } - for (int m=startFileNum; m<startFileNum+App.numOfMarks && !isCancelled(); m++) { - - if (App.multiFile == true) { - testFile = new File(dataDir.getAbsolutePath() - + File.separator+"testdata"+m+".jdm"); - } - wMark = new DiskMark(WRITE); - wMark.markNum = m; - long startTime = System.nanoTime(); - long totalBytesWrittenInMark = 0; - - String mode = "rw"; - if (App.writeSyncEnable) { mode = "rwd"; } - - try { - try (RandomAccessFile rAccFile = new RandomAccessFile(testFile,mode)) { - for (int b=0; b<numOfBlocks; b++) { - if (App.blockSequence == DiskRun.BlockSequence.RANDOM) { - int rLoc = Util.randInt(0, numOfBlocks-1); - rAccFile.seek(rLoc*blockSize); - } else { - rAccFile.seek(b*blockSize); - } - rAccFile.write(blockArr, 0, blockSize); - totalBytesWrittenInMark += blockSize; - wUnitsComplete++; - unitsComplete = rUnitsComplete + wUnitsComplete; - percentComplete = (float)unitsComplete/(float)unitsTotal * 100f; - setProgress((int)percentComplete); - } - } - } catch (FileNotFoundException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } - long endTime = System.nanoTime(); - long elapsedTimeNs = endTime - startTime; - double sec = (double)elapsedTimeNs / (double)1000000000; - double mbWritten = (double)totalBytesWrittenInMark / (double)MEGABYTE; - wMark.bwMbSec = mbWritten / sec; - msg("m:"+m+" write IO is "+wMark.getBwMbSec()+" MB/s " - + "("+Util.displayString(mbWritten)+ "MB written in " - + Util.displayString(sec)+" sec)"); - App.updateMetrics(wMark); - publish(wMark); - - run.runMax = wMark.cumMax; - run.runMin = wMark.cumMin; - run.runAvg = wMark.cumAvg; - run.endTime = new Date(); - } - - EntityManager em = EM.getEntityManager(); - em.getTransaction().begin(); - em.persist(run); - em.getTransaction().commit(); - - Gui.runPanel.addRun(run); - } - - - // try renaming all files to clear catch - if (App.readTest && App.writeTest && !isCancelled()) { - JOptionPane.showMessageDialog(Gui.mainFrame, - "For valid READ measurements please clear the disk cache by\n" + - "using the included RAMMap.exe or flushmem.exe utilities.\n" + - "Removable drives can be disconnected and reconnected.\n" + - "For system drives use the WRITE and READ operations \n" + - "independantly by doing a cold reboot after the WRITE", - "Clear Disk Cache Now",JOptionPane.PLAIN_MESSAGE); - } - - if (App.readTest) { - DiskRun run = new DiskRun(DiskRun.IOMode.READ, App.blockSequence); - run.numMarks = App.numOfMarks; - run.numBlocks = App.numOfBlocks; - run.blockSize = App.blockSizeKb; - run.txSize = App.targetTxSizeKb(); - run.setDiskInfo(Util.getDiskInfo(dataDir)); - - msg("disk info: ("+ run.getDiskInfo()+")"); - - Gui.chartPanel.getChart().getTitle().setVisible(true); - Gui.chartPanel.getChart().getTitle().setText(run.getDiskInfo()); - - for (int m=startFileNum; m<startFileNum+App.numOfMarks && !isCancelled(); m++) { - - if (App.multiFile == true) { - testFile = new File(dataDir.getAbsolutePath() - + File.separator+"testdata"+m+".jdm"); - } - rMark = new DiskMark(READ); - rMark.markNum = m; - long startTime = System.nanoTime(); - long totalBytesReadInMark = 0; - - try { - try (RandomAccessFile rAccFile = new RandomAccessFile(testFile,"r")) { - for (int b=0; b<numOfBlocks; b++) { - if (App.blockSequence == DiskRun.BlockSequence.RANDOM) { - int rLoc = Util.randInt(0, numOfBlocks-1); - rAccFile.seek(rLoc*blockSize); - } else { - rAccFile.seek(b*blockSize); - } - rAccFile.readFully(blockArr, 0, blockSize); - totalBytesReadInMark += blockSize; - rUnitsComplete++; - unitsComplete = rUnitsComplete + wUnitsComplete; - percentComplete = (float)unitsComplete/(float)unitsTotal * 100f; - setProgress((int)percentComplete); - } - } - } catch (FileNotFoundException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex); - } - long endTime = System.nanoTime(); - long elapsedTimeNs = endTime - startTime; - double sec = (double)elapsedTimeNs / (double)1000000000; - double mbRead = (double) totalBytesReadInMark / (double) MEGABYTE; - rMark.bwMbSec = mbRead / sec; - msg("m:"+m+" READ IO is "+rMark.bwMbSec+" MB/s " - + "(MBread "+mbRead+" in "+sec+" sec)"); - App.updateMetrics(rMark); - publish(rMark); - - run.runMax = rMark.cumMax; - run.runMin = rMark.cumMin; - run.runAvg = rMark.cumAvg; - run.endTime = new Date(); - } - - EntityManager em = EM.getEntityManager(); - em.getTransaction().begin(); - em.persist(run); - em.getTransaction().commit(); - - Gui.runPanel.addRun(run); - } - App.nextMarkNumber += App.numOfMarks; - return true; - } - - @Override - protected void process(List<DiskMark> markList) { - markList.stream().forEach((m) -> { - if (m.type==DiskMark.MarkType.WRITE) { - Gui.addWriteMark(m); - } else { - Gui.addReadMark(m); - } - }); - } - - @Override - protected void done() { - if (App.autoRemoveData) { - Util.deleteDirectory(dataDir); - } - App.state = App.State.IDLE_STATE; - Gui.mainFrame.adjustSensitivity(); - } -} diff --git a/src/jdiskmark/EM.java b/src/jdiskmark/EM.java index 1765971..1268496 100644 --- a/src/jdiskmark/EM.java +++ b/src/jdiskmark/EM.java @@ -1,16 +1,12 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ + package jdiskmark; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.Persistence; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; /** - * + * Access class for the entity manager static reference * @author James */ public class EM { @@ -19,7 +15,7 @@ public class EM { static EntityManager getEntityManager() { if (em == null) { - EntityManagerFactory emf = Persistence.createEntityManagerFactory("jDiskMarkPU"); + EntityManagerFactory emf = Persistence.createEntityManagerFactory("JDiskMarkPU"); em = emf.createEntityManager(); } return em; diff --git a/src/jdiskmark/Gui.java b/src/jdiskmark/Gui.java index aa97af0..e8299ba 100644 --- a/src/jdiskmark/Gui.java +++ b/src/jdiskmark/Gui.java @@ -1,114 +1,306 @@ - + package jdiskmark; +import com.formdev.flatlaf.FlatLightLaf; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; +import java.awt.Shape; +import java.awt.geom.Rectangle2D; +import java.io.File; import java.text.NumberFormat; +import java.util.ArrayList; +import javax.swing.JOptionPane; import javax.swing.JProgressBar; -import org.jfree.chart.ChartFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import org.jfree.chart.ChartMouseEvent; +import org.jfree.chart.ChartMouseListener; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; -import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; +import org.jfree.ui.RectangleInsets; +import jdiskmark.App; + /** - * Store gui references for easy access + * Store GUI references for easy access */ public final class Gui { + public static enum Palette { CLASSIC, BLUE_GREEN, BARD_COOL, BARD_WARM }; + public static Palette palette = Palette.CLASSIC; + + private static jdiskmark.RenderFrequencyMode renderFrequencyMode = jdiskmark.RenderFrequencyMode.PER_SAMPLE; + + public static RenderFrequencyMode getRenderFrequencyMode() { + return renderFrequencyMode; + } + + public static void setRenderFrequencyMode(RenderFrequencyMode mode) { + renderFrequencyMode = mode; + } + public static ChartPanel chartPanel = null; + public static JLabel renderModeLabel = new JLabel("Render Mode: PER_SAMPLE"); public static MainFrame mainFrame = null; - public static SelectFrame selFrame = null; - public static XYSeries wSeries, wAvgSeries, wMaxSeries, wMinSeries; - public static XYSeries rSeries, rAvgSeries, rMaxSeries, rMinSeries; + public static AdvancedOptionsFrame advancedFrame = null; + public static SelectDriveFrame selFrame = null; + public static XYSeries wSeries, wAvgSeries, wMaxSeries, wMinSeries, wDrvAccess; + public static XYSeries rSeries, rAvgSeries, rMaxSeries, rMinSeries, rDrvAccess; + public static NumberAxis msAxis; public static JFreeChart chart; public static JProgressBar progressBar = null; - public static RunPanel runPanel = null; + public static BenchmarkPanel runPanel = null; + + public static XYLineAndShapeRenderer bwRenderer; + public static XYLineAndShapeRenderer msRenderer; + + /** + * Setup the look and feel + */ + public static void configureLaf() { + try { + if (App.os.contains("Windows")) { + UIManager.setLookAndFeel(new FlatLightLaf()); // Light theme + // Or: UIManager.setLookAndFeel(new FlatDarkLaf()); // Dark theme + } else if (App.os.contains("Mac OS")) { + UIManager.setLookAndFeel("apple.laf.AquaLookAndFeel"); +// for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { +// if ("Nimbus".equals(info.getName())) { +// UIManager.setLookAndFeel(info.getClassName()); +// break; +// } +// } + } else if (App.os.contains("Linux")) { + UIManager.setLookAndFeel(new FlatLightLaf()); // Light theme +// for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { +// if ("Nimbus".equals(info.getName())) { +// UIManager.setLookAndFeel(info.getClassName()); +// break; +// } +// } + } + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { + //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> + /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. + * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html + */ + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(MainFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); + } + //</editor-fold> + } + } - public static ChartPanel createChartPanel() { + public static void updateRenderView() { + RenderFrequencyMode mode = Gui.mainFrame.getRenderMode(); + + boolean showChart = true; // all modes show chart in this example + boolean showTable = (mode == RenderFrequencyMode.PER_100MS || + mode == RenderFrequencyMode.PER_500MS || + mode == RenderFrequencyMode.PER_1000MS); + + if (chartPanel != null) { + chartPanel.setVisible(showChart); + } + + Gui.mainFrame.setTableVisible(showTable); - wSeries = new XYSeries("Writes"); + } + + public static void refreshRenderLabel() { + if (renderModeLabel != null) { + renderModeLabel.setText("Render Mode: " + getRenderFrequencyMode()); + } + } + + public static JPanel createChartPanel() { + + wSeries = new XYSeries("Write"); wAvgSeries = new XYSeries("Write Avg"); wMaxSeries = new XYSeries("Write Max"); wMinSeries = new XYSeries("Write Min"); + wDrvAccess = new XYSeries("Write Access"); - rSeries = new XYSeries("Reads"); + rSeries = new XYSeries("Read"); rAvgSeries = new XYSeries("Read Avg"); rMaxSeries = new XYSeries("Read Max"); rMinSeries = new XYSeries("Read Min"); + rDrvAccess = new XYSeries("Read Access"); + + // primary dataset mapped against the bw axis + XYSeriesCollection bwDataset = new XYSeriesCollection(); + bwDataset.addSeries(wSeries); + bwDataset.addSeries(wAvgSeries); + bwDataset.addSeries(wMaxSeries); + bwDataset.addSeries(wMinSeries); + bwDataset.addSeries(rSeries); + bwDataset.addSeries(rAvgSeries); + bwDataset.addSeries(rMaxSeries); + bwDataset.addSeries(rMinSeries); + + // secondary dataset mapped against ns to show disk access time + XYSeriesCollection msDataset = new XYSeriesCollection(); + msDataset.addSeries(wDrvAccess); + msDataset.addSeries(rDrvAccess); + + // setup plot + XYPlot plot = new XYPlot(); + plot.setBackgroundPaint(Color.DARK_GRAY.darker()); + plot.setOutlinePaint(Color.WHITE); + plot.setDataset(0, bwDataset); + plot.setDataset(1, msDataset); + + //customize the plot with renderers and axis + bwRenderer = new XYLineAndShapeRenderer(true, false); + msRenderer = new XYLineAndShapeRenderer(true, false); + + // disable lines and enable shapes + msRenderer.setSeriesLinesVisible(0, false); + msRenderer.setSeriesLinesVisible(1, false); + Shape s0 = new Rectangle2D.Double(-2.0, -2.0, 4.0, 4.0); + Shape s1 = new Rectangle2D.Double(-2.0, -2.0, 4.0, 4.0); + msRenderer.setSeriesShape(0, s0); + msRenderer.setSeriesShape(1, s1); + msRenderer.setSeriesShapesVisible(0, true); + msRenderer.setSeriesShapesVisible(1, true); + + // link renderers to the plot + plot.setRenderer(0, bwRenderer); + plot.setRenderer(1, msRenderer); + + // y axis on the left + NumberAxis bwAxis = new NumberAxis("Bandwidth (MB/s)"); + bwAxis.setAutoRangeIncludesZero(false); + + // y axis on the right + msAxis = new NumberAxis("Access Time (ms)"); + msAxis.setAutoRange(true); + msAxis.setAutoRangeIncludesZero(false); + + // x axis on the bottom + NumberAxis sampleAxis = new NumberAxis(); + sampleAxis.setNumberFormatOverride(NumberFormat.getNumberInstance()); + sampleAxis.setAutoRangeIncludesZero(false); + + // link the axis to the plot + plot.setRangeAxis(0, bwAxis); + plot.setRangeAxis(1, msAxis); + plot.setDomainAxis(sampleAxis); + + // configure the locations + plot.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); + plot.setRangeAxisLocation(0, AxisLocation.TOP_OR_LEFT); + plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT); + + // add gap between the plot area and axis so they are detached + plot.setAxisOffset(new RectangleInsets(3, 3, 3, 3)); + + // Map the data to the appropriate axis + plot.mapDatasetToRangeAxis(0, 0); + plot.mapDatasetToRangeAxis(1, 1); + + chart = new JFreeChart("", null , plot, true); + + chart.getTitle().setFont(new Font("Verdana", Font.BOLD, 17)); - XYSeriesCollection dataset = new XYSeriesCollection(); - dataset.addSeries(wSeries); - dataset.addSeries(wAvgSeries); - dataset.addSeries(wMaxSeries); - dataset.addSeries(wMinSeries); - dataset.addSeries(rSeries); - dataset.addSeries(rAvgSeries); - dataset.addSeries(rMaxSeries); - dataset.addSeries(rMinSeries); - - chart = ChartFactory.createXYLineChart( - "XY Chart", // Title - null, // x-axis Label - "Bandwidth MB/s", // y-axis Label - dataset, // Dataset - PlotOrientation.VERTICAL, // Plot Orientation - true,// Show Legend - true, // Use tooltips - false // Configure chart to generate URLs? - ); - XYPlot plot = chart.getXYPlot(); - plot.setBackgroundPaint(Color.DARK_GRAY); - ((NumberAxis) plot.getRangeAxis()).setAutoRangeIncludesZero(false); - NumberAxis range = (NumberAxis) plot.getDomainAxis(); - range.setNumberFormatOverride(NumberFormat.getNumberInstance()); - chart.getTitle().setVisible(false); chartPanel = new ChartPanel(chart) { - // Only way to set the size of chart panel - // ref: http://www.jfree.org/phpBB2/viewtopic.php?p=75516 @Override public Dimension getPreferredSize() { return new Dimension(500, 325); } }; - plot.getRenderer().setSeriesPaint(0, Color.YELLOW); - plot.getRenderer().setSeriesPaint(1, Color.WHITE); - plot.getRenderer().setSeriesPaint(2, Color.GREEN); - plot.getRenderer().setSeriesPaint(3, Color.RED); - plot.getRenderer().setSeriesPaint(4, Color.LIGHT_GRAY); - plot.getRenderer().setSeriesPaint(5, Color.ORANGE); - plot.getRenderer().setSeriesPaint(6, Color.GREEN); - plot.getRenderer().setSeriesPaint(7, Color.RED); - updateLegend(); - return chartPanel; - } - - public static void addWriteMark(DiskMark mark) { - wSeries.add(mark.markNum, mark.bwMbSec); - wAvgSeries.add(mark.markNum, mark.cumAvg); + chartPanel.addChartMouseListener(new ChartMouseListener() { + private long lastClickTime = 0; + @Override + public void chartMouseClicked(ChartMouseEvent event) { + long currentTime = System.currentTimeMillis(); + if (currentTime - lastClickTime < 500) { // Check for double click + /* TODO: implement Detect click on chart title area */ + //Rectangle2D titleArea = chart.getTitle().getBounds(); + //boolean isInTitleArea = titleArea.contains(event.getTrigger().getX(), event.getTrigger().getY()); + //if (isInTitleArea) { + // open selection dialog + Gui.browseLocation(); + //} + } + lastClickTime = currentTime; + } + + @Override + public void chartMouseMoved(ChartMouseEvent cme) { + // no action + } + }); + updateLegendAndAxis(); + + // Creates a wrapper panel with vertical layout + JPanel wrapperPanel = new JPanel(new java.awt.BorderLayout()); + + // Creates a small left-aligned panel for the label + JPanel topPanel = new JPanel(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 5, 2)); + topPanel.setOpaque(false); + + // Render Mode label + renderModeLabel = new JLabel("Render Mode: " + Gui.getRenderFrequencyMode()); + renderModeLabel.setFont(new Font("Verdana", Font.BOLD, 10)); + renderModeLabel.setForeground(Color.BLACK); + + // Add subtle light-gray background with slight transparency + renderModeLabel.setOpaque(true); + renderModeLabel.setBackground(new Color(240, 240, 240, 200)); // light gray w/ transparency + renderModeLabel.setBorder(javax.swing.BorderFactory.createEmptyBorder(2, 6, 2, 6)); // padding + + // Add label to top panel + topPanel.add(renderModeLabel); + + // Add top panel and chart panel to wrapper + wrapperPanel.add(topPanel, java.awt.BorderLayout.NORTH); + wrapperPanel.add(chartPanel, java.awt.BorderLayout.CENTER); + + return wrapperPanel; + } + + public static void addWriteSample(Sample s) { + wSeries.add(s.sampleNum, s.bwMbSec); + wAvgSeries.add(s.sampleNum, s.cumAvg); if (App.showMaxMin) { - wMaxSeries.add(mark.markNum, mark.cumMax); - wMinSeries.add(mark.markNum, mark.cumMin); + wMaxSeries.add(s.sampleNum, s.cumMax); + wMinSeries.add(s.sampleNum, s.cumMin); + } + if (App.showDriveAccess) { + wDrvAccess.add(s.sampleNum, s.accessTimeMs); } Gui.mainFrame.refreshWriteMetrics(); - System.out.println(mark.toString()); + //System.out.println(s.toString()); } - public static void addReadMark(DiskMark mark) { - rSeries.add(mark.markNum, mark.bwMbSec); - rAvgSeries.add(mark.markNum, mark.cumAvg); + public static void addReadSample(Sample s) { + rSeries.add(s.sampleNum, s.bwMbSec); + rAvgSeries.add(s.sampleNum, s.cumAvg); if (App.showMaxMin) { - rMaxSeries.add(mark.markNum, mark.cumMax); - rMinSeries.add(mark.markNum, mark.cumMin); + rMaxSeries.add(s.sampleNum, s.cumMax); + rMinSeries.add(s.sampleNum, s.cumMin); + } + if (App.showDriveAccess) { + rDrvAccess.add(s.sampleNum, s.accessTimeMs); } Gui.mainFrame.refreshReadMetrics(); - System.out.println(mark.toString()); + //System.out.println(s.toString()); } - public static void resetTestData() { + public static void resetBenchmarkData() { wSeries.clear(); rSeries.clear(); wAvgSeries.clear(); @@ -117,20 +309,302 @@ public static void resetTestData() { rMaxSeries.clear(); wMinSeries.clear(); rMinSeries.clear(); + wDrvAccess.clear(); + rDrvAccess.clear(); progressBar.setValue(0); Gui.mainFrame.refreshReadMetrics(); Gui.mainFrame.refreshWriteMetrics(); } - public static void updateLegend() { - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(0, App.writeTest); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(1, App.writeTest); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(2, App.writeTest&&App.showMaxMin); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(3, App.writeTest&&App.showMaxMin); + public static void updateLegendAndAxis() { + bwRenderer.setSeriesVisibleInLegend(0, App.isWriteEnabled()); + bwRenderer.setSeriesVisibleInLegend(1, App.isWriteEnabled()); + bwRenderer.setSeriesVisibleInLegend(2, App.isWriteEnabled() && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(3, App.isWriteEnabled() && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(4, App.isReadEnabled()); + bwRenderer.setSeriesVisibleInLegend(5, App.isReadEnabled()); + bwRenderer.setSeriesVisibleInLegend(6, App.isReadEnabled() && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(7, App.isReadEnabled() && App.showMaxMin); + + msRenderer.setSeriesVisibleInLegend(0, App.isWriteEnabled() && App.showDriveAccess); + msRenderer.setSeriesVisibleInLegend(1, App.isReadEnabled() && App.showDriveAccess); + + + msAxis.setVisible(App.showDriveAccess); + } + + public static void updateLegendAndAxis(BenchmarkOperation o) { + boolean isWriteTest = o.ioMode == BenchmarkOperation.IOMode.WRITE; + boolean isReadTest = o.ioMode == BenchmarkOperation.IOMode.READ; + bwRenderer.setSeriesVisibleInLegend(0, isWriteTest); + bwRenderer.setSeriesVisibleInLegend(1, isWriteTest); + bwRenderer.setSeriesVisibleInLegend(2, isWriteTest && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(3, isWriteTest && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(4, isReadTest); + bwRenderer.setSeriesVisibleInLegend(5, isReadTest); + bwRenderer.setSeriesVisibleInLegend(6, isReadTest && App.showMaxMin); + bwRenderer.setSeriesVisibleInLegend(7, isReadTest && App.showMaxMin); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(4, App.readTest); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(5, App.readTest); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(6, App.readTest&&App.showMaxMin); - chart.getXYPlot().getRenderer().setSeriesVisibleInLegend(7, App.readTest&&App.showMaxMin); + msRenderer.setSeriesVisibleInLegend(0, isWriteTest && App.showDriveAccess); + msRenderer.setSeriesVisibleInLegend(1, isReadTest && App.showDriveAccess); + + msAxis.setVisible(App.showDriveAccess); + } + + static public void updateDiskInfo() { + Gui.mainFrame.setLocation(App.locationDir.getAbsolutePath()); + Gui.chart.getTitle().setText(App.getDriveInfo()); + } + + /** + * GH-2 need solution for dropping catch + */ + static public void dropCache() { + String osName = System.getProperty("os.name"); + if (osName.contains("Linux")) { + if (App.isRoot) { + // GH-2 automate catch dropping + UtilOs.flushDataToDriveLinux(); + UtilOs.dropWriteCacheLinux(); + } else { + /* Revised the drop_caches command so it works. - JSL 2024-01-16 */ + JOptionPane.showMessageDialog(Gui.mainFrame, + """ + Run JDiskMark with sudo to automatically clear the disk cache. + + For a valid READ benchmark please clear the disk cache now + by using: \"sudo sh -c \'sync; echo 1 > /proc/sys/vm/drop_caches\'\". + + Press OK to continue when disk cache has been dropped.""", + "Clear Disk Cache Now", + JOptionPane.PLAIN_MESSAGE); + } + } else if (osName.contains("Mac OS")) { + if (App.isRoot) { + // GH-2 automate catch dropping + UtilOs.flushDataToDriveMacOs(); + UtilOs.dropWriteCacheMacOs(); + } else { + JOptionPane.showMessageDialog(Gui.mainFrame, + """ + For valid READ benchmarks please clear the disk cache. + + Removable drives can be disconnected and reconnected. + + For system drives perform a WRITE benchmark, restart + the OS and then perform a READ benchmark. + + Press OK to continue when disk cache has been cleared.""", + "Clear Disk Cache Now", + JOptionPane.PLAIN_MESSAGE); + } + } else if (osName.contains("Windows")) { + File emptyStandbyListExe = new File(".\\" + App.ESBL_EXE); + if (!emptyStandbyListExe.exists()) { + // jpackage windows relative environment + emptyStandbyListExe = new File(".\\app\\" + App.ESBL_EXE); + } + System.out.println("emptyStandbyListExe.exist=" + emptyStandbyListExe.exists()); + if (App.isAdmin && emptyStandbyListExe.exists()) { + // GH-2 drop cahe, delays in place of flushing cache + try { Thread.sleep(1300); } catch (InterruptedException ex) {} + UtilOs.emptyStandbyListWindows(emptyStandbyListExe); + try { Thread.sleep(700); } catch (InterruptedException ex) {} + } else if (App.isAdmin && !emptyStandbyListExe.exists()) { + JOptionPane.showMessageDialog(Gui.mainFrame, + """ + Unable to find EmptyStandbyList.exe. This must be + present in the install directory for the disk cache + to be automatically cleared. + + For valid READ benchmarks please clear the disk cache by + using EmptyStandbyList.exe or RAMMap.exe utilities. + + For system drives perform a WRITE benchmark, restart + the OS and then perform a READ benchmark. + + Press OK to continue when disk cache has been cleared. + """, + "Missing Disk Cache Utility", + JOptionPane.WARNING_MESSAGE); + } else if (!App.isAdmin) { + JOptionPane.showMessageDialog(Gui.mainFrame, + """ + Run JDiskMark as admin to automatically clear the disk cache. + + For valid READ benchmarks please clear the disk cache by + using EmptyStandbyList.exe or RAMMap.exe utilities. + + For system drives perform a WRITE benchmark, restart + the OS and then perform a READ benchmark. + + Press OK to continue when disk cache has been cleared.""", + "Clear Disk Cache Now", + JOptionPane.PLAIN_MESSAGE); + } + } else { + String messagePrompt = "Unrecognized OS: " + osName + "\n" + + """ + For valid READ benchmarks please clear the disk cache now. + + Removable drives can be disconnected and reconnected. + + For system drives perform a WRITE benchmark, restart + the OS and then perform a READ benchmarks benchmark. + + Press OK to continue when disk cache has been cleared."""; + JOptionPane.showMessageDialog(Gui.mainFrame, + messagePrompt, + "Clear Disk Cache Now", + JOptionPane.PLAIN_MESSAGE); + } + } + + static public void loadOperation(BenchmarkOperation operation) { + Benchmark benchmark = operation.getBenchmark(); + System.out.println("benchmark type: " + benchmark.benchmarkType + " o: " + operation.ioMode); + resetBenchmarkData(); + updateLegendAndAxis(operation); + chart.getTitle().setText(benchmark.getDriveInfo()); + ArrayList<Sample> samples = operation.getSamples(); + System.out.println("samples=" + samples.size()); + for (Sample s : samples) { + if (operation.ioMode == BenchmarkOperation.IOMode.READ) { + addReadSample(s); + } else { + addWriteSample(s); + } + } + App.benchmarkType = benchmark.benchmarkType; + App.numOfBlocks = operation.numBlocks; + App.numOfSamples = operation.numSamples; + App.blockSizeKb = operation.blockSize; + App.blockSequence = operation.blockOrder; + App.numOfThreads = operation.numThreads; + Gui.mainFrame.loadSettings(); + switch (operation.ioMode) { + case BenchmarkOperation.IOMode.READ -> { + App.rAvg = operation.bwAvg; + App.rMax = operation.bwMax; + App.rMin = operation.bwMin; + App.rAcc = operation.accAvg; + App.rIops = operation.iops; + Gui.mainFrame.refreshReadMetrics(); + } + case BenchmarkOperation.IOMode.WRITE -> { + App.wAvg = operation.bwAvg; + App.wMax = operation.bwMax; + App.wMin = operation.bwMin; + App.wAcc = operation.accAvg; + App.wIops = operation.iops; + Gui.mainFrame.refreshWriteMetrics(); + } + } + } + + /** + * The original color scheme. + */ + static void setClassicColorScheme() { + System.out.println("setting classic palette"); + palette = Palette.CLASSIC; + + // configure the bw series colors + bwRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + bwRenderer.setSeriesPaint(0, Color.YELLOW); // write + bwRenderer.setSeriesPaint(1, Color.WHITE); // w avg + bwRenderer.setSeriesPaint(2, Color.GREEN); // w max + bwRenderer.setSeriesPaint(3, Color.RED); // w min + bwRenderer.setSeriesPaint(4, Color.LIGHT_GRAY); // read + bwRenderer.setSeriesPaint(5, Color.ORANGE); // r avg + bwRenderer.setSeriesPaint(6, Color.GREEN.darker()); // r max + bwRenderer.setSeriesPaint(7, Color.RED.darker()); // r min + + // configure the access time ms colors + msRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + msRenderer.setSeriesPaint(0, Color.CYAN); // w acc + msRenderer.setSeriesPaint(1, Color.MAGENTA); // r acc + } + + /** + * Here is my blue green scheme. can be improved. + */ + static void setBlueGreenScheme() { + System.out.println("setting blue green palette"); + palette = Palette.BLUE_GREEN; + + // configure the bw series colors + + // these are bluish + bwRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + bwRenderer.setSeriesPaint(0, new Color(0x7C9CDC)); // write + bwRenderer.setSeriesPaint(1, new Color(0x2A5CB0)); // w avg + bwRenderer.setSeriesPaint(2, new Color(0xBCD2EF)); // w max + bwRenderer.setSeriesPaint(3, new Color(0xBFD5EA)); // w min + + // these are green + bwRenderer.setSeriesPaint(4, new Color(0xAACC00)); // read + bwRenderer.setSeriesPaint(5, new Color(0x008080)); // r avg + bwRenderer.setSeriesPaint(6, new Color(0x6B8E23)); // r max + bwRenderer.setSeriesPaint(7, new Color(0x228B22)); // r min + + // configure the access time ms colors + msRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + msRenderer.setSeriesPaint(0, new Color(0x7C9CDC)); // w acc + msRenderer.setSeriesPaint(1, new Color(0xAACC00)); // r acc + } + + /** + * Cool color scheme proposed by Bard + */ + static void setCoolColorScheme() { + System.out.println("setting cool palette"); + palette = Palette.BARD_COOL; + + // configure the bw series colors + bwRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + bwRenderer.setSeriesPaint(0, new Color(0x54a0ff)); // write + bwRenderer.setSeriesPaint(1, new Color(0x808080)); // w avg + bwRenderer.setSeriesPaint(2, new Color(0x4CAF50)); // w max + bwRenderer.setSeriesPaint(3, new Color(0xFF5722)); // w min + bwRenderer.setSeriesPaint(4, new Color(0x00BCD4)); // read + bwRenderer.setSeriesPaint(5, new Color(0x9E9E9E)); // r avg + bwRenderer.setSeriesPaint(6, new Color(0x66BB6A)); // r max + bwRenderer.setSeriesPaint(7, new Color(0xF44336)); // r min + + // configure the access time ms colors + msRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + msRenderer.setSeriesPaint(0, new Color(0x54a0ff)); // w acc + msRenderer.setSeriesPaint(1, new Color(0x00BCD4)); // r acc + } + + + static void setWarmColorScheme() { + System.out.println("setting warm palette"); + palette = Palette.BARD_WARM; + + // configure the bw series colors + bwRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + bwRenderer.setSeriesPaint(0, new Color(0xFFC107)); // write + bwRenderer.setSeriesPaint(1, new Color(0xEBEBEB)); // w avg + bwRenderer.setSeriesPaint(2, new Color(0x4CAF50)); // w max + bwRenderer.setSeriesPaint(3, new Color(0xFF5722)); // w min + bwRenderer.setSeriesPaint(4, new Color(0xE91E63)); // read + bwRenderer.setSeriesPaint(5, new Color(0xD3D3D3)); // r avg + bwRenderer.setSeriesPaint(6, new Color(0x66BB6A)); // r max + bwRenderer.setSeriesPaint(7, new Color(0xF44336)); // r min + + // configure the access time ms colors + msRenderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator()); + msRenderer.setSeriesPaint(0, new Color(0xFFC107)); // w acc + msRenderer.setSeriesPaint(1, new Color(0xE91E63)); // r acc + } + + public static void browseLocation() { + if (App.locationDir != null && App.locationDir.exists()) { + Gui.selFrame.setInitDir(App.locationDir); + } + Gui.selFrame.setLocationRelativeTo(Gui.mainFrame); + Gui.selFrame.setVisible(true); } } diff --git a/src/jdiskmark/LocalDateTimeAttributeConverter.java b/src/jdiskmark/LocalDateTimeAttributeConverter.java new file mode 100644 index 0000000..86d99fc --- /dev/null +++ b/src/jdiskmark/LocalDateTimeAttributeConverter.java @@ -0,0 +1,21 @@ + +package jdiskmark; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import java.sql.Timestamp; +import java.time.LocalDateTime; + +@Converter(autoApply = true) +public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> { + + @Override + public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) { + return localDateTime == null ? null : Timestamp.valueOf(localDateTime); + } + + @Override + public LocalDateTime convertToEntityAttribute(Timestamp timestamp) { + return timestamp == null ? null : timestamp.toLocalDateTime(); + } +} diff --git a/src/jdiskmark/MainFrame.form b/src/jdiskmark/MainFrame.form index 76c006e..25c824b 100644 --- a/src/jdiskmark/MainFrame.form +++ b/src/jdiskmark/MainFrame.form @@ -2,12 +2,9 @@ <Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo"> <NonVisualComponents> - <Component class="javax.swing.JButton" name="jButton1"> - <Properties> - <Property name="text" type="java.lang.String" value="jButton1"/> - </Properties> + <Component class="javax.swing.ButtonGroup" name="palettebuttonGroup"> </Component> - <Menu class="javax.swing.JMenuBar" name="jMenuBar1"> + <Menu class="javax.swing.JMenuBar" name="menuBar"> <SubComponents> <Menu class="javax.swing.JMenu" name="fileMenu"> <Properties> @@ -22,24 +19,16 @@ <Property name="text" type="java.lang.String" value="Exit"/> </Properties> <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jMenuItem1ActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exitMenuItemActionPerformed"/> </Events> </MenuItem> </SubComponents> </Menu> - <Menu class="javax.swing.JMenu" name="optionMenu"> + <Menu class="javax.swing.JMenu" name="actionMenu"> <Properties> - <Property name="text" type="java.lang.String" value="Options"/> + <Property name="text" type="java.lang.String" value="Action"/> </Properties> <SubComponents> - <MenuItem class="javax.swing.JMenuItem" name="clearRunsItem"> - <Properties> - <Property name="text" type="java.lang.String" value="Clear Previous Runs"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clearRunsItemActionPerformed"/> - </Events> - </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="clearLogsItem"> <Properties> <Property name="text" type="java.lang.String" value="Clear Event Logs"/> @@ -50,12 +39,28 @@ </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="deleteDataMenuItem"> <Properties> - <Property name="text" type="java.lang.String" value="Delete Data Dir"/> + <Property name="text" type="java.lang.String" value="Delete Data Directory"/> </Properties> <Events> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteDataMenuItemActionPerformed"/> </Events> </MenuItem> + <MenuItem class="javax.swing.JMenuItem" name="deleteSelBenchmarksItem"> + <Properties> + <Property name="text" type="java.lang.String" value="Delete Selected Benchmark"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteSelBenchmarksItemActionPerformed"/> + </Events> + </MenuItem> + <MenuItem class="javax.swing.JMenuItem" name="deleteAllBenchmarksItem"> + <Properties> + <Property name="text" type="java.lang.String" value="Delete All Benchmarks"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteAllBenchmarksItemActionPerformed"/> + </Events> + </MenuItem> <MenuItem class="javax.swing.JMenuItem" name="resetSequenceMenuItem"> <Properties> <Property name="text" type="java.lang.String" value="Reset Sequence"/> @@ -64,7 +69,29 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetSequenceMenuItemActionPerformed"/> </Events> </MenuItem> - <MenuItem class="javax.swing.JPopupMenu$Separator" name="jSeparator1"> + <MenuItem class="javax.swing.JMenuItem" name="resetBenchmarkItem"> + <Properties> + <Property name="text" type="java.lang.String" value="Reset Benchmark"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetBenchmarkItemActionPerformed"/> + </Events> + </MenuItem> + </SubComponents> + </Menu> + <Menu class="javax.swing.JMenu" name="optionMenu"> + <Properties> + <Property name="text" type="java.lang.String" value="Options"/> + </Properties> + <SubComponents> + <MenuItem class="javax.swing.JCheckBoxMenuItem" name="writeSyncCheckBoxMenuItem"> + <Properties> + <Property name="selected" type="boolean" value="true"/> + <Property name="text" type="java.lang.String" value="Write Sync"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="writeSyncCheckBoxMenuItemActionPerformed"/> + </Events> </MenuItem> <MenuItem class="javax.swing.JCheckBoxMenuItem" name="multiFileCheckBoxMenuItem"> <Properties> @@ -93,6 +120,8 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="autoResetCheckBoxMenuItemActionPerformed"/> </Events> </MenuItem> + <MenuItem class="javax.swing.JPopupMenu$Separator" name="jSeparator2"> + </MenuItem> <MenuItem class="javax.swing.JCheckBoxMenuItem" name="showMaxMinCheckBoxMenuItem"> <Properties> <Property name="selected" type="boolean" value="true"/> @@ -102,13 +131,79 @@ <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showMaxMinCheckBoxMenuItemActionPerformed"/> </Events> </MenuItem> - <MenuItem class="javax.swing.JCheckBoxMenuItem" name="writeSyncCheckBoxMenuItem"> + <MenuItem class="javax.swing.JCheckBoxMenuItem" name="showAccessCheckBoxMenuItem"> <Properties> <Property name="selected" type="boolean" value="true"/> - <Property name="text" type="java.lang.String" value="Write Sync"/> + <Property name="text" type="java.lang.String" value="Show Access Time"/> </Properties> <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="writeSyncCheckBoxMenuItemActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showAccessCheckBoxMenuItemActionPerformed"/> + </Events> + </MenuItem> + <MenuItem class="javax.swing.JPopupMenu$Separator" name="jSeparator1"> + </MenuItem> + <Menu class="javax.swing.JMenu" name="colorPaletteMenu"> + <Properties> + <Property name="text" type="java.lang.String" value="Color Palette"/> + <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> + <ComponentRef name="palettebuttonGroup"/> + </Property> + </Properties> + <SubComponents> + <MenuItem class="javax.swing.JRadioButtonMenuItem" name="classicPaletteMenuItem"> + <Properties> + <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> + <ComponentRef name="palettebuttonGroup"/> + </Property> + <Property name="text" type="java.lang.String" value="Classic"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="classicPaletteMenuItemActionPerformed"/> + </Events> + </MenuItem> + <MenuItem class="javax.swing.JRadioButtonMenuItem" name="blueGreenPaletteMenuItem"> + <Properties> + <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> + <ComponentRef name="palettebuttonGroup"/> + </Property> + <Property name="text" type="java.lang.String" value="Blue Green"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="blueGreenPaletteMenuItemActionPerformed"/> + </Events> + </MenuItem> + <MenuItem class="javax.swing.JRadioButtonMenuItem" name="bardCoolPaletteMenuItem"> + <Properties> + <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> + <ComponentRef name="palettebuttonGroup"/> + </Property> + <Property name="text" type="java.lang.String" value="Bard Cool"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bardCoolPaletteMenuItemActionPerformed"/> + </Events> + </MenuItem> + <MenuItem class="javax.swing.JRadioButtonMenuItem" name="bardWarmPaletteMenuItem"> + <Properties> + <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor"> + <ComponentRef name="palettebuttonGroup"/> + </Property> + <Property name="text" type="java.lang.String" value="Bard Warm"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bardWarmPaletteMenuItemActionPerformed"/> + </Events> + </MenuItem> + </SubComponents> + </Menu> + <MenuItem class="javax.swing.JPopupMenu$Separator" name="jSeparator3"> + </MenuItem> + <MenuItem class="javax.swing.JMenuItem" name="jMenuItem3"> + <Properties> + <Property name="text" type="java.lang.String" value="Advanced Options"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jMenuItem3ActionPerformed"/> </Events> </MenuItem> </SubComponents> @@ -123,7 +218,7 @@ <Property name="text" type="java.lang.String" value="About..."/> </Properties> <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jMenuItem2ActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="aboutMenuItemActionPerformed"/> </Events> </MenuItem> </SubComponents> @@ -133,10 +228,10 @@ </NonVisualComponents> <Properties> <Property name="defaultCloseOperation" type="int" value="3"/> - <Property name="title" type="java.lang.String" value="jDiskMark"/> + <Property name="title" type="java.lang.String" value="JDiskMark"/> </Properties> <SyntheticProperties> - <SyntheticProperty name="menuBar" type="java.lang.String" value="jMenuBar1"/> + <SyntheticProperty name="menuBar" type="java.lang.String" value="menuBar"/> <SyntheticProperty name="formSizePolicy" type="int" value="1"/> <SyntheticProperty name="generateCenter" type="boolean" value="false"/> </SyntheticProperties> @@ -155,17 +250,30 @@ <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jPanel2" max="32767" attributes="0"/> - <Component id="progressPanel" alignment="1" max="32767" attributes="0"/> - <Component id="tabbedPane" alignment="0" pref="1000" max="32767" attributes="0"/> + <Group type="102" attributes="0"> + <Component id="tabbedPane" max="32767" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + </Group> + <Component id="progressPanel" max="32767" attributes="0"/> + <Group type="102" attributes="0"> + <Component id="controlsPanel" min="-2" pref="242" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="mountPanel" max="32767" attributes="0"/> + </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Component id="jPanel2" min="-2" max="-2" attributes="0"/> + <Group type="102" attributes="0"> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Group type="102" attributes="0"> + <EmptySpace max="-2" attributes="0"/> + <Component id="controlsPanel" min="-2" pref="419" max="-2" attributes="0"/> + </Group> + <Component id="mountPanel" max="32767" attributes="0"/> + </Group> <EmptySpace max="-2" attributes="0"/> - <Component id="tabbedPane" max="32767" attributes="0"/> + <Component id="tabbedPane" pref="118" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/> <Component id="progressPanel" min="-2" max="-2" attributes="0"/> </Group> @@ -173,522 +281,649 @@ </DimensionLayout> </Layout> <SubComponents> - <Container class="javax.swing.JPanel" name="jPanel2"> + <Container class="javax.swing.JTabbedPane" name="tabbedPane"> - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="1" attributes="0"> - <Component id="mountPanel" max="32767" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="controlsPanel" min="-2" pref="217" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="1" max="-2" attributes="0"> - <Component id="mountPanel" alignment="0" max="32767" attributes="0"/> - <Component id="controlsPanel" min="-2" pref="381" max="-2" attributes="0"/> - </Group> - <EmptySpace min="0" pref="6" max="32767" attributes="0"/> - </Group> - </Group> - </DimensionLayout> - </Layout> + <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/> <SubComponents> - <Container class="javax.swing.JPanel" name="mountPanel"> - <Properties> - <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor"> - <Color blue="99" green="33" red="0" type="rgb"/> - </Property> - <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[503, 200]"/> - </Property> - </Properties> + <Component class="jdiskmark.BenchmarkPanel" name="runPanel"> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> + <JTabbedPaneConstraints tabName="Benchmark Operations"> + <Property name="tabTitle" type="java.lang.String" value="Benchmark Operations"/> + </JTabbedPaneConstraints> + </Constraint> + </Constraints> + </Component> + <Container class="javax.swing.JScrollPane" name="eventScrollPane"> + <AuxValues> + <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> + </AuxValues> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> + <JTabbedPaneConstraints tabName="Events"> + <Property name="tabTitle" type="java.lang.String" value="Events"/> + </JTabbedPaneConstraints> + </Constraint> + </Constraints> - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <EmptySpace min="0" pref="0" max="32767" attributes="0"/> - </Group> - </DimensionLayout> - </Layout> + <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> + <SubComponents> + <Component class="javax.swing.JTextArea" name="msgTextArea"> + <Properties> + <Property name="editable" type="boolean" value="false"/> + <Property name="columns" type="int" value="20"/> + <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> + <Font name="Monospaced" size="11" style="0"/> + </Property> + <Property name="rows" type="int" value="5"/> + <Property name="tabSize" type="int" value="4"/> + </Properties> + </Component> + </SubComponents> </Container> - <Container class="javax.swing.JPanel" name="controlsPanel"> - <Properties> - <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[250, 420]"/> - </Property> - </Properties> + <Container class="javax.swing.JPanel" name="locationPanel"> + <Constraints> + <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> + <JTabbedPaneConstraints tabName="Drive Location"> + <Property name="tabTitle" type="java.lang.String" value="Drive Location"/> + </JTabbedPaneConstraints> + </Constraint> + </Constraints> <Layout> <DimensionLayout dim="0"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" attributes="0"> + <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="1" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel10" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel11" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel12" alignment="0" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="32767" attributes="0"/> - <Group type="103" groupAlignment="1" max="-2" attributes="0"> - <Component id="rMaxLabel" alignment="0" pref="80" max="32767" attributes="0"/> - <Component id="rMinLabel" alignment="0" max="32767" attributes="0"/> - <Component id="wAvgLabel" alignment="0" max="32767" attributes="0"/> - <Component id="wMaxLabel" alignment="0" max="32767" attributes="0"/> - <Component id="wMinLabel" alignment="0" max="32767" attributes="0"/> - <Component id="rAvgLabel" max="32767" attributes="0"/> - </Group> - <EmptySpace min="-2" pref="20" max="-2" attributes="0"/> + <Group type="102" attributes="0"> + <Component id="jLabel22" min="-2" pref="598" max="-2" attributes="0"/> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> </Group> - <Component id="jLabel13" alignment="0" max="32767" attributes="0"/> - <Group type="102" alignment="1" attributes="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel8" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel6" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel9" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="jLabel14" alignment="0" min="-2" max="-2" attributes="0"/> - <Component id="resetButton" min="-2" pref="99" max="-2" attributes="0"/> - </Group> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Group type="103" groupAlignment="1" attributes="0"> - <Component id="fileSizeLabel" alignment="1" max="32767" attributes="0"/> - <Group type="102" alignment="1" attributes="0"> - <Group type="103" groupAlignment="1" attributes="0"> - <Component id="blockSizeCombo" alignment="0" pref="0" max="32767" attributes="0"/> - <Component id="numBlocksCombo" alignment="0" pref="0" max="32767" attributes="0"/> - <Component id="numFilesCombo" alignment="0" pref="0" max="32767" attributes="0"/> - <Component id="orderComboBox" alignment="0" max="32767" attributes="0"/> - <Component id="modeCombo" alignment="0" pref="94" max="32767" attributes="0"/> - <Component id="startButton" pref="77" max="32767" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - </Group> - </Group> + <Group type="102" attributes="0"> + <Component id="locationText" min="-2" pref="480" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="jLabel15" pref="191" max="32767" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="chooseButton" min="-2" pref="100" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="openLocButton" min="-2" pref="74" max="-2" attributes="0"/> </Group> </Group> + <EmptySpace max="-2" attributes="0"/> </Group> </Group> </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0"> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="startButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="resetButton" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="32767" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="modeCombo" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="orderComboBox" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jLabel14" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel8" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="numFilesCombo" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="numBlocksCombo" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="blockSizeCombo" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel9" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="fileSizeLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace type="unrelated" max="-2" attributes="0"/> - <Component id="jLabel13" min="-2" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="wMinLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="wMaxLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="wAvgLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel10" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="rMinLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> - <EmptySpace max="-2" attributes="0"/> - <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel11" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="rMaxLabel" alignment="3" min="-2" max="-2" attributes="0"/> - </Group> <EmptySpace max="-2" attributes="0"/> <Group type="103" groupAlignment="3" attributes="0"> - <Component id="jLabel12" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="rAvgLabel" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="locationText" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="chooseButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="openLocButton" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel15" alignment="3" min="-2" max="-2" attributes="0"/> </Group> - <EmptySpace min="-2" pref="70" max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="jLabel22" min="-2" max="-2" attributes="0"/> + <EmptySpace pref="20" max="32767" attributes="0"/> </Group> </Group> </DimensionLayout> </Layout> <SubComponents> - <Component class="javax.swing.JLabel" name="jLabel1"> - <Properties> - <Property name="text" type="java.lang.String" value="Write Min"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="jLabel2"> - <Properties> - <Property name="text" type="java.lang.String" value="Write Max"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="jLabel3"> - <Properties> - <Property name="text" type="java.lang.String" value="Write Avg"/> - </Properties> - </Component> - <Component class="javax.swing.JComboBox" name="numBlocksCombo"> - <Properties> - <Property name="editable" type="boolean" value="true"/> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="15"> - <StringItem index="0" value="1"/> - <StringItem index="1" value="2"/> - <StringItem index="2" value="3"/> - <StringItem index="3" value="4"/> - <StringItem index="4" value="8"/> - <StringItem index="5" value="16"/> - <StringItem index="6" value="32"/> - <StringItem index="7" value="64"/> - <StringItem index="8" value="128"/> - <StringItem index="9" value="256"/> - <StringItem index="10" value="512"/> - <StringItem index="11" value="1024"/> - <StringItem index="12" value="2048"/> - <StringItem index="13" value="4096"/> - <StringItem index="14" value="8192"/> - </StringArray> - </Property> - <Property name="selectedIndex" type="int" value="6"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numBlocksComboActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="jLabel5"> - <Properties> - <Property name="text" type="java.lang.String" value="No. Blocks"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="jLabel6"> - <Properties> - <Property name="text" type="java.lang.String" value="Block (KB)"/> - </Properties> - </Component> - <Component class="javax.swing.JComboBox" name="blockSizeCombo"> - <Properties> - <Property name="editable" type="boolean" value="true"/> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="11"> - <StringItem index="0" value="2"/> - <StringItem index="1" value="4"/> - <StringItem index="2" value="8"/> - <StringItem index="3" value="16"/> - <StringItem index="4" value="32"/> - <StringItem index="5" value="64"/> - <StringItem index="6" value="128"/> - <StringItem index="7" value="256"/> - <StringItem index="8" value="512"/> - <StringItem index="9" value="1024"/> - <StringItem index="10" value="2048"/> - </StringArray> - </Property> - <Property name="selectedIndex" type="int" value="8"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="blockSizeComboActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JButton" name="startButton"> - <Properties> - <Property name="text" type="java.lang.String" value="Start"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="startButtonActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="jLabel8"> - <Properties> - <Property name="text" type="java.lang.String" value="No. Marks"/> - </Properties> - </Component> - <Component class="javax.swing.JComboBox" name="numFilesCombo"> + <Component class="javax.swing.JButton" name="chooseButton"> <Properties> - <Property name="editable" type="boolean" value="true"/> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="7"> - <StringItem index="0" value="25"/> - <StringItem index="1" value="50"/> - <StringItem index="2" value="75"/> - <StringItem index="3" value="100"/> - <StringItem index="4" value="150"/> - <StringItem index="5" value="200"/> - <StringItem index="6" value="250"/> - </StringArray> - </Property> + <Property name="text" type="java.lang.String" value="Browse"/> </Properties> <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numFilesComboActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chooseButtonActionPerformed"/> </Events> </Component> - <Component class="javax.swing.JLabel" name="jLabel4"> + <Component class="javax.swing.JTextField" name="locationText"> <Properties> - <Property name="text" type="java.lang.String" value="IO Mode"/> + <Property name="editable" type="boolean" value="false"/> </Properties> </Component> - <Component class="javax.swing.JComboBox" name="modeCombo"> + <Component class="javax.swing.JButton" name="openLocButton"> <Properties> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="3"> - <StringItem index="0" value="write"/> - <StringItem index="1" value="read"/> - <StringItem index="2" value="write&read"/> - </StringArray> - </Property> + <Property name="text" type="java.lang.String" value="Open"/> </Properties> <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="modeComboActionPerformed"/> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openLocButtonActionPerformed"/> </Events> </Component> - <Component class="javax.swing.JLabel" name="jLabel9"> - <Properties> - <Property name="text" type="java.lang.String" value="Mark Size (KB)"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="fileSizeLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="jLabel10"> - <Properties> - <Property name="text" type="java.lang.String" value="Read Min"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="jLabel11"> + <Component class="javax.swing.JLabel" name="jLabel15"> <Properties> - <Property name="text" type="java.lang.String" value="Read Max"/> + <Property name="text" type="java.lang.String" value="/JDiskMarkData"/> </Properties> </Component> - <Component class="javax.swing.JLabel" name="jLabel12"> + <Component class="javax.swing.JLabel" name="jLabel22"> <Properties> - <Property name="text" type="java.lang.String" value="Read Avg"/> - </Properties> - </Component> - <Component class="javax.swing.JButton" name="resetButton"> - <Properties> - <Property name="text" type="java.lang.String" value="Reset"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetButtonActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="jLabel13"> - <Properties> - <Property name="horizontalAlignment" type="int" value="0"/> - <Property name="text" type="java.lang.String" value="Tx Rates (MB/s)"/> - </Properties> - </Component> - <Component class="javax.swing.JComboBox" name="orderComboBox"> - <Properties> - <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> - <StringArray count="0"/> - </Property> - <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> - <Dimension value="[106, 32767]"/> - </Property> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="orderComboBoxActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="jLabel14"> - <Properties> - <Property name="text" type="java.lang.String" value="Block Order"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="wMinLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="wMaxLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="wAvgLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="rMinLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="rMaxLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> - </Properties> - </Component> - <Component class="javax.swing.JLabel" name="rAvgLabel"> - <Properties> - <Property name="text" type="java.lang.String" value="- -"/> + <Property name="text" type="java.lang.String" value="Specify the location where the data files will be generated and read from to assess each sample's bandwidth."/> </Properties> </Component> </SubComponents> </Container> </SubComponents> </Container> - <Container class="javax.swing.JTabbedPane" name="tabbedPane"> - - <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/> - <SubComponents> - <Component class="jdiskmark.RunPanel" name="runPanel"> - <Constraints> - <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> - <JTabbedPaneConstraints tabName="Previous Runs"> - <Property name="tabTitle" type="java.lang.String" value="Previous Runs"/> - </JTabbedPaneConstraints> - </Constraint> - </Constraints> - </Component> - <Container class="javax.swing.JScrollPane" name="eventScrollPane"> - <AuxValues> - <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/> - </AuxValues> - <Constraints> - <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> - <JTabbedPaneConstraints tabName="Event Logs"> - <Property name="tabTitle" type="java.lang.String" value="Event Logs"/> - </JTabbedPaneConstraints> - </Constraint> - </Constraints> + <Container class="javax.swing.JPanel" name="mountPanel"> + <Properties> + <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor"> + <Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo"> + <EtchetBorder/> + </Border> + </Property> + <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[503, 200]"/> + </Property> + <Property name="name" type="java.lang.String" value="renderCombo" noResource="true"/> + </Properties> - <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/> - <SubComponents> - <Component class="javax.swing.JTextArea" name="msgTextArea"> - <Properties> - <Property name="editable" type="boolean" value="false"/> - <Property name="columns" type="int" value="20"/> - <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor"> - <Font name="Monospaced" size="11" style="0"/> - </Property> - <Property name="rows" type="int" value="5"/> - <Property name="tabSize" type="int" value="4"/> - </Properties> - </Component> - </SubComponents> - </Container> - <Container class="javax.swing.JPanel" name="locationPanel"> - <Constraints> - <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription"> - <JTabbedPaneConstraints tabName="Data Location"> - <Property name="tabTitle" type="java.lang.String" value="Data Location"/> - </JTabbedPaneConstraints> - </Constraint> - </Constraints> + <Layout> + <DimensionLayout dim="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + </DimensionLayout> + <DimensionLayout dim="1"> + <Group type="103" groupAlignment="0" attributes="0"> + <EmptySpace min="0" pref="0" max="32767" attributes="0"/> + </Group> + </DimensionLayout> + </Layout> + </Container> + <Container class="javax.swing.JPanel" name="controlsPanel"> + <Properties> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[250, 420]"/> + </Property> + </Properties> - <Layout> - <DimensionLayout dim="0"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> - <Component id="locationText" min="-2" pref="480" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="jLabel15" pref="278" max="32767" attributes="0"/> - <EmptySpace type="separate" max="-2" attributes="0"/> - <Component id="chooseButton" min="-2" pref="100" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> - <Component id="openLocButton" min="-2" pref="74" max="-2" attributes="0"/> - <EmptySpace max="-2" attributes="0"/> + <Layout> + <DimensionLayout dim="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" attributes="0"> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Group type="102" alignment="0" attributes="0"> + <Component id="jLabel9" min="-2" max="-2" attributes="0"/> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Component id="sampleSizeLabel" min="-2" pref="104" max="-2" attributes="0"/> + </Group> + <Component id="startButton" alignment="0" max="32767" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel13" alignment="0" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> + <Component id="jLabel18" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="wIopsLabel" min="-2" pref="60" max="-2" attributes="0"/> + </Group> + <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0"> + <Group type="102" alignment="1" attributes="0"> + <Component id="jLabel16" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="wAccessLabel" max="32767" attributes="0"/> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel3" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="1" max="-2" attributes="0"> + <Component id="wAvgLabel" max="32767" attributes="0"/> + <Component id="wMaxLabel" alignment="0" max="32767" attributes="0"/> + <Component id="wMinLabel" alignment="0" min="-2" pref="61" max="-2" attributes="0"/> + </Group> + </Group> + </Group> + </Group> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel20" alignment="0" min="-2" max="-2" attributes="0"/> + <Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0"> + <Group type="102" alignment="1" attributes="0"> + <Component id="jLabel19" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="rIopsLabel" max="32767" attributes="0"/> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="1" attributes="0"> + <Component id="jLabel11" min="-2" max="-2" attributes="0"/> + <Component id="jLabel10" min="-2" max="-2" attributes="0"/> + <Component id="jLabel12" alignment="1" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Component id="rMinLabel" max="32767" attributes="0"/> + <Component id="rMaxLabel" max="32767" attributes="0"/> + <Component id="rAvgLabel" min="-2" pref="65" max="-2" attributes="0"/> + </Group> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Component id="jLabel17" min="-2" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> + <Component id="rAccessLabel" min="-2" pref="40" max="-2" attributes="0"/> + </Group> + </Group> + </Group> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel5" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel4" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel6" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel8" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace min="18" pref="18" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="103" groupAlignment="0" max="-2" attributes="0"> + <Component id="typeCombo" alignment="0" max="32767" attributes="0"/> + <Component id="blockSizeCombo" alignment="0" pref="100" max="32767" attributes="0"/> + <Component id="numSamplesCombo" alignment="0" max="32767" attributes="0"/> + </Group> + <Component id="numBlocksCombo" alignment="0" min="-2" pref="100" max="-2" attributes="0"/> + </Group> + </Group> + </Group> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel21" alignment="0" min="-2" max="-2" attributes="0"/> + <Component id="jLabel14" alignment="0" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="orderComboBox" min="-2" pref="100" max="-2" attributes="0"/> + <Component id="numThreadsCombo" min="-2" pref="100" max="-2" attributes="0"/> + </Group> + </Group> </Group> + <EmptySpace pref="27" max="32767" attributes="0"/> </Group> - </DimensionLayout> - <DimensionLayout dim="1"> - <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="0" attributes="0"> - <EmptySpace max="-2" attributes="0"/> + </Group> + </DimensionLayout> + <DimensionLayout dim="1"> + <Group type="103" groupAlignment="0" attributes="0"> + <Group type="102" alignment="0" attributes="0"> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel4" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="typeCombo" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel21" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="numThreadsCombo" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Group type="103" groupAlignment="1" attributes="0"> + <Component id="orderComboBox" min="-2" max="-2" attributes="0"/> + <Component id="jLabel14" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="numBlocksCombo" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel5" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="blockSizeCombo" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel8" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="numSamplesCombo" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace type="separate" max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="sampleSizeLabel" min="-2" max="-2" attributes="0"/> + <Component id="jLabel9" alignment="1" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Component id="startButton" max="32767" attributes="0"/> + <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel13" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel20" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="wMinLabel" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel10" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="rMinLabel" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="3" attributes="0"> - <Component id="locationText" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="chooseButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="openLocButton" alignment="3" min="-2" max="-2" attributes="0"/> - <Component id="jLabel15" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel11" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="rMaxLabel" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="wMaxLabel" alignment="3" min="-2" pref="16" max="-2" attributes="0"/> </Group> - <EmptySpace pref="140" max="32767" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel3" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="wAvgLabel" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel12" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="rAvgLabel" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel16" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="wAccessLabel" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel17" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="rAccessLabel" alignment="3" min="-2" max="-2" attributes="0"/> + </Group> + <EmptySpace min="-2" pref="8" max="-2" attributes="0"/> + <Group type="103" groupAlignment="3" attributes="0"> + <Component id="jLabel18" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="wIopsLabel" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="jLabel19" alignment="3" min="-2" max="-2" attributes="0"/> + <Component id="rIopsLabel" alignment="3" min="-2" max="-2" attributes="0"/> </Group> </Group> - </DimensionLayout> - </Layout> - <SubComponents> - <Component class="javax.swing.JButton" name="chooseButton"> - <Properties> - <Property name="text" type="java.lang.String" value="Browse"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chooseButtonActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JTextField" name="locationText"> - <Properties> - <Property name="editable" type="boolean" value="false"/> - </Properties> - </Component> - <Component class="javax.swing.JButton" name="openLocButton"> - <Properties> - <Property name="text" type="java.lang.String" value="Open"/> - </Properties> - <Events> - <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openLocButtonActionPerformed"/> - </Events> - </Component> - <Component class="javax.swing.JLabel" name="jLabel15"> - <Properties> - <Property name="text" type="java.lang.String" value="/jDiskMarkData"/> - </Properties> - </Component> - </SubComponents> - </Container> + </Group> + </DimensionLayout> + </Layout> + <SubComponents> + <Component class="javax.swing.JComboBox" name="numBlocksCombo"> + <Properties> + <Property name="editable" type="boolean" value="true"/> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="15"> + <StringItem index="0" value="1"/> + <StringItem index="1" value="2"/> + <StringItem index="2" value="3"/> + <StringItem index="3" value="4"/> + <StringItem index="4" value="8"/> + <StringItem index="5" value="16"/> + <StringItem index="6" value="32"/> + <StringItem index="7" value="64"/> + <StringItem index="8" value="128"/> + <StringItem index="9" value="256"/> + <StringItem index="10" value="512"/> + <StringItem index="11" value="1024"/> + <StringItem index="12" value="2048"/> + <StringItem index="13" value="4096"/> + <StringItem index="14" value="8192"/> + </StringArray> + </Property> + <Property name="selectedIndex" type="int" value="6"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numBlocksComboActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JLabel" name="jLabel5"> + <Properties> + <Property name="text" type="java.lang.String" value="Blocks / Sample"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel6"> + <Properties> + <Property name="text" type="java.lang.String" value="Block Size (KB)"/> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="blockSizeCombo"> + <Properties> + <Property name="editable" type="boolean" value="true"/> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="11"> + <StringItem index="0" value="2"/> + <StringItem index="1" value="4"/> + <StringItem index="2" value="8"/> + <StringItem index="3" value="16"/> + <StringItem index="4" value="32"/> + <StringItem index="5" value="64"/> + <StringItem index="6" value="128"/> + <StringItem index="7" value="256"/> + <StringItem index="8" value="512"/> + <StringItem index="9" value="1024"/> + <StringItem index="10" value="2048"/> + </StringArray> + </Property> + <Property name="selectedIndex" type="int" value="8"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="blockSizeComboActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JLabel" name="jLabel8"> + <Properties> + <Property name="text" type="java.lang.String" value="No. Samples"/> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="numSamplesCombo"> + <Properties> + <Property name="editable" type="boolean" value="true"/> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="10"> + <StringItem index="0" value="50"/> + <StringItem index="1" value="100"/> + <StringItem index="2" value="200"/> + <StringItem index="3" value="300"/> + <StringItem index="4" value="500"/> + <StringItem index="5" value="1000"/> + <StringItem index="6" value="2000"/> + <StringItem index="7" value="3000"/> + <StringItem index="8" value="5000"/> + <StringItem index="9" value="10000"/> + </StringArray> + </Property> + <Property name="selectedIndex" type="int" value="2"/> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[72, 24]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numSamplesComboActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JLabel" name="jLabel4"> + <Properties> + <Property name="text" type="java.lang.String" value="Benchmark Type"/> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="typeCombo"> + <Properties> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="1"> + <StringItem index="0" value=""/> + </StringArray> + </Property> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[60, 24]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="typeComboActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JLabel" name="jLabel9"> + <Properties> + <Property name="text" type="java.lang.String" value="Sample Size (KB)"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="sampleSizeLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[70, 18]"/> + </Property> + </Properties> + </Component> + <Component class="javax.swing.JComboBox" name="orderComboBox"> + <Properties> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="0"/> + </Property> + <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> + <Dimension value="[106, 32767]"/> + </Property> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="orderComboBoxActionPerformed"/> + </Events> + <AuxValues> + <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<BenchmarkOperation.BlockSequence>"/> + </AuxValues> + </Component> + <Component class="javax.swing.JLabel" name="jLabel14"> + <Properties> + <Property name="text" type="java.lang.String" value="Block Order"/> + </Properties> + </Component> + <Component class="javax.swing.JButton" name="startButton"> + <Properties> + <Property name="text" type="java.lang.String" value="Start"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="startButtonActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JComboBox" name="numThreadsCombo"> + <Properties> + <Property name="editable" type="boolean" value="true"/> + <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor"> + <StringArray count="5"> + <StringItem index="0" value="1"/> + <StringItem index="1" value="2"/> + <StringItem index="2" value="4"/> + <StringItem index="3" value="8"/> + <StringItem index="4" value="16"/> + </StringArray> + </Property> + <Property name="selectedIndex" type="int" value="2"/> + </Properties> + <Events> + <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numThreadsComboActionPerformed"/> + </Events> + </Component> + <Component class="javax.swing.JLabel" name="jLabel21"> + <Properties> + <Property name="text" type="java.lang.String" value="Number Threads"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="wAvgLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel19"> + <Properties> + <Property name="text" type="java.lang.String" value="IOPS"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="rMinLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="wIopsLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="rMaxLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="rIopsLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="rAvgLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel20"> + <Properties> + <Property name="horizontalAlignment" type="int" value="0"/> + <Property name="text" type="java.lang.String" value="Read IO (MB/s)"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel10"> + <Properties> + <Property name="text" type="java.lang.String" value="Min"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel17"> + <Properties> + <Property name="text" type="java.lang.String" value="Acc (ms)"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel1"> + <Properties> + <Property name="text" type="java.lang.String" value="Min"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="rAccessLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel2"> + <Properties> + <Property name="text" type="java.lang.String" value="Max"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel16"> + <Properties> + <Property name="text" type="java.lang.String" value="Acc (ms)"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel3"> + <Properties> + <Property name="text" type="java.lang.String" value="Avg"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel13"> + <Properties> + <Property name="horizontalAlignment" type="int" value="0"/> + <Property name="text" type="java.lang.String" value="Write IO (MB/s)"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="wAccessLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel11"> + <Properties> + <Property name="text" type="java.lang.String" value="Max"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="wMinLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel12"> + <Properties> + <Property name="text" type="java.lang.String" value="Avg"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="wMaxLabel"> + <Properties> + <Property name="text" type="java.lang.String" value="- -"/> + </Properties> + </Component> + <Component class="javax.swing.JLabel" name="jLabel18"> + <Properties> + <Property name="text" type="java.lang.String" value="IOPS"/> + </Properties> + </Component> </SubComponents> </Container> <Container class="javax.swing.JPanel" name="progressPanel"> @@ -698,8 +933,8 @@ <Group type="103" groupAlignment="0" attributes="0"> <Group type="102" alignment="1" attributes="0"> <EmptySpace max="-2" attributes="0"/> - <Component id="jLabel7" min="-2" pref="69" max="-2" attributes="0"/> - <EmptySpace type="unrelated" max="-2" attributes="0"/> + <Component id="jLabel7" min="-2" pref="83" max="-2" attributes="0"/> + <EmptySpace max="-2" attributes="0"/> <Component id="totalTxProgBar" max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/> </Group> @@ -707,24 +942,26 @@ </DimensionLayout> <DimensionLayout dim="1"> <Group type="103" groupAlignment="0" attributes="0"> - <Group type="102" alignment="1" attributes="0"> - <EmptySpace max="32767" attributes="0"/> - <Component id="totalTxProgBar" min="-2" max="-2" attributes="0"/> + <Group type="102" alignment="0" attributes="0"> <EmptySpace max="-2" attributes="0"/> + <Group type="103" groupAlignment="0" attributes="0"> + <Component id="jLabel7" max="32767" attributes="0"/> + <Component id="totalTxProgBar" max="32767" attributes="0"/> + </Group> + <EmptySpace min="-2" max="-2" attributes="0"/> </Group> - <Component id="jLabel7" alignment="1" max="32767" attributes="0"/> </Group> </DimensionLayout> </Layout> <SubComponents> + <Component class="javax.swing.JProgressBar" name="totalTxProgBar"> + </Component> <Component class="javax.swing.JLabel" name="jLabel7"> <Properties> <Property name="horizontalAlignment" type="int" value="2"/> <Property name="text" type="java.lang.String" value="Total Tx (KB)"/> </Properties> </Component> - <Component class="javax.swing.JProgressBar" name="totalTxProgBar"> - </Component> </SubComponents> </Container> </SubComponents> diff --git a/src/jdiskmark/MainFrame.java b/src/jdiskmark/MainFrame.java index 8a60b05..3a8d7e4 100644 --- a/src/jdiskmark/MainFrame.java +++ b/src/jdiskmark/MainFrame.java @@ -3,46 +3,165 @@ import static jdiskmark.App.dataDir; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Desktop; import java.io.IOException; import java.text.DecimalFormat; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.DefaultComboBoxModel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.text.DefaultCaret; +import javax.swing.JComboBox; +import javax.swing.JPopupMenu; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.ButtonGroup; + + /** - * + * The parent frame of the app */ public final class MainFrame extends javax.swing.JFrame { - DecimalFormat df = new DecimalFormat("###.###"); + public static final DecimalFormat DF = new DecimalFormat("###.##"); /** * Creates new form MainFrame */ - public MainFrame() { - initComponents(); - Gui.createChartPanel(); - mountPanel.setLayout(new BorderLayout()); - Gui.chartPanel.setSize(mountPanel.getSize()); - Gui.chartPanel.setSize(mountPanel.getWidth(), 200); - mountPanel.add(Gui.chartPanel); - totalTxProgBar.setStringPainted(true); - totalTxProgBar.setValue(0); - totalTxProgBar.setString(""); - setTitle(getTitle()+" "+App.getVersion()); - - // auto scroll the text area. - DefaultCaret caret = (DefaultCaret) msgTextArea.getCaret(); - caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); - - // init order combo box - orderComboBox.addItem(DiskRun.BlockSequence.SEQUENTIAL); - orderComboBox.addItem(DiskRun.BlockSequence.RANDOM); + + private javax.swing.JLabel renderModeLabel; + private javax.swing.JComboBox<RenderFrequencyMode> renderModeCombo; + + private RenderFrequencyMode renderMode = RenderFrequencyMode.PER_SAMPLE; + public RenderFrequencyMode getRenderMode() { + return Gui.getRenderFrequencyMode(); } + @SuppressWarnings("unchecked") + public MainFrame() { + Gui.mainFrame = this; // safe enough here, only reference used after initComponents() + + initComponents(); + + // Submenu for Render Interval + // Render Mode submenu with radio buttons + JMenu renderModeMenu = new JMenu("Render Mode"); + + JRadioButtonMenuItem perSampleItem = new JRadioButtonMenuItem("Per Sample"); + JRadioButtonMenuItem perOperationItem = new JRadioButtonMenuItem("Per Operation"); + JRadioButtonMenuItem per1000msItem = new JRadioButtonMenuItem("Per 1000 ms"); + JRadioButtonMenuItem per500msItem = new JRadioButtonMenuItem("Per 500 ms"); + + ButtonGroup group = new ButtonGroup(); + group.add(perSampleItem); + group.add(perOperationItem); + group.add(per1000msItem); + group.add(per500msItem); + + renderModeMenu.add(perSampleItem); + renderModeMenu.add(perOperationItem); + renderModeMenu.addSeparator(); + renderModeMenu.add(per1000msItem); + renderModeMenu.add(per500msItem); + + // initialize selected item from current app state + switch (Gui.getRenderFrequencyMode()) { + case PER_SAMPLE -> perSampleItem.setSelected(true); + case PER_OPERATION -> perOperationItem.setSelected(true); + case PER_1000MS -> per1000msItem.setSelected(true); + case PER_500MS -> per500msItem.setSelected(true); + } + + perSampleItem.addActionListener(e -> { + Gui.setRenderFrequencyMode(RenderFrequencyMode.PER_SAMPLE); + Gui.renderModeLabel.setText("Render Mode: Per Sample"); + Gui.updateRenderView(); + }); + + perOperationItem.addActionListener(e -> { + Gui.setRenderFrequencyMode(RenderFrequencyMode.PER_OPERATION); + Gui.renderModeLabel.setText("Render Mode: Per Operation"); + Gui.updateRenderView(); + }); + + per1000msItem.addActionListener(e -> { + Gui.setRenderFrequencyMode(RenderFrequencyMode.PER_1000MS); + Gui.renderModeLabel.setText("Render Mode: Per 1000 ms"); + Gui.updateRenderView(); + }); + + per500msItem.addActionListener(e -> { + Gui.setRenderFrequencyMode(RenderFrequencyMode.PER_500MS); + Gui.renderModeLabel.setText("Render Mode: Per 500 ms"); + Gui.updateRenderView(); + }); + + + // Add to Options menu + optionMenu.addSeparator(); + optionMenu.add(renderModeMenu); + + + // --------- EXISTING UI SETUP BELOW --------- + + // for diagnostics + // controlsPanel.setBackground(Color.blue); + + DefaultComboBoxModel<Benchmark.BenchmarkType> ioModel = + new DefaultComboBoxModel<>(Benchmark.BenchmarkType.values()); + typeCombo.setModel(ioModel); + + startButton.requestFocus(); + mountPanel.setLayout(new BorderLayout()); + + JPanel wrapperPanel = Gui.createChartPanel(); + wrapperPanel.setPreferredSize(new java.awt.Dimension(mountPanel.getWidth(), 200)); + mountPanel.add(wrapperPanel, BorderLayout.CENTER); + mountPanel.revalidate(); + mountPanel.repaint(); + + totalTxProgBar.setStringPainted(true); + totalTxProgBar.setValue(0); + totalTxProgBar.setString(""); + + StringBuilder titleSb = new StringBuilder(); + titleSb.append(getTitle()).append(" ").append(App.VERSION); + + initializeComboSettings(); + + // architecture + if (App.arch != null && !App.arch.isEmpty()) { + titleSb.append(" - ").append(App.arch); + } + + // processor name + if (App.processorName != null && !App.processorName.isEmpty()) { + titleSb.append(" - ").append(App.processorName); + } + + // permission indicator + if (App.isAdmin) titleSb.append(" [Admin]"); + if (App.isRoot) titleSb.append(" [root]"); + + setTitle(titleSb.toString()); + + // auto scroll the text area + DefaultCaret caret = (DefaultCaret) msgTextArea.getCaret(); + caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); + + // init order combo box + orderComboBox.addItem(BenchmarkOperation.BlockSequence.SEQUENTIAL); + orderComboBox.addItem(BenchmarkOperation.BlockSequence.RANDOM); + } + + + + public JPanel getMountPanel() { return mountPanel; } @@ -51,7 +170,7 @@ public JPanel getMountPanel() { * This method is called when the gui needs to be updated after a new config * has been loaded. */ - public void refreshConfig() { + public void loadConfig() { if (App.locationDir != null) { // set the location dir if not null setLocation(App.locationDir.getAbsolutePath()); } @@ -59,24 +178,43 @@ public void refreshConfig() { autoRemoveCheckBoxMenuItem.setSelected(App.autoRemoveData); autoResetCheckBoxMenuItem.setSelected(App.autoReset); showMaxMinCheckBoxMenuItem.setSelected(App.showMaxMin); + showAccessCheckBoxMenuItem.setSelected(App.showDriveAccess); writeSyncCheckBoxMenuItem.setSelected(App.writeSyncEnable); - - String modeStr = "unset"; - if (!App.readTest && App.writeTest) { modeStr = "write"; } - else if (App.readTest && !App.writeTest) { modeStr = "read"; } - else if (App.readTest && App.writeTest) { modeStr = "write&read"; } - else { msg("WARNING: invalid mode detected"); } - modeCombo.setSelectedItem(modeStr); - - //String blockOrderStr = App.randomEnable ? "random":"sequential"; + + switch (Gui.palette) { + case Gui.Palette.CLASSIC -> { + classicPaletteMenuItem.setSelected(true); + Gui.setClassicColorScheme(); + } + case Gui.Palette.BLUE_GREEN -> { + blueGreenPaletteMenuItem.setSelected(true); + Gui.setBlueGreenScheme(); + } + case Gui.Palette.BARD_COOL -> { + bardCoolPaletteMenuItem.setSelected(true); + Gui.setCoolColorScheme(); + } + case Gui.Palette.BARD_WARM -> { + bardWarmPaletteMenuItem.setSelected(true); + Gui.setWarmColorScheme(); + } + } + } + + public void initializeComboSettings() { + typeCombo.setSelectedItem(App.benchmarkType); + loadSettings(); + } + + public void loadSettings() { + typeCombo.setSelectedItem(App.benchmarkType); + numThreadsCombo.setSelectedItem(String.valueOf(App.numOfThreads)); orderComboBox.setSelectedItem(App.blockSequence); - - numFilesCombo.setSelectedItem(String.valueOf(App.numOfMarks)); numBlocksCombo.setSelectedItem(String.valueOf(App.numOfBlocks)); - blockSizeCombo.setSelectedItem(String.valueOf(App.blockSizeKb)); + blockSizeCombo.setSelectedItem(String.valueOf(App.blockSizeKb)); + numSamplesCombo.setSelectedItem(String.valueOf(App.numOfSamples)); } - /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -86,73 +224,161 @@ public void refreshConfig() { // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { - jButton1 = new javax.swing.JButton(); - jPanel2 = new javax.swing.JPanel(); + palettebuttonGroup = new javax.swing.ButtonGroup(); + tabbedPane = new javax.swing.JTabbedPane(); + runPanel = new jdiskmark.BenchmarkPanel(); + eventScrollPane = new javax.swing.JScrollPane(); + msgTextArea = new javax.swing.JTextArea(); + locationPanel = new javax.swing.JPanel(); + chooseButton = new javax.swing.JButton(); + locationText = new javax.swing.JTextField(); + openLocButton = new javax.swing.JButton(); + jLabel15 = new javax.swing.JLabel(); + jLabel22 = new javax.swing.JLabel(); mountPanel = new javax.swing.JPanel(); controlsPanel = new javax.swing.JPanel(); - jLabel1 = new javax.swing.JLabel(); - jLabel2 = new javax.swing.JLabel(); - jLabel3 = new javax.swing.JLabel(); numBlocksCombo = new javax.swing.JComboBox(); jLabel5 = new javax.swing.JLabel(); jLabel6 = new javax.swing.JLabel(); blockSizeCombo = new javax.swing.JComboBox(); - startButton = new javax.swing.JButton(); jLabel8 = new javax.swing.JLabel(); - numFilesCombo = new javax.swing.JComboBox(); + numSamplesCombo = new javax.swing.JComboBox(); jLabel4 = new javax.swing.JLabel(); - modeCombo = new javax.swing.JComboBox(); + typeCombo = new javax.swing.JComboBox(); jLabel9 = new javax.swing.JLabel(); - fileSizeLabel = new javax.swing.JLabel(); - jLabel10 = new javax.swing.JLabel(); - jLabel11 = new javax.swing.JLabel(); - jLabel12 = new javax.swing.JLabel(); - resetButton = new javax.swing.JButton(); - jLabel13 = new javax.swing.JLabel(); - orderComboBox = new javax.swing.JComboBox(); + sampleSizeLabel = new javax.swing.JLabel(); + orderComboBox = new javax.swing.JComboBox<>(); jLabel14 = new javax.swing.JLabel(); - wMinLabel = new javax.swing.JLabel(); - wMaxLabel = new javax.swing.JLabel(); + startButton = new javax.swing.JButton(); + numThreadsCombo = new javax.swing.JComboBox(); + jLabel21 = new javax.swing.JLabel(); wAvgLabel = new javax.swing.JLabel(); + jLabel19 = new javax.swing.JLabel(); rMinLabel = new javax.swing.JLabel(); + wIopsLabel = new javax.swing.JLabel(); rMaxLabel = new javax.swing.JLabel(); + rIopsLabel = new javax.swing.JLabel(); rAvgLabel = new javax.swing.JLabel(); - tabbedPane = new javax.swing.JTabbedPane(); - runPanel = new jdiskmark.RunPanel(); - eventScrollPane = new javax.swing.JScrollPane(); - msgTextArea = new javax.swing.JTextArea(); - locationPanel = new javax.swing.JPanel(); - chooseButton = new javax.swing.JButton(); - locationText = new javax.swing.JTextField(); - openLocButton = new javax.swing.JButton(); - jLabel15 = new javax.swing.JLabel(); + jLabel20 = new javax.swing.JLabel(); + jLabel10 = new javax.swing.JLabel(); + jLabel17 = new javax.swing.JLabel(); + jLabel1 = new javax.swing.JLabel(); + rAccessLabel = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel16 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + jLabel13 = new javax.swing.JLabel(); + wAccessLabel = new javax.swing.JLabel(); + jLabel11 = new javax.swing.JLabel(); + wMinLabel = new javax.swing.JLabel(); + jLabel12 = new javax.swing.JLabel(); + wMaxLabel = new javax.swing.JLabel(); + jLabel18 = new javax.swing.JLabel(); progressPanel = new javax.swing.JPanel(); - jLabel7 = new javax.swing.JLabel(); totalTxProgBar = new javax.swing.JProgressBar(); - jMenuBar1 = new javax.swing.JMenuBar(); + jLabel7 = new javax.swing.JLabel(); + menuBar = new javax.swing.JMenuBar(); fileMenu = new javax.swing.JMenu(); jMenuItem1 = new javax.swing.JMenuItem(); - optionMenu = new javax.swing.JMenu(); - clearRunsItem = new javax.swing.JMenuItem(); + actionMenu = new javax.swing.JMenu(); clearLogsItem = new javax.swing.JMenuItem(); deleteDataMenuItem = new javax.swing.JMenuItem(); + deleteSelBenchmarksItem = new javax.swing.JMenuItem(); + deleteAllBenchmarksItem = new javax.swing.JMenuItem(); resetSequenceMenuItem = new javax.swing.JMenuItem(); - jSeparator1 = new javax.swing.JPopupMenu.Separator(); + resetBenchmarkItem = new javax.swing.JMenuItem(); + optionMenu = new javax.swing.JMenu(); + writeSyncCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); multiFileCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); autoRemoveCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); autoResetCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); + jSeparator2 = new javax.swing.JPopupMenu.Separator(); showMaxMinCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); - writeSyncCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); + showAccessCheckBoxMenuItem = new javax.swing.JCheckBoxMenuItem(); + jSeparator1 = new javax.swing.JPopupMenu.Separator(); + colorPaletteMenu = new javax.swing.JMenu(); + classicPaletteMenuItem = new javax.swing.JRadioButtonMenuItem(); + blueGreenPaletteMenuItem = new javax.swing.JRadioButtonMenuItem(); + bardCoolPaletteMenuItem = new javax.swing.JRadioButtonMenuItem(); + bardWarmPaletteMenuItem = new javax.swing.JRadioButtonMenuItem(); + jSeparator3 = new javax.swing.JPopupMenu.Separator(); + jMenuItem3 = new javax.swing.JMenuItem(); helpMenu = new javax.swing.JMenu(); jMenuItem2 = new javax.swing.JMenuItem(); - jButton1.setText("jButton1"); - setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - setTitle("jDiskMark"); + setTitle("JDiskMark"); + + tabbedPane.addTab("Benchmark Operations", runPanel); - mountPanel.setBackground(new java.awt.Color(0, 51, 153)); + msgTextArea.setEditable(false); + msgTextArea.setColumns(20); + msgTextArea.setFont(new java.awt.Font("Monospaced", 0, 11)); // NOI18N + msgTextArea.setRows(5); + msgTextArea.setTabSize(4); + eventScrollPane.setViewportView(msgTextArea); + + tabbedPane.addTab("Events", eventScrollPane); + + chooseButton.setText("Browse"); + chooseButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + chooseButtonActionPerformed(evt); + } + }); + + locationText.setEditable(false); + + openLocButton.setText("Open"); + openLocButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + openLocButtonActionPerformed(evt); + } + }); + + jLabel15.setText("/JDiskMarkData"); + + jLabel22.setText("Specify the location where the data files will be generated and read from to assess each sample's bandwidth."); + + javax.swing.GroupLayout locationPanelLayout = new javax.swing.GroupLayout(locationPanel); + locationPanel.setLayout(locationPanelLayout); + locationPanelLayout.setHorizontalGroup( + locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(locationPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(locationPanelLayout.createSequentialGroup() + .addComponent(jLabel22, javax.swing.GroupLayout.PREFERRED_SIZE, 598, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(locationPanelLayout.createSequentialGroup() + .addComponent(locationText, javax.swing.GroupLayout.PREFERRED_SIZE, 480, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel15, javax.swing.GroupLayout.DEFAULT_SIZE, 191, Short.MAX_VALUE) + .addGap(18, 18, 18) + .addComponent(chooseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(openLocButton, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + locationPanelLayout.setVerticalGroup( + locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(locationPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(locationText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(chooseButton) + .addComponent(openLocButton) + .addComponent(jLabel15)) + .addGap(18, 18, 18) + .addComponent(jLabel22) + .addContainerGap(20, Short.MAX_VALUE)) + ); + + tabbedPane.addTab("Drive Location", locationPanel); + + mountPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); mountPanel.setMaximumSize(new java.awt.Dimension(503, 200)); + mountPanel.setName("renderCombo"); // NOI18N javax.swing.GroupLayout mountPanelLayout = new javax.swing.GroupLayout(mountPanel); mountPanel.setLayout(mountPanelLayout); @@ -167,12 +393,6 @@ private void initComponents() { controlsPanel.setPreferredSize(new java.awt.Dimension(250, 420)); - jLabel1.setText("Write Min"); - - jLabel2.setText("Write Max"); - - jLabel3.setText("Write Avg"); - numBlocksCombo.setEditable(true); numBlocksCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "1", "2", "3", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192" })); numBlocksCombo.setSelectedIndex(6); @@ -182,9 +402,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); - jLabel5.setText("No. Blocks"); + jLabel5.setText("Blocks / Sample"); - jLabel6.setText("Block (KB)"); + jLabel6.setText("Block Size (KB)"); blockSizeCombo.setEditable(true); blockSizeCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048" })); @@ -195,73 +415,106 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); - startButton.setText("Start"); - startButton.addActionListener(new java.awt.event.ActionListener() { + jLabel8.setText("No. Samples"); + + numSamplesCombo.setEditable(true); + numSamplesCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "50", "100", "200", "300", "500", "1000", "2000", "3000", "5000", "10000" })); + numSamplesCombo.setSelectedIndex(2); + numSamplesCombo.setPreferredSize(new java.awt.Dimension(72, 24)); + numSamplesCombo.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - startButtonActionPerformed(evt); + numSamplesComboActionPerformed(evt); } }); - jLabel8.setText("No. Marks"); + jLabel4.setText("Benchmark Type"); - numFilesCombo.setEditable(true); - numFilesCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "25", "50", "75", "100", "150", "200", "250" })); - numFilesCombo.addActionListener(new java.awt.event.ActionListener() { + typeCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "" })); + typeCombo.setPreferredSize(new java.awt.Dimension(60, 24)); + typeCombo.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - numFilesComboActionPerformed(evt); + typeComboActionPerformed(evt); } }); - jLabel4.setText("IO Mode"); + jLabel9.setText("Sample Size (KB)"); + + sampleSizeLabel.setText("- -"); + sampleSizeLabel.setPreferredSize(new java.awt.Dimension(70, 18)); - modeCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "write", "read", "write&read" })); - modeCombo.addActionListener(new java.awt.event.ActionListener() { + orderComboBox.setMaximumSize(new java.awt.Dimension(106, 32767)); + orderComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - modeComboActionPerformed(evt); + orderComboBoxActionPerformed(evt); } }); - jLabel9.setText("Mark Size (KB)"); - - fileSizeLabel.setText("- -"); - - jLabel10.setText("Read Min"); - - jLabel11.setText("Read Max"); - - jLabel12.setText("Read Avg"); + jLabel14.setText("Block Order"); - resetButton.setText("Reset"); - resetButton.addActionListener(new java.awt.event.ActionListener() { + startButton.setText("Start"); + startButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - resetButtonActionPerformed(evt); + startButtonActionPerformed(evt); } }); - jLabel13.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); - jLabel13.setText("Tx Rates (MB/s)"); - - orderComboBox.setMaximumSize(new java.awt.Dimension(106, 32767)); - orderComboBox.addActionListener(new java.awt.event.ActionListener() { + numThreadsCombo.setEditable(true); + numThreadsCombo.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "1", "2", "4", "8", "16" })); + numThreadsCombo.setSelectedIndex(2); + numThreadsCombo.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - orderComboBoxActionPerformed(evt); + numThreadsComboActionPerformed(evt); } }); - jLabel14.setText("Block Order"); - - wMinLabel.setText("- -"); - - wMaxLabel.setText("- -"); + jLabel21.setText("Number Threads"); wAvgLabel.setText("- -"); + jLabel19.setText("IOPS"); + rMinLabel.setText("- -"); + wIopsLabel.setText("- -"); + rMaxLabel.setText("- -"); + rIopsLabel.setText("- -"); + rAvgLabel.setText("- -"); + jLabel20.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel20.setText("Read IO (MB/s)"); + + jLabel10.setText("Min"); + + jLabel17.setText("Acc (ms)"); + + jLabel1.setText("Min"); + + rAccessLabel.setText("- -"); + + jLabel2.setText("Max"); + + jLabel16.setText("Acc (ms)"); + + jLabel3.setText("Avg"); + + jLabel13.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel13.setText("Write IO (MB/s)"); + + wAccessLabel.setText("- -"); + + jLabel11.setText("Max"); + + wMinLabel.setText("- -"); + + jLabel12.setText("Avg"); + + wMaxLabel.setText("- -"); + + jLabel18.setText("IOPS"); + javax.swing.GroupLayout controlsPanelLayout = new javax.swing.GroupLayout(controlsPanel); controlsPanel.setLayout(controlsPanelLayout); controlsPanelLayout.setHorizontalGroup( @@ -269,182 +522,149 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { .addGroup(controlsPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlsPanelLayout.createSequentialGroup() + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel9) + .addGap(18, 18, 18) + .addComponent(sampleSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(startButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel13) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel18) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(wIopsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel16) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(wAccessLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, controlsPanelLayout.createSequentialGroup() + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1) + .addComponent(jLabel2) + .addComponent(jLabel3)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(wAvgLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(wMaxLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(wMinLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE))))) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel20) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel19) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(rIopsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, controlsPanelLayout.createSequentialGroup() + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(jLabel11) + .addComponent(jLabel10) + .addComponent(jLabel12)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(rMinLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(rMaxLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(rAvgLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 65, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel17) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(rAccessLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE))))) + .addGroup(controlsPanelLayout.createSequentialGroup() + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel5) + .addComponent(jLabel4) + .addComponent(jLabel6) + .addComponent(jLabel8)) + .addGap(18, 18, 18) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(typeCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(blockSizeCombo, 0, 100, Short.MAX_VALUE) + .addComponent(numSamplesCombo, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(numBlocksCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGroup(controlsPanelLayout.createSequentialGroup() .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel1) - .addComponent(jLabel2) - .addComponent(jLabel3) - .addComponent(jLabel10) - .addComponent(jLabel11) - .addComponent(jLabel12)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(rMaxLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 80, Short.MAX_VALUE) - .addComponent(rMinLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(wAvgLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(wMaxLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(wMinLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(rAvgLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addGap(20, 20, 20)) - .addComponent(jLabel13, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlsPanelLayout.createSequentialGroup() + .addComponent(jLabel21) + .addComponent(jLabel14)) + .addGap(18, 18, 18) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel5) - .addComponent(jLabel8) - .addComponent(jLabel6) - .addComponent(jLabel9) - .addComponent(jLabel4) - .addComponent(jLabel14) - .addComponent(resetButton, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(controlsPanelLayout.createSequentialGroup() - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(blockSizeCombo, javax.swing.GroupLayout.Alignment.LEADING, 0, 1, Short.MAX_VALUE) - .addComponent(numBlocksCombo, javax.swing.GroupLayout.Alignment.LEADING, 0, 1, Short.MAX_VALUE) - .addComponent(numFilesCombo, javax.swing.GroupLayout.Alignment.LEADING, 0, 1, Short.MAX_VALUE) - .addComponent(orderComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(modeCombo, javax.swing.GroupLayout.Alignment.LEADING, 0, 94, Short.MAX_VALUE) - .addComponent(startButton, javax.swing.GroupLayout.DEFAULT_SIZE, 77, Short.MAX_VALUE)) - .addContainerGap()))))) + .addComponent(orderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(numThreadsCombo, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addContainerGap(27, Short.MAX_VALUE)) ); controlsPanelLayout.setVerticalGroup( controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlsPanelLayout.createSequentialGroup() - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(startButton) - .addComponent(resetButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel4) - .addComponent(modeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(typeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel21) + .addComponent(numThreadsCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(orderComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel14)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel8) - .addComponent(numFilesCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(18, 18, 18) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel5) - .addComponent(numBlocksCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(numBlocksCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel5)) + .addGap(18, 18, 18) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jLabel6) .addComponent(blockSizeCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel9) - .addComponent(fileSizeLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jLabel13) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) - .addComponent(wMinLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(18, 18, 18) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel2) - .addComponent(wMaxLabel)) + .addComponent(jLabel8) + .addComponent(numSamplesCombo, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGap(18, 18, 18) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(sampleSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(jLabel9, javax.swing.GroupLayout.Alignment.TRAILING)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(startButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel3) - .addComponent(wAvgLabel)) + .addComponent(jLabel13) + .addComponent(jLabel20)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel1) + .addComponent(wMinLabel) .addComponent(jLabel10) .addComponent(rMinLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel11) - .addComponent(rMaxLabel)) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel11) + .addComponent(rMaxLabel)) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent(wMaxLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(wAvgLabel) .addComponent(jLabel12) .addComponent(rAvgLabel)) - .addGap(70, 70, 70)) - ); - - javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); - jPanel2.setLayout(jPanel2Layout); - jPanel2Layout.setHorizontalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() - .addComponent(mountPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(controlsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 217, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - jPanel2Layout.setVerticalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(mountPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(controlsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 381, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 6, Short.MAX_VALUE)) - ); - - tabbedPane.addTab("Previous Runs", runPanel); - - msgTextArea.setEditable(false); - msgTextArea.setColumns(20); - msgTextArea.setFont(new java.awt.Font("Monospaced", 0, 11)); // NOI18N - msgTextArea.setRows(5); - msgTextArea.setTabSize(4); - eventScrollPane.setViewportView(msgTextArea); - - tabbedPane.addTab("Event Logs", eventScrollPane); - - chooseButton.setText("Browse"); - chooseButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - chooseButtonActionPerformed(evt); - } - }); - - locationText.setEditable(false); - - openLocButton.setText("Open"); - openLocButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - openLocButtonActionPerformed(evt); - } - }); - - jLabel15.setText("/jDiskMarkData"); - - javax.swing.GroupLayout locationPanelLayout = new javax.swing.GroupLayout(locationPanel); - locationPanel.setLayout(locationPanelLayout); - locationPanelLayout.setHorizontalGroup( - locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(locationPanelLayout.createSequentialGroup() - .addContainerGap() - .addComponent(locationText, javax.swing.GroupLayout.PREFERRED_SIZE, 480, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jLabel15, javax.swing.GroupLayout.DEFAULT_SIZE, 278, Short.MAX_VALUE) - .addGap(18, 18, 18) - .addComponent(chooseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 100, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(openLocButton, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - locationPanelLayout.setVerticalGroup( - locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(locationPanelLayout.createSequentialGroup() - .addContainerGap() - .addGroup(locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(locationText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(chooseButton) - .addComponent(openLocButton) - .addComponent(jLabel15)) - .addContainerGap(140, Short.MAX_VALUE)) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel16) + .addComponent(wAccessLabel) + .addComponent(jLabel17) + .addComponent(rAccessLabel)) + .addGap(8, 8, 8) + .addGroup(controlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel18) + .addComponent(wIopsLabel) + .addComponent(jLabel19) + .addComponent(rIopsLabel))) ); - tabbedPane.addTab("Data Location", locationPanel); - jLabel7.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); jLabel7.setText("Total Tx (KB)"); @@ -454,42 +674,35 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { progressPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, progressPanelLayout.createSequentialGroup() .addContainerGap() - .addComponent(jLabel7, javax.swing.GroupLayout.PREFERRED_SIZE, 69, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel7, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(totalTxProgBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addContainerGap()) ); progressPanelLayout.setVerticalGroup( progressPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, progressPanelLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(totalTxProgBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(progressPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(progressPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel7, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(totalTxProgBar, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) - .addComponent(jLabel7, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); fileMenu.setText("File"); - jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_MASK)); + jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_DOWN_MASK)); jMenuItem1.setText("Exit"); jMenuItem1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - jMenuItem1ActionPerformed(evt); + exitMenuItemActionPerformed(evt); } }); fileMenu.add(jMenuItem1); - jMenuBar1.add(fileMenu); + menuBar.add(fileMenu); - optionMenu.setText("Options"); - - clearRunsItem.setText("Clear Previous Runs"); - clearRunsItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - clearRunsItemActionPerformed(evt); - } - }); - optionMenu.add(clearRunsItem); + actionMenu.setText("Action"); clearLogsItem.setText("Clear Event Logs"); clearLogsItem.addActionListener(new java.awt.event.ActionListener() { @@ -497,15 +710,31 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { clearLogsItemActionPerformed(evt); } }); - optionMenu.add(clearLogsItem); + actionMenu.add(clearLogsItem); - deleteDataMenuItem.setText("Delete Data Dir"); + deleteDataMenuItem.setText("Delete Data Directory"); deleteDataMenuItem.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { deleteDataMenuItemActionPerformed(evt); } }); - optionMenu.add(deleteDataMenuItem); + actionMenu.add(deleteDataMenuItem); + + deleteSelBenchmarksItem.setText("Delete Selected Benchmark"); + deleteSelBenchmarksItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteSelBenchmarksItemActionPerformed(evt); + } + }); + actionMenu.add(deleteSelBenchmarksItem); + + deleteAllBenchmarksItem.setText("Delete All Benchmarks"); + deleteAllBenchmarksItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + deleteAllBenchmarksItemActionPerformed(evt); + } + }); + actionMenu.add(deleteAllBenchmarksItem); resetSequenceMenuItem.setText("Reset Sequence"); resetSequenceMenuItem.addActionListener(new java.awt.event.ActionListener() { @@ -513,8 +742,28 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { resetSequenceMenuItemActionPerformed(evt); } }); - optionMenu.add(resetSequenceMenuItem); - optionMenu.add(jSeparator1); + actionMenu.add(resetSequenceMenuItem); + + resetBenchmarkItem.setText("Reset Benchmark"); + resetBenchmarkItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + resetBenchmarkItemActionPerformed(evt); + } + }); + actionMenu.add(resetBenchmarkItem); + + menuBar.add(actionMenu); + + optionMenu.setText("Options"); + + writeSyncCheckBoxMenuItem.setSelected(true); + writeSyncCheckBoxMenuItem.setText("Write Sync"); + writeSyncCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + writeSyncCheckBoxMenuItemActionPerformed(evt); + } + }); + optionMenu.add(writeSyncCheckBoxMenuItem); multiFileCheckBoxMenuItem.setSelected(true); multiFileCheckBoxMenuItem.setText("Multi Data File"); @@ -542,6 +791,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { } }); optionMenu.add(autoResetCheckBoxMenuItem); + optionMenu.add(jSeparator2); showMaxMinCheckBoxMenuItem.setSelected(true); showMaxMinCheckBoxMenuItem.setText("Show Max Min"); @@ -552,106 +802,172 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { }); optionMenu.add(showMaxMinCheckBoxMenuItem); - writeSyncCheckBoxMenuItem.setSelected(true); - writeSyncCheckBoxMenuItem.setText("Write Sync"); - writeSyncCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() { + showAccessCheckBoxMenuItem.setSelected(true); + showAccessCheckBoxMenuItem.setText("Show Access Time"); + showAccessCheckBoxMenuItem.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - writeSyncCheckBoxMenuItemActionPerformed(evt); + showAccessCheckBoxMenuItemActionPerformed(evt); } }); - optionMenu.add(writeSyncCheckBoxMenuItem); + optionMenu.add(showAccessCheckBoxMenuItem); + optionMenu.add(jSeparator1); + + colorPaletteMenu.setText("Color Palette"); + palettebuttonGroup.add(colorPaletteMenu); + + palettebuttonGroup.add(classicPaletteMenuItem); + classicPaletteMenuItem.setText("Classic"); + classicPaletteMenuItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + classicPaletteMenuItemActionPerformed(evt); + } + }); + colorPaletteMenu.add(classicPaletteMenuItem); + + palettebuttonGroup.add(blueGreenPaletteMenuItem); + blueGreenPaletteMenuItem.setText("Blue Green"); + blueGreenPaletteMenuItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + blueGreenPaletteMenuItemActionPerformed(evt); + } + }); + colorPaletteMenu.add(blueGreenPaletteMenuItem); + + palettebuttonGroup.add(bardCoolPaletteMenuItem); + bardCoolPaletteMenuItem.setText("Bard Cool"); + bardCoolPaletteMenuItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bardCoolPaletteMenuItemActionPerformed(evt); + } + }); + colorPaletteMenu.add(bardCoolPaletteMenuItem); + + palettebuttonGroup.add(bardWarmPaletteMenuItem); + bardWarmPaletteMenuItem.setText("Bard Warm"); + bardWarmPaletteMenuItem.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + bardWarmPaletteMenuItemActionPerformed(evt); + } + }); + colorPaletteMenu.add(bardWarmPaletteMenuItem); + + optionMenu.add(colorPaletteMenu); + optionMenu.add(jSeparator3); + + jMenuItem3.setText("Advanced Options"); + jMenuItem3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jMenuItem3ActionPerformed(evt); + } + }); + optionMenu.add(jMenuItem3); - jMenuBar1.add(optionMenu); + menuBar.add(optionMenu); helpMenu.setText("Help"); jMenuItem2.setText("About..."); jMenuItem2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - jMenuItem2ActionPerformed(evt); + aboutMenuItemActionPerformed(evt); } }); helpMenu.add(jMenuItem2); - jMenuBar1.add(helpMenu); + menuBar.add(helpMenu); - setJMenuBar(jMenuBar1); + setJMenuBar(menuBar); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel2, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(progressPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1000, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(tabbedPane) + .addContainerGap()) + .addComponent(progressPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(controlsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 242, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(mountPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(controlsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 419, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(mountPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(tabbedPane) + .addComponent(tabbedPane, javax.swing.GroupLayout.DEFAULT_SIZE, 118, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(progressPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); pack(); }// </editor-fold>//GEN-END:initComponents - + private void chooseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chooseButtonActionPerformed - if (App.locationDir != null && App.locationDir.exists()) { - Gui.selFrame.setInitDir(App.locationDir); - } - Gui.selFrame.setVisible(true); + Gui.browseLocation(); }//GEN-LAST:event_chooseButtonActionPerformed private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonActionPerformed - if (App.state==App.State.DISK_TEST_STATE) { + if (App.state == App.State.DISK_TEST_STATE) { App.cancelBenchmark(); - } else if (App.state==App.State.IDLE_STATE) { + } else if (App.state == App.State.IDLE_STATE) { applyTestParams(); + App.saveConfig(); App.startBenchmark(); } - }//GEN-LAST:event_startButtonActionPerformed private void blockSizeComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_blockSizeComboActionPerformed - App.blockSizeKb = Integer.valueOf((String) blockSizeCombo.getSelectedItem()); - fileSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); - totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); + // NOTE: selecting a value from dropdown does not trigger the below + if (blockSizeCombo.hasFocus()) { + App.blockSizeKb = Integer.parseInt((String) blockSizeCombo.getSelectedItem()); + sampleSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); + totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); + App.saveConfig(); + } }//GEN-LAST:event_blockSizeComboActionPerformed private void numBlocksComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numBlocksComboActionPerformed - App.numOfBlocks = Integer.valueOf((String) numBlocksCombo.getSelectedItem()); - fileSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); - totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); + // NOTE: selecting a value from dropdown does not trigger the below + if (numBlocksCombo.hasFocus()) { + App.numOfBlocks = Integer.parseInt((String) numBlocksCombo.getSelectedItem()); + sampleSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); + totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); + App.saveConfig(); + } }//GEN-LAST:event_numBlocksComboActionPerformed - private void numFilesComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numFilesComboActionPerformed - App.numOfMarks = Integer.valueOf((String) numFilesCombo.getSelectedItem()); - fileSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); - totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); - }//GEN-LAST:event_numFilesComboActionPerformed - - private void resetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetButtonActionPerformed - App.resetTestData(); - Gui.resetTestData(); - }//GEN-LAST:event_resetButtonActionPerformed + private void numSamplesComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numSamplesComboActionPerformed + // NOTE: selecting a value from dropdown does not trigger the below + if (numSamplesCombo.hasFocus()) { + App.numOfSamples = Integer.parseInt((String) numSamplesCombo.getSelectedItem()); + sampleSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); + totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); + App.saveConfig(); + } + }//GEN-LAST:event_numSamplesComboActionPerformed - private void modeComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modeComboActionPerformed - String modeStr = (String) modeCombo.getSelectedItem(); - App.readTest = modeStr.contains("read"); - App.writeTest = modeStr.contains("write"); - }//GEN-LAST:event_modeComboActionPerformed + private void typeComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_typeComboActionPerformed + if (typeCombo.hasFocus()) { + Benchmark.BenchmarkType mode = (Benchmark.BenchmarkType) typeCombo.getSelectedItem(); + App.benchmarkType = mode; + App.saveConfig(); + } + }//GEN-LAST:event_typeComboActionPerformed - private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed + private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed System.exit(0); - }//GEN-LAST:event_jMenuItem1ActionPerformed + }//GEN-LAST:event_exitMenuItemActionPerformed - private void jMenuItem2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed + private void aboutMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutMenuItemActionPerformed JOptionPane.showMessageDialog(Gui.mainFrame, - "jDiskMark "+App.getVersion(),"About...",JOptionPane.PLAIN_MESSAGE); - }//GEN-LAST:event_jMenuItem2ActionPerformed + "JDiskMark " + App.VERSION, "About...", JOptionPane.PLAIN_MESSAGE); + }//GEN-LAST:event_aboutMenuItemActionPerformed private void openLocButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openLocButtonActionPerformed try { @@ -690,10 +1006,14 @@ private void resetSequenceMenuItemActionPerformed(java.awt.event.ActionEvent evt private void showMaxMinCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showMaxMinCheckBoxMenuItemActionPerformed App.showMaxMin = showMaxMinCheckBoxMenuItem.getState(); + App.saveConfig(); }//GEN-LAST:event_showMaxMinCheckBoxMenuItemActionPerformed private void orderComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_orderComboBoxActionPerformed - App.blockSequence = (DiskRun.BlockSequence) orderComboBox.getSelectedItem(); + if (orderComboBox.hasFocus()) { + App.blockSequence = (BenchmarkOperation.BlockSequence) orderComboBox.getSelectedItem(); + App.saveConfig(); + } }//GEN-LAST:event_orderComboBoxActionPerformed private void writeSyncCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_writeSyncCheckBoxMenuItemActionPerformed @@ -701,26 +1021,93 @@ private void writeSyncCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent App.saveConfig(); }//GEN-LAST:event_writeSyncCheckBoxMenuItemActionPerformed - private void clearRunsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearRunsItemActionPerformed - App.msg("Clearing previous runs."); - App.clearSavedRuns(); - }//GEN-LAST:event_clearRunsItemActionPerformed + private void deleteAllBenchmarksItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteAllBenchmarksItemActionPerformed + int result = JOptionPane.showConfirmDialog(this, + "Delete all benchmarks?", + "Confirm Delete", + JOptionPane.YES_NO_OPTION); + if (result == JOptionPane.YES_OPTION) { + App.msg("Deleting all benchmarks."); + App.deleteAllBenchmarks(); + } + }//GEN-LAST:event_deleteAllBenchmarksItemActionPerformed + + private void showAccessCheckBoxMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showAccessCheckBoxMenuItemActionPerformed + App.showDriveAccess = showAccessCheckBoxMenuItem.getState(); + App.saveConfig(); + }//GEN-LAST:event_showAccessCheckBoxMenuItemActionPerformed + + private void blueGreenPaletteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_blueGreenPaletteMenuItemActionPerformed + Gui.setBlueGreenScheme(); + App.saveConfig(); + }//GEN-LAST:event_blueGreenPaletteMenuItemActionPerformed + + private void classicPaletteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classicPaletteMenuItemActionPerformed + Gui.setClassicColorScheme(); + App.saveConfig(); + }//GEN-LAST:event_classicPaletteMenuItemActionPerformed + + private void bardCoolPaletteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bardCoolPaletteMenuItemActionPerformed + Gui.setCoolColorScheme(); + App.saveConfig(); + }//GEN-LAST:event_bardCoolPaletteMenuItemActionPerformed + + private void bardWarmPaletteMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bardWarmPaletteMenuItemActionPerformed + Gui.setWarmColorScheme(); + App.saveConfig(); + }//GEN-LAST:event_bardWarmPaletteMenuItemActionPerformed + + private void deleteSelBenchmarksItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteSelBenchmarksItemActionPerformed + int result = JOptionPane.showConfirmDialog(this, + "Delete selected benchmarks?", + "Confirm Delete", + JOptionPane.YES_NO_OPTION); + + if (result == JOptionPane.YES_OPTION) { + App.msg("Deleting selected benchmarks."); + List<Long> benchmarkIds = Gui.runPanel.getSelectedIds(); + App.deleteBenchmarks(benchmarkIds); + } + }//GEN-LAST:event_deleteSelBenchmarksItemActionPerformed + + private void resetBenchmarkItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetBenchmarkItemActionPerformed + App.resetTestData(); + Gui.resetBenchmarkData(); + Gui.updateLegendAndAxis(); + }//GEN-LAST:event_resetBenchmarkItemActionPerformed + + private void numThreadsComboActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numThreadsComboActionPerformed + // NOTE: selecting a value from dropdown does not trigger the below + if (numThreadsCombo.hasFocus()) { + App.numOfThreads = Integer.parseInt((String) numThreadsCombo.getSelectedItem()); + App.saveConfig(); + } + }//GEN-LAST:event_numThreadsComboActionPerformed + + private void jMenuItem3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem3ActionPerformed + Gui.advancedFrame.setVisible(true); + }//GEN-LAST:event_jMenuItem3ActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JMenu actionMenu; private javax.swing.JCheckBoxMenuItem autoRemoveCheckBoxMenuItem; private javax.swing.JCheckBoxMenuItem autoResetCheckBoxMenuItem; + private javax.swing.JRadioButtonMenuItem bardCoolPaletteMenuItem; + private javax.swing.JRadioButtonMenuItem bardWarmPaletteMenuItem; private javax.swing.JComboBox blockSizeCombo; + private javax.swing.JRadioButtonMenuItem blueGreenPaletteMenuItem; private javax.swing.JButton chooseButton; + private javax.swing.JRadioButtonMenuItem classicPaletteMenuItem; private javax.swing.JMenuItem clearLogsItem; - private javax.swing.JMenuItem clearRunsItem; + private javax.swing.JMenu colorPaletteMenu; private javax.swing.JPanel controlsPanel; + private javax.swing.JMenuItem deleteAllBenchmarksItem; private javax.swing.JMenuItem deleteDataMenuItem; + private javax.swing.JMenuItem deleteSelBenchmarksItem; private javax.swing.JScrollPane eventScrollPane; private javax.swing.JMenu fileMenu; - private javax.swing.JLabel fileSizeLabel; private javax.swing.JMenu helpMenu; - private javax.swing.JButton jButton1; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel10; private javax.swing.JLabel jLabel11; @@ -728,7 +1115,14 @@ private void clearRunsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN private javax.swing.JLabel jLabel13; private javax.swing.JLabel jLabel14; private javax.swing.JLabel jLabel15; + private javax.swing.JLabel jLabel16; + private javax.swing.JLabel jLabel17; + private javax.swing.JLabel jLabel18; + private javax.swing.JLabel jLabel19; private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel20; + private javax.swing.JLabel jLabel21; + private javax.swing.JLabel jLabel22; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; @@ -736,34 +1130,44 @@ private void clearRunsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN private javax.swing.JLabel jLabel7; private javax.swing.JLabel jLabel8; private javax.swing.JLabel jLabel9; - private javax.swing.JMenuBar jMenuBar1; private javax.swing.JMenuItem jMenuItem1; private javax.swing.JMenuItem jMenuItem2; - private javax.swing.JPanel jPanel2; + private javax.swing.JMenuItem jMenuItem3; private javax.swing.JPopupMenu.Separator jSeparator1; + private javax.swing.JPopupMenu.Separator jSeparator2; + private javax.swing.JPopupMenu.Separator jSeparator3; private javax.swing.JPanel locationPanel; private javax.swing.JTextField locationText; - private javax.swing.JComboBox modeCombo; + private javax.swing.JMenuBar menuBar; private javax.swing.JPanel mountPanel; private javax.swing.JTextArea msgTextArea; private javax.swing.JCheckBoxMenuItem multiFileCheckBoxMenuItem; private javax.swing.JComboBox numBlocksCombo; - private javax.swing.JComboBox numFilesCombo; + private javax.swing.JComboBox numSamplesCombo; + private javax.swing.JComboBox numThreadsCombo; private javax.swing.JButton openLocButton; private javax.swing.JMenu optionMenu; - private javax.swing.JComboBox orderComboBox; + private javax.swing.JComboBox<BenchmarkOperation.BlockSequence> orderComboBox; + private javax.swing.ButtonGroup palettebuttonGroup; private javax.swing.JPanel progressPanel; + private javax.swing.JLabel rAccessLabel; private javax.swing.JLabel rAvgLabel; + private javax.swing.JLabel rIopsLabel; private javax.swing.JLabel rMaxLabel; private javax.swing.JLabel rMinLabel; - private javax.swing.JButton resetButton; + private javax.swing.JMenuItem resetBenchmarkItem; private javax.swing.JMenuItem resetSequenceMenuItem; - private jdiskmark.RunPanel runPanel; + private jdiskmark.BenchmarkPanel runPanel; + private javax.swing.JLabel sampleSizeLabel; + private javax.swing.JCheckBoxMenuItem showAccessCheckBoxMenuItem; private javax.swing.JCheckBoxMenuItem showMaxMinCheckBoxMenuItem; private javax.swing.JButton startButton; private javax.swing.JTabbedPane tabbedPane; private javax.swing.JProgressBar totalTxProgBar; + private javax.swing.JComboBox typeCombo; + private javax.swing.JLabel wAccessLabel; private javax.swing.JLabel wAvgLabel; + private javax.swing.JLabel wIopsLabel; private javax.swing.JLabel wMaxLabel; private javax.swing.JLabel wMinLabel; private javax.swing.JCheckBoxMenuItem writeSyncCheckBoxMenuItem; @@ -778,35 +1182,43 @@ public void msg(String message) { } public void applyTestParams() { - String modeStr = (String) modeCombo.getSelectedItem(); - App.readTest = modeStr.contains("read"); - App.writeTest = modeStr.contains("write"); - App.blockSequence = (DiskRun.BlockSequence)orderComboBox.getSelectedItem(); - App.numOfMarks = Integer.valueOf((String) numFilesCombo.getSelectedItem()); - App.numOfBlocks = Integer.valueOf((String) numBlocksCombo.getSelectedItem()); - App.blockSizeKb = Integer.valueOf((String) blockSizeCombo.getSelectedItem()); - fileSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); + Benchmark.BenchmarkType mode = (Benchmark.BenchmarkType) typeCombo.getSelectedItem(); + App.benchmarkType = mode; + App.blockSequence = (BenchmarkOperation.BlockSequence) orderComboBox.getSelectedItem(); + App.numOfSamples = Integer.parseInt((String) numSamplesCombo.getSelectedItem()); + App.numOfBlocks = Integer.parseInt((String) numBlocksCombo.getSelectedItem()); + App.blockSizeKb = Integer.parseInt((String) blockSizeCombo.getSelectedItem()); + App.numOfThreads = Integer.parseInt((String) numThreadsCombo.getSelectedItem()); + sampleSizeLabel.setText(String.valueOf(App.targetMarkSizeKb())); totalTxProgBar.setString(String.valueOf(App.targetTxSizeKb())); } public void refreshWriteMetrics() { String value; - value = App.wMin==-1 ? "- -" : df.format(App.wMin); + value = App.wMin == -1 ? "- -" : DF.format(App.wMin); wMinLabel.setText(value); - value = App.wMax==-1 ? "- -" : df.format(App.wMax); + value = App.wMax == -1 ? "- -" : DF.format(App.wMax); wMaxLabel.setText(value); - value = App.wAvg==-1 ? "- -" : df.format(App.wAvg); + value = App.wAvg == -1 ? "- -" : DF.format(App.wAvg); wAvgLabel.setText(value); + value = App.wAcc == -1 ? "- -" : DF.format(App.wAcc); + wAccessLabel.setText(value); + value = App.wIops == -1 ? "- -" : String.valueOf(App.wIops); + wIopsLabel.setText(value); } public void refreshReadMetrics() { String value; - value = App.rMin==-1 ? "- -" : df.format(App.rMin); + value = App.rMin == -1 ? "- -" : DF.format(App.rMin); rMinLabel.setText(value); - value = App.rMax==-1 ? "- -" : df.format(App.rMax); + value = App.rMax == -1 ? "- -" : DF.format(App.rMax); rMaxLabel.setText(value); - value = App.rAvg==-1 ? "- -" : df.format(App.rAvg); + value = App.rAvg == -1 ? "- -" : DF.format(App.rAvg); rAvgLabel.setText(value); + value = App.rAcc == -1 ? "- -" : DF.format(App.rAcc); + rAccessLabel.setText(value); + value = App.rIops == -1 ? "- -" : String.valueOf(App.rIops); + rIopsLabel.setText(value); } public javax.swing.JProgressBar getProgressBar() { @@ -817,24 +1229,43 @@ public void clearMessages() { msgTextArea.setText(""); } - public void adjustSensitivity() { - if (App.state == App.State.DISK_TEST_STATE) { - startButton.setText("Cancel"); - orderComboBox.setEnabled(false); - blockSizeCombo.setEnabled(false); - numBlocksCombo.setEnabled(false); - numFilesCombo.setEnabled(false); - modeCombo.setEnabled(false); - resetButton.setEnabled(false); - } else if (App.state == App.State.IDLE_STATE){ - startButton.setText("Start"); - orderComboBox.setEnabled(true); - blockSizeCombo.setEnabled(true); - numBlocksCombo.setEnabled(true); - numFilesCombo.setEnabled(true); - modeCombo.setEnabled(true); - resetButton.setEnabled(true); - } + public void setTableVisible(boolean visible) { + // TEMP: No table component yet, so just log or stub + System.out.println("Requested to set table visibility to: " + visible); } + + public void adjustSensitivity() { + switch (App.state) { + case App.State.DISK_TEST_STATE -> { + startButton.setText("Cancel"); + orderComboBox.setEnabled(false); + blockSizeCombo.setEnabled(false); + numBlocksCombo.setEnabled(false); + numSamplesCombo.setEnabled(false); + typeCombo.setEnabled(false); + numThreadsCombo.setEnabled(false); + resetBenchmarkItem.setEnabled(false); + } + case App.State.IDLE_STATE -> { + startButton.setText("Start"); + orderComboBox.setEnabled(true); + blockSizeCombo.setEnabled(true); + numBlocksCombo.setEnabled(true); + numSamplesCombo.setEnabled(true); + typeCombo.setEnabled(true); + numThreadsCombo.setEnabled(true); + resetBenchmarkItem.setEnabled(true); + } + } + } + // Replace lowercase mode options with proper casing + +// @SuppressWarnings("unchecked") +// private void configureModeCombo() { +// typeCombo.removeAllItems(); +// typeCombo.addItem("Write"); +// typeCombo.addItem("Read"); +// typeCombo.addItem("Read & Write"); +// } } diff --git a/src/jdiskmark/OperationTableSelectionListener.java b/src/jdiskmark/OperationTableSelectionListener.java new file mode 100644 index 0000000..a9b9143 --- /dev/null +++ b/src/jdiskmark/OperationTableSelectionListener.java @@ -0,0 +1,33 @@ + +package jdiskmark; + +import javax.swing.JTable; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import static jdiskmark.BenchmarkPanel.START_TIME_COLUMN; + +public class OperationTableSelectionListener implements ListSelectionListener { + final private JTable table; + + public OperationTableSelectionListener(JTable table) { + this.table = table; + } + + @Override + public void valueChanged(ListSelectionEvent e) { + // This prevents the event from firing twice (once for the old selection, once for the new) + if (e.getValueIsAdjusting()) { + return; + } + + int selectedRow = table.getSelectedRow(); + if (selectedRow != -1) { + String timeString = (String) table.getValueAt(selectedRow, START_TIME_COLUMN); + System.out.println("selected operation starttime=" + timeString); + BenchmarkOperation operation = App.operations.get(timeString); + if (operation != null) { + Gui.loadOperation(operation); + } + } + } +} diff --git a/src/jdiskmark/RenderFrequencyMode.java b/src/jdiskmark/RenderFrequencyMode.java new file mode 100644 index 0000000..7c8ab85 --- /dev/null +++ b/src/jdiskmark/RenderFrequencyMode.java @@ -0,0 +1,29 @@ +package jdiskmark; + +public enum RenderFrequencyMode { + PER_SAMPLE("Per Sample"), + PER_OPERATION("Per Operation"), + PER_100MS("Per 100ms"), + PER_500MS("Per 500ms"), + PER_1000MS("Per 1000ms"); + + private final String label; + + RenderFrequencyMode(String label) { + this.label = label; + } + + @Override + public String toString() { + return label; + } + + public long getIntervalMillis() { + return switch (this) { + case PER_100MS -> 100; + case PER_500MS -> 500; + case PER_1000MS -> 1000; + default -> 0; + }; + } +} \ No newline at end of file diff --git a/src/jdiskmark/RenderFrequencyModeAttributeConverter.java b/src/jdiskmark/RenderFrequencyModeAttributeConverter.java new file mode 100644 index 0000000..790ee19 --- /dev/null +++ b/src/jdiskmark/RenderFrequencyModeAttributeConverter.java @@ -0,0 +1,18 @@ +package jdiskmark; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +@Converter(autoApply = true) +public class RenderFrequencyModeAttributeConverter implements AttributeConverter<RenderFrequencyMode, String> { + + @Override + public String convertToDatabaseColumn(RenderFrequencyMode mode) { + return (mode == null) ? null : mode.name(); + } + + @Override + public RenderFrequencyMode convertToEntityAttribute(String dbData) { + return (dbData == null) ? null : RenderFrequencyMode.valueOf(dbData); + } +} \ No newline at end of file diff --git a/src/jdiskmark/RightTableCellRenderer.java b/src/jdiskmark/RightTableCellRenderer.java new file mode 100644 index 0000000..df1ec25 --- /dev/null +++ b/src/jdiskmark/RightTableCellRenderer.java @@ -0,0 +1,18 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package jdiskmark; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; + +public class RightTableCellRenderer extends DefaultTableCellRenderer { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setHorizontalAlignment(SwingConstants.RIGHT); + return c; + } +} \ No newline at end of file diff --git a/src/jdiskmark/RunPanel.java b/src/jdiskmark/RunPanel.java deleted file mode 100644 index 4103c83..0000000 --- a/src/jdiskmark/RunPanel.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package jdiskmark; - -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import javax.swing.table.DefaultTableModel; - -/** - * - * @author James - */ -public class RunPanel extends javax.swing.JPanel { - - /** - * Creates new form TestPanel - */ - public RunPanel() { - initComponents(); - Gui.runPanel = RunPanel.this; - - // auto scroll to bottom when a new record is added - runTable.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - runTable.scrollRectToVisible(runTable.getCellRect(runTable.getRowCount()-1, 0, true)); - } - }); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents - private void initComponents() { - - jScrollPane1 = new javax.swing.JScrollPane(); - runTable = new javax.swing.JTable(); - - runTable.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - - }, - new String [] { - "Disk Info", "IO Mode", "Block Order", "Marks", "Blocks", "B. Size", "Tx Size", "Start Time", "Duration", "Max (MB/s)", "Min (MB/s)", "Avg (MB/s)" - } - ) { - boolean[] canEdit = new boolean [] { - false, false, false, false, false, false, false, false, false, false, false, false - }; - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit [columnIndex]; - } - }); - jScrollPane1.setViewportView(runTable); - if (runTable.getColumnModel().getColumnCount() > 0) { - runTable.getColumnModel().getColumn(0).setPreferredWidth(35); - runTable.getColumnModel().getColumn(1).setPreferredWidth(25); - runTable.getColumnModel().getColumn(2).setPreferredWidth(50); - runTable.getColumnModel().getColumn(3).setPreferredWidth(7); - runTable.getColumnModel().getColumn(4).setPreferredWidth(10); - runTable.getColumnModel().getColumn(5).setPreferredWidth(10); - runTable.getColumnModel().getColumn(6).setPreferredWidth(10); - runTable.getColumnModel().getColumn(7).setPreferredWidth(100); - runTable.getColumnModel().getColumn(8).setPreferredWidth(20); - runTable.getColumnModel().getColumn(9).setPreferredWidth(32); - runTable.getColumnModel().getColumn(10).setPreferredWidth(32); - runTable.getColumnModel().getColumn(11).setPreferredWidth(32); - } - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 789, Short.MAX_VALUE) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 118, Short.MAX_VALUE) - .addContainerGap()) - ); - }// </editor-fold>//GEN-END:initComponents - - - // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JTable runTable; - // End of variables declaration//GEN-END:variables - - public void addRun(DiskRun run) { - DefaultTableModel model = (DefaultTableModel) this.runTable.getModel(); - model.addRow( - new Object[] { - run.diskInfo, - run.ioMode, - run.blockOrder, - run.numMarks, - run.numBlocks, - run.blockSize, - run.txSize, - run.getStartTimeString(), - run.getDuration(), - run.getMax(), - run.getMin(), - run.getAvg(), - }); - } - - public void clearTable() { - DefaultTableModel model = (DefaultTableModel) this.runTable.getModel(); - while (model.getRowCount() > 0) { - model.removeRow(0); - } - } -} diff --git a/src/jdiskmark/Sample.java b/src/jdiskmark/Sample.java new file mode 100644 index 0000000..2b7026d --- /dev/null +++ b/src/jdiskmark/Sample.java @@ -0,0 +1,87 @@ + +package jdiskmark; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.text.DecimalFormat; + +/** + * A unit of IO measurement + */ +public class Sample { + + static final DecimalFormat DF = new DecimalFormat("###.###"); + public enum Type { READ, WRITE; } + + + Type type; + int sampleNum = 0; // x-axis + double bwMbSec = 0; // y-axis + double cumAvg = 0; + double cumMax = 0; + double cumMin = 0; + double accessTimeMs; + double cumAccTimeMs; + + + // needed for jackson + public Sample() {} + + Sample(Type type, int sampleNumber) { + this.type = type; + sampleNum = sampleNumber; + } + + @Override + public String toString() { + return "Sample(" + type + "): " + sampleNum + " bwMBs=" + getBwMbSecDisplay() + + " avg=" + getAvgDisplay() + " accessTimeMs=" + accessTimeMs; + } + + // getters and setters + + public Type getType() { return type; } + public void setType(Type type) { this.type = type; } + + public int getSampleNum() { return sampleNum; } + public void setSampleNum(int number) { sampleNum = number; } + + // bandwidth statistics + + public double getBwMbSec() { return bwMbSec; } + public void setBwMbSec(double bwMb) { bwMbSec = bwMb; } + + public double getAvg() { return cumAvg; } + public void setAvg(double avg) { cumAvg = avg; } + + public double getMax() { return cumMax; } + public void setMax(double max) { cumMax = max; } + + public double getMin() { return cumMin; } + public void setMin(double min) { cumMin = min; } + + // access time statistics + + public double getAccessTimeMs() { return accessTimeMs; } + public void setAccessTimeMs(double accessTime) { accessTimeMs = accessTime; } + + public double getCumAccTimeMs() { return cumAccTimeMs; } + public void setCumAccTimeMs(double cumAccTime) { cumAccTimeMs = cumAccTime; } + + // display methods + @JsonIgnore + public String getBwMbSecDisplay() { + return DF.format(bwMbSec); + } + @JsonIgnore + public String getAvgDisplay() { + return DF.format(cumAvg); + } + @JsonIgnore + public String getMaxDisplay() { + return DF.format(cumMax); + } + @JsonIgnore + public String getMinDisplay() { + return DF.format(cumMin); + } +} \ No newline at end of file diff --git a/src/jdiskmark/SampleAttributeConverter.java b/src/jdiskmark/SampleAttributeConverter.java new file mode 100644 index 0000000..595c67e --- /dev/null +++ b/src/jdiskmark/SampleAttributeConverter.java @@ -0,0 +1,41 @@ + +package jdiskmark; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +@Converter +public class SampleAttributeConverter implements AttributeConverter<Object, byte[]> { + + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public byte[] convertToDatabaseColumn(Object attribute) { + try { + return mapper.writeValueAsBytes(attribute); + } catch (JsonProcessingException e) { + Logger.getLogger(SampleAttributeConverter.class.getName()).log(Level.SEVERE, null, e); + return new byte[0]; + } + } + + @Override + public Object convertToEntityAttribute(byte[] dbData) { + if (dbData == null) { + return new ArrayList<>(); + } + try { + // Deserialize directly into a List of Samples + return mapper.readValue(dbData, mapper.getTypeFactory().constructCollectionType(ArrayList.class, Sample.class)); + } catch (IOException e) { + Logger.getLogger(SampleAttributeConverter.class.getName()).log(Level.SEVERE, null, e); + return new ArrayList<>(); + } + } +} \ No newline at end of file diff --git a/src/jdiskmark/SelectFrame.form b/src/jdiskmark/SelectDriveFrame.form similarity index 100% rename from src/jdiskmark/SelectFrame.form rename to src/jdiskmark/SelectDriveFrame.form diff --git a/src/jdiskmark/SelectFrame.java b/src/jdiskmark/SelectDriveFrame.java similarity index 79% rename from src/jdiskmark/SelectFrame.java rename to src/jdiskmark/SelectDriveFrame.java index 27e4a47..f4a7353 100644 --- a/src/jdiskmark/SelectFrame.java +++ b/src/jdiskmark/SelectDriveFrame.java @@ -6,19 +6,14 @@ /** * */ -public class SelectFrame extends javax.swing.JFrame { +public class SelectDriveFrame extends javax.swing.JFrame { /** * Creates new form SelectFrame */ - public SelectFrame() { + public SelectDriveFrame() { initComponents(); setLocationRelativeTo(Gui.mainFrame); - if (App.locationDir == null) { - App.locationDir = new File(System.getProperty("user.home")); - App.dataDir = new File(App.locationDir.getAbsolutePath() - +File.separator+App.DATADIRNAME); - } } /** @@ -57,16 +52,17 @@ public void actionPerformed(java.awt.event.ActionEvent evt) { }// </editor-fold>//GEN-END:initComponents private void jFileChooser1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jFileChooser1ActionPerformed - // TODO add your handling code here: System.out.println(evt); switch (evt.getActionCommand()) { - case "ApproveSelection": - App.locationDir = jFileChooser1.getSelectedFile(); + case "ApproveSelection" -> { + App.setLocationDir(jFileChooser1.getSelectedFile()); App.saveConfig(); - Gui.mainFrame.setLocation(App.locationDir.getAbsolutePath()); - break; - case "CancelSelection": - break; + Gui.updateDiskInfo(); + Gui.resetBenchmarkData(); + Gui.runPanel.deselect(); + } + case "CancelSelection" -> { + } } Gui.selFrame.setVisible(false); Gui.selFrame.dispose(); diff --git a/src/jdiskmark/Util.java b/src/jdiskmark/Util.java index b14561a..7217c6d 100644 --- a/src/jdiskmark/Util.java +++ b/src/jdiskmark/Util.java @@ -3,24 +3,25 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; -import java.io.RandomAccessFile; import java.nio.file.Path; import java.nio.file.Paths; import java.text.DecimalFormat; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; import java.util.Random; import javax.swing.filechooser.FileSystemView; /** - * Utility methods for jDiskMark + * Utility methods for JDiskMark */ public class Util { static final DecimalFormat DF = new DecimalFormat("###.##"); + static final String ERROR_DRIVE_INFO = "unable to detect drive info"; + /** * Deletes the Directory and all files within * @param path @@ -62,50 +63,39 @@ public static int randInt(int min, int max) { return randomNum; } - /* - * Not used kept here for reference. - */ - static public void readPhysicalDrive() throws FileNotFoundException, IOException { - File diskRoot = new File ("\\\\.\\PhysicalDrive0"); - RandomAccessFile diskAccess = new RandomAccessFile (diskRoot, "r"); - byte[] content = new byte[1024]; - diskAccess.readFully (content); - System.out.println("done reading fully"); - System.out.println("content "+Arrays.toString(content)); - } - /* * Not used kept here for reference. */ public static void sysStats() { /* Total number of processors or cores available to the JVM */ System.out.println("Available processors (cores): " + - Runtime.getRuntime().availableProcessors()); + Runtime.getRuntime().availableProcessors()); /* Total amount of free memory available to the JVM */ System.out.println("Free memory (bytes): " + - Runtime.getRuntime().freeMemory()); + Runtime.getRuntime().freeMemory()); /* This will return Long.MAX_VALUE if there is no preset limit */ long maxMemory = Runtime.getRuntime().maxMemory(); + /* Maximum amount of memory the JVM will attempt to use */ System.out.println("Maximum memory (bytes): " + - (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory)); + (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory)); /* Total memory currently available to the JVM */ System.out.println("Total memory available to JVM (bytes): " + - Runtime.getRuntime().totalMemory()); + Runtime.getRuntime().totalMemory()); /* Get a list of all filesystem roots on this system */ File[] roots = File.listRoots(); /* For each filesystem root, print some info */ for (File root : roots) { - System.out.println("File system root: " + root.getAbsolutePath()); - System.out.println("Total space (bytes): " + root.getTotalSpace()); - System.out.println("Free space (bytes): " + root.getFreeSpace()); - System.out.println("Usable space (bytes): " + root.getUsableSpace()); - System.out.println("Drive Type: "+getDriveType(root)); + System.out.println("File system root: " + root.getAbsolutePath()); + System.out.println("Total space (bytes): " + root.getTotalSpace()); + System.out.println("Free space (bytes): " + root.getFreeSpace()); + System.out.println("Usable space (bytes): " + root.getUsableSpace()); + System.out.println("Drive Type: " + getDriveType(root)); } } @@ -125,255 +115,143 @@ public static String getDriveType(File file) { } /** - * Get OS specific disk info based on the drive the path is mapped to. + * Get disk model info based on the drive the path is mapped to. * * @param dataDir the data directory being used in the run. * @return Disk info if available. */ - public static String getDiskInfo(File dataDir) { - System.out.println("os: "+System.getProperty("os.name")); + public static String getDriveModel(File dataDir) { + System.out.println("os: " + System.getProperty("os.name")); Path dataDirPath = Paths.get(dataDir.getAbsolutePath()); String osName = System.getProperty("os.name"); + String deviceModel; if (osName.contains("Linux")) { // get disk info for linux - String devicePath = Util.getDeviceFromPath(dataDirPath); - String deviceModel = Util.getDeviceModel(devicePath); - String deviceSize = Util.getDeviceSize(devicePath); - return deviceModel + " (" + deviceSize +")"; - } else if (osName.contains("Mac OS X")) { - // get disk info for max os x - String devicePath = Util.getDeviceFromPathOSX(dataDirPath); - String deviceModel = Util.getDeviceModelOSX(devicePath); + String partition = UtilOs.getPartitionFromFilePathLinux(dataDirPath); + List<String> deviceNames = UtilOs.getDeviceNamesFromPartitionLinux(partition); + + // handle single physical drive + if (deviceNames.size() == 1) { + String devicePath = "/dev/" + deviceNames.getFirst(); + return UtilOs.getDeviceModelLinux(devicePath); + } + + // GH-3 handle multiple drives (LVM or RAID partitions) + if (deviceNames.size() > 1) { + StringBuilder sb = new StringBuilder(); + for (String dName : deviceNames) { + String devicePath = "/dev/" + dName; + deviceModel = UtilOs.getDeviceModelLinux(devicePath); + if (sb.length() > 0) { + sb.append(":"); + } + sb.append(deviceModel); + } + return "Multiple drives: " + sb.toString(); + } + + return ERROR_DRIVE_INFO; + } else if (osName.contains("Mac OS")) { + // get disk info for os x + String devicePath = UtilOs.getDeviceFromPathMacOs(dataDirPath); + System.out.println("devicePath=" + devicePath); + deviceModel = UtilOs.getDeviceModelMacOs(devicePath); + System.out.println("deviceModel=" + deviceModel); return deviceModel; } else if (osName.contains("Windows")) { // get disk info for windows String driveLetter = dataDirPath.getRoot().toFile().toString().split(":")[0]; - return Util.getModelFromLetter2(driveLetter); + if (driveLetter.length() == 1 && Character.isLetter(driveLetter.charAt(0))) { + // Only proceed if the driveLetter is a single character and a letter + deviceModel = UtilOs.getDriveModelWindows(driveLetter); + System.out.println("deviceModel=" + deviceModel); + return deviceModel; + } + return ERROR_DRIVE_INFO; } return "OS not supported"; } - /** - * This method became obsolete with an updated version of windows 10. - * A newer version of the method is used. - * - * Get the drive model description based on the windows drive letter. - * Uses the powershell script disk-model.ps1 - * - * This appears to be the output of the original ps script before the update: + /* + * Example input win11 (english): + * + * C:\Users\james>cmd.exe /c fsutil volume diskfree c:\\users\james + * Total free bytes : 3,421,135,929,344 ( 3.1 TB) + * Total bytes : 3,999,857,111,040 ( 3.6 TB) + * Total quota free bytes : 3,421,135,929,344 ( 3.1 TB) + * Unavailable pool bytes : 0 ( 0.0 KB) + * Quota unavailable pool bytes : 0 ( 0.0 KB) + * Used bytes : 575,413,735,424 (535.9 GB) + * Total Reserved bytes : 3,307,446,272 ( 3.1 GB) + * Volume storage reserved bytes : 2,739,572,736 ( 2.6 GB) + * Available committed bytes : 0 ( 0.0 KB) + * Pool available bytes : 0 ( 0.0 KB) * - * d:\>powershell -ExecutionPolicy ByPass -File tmp.ps1 - - DiskSize : 128034708480 - RawSize : 117894545408 - FreeSpace : 44036825088 - Disk : \\.\PHYSICALDRIVE1 - DriveLetter : C: - DiskModel : SanDisk SD6SF1M128G - VolumeName : OS_Install - Size : 117894541312 - Partition : Disk #1, Partition #2 + * Example input (spanish): + * + * fsutil volume diskfree e:\ + * Total de bytes libres: 26,021,392,384 ( 24.2 GB) + * Total de bytes: 512,108,785,664 (476.9 GB) + * Cuota total de bytes libres: 26,021,392,384 ( 24.2 GB) + */ + public static DiskUsageInfo getDiskUsage(String diskPath) throws IOException, InterruptedException { + ProcessBuilder pb = new ProcessBuilder(); - DiskSize : 320070320640 - RawSize : 320070836224 - FreeSpace : 29038071808 - Disk : \\.\PHYSICALDRIVE2 - DriveLetter : E: - DiskModel : TOSHIBA External USB 3.0 USB Device - VolumeName : TOSHIBA EXT - Size : 320070832128 - Partition : Disk #2, Partition #0 + // Choose the appropriate command for the operating system: + if (App.os.startsWith("Windows")) { + pb.command("cmd.exe", "/c", "fsutil volume diskfree " + diskPath); + } else { + // command is same for linux and mac os + pb.command("df", "-k", diskPath); + } - * We should be able to modify the new parser to detect the - * output type and adjust parsing as needed. - * - * @param driveLetter The single character drive letter. - * @return Disk Drive Model description or empty string if not found. - */ - @Deprecated - public static String getModelFromLetter(String driveLetter) { - try { - Process p = Runtime.getRuntime().exec("powershell -ExecutionPolicy ByPass -File disk-model.ps1"); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line=reader.readLine(); + Process process = pb.start(); + + // Capture the output from the command: + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + List<String> outputLines = new ArrayList<>(); + while ((line = reader.readLine()) != null) { + outputLines.add(line); + } - String curDriveLetter = null; - String curDiskModel = null; - while (line != null) { - System.out.println(line); - if (line.trim().isEmpty()) { - if (curDriveLetter != null && curDiskModel != null && - curDriveLetter.equalsIgnoreCase(driveLetter)) { - return curDiskModel; - } - } - if (line.contains("DriveLetter : ")) { - curDriveLetter = line.split(" : ")[1].substring(0, 1); - System.out.println("current letter=" + curDriveLetter); - } - if (line.contains("DiskModel : ")) { - curDiskModel = line.split(" : ")[1]; - System.out.println("current model="+curDiskModel); - } - line = reader.readLine(); + int exitCode = process.waitFor(); + if (exitCode != 0) { + if (App.os.startsWith("Windows")) { + /* GH-21 windows parsing handles non NTFS partitions like + * FAT32 used for USB sticks + */ + System.out.println("exit code: " + exitCode); + } else if (App.os.contains("Mac OS")) { + throw new IOException("Command execution failed with exit code: " + exitCode); + } else if (App.os.contains("Linux")) { + throw new IOException("Command execution failed with exit code: " + exitCode); } } - catch(IOException | InterruptedException e) {} - return null; - } - - /** - * Get the drive model description based on the windows drive letter. - * Uses the powershell script disk-model.ps1 - * - * Parses output such as the following: - * - * DiskModel DriveLetter - * --------- ----------- - * ST31500341AS ATA Device D: - * Samsung SSD 850 EVO 1TB ATA Device C: - * - * Tested on Windows 10 on 3/6/2017 - * - * @param driveLetter - * @return - */ - public static String getModelFromLetter2(String driveLetter) { - try { - Process p = Runtime.getRuntime().exec("powershell -ExecutionPolicy ByPass -File disk-model.ps1"); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - while (line != null) { - System.out.println(line); - if (line.trim().endsWith(driveLetter + ":")) { - String model = line.split(driveLetter + ":")[0]; - System.out.println("model is: "+model); - return model; - } - line = reader.readLine(); - } - } catch(IOException | InterruptedException e) { - System.err.println("IO exception retrieveing disk info"); + if (App.os.startsWith("Windows")) { + // GH-22 non local support for capacity reporting + return UtilOs.getCapacityWindows(UtilOs.getDriveLetterWindows(Paths.get(diskPath))); + // Original capicity implementation w english and spanish support + //return UtilOs.parseDiskUsageInfoWindows(outputLines); + } else if (App.os.contains("Mac OS")) { + return UtilOs.parseDiskUsageInfoMacOs(outputLines); + } else if (App.os.contains("Linux")) { + return UtilOs.parseDiskUsageInfoLinux(outputLines); } - return null; - } - - /** - * On Linux OS get the device path when given a file path. - * eg. filePath = /home/james/Desktop/jDiskMarkData - * devicePath = /dev/sda - * - * @param path the file path - * @return the device path - */ - static public String getDeviceFromPath(Path path) { - try { - Process p = Runtime.getRuntime().exec("df "+path.toString()); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - String curDevice; - while (line != null) { - //System.out.println(line); - if (line.contains("/dev/")) { - curDevice = line.split(" ")[0]; - // strip the partition digit if it is numeric - if (curDevice.substring(curDevice.length()-1).matches("[0-9]{1}")) { - curDevice = curDevice.substring(0,curDevice.length()-1); - } - return curDevice; - } - line = reader.readLine(); - } - } catch(IOException | InterruptedException e) {} - return null; - } - - - /** - * On Linux OS use the lsblk command to get the disk model number for a - * specific Device ie. /dev/sda - * - * @param devicePath path of the device - * @return the disk model number - */ - static public String getDeviceModel(String devicePath) { - try { - Process p = Runtime.getRuntime().exec("lsblk "+devicePath+" --output MODEL"); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - while (line != null) { - //System.out.println(line); - if (!line.equals("MODEL") && !line.trim().isEmpty()) { - return line; - } - line = reader.readLine(); - } - } catch(IOException | InterruptedException e) {} - return null; - } - - /** - * On Linux OS use the lsblk command to get the disk size for a - * specific Device ie. /dev/sda - * - * @param devicePath path of the device - * @return the size of the device - */ - static public String getDeviceSize(String devicePath) { - try { - Process p = Runtime.getRuntime().exec("lsblk "+devicePath+" --output SIZE"); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - while (line != null) { - //System.out.println(line); - if (!line.contains("SIZE") && !line.trim().isEmpty()) { - return line; - } - line = reader.readLine(); - } - } catch(IOException | InterruptedException e) {} - return null; + return new DiskUsageInfo(); } - - static public String getDeviceFromPathOSX(Path path) { - try { - Process p = Runtime.getRuntime().exec("df "+path.toString()); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - String curDevice; - while (line != null) { - //System.out.println(line); - if (line.contains("/dev/")) { - curDevice = line.split(" ")[0]; - return curDevice; - } - line = reader.readLine(); - } - } catch(IOException | InterruptedException e) {} - return null; - } - - static public String getDeviceModelOSX(String devicePath) { - try { - String command = "diskutil info "+devicePath; - Process p = Runtime.getRuntime().exec(command); - p.waitFor(); - BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); - String line = reader.readLine(); - while (line != null) { - if (line.contains("Device / Media Name:")) { - return line.split("Device / Media Name:")[1].trim(); - } - line = reader.readLine(); + + public static String getPartitionId(Path path) { + if (System.getProperty("os.name").startsWith("Windows")) { + String driveLetter = UtilOs.getDriveLetterWindows(path); + return driveLetter; + } else { + String partitionPath = UtilOs.getPartitionFromFilePathLinux(path); + if (partitionPath.contains("/dev/")) { + return partitionPath.split("/dev/")[1]; } - } catch(IOException | InterruptedException e) {} - return null; + return partitionPath; + } } } diff --git a/src/jdiskmark/UtilOs.java b/src/jdiskmark/UtilOs.java new file mode 100644 index 0000000..380d453 --- /dev/null +++ b/src/jdiskmark/UtilOs.java @@ -0,0 +1,787 @@ + +package jdiskmark; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * OS Specific Utility methods for JDiskMark + */ +public class UtilOs { + + public static final Logger LOGGER = Logger.getLogger(UtilOs.class.getName()); + + /** The disk model power shell utility. */ + public static final String DISK_MODEL_PS_FILENAME = "disk-model.ps1"; + + /** The capacity power shell utility. */ + public static final String CAPACITY_PS_FILENAME = "capacity.ps1"; + + /* Not used kept here for reference. */ + static public void readPhysicalDriveWindows() throws FileNotFoundException, IOException { + File diskRoot = new File ("\\\\.\\PhysicalDrive0"); + RandomAccessFile diskAccess = new RandomAccessFile (diskRoot, "r"); + byte[] content = new byte[1024]; + diskAccess.readFully (content); + System.out.println("done reading fully"); + System.out.println("content " + Arrays.toString(content)); + } + + /** + * This method became obsolete with an updated version of windows 10. + * A newer version of the method is used. + * + * Get the drive model description based on the windows drive letter. + * Uses the powershell script disk-model.ps1 + * + * This appears to be the output of the original ps script before the update: + * + * d:\>powershell -ExecutionPolicy ByPass -File tmp.ps1 + + DiskSize : 128034708480 + RawSize : 117894545408 + FreeSpace : 44036825088 + Disk : \\.\PHYSICALDRIVE1 + DriveLetter : C: + DiskModel : SanDisk SD6SF1M128G + VolumeName : OS_Install + Size : 117894541312 + Partition : Disk #1, Partition #2 + + DiskSize : 320070320640 + RawSize : 320070836224 + FreeSpace : 29038071808 + Disk : \\.\PHYSICALDRIVE2 + DriveLetter : E: + DiskModel : TOSHIBA External USB 3.0 USB Device + VolumeName : TOSHIBA EXT + Size : 320070832128 + Partition : Disk #2, Partition #0 + + * We should be able to modify the new parser to detect the + * output type and adjust parsing as needed. + * + * @param driveLetter The single character drive letter. + * @return Disk Drive Model description or empty string if not found. + */ + @Deprecated + public static String getDriveModelLegacyWindows(String driveLetter) { + try { + Process p = Runtime.getRuntime().exec("powershell -ExecutionPolicy ByPass -File disk-model.ps1"); + p.waitFor(); + BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line = reader.readLine(); + + String curDriveLetter = null; + String curDiskModel = null; + while (line != null) { + System.out.println(line); + if (line.trim().isEmpty()) { + if (curDriveLetter != null && curDiskModel != null && + curDriveLetter.equalsIgnoreCase(driveLetter)) { + return curDiskModel; + } + } + if (line.contains("DriveLetter : ")) { + curDriveLetter = line.split(" : ")[1].substring(0, 1); + System.out.println("current letter=" + curDriveLetter); + } + if (line.contains("DiskModel : ")) { + curDiskModel = line.split(" : ")[1]; + System.out.println("current model=" + curDiskModel); + } + line = reader.readLine(); + } + } + catch(IOException | InterruptedException e) { + Logger.getLogger(UtilOs.class.getName()).log(Level.SEVERE, null, e); + } + return null; + } + + public static String getDriveLetterWindows(Path dataDirPath) { + // get disk info for windows + String driveLetter = dataDirPath.getRoot().toFile().toString().split(":")[0]; + if (driveLetter.length() == 1 && Character.isLetter(driveLetter.charAt(0))) { + // Only proceed if the driveLetter is a single character and a letter + return driveLetter; + } + return "unknown"; + } + + /** + * Get the drive model description based on the windows drive letter. + * Uses the powershell script disk-model.ps1 + * + * Parses output such as the following: + * + * DiskModel DriveLetter + * --------- ----------- + * ST31500341AS ATA Device D: + * Samsung SSD 850 EVO 1TB ATA Device C: + * + * Tested on Windows 10 on 3/6/2017 + * + * @param driveLetter as a string + * @return the model as a string + */ + public static String getDriveModelWindows(String driveLetter) { + File diskModelPsFile = new File(DISK_MODEL_PS_FILENAME); + if (!diskModelPsFile.exists()) { + diskModelPsFile = new File(".//app//" + DISK_MODEL_PS_FILENAME); + } + + try { + ProcessBuilder pb = new ProcessBuilder("powershell", "-ExecutionPolicy", + "ByPass", "-File", diskModelPsFile.getAbsolutePath()); + pb.redirectErrorStream(true); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + if (line.trim().endsWith(driveLetter + ":")) { + String model = line.split(driveLetter + ":")[0].trim(); + System.out.println("model is: " + model); + return model; + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "IO exception getting model", e); + } + return null; + } + + public static DiskUsageInfo getCapacityWindows(String driveLetter) { + File capacityPsFile = new File(CAPACITY_PS_FILENAME); + if (!capacityPsFile.exists()) { + capacityPsFile = new File(".//app//" + CAPACITY_PS_FILENAME); + } + + DiskUsageInfo usageInfo = new DiskUsageInfo(); + + try { + ProcessBuilder pb = new ProcessBuilder("powershell", "-ExecutionPolicy", + "ByPass", "-File", capacityPsFile.getAbsolutePath(), driveLetter); + pb.redirectErrorStream(true); + + final Process process = pb.start(); + + // Create a thread to handle the error stream + Thread errorThread = new Thread(() -> { + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String errorLine; + while ((errorLine = errorReader.readLine()) != null) { + LOGGER.log(Level.SEVERE, "PowerShell script error: {0}", errorLine); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error reading from error stream", e); + } + }); + errorThread.start(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + StringBuilder jsonBuilder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + jsonBuilder.append(line); + } + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonBuilder.toString()); + usageInfo.totalGb = rootNode.get("TotalSpaceGb").asDouble(); + usageInfo.freeGb = rootNode.get("FreeSpaceGb").asDouble(); + usageInfo.usedGb = rootNode.get("UsedSpaceGb").asDouble(); + usageInfo.calcPercentageUsed(); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "IO exception retrieving disk capacity: " + e.getLocalizedMessage(), e); + } + return usageInfo; + } + + /** + * On Linux OS get the device path when given a file path. + * eg. filePath = /home/james/Desktop/JDiskMarkData + * devicePath = /dev/sda + * + * Example command and output: + * $ df /home/james/JDiskMarkData + * Filesystem 1K-blocks Used Available Use% Mounted on + * /dev/sda2 238737052 54179492 172357524 24% / + * + * @param path the file path + * @return the device path + */ + static public String getPartitionFromFilePathLinux(Path path) { + System.out.println("filePath=" + path.toString()); + try { + ProcessBuilder pb = new ProcessBuilder("df", "-k", path.toString()); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + String curPartition; + while ((line = reader.readLine()) != null) { + System.out.println("curLine=" + line); + if (line.contains("/dev/")) { + curPartition = line.split(" ")[0]; + return curPartition; + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + return null; + } + + /** + * This method returns a list to handle multiple physical drives + * in case the partition is part of an LVM or RAID in Linux + * @param partition the partition to look up + * @return list of physical drives + */ + static public List<String> getDeviceNamesFromPartitionLinux(String partition) { + List<String> deviceNames = new ArrayList<>(); + try { + ProcessBuilder pb = new ProcessBuilder("lsblk", "-no", "pkname", partition); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + // detect multiple lines and if so indicate it is an LVM + String line; + while ((line = reader.readLine()) != null) { + System.err.println("devName=" + line); + deviceNames.add(line); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + return deviceNames; + } + + /** + * On Linux OS use the lsblk command to get the disk model number for a + * specific Device ie. /dev/sda + * + * Example output of command: + * ~$ lsblk /dev/sda --output MODEL + * MODEL + * Samsung SSD 860 EVO M.2 250GB + * + * @param devicePath path of the device + * @return the disk model number + */ + static public String getDeviceModelLinux(String devicePath) { + try { + ProcessBuilder pb = new ProcessBuilder("lsblk", devicePath, "--output", "MODEL"); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + // return the first line that does not contain the header + if (!line.equals("MODEL") && !line.trim().isEmpty()) { + return line.trim(); + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + return null; + } + + /** + * On Linux OS use the lsblk command to get the disk size for a + * specific Device ie. /dev/sda + * + * The full command is: + * + * $ lsblk /dev/sda + * NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS + * sda 8:0 0 232.9G 0 disk + * ├─sda1 8:1 0 512M 0 part /boot/efi + * └─sda2 8:2 0 232.4G 0 part /var/snap/firefox/common/host-hunspell + * + * Retrieving just the size column is: + * + * $ lsblk /dev/sda --output SIZE + * SIZE + * 232.9G + * 512M + * 232.4G + * + * @param devicePath path of the device + * @return the size of the device + */ + static public String getDeviceSizeLinux(String devicePath) { + System.out.println("getting size of " + devicePath); + try { + ProcessBuilder pb = new ProcessBuilder("lsblk", devicePath, "--output", "SIZE"); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + // return the first entry which is not the column header + while ((line = reader.readLine()) != null) { + if (!line.contains("SIZE") && !line.trim().isEmpty()) { + return line; + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + return null; + } + + static public String getDeviceFromPathMacOs(Path path) { + try { + ProcessBuilder pb = new ProcessBuilder("df", "-k", path.toString()); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + if (line.contains("/dev/")) { + return line.split(" ")[0]; + } + } + } catch(IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + return null; + } + + static public String getDeviceModelMacOs(String devicePath) { + + if (devicePath == null || devicePath.isEmpty()) { + throw new IllegalArgumentException("Invalid device path"); + } + + try { + ProcessBuilder pb = new ProcessBuilder("diskutil", "info", devicePath); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + pb.redirectErrorStream(true); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("Device / Media Name:")) { + return line.split("Device / Media Name:")[1].trim(); + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + String deviceId = devicePath; + if (deviceId.contains("/dev/")) { + deviceId = deviceId.split("/dev/")[1]; + } + + try { + ProcessBuilder pb = new ProcessBuilder("system_profiler", "SPStorageDataType"); + pb.redirectErrorStream(true); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(deviceId)) { + // Lines after deviceId + String lineAfterId; + while ((lineAfterId = reader.readLine()) != null) { + if (lineAfterId.contains("Device Name: ")) { + return lineAfterId.split("Device Name: ")[1]; + } + } + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + return "Model unavailable for " + deviceId; + } + + static public void flushDataToDriveMacOs() { + flushDataToDriveLinux(); + } + + /** + * GH-2 flush data to disk + */ + static public void flushDataToDriveLinux() { + String[] command = {"sync"}; + System.out.println("running: " + Arrays.toString(command)); + + try { + ProcessBuilder builder = new ProcessBuilder(command); + Process process = builder.start(); + int exitValue = process.waitFor(); + + try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + System.out.println("Standard Output:"); + while ((line = outputReader.readLine()) != null) { + System.out.println(line); + } + } + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + System.err.println("Standard Error:"); + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + } + + System.out.println("EXIT VALUE: " + exitValue); + + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, null, e); + } + } + + static public void dropWriteCacheMacOs() { + + String[] command = {"purge"}; + System.out.println("running: " + Arrays.toString(command)); + + try { + ProcessBuilder builder = new ProcessBuilder(command); + Process process = builder.start(); + int exitValue = process.waitFor(); + + try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + System.out.println("Standard Output:"); + while ((line = outputReader.readLine()) != null) { + System.out.println(line); + } + } + + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + System.err.println("Standard Error:"); + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + } + + System.out.println("EXIT VALUE: " + exitValue); + + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Error executing command", e); + } + } + + /** + * GH-2 Drop the write catch, used to prevent invalid read measurement + */ + static public void dropWriteCacheLinux() { + + String[] command = {"/bin/sh", "-c", "echo 1 > /proc/sys/vm/drop_caches"}; + System.out.println("running: " + Arrays.toString(command)); + + try { + ProcessBuilder builder = new ProcessBuilder(command); + Process process = builder.start(); + int exitValue = process.waitFor(); + + try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + System.out.println("Standard Output:"); + while ((line = outputReader.readLine()) != null) { + System.out.println(line); + } + } + + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + System.err.println("Standard Error:"); + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + } + + System.out.println("EXIT VALUE: " + exitValue); + + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Error executing command", e); + } + } + + public static boolean isRunningAsRootMacOs() { + return isRunningAsRootLinux(); + } + + public static boolean isRunningAsRootLinux() { + try { + ProcessBuilder pb = new ProcessBuilder("id", "-u"); + Process process = pb.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line = reader.readLine(); + if (line != null) { + int uid = Integer.parseInt(line); + return uid == 0; + } + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Error executing command", e); + return false; + } + return false; + } + + static boolean isRunningAsAdminWindows() { + try { + ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "net session"); + // Redirect output and error streams to avoid hanging if admin privileges are missing + pb.redirectOutput(ProcessBuilder.Redirect.DISCARD); + pb.redirectError(ProcessBuilder.Redirect.DISCARD); + Process process = pb.start(); + int exitCode = process.waitFor(); + return exitCode == 0; + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Error executing command", e); + return false; + } + } + + static public void emptyStandbyListWindows(File esblExe) { + + // there seem to be some testing issues with only doing the standbylist + //String[] command = { ".\\EmptyStandbyList.exe", "standbylist" }; + + //String[] command = {".\\EmptyStandbyList.exe"}; + String[] command = { esblExe.getAbsolutePath() }; + System.out.println("running: " + Arrays.toString(command)); + + try { + ProcessBuilder builder = new ProcessBuilder(command); + Process process = builder.start(); + int exitValue = process.waitFor(); + + try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + System.out.println("Standard Output:"); + while ((line = outputReader.readLine()) != null) { + System.out.println(line); + } + } + + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + String line; + System.err.println("Standard Error:"); + while ((line = errorReader.readLine()) != null) { + System.err.println(line); + } + } + System.out.println("EXIT VALUE: " + exitValue); + + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, "Error executing command", e); + } + } + + /** + * $ df -h /home/james + * Filesystem Size Used Avail Use% Mounted on + * /dev/sda2 228G 52G 165G 24% / + * + * @param outputLines + * @return usage object + */ + static DiskUsageInfo parseDiskUsageInfoLinux(List<String> outputLines) { + String usageLine = outputLines.get(1); // Assuming the relevant information is on the second line + String[] parts = usageLine.trim().split("\\s+"); + + /* Grab relevant bits from df output and convert from kilobytes to gigabytes. - JSL 2024-01-06 */ + double usedGb = Double.parseDouble(parts[2])/Math.pow(2,20); + double totalGb = Double.parseDouble(parts[1])/Math.pow(2,20); + double percentUsed = usedGb / totalGb * 100; + + return new DiskUsageInfo(percentUsed, usedGb, totalGb); + } + + /** + * $ df -h /Users/james + * Filesystem Size Used Avail Capacity iused ifree %iused Mounted on + * /dev/disk1s1 466Gi 191Gi 273Gi 42% 947563 9223372036853828244 0% / + * + * @param outputLines + * @return usage object + */ + static DiskUsageInfo parseDiskUsageInfoMacOs(List<String> outputLines) { + String usageLine = outputLines.get(1); // Assuming the relevant information is on the second line + String[] parts = usageLine.trim().split("\\s+"); + + /* Grab relevant bits from df output and convert from kilobytes to gigabytes. - JSL 2024-01-06 */ + double usedGb = Double.parseDouble(parts[2])/Math.pow(2,20); + double totalGb = Double.parseDouble(parts[1])/Math.pow(2,20); + double percentUsed = usedGb / totalGb * 100; + + return new DiskUsageInfo(percentUsed, usedGb, totalGb); + } + + /** + * This parses disk usage on windows, tested on w11. + * + * >cmd.exe /c fsutil volume diskfree c:\Users\james + * Total free bytes : 35,466,014,720 ( 33.0 GB) + * Total bytes : 511,324,794,880 (476.2 GB) + * Total quota free bytes : 35,466,014,720 ( 33.0 GB) + * Unavailable pool bytes : 0 ( 0.0 KB) + * Quota unavailable pool bytes : 0 ( 0.0 KB) + * Used bytes : 475,832,217,600 (443.2 GB) + * Total Reserved bytes : 26,562,560 ( 25.3 MB) + * Volume storage reserved bytes : 0 ( 0.0 KB) + * Available committed bytes : 0 ( 0.0 KB) + * Pool available bytes : 0 ( 0.0 KB) + * + * @param outputLines lines to parse + * @return A data structure with disk usage + */ + @Deprecated + public static DiskUsageInfo parseDiskUsageInfoWindows(List<String> outputLines) { + double freeGb = 0; + double usedGb = 0; + double totalGb = 0; + boolean usedBytesDetected = false; + for (int i = 0; i < outputLines.size(); i++) { + String line = outputLines.get(i); + if (line.contains("Total bytes") + || line.contains("Total de bytes:") // spanish + ) { + line = line.split(":")[1].trim().split("\\s+")[0]; + String bytes = line.replace(",", ""); + long totalBytes = Long.parseLong(bytes); + totalGb = (double) totalBytes / (double) (1024.0 * 1024.0 * 1024.0); + } else if (line.contains("Used bytes")) { + line = line.split(":")[1].trim().split("\\s+")[0]; + String bytes = line.replace(",", ""); + long usedBytes = Long.parseLong(bytes); + usedGb = (double) usedBytes / (double) (1024.0 * 1024.0 * 1024.0); + usedBytesDetected = true; + } else if (line.contains("Total free bytes") + || line.contains("Total de bytes:") // spanish + ) { + line = line.split(":")[1].trim().split("\\s+")[0]; + String bytes = line.replace(",", ""); + long freeBytes = Long.parseLong(bytes); + freeGb = (double) freeBytes / (double) (1024.0 * 1024.0 * 1024.0); + } + } + if (!usedBytesDetected) { + usedGb = totalGb - freeGb; + } + double percentUsed = usedGb / totalGb * 100; + System.out.println("-------------------------------------"); + System.out.println("freeGb=" + freeGb); + System.out.println("usedGb=" + usedGb); + System.out.println("totalGb=" + totalGb); + System.out.println("percentUsed=" + percentUsed); + return new DiskUsageInfo(percentUsed, freeGb, usedGb, totalGb); + } + + public static String getProcessorNameWindows() { + try { + ProcessBuilder pb = new ProcessBuilder("wmic", "cpu", "get", "Name"); + Process process = pb.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + if (!line.startsWith("Name") && !line.trim().isEmpty()) { + return line.trim(); + } + } + } + int exitCode = process.waitFor(); + if (exitCode != 0) { + // Handle error if the process didn't exit successfully + LOGGER.log(Level.SEVERE, + "Failed to get processor name. Exit code: {0}", exitCode); + } + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + return ""; // Return an empty string if no processor name was found + } + + public static String getProcessorNameMacOS() { + try { + ProcessBuilder pb = new ProcessBuilder("sysctl", "-n", "machdep.cpu.brand_string"); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + Process process = pb.start(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line = reader.readLine(); + return line.trim(); // The first line contains the processor name + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + return ""; + } + + public static String getProcessorNameLinux() { + try { + // Use lscpu command to get details about the CPU + ProcessBuilder pb = new ProcessBuilder("lscpu"); + Map<String, String> env = pb.environment(); + env.put("LC_ALL", "C"); // set language to english + Process process = pb.start(); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + // Search for the line starting with "Model name:" + if (line.startsWith("Model name:")) { + // Extract the processor name after the colon + return line.substring(line.indexOf(":") + 2).trim(); + } + } + } + + int exitCode = process.waitFor(); + if (exitCode != 0) { + // Handle error if the process didn't exit successfully + Logger.getLogger(UtilOs.class.getName()).log(Level.SEVERE, + "Failed to get processor name. Exit code: {0}", exitCode); + } + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.SEVERE, null, e); + } + + return ""; // Return an empty string if no processor name was found + } +}