444 Expose Application Directories via User Preferences (#458)

* Refactors DirectoryPreference from PLaylistPreference.
Adds editor to PreferenceEditor for directories.
Organizes preference editor tree.

* Adds recording directory preference to editor and updates recording
manager to dynamically use this property for recordings.

* Adds event logs directory preference to editor and updates event log
manager to dynamically use this property for event logging.

* Adds application log directory preference.  Updates logging to use user
preference for application log directory.  Moves logging initialization
into the main application class and removes file based logging configs
from the logback.xml configuration file.

* Refactors application logging into its own class and adds prefeence
update monitoring to dynamically stop/start the logger in the new
directory location.

* Adds streaming and screen capture directories to the user preferences
editor and updates streaming and screen captures to dynamically adapt
to user changes.
This commit is contained in:
Denny Sheirer
2018-12-27 06:12:03 -05:00
committed by GitHub
parent 5b5a1431b4
commit 347a71f097
23 changed files with 1491 additions and 253 deletions

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2016 Dennis Sheirer
* Copyright (C) 2014-2018 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
@@ -14,14 +15,15 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* *****************************************************************************
*/
package io.github.dsheirer.audio.broadcast;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
import io.github.dsheirer.icon.IconManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
@@ -79,17 +81,15 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Reusa
/**
* Model for managing Broadcast configurations and any associated broadcaster instances.
*/
public BroadcastModel(AliasModel aliasModel, IconManager iconManager)
public BroadcastModel(AliasModel aliasModel, IconManager iconManager, UserPreferences userPreferences)
{
mAliasModel = aliasModel;
mIconManager = iconManager;
mStreamManager = new StreamManager(new CompletedRecordingListener(), BroadcastFormat.MP3,
SystemProperties.getInstance().getApplicationFolder(TEMPORARY_STREAM_DIRECTORY));
mStreamManager = new StreamManager(new CompletedRecordingListener(), BroadcastFormat.MP3, userPreferences);
mStreamManager.start();
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(),
15l, 15l, TimeUnit.SECONDS);
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(), 15l, 15l, TimeUnit.SECONDS);
removeOrphanedTemporaryRecordings();
}

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* Copyright (C) 2014-2018 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
@@ -14,10 +15,11 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* *****************************************************************************
*/
package io.github.dsheirer.audio.broadcast;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.AudioRecorder;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableAudioPacket;
@@ -26,7 +28,6 @@ import io.github.dsheirer.util.TimeStamp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
@@ -46,7 +47,7 @@ public class StreamManager implements Listener<ReusableAudioPacket>
private Listener<AudioRecording> mAudioRecordingListener;
private BroadcastFormat mBroadcastFormat;
private Path mTempDirectory;
private UserPreferences mUserPreferences;
private Map<Integer,AudioRecorder> mStreamRecorders = new HashMap<>();
private Runnable mRecorderMonitor;
private ScheduledFuture<?> mRecorderMonitorFuture;
@@ -61,15 +62,14 @@ public class StreamManager implements Listener<ReusableAudioPacket>
* Completed streamable audio recordings are nominated to the output listener (for broadcast) upon completion
*
* @param listener to receive completed audio recordings
* @param tempDirectory where to store temporary audio recordings
* @param broadcastFormat for the stream
* @param userPreferences to obtain the temporary streaming recording directory
*/
public StreamManager(Listener<AudioRecording> listener, BroadcastFormat broadcastFormat, Path tempDirectory)
public StreamManager(Listener<AudioRecording> listener, BroadcastFormat broadcastFormat, UserPreferences userPreferences)
{
assert (tempDirectory != null && Files.isDirectory(tempDirectory));
mAudioRecordingListener = listener;
mBroadcastFormat = broadcastFormat;
mTempDirectory = tempDirectory;
mUserPreferences = userPreferences;
}
/**
@@ -204,7 +204,7 @@ public class StreamManager implements Listener<ReusableAudioPacket>
sb.append(TimeStamp.getLongTimeStamp("_"));
sb.append(mBroadcastFormat.getFileExtension());
Path temporaryRecordingPath = mTempDirectory.resolve(sb.toString());
Path temporaryRecordingPath = mUserPreferences.getDirectoryPreference().getDirectoryStreaming().resolve(sb.toString());
return temporaryRecordingPath;
}

View File

@@ -36,6 +36,7 @@ import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.gui.preference.PreferenceEditorViewRequest;
import io.github.dsheirer.icon.IconManager;
import io.github.dsheirer.log.ApplicationLog;
import io.github.dsheirer.map.MapService;
import io.github.dsheirer.module.log.EventLogManager;
import io.github.dsheirer.playlist.PlaylistManager;
@@ -56,7 +57,6 @@ import io.github.dsheirer.spectrum.SpectralDisplayPanel;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import net.miginfocom.swing.MigLayout;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -89,6 +89,7 @@ import java.util.List;
public class SDRTrunk implements Listener<TunerEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class);
private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible";
private boolean mBroadcastStatusVisible;
@@ -106,21 +107,14 @@ public class SDRTrunk implements Listener<TunerEvent>
private JideSplitPane mSplitPane;
private JavaFxWindowManager mJavaFxWindowManager;
private UserPreferences mUserPreferences = new UserPreferences();
private ApplicationLog mApplicationLog;
private String mTitle;
public SDRTrunk()
{
mLog.info("*******************************************************************");
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
mLog.info("*******************************************************************");
mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]");
mLog.info("Host OS Name: " + System.getProperty("os.name"));
mLog.info("Host OS Arch: " + System.getProperty("os.arch"));
mLog.info("Host OS Version: " + System.getProperty("os.version"));
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory()));
mApplicationLog = new ApplicationLog(mUserPreferences);
mApplicationLog.start();
//Setup the application home directory
Path home = getHomePath();
@@ -151,9 +145,9 @@ public class SDRTrunk implements Listener<TunerEvent>
ChannelMapModel channelMapModel = new ChannelMapModel();
EventLogManager eventLogManager = new EventLogManager();
EventLogManager eventLogManager = new EventLogManager(mUserPreferences);
RecorderManager recorderManager = new RecorderManager(aliasModel);
RecorderManager recorderManager = new RecorderManager(aliasModel, mUserPreferences);
mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences);
@@ -170,7 +164,7 @@ public class SDRTrunk implements Listener<TunerEvent>
AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(mSourceManager.getMixerManager());
mBroadcastModel = new BroadcastModel(aliasModel, mIconManager);
mBroadcastModel = new BroadcastModel(aliasModel, mIconManager, mUserPreferences);
//Audio packets are routed through the audio packet manager for metadata enrichment and then
//distributed to the audio packet processors (ie playback, recording, streaming, etc.)
@@ -373,47 +367,20 @@ public class SDRTrunk implements Listener<TunerEvent>
{
Robot robot = new Robot();
final BufferedImage image =
robot.createScreenCapture(mMainGui.getBounds());
final BufferedImage image = robot.createScreenCapture(mMainGui.getBounds());
SystemProperties props = SystemProperties.getInstance();
String filename = TimeStamp.getTimeStamp("_") + "_screen_capture.png";
Path capturePath = props.getApplicationFolder("screen_captures");
final Path captureFile = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().resolve(filename);
if(!Files.exists(capturePath))
{
EventQueue.invokeLater(() -> {
try
{
Files.createDirectory(capturePath);
ImageIO.write(image, "png", captureFile.toFile());
}
catch(IOException e)
{
mLog.error("Couldn't create 'screen_captures' "
+ "subdirectory in the " +
"SDRTrunk application directory", e);
}
}
String filename = TimeStamp.getTimeStamp("_") +
"_screen_capture.png";
final Path captureFile = capturePath.resolve(filename);
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
try
{
ImageIO.write(image, "png",
captureFile.toFile());
}
catch(IOException e)
{
mLog.error("Couldn't write screen capture to "
+ "file [" + captureFile.toString() + "]", e);
}
mLog.error("Couldn't write screen capture to file [" + captureFile.toString() + "]", e);
}
});
}
@@ -442,6 +409,7 @@ public class SDRTrunk implements Listener<TunerEvent>
mLog.info("Releasing tuners ...");
mSourceManager.shutdown();
mLog.info("Shutdown complete.");
mApplicationLog.stop();
}
/**

View File

@@ -20,6 +20,7 @@
package io.github.dsheirer.gui.preference;
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
import io.github.dsheirer.preference.UserPreferences;
import javafx.scene.Node;
@@ -33,10 +34,12 @@ public class PreferenceEditorFactory
{
switch(preferenceEditorType)
{
case TALKGROUP_FORMAT:
return new TalkgroupFormatPreferenceEditor(userPreferences);
case CHANNEL_EVENT:
return new DecodeEventViewPreferenceEditor(userPreferences);
case DIRECTORY:
return new DirectoryPreferenceEditor(userPreferences);
case TALKGROUP_FORMAT:
return new TalkgroupFormatPreferenceEditor(userPreferences);
case TUNER:
return new TunerPreferenceEditor(userPreferences);
}

View File

@@ -25,9 +25,10 @@ package io.github.dsheirer.gui.preference;
*/
public enum PreferenceEditorType
{
CHANNEL_EVENT("Channel Event"),
TALKGROUP_FORMAT("Talkgroup Display"),
TUNER("Tuner");
CHANNEL_EVENT("Channel Events"),
DIRECTORY("Directories"),
TALKGROUP_FORMAT("Talkgroups"),
TUNER("Tuners");
private String mLabel;

View File

@@ -104,7 +104,7 @@ public class PreferencesEditor extends Application
public void start(Stage stage) throws Exception
{
stage.setTitle("Preferences");
Scene scene = new Scene(getBorderPane(), 800, 500);
Scene scene = new Scene(getBorderPane(), 900, 500);
stage.setScene(scene);
stage.show();
}
@@ -192,10 +192,19 @@ public class PreferencesEditor extends Application
TreeItem<String> displayItem = new TreeItem<>("Display");
displayItem.getChildren().add(new TreeItem(PreferenceEditorType.CHANNEL_EVENT));
displayItem.getChildren().add(new TreeItem(PreferenceEditorType.TALKGROUP_FORMAT));
displayItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER));
treeRoot.getChildren().add(displayItem);
displayItem.setExpanded(true);
TreeItem<String> storageItem = new TreeItem<>("File Storage");
storageItem.getChildren().add(new TreeItem(PreferenceEditorType.DIRECTORY));
treeRoot.getChildren().add(storageItem);
storageItem.setExpanded(true);
TreeItem<String> sourceItem = new TreeItem<>("Source");
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER));
treeRoot.getChildren().add(sourceItem);
sourceItem.setExpanded(true);
mEditorSelectionTreeView = new TreeView();
mEditorSelectionTreeView.setShowRoot(false);
mEditorSelectionTreeView.setRoot(treeRoot);

