From dcb4213711420aa2571e9fd4949c12cdf4ac4c21 Mon Sep 17 00:00:00 2001 From: Steve Myers <35839355+smyers119@users.noreply.github.com> Date: Fri, 18 Feb 2022 22:26:27 -0500 Subject: [PATCH] Merge Headless Merged Headless changes into current master. tested and working. --- build.gradle | 4 +- .../BroadcastifyCallBroadcaster.java | 2 + .../metadata/ChannelMetadataModel.java | 73 ++++---- .../channel/ChannelProcessingManager.java | 17 +- .../java/io/github/dsheirer/gui/SDRTrunk.java | 173 ++++++++++++------ .../decode/event/MessageActivityModel.java | 29 +-- 6 files changed, 198 insertions(+), 100 deletions(-) diff --git a/build.gradle b/build.gradle index fcd1e8835..86311c36e 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,7 @@ repositories { maven { url "https://jitpack.io" } } -version = '0.5.0-alpha7' +version = '0.5.0-alpha8' sourceCompatibility = '17' javafx { @@ -88,6 +88,8 @@ dependencies { implementation 'javax.usb:usb-api:1.0.2' implementation 'net.coderazzi:tablefilter-swing:5.4.0' implementation 'org.apache.commons:commons-compress:1.20' + implementation 'commons-cli:commons-cli:1.4' + implementation 'commons-io:commons-io:2.7' implementation 'org.apache.commons:commons-lang3:3.8.1' implementation 'org.apache.commons:commons-math3:3.6.1' implementation 'org.apache.commons:commons-csv:1.9.0' diff --git a/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyCallBroadcaster.java b/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyCallBroadcaster.java index 276bb6606..251cc9a08 100644 --- a/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyCallBroadcaster.java +++ b/src/main/java/io/github/dsheirer/audio/broadcast/broadcastify/BroadcastifyCallBroadcaster.java @@ -92,6 +92,7 @@ public BroadcastifyCallBroadcaster(BroadcastifyCallConfiguration config, AliasMo @Override public void start() { + System.out.println("Started audio recording thread."); setBroadcastState(BroadcastState.CONNECTING); String response = testConnection(getBroadcastConfiguration()); mLastConnectionAttempt = System.currentTimeMillis(); @@ -211,6 +212,7 @@ private void processRecordingQueue() if(isValid(audioRecording) && audioRecording.getRecordingLength() > 0) { + System.out.println("Processing recording."); float durationSeconds = (float)(audioRecording.getRecordingLength() / 1E3f); long timestampSeconds = (int)(audioRecording.getStartTime() / 1E3); String talkgroup = getTo(audioRecording); diff --git a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java index f98f774a2..d1c55a28d 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/ChannelMetadataModel.java @@ -22,6 +22,8 @@ package io.github.dsheirer.channel.metadata; import com.google.common.eventbus.Subscribe; +import io.github.dsheirer.gui.SDRTrunk; + import io.github.dsheirer.alias.Alias; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.eventbus.MyEventBus; @@ -74,13 +76,16 @@ public void preferenceUpdated(PreferenceType preferenceType) { if(preferenceType == PreferenceType.TALKGROUP_FORMAT) { - EventQueue.invokeLater(() -> { - for(int row = 0; row < mChannelMetadata.size(); row++) - { - fireTableCellUpdated(row, COLUMN_USER_FROM); - fireTableCellUpdated(row, COLUMN_USER_TO); - } - }); + if (!SDRTrunk.mHeadlessMode) { + + EventQueue.invokeLater(() -> { + for(int row = 0; row < mChannelMetadata.size(); row++) + { + fireTableCellUpdated(row, COLUMN_USER_FROM); + fireTableCellUpdated(row, COLUMN_USER_TO); + } + }); + } } } @@ -114,21 +119,24 @@ public void setChannelAddListener(Listener listener) public void add(ChannelAndMetadata channelAndMetadata) { //Execute on the swing thread to avoid threading issues - EventQueue.invokeLater(() -> { - for(ChannelMetadata channelMetadata: channelAndMetadata.getChannelMetadata()) - { - mChannelMetadata.add(channelMetadata); - mMetadataChannelMap.put(channelMetadata, channelAndMetadata.getChannel()); - int index = mChannelMetadata.indexOf(channelMetadata); - fireTableRowsInserted(index, index); - channelMetadata.setUpdateEventListener(ChannelMetadataModel.this); - } + if (!SDRTrunk.mHeadlessMode) { - if(mChannelAddListener != null) - { - mChannelAddListener.receive(channelAndMetadata); - } - }); + EventQueue.invokeLater(() -> { + for(ChannelMetadata channelMetadata: channelAndMetadata.getChannelMetadata()) + { + mChannelMetadata.add(channelMetadata); + mMetadataChannelMap.put(channelMetadata, channelAndMetadata.getChannel()); + int index = mChannelMetadata.indexOf(channelMetadata); + fireTableRowsInserted(index, index); + channelMetadata.setUpdateEventListener(ChannelMetadataModel.this); + } + + if(mChannelAddListener != null) + { + mChannelAddListener.receive(channelAndMetadata); + } + }); + } } /** @@ -147,17 +155,20 @@ public void updateChannelMetadataToChannelMap(Collection channe public void remove(ChannelMetadata channelMetadata) { //Execute on the swing thread to avoid threading issues - EventQueue.invokeLater(() -> { - channelMetadata.removeUpdateEventListener(); - int index = mChannelMetadata.indexOf(channelMetadata); - mChannelMetadata.remove(channelMetadata); - mMetadataChannelMap.remove(channelMetadata); + if (!SDRTrunk.mHeadlessMode) { - if(index >= 0) - { - fireTableRowsDeleted(index, index); - } - }); + EventQueue.invokeLater(() -> { + channelMetadata.removeUpdateEventListener(); + int index = mChannelMetadata.indexOf(channelMetadata); + mChannelMetadata.remove(channelMetadata); + mMetadataChannelMap.remove(channelMetadata); + + if(index >= 0) + { + fireTableRowsDeleted(index, index); + } + }); + } } /** diff --git a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java index 197f85135..11dba0617 100644 --- a/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java +++ b/src/main/java/io/github/dsheirer/controller/channel/ChannelProcessingManager.java @@ -50,6 +50,7 @@ import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.config.SourceConfigTuner; import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency; +import io.github.dsheirer.gui.SDRTrunk; import io.github.dsheirer.util.ThreadPool; import javafx.application.Platform; import org.slf4j.Logger; @@ -311,7 +312,11 @@ private void startProcessing(ChannelStartProcessingRequest request) throws Chann if(source == null) { //This has to be done on the FX event thread when the playlist editor is constructed - Platform.runLater(() -> channel.setProcessing(false)); + if (!SDRTrunk.mHeadlessMode) { + Platform.runLater(() -> channel.setProcessing(false)); + } else { + channel.setProcessing(false); + } mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_PROCESSING_START_REJECTED, TUNER_UNAVAILABLE_DESCRIPTION)); @@ -466,7 +471,11 @@ else if(request.hasChildDecodeEventHistory()) processingChain.start(); //This has to be done on the FX event thread when the playlist editor is constructed - Platform.runLater(() -> channel.setProcessing(true)); + if (!SDRTrunk.mHeadlessMode) { + Platform.runLater(() -> channel.setProcessing(true)); + } else { + channel.setProcessing(true); + } getChannelMetadataModel().add(new ChannelAndMetadata(channel, processingChain.getChannelState().getChannelMetadata())); @@ -481,7 +490,9 @@ else if(request.hasChildDecodeEventHistory()) private void stopProcessing(Channel channel) throws ChannelException { //This has to be done on the FX event thread when the playlist editor is constructed - Platform.runLater(() -> channel.setProcessing(false)); + if (!SDRTrunk.mHeadlessMode) { + Platform.runLater(() -> channel.setProcessing(false)); + } if(mProcessingChains.containsKey(channel)) { diff --git a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java index b2691b744..0df99bd07 100644 --- a/src/main/java/io/github/dsheirer/gui/SDRTrunk.java +++ b/src/main/java/io/github/dsheirer/gui/SDRTrunk.java @@ -29,6 +29,7 @@ import io.github.dsheirer.controller.ControllerPanel; import io.github.dsheirer.controller.channel.Channel; import io.github.dsheirer.controller.channel.ChannelAutoStartFrame; +import io.github.dsheirer.controller.channel.ChannelEvent; import io.github.dsheirer.controller.channel.ChannelSelectionManager; import io.github.dsheirer.eventbus.MyEventBus; import io.github.dsheirer.gui.icon.ViewIconManagerRequest; @@ -58,6 +59,13 @@ import jiconfont.icons.font_awesome.FontAwesome; import jiconfont.swing.IconFontSwing; import net.miginfocom.swing.MigLayout; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -113,31 +121,55 @@ public class SDRTrunk implements Listener private SourceManager mSourceManager; private SettingsManager mSettingsManager; private SpectralDisplayPanel mSpectralPanel; - private JFrame mMainGui = new JFrame(); + private JFrame mMainGui; private JideSplitPane mSplitPane; private JavaFxWindowManager mJavaFxWindowManager; private UserPreferences mUserPreferences = new UserPreferences(); private ApplicationLog mApplicationLog; + public static boolean mHeadlessMode; + public static boolean mSilentMode; private String mTitle; - public SDRTrunk() + public SDRTrunk(String[] args) { mApplicationLog = new ApplicationLog(mUserPreferences); mApplicationLog.start(); + //Handle command-line options + Options options = new Options(); + options.addOption(null, "headless", false, "Disables the application GUI"); + options.addOption(null, "silent", false, "Disables audio output"); + options.addOption("h", "help", false, "Displays usage information"); + CommandLineParser parser = new DefaultParser(); + try { + CommandLine cli = parser.parse(options, args); + if (cli.hasOption("help")) { + HelpFormatter formatter = new HelpFormatter(); + formatter.printHelp("SDRTrunk", options); + System.exit(0); + } + mHeadlessMode = cli.hasOption("headless"); + mSilentMode = cli.hasOption("silent"); + } catch (ParseException e) { + mLog.error("Error trying to parse command line options"); + e.printStackTrace(); + } + String operatingSystem = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); - if(operatingSystem.contains("mac") || operatingSystem.contains("nux")) - { - try - { - UIManager.setLookAndFeel(MetalLookAndFeel.class.getName()); - LookAndFeelFactory.installJideExtension(); - } - catch(Exception e) + if (!mHeadlessMode) { + if(operatingSystem.contains("mac") || operatingSystem.contains("nux")) { - mLog.error("Error trying to set Metal look and feel for OS [" + operatingSystem + "]"); + try + { + UIManager.setLookAndFeel(MetalLookAndFeel.class.getName()); + LookAndFeelFactory.installJideExtension(); + } + catch(Exception e) + { + mLog.error("Error trying to set Metal look and feel for OS [" + operatingSystem + "]"); + } } } @@ -169,10 +201,16 @@ public SDRTrunk() AliasModel aliasModel = new AliasModel(); EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences); mPlaylistManager = new PlaylistManager(mUserPreferences, mSourceManager, aliasModel, eventLogManager, mIconModel); - mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mPlaylistManager); + if (!mHeadlessMode) { + mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mPlaylistManager); + } new ChannelSelectionManager(mPlaylistManager.getChannelModel()); - AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(mUserPreferences); + AudioPlaybackManager audioPlaybackManager = null; + if (!mSilentMode) + { + audioPlaybackManager = new AudioPlaybackManager(mUserPreferences); + } mAudioRecordingManager = new AudioRecordingManager(mUserPreferences); mAudioRecordingManager.start(); @@ -184,44 +222,55 @@ public SDRTrunk() DuplicateCallDetector duplicateCallDetector = new DuplicateCallDetector(mUserPreferences); mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(duplicateCallDetector); - mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(audioPlaybackManager); + if (!mSilentMode) + { + mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(audioPlaybackManager); + } mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(mAudioRecordingManager); mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(mAudioStreamingManager); - MapService mapService = new MapService(mIconModel); - mPlaylistManager.getChannelProcessingManager().addDecodeEventListener(mapService); + if (!mHeadlessMode) { + mMainGui = new JFrame(); + MapService mapService = new MapService(mIconModel); + mPlaylistManager.getChannelProcessingManager().addDecodeEventListener(mapService); - mControllerPanel = new ControllerPanel(mPlaylistManager, audioPlaybackManager, mIconModel, mapService, - mSettingsManager, mSourceManager, mUserPreferences); + mControllerPanel = new ControllerPanel(mPlaylistManager, audioPlaybackManager, mIconModel, mapService, + mSettingsManager, mSourceManager, mUserPreferences); - mSpectralPanel = new SpectralDisplayPanel(mPlaylistManager, mSettingsManager, tunerModel); + mSpectralPanel = new SpectralDisplayPanel(mPlaylistManager, mSettingsManager, tunerModel); - TunerSpectralDisplayManager tunerSpectralDisplayManager = new TunerSpectralDisplayManager(mSpectralPanel, - mPlaylistManager, mSettingsManager, tunerModel); - tunerModel.addListener(tunerSpectralDisplayManager); - tunerModel.addListener(this); + TunerSpectralDisplayManager tunerSpectralDisplayManager = new TunerSpectralDisplayManager(mSpectralPanel, + mPlaylistManager, mSettingsManager, tunerModel); + tunerModel.addListener(tunerSpectralDisplayManager); + tunerModel.addListener(this); + } mPlaylistManager.init(); - mLog.info("starting main application gui"); - - //Initialize the GUI - initGUI(); - - tunerModel.requestFirstTunerDisplay(); - - //Start the gui - EventQueue.invokeLater(() -> { - try - { - mMainGui.setVisible(true); - autoStartChannels(); - } - catch(Exception e) - { - e.printStackTrace(); - } - }); + if (mHeadlessMode) { + autoStartChannels(); + } + else + { + mLog.info("starting main application gui"); + //Initialize the GUI + initGUI(); + + tunerModel.requestFirstTunerDisplay(); + + //Start the gui + EventQueue.invokeLater(() -> { + try + { + mMainGui.setVisible(true); + autoStartChannels(); + } + catch(Exception e) + { + e.printStackTrace(); + } + }); + } } /** @@ -235,8 +284,18 @@ private void autoStartChannels() if(channels.size() > 0) { - ChannelAutoStartFrame autoStartFrame = new ChannelAutoStartFrame(mPlaylistManager.getChannelProcessingManager(), - channels); + if (mHeadlessMode) + { + for (Channel channel : channels) + { + mPlaylistManager.getChannelProcessingManager().receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE)); + } + } + else + { + ChannelAutoStartFrame autoStartFrame = new ChannelAutoStartFrame(mPlaylistManager.getChannelProcessingManager(), + channels); + } } } @@ -421,19 +480,25 @@ public void actionPerformed(ActionEvent arg0) private void processShutdown() { mLog.info("Application shutdown started ..."); - mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, mMainGui.getLocation()); - mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, mMainGui.getSize()); - mUserPreferences.getSwingPreference().setMaximized(WINDOW_FRAME_IDENTIFIER, - (mMainGui.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH); - mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize()); - mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize()); - mJavaFxWindowManager.shutdown(); + if (mMainGui != null) + { + mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, mMainGui.getLocation()); + mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, mMainGui.getSize()); + mUserPreferences.getSwingPreference().setMaximized(WINDOW_FRAME_IDENTIFIER, + (mMainGui.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH); + mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize()); + mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize()); + mJavaFxWindowManager.shutdown(); + } mLog.info("Stopping channels ..."); mPlaylistManager.getChannelProcessingManager().shutdown(); mAudioRecordingManager.stop(); - mLog.info("Stopping spectral display ..."); - mSpectralPanel.clearTuner(); + if (mSpectralPanel != null) + { + mLog.info("Stopping spectral display ..."); + mSpectralPanel.clearTuner(); + } mSourceManager.shutdown(); mLog.info("Shutdown complete."); mApplicationLog.stop(); @@ -621,6 +686,6 @@ public void menuCanceled(MenuEvent e) { } */ public static void main(String[] args) { - new SDRTrunk(); + new SDRTrunk(args); } } diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java index b597baca7..d6afe2d09 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java @@ -31,6 +31,7 @@ import java.awt.EventQueue; import java.text.SimpleDateFormat; import java.util.LinkedList; +import io.github.dsheirer.gui.SDRTrunk; import java.util.List; public class MessageActivityModel extends AbstractTableModel implements Listener @@ -67,17 +68,20 @@ public void setFilters(FilterSet filterSet) */ public void clear() { + if (!SDRTrunk.mHeadlessMode) { EventQueue.invokeLater(() -> { mMessageItems.clear(); fireTableDataChanged(); }); } + } /** * Clears the current messages and loads the messages argument */ public void clearAndSet(List messages) { + if (!SDRTrunk.mHeadlessMode) { EventQueue.invokeLater(() -> { mMessageItems.clear(); fireTableDataChanged(); @@ -87,6 +91,7 @@ public void clearAndSet(List messages) } }); } + } public FilterSet getMessageFilterSet() { @@ -137,18 +142,20 @@ public void receive(final IMessage message) { final MessageItem messageItem = new MessageItem(message); - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() + if (!SDRTrunk.mHeadlessMode) { + EventQueue.invokeLater(new Runnable() { - mMessageItems.addFirst(messageItem); - - MessageActivityModel.this.fireTableRowsInserted(0, 0); - - prune(); - } - }); + @Override + public void run() + { + mMessageItems.addFirst(messageItem); + + MessageActivityModel.this.fireTableRowsInserted(0, 0); + + prune(); + } + }); + } } }