From dba50744a957c241ba3a2ba2edce2797f3e09235 Mon Sep 17 00:00:00 2001 From: Dennis Sheirer Date: Sun, 12 Nov 2023 07:19:10 -0500 Subject: [PATCH] #1731 Adds application status bar with progress indicators for CPU, memory and disk space (event logs / recordings) usage. Updates user preferences to include threshold settings in the directories editor for max size for event logs and recordings. --- .../dsheirer/gui/JavaFxWindowManager.java | 24 ++ .../java/io/github/dsheirer/gui/SDRTrunk.java | 36 +-- .../directory/DirectoryPreferenceEditor.java | 55 +++- .../dsheirer/monitor/ResourceMonitor.java | 272 ++++++++++++++++++ .../io/github/dsheirer/monitor/StatusBox.java | 96 +++++++ .../github/dsheirer/monitor/StatusPanel.java | 37 +++ .../directory/DirectoryPreference.java | 66 ++++- 7 files changed, 561 insertions(+), 25 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/monitor/ResourceMonitor.java create mode 100644 src/main/java/io/github/dsheirer/monitor/StatusBox.java create mode 100644 src/main/java/io/github/dsheirer/monitor/StatusPanel.java diff --git a/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java index 22699d781..2142534c4 100644 --- a/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java +++ b/src/main/java/io/github/dsheirer/gui/JavaFxWindowManager.java @@ -41,6 +41,8 @@ import io.github.dsheirer.jmbe.JmbeEditor; import io.github.dsheirer.jmbe.JmbeEditorRequest; import io.github.dsheirer.module.log.EventLogManager; +import io.github.dsheirer.monitor.ResourceMonitor; +import io.github.dsheirer.monitor.StatusBox; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.source.tuner.manager.TunerManager; @@ -90,6 +92,7 @@ public class JavaFxWindowManager extends Application private Stage mPlaylistStage; private Stage mUserPreferencesStage; private Stage mRecordingViewerStage; + private JFXPanel mStatusPanel; /** * Constructs an instance. Note: this constructor is used for Swing applications. @@ -118,6 +121,27 @@ public JavaFxWindowManager() setup(); } + /** + * Creates or accesses the JavaFX status panel, used by the main application GUI. + * @param resourceMonitor for statistics + * @return JFXPanel accessible on Swing thread that delegates JavaFX scene creation to the FX event thread. + */ + public JFXPanel getStatusPanel(ResourceMonitor resourceMonitor) + { + if(mStatusPanel == null) + { + mStatusPanel = new JFXPanel(); + + //JFXPanel has to be populated on the FX event thread + Platform.runLater(() -> { + Scene scene = new Scene(new StatusBox(resourceMonitor)); + mStatusPanel.setScene(scene); + }); + } + + return mStatusPanel; + } + private void setup() { //Register this class to receive events via each method annotated with @Subscribe diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index a653877fe..ddb6c70d4 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -43,6 +43,7 @@ import io.github.dsheirer.log.ApplicationLog; import io.github.dsheirer.map.MapService; import io.github.dsheirer.module.log.EventLogManager; +import io.github.dsheirer.monitor.ResourceMonitor; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.properties.SystemProperties; @@ -68,7 +69,6 @@ import java.awt.Point; import java.awt.Robot; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -82,6 +82,7 @@ import java.util.Locale; import java.util.Optional; import javafx.application.Platform; +import javafx.embed.swing.JFXPanel; import javafx.scene.control.ButtonType; import jiconfont.icons.font_awesome.FontAwesome; import jiconfont.swing.IconFontSwing; @@ -128,6 +129,8 @@ public class SDRTrunk implements Listener private UserPreferences mUserPreferences = new UserPreferences(); private TunerManager mTunerManager; private ApplicationLog mApplicationLog; + private ResourceMonitor mResourceMonitor; + private JFXPanel mStatusPanel; private String mTitle; @@ -141,6 +144,8 @@ public SDRTrunk() mApplicationLog = new ApplicationLog(mUserPreferences); mApplicationLog.start(); + mResourceMonitor = new ResourceMonitor(mUserPreferences); + String operatingSystem = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); if(operatingSystem.contains("mac") || operatingSystem.contains("nux")) @@ -316,7 +321,7 @@ private void autoStartChannels() */ private void initGUI() { - mMainGui.setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]")); + mMainGui.setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill][]")); /** * Setup main JFrame window @@ -382,6 +387,10 @@ private void initGUI() mMainGui.add(mSplitPane, "cell 0 0,span,grow"); + mResourceMonitor.start(); + mStatusPanel = mJavaFxWindowManager.getStatusPanel(mResourceMonitor); + mMainGui.add(mStatusPanel, "span,growx"); + /** * Menu items */ @@ -392,14 +401,9 @@ private void initGUI() menuBar.add(fileMenu); JMenuItem exitMenu = new JMenuItem("Exit"); - exitMenu.addActionListener( - new ActionListener() - { - public void actionPerformed(ActionEvent event) - { - processShutdown(); - System.exit(0); - } + exitMenu.addActionListener(event -> { + processShutdown(); + System.exit(0); } ); @@ -571,6 +575,7 @@ private void processShutdown() mLog.info("Stopping channels ..."); mPlaylistManager.getChannelProcessingManager().shutdown(); mAudioRecordingManager.stop(); + mResourceMonitor.stop(); mLog.info("Stopping spectral display ..."); mSpectralPanel.clearTuner(); @@ -742,14 +747,9 @@ public BroadcastStatusVisibleMenuItem(ControllerPanel controllerPanel) setSelected(mBroadcastStatusPanel != null); - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - toggleBroadcastStatusPanelVisibility(); - setSelected(mBroadcastStatusVisible); - } + addActionListener(e -> { + toggleBroadcastStatusPanelVisibility(); + setSelected(mBroadcastStatusVisible); }); } } diff --git a/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java index 232533f31..3bc3ad681 100644 --- a/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java +++ b/src/main/java/io/github/dsheirer/gui/preference/directory/DirectoryPreferenceEditor.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ import io.github.dsheirer.preference.PreferenceType; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.directory.DirectoryPreference; +import java.io.File; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; @@ -31,6 +32,7 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.Separator; +import javafx.scene.control.Spinner; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; @@ -39,8 +41,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; - /** * Preference settings for channel event view @@ -97,6 +97,9 @@ public class DirectoryPreferenceEditor extends HBox private Button mResetStreamingButton; private Label mStreamingPathLabel; + private Spinner mRecordingSpinner; + private Spinner mEventLogSpinner; + public DirectoryPreferenceEditor(UserPreferences userPreferences) { mDirectoryPreference = userPreferences.getDirectoryPreference(); @@ -247,11 +250,57 @@ private GridPane getEditorPane() GridPane.setMargin(getResetStreamingButton(), new Insets(2, 0, 2, 0)); mEditorPane.add(getResetStreamingButton(), 3, row++); + + Label monitorLabel = new Label("File storage usage monitoring - maximum size thresholds (MB)"); + GridPane.setMargin(monitorLabel, new Insets(15, 0, 2, 0)); + mEditorPane.add(monitorLabel, 0, row++, 4, 1); + mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, row++, 4, 1); + + mEditorPane.add(new Label("Event Logs"), 0, row); + GridPane.setMargin(getEventLogSpinner(), new Insets(2, 2, 2, 0)); + mEditorPane.add(getEventLogSpinner(), 1, row++); + + mEditorPane.add(new Label("Recordings"), 0, row); + GridPane.setMargin(getRecordingSpinner(), new Insets(2, 2, 2, 0)); + mEditorPane.add(getRecordingSpinner(), 1, row); } return mEditorPane; } + /** + * Recording directory maximum size threshold spinner + * @return spinner + */ + private Spinner getRecordingSpinner() + { + if(mRecordingSpinner == null) + { + mRecordingSpinner = new Spinner<>(100, Integer.MAX_VALUE, mDirectoryPreference.getDirectoryMaxUsageRecordings(), 100); + mRecordingSpinner.valueProperty().addListener((observable, oldValue, newValue) -> mDirectoryPreference + .setDirectoryMaxUsageRecordings(newValue)); + } + + return mRecordingSpinner; + } + + /** + * Event log directory maximum size threshold spinner + * @return spinner + */ + private Spinner getEventLogSpinner() + { + if(mEventLogSpinner == null) + { + mEventLogSpinner = new Spinner<>(100, Integer.MAX_VALUE, mDirectoryPreference.getDirectoryMaxUsageEventLogs(), 100); + mEventLogSpinner.setEditable(true); + mEventLogSpinner.valueProperty().addListener((observable, oldValue, newValue) -> mDirectoryPreference + .setDirectoryMaxUsageEventLogs(newValue)); + } + + return mEventLogSpinner; + } + private Label getApplicationRootLabel() { if(mApplicationRootLabel == null) diff --git a/src/main/java/io/github/dsheirer/monitor/ResourceMonitor.java b/src/main/java/io/github/dsheirer/monitor/ResourceMonitor.java new file mode 100644 index 000000000..1ac8e425c --- /dev/null +++ b/src/main/java/io/github/dsheirer/monitor/ResourceMonitor.java @@ -0,0 +1,272 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.monitor; + +import io.github.dsheirer.log.LoggingSuppressor; +import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.util.ThreadPool; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.OperatingSystemMXBean; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import javafx.application.Platform; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.LongProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleLongProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides real-time monitoring of application resource usage. + * + * Monitors: + * - CPU usage + * - RAM usage + * - Drive Space - Event Logs + * - Drive Space - Recordings + */ +public class ResourceMonitor +{ + private static final Logger sLog = LoggerFactory.getLogger(ResourceMonitor.class); + private static final LoggingSuppressor sLogSuppressor = new LoggingSuppressor(sLog); + private static final int SCALOR_MEGABYTE = 1024 * 1024; + private UserPreferences mUserPreferences; + private ScheduledFuture mMemoryCpuMonitorFuture; + private ScheduledFuture mStorageMonitorFuture; + private LongProperty mMemoryTotal = new SimpleLongProperty(); + private LongProperty mMemoryAllocated = new SimpleLongProperty(); + private LongProperty mMemoryUsed = new SimpleLongProperty(); + private DoubleProperty mJavaMemoryUsedPercentage = new SimpleDoubleProperty(); + private DoubleProperty mSystemMemoryUsedPercentage = new SimpleDoubleProperty(); + private DoubleProperty mCpuPercentage = new SimpleDoubleProperty(); + private DoubleProperty mDirectoryUsePercentEventLogs = new SimpleDoubleProperty(); + private DoubleProperty mDirectoryUsePercentRecordings = new SimpleDoubleProperty(); + private StringProperty mFileSizeEventLogs = new SimpleStringProperty(); + private StringProperty mFileSizeRecordings = new SimpleStringProperty(); + private OperatingSystemMXBean mOperatingSystemMXBean; + + /** + * Constructs an instance + */ + public ResourceMonitor(UserPreferences userPreferences) + { + mUserPreferences = userPreferences; + + try + { + mOperatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + } + catch(Exception e) + { + sLog.error("Error accessing operating system MX bean to monitor CPU usage", e); + } + + mMemoryTotal.set(Runtime.getRuntime().maxMemory()); + } + + /** + * Starts resource monitoring. + */ + public void start() + { + if(mMemoryCpuMonitorFuture == null) + { + mMemoryCpuMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> updateCpuMemory(), 1, 1, TimeUnit.SECONDS); + } + + if(mStorageMonitorFuture == null) + { + mStorageMonitorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> updateDirectoryUsage(), 1,30, TimeUnit.SECONDS); + } + } + + /** + * Stops resource monitoring. + */ + public void stop() + { + if(mMemoryCpuMonitorFuture != null) + { + mMemoryCpuMonitorFuture.cancel(true); + mMemoryCpuMonitorFuture = null; + } + + if(mStorageMonitorFuture != null) + { + mStorageMonitorFuture.cancel(true); + mStorageMonitorFuture = null; + } + } + + /** + * Timer-driven method to update CPU and memory usage statistics. + */ + private void updateCpuMemory() + { + double cpuLoadScaled = 0.0; + + if(mOperatingSystemMXBean != null) + { + double load = mOperatingSystemMXBean.getSystemLoadAverage(); + cpuLoadScaled = load / mOperatingSystemMXBean.getAvailableProcessors(); + } + + final double loadFinal = cpuLoadScaled; + + Platform.runLater(() -> { + mMemoryAllocated.set(Runtime.getRuntime().totalMemory()); + mMemoryUsed.set(mMemoryAllocated.getValue() - Runtime.getRuntime().freeMemory()); + mJavaMemoryUsedPercentage.set((double)mMemoryUsed.get() / (double)mMemoryAllocated.get()); + mSystemMemoryUsedPercentage.set((double)mMemoryAllocated.get() / (double)mMemoryTotal.get()); + mCpuPercentage.set(loadFinal); + }); + } + + /** + * Timer-driven method to update directory space usage statistics + */ + private void updateDirectoryUsage() + { + long thresholdEventLog = mUserPreferences.getDirectoryPreference().getDirectoryMaxUsageEventLogs() * SCALOR_MEGABYTE; + long thresholdRecording = mUserPreferences.getDirectoryPreference().getDirectoryMaxUsageRecordings() * SCALOR_MEGABYTE; + + Path recordingPath = mUserPreferences.getDirectoryPreference().getDirectoryRecording(); + Path eventLogsPath = mUserPreferences.getDirectoryPreference().getDirectoryEventLog(); + + try + { + FileStore recordingFileStore = Files.getFileStore(recordingPath); + FileStore eventLogsFileStore = Files.getFileStore(eventLogsPath); + + long recordingAvailable = recordingFileStore.getUsableSpace(); + long eventLogsAvailable = eventLogsFileStore.getUsableSpace(); + + long eventLogUsed = FileUtils.sizeOfDirectory(eventLogsPath.toFile()); + long recordingUsed = FileUtils.sizeOfDirectory(recordingPath.toFile()); + + long eventLogMax = Math.min(thresholdEventLog, eventLogUsed + eventLogsAvailable); + long recordingMax = Math.min(thresholdRecording, recordingUsed + recordingAvailable); + + Platform.runLater(() -> { + mDirectoryUsePercentEventLogs.set((double)eventLogUsed / (double)eventLogMax); + mDirectoryUsePercentRecordings.set((double)recordingUsed / (double)recordingMax); + mFileSizeEventLogs.set(FileUtils.byteCountToDisplaySize(eventLogUsed)); + mFileSizeRecordings.set(FileUtils.byteCountToDisplaySize(recordingUsed)); + }); + } + catch(IOException ioe) + { + sLogSuppressor.error("Log Once", 1, "Unable to monitor file system - " + ioe.getMessage()); + } + } + + /** + * Directory use percentage for event logs directory. + * @return usage in range 0.0 - 1.0 (or larger if it exceeds the threshold) + */ + public DoubleProperty directoryUsePercentEventLogsProperty() + { + return mDirectoryUsePercentEventLogs; + } + + /** + * Directory use percentage for recordings directory. + * @return usage in range 0.0 - 1.0 (or larger if it exceeds the threshold) + */ + public DoubleProperty directoryUsePercentRecordingsProperty() + { + return mDirectoryUsePercentRecordings; + } + + /** + * Formatted value property for file size in event logs directory. + * @return print friendly value property + */ + public StringProperty fileSizeEventLogsProperty() + { + return mFileSizeEventLogs; + } + + /** + * Formatted value property for file size in recordings directory. + * @return print friendly value property + */ + public StringProperty fileSizeRecordingsProperty() + { + return mFileSizeRecordings; + } + + /** + * CPU usage percentage. + * @return usage in range 0.0 - 1.0 + */ + public DoubleProperty cpuPercentageProperty() + { + return mCpuPercentage; + } + + /** + * Property for total system memory + */ + public LongProperty memoryTotalProperty() + { + return mMemoryTotal; + } + + /** + * Property for memory currently allocated to the JVM + */ + public LongProperty memoryAllocatedProperty() + { + return mMemoryAllocated; + } + + /** + * Property for memory used by the JVM (out of what has been allocated) + */ + public LongProperty memoryUsedProperty() + { + return mMemoryUsed; + } + + /** + * Property for memory used vs memory allocated to the JVM. + */ + public DoubleProperty javaMemoryUsedPercentageProperty() + { + return mJavaMemoryUsedPercentage; + } + + /** + * Property for JVM allocated vs total system memory. + */ + public DoubleProperty systemMemoryUsedPercentageProperty() + { + return mSystemMemoryUsedPercentage; + } +} diff --git a/src/main/java/io/github/dsheirer/monitor/StatusBox.java b/src/main/java/io/github/dsheirer/monitor/StatusBox.java new file mode 100644 index 000000000..aec03e1ea --- /dev/null +++ b/src/main/java/io/github/dsheirer/monitor/StatusBox.java @@ -0,0 +1,96 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.monitor; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressBar; +import javafx.scene.layout.HBox; + +/** + * JavaFX status panel box. + */ +public class StatusBox extends HBox +{ + private ResourceMonitor mResourceMonitor; + + /** + * Constructs an instance. + * @param resourceMonitor for accessing resource usage statistics. + */ + public StatusBox(ResourceMonitor resourceMonitor) + { + mResourceMonitor = resourceMonitor; + setPadding(new Insets(0, 0, 2, 0)); + setSpacing(6); + Label cpuLabel = new Label("CPU:"); + cpuLabel.setPadding(new Insets(0, 0, 0, 10)); + cpuLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(cpuLabel); + + ProgressBar cpuIndicator = new ProgressBar(); + cpuIndicator.progressProperty().bind(mResourceMonitor.cpuPercentageProperty()); + getChildren().add(cpuIndicator); + + Label memoryLabel = new Label("Allocated Memory:"); + memoryLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(memoryLabel); + + ProgressBar memoryBar = new ProgressBar(); + memoryBar.progressProperty().bind(mResourceMonitor.systemMemoryUsedPercentageProperty()); + getChildren().add(memoryBar); + + Label javaMemoryLabel = new Label("Used Memory:"); + javaMemoryLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(javaMemoryLabel); + + ProgressBar javaMemoryBar = new ProgressBar(); + javaMemoryBar.progressProperty().bind(mResourceMonitor.javaMemoryUsedPercentageProperty()); + getChildren().add(javaMemoryBar); + + Label eventLogsLabel = new Label("Event Logs:"); + eventLogsLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(eventLogsLabel); + + ProgressBar eventLogsBar = new ProgressBar(); + eventLogsBar.progressProperty().bind(mResourceMonitor.directoryUsePercentEventLogsProperty()); + getChildren().add(eventLogsBar); + + Label eventLogsSizeLabel = new Label(); + eventLogsSizeLabel.textProperty().bind(mResourceMonitor.fileSizeEventLogsProperty()); + eventLogsSizeLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(eventLogsSizeLabel); + + Label recordingsLabel = new Label("Recordings:"); + recordingsLabel.setPadding(new Insets(0, 0, 0, 10)); + recordingsLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(recordingsLabel); + + ProgressBar recordingsBar = new ProgressBar(); + recordingsBar.progressProperty().bind(mResourceMonitor.directoryUsePercentRecordingsProperty()); + getChildren().add(recordingsBar); + + Label recordingsSizeLabel = new Label(); + recordingsSizeLabel.textProperty().bind(mResourceMonitor.fileSizeRecordingsProperty()); + recordingsSizeLabel.setAlignment(Pos.CENTER_RIGHT); + getChildren().add(recordingsSizeLabel); + } +} diff --git a/src/main/java/io/github/dsheirer/monitor/StatusPanel.java b/src/main/java/io/github/dsheirer/monitor/StatusPanel.java new file mode 100644 index 000000000..8cc681875 --- /dev/null +++ b/src/main/java/io/github/dsheirer/monitor/StatusPanel.java @@ -0,0 +1,37 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.monitor; + +import javafx.embed.swing.JFXPanel; + +/** + * Status panel providing resource usage statistics. + */ +public class StatusPanel extends JFXPanel +{ + private ResourceMonitor mResourceMonitor; + + public StatusPanel(ResourceMonitor resourceMonitor) + { + mResourceMonitor = resourceMonitor; + } + + +} diff --git a/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java index 3b6aca7eb..4ef868327 100644 --- a/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java +++ b/src/main/java/io/github/dsheirer/preference/directory/DirectoryPreference.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,13 +22,12 @@ import io.github.dsheirer.preference.Preference; import io.github.dsheirer.preference.PreferenceType; import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.prefs.Preferences; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * User preferences for directories @@ -38,6 +37,9 @@ public class DirectoryPreference extends Preference private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreference.class); private Preferences mPreferences = Preferences.userNodeForPackage(DirectoryPreference.class); + private static final int DEFAULT_USAGE_THRESHOLD_RECORDINGS_MB = 2000; + private static final int DEFAULT_USAGE_THRESHOLD_EVENT_LOGS_MB = 200; + private static final String DIRECTORY_APPLICATION_ROOT = "SDRTrunk"; private static final String DIRECTORY_APPLICATION_LOG = "logs"; private static final String DIRECTORY_CONFIGURATION = "configuration"; @@ -57,6 +59,8 @@ public class DirectoryPreference extends Preference private static final String PREFERENCE_KEY_DIRECTORY_RECORDING = "directory.recording"; private static final String PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE = "directory.screen.capture"; private static final String PREFERENCE_KEY_DIRECTORY_STREAMING = "directory.streaming"; + private static final String PREFERENCE_KEY_DIRECTORY_MAX_USAGE_RECORDINGS = "directory.max.usage.recordings"; + private static final String PREFERENCE_KEY_DIRECTORY_MAX_USAGE_EVENT_LOGS = "directory.max.usage.event.logs"; private Path mDirectoryApplicationRoot; private Path mDirectoryApplicationLogs; @@ -67,6 +71,8 @@ public class DirectoryPreference extends Preference private Path mDirectoryRecording; private Path mDirectoryScreenCapture; private Path mDirectoryStreaming; + private Integer mDirectoryMaxUsageRecordings; + private Integer mDirectoryMaxUsageEventLogs; /** * Constructs this preference with an update listener @@ -127,6 +133,58 @@ private void nullifyApplicationChildDirectories() mDirectoryStreaming = null; } + /** + * User-specified maximum drive space usage for the recording directory. + * @return max usage threshold in MB. + */ + public int getDirectoryMaxUsageRecordings() + { + if(mDirectoryMaxUsageRecordings == null) + { + mDirectoryMaxUsageRecordings = mPreferences.getInt(PREFERENCE_KEY_DIRECTORY_MAX_USAGE_RECORDINGS, + DEFAULT_USAGE_THRESHOLD_RECORDINGS_MB); + } + + return mDirectoryMaxUsageRecordings; + } + + /** + * Sets the threshold for maximum drive space usage for the recordings directory. + * @param thresholdMB threshold in MB + */ + public void setDirectoryMaxUsageRecordings(int thresholdMB) + { + mPreferences.putInt(PREFERENCE_KEY_DIRECTORY_MAX_USAGE_RECORDINGS, thresholdMB); + mDirectoryMaxUsageRecordings = thresholdMB; + notifyPreferenceUpdated(); + } + + /** + * User-specified maximum drive space usage for the event logs directory. + * @return max usage threshold in MB. + */ + public int getDirectoryMaxUsageEventLogs() + { + if(mDirectoryMaxUsageEventLogs == null) + { + mDirectoryMaxUsageEventLogs = mPreferences.getInt(PREFERENCE_KEY_DIRECTORY_MAX_USAGE_EVENT_LOGS, + DEFAULT_USAGE_THRESHOLD_EVENT_LOGS_MB); + } + + return mDirectoryMaxUsageEventLogs; + } + + /** + * Sets the threshold for maximum drive space usage for the event logs directory. + * @param thresholdMB threshold in MB + */ + public void setDirectoryMaxUsageEventLogs(int thresholdMB) + { + mPreferences.putInt(PREFERENCE_KEY_DIRECTORY_MAX_USAGE_EVENT_LOGS, thresholdMB); + mDirectoryMaxUsageEventLogs = thresholdMB; + notifyPreferenceUpdated(); + } + /** * Removes a stored recording directory preference so that the default path can be used again */