View File

@@ -0,0 +1,684 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.gui.preference.directory;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.directory.DirectoryPreference;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
/**
* Preference settings for channel event view
*/
public class DirectoryPreferenceEditor extends HBox
{
private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreferenceEditor.class);
private DirectoryPreference mDirectoryPreference;
private GridPane mEditorPane;
private Label mApplicationRootLabel;
private Button mChangeApplicationRootButton;
private Button mResetApplicationRootButton;
private Label mApplicationRootPathLabel;
private Label mApplicationLogsLabel;
private Button mChangeApplicationLogsButton;
private Button mResetApplicationLogsButton;
private Label mApplicationLogsPathLabel;
private Label mEventLogsLabel;
private Button mChangeEventLogsButton;
private Button mResetEventLogsButton;
private Label mEventLogsPathLabel;
private Label mPlaylistLabel;
private Button mChangePlaylistButton;
private Button mResetPlaylistButton;
private Label mPlaylistPathLabel;
private Label mRecordingLabel;
private Button mChangeRecordingButton;
private Button mResetRecordingButton;
private Label mRecordingPathLabel;
private Label mScreenCaptureLabel;
private Button mChangeScreenCaptureButton;
private Button mResetScreenCaptureButton;
private Label mScreenCapturePathLabel;
private Label mStreamingLabel;
private Button mChangeStreamingButton;
private Button mResetStreamingButton;
private Label mStreamingPathLabel;
public DirectoryPreferenceEditor(UserPreferences userPreferences)
{
mDirectoryPreference = userPreferences.getDirectoryPreference();
//Register to receive directory preference update notifications so we can update the path labels
MyEventBus.getEventBus().register(this);
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
private GridPane getEditorPane()
{
if(mEditorPane == null)
{
mEditorPane = new GridPane();
mEditorPane.setPadding(new Insets(10, 10, 10, 10));
int row = 0;
Label itemLabel = new Label("Item");
GridPane.setMargin(itemLabel, new Insets(0, 10, 0, 0));
mEditorPane.add(itemLabel, 0, row);
Label directoryLabel = new Label("Directory");
GridPane.setMargin(directoryLabel, new Insets(0, 10, 0, 0));
mEditorPane.add(directoryLabel, 1, row++);
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, row++, 4, 1);
GridPane.setMargin(getApplicationRootLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getApplicationRootLabel(), 0, row);
GridPane.setMargin(getApplicationRootPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getApplicationRootPathLabel(), 1, row);
GridPane.setMargin(getChangeApplicationRootButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeApplicationRootButton(), 2, row);
GridPane.setMargin(getResetApplicationRootButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetApplicationRootButton(), 3, row++);
GridPane.setMargin(getApplicationLogsLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getApplicationLogsLabel(), 0, row);
GridPane.setMargin(getApplicationLogsPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getApplicationLogsPathLabel(), 1, row);
GridPane.setMargin(getChangeApplicationLogsButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeApplicationLogsButton(), 2, row);
GridPane.setMargin(getResetApplicationLogsButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetApplicationLogsButton(), 3, row++);
GridPane.setMargin(getEventLogsLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getEventLogsLabel(), 0, row);
GridPane.setMargin(getEventLogsPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getEventLogsPathLabel(), 1, row);
GridPane.setMargin(getChangeEventLogsButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeEventLogsButton(), 2, row);
GridPane.setMargin(getResetEventLogsButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetEventLogsButton(), 3, row++);
GridPane.setMargin(getPlaylistLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getPlaylistLabel(), 0, row);
GridPane.setMargin(getPlaylistPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getPlaylistPathLabel(), 1, row);
GridPane.setMargin(getChangePlaylistButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangePlaylistButton(), 2, row);
GridPane.setMargin(getResetPlaylistButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetPlaylistButton(), 3, row++);
GridPane.setMargin(getRecordingLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getRecordingLabel(), 0, row);
GridPane.setMargin(getRecordingPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getRecordingPathLabel(), 1, row);
GridPane.setMargin(getChangeRecordingButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeRecordingButton(), 2, row);
GridPane.setMargin(getResetRecordingButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetRecordingButton(), 3, row++);
GridPane.setMargin(getScreenCaptureLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getScreenCaptureLabel(), 0, row);
GridPane.setMargin(getScreenCapturePathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getScreenCapturePathLabel(), 1, row);
GridPane.setMargin(getChangeScreenCaptureButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeScreenCaptureButton(), 2, row);
GridPane.setMargin(getResetScreenCaptureButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetScreenCaptureButton(), 3, row++);
GridPane.setMargin(getStreamingLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getStreamingLabel(), 0, row);
GridPane.setMargin(getStreamingPathLabel(), new Insets(0, 10, 0, 0));
mEditorPane.add(getStreamingPathLabel(), 1, row);
GridPane.setMargin(getChangeStreamingButton(), new Insets(2, 10, 2, 0));
mEditorPane.add(getChangeStreamingButton(), 2, row);
GridPane.setMargin(getResetStreamingButton(), new Insets(2, 0, 2, 0));
mEditorPane.add(getResetStreamingButton(), 3, row++);
}
return mEditorPane;
}
private Label getApplicationRootLabel()
{
if(mApplicationRootLabel == null)
{
mApplicationRootLabel = new Label("Application Root");
}
return mApplicationRootLabel;
}
private Button getChangeApplicationRootButton()
{
if(mChangeApplicationRootButton == null)
{
mChangeApplicationRootButton = new Button("Change...");
mChangeApplicationRootButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Application Root Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryApplicationRoot().toFile());
Stage stage = (Stage)getChangeApplicationRootButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryApplicationRoot(selected.toPath());
}
}
});
}
return mChangeApplicationRootButton;
}
private Button getResetApplicationRootButton()
{
if(mResetApplicationRootButton == null)
{
mResetApplicationRootButton = new Button("Reset");
mResetApplicationRootButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryApplicationRoot();
}
});
}
return mResetApplicationRootButton;
}
private Label getApplicationRootPathLabel()
{
if(mApplicationRootPathLabel == null)
{
mApplicationRootPathLabel = new Label(mDirectoryPreference.getDirectoryApplicationRoot().toString());
}
return mApplicationRootPathLabel;
}
private Label getApplicationLogsLabel()
{
if(mApplicationLogsLabel == null)
{
mApplicationLogsLabel = new Label("Application Logs");
}
return mApplicationLogsLabel;
}
private Button getChangeApplicationLogsButton()
{
if(mChangeApplicationLogsButton == null)
{
mChangeApplicationLogsButton = new Button("Change...");
mChangeApplicationLogsButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Application Logs Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryApplicationLog().toFile());
Stage stage = (Stage)getChangeApplicationLogsButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryApplicationLogs(selected.toPath());
}
}
});
}
return mChangeApplicationLogsButton;
}
private Button getResetApplicationLogsButton()
{
if(mResetApplicationLogsButton == null)
{
mResetApplicationLogsButton = new Button("Reset");
mResetApplicationLogsButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryApplicationLogs();
}
});
}
return mResetApplicationLogsButton;
}
private Label getApplicationLogsPathLabel()
{
if(mApplicationLogsPathLabel == null)
{
mApplicationLogsPathLabel = new Label(mDirectoryPreference.getDirectoryApplicationLog().toString());
}
return mApplicationLogsPathLabel;
}
private Label getEventLogsLabel()
{
if(mEventLogsLabel == null)
{
mEventLogsLabel = new Label("Event Logs");
}
return mEventLogsLabel;
}
private Button getChangeEventLogsButton()
{
if(mChangeEventLogsButton == null)
{
mChangeEventLogsButton = new Button("Change...");
mChangeEventLogsButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Event Logs Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryEventLog().toFile());
Stage stage = (Stage)getChangeEventLogsButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryEventLogs(selected.toPath());
}
}
});
}
return mChangeEventLogsButton;
}
private Button getResetEventLogsButton()
{
if(mResetEventLogsButton == null)
{
mResetEventLogsButton = new Button("Reset");
mResetEventLogsButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryEventLogs();
}
});
}
return mResetEventLogsButton;
}
private Label getEventLogsPathLabel()
{
if(mEventLogsPathLabel == null)
{
mEventLogsPathLabel = new Label(mDirectoryPreference.getDirectoryEventLog().toString());
}
return mEventLogsPathLabel;
}
private Label getPlaylistLabel()
{
if(mPlaylistLabel == null)
{
mPlaylistLabel = new Label("Playlists");
}
return mPlaylistLabel;
}
private Button getChangePlaylistButton()
{
if(mChangePlaylistButton == null)
{
mChangePlaylistButton = new Button("Change...");
mChangePlaylistButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Playlist Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryPlaylist().toFile());
Stage stage = (Stage)getChangePlaylistButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryPlaylist(selected.toPath());
}
}
});
}
return mChangePlaylistButton;
}
private Button getResetPlaylistButton()
{
if(mResetPlaylistButton == null)
{
mResetPlaylistButton = new Button("Reset");
mResetPlaylistButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryPlaylist();
}
});
}
return mResetPlaylistButton;
}
private Label getPlaylistPathLabel()
{
if(mPlaylistPathLabel == null)
{
mPlaylistPathLabel = new Label(mDirectoryPreference.getDirectoryPlaylist().toString());
}
return mPlaylistPathLabel;
}
private Label getRecordingLabel()
{
if(mRecordingLabel == null)
{
mRecordingLabel = new Label("Recordings");
}
return mRecordingLabel;
}
private Button getChangeRecordingButton()
{
if(mChangeRecordingButton == null)
{
mChangeRecordingButton = new Button("Change...");
mChangeRecordingButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Recording Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryRecording().toFile());
Stage stage = (Stage)getChangeRecordingButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryRecording(selected.toPath());
}
}
});
}
return mChangeRecordingButton;
}
private Button getResetRecordingButton()
{
if(mResetRecordingButton == null)
{
mResetRecordingButton = new Button("Reset");
mResetRecordingButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryRecording();
}
});
}
return mResetRecordingButton;
}
private Label getRecordingPathLabel()
{
if(mRecordingPathLabel == null)
{
mRecordingPathLabel = new Label(mDirectoryPreference.getDirectoryRecording().toString());
}
return mRecordingPathLabel;
}
private Label getScreenCaptureLabel()
{
if(mScreenCaptureLabel == null)
{
mScreenCaptureLabel = new Label("Screen Captures");
}
return mScreenCaptureLabel;
}
private Button getChangeScreenCaptureButton()
{
if(mChangeScreenCaptureButton == null)
{
mChangeScreenCaptureButton = new Button("Change...");
mChangeScreenCaptureButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Screen Capture Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryScreenCapture().toFile());
Stage stage = (Stage)getChangeScreenCaptureButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryScreenCapture(selected.toPath());
}
}
});
}
return mChangeScreenCaptureButton;
}
private Button getResetScreenCaptureButton()
{
if(mResetScreenCaptureButton == null)
{
mResetScreenCaptureButton = new Button("Reset");
mResetScreenCaptureButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryScreenCapture();
}
});
}
return mResetScreenCaptureButton;
}
private Label getScreenCapturePathLabel()
{
if(mScreenCapturePathLabel == null)
{
mScreenCapturePathLabel = new Label(mDirectoryPreference.getDirectoryScreenCapture().toString());
}
return mScreenCapturePathLabel;
}
private Label getStreamingLabel()
{
if(mStreamingLabel == null)
{
mStreamingLabel = new Label("Streaming");
}
return mStreamingLabel;
}
private Button getChangeStreamingButton()
{
if(mChangeStreamingButton == null)
{
mChangeStreamingButton = new Button("Change...");
mChangeStreamingButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select Streaming Folder");
directoryChooser.setInitialDirectory(mDirectoryPreference.getDirectoryStreaming().toFile());
Stage stage = (Stage)getChangeStreamingButton().getScene().getWindow();
File selected = directoryChooser.showDialog(stage);
if(selected != null)
{
mDirectoryPreference.setDirectoryStreaming(selected.toPath());
}
}
});
}
return mChangeStreamingButton;
}
private Button getResetStreamingButton()
{
if(mResetStreamingButton == null)
{
mResetStreamingButton = new Button("Reset");
mResetStreamingButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
mDirectoryPreference.resetDirectoryStreaming();
}
});
}
return mResetStreamingButton;
}
private Label getStreamingPathLabel()
{
if(mStreamingPathLabel == null)
{
mStreamingPathLabel = new Label(mDirectoryPreference.getDirectoryStreaming().toString());
}
return mStreamingPathLabel;
}
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType != null && preferenceType == PreferenceType.DIRECTORY)
{
getApplicationRootPathLabel().setText(mDirectoryPreference.getDirectoryApplicationRoot().toString());
getApplicationLogsPathLabel().setText(mDirectoryPreference.getDirectoryApplicationLog().toString());
getEventLogsPathLabel().setText(mDirectoryPreference.getDirectoryEventLog().toString());
getPlaylistPathLabel().setText(mDirectoryPreference.getDirectoryPlaylist().toString());
getRecordingPathLabel().setText(mDirectoryPreference.getDirectoryRecording().toString());
getScreenCapturePathLabel().setText(mDirectoryPreference.getDirectoryScreenCapture().toString());
getStreamingPathLabel().setText(mDirectoryPreference.getDirectoryStreaming().toString());
}
}
}

View File

@@ -68,6 +68,7 @@ public class TunerPreferenceEditor extends HBox
GridPane.setMargin(getChannelizerLabel(), new Insets(0, 10, 0, 0));
GridPane.setHalignment(getChannelizerLabel(), HPos.LEFT);
mEditorPane.add(getChannelizerLabel(), 0, 0);
GridPane.setMargin(getChannelizerTypeChoiceBox(), new Insets(2, 0, 2, 0));
mEditorPane.add(getChannelizerTypeChoiceBox(), 1, 0);
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, 1, 2, 1);
mEditorPane.add(getPolyphaseLabel(), 0, 2, 2, 1);

View File

@@ -0,0 +1,166 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.log;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
/**
* Logback and SLF4j logging implementation.
*/
public class ApplicationLog
{
private final static Logger mLog = LoggerFactory.getLogger(ApplicationLog.class);
private static final String APPLICATION_LOG_FILENAME = "sdrtrunk_app.log";
private static final int APPLICATION_LOG_MAX_HISTORY = 10;
private UserPreferences mUserPreferences;
private RollingFileAppender mRollingFileAppender;
private Path mApplicationLogPath;
/**
* Constructs the application log instance. Note: use the start() method to initiate logging.
* @param userPreferences
*/
public ApplicationLog(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
MyEventBus.getEventBus().register(this);
}
@Subscribe
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.DIRECTORY && mRollingFileAppender != null && mApplicationLogPath != null)
{
Path applicationLogPath = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog();
//Restart the application log if the path is updated
if(!applicationLogPath.equals(mApplicationLogPath))
{
mLog.info("Application logging directory has changed [" + applicationLogPath.toString() + " ] - restarting logging");
stop();
start();
}
}
}
/**
* Starts application logging
*/
public void start()
{
if(mRollingFileAppender == null)
{
mApplicationLogPath = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog();
Path logfile = mApplicationLogPath.resolve(APPLICATION_LOG_FILENAME);
mLog.info("Application Log File: " + logfile.toString());
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setContext(loggerContext);
encoder.setPattern("%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n");
encoder.start();
mRollingFileAppender = new RollingFileAppender();
mRollingFileAppender.setContext(loggerContext);
mRollingFileAppender.setAppend(true);
mRollingFileAppender.setName("FILE");
mRollingFileAppender.setEncoder(encoder);
mRollingFileAppender.setFile(logfile.toString());
ThresholdFilter thresholdFilter = new ThresholdFilter();
thresholdFilter.setLevel(Level.DEBUG.toString());
thresholdFilter.setContext(loggerContext);
thresholdFilter.setName("sdrtrunk threshold filter");
thresholdFilter.start();
mRollingFileAppender.addFilter(thresholdFilter);
String pattern = logfile.toString().replace(APPLICATION_LOG_FILENAME, "%d{yyyyMMdd}_" + APPLICATION_LOG_FILENAME);
TimeBasedRollingPolicy rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(loggerContext);
rollingPolicy.setFileNamePattern(pattern);
rollingPolicy.setMaxHistory(APPLICATION_LOG_MAX_HISTORY);
rollingPolicy.setParent(mRollingFileAppender);
rollingPolicy.start();
mRollingFileAppender.setRollingPolicy(rollingPolicy);
mRollingFileAppender.start();
Logger logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
((ch.qos.logback.classic.Logger)logger).setLevel(Level.ALL);
((ch.qos.logback.classic.Logger)logger).addAppender(mRollingFileAppender);
// StatusPrinter.print(loggerContext);
mLog.info("");
mLog.info("*******************************************************************");
mLog.info("**** sdrtrunk: a trunked radio and digital decoding application ***");
mLog.info("**** website: https://github.com/dsheirer/sdrtrunk ***");
mLog.info("*******************************************************************");
mLog.info("Memory Logging Format: [Used/Allocated PercentUsed%]");
mLog.info("Host OS Name: " + System.getProperty("os.name"));
mLog.info("Host OS Arch: " + System.getProperty("os.arch"));
mLog.info("Host OS Version: " + System.getProperty("os.version"));
mLog.info("Host CPU Cores: " + Runtime.getRuntime().availableProcessors());
mLog.info("Host Max Java Memory: " + FileUtils.byteCountToDisplaySize(Runtime.getRuntime().maxMemory()));
mLog.info("Storage Directories:");
mLog.info(" Application Root: " + mUserPreferences.getDirectoryPreference().getDirectoryApplicationRoot());
mLog.info(" Application Log: " + mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog());
mLog.info(" Event Log: " + mUserPreferences.getDirectoryPreference().getDirectoryEventLog());
mLog.info(" Playlist: " + mUserPreferences.getDirectoryPreference().getDirectoryPlaylist());
mLog.info(" Recordings: " + mUserPreferences.getDirectoryPreference().getDirectoryRecording());
}
}
/**
* Stops the log file appender and nullifies it for shutdown or for reinitialization.
*/
public void stop()
{
if(mRollingFileAppender != null)
{
mLog.info("Stopping application logging");
mRollingFileAppender.stop();
LoggerContext loggerContext = (LoggerContext)LoggerFactory.getILoggerFactory();
Logger logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
((ch.qos.logback.classic.Logger)logger).detachAppender(mRollingFileAppender);
mRollingFileAppender = null;
}
}
}

View File

@@ -26,6 +26,7 @@ import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.channel.IChannelDescriptor;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.icon.IconManager;
import io.github.dsheirer.identifier.Form;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
@@ -202,7 +203,15 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
sb.append(",");
}
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
if(identifier.getForm() == Form.TALKGROUP)
{
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
}
else
{
sb.append(identifier);
}
}
return sb.toString();

View File

@@ -1039,7 +1039,8 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
ic.update(from);
ic.update(to);
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
@@ -1054,7 +1055,8 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen
{
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
ic.update(from);
ic.update(to);
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
.channel(getCurrentChannel())
@@ -2144,9 +2146,12 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen
.identifiers(icRoaming)
.build());
break;
case MOTOROLA_OSP_OPCODE_7:
mLog.info("MOTOROLA OPCODE 7: " + tsbk.getMessage().toHexString());
break;
default:
mLog.debug("Unrecognized TSBK Opcode: " + tsbk.getOpcode().name() + " VENDOR:" + tsbk.getVendor() +
" OPCODE:" + tsbk.getOpcodeNumber());
// mLog.debug("Unrecognized TSBK Opcode: " + tsbk.getOpcode().name() + " VENDOR:" + tsbk.getVendor() +
// " OPCODE:" + tsbk.getOpcodeNumber());
break;
}
}
@@ -2379,8 +2384,8 @@ public class P25DecoderState extends DecoderState implements IChannelEventListen
.build());
break;
default:
mLog.debug("Unrecognized LCW Opcode: " + lcw.getOpcode().name() + " VENDOR:" + lcw.getVendor() +
" OPCODE:" + lcw.getOpcodeNumber());
// mLog.debug("Unrecognized LCW Opcode: " + lcw.getOpcode().name() + " VENDOR:" + lcw.getVendor() +
// " OPCODE:" + lcw.getOpcodeNumber());
break;
}
}

View File

@@ -181,7 +181,7 @@ public class P25MessageFramer2 implements Listener<Dibit>, IDataUnitDetectListen
}
catch(BitSetFullException bsfe)
{
mLog.debug("Message full exception - unexpected");
// mLog.debug("Message full exception - unexpected");
//Reset so that we can start over again
reset(0);
@@ -280,9 +280,9 @@ public class P25MessageFramer2 implements Listener<Dibit>, IDataUnitDetectListen
mTrailingDibitsToSuppress = 22;
break;
default:
mLog.debug("*** MORE THAN 5 PDU BLOCKS DETECTED [" +
mPDUSequence.getHeader().getBlocksToFollowCount() +
"] - DETERMINE TRAILING NULL COUNT TO SUPPRESS AND UPDATE CODE");
// mLog.debug("*** MORE THAN 5 PDU BLOCKS DETECTED [" +
// mPDUSequence.getHeader().getBlocksToFollowCount() +
// "] - DETERMINE TRAILING NULL COUNT TO SUPPRESS AND UPDATE CODE");
break;
}
@@ -298,7 +298,7 @@ public class P25MessageFramer2 implements Listener<Dibit>, IDataUnitDetectListen
}
else
{
mLog.error("Received PDU data block with out a preceeding data header");
// mLog.error("Received PDU data block with out a preceeding data header");
reset(mDataUnitID.getMessageLength());
}
break;
@@ -489,39 +489,6 @@ public class P25MessageFramer2 implements Listener<Dibit>, IDataUnitDetectListen
}
});
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_040600_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180922_042033_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045614_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //Has Moto LC Opcode 01
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_045647_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050325_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050403_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_050721_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051057_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits"); //ARS PDUs and SNDCP PDUs
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_051808_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_EMERG_AND_PACKETS.bits"); //ARS and SNCDP PDUs
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20180923_052519_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091511_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181008_091650_9600BPS_CNYICC_Onondaga Simulcast_LCN 15 Control.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101745_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101850_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_101855_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102145_9600BPS_CNYICC_Onondaga Simulcast_LCN 04.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102451_9600BPS_CNYICC_Onondaga Simulcast_LCN 09_lots_of_PACKET_DATA.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102456_9600BPS_CNYICC_Onondaga Simulcast_LCN 10.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102459_9600BPS_CNYICC_Onondaga Simulcast_LCN 11.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_102501_9600BPS_CNYICC_Onondaga Simulcast_LCN 12.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181021_103208_9600BPS_CNYICC_Onondaga Simulcast_LCN 09.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140834_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_140851_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141547_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_141607_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142300_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_142319_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143012_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143031_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143723_9600BPS_CNYICC_Onondaga Simulcast_LCN 06.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181028_143741_9600BPS_CNYICC_Onondaga Simulcast_LCN 07.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_100905_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_101622_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181102_102339_9600BPS_CNYICC_Onondaga Simulcast_LCN 08.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_134948_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits");
// Path path = Paths.get("/home/denny/SDRTrunk/recordings/20181103_144312_9600BPS_CNYICC_Oswego Simulcast_LCN 04.bits"); //Interesting UDP port 231 packets (oswego LCN 4)

View File

@@ -286,6 +286,7 @@ public class P25TrafficChannelManager extends Module implements IDecodeEventProv
break;
case OSP_SNDCP_DATA_CHANNEL_GRANT:
case OSP_GROUP_DATA_CHANNEL_GRANT:
case OSP_INDIVIDUAL_DATA_CHANNEL_GRANT:
type = encrypted ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL;
break;

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* Copyright (C) 2014-2018 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
@@ -14,8 +15,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
* *****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.message;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
@@ -88,7 +89,7 @@ public abstract class P25Message extends Message
/**
* The transmitted binary message
*/
protected CorrectedBinaryMessage getMessage()
public CorrectedBinaryMessage getMessage()
{
return mMessage;
}

View File

@@ -1,3 +1,23 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.message.tsbk;
import io.github.dsheirer.module.decode.p25.reference.Direction;
@@ -150,6 +170,7 @@ public enum Opcode
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT(2, "PTCH GRP VCHN GR", "MOTOROLA PATCH GROUP CHANNEL GRANT"),
MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE(3, "PTCH GRP VCH UPD", "MOTOROLA PATCH GROUP CHANNEL GRANT UPDATE"),
MOTOROLA_OSP_TRAFFIC_CHANNEL_ID(5, "CHANNEL CWID", "CHANNEL CWID"),
MOTOROLA_OSP_OPCODE_7(7, "MOTOROLA OPCODE 7", "MOTOROLA OPCODE 07"),
MOTOROLA_OSP_SYSTEM_LOADING(9, "SYSTEM LOADING", "SYSTEM LOADING"),
MOTOROLA_OSP_CONTROL_CHANNEL_ID(11, "CCH BASE STAT ID", "CONTROL CHANNEL BASE STATION ID"),
MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN(14, "CCH PLND SHUTDWN", "CONTROL CHANNEL PLANNED SHUTDOWN"),
@@ -272,6 +293,8 @@ public enum Opcode
return MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE;
case 0x05:
return MOTOROLA_OSP_TRAFFIC_CHANNEL_ID;
case 0x07:
return MOTOROLA_OSP_OPCODE_7;
case 0x09:
return MOTOROLA_OSP_SYSTEM_LOADING;
case 0x0B:

View File

@@ -22,7 +22,7 @@ package io.github.dsheirer.module.log;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.module.Module;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.util.StringUtils;
import org.slf4j.Logger;
@@ -36,12 +36,11 @@ public class EventLogManager
{
private final static Logger mLog = LoggerFactory.getLogger(EventLogManager.class);
private Path mDirectory;
private UserPreferences mUserPreferences;
public EventLogManager()
public EventLogManager(UserPreferences userPreferences)
{
mDirectory = SystemProperties.getInstance()
.getApplicationFolder("event_logs");
mUserPreferences = userPreferences;
}
public List<Module> getLoggers(Channel channel)
@@ -89,16 +88,18 @@ public class EventLogManager
sb.append(eventLogType.getFileSuffix());
sb.append(".log");
Path eventLogDirectory = mUserPreferences.getDirectoryPreference().getDirectoryEventLog();
switch(eventLogType)
{
case CALL_EVENT:
return new DecodeEventLogger(mDirectory, sb.toString(), frequency);
return new DecodeEventLogger(eventLogDirectory, sb.toString(), frequency);
case DECODED_MESSAGE:
return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency);
return new MessageEventLogger(eventLogDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency);
case TRAFFIC_CALL_EVENT:
return new DecodeEventLogger(mDirectory, sb.toString(), frequency);
return new DecodeEventLogger(eventLogDirectory, sb.toString(), frequency);
case TRAFFIC_DECODED_MESSAGE:
return new MessageEventLogger(mDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency);
return new MessageEventLogger(eventLogDirectory, sb.toString(), MessageEventLogger.Type.DECODED, frequency);
default:
return null;
}

View File

@@ -33,7 +33,7 @@ import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.map.ChannelMapEvent;
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.playlist.FilePreferences;
import io.github.dsheirer.preference.playlist.PlaylistPreference;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
@@ -186,7 +186,7 @@ public class PlaylistManager implements Listener<ChannelEvent>
*/
private void save()
{
FilePreferences files = mUserPreferences.getFilePreferences();
PlaylistPreference files = mUserPreferences.getPlaylistPreference();
PlaylistV2 playlist = new PlaylistV2();
@@ -239,7 +239,7 @@ public class PlaylistManager implements Listener<ChannelEvent>
Files.delete(files.getPlaylistLock());
}
mUserPreferences.getFilePreferences().setPlaylistLastAccessedPath(files.getPlaylist());
mUserPreferences.getPlaylistPreference().setPlaylistLastAccessedPath(files.getPlaylist());
}
catch(IOException ioe)
{
@@ -256,7 +256,7 @@ public class PlaylistManager implements Listener<ChannelEvent>
*/
public PlaylistV2 load()
{
FilePreferences files = mUserPreferences.getFilePreferences();
PlaylistPreference files = mUserPreferences.getPlaylistPreference();
PlaylistV2 playlist = null;

View File

@@ -26,7 +26,7 @@ package io.github.dsheirer.preference;
public enum PreferenceType
{
DECODE_EVENT,
FILES,
DIRECTORY,
IDENTIFIER,
TUNER;
}

View File

@@ -21,19 +21,34 @@
package io.github.dsheirer.preference;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.preference.directory.DirectoryPreference;
import io.github.dsheirer.preference.event.DecodeEventPreference;
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
import io.github.dsheirer.preference.playlist.FilePreferences;
import io.github.dsheirer.preference.playlist.PlaylistPreference;
import io.github.dsheirer.preference.tuner.TunerPreference;
import io.github.dsheirer.sample.Listener;
/**
* User Preferences. A collection of preferences that can be accessed by preference type.
*
* Note: user preference updates are broadcast throughout the system using the Google Guava Event Bus. Each component
* can register with the event bus and annotate a method to receive updates:
*
* To register a component to receive events, add this in the constructor:
* MyEventBus.getEventBus().register(this);
*
* To receive preference update notifications, annotate a method with @Subscribe and use a PreferenceType argument:
*
* @Subscribe
* public void preferenceUpdated(PreferenceType preferenceType)
* {
* }
*/
public class UserPreferences implements Listener<PreferenceType>
{
private DecodeEventPreference mDecodeEventPreference;
private FilePreferences mFilePreferences;
private DirectoryPreference mDirectoryPreference;
private PlaylistPreference mPlaylistPreference;
private TalkgroupFormatPreference mTalkgroupFormatPreference;
private TunerPreference mTunerPreference;
@@ -53,12 +68,20 @@ public class UserPreferences implements Listener<PreferenceType>
return mDecodeEventPreference;
}
/**
* Directory preferences
*/
public DirectoryPreference getDirectoryPreference()
{
return mDirectoryPreference;
}
/**
* Playlist preferences
*/
public FilePreferences getFilePreferences()
public PlaylistPreference getPlaylistPreference()
{
return mFilePreferences;
return mPlaylistPreference;
}
/**
@@ -83,7 +106,8 @@ public class UserPreferences implements Listener<PreferenceType>
private void loadPreferenceTypes()
{
mDecodeEventPreference = new DecodeEventPreference(this);
mFilePreferences = new FilePreferences(this::receive);
mDirectoryPreference = new DirectoryPreference(this::receive);
mPlaylistPreference = new PlaylistPreference(this, mDirectoryPreference);
mTalkgroupFormatPreference = new TalkgroupFormatPreference(this);
mTunerPreference = new TunerPreference(this);
}

View File

@@ -0,0 +1,444 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.preference.directory;
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;
/**
* User preferences for directories
*/
public class DirectoryPreference extends Preference
{
private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreference.class);
private Preferences mPreferences = Preferences.userNodeForPackage(DirectoryPreference.class);
private static final String DIRECTORY_APPLICATION_ROOT = "SDRTrunk";
private static final String DIRECTORY_APPLICATION_LOG = "logs";
private static final String DIRECTORY_EVENT_LOG = "event_logs";
private static final String DIRECTORY_PLAYLIST = "playlist";
private static final String DIRECTORY_RECORDING = "recordings";
private static final String DIRECTORY_SCREEN_CAPTURE = "screen_captures";
private static final String DIRECTORY_STREAMING = "streaming";
private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT = "directory.application.root";
private static final String PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS = "directory.application.logs";
private static final String PREFERENCE_KEY_DIRECTORY_EVENT_LOGS = "directory.event.logs";
private static final String PREFERENCE_KEY_DIRECTORY_PLAYLIST = "directory.playlist";
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 Path mDirectoryApplicationRoot;
private Path mDirectoryApplicationLogs;
private Path mDirectoryEventLogs;
private Path mDirectoryPlaylist;
private Path mDirectoryRecording;
private Path mDirectoryScreenCapture;
private Path mDirectoryStreaming;
/**
* Constructs this preference with an update listener
* @param updateListener to receive notifications whenever these preferences change
*/
public DirectoryPreference(Listener<PreferenceType> updateListener)
{
super(updateListener);
}
@Override
public PreferenceType getPreferenceType()
{
return PreferenceType.DIRECTORY;
}
/**
* Path to the application root directory
*/
public Path getDirectoryApplicationRoot()
{
if(mDirectoryApplicationRoot == null)
{
mDirectoryApplicationRoot = getPath(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT, getDefaultApplicationDirectory());
createDirectory(mDirectoryApplicationRoot);
}
return mDirectoryApplicationRoot;
}
/**
* Sets the path to the application root directory
*/
public void setDirectoryApplicationRoot(Path path)
{
mDirectoryApplicationRoot = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT, path.toString());
//Set the child paths to null so that they'll be recreated on next access. If the user has explicitly set a
//path override for any of these directories, then it will be retained.
nullifyApplicationChildDirectories();
notifyPreferenceUpdated();
}
/**
* Nullifies each of the application root child directories so that they can be re-created from application root
* or re-created from a persisted override path.
*/
private void nullifyApplicationChildDirectories()
{
mDirectoryApplicationLogs = null;
mDirectoryEventLogs = null;
mDirectoryPlaylist = null;
mDirectoryRecording = null;
mDirectoryScreenCapture = null;
mDirectoryStreaming = null;
}
/**
* Removes a stored recording directory preference so that the default path can be used again
*/
public void resetDirectoryApplicationRoot()
{
mDirectoryApplicationRoot = null;
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_APPLICATION_ROOT);
//Set the child paths to null so that they'll be recreated on next access. If the user has explicitly set a
//path override for any of these directories, then it will be retained.
nullifyApplicationChildDirectories();
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing application logs
*/
public Path getDirectoryApplicationLog()
{
if(mDirectoryApplicationLogs == null)
{
mDirectoryApplicationLogs = getPath(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS, getDefaultApplicationLogsDirectory());
createDirectory(mDirectoryApplicationLogs);
}
return mDirectoryApplicationLogs;
}
/**
* Sets the path to the application logs folder
*/
public void setDirectoryApplicationLogs(Path path)
{
mDirectoryApplicationLogs = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored application logs directory preference so that the default path can be used again
*/
public void resetDirectoryApplicationLogs()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_APPLICATION_LOGS);
mDirectoryApplicationLogs = null;
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing event logs
*/
public Path getDirectoryEventLog()
{
if(mDirectoryEventLogs == null)
{
mDirectoryEventLogs = getPath(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS, getDefaultEventLogsDirectory());
createDirectory(mDirectoryEventLogs);
}
return mDirectoryEventLogs;
}
/**
* Sets the path to the event logs folder
*/
public void setDirectoryEventLogs(Path path)
{
mDirectoryEventLogs = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored playlist directory preference so that the default path can be used again
*/
public void resetDirectoryEventLogs()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_EVENT_LOGS);
mDirectoryEventLogs = null;
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing playlist
*/
public Path getDirectoryPlaylist()
{
if(mDirectoryPlaylist == null)
{
mDirectoryPlaylist = getPath(PREFERENCE_KEY_DIRECTORY_PLAYLIST, getDefaultPlaylistDirectory());
createDirectory(mDirectoryPlaylist);
}
return mDirectoryPlaylist;
}
/**
* Sets the path to the playlist folder
*/
public void setDirectoryPlaylist(Path path)
{
mDirectoryPlaylist = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_PLAYLIST, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored playlist directory preference so that the default path can be used again
*/
public void resetDirectoryPlaylist()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_PLAYLIST);
mDirectoryPlaylist = null;
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing recordings
*/
public Path getDirectoryRecording()
{
if(mDirectoryRecording == null)
{
mDirectoryRecording = getPath(PREFERENCE_KEY_DIRECTORY_RECORDING, getDefaultRecordingDirectory());
createDirectory(mDirectoryRecording);
}
return mDirectoryRecording;
}
/**
* Sets the path to the playlist folder
*/
public void setDirectoryRecording(Path path)
{
mDirectoryRecording = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_RECORDING, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored recording directory preference so that the default path can be used again
*/
public void resetDirectoryRecording()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_RECORDING);
mDirectoryRecording = null;
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing screen capture
*/
public Path getDirectoryScreenCapture()
{
if(mDirectoryScreenCapture == null)
{
mDirectoryScreenCapture = getPath(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE, getDefaultScreenCaptureDirectory());
createDirectory(mDirectoryScreenCapture);
}
return mDirectoryScreenCapture;
}
/**
* Sets the path to the screen capture folder
*/
public void setDirectoryScreenCapture(Path path)
{
mDirectoryScreenCapture = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored screen capture directory preference so that the default path can be used again
*/
public void resetDirectoryScreenCapture()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_SCREEN_CAPTURE);
mDirectoryScreenCapture = null;
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing streaming temporary recordings
*/
public Path getDirectoryStreaming()
{
if(mDirectoryStreaming == null)
{
mDirectoryStreaming = getPath(PREFERENCE_KEY_DIRECTORY_STREAMING, getDefaultStreamingDirectory());
createDirectory(mDirectoryStreaming);
}
return mDirectoryStreaming;
}
/**
* Sets the path to the streaming folder
*/
public void setDirectoryStreaming(Path path)
{
mDirectoryStreaming = path;
mPreferences.put(PREFERENCE_KEY_DIRECTORY_STREAMING, path.toString());
notifyPreferenceUpdated();
}
/**
* Removes a stored streaming directory preference so that the default path can be used again
*/
public void resetDirectoryStreaming()
{
mPreferences.remove(PREFERENCE_KEY_DIRECTORY_STREAMING);
mDirectoryStreaming = null;
notifyPreferenceUpdated();
}
/**
* Default application root directory
*/
private Path getDefaultApplicationDirectory()
{
return Paths.get(System.getProperty("user.home"), DIRECTORY_APPLICATION_ROOT);
}
/**
* Default event logs directory
*/
public Path getDefaultApplicationLogsDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_APPLICATION_LOG);
}
/**
* Default event logs directory
*/
public Path getDefaultEventLogsDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_EVENT_LOG);
}
/**
* Default playlist directory
*/
public Path getDefaultPlaylistDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_PLAYLIST);
}
/**
* Default playlist directory
*/
public Path getDefaultRecordingDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_RECORDING);
}
/**
* Default screen capture directory
*/
public Path getDefaultScreenCaptureDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_SCREEN_CAPTURE);
}
/**
* Default streaming directory
*/
public Path getDefaultStreamingDirectory()
{
return getDirectoryApplicationRoot().resolve(DIRECTORY_STREAMING);
}
/**
* Returns the path stored in preferences and referenced by the key argument if it exists, otherwise returns the
* default path.
*
* Note: the default path is not checked for existence.
*
* @param key to the preferences service for the path
* @param defaultPath to use if the preferred path does not exist
* @return requested path
*/
private Path getPath(String key, Path defaultPath)
{
String stringPath = mPreferences.get(key, defaultPath.toString());
if(stringPath != null && !stringPath.isEmpty())
{
Path temp = Paths.get(stringPath);
if(Files.exists(temp))
{
return temp;
}
}
return defaultPath;
}
/**
* Creates a directory if it does not already exist
*/
private void createDirectory(Path directory)
{
if(!Files.exists(directory))
{
try
{
Files.createDirectory(directory);
mLog.info("Created directory [" + directory.toString() + "]");
}
catch(Exception e)
{
mLog.error("Error creating directory [" + directory.toString() + "]");
}
}
}
}

View File

@@ -22,6 +22,7 @@ package io.github.dsheirer.preference.playlist;
import io.github.dsheirer.preference.Preference;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.directory.DirectoryPreference;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,39 +35,37 @@ import java.util.prefs.Preferences;
/**
* User preferences for playlists
*/
public class FilePreferences extends Preference
public class PlaylistPreference extends Preference
{
private final static Logger mLog = LoggerFactory.getLogger(FilePreferences.class);
private Preferences mPreferences = Preferences.userNodeForPackage(FilePreferences.class);
private final static Logger mLog = LoggerFactory.getLogger(PlaylistPreference.class);
private Preferences mPreferences = Preferences.userNodeForPackage(PlaylistPreference.class);
private static final String FOLDER_APPLICATION_ROOT = "SDRTrunk";
private static final String FOLDER_PLAYLIST = "playlist";
private static final String FILE_PLAYLIST = "default.xml";
private static final String FILE_LEGACY_PLAYLIST = "playlist_v2.xml";
private static final String PREFERENCE_KEY_PLAYLIST_FOLDER = "playlist.folder";
private static final String PREFERENCE_KEY_PLAYLIST_LAST_ACCESSED = "playlist.last.accessed";
private static final String PREFERENCE_KEY_PLAYLIST_STARTUP = "playlist.default";
private static final String PREFERENCE_KEY_PLAYLIST_USE_LAST_ACCESSED = "playlist.use.last.accessed";
private Path mPlaylistFolder;
private Path mPlaylistStartupPath;
private Path mPlaylistLastAccessedPath;
private Boolean mUsePlaylistLastAccessedByDefault = true;
private DirectoryPreference mDirectoryPreference;
/**
* Constructs this preference with an update listener
* @param updateListener to receive notifications whenever these preferences change
*/
public FilePreferences(Listener<PreferenceType> updateListener)
public PlaylistPreference(Listener<PreferenceType> updateListener, DirectoryPreference directoryPreference)
{
super(updateListener);
mDirectoryPreference = directoryPreference;
}
@Override
public PreferenceType getPreferenceType()
{
return PreferenceType.FILES;
return PreferenceType.DIRECTORY;
}
@@ -90,7 +89,7 @@ public class FilePreferences extends Preference
*/
public Path getLegacyPlaylist()
{
return getPlaylistFolder().resolve(FILE_LEGACY_PLAYLIST);
return mDirectoryPreference.getDirectoryPlaylist().resolve(FILE_LEGACY_PLAYLIST);
}
/**
@@ -157,29 +156,6 @@ public class FilePreferences extends Preference
notifyPreferenceUpdated();
}
/**
* Path to the folder for storing playlist
*/
public Path getPlaylistFolder()
{
if(mPlaylistFolder == null)
{
mPlaylistFolder = getPath(PREFERENCE_KEY_PLAYLIST_FOLDER, getDefaultPlaylistFolder());
}
return mPlaylistFolder;
}
/**
* Sets the path to the playlist folder
*/
public void setPlaylistFolder(Path path)
{
mPlaylistFolder = path;
mPreferences.put(PREFERENCE_KEY_PLAYLIST_FOLDER, path.toString());
notifyPreferenceUpdated();
}
/**
* Indicates the playlist to load on startup, either the last accessed playlist (true) or the default playlist (false).
*/
@@ -204,32 +180,12 @@ public class FilePreferences extends Preference
}
/**
* Default application root path
*/
private Path getDefaultApplicationFolder()
{
Path defaultApplicationPath = Paths.get(System.getProperty("user.home"), FOLDER_APPLICATION_ROOT);
createDirectory(defaultApplicationPath);
return defaultApplicationPath;
}
/**
* Default playlist folder
*/
public Path getDefaultPlaylistFolder()
{
Path defaultPlaylistFolder = getDefaultApplicationFolder().resolve(FOLDER_PLAYLIST);
createDirectory(defaultPlaylistFolder);
return defaultPlaylistFolder;
}
/**
* Default playlist
*/
private Path getDefaultPlaylistPath()
{
return getDefaultPlaylistFolder().resolve(FILE_PLAYLIST);
return mDirectoryPreference.getDefaultPlaylistDirectory().resolve(FILE_PLAYLIST);
}
/**
@@ -244,7 +200,7 @@ public class FilePreferences extends Preference
*/
private Path getPath(String key, Path defaultPath)
{
String stringPath = mPreferences.get(PREFERENCE_KEY_PLAYLIST_STARTUP, defaultPath.toString());
String stringPath = mPreferences.get(key, defaultPath.toString());
if(stringPath != null && !stringPath.isEmpty())
{
@@ -257,23 +213,4 @@ public class FilePreferences extends Preference
return defaultPath;
}
/**
* Creates a directory if it does not already exist
*/
private void createDirectory(Path directory)
{
if(!Files.exists(directory) && Files.isDirectory(directory))
{
try
{
Files.createDirectory(directory);
mLog.info("Created directory [" + directory.toString() + "]");
}
catch(Exception e)
{
mLog.error("Error creating directory [" + directory.toString() + "]");
}
}
}
}

View File

@@ -1,18 +1,22 @@
/*******************************************************************************
* sdr-trunk
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 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 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.
* 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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.record;
import io.github.dsheirer.alias.AliasList;
@@ -24,7 +28,7 @@ import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.integer.IntegerIdentifier;
import io.github.dsheirer.identifier.string.StringIdentifier;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.wave.AudioPacketWaveRecorder;
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
import io.github.dsheirer.record.wave.WaveMetadata;
@@ -60,6 +64,7 @@ public class RecorderManager implements Listener<ReusableAudioPacket>
private List<ReusableAudioPacket> mAudioPackets = new ArrayList<>();
private ScheduledFuture<?> mBufferProcessorFuture;
private AliasModel mAliasModel;
private UserPreferences mUserPreferences;
private int mUnknownAudioRecordingIndex = 1;
private boolean mCanStartNewRecorders = true;
@@ -71,9 +76,10 @@ public class RecorderManager implements Listener<ReusableAudioPacket>
* indicating that the call is complete. A separate recording monitor periodically checks for idled recorders to
* be stopped for cases when the channel fails to send an end-call audio packet.
*/
public RecorderManager(AliasModel aliasModel)
public RecorderManager(AliasModel aliasModel, UserPreferences userPreferences)
{
mAliasModel = aliasModel;
mUserPreferences = userPreferences;
mAudioPacketQueue.setOverflowListener(new IOverflowListener()
{
@Override
@@ -299,10 +305,13 @@ public class RecorderManager implements Listener<ReusableAudioPacket>
return getRecordingBasePath().resolve(sbFinal.toString());
}
/**
* Base path to recordings folder
* @return
*/
public Path getRecordingBasePath()
{
return SystemProperties.getInstance().getApplicationFolder("recordings");
return mUserPreferences.getDirectoryPreference().getDirectoryRecording();
}
/**

View File

@@ -1,6 +1,7 @@
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!--
~ *******************************************************************************
~ sdrtrunk
~ Copyright (C) 2014-2017 Dennis Sheirer
~ Copyright (C) 2014-2018 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
@@ -14,29 +15,13 @@
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>
~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
~ ******************************************************************************
-->
<configuration>
<conversionRule conversionWord="memory_usage" converterClass="io.github.dsheirer.util.MemoryUsageLogger" />
<timestamp key="appstart" datePattern="HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${user.home}/SDRTrunk/logs/sdrtrunk_app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily log rollover -->
<fileNamePattern>${user.home}/SDRTrunk/logs/%d{yyyyMMdd}_sdrtrunk_app.log</fileNamePattern>
<maxHistory>10</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%-25(%d{yyyyMMdd HHmmss.SSS} [%thread]) %-5level %logger{30} - %msg %memory_usage%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-12(%d{HH:mm:ss.SSS}) %-5level %logger{25} - %msg %memory_usage%n</pattern>