mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Initial commit of redesigned internal processing and audio management.
Note: Several of the decoders are currently non-functioning in this commit. If you want the latest functioning codebase, please checkout the commit prior to this one. All decoders will eventually be updated with this new architecture. Currently broken: - Logging and recording - Aux decoders (Fleetsync/MDC/LoJack/Tait) - LTR/Passport decoders Changes in this commit: - Module architecture for decoders and components with central provisioning by the DecoderFactory - Transition from Complex sample stream processing to float buffer processing for improved memory management - Redesigned filter structures - to support buffers and streams - Resolved several memory leak issues - Universal sharing of the thread pool manager - Added centralized audio management with call/user prioritization and new gui with mute/volume controls - Embedded audio metadata for eventual streaming support - Redesigned ProcessingChain to support independent re-use - Added new TrafficChannelManager to handle traffic channel allocations - Traffic Channel pool size and Call timeout available in P25 and MPT1327 configs - Redesigned channel and decoder gui components
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
* Copyright (C) 2014,2015 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
|
||||
@@ -29,6 +29,8 @@ import javax.swing.tree.MutableTreeNode;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import map.IconManager;
|
||||
import module.decode.DecoderType;
|
||||
import module.decode.config.DecodeConfigFactory;
|
||||
import playlist.PlaylistNode;
|
||||
import source.config.SourceConfigTuner;
|
||||
import source.recording.RecordingGroupNode;
|
||||
@@ -37,14 +39,11 @@ import source.tuner.TunerGroupNode;
|
||||
import source.tuner.TunerNode;
|
||||
import source.tuner.TunerSelectionListener;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelEventListener;
|
||||
import controller.channel.ChannelNode;
|
||||
import controller.site.Site;
|
||||
import controller.system.System;
|
||||
import controller.system.SystemListNode;
|
||||
import controller.system.SystemNode;
|
||||
import decode.DecoderType;
|
||||
import decode.config.DecodeConfigFactory;
|
||||
|
||||
public class ConfigurationControllerModel extends DefaultTreeModel
|
||||
{
|
||||
@@ -54,12 +53,8 @@ public class ConfigurationControllerModel extends DefaultTreeModel
|
||||
|
||||
private ResourceManager mResourceManager;
|
||||
|
||||
private ArrayList<ChannelEventListener> mChannelConfigChangeListeners =
|
||||
new ArrayList<ChannelEventListener>();
|
||||
|
||||
private ArrayList<TunerSelectionListener> mTunerSelectionListeners =
|
||||
new ArrayList<TunerSelectionListener>();
|
||||
|
||||
|
||||
private PlaylistNode mPlaylistNode;
|
||||
private TunerGroupNode mTunerGroupNode;
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
@@ -30,23 +29,24 @@ import javax.swing.event.ChangeListener;
|
||||
|
||||
import map.MapPanel;
|
||||
import message.Message;
|
||||
import module.decode.event.CallEventPanel;
|
||||
import module.decode.event.MessageActivityPanel;
|
||||
import module.decode.state.ChannelList;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import sample.Listener;
|
||||
import spectrum.ChannelSpectrumPanel;
|
||||
import audio.AudioPanel;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
|
||||
import controller.activity.CallEventPanel;
|
||||
import controller.activity.MessageActivityPanel;
|
||||
import controller.channel.ChannelManager;
|
||||
import controller.state.ChannelStateList;
|
||||
|
||||
public class ControllerPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ChannelStateList mChannelStateList;
|
||||
private ChannelList mChannelStateList;
|
||||
|
||||
private CallEventPanel mCallEventPanel = new CallEventPanel();
|
||||
|
||||
@@ -135,7 +135,8 @@ public class ControllerPanel extends JPanel
|
||||
|
||||
|
||||
/* Channel state list */
|
||||
mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() );
|
||||
mChannelStateList = new ChannelList( mResourceManager.getPlaylistManager(),
|
||||
mResourceManager.getSettingsManager() );
|
||||
|
||||
/* Register each of the components to receive channel events when the
|
||||
* channels are selected or change */
|
||||
@@ -147,12 +148,20 @@ public class ControllerPanel extends JPanel
|
||||
|
||||
JScrollPane channelStateListScroll = new JScrollPane();
|
||||
channelStateListScroll.getViewport().setView( mChannelStateList );
|
||||
channelStateListScroll.setPreferredSize( new Dimension( 150,
|
||||
(int)channelStateListScroll.getPreferredSize().getHeight() ) );
|
||||
channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) );
|
||||
|
||||
AudioPanel audioPanel = new AudioPanel( mResourceManager.getSettingsManager(),
|
||||
mResourceManager.getAudioManager() );
|
||||
audioPanel.setPreferredSize( new Dimension( 200, audioPanel.getPreferredHeight() ) );
|
||||
|
||||
JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT );
|
||||
audioChannelListSplit.setDividerSize( 5 );
|
||||
audioChannelListSplit.add( audioPanel );
|
||||
audioChannelListSplit.add( channelStateListScroll );
|
||||
|
||||
JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT );
|
||||
channelSplit.setDividerSize( 5 );
|
||||
channelSplit.add( channelStateListScroll );
|
||||
channelSplit.add( audioChannelListSplit );
|
||||
channelSplit.add( mTabbedPane );
|
||||
|
||||
add( channelSplit );
|
||||
|
||||
48
src/controller/NamingThreadFactory.java
Normal file
48
src/controller/NamingThreadFactory.java
Normal file
@@ -0,0 +1,48 @@
|
||||
package controller;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Thread factory that applies custom names to threads
|
||||
*/
|
||||
public class NamingThreadFactory implements ThreadFactory
|
||||
{
|
||||
static final AtomicInteger poolNumber = new AtomicInteger(1);
|
||||
|
||||
final ThreadGroup mThreadGroup;
|
||||
|
||||
final AtomicInteger mThreadNumber = new AtomicInteger(1);
|
||||
|
||||
final String mNamePrefix;
|
||||
|
||||
public NamingThreadFactory( String prefix )
|
||||
{
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
|
||||
mThreadGroup = (s != null)? s.getThreadGroup() :
|
||||
Thread.currentThread().getThreadGroup();
|
||||
|
||||
mNamePrefix = prefix + " pool-" +
|
||||
poolNumber.getAndIncrement() +
|
||||
"-thread-";
|
||||
}
|
||||
|
||||
public Thread newThread( Runnable runnable )
|
||||
{
|
||||
Thread thread = new Thread(mThreadGroup, runnable,
|
||||
mNamePrefix + mThreadNumber.getAndIncrement(), 0 );
|
||||
|
||||
if( thread.isDaemon() )
|
||||
{
|
||||
thread.setDaemon( false );
|
||||
}
|
||||
|
||||
if( thread.getPriority() != Thread.NORM_PRIORITY )
|
||||
{
|
||||
thread.setPriority( Thread.NORM_PRIORITY );
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
******************************************************************************/
|
||||
package controller;
|
||||
|
||||
import alias.action.AliasActionManager;
|
||||
import map.MapService;
|
||||
import playlist.PlaylistManager;
|
||||
import record.RecorderManager;
|
||||
@@ -25,6 +24,7 @@ import settings.SettingsManager;
|
||||
import source.SourceManager;
|
||||
import source.recording.RecordingSourceManager;
|
||||
import source.tuner.TunerManager;
|
||||
import audio.AudioManager;
|
||||
import controller.channel.ChannelManager;
|
||||
import eventlog.EventLogManager;
|
||||
|
||||
@@ -33,6 +33,7 @@ import eventlog.EventLogManager;
|
||||
*/
|
||||
public class ResourceManager
|
||||
{
|
||||
private AudioManager mAudioManager;
|
||||
private ChannelManager mChannelManager;
|
||||
private EventLogManager mEventLogManager;
|
||||
private PlaylistManager mPlaylistManager;
|
||||
@@ -67,13 +68,24 @@ public class ResourceManager
|
||||
* ChannelManager requires a reference to the ResourceManager so that
|
||||
* channel processing chains can access system wide resources
|
||||
*/
|
||||
mThreadPoolManager = new ThreadPoolManager();
|
||||
mAudioManager = new AudioManager( mThreadPoolManager );
|
||||
mChannelManager = new ChannelManager( this );
|
||||
mSourceManager = new SourceManager( this );
|
||||
mRecordingSourceManager = new RecordingSourceManager( this );
|
||||
mTunerManager = new TunerManager( this );
|
||||
mController = new ConfigurationControllerModel( this );
|
||||
mMapService = new MapService( this );
|
||||
mThreadPoolManager = new ThreadPoolManager();
|
||||
}
|
||||
|
||||
public AudioManager getAudioManager()
|
||||
{
|
||||
return mAudioManager;
|
||||
}
|
||||
|
||||
public void setAudioManager( AudioManager audioManager )
|
||||
{
|
||||
mAudioManager = audioManager;
|
||||
}
|
||||
|
||||
public ConfigurationControllerModel getController()
|
||||
|
||||
@@ -19,13 +19,22 @@ package controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ThreadPoolManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( ThreadPoolManager.class );
|
||||
|
||||
private int THREAD_POOL_SIZE = 2;
|
||||
private int MAX_TASK_COUNT = 24;
|
||||
|
||||
@@ -46,7 +55,8 @@ public class ThreadPoolManager
|
||||
{
|
||||
if( mExecutor == null )
|
||||
{
|
||||
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE );
|
||||
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
|
||||
new NamingThreadFactory( "sdrtrunk" ) );
|
||||
}
|
||||
|
||||
if( mTasks.size() <= MAX_TASK_COUNT )
|
||||
@@ -71,7 +81,8 @@ public class ThreadPoolManager
|
||||
{
|
||||
if( mExecutor == null )
|
||||
{
|
||||
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE );
|
||||
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
|
||||
new NamingThreadFactory( "sdrtrunk" ) );
|
||||
}
|
||||
|
||||
if( mTasks.size() <= MAX_TASK_COUNT )
|
||||
@@ -109,6 +120,7 @@ public class ThreadPoolManager
|
||||
|
||||
public enum ThreadType
|
||||
{
|
||||
AUDIO_PROCESSING,
|
||||
SOURCE_SAMPLE_PROCESSING,
|
||||
DECIMATION,
|
||||
DECODER;
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
public class ActivitySummaryFrame extends JFrame
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ActivitySummaryFrame( String summary )
|
||||
{
|
||||
this( summary, null );
|
||||
}
|
||||
|
||||
public ActivitySummaryFrame( String summary, Component displayOver )
|
||||
{
|
||||
setTitle( "Activity Summary" );
|
||||
setLocationRelativeTo( displayOver );
|
||||
setSize( 400, 400 );
|
||||
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill][]" ) );
|
||||
|
||||
JTextArea summaryText = new JTextArea( summary );
|
||||
|
||||
JScrollPane scroller = new JScrollPane( summaryText );
|
||||
scroller.setViewportView( summaryText );
|
||||
|
||||
add( scroller, "wrap" );
|
||||
|
||||
JButton close = new JButton( "Close" );
|
||||
close.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
dispose();
|
||||
}
|
||||
} );
|
||||
|
||||
add( close );
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
setVisible( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
|
||||
public interface ActivitySummaryProvider extends Listener<Message>
|
||||
{
|
||||
public String getSummary();
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import decode.DecoderType;
|
||||
|
||||
public abstract class CallEvent
|
||||
{
|
||||
protected long mEventStart = System.currentTimeMillis();
|
||||
protected long mEventEnd;
|
||||
|
||||
private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd','HHmmss" );
|
||||
|
||||
protected DecoderType mDecoderType;
|
||||
protected CallEventType mCallEventType;
|
||||
protected AliasList mAliasList;
|
||||
protected String mFromID;
|
||||
protected String mToID;
|
||||
protected String mDetails;
|
||||
|
||||
public CallEvent( DecoderType decoder,
|
||||
CallEventType callEventType,
|
||||
AliasList aliasList,
|
||||
String fromID,
|
||||
String toID,
|
||||
String details )
|
||||
{
|
||||
mDecoderType = decoder;
|
||||
mCallEventType = callEventType;
|
||||
mAliasList = aliasList;
|
||||
mFromID = fromID;
|
||||
mToID = toID;
|
||||
mDetails = details;
|
||||
}
|
||||
|
||||
public void setEnd( long end )
|
||||
{
|
||||
mEventEnd = end;
|
||||
}
|
||||
|
||||
public DecoderType getDecoderType()
|
||||
{
|
||||
return mDecoderType;
|
||||
}
|
||||
|
||||
public CallEventType getCallEventType()
|
||||
{
|
||||
return mCallEventType;
|
||||
}
|
||||
|
||||
public AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
public void setAliasList( AliasList aliasList )
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
}
|
||||
|
||||
public boolean hasAliasList()
|
||||
{
|
||||
return mAliasList != null;
|
||||
}
|
||||
|
||||
public long getEventStartTime()
|
||||
{
|
||||
return mEventStart;
|
||||
}
|
||||
|
||||
public long getEventEndTime()
|
||||
{
|
||||
return mEventEnd;
|
||||
}
|
||||
|
||||
public String getFromID()
|
||||
{
|
||||
return mFromID;
|
||||
}
|
||||
|
||||
public boolean hasFromID()
|
||||
{
|
||||
return mFromID != null;
|
||||
}
|
||||
|
||||
public void setFromID( String id )
|
||||
{
|
||||
mFromID = id;
|
||||
}
|
||||
|
||||
public String getToID()
|
||||
{
|
||||
return mToID;
|
||||
}
|
||||
|
||||
public boolean hasToID()
|
||||
{
|
||||
return mToID != null;
|
||||
}
|
||||
|
||||
public void setToID( String id )
|
||||
{
|
||||
mToID = id;
|
||||
}
|
||||
|
||||
public String getDetails()
|
||||
{
|
||||
return mDetails;
|
||||
}
|
||||
|
||||
public void setDetails( String details )
|
||||
{
|
||||
mDetails = details;
|
||||
}
|
||||
|
||||
public abstract Alias getFromIDAlias();
|
||||
|
||||
public abstract Alias getToIDAlias();
|
||||
|
||||
public abstract String getChannel();
|
||||
|
||||
public abstract long getFrequency();
|
||||
|
||||
public static String getCSVHeader()
|
||||
{
|
||||
return "START_DATE,START_TIME,END_DATE,END_TIME,"
|
||||
+ "DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,"
|
||||
+ "CHANNEL,FREQUENCY,DETAILS";
|
||||
}
|
||||
|
||||
public String toCSV()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "'" );
|
||||
sb.append( mSDF.format( new Date( getEventStartTime() ) ) );
|
||||
sb.append( "','" );
|
||||
if( mEventEnd != 0 )
|
||||
{
|
||||
sb.append( mSDF.format( new Date( getEventEndTime() ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "','" );
|
||||
}
|
||||
sb.append( "','" );
|
||||
sb.append( getDecoderType().toString() );
|
||||
sb.append( "','" );
|
||||
sb.append( getCallEventType().toString() );
|
||||
sb.append( "'," );
|
||||
if( getFromID() != null )
|
||||
{
|
||||
sb.append( "'" );
|
||||
sb.append( getFromID() );
|
||||
sb.append( "'" );
|
||||
}
|
||||
sb.append( "," );
|
||||
|
||||
Alias fromAlias = getFromIDAlias();
|
||||
|
||||
if( fromAlias != null )
|
||||
{
|
||||
sb.append( "'" );
|
||||
sb.append( fromAlias.getName() );
|
||||
sb.append( "'" );
|
||||
}
|
||||
sb.append( "," );
|
||||
|
||||
if( getToID() != null )
|
||||
{
|
||||
sb.append( "'" );
|
||||
sb.append( getToID() );
|
||||
sb.append( "'" );
|
||||
}
|
||||
sb.append( "," );
|
||||
|
||||
Alias toAlias = getToIDAlias();
|
||||
|
||||
if( toAlias != null )
|
||||
{
|
||||
sb.append( "'" );
|
||||
sb.append( toAlias.getName() );
|
||||
sb.append( "'" );
|
||||
}
|
||||
sb.append( ",'" );
|
||||
sb.append( getChannel() );
|
||||
sb.append( "','" );
|
||||
sb.append( getFrequency() );
|
||||
sb.append( "'," );
|
||||
if( getDetails() != null )
|
||||
{
|
||||
sb.append( "'" );
|
||||
sb.append( getDetails() );
|
||||
sb.append( "'" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public enum CallEventType
|
||||
{
|
||||
CALL( "Call" ),
|
||||
CALL_ALERT( "Call Alert" ),
|
||||
CALL_END( "Call End" ),
|
||||
CALL_DETECT( "Call Detect" ),
|
||||
CALL_UNIQUE_ID( "UID Call" ),
|
||||
CALL_NO_TUNER( "Call - No Tuner" ),
|
||||
CALL_TIMEOUT( "Call Timeout" ),
|
||||
COMMAND( "Command" ),
|
||||
DATA_CALL( "Data Call" ),
|
||||
DEREGISTER( "Deregister" ),
|
||||
EMERGENCY( "EMERGENCY" ),
|
||||
ENCRYPTED_CALL( "Encrypted Call" ),
|
||||
FUNCTION( "Function" ),
|
||||
GPS( "GPS" ),
|
||||
ID_ANI( "ANI" ),
|
||||
ID_UNIQUE( "Unique ID" ),
|
||||
PAGE( "Page" ),
|
||||
QUERY( "Query" ),
|
||||
REGISTER( "Register" ),
|
||||
REGISTER_ESN( "ESN" ),
|
||||
RESPONSE( "Response" ),
|
||||
SDM( "Short Data Message" ),
|
||||
STATUS( "Status" ),
|
||||
TELEPHONE_CALL( "Telephone Call" ),
|
||||
UNKNOWN( "Unknown" ),
|
||||
INVALID( "Invalid" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private CallEventType( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
|
||||
import settings.SettingsManager;
|
||||
import alias.Alias;
|
||||
|
||||
/**
|
||||
* Custom renderer for JTable cells that contain Alias objects. Renders the
|
||||
* alias name, and if there is an icon name, attempts to get that icon from
|
||||
* the settings manager and render it.
|
||||
*/
|
||||
public class CallEventAliasCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private SettingsManager mSettingsManager;
|
||||
|
||||
public CallEventAliasCellRenderer( SettingsManager settingsManager )
|
||||
{
|
||||
super();
|
||||
mSettingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public void setValue( Object obj )
|
||||
{
|
||||
if( obj != null && obj instanceof Alias )
|
||||
{
|
||||
Alias alias = (Alias)obj;
|
||||
|
||||
setText( alias.getName() );
|
||||
|
||||
ImageIcon icon = getIcon( alias, 12 );
|
||||
|
||||
if( icon != null )
|
||||
{
|
||||
setIcon( icon );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setText( " " );
|
||||
setIcon( null );
|
||||
}
|
||||
}
|
||||
|
||||
private ImageIcon getIcon( Alias alias, int height )
|
||||
{
|
||||
if( mSettingsManager != null && alias != null )
|
||||
{
|
||||
return mSettingsManager.getImageIcon( alias.getIconName(), height );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import controller.activity.CallEvent.CallEventType;
|
||||
|
||||
public class CallEventModel extends AbstractTableModel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( CallEventModel.class );
|
||||
|
||||
private static DecimalFormat mFrequencyFormatter =
|
||||
new DecimalFormat( "0.000000" );
|
||||
|
||||
private static final int DEFAULT_ICON_HEIGHT = 14;
|
||||
|
||||
public static final int TIME = 0;
|
||||
public static final int EVENT = 1;
|
||||
public static final int FROM_ID = 2;
|
||||
public static final int FROM_ALIAS = 3;
|
||||
public static final int TO_ID = 4;
|
||||
public static final int TO_ALIAS = 5;
|
||||
public static final int CHANNEL = 6;
|
||||
public static final int FREQUENCY = 7;
|
||||
public static final int DETAILS = 8;
|
||||
|
||||
protected int mMaxMessages = 500;
|
||||
|
||||
protected LinkedList<CallEvent> mEvents = new LinkedList<CallEvent>();
|
||||
|
||||
protected String[] mHeaders = new String[] { "Time",
|
||||
"Event",
|
||||
"From",
|
||||
"Alias",
|
||||
"To",
|
||||
"Alias",
|
||||
"Channel",
|
||||
"Frequency",
|
||||
"Details" };
|
||||
|
||||
private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" );
|
||||
|
||||
private Broadcaster<CallEvent> mBroadcaster = new Broadcaster<CallEvent>();
|
||||
private boolean mNewMessagesFirst = true;
|
||||
|
||||
public CallEventModel()
|
||||
{
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
synchronized( mEvents )
|
||||
{
|
||||
mEvents.clear();
|
||||
}
|
||||
|
||||
mBroadcaster.dispose();
|
||||
}
|
||||
|
||||
public void flush()
|
||||
{
|
||||
/* Flush all events to listeners ( normally the call event logger ) */
|
||||
synchronized( mEvents )
|
||||
{
|
||||
for( CallEvent event: mEvents )
|
||||
{
|
||||
mBroadcaster.receive( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount( int count )
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
public void add( final CallEvent event )
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized ( mEvents )
|
||||
{
|
||||
mEvents.addFirst( event );
|
||||
}
|
||||
|
||||
fireTableRowsInserted( 0, 0 );
|
||||
|
||||
prune();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove( final CallEvent event )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
synchronized ( mEvents )
|
||||
{
|
||||
final int row = mEvents.indexOf( event );
|
||||
|
||||
mEvents.remove( event );
|
||||
|
||||
if( row != -1 )
|
||||
{
|
||||
fireTableRowsDeleted( row, row );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public int indexOf( CallEvent event )
|
||||
{
|
||||
return mEvents.indexOf( event );
|
||||
}
|
||||
|
||||
public void setEnd( final CallEvent event )
|
||||
{
|
||||
try
|
||||
{
|
||||
EventQueue.invokeAndWait( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if( event != null )
|
||||
{
|
||||
event.setEnd( System.currentTimeMillis() );
|
||||
|
||||
synchronized ( mEvents )
|
||||
{
|
||||
int row = indexOf( event );
|
||||
|
||||
if( row != -1 )
|
||||
{
|
||||
fireTableCellUpdated( row, TIME );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "CallEventModel - couldn't log end time - event was null" );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFromID( final CallEvent event, final String from )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if( event != null )
|
||||
{
|
||||
event.setFromID( from );
|
||||
|
||||
synchronized ( mEvents )
|
||||
{
|
||||
int row = indexOf( event );
|
||||
|
||||
if( row != -1 )
|
||||
{
|
||||
fireTableCellUpdated( row, FROM_ID );
|
||||
fireTableCellUpdated( row, FROM_ALIAS );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "CallEventModel - tried to set from ID on "
|
||||
+ "call event - couldn't find index of event" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
synchronized( mEvents )
|
||||
{
|
||||
while( mEvents.size() > mMaxMessages )
|
||||
{
|
||||
CallEvent event = mEvents.removeLast();
|
||||
|
||||
mBroadcaster.receive( event );
|
||||
|
||||
fireTableRowsDeleted( mEvents.size(), mEvents.size() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mEvents.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return mHeaders.length;
|
||||
}
|
||||
|
||||
public String getColumnName( int column )
|
||||
{
|
||||
return mHeaders[ column ];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex )
|
||||
{
|
||||
synchronized( mEvents )
|
||||
{
|
||||
switch( columnIndex )
|
||||
{
|
||||
case TIME:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( mSDFTime.format(
|
||||
mEvents.get( rowIndex ).getEventStartTime() ) );
|
||||
|
||||
if( mEvents.get( rowIndex ).getEventEndTime() != 0 )
|
||||
{
|
||||
sb.append( " - " );
|
||||
sb.append( mSDFTime.format(
|
||||
mEvents.get( rowIndex ).getEventEndTime() ) );
|
||||
}
|
||||
else if( mEvents.get( rowIndex )
|
||||
.getCallEventType() == CallEventType.CALL )
|
||||
{
|
||||
sb.append( " - In Progress" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
case EVENT:
|
||||
return mEvents.get( rowIndex ).getCallEventType();
|
||||
case FROM_ID:
|
||||
return mEvents.get( rowIndex ).getFromID();
|
||||
case FROM_ALIAS:
|
||||
return mEvents.get( rowIndex ).getFromIDAlias();
|
||||
case TO_ID:
|
||||
return mEvents.get( rowIndex ).getToID();
|
||||
case TO_ALIAS:
|
||||
return mEvents.get( rowIndex ).getToIDAlias();
|
||||
case CHANNEL:
|
||||
return mEvents.get( rowIndex ).getChannel();
|
||||
case FREQUENCY:
|
||||
long frequency = mEvents.get( rowIndex ).getFrequency();
|
||||
|
||||
if( frequency != 0 )
|
||||
{
|
||||
return mFrequencyFormatter.format( (double)frequency / 1E6d );
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case DETAILS:
|
||||
return mEvents.get( rowIndex ).getDetails();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.channel.ChannelEventListener;
|
||||
|
||||
public class CallEventPanel extends JPanel implements ChannelEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JScrollPane mEmptyScroller;
|
||||
private Channel mDisplayedChannel;
|
||||
|
||||
public CallEventPanel()
|
||||
{
|
||||
setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") );
|
||||
|
||||
JTable table = new JTable( new CallEventModel() );
|
||||
table.setAutoCreateRowSorter( true );
|
||||
table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
|
||||
mEmptyScroller = new JScrollPane( table );
|
||||
|
||||
add( mEmptyScroller );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelChanged( ChannelEvent event )
|
||||
{
|
||||
if( event.getEvent() == Event.CHANGE_SELECTED &&
|
||||
event.getChannel().isSelected() )
|
||||
{
|
||||
if( mDisplayedChannel == null ||
|
||||
( mDisplayedChannel != null &&
|
||||
mDisplayedChannel != event.getChannel() ) )
|
||||
{
|
||||
removeAll();
|
||||
|
||||
JScrollPane scroller = new JScrollPane(
|
||||
event.getChannel().getProcessingChain().getChannelState()
|
||||
.getCallEventTable() );
|
||||
|
||||
add( scroller );
|
||||
|
||||
mDisplayedChannel = event.getChannel();
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
else if( event.getEvent() == Event.PROCESSING_STOPPED ||
|
||||
event.getEvent() == Event.CHANNEL_DELETED )
|
||||
{
|
||||
if( mDisplayedChannel != null &&
|
||||
mDisplayedChannel == event.getChannel() )
|
||||
{
|
||||
mDisplayedChannel = null;
|
||||
removeAll();
|
||||
add( mEmptyScroller );
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
import filter.FilterSet;
|
||||
|
||||
public class MessageActivityModel extends AbstractTableModel
|
||||
implements Listener<Message>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int sTIME = 0;
|
||||
private static final int sPROTOCOL = 1;
|
||||
private static final int sERROR_STATUS = 2;
|
||||
private static final int sMESSAGE = 3;
|
||||
private static final int sMESSAGE_BITS = 4;
|
||||
|
||||
protected int mMaxMessages = 500;
|
||||
protected LinkedList<Message> mMessages = new LinkedList<Message>();
|
||||
|
||||
protected int[] mColumnWidths = { 110, 110, 110, -1, -1 };
|
||||
|
||||
protected String[] mHeaders = new String[] { "Time",
|
||||
"Protocol",
|
||||
"Error Check",
|
||||
"Message",
|
||||
"Binary" };
|
||||
|
||||
private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" );
|
||||
|
||||
private boolean mNewMessagesFirst = true;
|
||||
|
||||
private FilterSet<Message> mMessageFilter;
|
||||
|
||||
public MessageActivityModel( FilterSet<Message> messageFilter )
|
||||
{
|
||||
mMessageFilter = messageFilter;
|
||||
}
|
||||
|
||||
public MessageActivityModel()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all messages from history
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
int messageCount = mMessages.size();
|
||||
|
||||
mMessages.clear();
|
||||
|
||||
fireTableRowsDeleted( 0, messageCount - 1 );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public FilterSet<Message> getMessageFilter()
|
||||
{
|
||||
return mMessageFilter;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mMessages.clear();
|
||||
}
|
||||
|
||||
public int[] getColumnWidths()
|
||||
{
|
||||
return mColumnWidths;
|
||||
}
|
||||
|
||||
public void setColumnWidths( int[] widths )
|
||||
{
|
||||
if( widths.length != 5 )
|
||||
{
|
||||
throw new IllegalArgumentException( "MessageActivityModel - "
|
||||
+ "column widths array should have 5 elements" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mColumnWidths = widths;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount( int count )
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
public void receive( final Message message )
|
||||
{
|
||||
if( mMessageFilter.passes( message ) )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mMessages.addFirst( message );
|
||||
|
||||
MessageActivityModel.this.fireTableRowsInserted( 0, 0 );
|
||||
|
||||
prune();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
while( mMessages.size() > mMaxMessages )
|
||||
{
|
||||
mMessages.removeLast();
|
||||
|
||||
super.fireTableRowsDeleted( mMessages.size() - 1, mMessages.size() - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mMessages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return mHeaders.length;
|
||||
}
|
||||
|
||||
public String getColumnName( int column )
|
||||
{
|
||||
return mHeaders[ column ];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex )
|
||||
{
|
||||
Message message = mMessages.get( rowIndex );
|
||||
|
||||
switch( columnIndex )
|
||||
{
|
||||
case sTIME:
|
||||
return mSDFTime.format( message.getDateReceived() );
|
||||
case sPROTOCOL:
|
||||
return message.getProtocol();
|
||||
case sERROR_STATUS:
|
||||
return message.getErrorStatus();
|
||||
case sMESSAGE:
|
||||
return message.getMessage();
|
||||
case sMESSAGE_BITS:
|
||||
return message.getBinaryMessage();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,316 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import filter.FilterEditorPanel;
|
||||
import filter.FilterSet;
|
||||
import gui.SDRTrunk;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import message.Message;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.jidesoft.swing.JideButton;
|
||||
import com.jidesoft.swing.JideSplitButton;
|
||||
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.channel.ChannelEventListener;
|
||||
|
||||
public class MessageActivityPanel extends JPanel implements ChannelEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class );
|
||||
|
||||
private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel();
|
||||
|
||||
private JTable mMessageTable;
|
||||
|
||||
private Channel mDisplayedChannel;
|
||||
|
||||
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
|
||||
|
||||
public MessageActivityPanel()
|
||||
{
|
||||
setLayout( new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]") );
|
||||
|
||||
add( mManagementPanel, "span,growx" );
|
||||
|
||||
mMessageTable = new JTable( EMPTY_MODEL );
|
||||
|
||||
add( new JScrollPane( mMessageTable ), "span,grow" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelChanged( ChannelEvent event )
|
||||
{
|
||||
boolean changed = false;
|
||||
|
||||
if( event.getEvent() == Event.CHANGE_SELECTED &&
|
||||
event.getChannel().isSelected() )
|
||||
{
|
||||
if( mDisplayedChannel == null ||
|
||||
( mDisplayedChannel != null &&
|
||||
mDisplayedChannel != event.getChannel() ) )
|
||||
{
|
||||
mDisplayedChannel = event.getChannel();
|
||||
|
||||
mMessageTable.setModel( event.getChannel().getProcessingChain()
|
||||
.getChannelState().getMessageActivityModel() );
|
||||
|
||||
if( mDisplayedChannel != null )
|
||||
{
|
||||
mManagementPanel.enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
mManagementPanel.disable();
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if( event.getEvent() == Event.PROCESSING_STOPPED ||
|
||||
event.getEvent() == Event.CHANNEL_DELETED )
|
||||
{
|
||||
if( mDisplayedChannel != null &&
|
||||
mDisplayedChannel == event.getChannel() )
|
||||
{
|
||||
mDisplayedChannel = null;
|
||||
|
||||
mMessageTable.setModel( EMPTY_MODEL );
|
||||
|
||||
mManagementPanel.disable();
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( changed )
|
||||
{
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageManagementPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
|
||||
private MessageFilterButton mFilterButton = new MessageFilterButton();
|
||||
|
||||
public MessageManagementPanel()
|
||||
{
|
||||
setLayout( new MigLayout("insets 2 2 5 5", "[]5[left,grow]", "") );
|
||||
|
||||
disable();
|
||||
|
||||
add( mFilterButton );
|
||||
add( mHistoryButton );
|
||||
}
|
||||
|
||||
public void enable()
|
||||
{
|
||||
mHistoryButton.setEnabled( true );
|
||||
mFilterButton.setEnabled( true );
|
||||
}
|
||||
|
||||
public void disable()
|
||||
{
|
||||
mHistoryButton.setEnabled( false );
|
||||
mFilterButton.setEnabled( false );
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageHistoryButton extends JideSplitButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MessageHistoryButton()
|
||||
{
|
||||
super( "Clear" );
|
||||
|
||||
JPanel historyPanel = new JPanel();
|
||||
|
||||
historyPanel.add( new JLabel( "Message History:" ) );
|
||||
|
||||
final JSlider slider = new JSlider();
|
||||
slider.setMinimum( 0 );
|
||||
slider.setMaximum( 2000 );
|
||||
slider.setMajorTickSpacing( 500 );
|
||||
slider.setPaintTicks( true );
|
||||
slider.setPaintLabels( true );
|
||||
|
||||
slider.addMouseListener( new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent arg0 )
|
||||
{
|
||||
if( SwingUtilities.isLeftMouseButton( arg0 ) &&
|
||||
arg0.getClickCount() == 2 )
|
||||
{
|
||||
slider.setValue( 500 ); //default
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseEntered( MouseEvent arg0 ) {}
|
||||
public void mouseExited( MouseEvent arg0 ) {}
|
||||
public void mousePressed( MouseEvent arg0 ) {}
|
||||
public void mouseReleased( MouseEvent arg0 ) {}
|
||||
} );
|
||||
|
||||
if( mDisplayedChannel != null )
|
||||
{
|
||||
slider.setValue( mDisplayedChannel.getProcessingChain()
|
||||
.getChannelState().getMessageActivityModel()
|
||||
.getMaxMessageCount() );
|
||||
}
|
||||
else
|
||||
{
|
||||
slider.setValue( 500 );
|
||||
}
|
||||
|
||||
slider.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent arg0 )
|
||||
{
|
||||
if( mDisplayedChannel != null )
|
||||
{
|
||||
mDisplayedChannel.getProcessingChain().getChannelState()
|
||||
.getMessageActivityModel()
|
||||
.setMaxMessageCount( slider.getValue() );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
historyPanel.add( slider );
|
||||
|
||||
add( historyPanel );
|
||||
|
||||
/* Clear messages */
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
if( mDisplayedChannel != null )
|
||||
{
|
||||
mDisplayedChannel.getProcessingChain().getChannelState()
|
||||
.getMessageActivityModel().clear();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageFilterButton extends JideButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MessageFilterButton()
|
||||
{
|
||||
super( "Filter" );
|
||||
|
||||
addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent arg0 )
|
||||
{
|
||||
if( mDisplayedChannel == null )
|
||||
{
|
||||
JOptionPane.showMessageDialog( MessageFilterButton.this,
|
||||
"Please enable and select a decoding channel to "
|
||||
+ "adjust the channel's message display filter" );
|
||||
}
|
||||
else
|
||||
{
|
||||
final JFrame editor = new JFrame();
|
||||
|
||||
editor.setTitle( "Message Filter Editor" );
|
||||
editor.setSize( 600, 400 );
|
||||
editor.setLocationRelativeTo( MessageFilterButton.this );
|
||||
editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
|
||||
editor.setLayout( new MigLayout( "", "[grow,fill]",
|
||||
"[grow,fill][][]" ) );
|
||||
|
||||
@SuppressWarnings( "unchecked" )
|
||||
FilterSet<Message> filter = (FilterSet<Message>)
|
||||
mDisplayedChannel
|
||||
.getProcessingChain().getChannelState()
|
||||
.getMessageActivityModel().getMessageFilter();
|
||||
|
||||
FilterEditorPanel<Message> panel =
|
||||
new FilterEditorPanel<Message>( filter );
|
||||
|
||||
JScrollPane scroller = new JScrollPane( panel );
|
||||
scroller.setViewportView( panel );
|
||||
|
||||
editor.add( scroller, "wrap" );
|
||||
|
||||
editor.add( new JLabel( "Right-click to select/deselect "
|
||||
+ "all nodes" ), "wrap" );
|
||||
|
||||
JButton close = new JButton( "Close" );
|
||||
close.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
editor.dispose();
|
||||
}
|
||||
} );
|
||||
|
||||
editor.add( close );
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
editor.setVisible( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.activity;
|
||||
|
||||
import alias.Alias;
|
||||
|
||||
/**
|
||||
* Getter methods to support the Message Activity Table/Model
|
||||
* @author denny
|
||||
*
|
||||
*/
|
||||
public interface MessageDetailsProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if the message is valid and has passed crc/integrity checks
|
||||
*/
|
||||
public abstract boolean isValid();
|
||||
|
||||
/**
|
||||
* Status of the CRC check of the message
|
||||
*/
|
||||
public abstract String getErrorStatus();
|
||||
|
||||
/**
|
||||
* Parsed Message
|
||||
* @return
|
||||
*/
|
||||
public abstract String getMessage();
|
||||
|
||||
/**
|
||||
* Raw ( 0 & 1 ) message bits
|
||||
*/
|
||||
public abstract String getBinaryMessage();
|
||||
|
||||
|
||||
/**
|
||||
* Decoded protocol
|
||||
*/
|
||||
public abstract String getProtocol();
|
||||
|
||||
/**
|
||||
* Event - call, data, idle, etc.
|
||||
*/
|
||||
public abstract String getEventType();
|
||||
|
||||
/**
|
||||
* Formatted from identifier
|
||||
*/
|
||||
public abstract String getFromID();
|
||||
|
||||
/**
|
||||
* From identifier alias (from AliasManager)
|
||||
*/
|
||||
public abstract Alias getFromIDAlias();
|
||||
|
||||
/**
|
||||
* Formatted to identifier
|
||||
*/
|
||||
public abstract String getToID();
|
||||
|
||||
/**
|
||||
* To identifier alias (from AliasManager)
|
||||
* @return
|
||||
*/
|
||||
public abstract Alias getToIDAlias();
|
||||
|
||||
}
|
||||
@@ -17,17 +17,13 @@
|
||||
******************************************************************************/
|
||||
package controller.channel;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
@@ -35,12 +31,23 @@ import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
import message.Message;
|
||||
import module.Module;
|
||||
import module.ProcessingChain;
|
||||
import module.decode.DecoderFactory;
|
||||
import module.decode.config.AuxDecodeConfiguration;
|
||||
import module.decode.config.DecodeConfigFactory;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEventModel;
|
||||
import module.decode.event.MessageActivityModel;
|
||||
import module.decode.state.ChannelState;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import record.config.RecordConfiguration;
|
||||
import sample.Listener;
|
||||
import source.Source;
|
||||
import source.SourceException;
|
||||
import source.SourceType;
|
||||
import source.config.SourceConfigFactory;
|
||||
import source.config.SourceConfigRecording;
|
||||
@@ -48,19 +55,15 @@ import source.config.SourceConfigTuner;
|
||||
import source.config.SourceConfiguration;
|
||||
import source.tuner.TunerChannel;
|
||||
import source.tuner.TunerChannel.Type;
|
||||
import source.tuner.frequency.AutomaticFrequencyControl;
|
||||
import alias.AliasList;
|
||||
import controller.ResourceManager;
|
||||
import controller.activity.ActivitySummaryFrame;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.config.Configuration;
|
||||
import controller.site.Site;
|
||||
import controller.state.ChannelState;
|
||||
import controller.state.ChannelState.State;
|
||||
import controller.system.System;
|
||||
import decode.config.AuxDecodeConfiguration;
|
||||
import decode.config.DecodeConfigFactory;
|
||||
import decode.config.DecodeConfiguration;
|
||||
import eventlog.MessageEventLogger;
|
||||
import eventlog.config.EventLogConfiguration;
|
||||
import filter.FilterSet;
|
||||
|
||||
@XmlSeeAlso( { Configuration.class } )
|
||||
@XmlRootElement( name = "channel" )
|
||||
@@ -68,15 +71,16 @@ public class Channel extends Configuration
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( Channel.class );
|
||||
|
||||
private static final boolean ENABLED = true;
|
||||
private static final boolean DISABLED = false;
|
||||
private static final boolean BROADCAST_CHANGE = true;
|
||||
|
||||
public enum ChannelType
|
||||
{
|
||||
STANDARD,
|
||||
TRAFFIC
|
||||
};
|
||||
|
||||
private static final boolean ENABLED = true;
|
||||
private static final boolean DISABLED = false;
|
||||
private static final boolean BROADCAST_CHANGE = true;
|
||||
|
||||
private CopyOnWriteArrayList<ChannelEventListener> mChannelListeners =
|
||||
new CopyOnWriteArrayList<ChannelEventListener>();
|
||||
@@ -84,8 +88,7 @@ public class Channel extends Configuration
|
||||
private CopyOnWriteArrayList<Listener<Message>> mMessageListeners =
|
||||
new CopyOnWriteArrayList<Listener<Message>>();
|
||||
|
||||
private HashMap<Integer,Channel> mTrafficChannels =
|
||||
new HashMap<Integer,Channel>();
|
||||
private Map<String,Channel> mTrafficChannels = new HashMap<>();
|
||||
|
||||
private DecodeConfiguration mDecodeConfiguration =
|
||||
DecodeConfigFactory.getDefaultDecodeConfiguration();
|
||||
@@ -105,7 +108,15 @@ public class Channel extends Configuration
|
||||
private boolean mEnabled;
|
||||
private boolean mSelected;
|
||||
private ChannelType mChannelType;
|
||||
|
||||
private CallEventModel mCallEventModel;
|
||||
private MessageActivityModel mMessageActivityModel;
|
||||
private MessageEventLogger mEventLogger;
|
||||
private ProcessingChain mProcessingChain;
|
||||
|
||||
private ChannelState mChannelState;
|
||||
private long mTrafficChannelTimeout =
|
||||
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS;
|
||||
|
||||
/**
|
||||
* Constructs a new standard channel with a default name of "New Channel"
|
||||
@@ -135,34 +146,46 @@ public class Channel extends Configuration
|
||||
mChannelType = channelType;
|
||||
}
|
||||
|
||||
public ChannelType getChannelType()
|
||||
{
|
||||
return mChannelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the channel has Automatic or Direct Frequency Control
|
||||
*/
|
||||
public boolean hasFrequencyControl()
|
||||
{
|
||||
return mProcessingChain != null &&
|
||||
mProcessingChain.getDecoder().hasFrequencyCorrectionControl();
|
||||
mProcessingChain.hasFrequencyCorrectionControl();
|
||||
}
|
||||
|
||||
public long getFrequencyCorrection()
|
||||
{
|
||||
if( mProcessingChain != null )
|
||||
if( hasFrequencyControl() )
|
||||
{
|
||||
return mProcessingChain.getDecoder().getFrequencyCorrection();
|
||||
return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel state traffic channel call end timer to the specified
|
||||
* value.
|
||||
*/
|
||||
public void setTrafficChannelTimeout( long milliseconds )
|
||||
{
|
||||
mTrafficChannelTimeout = milliseconds;
|
||||
|
||||
if( mChannelState != null )
|
||||
{
|
||||
mChannelState.setTrafficChannelTimeout( milliseconds );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the resource manager responsible for this channel and registers
|
||||
* the channel manager as a listener to this channel. Fires channel add
|
||||
* channel event to the newly added channel manager channel listener.
|
||||
*
|
||||
* Note: resource manager instance is applied after object construction
|
||||
* since we construct the channel objects from the xml playlist
|
||||
*/
|
||||
public void setResourceManager( ResourceManager resourceManager )
|
||||
{
|
||||
@@ -170,7 +193,7 @@ public class Channel extends Configuration
|
||||
|
||||
/* Add system-wide channel listeners onto this channel */
|
||||
addListeners( mResourceManager.getChannelManager().getChannelListeners() );
|
||||
|
||||
|
||||
/* If we're enabled, fire enable changed event to get processing started */
|
||||
if( mEnabled )
|
||||
{
|
||||
@@ -200,7 +223,7 @@ public class Channel extends Configuration
|
||||
*/
|
||||
public boolean isProcessing()
|
||||
{
|
||||
return mProcessingChain != null && mProcessingChain.isProcessing();
|
||||
return mProcessingChain != null && mProcessingChain.processing();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,100 +239,51 @@ public class Channel extends Configuration
|
||||
{
|
||||
mSelected = selected;
|
||||
|
||||
if( mProcessingChain != null &&
|
||||
mProcessingChain.isProcessing() &&
|
||||
mProcessingChain.getAudioOutput() != null )
|
||||
{
|
||||
mProcessingChain.getAudioOutput().setAudioPlaybackEnabled( selected );
|
||||
}
|
||||
|
||||
fireChannelEvent( Event.CHANGE_SELECTED );
|
||||
}
|
||||
|
||||
public JMenu getContextMenu()
|
||||
@XmlTransient
|
||||
public CallEventModel getCallEventModel()
|
||||
{
|
||||
JMenu menu = new JMenu( mSite + "-" + mName );
|
||||
|
||||
if( mEnabled )
|
||||
{
|
||||
JMenuItem disable = new JMenuItem( "Disable" );
|
||||
disable.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setEnabled( DISABLED, BROADCAST_CHANGE );
|
||||
getResourceManager().getPlaylistManager().save();
|
||||
}
|
||||
} );
|
||||
|
||||
menu.add( disable );
|
||||
|
||||
menu.add( new JSeparator() );
|
||||
|
||||
JMenuItem actySummaryItem =
|
||||
new JMenuItem( "Activity Summary" );
|
||||
|
||||
actySummaryItem.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
if( mResourceManager != null )
|
||||
{
|
||||
ProcessingChain chain = getProcessingChain();
|
||||
|
||||
if( chain != null )
|
||||
{
|
||||
ChannelState state = chain.getChannelState();
|
||||
|
||||
if( state != null )
|
||||
{
|
||||
String summary = state.getActivitySummary();
|
||||
|
||||
new ActivitySummaryFrame( summary,
|
||||
mResourceManager.getController().getTree() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
menu.add( actySummaryItem );
|
||||
}
|
||||
else
|
||||
{
|
||||
JMenuItem enable = new JMenuItem( "Enable" );
|
||||
enable.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
setEnabled( ENABLED, BROADCAST_CHANGE );
|
||||
getResourceManager().getPlaylistManager().save(); }
|
||||
} );
|
||||
|
||||
menu.add( enable );
|
||||
}
|
||||
|
||||
menu.add( new JSeparator() );
|
||||
|
||||
JMenuItem deleteItem = new JMenuItem( "Delete" );
|
||||
deleteItem.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
dispose();
|
||||
getResourceManager().getPlaylistManager().save(); }
|
||||
} );
|
||||
|
||||
menu.add( deleteItem );
|
||||
|
||||
|
||||
return menu;
|
||||
return mCallEventModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MessageEventLogger for this processing chain
|
||||
*/
|
||||
@XmlTransient
|
||||
public MessageEventLogger getEventLogger()
|
||||
{
|
||||
return mEventLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the MessageEventLogger for this processing chain
|
||||
*/
|
||||
public void setEventLogger( MessageEventLogger eventLogger )
|
||||
{
|
||||
mEventLogger = eventLogger;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public MessageActivityModel getMessageActivityModel()
|
||||
{
|
||||
return mMessageActivityModel;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public ChannelState getChannelState()
|
||||
{
|
||||
return mChannelState;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public ChannelType getChannelType()
|
||||
{
|
||||
return mChannelType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Orderly shutdown method when this channel is going to be deleted.
|
||||
*/
|
||||
@@ -393,6 +367,11 @@ public class Channel extends Configuration
|
||||
return mSystem;
|
||||
}
|
||||
|
||||
public boolean hasSystem()
|
||||
{
|
||||
return mSystem != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owning system for this channel and optionally broadcasts a
|
||||
* system change channel event.
|
||||
@@ -415,6 +394,11 @@ public class Channel extends Configuration
|
||||
return mSite;
|
||||
}
|
||||
|
||||
public boolean hasSite()
|
||||
{
|
||||
return mSite != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owning site for this channel and optionally broadcasts a site
|
||||
* change channel event.
|
||||
@@ -468,7 +452,14 @@ public class Channel extends Configuration
|
||||
|
||||
if( fireChannelEvent )
|
||||
{
|
||||
fireChannelEvent( Event.CHANGE_ENABLED );
|
||||
if( enabled )
|
||||
{
|
||||
fireChannelEvent( Event.CHANNEL_ENABLED );
|
||||
}
|
||||
else
|
||||
{
|
||||
fireChannelEvent( Event.CHANNEL_DISABLED );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,11 +513,6 @@ public class Channel extends Configuration
|
||||
public void setDecodeConfiguration( DecodeConfiguration config )
|
||||
{
|
||||
mDecodeConfiguration = config;
|
||||
|
||||
if( mProcessingChain != null && mProcessingChain.isRunning() )
|
||||
{
|
||||
mProcessingChain.updateDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -544,11 +530,6 @@ public class Channel extends Configuration
|
||||
public void setAuxDecodeConfiguration( AuxDecodeConfiguration config )
|
||||
{
|
||||
mAuxDecodeConfiguration = config;
|
||||
|
||||
if( mProcessingChain != null && mProcessingChain.isRunning() )
|
||||
{
|
||||
mProcessingChain.updateDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,11 +562,6 @@ public class Channel extends Configuration
|
||||
public void setSourceConfiguration( SourceConfiguration config )
|
||||
{
|
||||
mSourceConfiguration = config;
|
||||
|
||||
if( mProcessingChain != null && mProcessingChain.isRunning() )
|
||||
{
|
||||
mProcessingChain.updateSource();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -618,11 +594,6 @@ public class Channel extends Configuration
|
||||
public void setEventLogConfiguration( EventLogConfiguration config )
|
||||
{
|
||||
mEventLogConfiguration = config;
|
||||
|
||||
if( mProcessingChain != null && mProcessingChain.isRunning() )
|
||||
{
|
||||
mProcessingChain.updateEventLogging();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,11 +626,6 @@ public class Channel extends Configuration
|
||||
public void setRecordConfiguration( RecordConfiguration config )
|
||||
{
|
||||
mRecordConfiguration = config;
|
||||
|
||||
if( mProcessingChain != null && mProcessingChain.isRunning() )
|
||||
{
|
||||
mProcessingChain.updateRecording();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -730,38 +696,124 @@ public class Channel extends Configuration
|
||||
*/
|
||||
private void start()
|
||||
{
|
||||
if( mEnabled && mProcessingChain == null && mResourceManager != null )
|
||||
if( mEnabled && mResourceManager != null )
|
||||
{
|
||||
mProcessingChain = new ProcessingChain( this, mResourceManager );
|
||||
setupChannelState();
|
||||
|
||||
try
|
||||
{
|
||||
setup();
|
||||
|
||||
mProcessingChain.start();
|
||||
|
||||
fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED );
|
||||
}
|
||||
catch( SourceException se )
|
||||
{
|
||||
mLog.error( "Error obtaining source for channel" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setupChannelState()
|
||||
{
|
||||
if( mChannelState == null )
|
||||
{
|
||||
mChannelState = new ChannelState( mResourceManager
|
||||
.getThreadPoolManager(), mChannelType );
|
||||
|
||||
mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the channel and prepares to commense processing. Sets up the
|
||||
* processing chain and requests a source.
|
||||
*
|
||||
* @throws SourceException - if no source is available or if there is an
|
||||
* error obtaining a source
|
||||
*/
|
||||
public void setup() throws SourceException
|
||||
{
|
||||
setupProcessingChain();
|
||||
|
||||
setupSource();
|
||||
|
||||
//TODO: Add logging
|
||||
//TODO: Add recorder(s)
|
||||
}
|
||||
|
||||
private void setupProcessingChain()
|
||||
{
|
||||
if( mProcessingChain == null )
|
||||
{
|
||||
mProcessingChain = new ProcessingChain(
|
||||
mResourceManager.getThreadPoolManager() );
|
||||
|
||||
/* Get the optional alias list for the decode modules to use */
|
||||
AliasList aliasList = mResourceManager.getPlaylistManager()
|
||||
.getPlayist().getAliasDirectory().getAliasList( mAliasListName );
|
||||
|
||||
/* Add the channel state as a module */
|
||||
mProcessingChain.addModule( mChannelState );
|
||||
|
||||
/* Processing Modules */
|
||||
List<Module> modules = DecoderFactory.getModules( mChannelType,
|
||||
mResourceManager, mDecodeConfiguration, mAuxDecodeConfiguration,
|
||||
aliasList );
|
||||
mProcessingChain.addModules( modules );
|
||||
|
||||
/* Get message filters for the set of processing modules */
|
||||
FilterSet messageFilter = DecoderFactory.getMessageFilters( modules );
|
||||
|
||||
/* Setup the message activity model and add message listeners */
|
||||
mMessageActivityModel = new MessageActivityModel( messageFilter );
|
||||
mProcessingChain.addMessageListener( mMessageActivityModel );
|
||||
|
||||
/* Add audio manager as listener for audio packets */
|
||||
mProcessingChain.addAudioPacketListener(
|
||||
mResourceManager.getAudioManager() );
|
||||
|
||||
/* Add system-wide message listeners */
|
||||
mProcessingChain.addListeners(
|
||||
mProcessingChain.addMessageListeners(
|
||||
mResourceManager.getChannelManager().getMessageListeners() );
|
||||
|
||||
/* Add individual message listeners */
|
||||
mProcessingChain.addListeners( mMessageListeners );
|
||||
|
||||
mProcessingChain.start();
|
||||
|
||||
fireChannelEvent( Event.PROCESSING_STARTED );
|
||||
}
|
||||
/* Add individual message listeners */
|
||||
mProcessingChain.addMessageListeners( mMessageListeners );
|
||||
|
||||
/* If this is a traffic channel, override the call fade timeout before
|
||||
* we set the state to call, so that it will auto-expire after the
|
||||
* fade timeout and be auto deleted */
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
/* Create the call event model to receive call events */
|
||||
mCallEventModel = new CallEventModel();
|
||||
mProcessingChain.addCallEventListener( mCallEventModel );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to obtain a source and apply it to the processing chain.
|
||||
*
|
||||
* @throws SourceException - if the processing chain is not setup or if
|
||||
* there is an error produced while obtaining the source
|
||||
*/
|
||||
private void setupSource() throws SourceException
|
||||
{
|
||||
if( mProcessingChain != null )
|
||||
{
|
||||
if( mProcessingChain.isProcessing() )
|
||||
Source source = mResourceManager.getSourceManager()
|
||||
.getSource( getSourceConfiguration(), getDecodeConfiguration()
|
||||
.getDecoderType().getChannelBandwidth() );
|
||||
|
||||
if( source != null )
|
||||
{
|
||||
mProcessingChain.getChannelState().setCallFadeTimeout( 40000 );
|
||||
mProcessingChain.getChannelState().setState( State.CALL );
|
||||
mProcessingChain.setSource( source );
|
||||
}
|
||||
else
|
||||
{
|
||||
mProcessingChain.getChannelState().setCallFadeTimeout( 5000 );
|
||||
/* Channel state should already be no_tuner */
|
||||
throw new SourceException( "Couldn't obtain tuner channel source" );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SourceException( "Cannot obtain source for a null "
|
||||
+ "processing chain - setup the processing chain first" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,27 +822,14 @@ public class Channel extends Configuration
|
||||
*/
|
||||
private void stop()
|
||||
{
|
||||
/* Stop and remove any traffic channels */
|
||||
for( Channel traffic: mTrafficChannels.values() )
|
||||
{
|
||||
traffic.stop();
|
||||
traffic.dispose();
|
||||
}
|
||||
|
||||
mTrafficChannels.clear();
|
||||
|
||||
if( !mEnabled && mProcessingChain != null )
|
||||
{
|
||||
mProcessingChain.stop();
|
||||
|
||||
mProcessingChain.dispose();
|
||||
|
||||
mProcessingChain = null;
|
||||
|
||||
mSelected = false;
|
||||
}
|
||||
|
||||
fireChannelEvent( Event.PROCESSING_STOPPED );
|
||||
fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,9 +837,18 @@ public class Channel extends Configuration
|
||||
*/
|
||||
public void fireChannelEvent( Event event )
|
||||
{
|
||||
ChannelEvent channelEvent = new ChannelEvent( this, event );
|
||||
|
||||
for( ChannelEventListener listener: mChannelListeners )
|
||||
{
|
||||
listener.channelChanged( new ChannelEvent( this, event ) );
|
||||
listener.channelChanged( channelEvent );
|
||||
}
|
||||
|
||||
/* Send event to processing chain so modules can respond to channel
|
||||
* selection events */
|
||||
if( mProcessingChain != null )
|
||||
{
|
||||
mProcessingChain.getChannelEventListener().receive( channelEvent );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +861,7 @@ public class Channel extends Configuration
|
||||
if( !mChannelListeners.contains( listener ) )
|
||||
{
|
||||
mChannelListeners.add( listener );
|
||||
|
||||
|
||||
listener.channelChanged(
|
||||
new ChannelEvent( this, Event.CHANNEL_ADDED ) );
|
||||
}
|
||||
@@ -850,7 +898,7 @@ public class Channel extends Configuration
|
||||
|
||||
if( isProcessing() )
|
||||
{
|
||||
mProcessingChain.addListener( listener );
|
||||
mProcessingChain.addMessageListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,9 +906,9 @@ public class Channel extends Configuration
|
||||
{
|
||||
mMessageListeners.remove( listener );
|
||||
|
||||
if( isProcessing() )
|
||||
if( mProcessingChain != null )
|
||||
{
|
||||
mProcessingChain.removeListener( listener );
|
||||
mProcessingChain.removeMessageListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,13 +918,15 @@ public class Channel extends Configuration
|
||||
*/
|
||||
public boolean isWithin( long minimum, long maximum )
|
||||
{
|
||||
return getTunerChannel() != null &&
|
||||
getTunerChannel().isWithin( minimum, maximum );
|
||||
TunerChannel tunerChannel = getTunerChannel();
|
||||
|
||||
return tunerChannel != null &&
|
||||
tunerChannel.isWithin( minimum, maximum );
|
||||
}
|
||||
|
||||
public void addTrafficChannel( int channelNumber, Channel channel )
|
||||
public void addTrafficChannel( String channelID, Channel channel )
|
||||
{
|
||||
mTrafficChannels.put( channelNumber, channel );
|
||||
mTrafficChannels.put( channelID, channel );
|
||||
}
|
||||
|
||||
public boolean hasTrafficChannel( Channel channel )
|
||||
@@ -886,22 +936,11 @@ public class Channel extends Configuration
|
||||
|
||||
public void removeTrafficChannel( Channel channel )
|
||||
{
|
||||
int keyToRemove = -1;
|
||||
String name = channel.getChannelDisplayName();
|
||||
|
||||
if( mTrafficChannels.values().contains( channel ) )
|
||||
if( mTrafficChannels.containsKey( name ) )
|
||||
{
|
||||
for( Integer channelNumber: mTrafficChannels.keySet() )
|
||||
{
|
||||
if( mTrafficChannels.get( channelNumber ).equals( channel ) )
|
||||
{
|
||||
keyToRemove = channelNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( keyToRemove != -1 )
|
||||
{
|
||||
mTrafficChannels.remove( keyToRemove );
|
||||
mTrafficChannels.remove( name );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -910,8 +949,9 @@ public class Channel extends Configuration
|
||||
return Collections.unmodifiableCollection( mTrafficChannels.values() );
|
||||
}
|
||||
|
||||
public boolean hasTrafficChannel( int channelNumber )
|
||||
public boolean hasTrafficChannel( String name )
|
||||
{
|
||||
return mTrafficChannels.containsKey( channelNumber );
|
||||
return mTrafficChannels.containsKey( name );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ import javax.swing.JTextField;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
|
||||
import module.decode.AuxDecodeComponentEditor;
|
||||
import module.decode.DecodeComponentEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -47,8 +49,6 @@ import alias.AliasList;
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
|
||||
import controller.Editor;
|
||||
import decode.AuxDecodeComponentEditor;
|
||||
import decode.DecodeComponentEditor;
|
||||
import eventlog.EventLogComponentEditor;
|
||||
|
||||
public class ChannelEditor extends Editor implements ActionListener
|
||||
@@ -99,7 +99,7 @@ public class ChannelEditor extends Editor implements ActionListener
|
||||
/**
|
||||
* ComboBox: Alias Lists
|
||||
*/
|
||||
add( new JLabel( "Aliases:" ) );
|
||||
add( new JLabel( "Alias List:" ) );
|
||||
|
||||
mComboAliasLists = new JComboBox<AliasList>();
|
||||
|
||||
|
||||
@@ -34,19 +34,26 @@ public class ChannelEvent
|
||||
{
|
||||
CHANGE_ALIAS_LIST,
|
||||
CHANGE_DECODER,
|
||||
CHANGE_ENABLED,
|
||||
CHANGE_EVENT_LOGGER,
|
||||
CHANGE_NAME,
|
||||
CHANGE_SELECTED,
|
||||
CHANGE_SITE,
|
||||
CHANGE_RECORDER,
|
||||
CHANGE_SELECTED,
|
||||
CHANGE_SOURCE,
|
||||
CHANGE_SYSTEM,
|
||||
|
||||
CHANNEL_ADDED,
|
||||
CHANNEL_ADDED,
|
||||
CHANNEL_DELETED,
|
||||
|
||||
CHANNEL_ENABLED,
|
||||
CHANNEL_DISABLED,
|
||||
|
||||
PROCESSING_STARTED,
|
||||
PROCESSING_STOPPED,
|
||||
CHANNEL_PROCESSING_STARTED,
|
||||
CHANNEL_PROCESSING_STOPPED,
|
||||
|
||||
CHANNEL_STATE_RESET,
|
||||
|
||||
TRAFFIC_CHANNEL_ADDED,
|
||||
TRAFFIC_CHANNEL_DELETED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,8 +17,13 @@
|
||||
******************************************************************************/
|
||||
package controller.channel;
|
||||
|
||||
import gui.SDRTrunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import alias.action.AliasActionManager;
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
@@ -26,6 +31,8 @@ import controller.ResourceManager;
|
||||
|
||||
public class ChannelManager implements ChannelEventListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class );
|
||||
|
||||
private ArrayList<Channel> mChannels = new ArrayList<Channel>();
|
||||
|
||||
private ArrayList<ChannelEventListener> mChannelListeners =
|
||||
@@ -56,13 +63,13 @@ public class ChannelManager implements ChannelEventListener
|
||||
{
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case CHANNEL_ADDED:
|
||||
case CHANNEL_ENABLED:
|
||||
if( !mChannels.contains( event.getChannel() ) )
|
||||
{
|
||||
mChannels.add( event.getChannel() );
|
||||
}
|
||||
break;
|
||||
case CHANNEL_DELETED:
|
||||
case CHANNEL_DISABLED:
|
||||
mChannels.remove( event.getChannel() );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -78,17 +78,17 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene
|
||||
{
|
||||
/* Refresh the node for each of these events */
|
||||
case CHANGE_DECODER:
|
||||
case CHANGE_ENABLED:
|
||||
case CHANGE_NAME:
|
||||
case CHANGE_SELECTED:
|
||||
case CHANGE_SITE:
|
||||
case CHANGE_SYSTEM:
|
||||
case PROCESSING_STARTED:
|
||||
case PROCESSING_STOPPED:
|
||||
case CHANNEL_ENABLED:
|
||||
case CHANNEL_PROCESSING_STARTED:
|
||||
case CHANNEL_PROCESSING_STOPPED:
|
||||
this.refresh();
|
||||
break;
|
||||
/* We're being deleted, so cleanup */
|
||||
case CHANNEL_DELETED:
|
||||
case CHANNEL_DISABLED:
|
||||
delete();
|
||||
break;
|
||||
default:
|
||||
@@ -154,7 +154,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene
|
||||
public JPopupMenu getContextMenu()
|
||||
{
|
||||
JPopupMenu popupMenu = new JPopupMenu( "Channel Menu" );
|
||||
popupMenu.add( getChannel().getContextMenu() );
|
||||
// popupMenu.add( getChannel().getContextMenu() );
|
||||
|
||||
return popupMenu;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
******************************************************************************/
|
||||
package controller.channel;
|
||||
|
||||
import module.ProcessingChainOld;
|
||||
|
||||
public interface ChannelSelectionListener
|
||||
{
|
||||
public void channelSelected( ProcessingChain channel );
|
||||
public void channelSelected( ProcessingChainOld channel );
|
||||
}
|
||||
|
||||
8
src/controller/channel/IChannelEventListener.java
Normal file
8
src/controller/channel/IChannelEventListener.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package controller.channel;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IChannelEventListener
|
||||
{
|
||||
public Listener<ChannelEvent> getChannelEventListener();
|
||||
}
|
||||
9
src/controller/channel/IChannelEventProvider.java
Normal file
9
src/controller/channel/IChannelEventProvider.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package controller.channel;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IChannelEventProvider
|
||||
{
|
||||
public void setChannelEventListener( Listener<ChannelEvent> listener );
|
||||
public void removeChannelEventListener();
|
||||
}
|
||||
@@ -1,744 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.channel;
|
||||
|
||||
import gui.SDRTrunk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedTransferQueue;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import message.Message;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import record.Recorder;
|
||||
import record.wave.ComplexWaveRecorder;
|
||||
import record.wave.FloatWaveRecorder;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import sample.real.RealBuffer;
|
||||
import sample.real.RealSampleListener;
|
||||
import source.ComplexSource;
|
||||
import source.RealSource;
|
||||
import source.Source;
|
||||
import source.SourceException;
|
||||
import source.config.SourceConfigMixer;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerChannelSource;
|
||||
import source.tuner.frequency.AutomaticFrequencyControl;
|
||||
import source.tuner.frequency.FrequencyCorrectionControl;
|
||||
import alias.AliasList;
|
||||
import audio.IAudioOutput;
|
||||
import audio.SquelchListener;
|
||||
import audio.inverted.IAudioTypeListener;
|
||||
import controller.ResourceManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
import controller.activity.CallEvent;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.state.ChannelState;
|
||||
import controller.state.ChannelState.State;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderFactory;
|
||||
import eventlog.EventLogType;
|
||||
import eventlog.EventLogger;
|
||||
|
||||
/**
|
||||
* A channel has all of the pieces needed to wire together a source, decoder,
|
||||
* event logger and recorder and be started and stopped.
|
||||
*/
|
||||
public class ProcessingChain implements Listener<Message>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class );
|
||||
|
||||
protected AtomicBoolean mRunning = new AtomicBoolean();
|
||||
protected Channel mChannel;
|
||||
protected Source mSource;
|
||||
protected Decoder mDecoder;
|
||||
protected List<Recorder> mRecorders = new ArrayList<Recorder>();
|
||||
protected ArrayList<EventLogger> mEventLoggers = new ArrayList<EventLogger>();
|
||||
protected AutomaticFrequencyControl mAFC;
|
||||
protected ChannelState mChannelState;
|
||||
protected ResourceManager mResourceManager;
|
||||
private ScheduledFuture<?> mProcessorTask;
|
||||
private AliasList mAliasList;
|
||||
|
||||
private ComplexSampleReceiver mComplexReceiver = new ComplexSampleReceiver();
|
||||
private LinkedTransferQueue<List<ComplexSample>> mComplexQueue =
|
||||
new LinkedTransferQueue<List<ComplexSample>>();
|
||||
|
||||
private RealSampleReceiver mRealReceiver = new RealSampleReceiver();
|
||||
private LinkedTransferQueue<RealBuffer> mRealQueue =
|
||||
new LinkedTransferQueue<RealBuffer>();
|
||||
|
||||
protected Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
|
||||
public ProcessingChain( Channel channel, ResourceManager resourceManager )
|
||||
{
|
||||
mChannel = channel;
|
||||
mResourceManager = resourceManager;
|
||||
|
||||
if( mResourceManager != null )
|
||||
{
|
||||
mAliasList = mResourceManager.getPlaylistManager()
|
||||
.getPlayist().getAliasDirectory().getAliasList(
|
||||
mChannel.getAliasListName() );
|
||||
}
|
||||
}
|
||||
|
||||
public IAudioOutput getAudioOutput()
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
return mDecoder.getAudioOutput();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Listener<List<ComplexSample>> getComplexReceiver()
|
||||
{
|
||||
return mComplexReceiver;
|
||||
}
|
||||
|
||||
public Listener<RealBuffer> getRealReceiver()
|
||||
{
|
||||
return (Listener<RealBuffer>)mRealReceiver;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mChannel.getName();
|
||||
}
|
||||
|
||||
public AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
if( mProcessorTask != null )
|
||||
{
|
||||
mResourceManager.getThreadPoolManager().cancel( mProcessorTask );
|
||||
}
|
||||
|
||||
if( mChannelState != null )
|
||||
{
|
||||
mChannelState.dispose();
|
||||
}
|
||||
|
||||
mChannelState = null;
|
||||
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.dispose();
|
||||
}
|
||||
|
||||
mBroadcaster.dispose();
|
||||
mEventLoggers.clear();
|
||||
mRecorders.clear();
|
||||
|
||||
mResourceManager = null;
|
||||
}
|
||||
|
||||
public ResourceManager getResourceManager()
|
||||
{
|
||||
return mResourceManager;
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
if( !mRunning.get() && mChannel.getEnabled() )
|
||||
{
|
||||
updateSource();
|
||||
|
||||
updateDecoder();
|
||||
|
||||
updateEventLogging();
|
||||
|
||||
updateRecording();
|
||||
|
||||
mRunning.set( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info( getLogPrefix() + "start() invoked, but processing chain is "
|
||||
+ "already started" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs closeout operations to prepare for channel deletion
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
if( mRunning.get() )
|
||||
{
|
||||
updateSource();
|
||||
|
||||
updateDecoder();
|
||||
|
||||
updateRecording();
|
||||
|
||||
/* Flush call events to the logger */
|
||||
if( getChannel().getChannelType() == ChannelType.STANDARD )
|
||||
{
|
||||
getChannelState().getCallEventModel().flush();
|
||||
}
|
||||
|
||||
updateEventLogging();
|
||||
|
||||
mRunning.set( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.info( getLogPrefix() + "stop() invoked, but processing chain was "
|
||||
+ "already stopped" );
|
||||
}
|
||||
}
|
||||
|
||||
private String getLogPrefix()
|
||||
{
|
||||
return "Channel [" + mChannel.getName() + "] - ";
|
||||
}
|
||||
|
||||
public void updateSource()
|
||||
{
|
||||
/* Cleanup existing source */
|
||||
if( mSource != null )
|
||||
{
|
||||
if( mSource instanceof ComplexSource )
|
||||
{
|
||||
((ComplexSource)mSource).removeListener( this.getComplexReceiver() );
|
||||
}
|
||||
else if( mSource instanceof RealSource )
|
||||
{
|
||||
((RealSource)mSource)
|
||||
.removeListener( this.getRealReceiver() );
|
||||
}
|
||||
|
||||
if( mProcessorTask != null )
|
||||
{
|
||||
mResourceManager.getThreadPoolManager().cancel( mProcessorTask );
|
||||
}
|
||||
|
||||
mSource.dispose();
|
||||
|
||||
mSource = null;
|
||||
}
|
||||
|
||||
/* Get a new source if needed and start the sample processor thread */
|
||||
if( mChannel.getEnabled() && mResourceManager != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
mSource = mResourceManager.getSourceManager()
|
||||
.getSource( ProcessingChain.this );
|
||||
}
|
||||
catch ( SourceException e )
|
||||
{
|
||||
mLog.error( "Couldn't obtain source for processing chain [" +
|
||||
toString() + "]", e );
|
||||
|
||||
mSource = null;
|
||||
}
|
||||
|
||||
if( mSource != null )
|
||||
{
|
||||
if( mSource instanceof ComplexSource )
|
||||
{
|
||||
try
|
||||
{
|
||||
mProcessorTask =
|
||||
mResourceManager.getThreadPoolManager()
|
||||
.scheduleFixedRate( ThreadType.DECODER,
|
||||
new ComplexProcessor(),
|
||||
50, TimeUnit.MILLISECONDS );
|
||||
|
||||
((ComplexSource)mSource)
|
||||
.setListener( this.getComplexReceiver() );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
mLog.error( getLogPrefix() + "ProcessingChain - "
|
||||
+ "error scheduling complex sample processing "
|
||||
+ "thread - " + ree.getLocalizedMessage() );
|
||||
|
||||
mSource.dispose();
|
||||
}
|
||||
}
|
||||
else if( mSource instanceof RealSource )
|
||||
{
|
||||
try
|
||||
{
|
||||
mProcessorTask =
|
||||
mResourceManager.getThreadPoolManager()
|
||||
.scheduleFixedRate( ThreadType.DECODER,
|
||||
new RealProcessor(),
|
||||
50, TimeUnit.MILLISECONDS );
|
||||
|
||||
((RealSource)mSource)
|
||||
.setListener( this.getRealReceiver() );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
mLog.error( getLogPrefix() + "ProcessingChain - "
|
||||
+ "error scheduling float sample processing "
|
||||
+ "thread", ree );
|
||||
|
||||
mSource.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDecoder()
|
||||
{
|
||||
if( mChannel.getEnabled() )
|
||||
{
|
||||
AliasList aliasList = null;
|
||||
|
||||
if( mResourceManager != null )
|
||||
{
|
||||
aliasList = mResourceManager.getPlaylistManager()
|
||||
.getPlayist().getAliasDirectory().getAliasList(
|
||||
mChannel.getAliasListName() );
|
||||
}
|
||||
|
||||
/* Setup the channel state */
|
||||
mChannelState = DecoderFactory.getChannelStateNew( this, aliasList );
|
||||
|
||||
if( mSource == null )
|
||||
{
|
||||
mChannelState.setState( State.NO_TUNER );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDecoder = DecoderFactory.
|
||||
getDecoder( this, mSource.getSampleType(), aliasList );
|
||||
|
||||
if( mDecoder != null )
|
||||
{
|
||||
/* Register to receive decoded messages and auxiliary messages */
|
||||
mDecoder.addMessageListener( ProcessingChain.this );
|
||||
|
||||
/* Establish two-way communication between the frequency
|
||||
* correction controller and the tuner channel source */
|
||||
if( mSource instanceof TunerChannelSource &&
|
||||
mDecoder.hasFrequencyCorrectionControl() )
|
||||
{
|
||||
TunerChannelSource tcs = (TunerChannelSource)mSource;
|
||||
|
||||
FrequencyCorrectionControl fcc =
|
||||
mDecoder.getFrequencyCorrectionControl();
|
||||
|
||||
if( fcc != null )
|
||||
{
|
||||
tcs.addListener( fcc );
|
||||
fcc.setListener( tcs );
|
||||
}
|
||||
}
|
||||
|
||||
/* Register audio output to be controlled by the channel state */
|
||||
mChannelState.addListener( (SquelchListener)mDecoder.getAudioOutput() );
|
||||
mChannelState.setListener( (IAudioTypeListener)mDecoder.getAudioOutput() );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts, stops or changes event logging
|
||||
*/
|
||||
public void updateEventLogging()
|
||||
{
|
||||
/* Shutdown and remove existing event loggers */
|
||||
if( mEventLoggers != null )
|
||||
{
|
||||
for( EventLogger logger: mEventLoggers )
|
||||
{
|
||||
logger.stop();
|
||||
}
|
||||
|
||||
mEventLoggers.clear();
|
||||
}
|
||||
|
||||
/* Construct event loggers and add as message listeners */
|
||||
if( mChannel.getEnabled() &&
|
||||
mResourceManager != null )
|
||||
{
|
||||
//Get the event loggers
|
||||
ArrayList<EventLogType> types = getChannel()
|
||||
.getEventLogConfiguration().getLoggers();
|
||||
|
||||
for( EventLogType type: types )
|
||||
{
|
||||
EventLogger logger;
|
||||
|
||||
if( type == EventLogType.CALL_EVENT )
|
||||
{
|
||||
logger = mResourceManager.getEventLogManager()
|
||||
.getLogger( this, type );
|
||||
|
||||
getChannelState().getCallEventModel()
|
||||
.addListener( (Listener<CallEvent>)logger );
|
||||
}
|
||||
else
|
||||
{
|
||||
logger = mResourceManager.getEventLogManager()
|
||||
.getLogger( this, type );
|
||||
|
||||
addListener( (Listener<Message>)logger );
|
||||
}
|
||||
|
||||
logger.start();
|
||||
|
||||
mEventLoggers.add( logger );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts, stops or changes data recording
|
||||
*/
|
||||
public void updateRecording()
|
||||
{
|
||||
/* Remove any existing recorders */
|
||||
if( mRecorders != null )
|
||||
{
|
||||
for( Recorder recorder: mRecorders )
|
||||
{
|
||||
if( recorder != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
recorder.stop();
|
||||
}
|
||||
catch ( IOException ioe )
|
||||
{
|
||||
mLog.error( getLogPrefix() + "Couldn't stop recorder [" +
|
||||
recorder.getFileName() + "]", ioe );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Start any new recorders */
|
||||
if( mChannel.getEnabled() &&
|
||||
mResourceManager != null &&
|
||||
mDecoder != null )
|
||||
{
|
||||
mRecorders = mResourceManager.getRecorderManager()
|
||||
.getRecorder( this );
|
||||
|
||||
/* Iterate any returned recorders and start/add them */
|
||||
for( Recorder recorder: mRecorders )
|
||||
{
|
||||
try
|
||||
{
|
||||
recorder.start();
|
||||
|
||||
if( recorder instanceof FloatWaveRecorder )
|
||||
{
|
||||
mLog.info( getLogPrefix() + "- started audio recording [" +
|
||||
recorder.getFileName() + "]" );
|
||||
|
||||
mDecoder.addRealSampleListener( (FloatWaveRecorder)recorder );
|
||||
}
|
||||
else if( recorder instanceof ComplexWaveRecorder )
|
||||
{
|
||||
mLog.info( getLogPrefix() + "- started baseband recording [" +
|
||||
recorder.getFileName() + "]" );
|
||||
|
||||
mDecoder.addComplexListener( (ComplexWaveRecorder )recorder );
|
||||
}
|
||||
|
||||
}
|
||||
catch ( IOException e )
|
||||
{
|
||||
mLog.error( getLogPrefix() + "Unable to start recorder [" +
|
||||
recorder.getFileName() + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Channel getChannel()
|
||||
{
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
public Source getSource()
|
||||
{
|
||||
return mSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tuner that is sourcing this channel, or null if it's being
|
||||
* sourced from some other source
|
||||
*/
|
||||
public Tuner getTuner()
|
||||
{
|
||||
Tuner retVal = null;
|
||||
|
||||
if( mSource instanceof TunerChannelSource )
|
||||
{
|
||||
retVal = ((TunerChannelSource)mSource).getTuner();
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public Decoder getDecoder()
|
||||
{
|
||||
return mDecoder;
|
||||
}
|
||||
|
||||
public ChannelState getChannelState()
|
||||
{
|
||||
return mChannelState;
|
||||
}
|
||||
|
||||
public boolean isRunning()
|
||||
{
|
||||
return mRunning.get();
|
||||
}
|
||||
|
||||
public boolean isProcessing()
|
||||
{
|
||||
return isRunning() && mSource != null;
|
||||
}
|
||||
|
||||
private source.mixer.MixerChannel getMixerChannel()
|
||||
{
|
||||
source.mixer.MixerChannel retVal = null;
|
||||
|
||||
if( mChannel.getSourceConfiguration() instanceof SourceConfigMixer )
|
||||
{
|
||||
SourceConfigMixer mixer = (SourceConfigMixer)mChannel.getSourceConfiguration();
|
||||
|
||||
retVal = mixer.getChannel();
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message.isValid() )
|
||||
{
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the listener to receive demodulated samples from the decoder
|
||||
*/
|
||||
public void addRealListener( RealSampleListener listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.addRealSampleListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving demodulated samples from the decoder
|
||||
*/
|
||||
public void removeRealListener( RealSampleListener listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.removeRealListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive a copy of complex (I/Q) baseband samples
|
||||
* received by this processing chain
|
||||
*/
|
||||
public void addComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.addComplexListener( listener );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( getLogPrefix() + "attempt to add a complex sample "
|
||||
+ "listener, but the decoder is null" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving a copy of complex (I/Q) baseband
|
||||
* samples received by this processing chain
|
||||
*/
|
||||
public void removeComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
mDecoder.removeComplexListener( listener );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( getLogPrefix() + "attempt to remove a complex sample "
|
||||
+ "listener, but the decoder is null" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive messages
|
||||
*/
|
||||
public void addListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all listeners in the list to receive messages
|
||||
*/
|
||||
public void addListeners( List<Listener<Message>> listeners )
|
||||
{
|
||||
for( Listener<Message> listener: listeners )
|
||||
{
|
||||
addListener( listener );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving messages
|
||||
*/
|
||||
public void removeListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the real sample queue and distributes samples to the decoder.
|
||||
*/
|
||||
private class RealProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<RealBuffer> sampleBuffers = new ArrayList<RealBuffer>();
|
||||
|
||||
mRealQueue.drainTo( sampleBuffers, 4 );
|
||||
|
||||
for( RealBuffer sampleBuffer: sampleBuffers )
|
||||
{
|
||||
if( mDecoder != null )
|
||||
{
|
||||
float[] samples = sampleBuffer.getSamples();
|
||||
|
||||
for( float sample: samples )
|
||||
{
|
||||
mDecoder.getRealReceiver().receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
sampleBuffer.dispose();
|
||||
}
|
||||
|
||||
sampleBuffers.clear();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "error during processing chain real processor run", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the complex sample queue and distributes samples to the decoder.
|
||||
*/
|
||||
private class ComplexProcessor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<List<ComplexSample>> sampleSets =
|
||||
new ArrayList<List<ComplexSample>>();
|
||||
|
||||
mComplexQueue.drainTo( sampleSets, 16 );
|
||||
|
||||
for( List<ComplexSample> samples: sampleSets )
|
||||
{
|
||||
for( ComplexSample sample: samples )
|
||||
{
|
||||
if( sample != null && mDecoder != null )
|
||||
{
|
||||
mDecoder.getComplexReceiver().receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
sampleSets.clear();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "error during processing chain complex processor run", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal listener to receive complex (I/Q) baseband samples. Places
|
||||
* received samples into the queue managed by the ComplexProcessor.
|
||||
*/
|
||||
public class ComplexSampleReceiver implements Listener<List<ComplexSample>>
|
||||
{
|
||||
@Override
|
||||
public void receive( List<ComplexSample> samples )
|
||||
{
|
||||
mComplexQueue.add( samples );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal listener to receive real(ie demodulated) samples. Places
|
||||
* received samples into the queue managed by the RealProcessor
|
||||
*/
|
||||
public class RealSampleReceiver implements Listener<RealBuffer>
|
||||
{
|
||||
@Override
|
||||
public void receive( RealBuffer samples )
|
||||
{
|
||||
mRealQueue.add( samples );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
@@ -80,7 +80,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener
|
||||
@Override
|
||||
public void focusLost( FocusEvent arg0 )
|
||||
{
|
||||
mLog.info( "text field action event fired, new value:" + mNameText.getText() );
|
||||
mChannelMapCopy.setName( mNameText.getText() );
|
||||
}
|
||||
} );
|
||||
@@ -208,7 +207,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
mLog.info( "Action Performed:" + e.getActionCommand() );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -15,7 +15,7 @@
|
||||
* 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 controller.channel;
|
||||
package controller.channel.map;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
@@ -20,10 +20,10 @@ package controller.config;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
|
||||
import module.decode.config.AuxDecodeConfiguration;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import record.config.RecordConfiguration;
|
||||
import source.config.SourceConfiguration;
|
||||
import decode.config.AuxDecodeConfiguration;
|
||||
import decode.config.DecodeConfiguration;
|
||||
import eventlog.config.EventLogConfiguration;
|
||||
|
||||
@XmlSeeAlso( { AuxDecodeConfiguration.class,
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
|
||||
/**
|
||||
* ChannelState for auxiliary decoders.
|
||||
*/
|
||||
public abstract class AuxChannelState implements Listener<Message>
|
||||
{
|
||||
protected ChannelState mParentChannelState;
|
||||
protected Broadcaster<ChangedAttribute> mChangeBroadcaster =
|
||||
new Broadcaster<ChangedAttribute>();
|
||||
|
||||
public AuxChannelState( ChannelState parentState )
|
||||
{
|
||||
mParentChannelState = parentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for garbage collect
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mChangeBroadcaster.dispose();
|
||||
mParentChannelState = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call fade - change display and await a call to reset()
|
||||
*/
|
||||
public abstract void fade();
|
||||
|
||||
/**
|
||||
* Activity summary for this decoder
|
||||
*/
|
||||
public abstract String getActivitySummary();
|
||||
|
||||
/**
|
||||
* Call end - cleanup the display
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
public void addListener( Listener<ChangedAttribute> listener )
|
||||
{
|
||||
mChangeBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeListener( Listener<ChangedAttribute> listener )
|
||||
{
|
||||
mChangeBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
public void broadcastChange( ChangedAttribute attribute )
|
||||
{
|
||||
if( mChangeBroadcaster != null )
|
||||
{
|
||||
mChangeBroadcaster.broadcast( attribute );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import sample.Listener;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingChangeListener;
|
||||
import settings.SettingsManager;
|
||||
import alias.Alias;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
|
||||
public abstract class AuxStatePanel extends JPanel
|
||||
implements Listener<ChangedAttribute>,
|
||||
SettingChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
|
||||
protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
|
||||
|
||||
protected Color mColorLabelDetails;
|
||||
protected Color mColorLabelAuxDecoder;
|
||||
|
||||
protected SettingsManager mSettingsManager;
|
||||
protected AuxChannelState mAuxChannelState;
|
||||
|
||||
public AuxStatePanel( SettingsManager settingsManager,
|
||||
AuxChannelState auxChannelState )
|
||||
{
|
||||
mSettingsManager = settingsManager;
|
||||
mAuxChannelState = auxChannelState;
|
||||
|
||||
/* Register to receive event updates */
|
||||
mAuxChannelState.addListener( this );
|
||||
|
||||
/* Register to receive settings updates */
|
||||
mSettingsManager.addListener( this );
|
||||
|
||||
getColors();
|
||||
|
||||
setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
/* Deregister from changed settings events */
|
||||
mSettingsManager.removeListener( this );
|
||||
mSettingsManager = null;
|
||||
|
||||
mAuxChannelState = null;
|
||||
}
|
||||
|
||||
protected void addLabel( JLabel label, boolean wrap )
|
||||
{
|
||||
label.setFont( mFontAuxDecoder );
|
||||
label.setForeground( mColorLabelAuxDecoder );
|
||||
|
||||
if( wrap )
|
||||
{
|
||||
add( label, "wrap" );
|
||||
}
|
||||
else
|
||||
{
|
||||
add( label );
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateAliasLabel( JLabel label, Alias alias )
|
||||
{
|
||||
if( alias != null )
|
||||
{
|
||||
label.setText( alias.getName() );
|
||||
|
||||
String iconName = alias.getIconName();
|
||||
|
||||
ImageIcon icon = mSettingsManager.getImageIcon( iconName, 12 );
|
||||
|
||||
label.setIcon( icon );
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText( null );
|
||||
label.setIcon( null );
|
||||
}
|
||||
}
|
||||
|
||||
private void getColors()
|
||||
{
|
||||
mColorLabelDetails = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor();
|
||||
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
switch( colorSetting.getColorSettingName() )
|
||||
{
|
||||
case CHANNEL_STATE_LABEL_AUX_DECODER:
|
||||
mColorLabelAuxDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
mColorLabelDetails = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted( Setting setting ) {}
|
||||
}
|
||||
@@ -1,697 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.JTable;
|
||||
|
||||
import message.Message;
|
||||
import message.MessageFilterFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import properties.SystemProperties;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import audio.SquelchListener;
|
||||
import audio.SquelchListener.SquelchState;
|
||||
import audio.inverted.AudioType;
|
||||
import audio.inverted.IAudioTypeListener;
|
||||
import controller.activity.CallEvent;
|
||||
import controller.activity.CallEvent.CallEventType;
|
||||
import controller.activity.CallEventAliasCellRenderer;
|
||||
import controller.activity.CallEventModel;
|
||||
import controller.activity.MessageActivityModel;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ProcessingChain;
|
||||
import filter.FilterSet;
|
||||
|
||||
/**
|
||||
* ChannelState provides a state machine for tracking a voice or data call
|
||||
* through it's life cycle of CALL/DATA, FADE, and then END, and then back to
|
||||
* an IDLE state. This class also provides squelch control for unsquelching
|
||||
* sound to the speakers, and/or sound to an audio recorder
|
||||
*
|
||||
* State Descriptions:
|
||||
* IDLE No voice or data call activity
|
||||
* CALL/DATA A voice or data call has started, or is continuing
|
||||
* CONTROL Control channel
|
||||
* FADE The phase after a voice or data call when either an explicit
|
||||
* call end has been received, or when no new signalling updates
|
||||
* have been received, and the fade timer has expired. This phase
|
||||
* allows for gui updates to signal to the user that the call is
|
||||
* ended, while continuing to display the call details for the user
|
||||
* END The phase after the voice or data call FADE, when the call is
|
||||
* now over, and the gui should be reset to IDLE.
|
||||
*
|
||||
* When a call is started, invoke mTimerService.startFadeTimer() to start the
|
||||
* call fade monitoring timer process that will automatically signal a call end
|
||||
* once no more call signalling has been detected after the fade timeout has
|
||||
* occurred, by invoking the fade() method.
|
||||
*
|
||||
* When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke
|
||||
* the mTimerService.startResetTimer(). Once the reset timeout occurs,
|
||||
* the reset() method will be called, allowing the channel state to perform
|
||||
* any reset actions, like clearing status and id labels on any view panels.
|
||||
*
|
||||
* AuxChannelState class allows for auxiliary decoders like Fleetsync to both
|
||||
* have a state and leverage the state machine of the parent ChannelState.
|
||||
* This allows a non-squelching channel state like for conventional fm to have
|
||||
* an aux decoder like fleetsync, and allow the fleetsync auxchannelstate to
|
||||
* leverage the call/end states of the parent conventional channelstate.
|
||||
*
|
||||
*/
|
||||
public abstract class ChannelState implements Listener<Message>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ChannelState.class );
|
||||
|
||||
private State mState = State.IDLE;
|
||||
private Object mCallFadeLock = new Object();
|
||||
private Object mCallResetLock = new Object();
|
||||
private long mFadeTimeout = -1;
|
||||
private long mResetTimeout = -1;
|
||||
|
||||
protected long mCallFadeTimeout;
|
||||
protected long mCallResetTimeout;
|
||||
protected TimerService mTimerService = new TimerService();
|
||||
protected ArrayList<SquelchListener> mSquelchListeners =
|
||||
new ArrayList<SquelchListener>();
|
||||
protected ArrayList<AuxChannelState> mAuxChannelStates =
|
||||
new ArrayList<AuxChannelState>();
|
||||
protected Broadcaster<ChangedAttribute> mChangeBroadcaster =
|
||||
new Broadcaster<ChangedAttribute>();
|
||||
protected IAudioTypeListener mAudioTypeListener;
|
||||
protected AudioType mAudioType = AudioType.NORMAL;
|
||||
|
||||
protected CallEventModel mCallEventModel = new CallEventModel();
|
||||
|
||||
protected JTable mCallEventTable;
|
||||
|
||||
protected MessageActivityModel mMessageActivityModel;
|
||||
protected JTable mMessageActivityTable;
|
||||
|
||||
protected ProcessingChain mProcessingChain;
|
||||
|
||||
protected AliasList mAliasList;
|
||||
|
||||
protected CallEvent mCurrentCallEvent;
|
||||
|
||||
public ChannelState( ProcessingChain processingChain, AliasList aliasList )
|
||||
{
|
||||
mProcessingChain = processingChain;
|
||||
mAliasList = aliasList;
|
||||
|
||||
|
||||
/* Register to receive decoded messages from the processing chain */
|
||||
mProcessingChain.addListener( this );
|
||||
|
||||
SystemProperties props = SystemProperties.getInstance();
|
||||
mCallFadeTimeout = props.get( "call.fade.timeout", 2000 );
|
||||
mCallResetTimeout = props.get( "call.reset.timeout", 4000 );
|
||||
|
||||
/* Get a message filter for the primary decoder and aux decoders */
|
||||
FilterSet messageFilter = MessageFilterFactory
|
||||
.getMessageFilter( mProcessingChain.getChannel() );
|
||||
|
||||
mMessageActivityModel = new MessageActivityModel( messageFilter );
|
||||
}
|
||||
|
||||
public void setCurrentCallEvent( CallEvent callEvent )
|
||||
{
|
||||
mCurrentCallEvent = callEvent;
|
||||
}
|
||||
|
||||
public CallEvent getCurrentCallEvent()
|
||||
{
|
||||
return mCurrentCallEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the call event model to use for traffic channels, so that
|
||||
* all traffic channels can share the parent control channel call event
|
||||
* model.
|
||||
* @param model
|
||||
*/
|
||||
public void setCallEventModel( CallEventModel model )
|
||||
{
|
||||
mCallEventModel = model;
|
||||
}
|
||||
|
||||
public CallEventModel getCallEventModel()
|
||||
{
|
||||
return mCallEventModel;
|
||||
}
|
||||
|
||||
public void receiveCallEvent( CallEvent event )
|
||||
{
|
||||
mCallEventModel.add( event );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mTimerService.dispose();
|
||||
|
||||
mAuxChannelStates.clear();
|
||||
mSquelchListeners.clear();
|
||||
mChangeBroadcaster.dispose();
|
||||
mChangeBroadcaster = null;
|
||||
|
||||
if( mProcessingChain.getChannel().getChannelType() == ChannelType.STANDARD )
|
||||
{
|
||||
mCallEventModel.dispose();
|
||||
}
|
||||
/* If we're a traffic channel, set the call event model to null so that
|
||||
* we don't dispose the parent control channel's call event model */
|
||||
else
|
||||
{
|
||||
mCallEventModel = null;
|
||||
}
|
||||
|
||||
mMessageActivityModel.dispose();
|
||||
mMessageActivityModel = null;
|
||||
|
||||
mAliasList = null;
|
||||
mAudioTypeListener = null;
|
||||
mCallEventModel = null;
|
||||
mProcessingChain = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel Activity Summary - implemented by the sub-class.
|
||||
*/
|
||||
public abstract String getActivitySummary();
|
||||
|
||||
public AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
public boolean hasAliasList()
|
||||
{
|
||||
return mAliasList != null;
|
||||
}
|
||||
|
||||
public void broadcastChange( final ChangedAttribute attribute )
|
||||
{
|
||||
mChangeBroadcaster.broadcast( attribute );
|
||||
}
|
||||
|
||||
public void setCallFadeTimeout( long milliseconds )
|
||||
{
|
||||
mCallFadeTimeout = milliseconds;
|
||||
}
|
||||
|
||||
public JTable getCallEventTable()
|
||||
{
|
||||
if( mCallEventTable == null )
|
||||
{
|
||||
mCallEventTable = new JTable( mCallEventModel );
|
||||
mCallEventTable.setAutoCreateRowSorter( true );
|
||||
mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
|
||||
|
||||
CallEventAliasCellRenderer renderer =
|
||||
new CallEventAliasCellRenderer( mProcessingChain
|
||||
.getResourceManager().getSettingsManager() );
|
||||
|
||||
mCallEventTable.getColumnModel().getColumn( 3 )
|
||||
.setCellRenderer( renderer );
|
||||
|
||||
mCallEventTable.getColumnModel().getColumn( 5 )
|
||||
.setCellRenderer( renderer );
|
||||
}
|
||||
|
||||
return mCallEventTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message activity model.
|
||||
*/
|
||||
public MessageActivityModel getMessageActivityModel()
|
||||
{
|
||||
return mMessageActivityModel;
|
||||
}
|
||||
|
||||
public void addAuxChannelState( AuxChannelState state )
|
||||
{
|
||||
mAuxChannelStates.add( state );
|
||||
}
|
||||
|
||||
public ArrayList<AuxChannelState> getAuxChannelStates()
|
||||
{
|
||||
return mAuxChannelStates;
|
||||
}
|
||||
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( mMessageActivityModel != null )
|
||||
{
|
||||
mMessageActivityModel.receive( message );
|
||||
}
|
||||
|
||||
for( AuxChannelState state: mAuxChannelStates )
|
||||
{
|
||||
if( state != null )
|
||||
{
|
||||
state.receive( message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ProcessingChain getProcessingChain()
|
||||
{
|
||||
return mProcessingChain;
|
||||
}
|
||||
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public void setState( State state )
|
||||
{
|
||||
/* Let all CALL state changes go through in order to update the fade
|
||||
* timer, otherwise, don't update the state if it hasn't changed */
|
||||
if( mState != state || state == State.CALL || state == State.DATA || state == State.ENCRYPTED )
|
||||
{
|
||||
if( mState.canChangeTo( state ) )
|
||||
{
|
||||
mState = state;
|
||||
|
||||
switch( state )
|
||||
{
|
||||
case CONTROL:
|
||||
setSquelchState( SquelchState.SQUELCH );
|
||||
break;
|
||||
case CALL:
|
||||
setSquelchState( SquelchState.UNSQUELCH );
|
||||
mTimerService.stopResetTimer();
|
||||
mTimerService.startFadeTimer();
|
||||
break;
|
||||
case DATA:
|
||||
mTimerService.stopResetTimer();
|
||||
mTimerService.startFadeTimer();
|
||||
break;
|
||||
case ENCRYPTED:
|
||||
setSquelchState( SquelchState.SQUELCH );
|
||||
mTimerService.stopResetTimer();
|
||||
mTimerService.startFadeTimer();
|
||||
case NO_TUNER:
|
||||
mTimerService.stopResetTimer();
|
||||
if( getProcessingChain().getChannel()
|
||||
.getChannelType() == ChannelType.TRAFFIC )
|
||||
{
|
||||
mTimerService.startFadeTimer();
|
||||
}
|
||||
break;
|
||||
case FADE:
|
||||
mTimerService.stopFadeTimer();
|
||||
mTimerService.startResetTimer();
|
||||
setSquelchState( SquelchState.SQUELCH );
|
||||
if( mAudioTypeListener != null )
|
||||
{
|
||||
mAudioTypeListener.setAudioType( mAudioType );
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
mTimerService.stopResetTimer();
|
||||
mTimerService.stopFadeTimer();
|
||||
setSquelchState( SquelchState.SQUELCH );
|
||||
if( mAudioTypeListener != null )
|
||||
{
|
||||
mAudioTypeListener.setAudioType( mAudioType );
|
||||
}
|
||||
|
||||
/* If this is a traffic channel, stop it */
|
||||
if( mProcessingChain.getChannel()
|
||||
.getChannelType() == ChannelType.TRAFFIC )
|
||||
{
|
||||
mProcessingChain.getChannel().setEnabled( false );
|
||||
}
|
||||
break;
|
||||
case IDLE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
broadcastChange( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "Channel State - can't change from [" +
|
||||
mState.toString() + "] to [" + state.toString() + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* End method to reset the channel state once a call has completely
|
||||
* ended and now you want to return the channel state to idle
|
||||
*/
|
||||
protected void reset()
|
||||
{
|
||||
setState( State.END );
|
||||
|
||||
for( AuxChannelState state: mAuxChannelStates )
|
||||
{
|
||||
state.reset();
|
||||
}
|
||||
|
||||
//Reset the state to idle
|
||||
setState( State.IDLE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to perform any call fade actions. On fade, you should change the
|
||||
* channel state to fading, and construct a call-reset timer to execute a few
|
||||
* seconds later. This allows the view to signal to the user that the call
|
||||
* has faded/ended, and display faded information to the user, until the
|
||||
* cleanup() task is run.
|
||||
*/
|
||||
protected void fade( CallEventType type )
|
||||
{
|
||||
for( AuxChannelState state: mAuxChannelStates )
|
||||
{
|
||||
state.fade();
|
||||
}
|
||||
|
||||
setState( State.FADE );
|
||||
}
|
||||
|
||||
public void setListener( IAudioTypeListener listener )
|
||||
{
|
||||
mAudioTypeListener = listener;
|
||||
}
|
||||
|
||||
public void addListener( SquelchListener listener )
|
||||
{
|
||||
mSquelchListeners.add( listener );
|
||||
}
|
||||
|
||||
public void removeListener( SquelchListener listener )
|
||||
{
|
||||
mSquelchListeners.remove( listener );
|
||||
}
|
||||
|
||||
protected void setSquelchState( SquelchState state )
|
||||
{
|
||||
Iterator<SquelchListener> it = mSquelchListeners.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
it.next().setSquelch( state );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Timer service
|
||||
*/
|
||||
public class TimerService extends ScheduledThreadPoolExecutor
|
||||
{
|
||||
private long mTimerMonitorRevisitRate = 100; //100 milliseconds
|
||||
|
||||
private ScheduledFuture<?> mTimerMonitor;
|
||||
|
||||
public TimerService()
|
||||
{
|
||||
super( 1 );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
if( mTimerMonitor != null )
|
||||
{
|
||||
mTimerMonitor.cancel( true );
|
||||
}
|
||||
|
||||
this.shutdownNow();
|
||||
}
|
||||
|
||||
private void checkMonitor()
|
||||
{
|
||||
boolean createTimer = false;
|
||||
|
||||
synchronized( mCallFadeLock )
|
||||
{
|
||||
if( mFadeTimeout != -1 )
|
||||
{
|
||||
createTimer = true;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized( mCallResetLock )
|
||||
{
|
||||
if( mResetTimeout != -1 )
|
||||
{
|
||||
createTimer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( createTimer )
|
||||
{
|
||||
//Check to see if the timer is already running, if not, start it
|
||||
|
||||
if( mTimerMonitor == null || mTimerMonitor.isDone() )
|
||||
{
|
||||
scheduleAtFixedRate( new StateTimeoutMonitor(),
|
||||
mTimerMonitorRevisitRate,
|
||||
mTimerMonitorRevisitRate,
|
||||
TimeUnit.MILLISECONDS );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mTimerMonitor != null )
|
||||
{
|
||||
mTimerMonitor.cancel( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startFadeTimer()
|
||||
{
|
||||
synchronized( mCallFadeLock )
|
||||
{
|
||||
mFadeTimeout = System.currentTimeMillis() + mCallFadeTimeout;
|
||||
}
|
||||
|
||||
checkMonitor();
|
||||
}
|
||||
|
||||
public void resetFadeTimer()
|
||||
{
|
||||
startFadeTimer();
|
||||
}
|
||||
|
||||
public void stopFadeTimer()
|
||||
{
|
||||
synchronized( mCallFadeLock )
|
||||
{
|
||||
mFadeTimeout = -1;
|
||||
}
|
||||
|
||||
checkMonitor();
|
||||
}
|
||||
|
||||
public void startResetTimer()
|
||||
{
|
||||
synchronized( mCallResetLock )
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis() + mCallResetTimeout;
|
||||
}
|
||||
|
||||
checkMonitor();
|
||||
}
|
||||
|
||||
public void stopResetTimer()
|
||||
{
|
||||
synchronized( mCallResetLock )
|
||||
{
|
||||
mResetTimeout = -1;
|
||||
}
|
||||
|
||||
checkMonitor();
|
||||
}
|
||||
}
|
||||
|
||||
public class StateTimeoutMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
synchronized( mCallFadeLock )
|
||||
{
|
||||
if( mFadeTimeout != -1 && now >= mFadeTimeout )
|
||||
{
|
||||
mFadeTimeout = -1;
|
||||
|
||||
fade( CallEventType.CALL_TIMEOUT );
|
||||
}
|
||||
}
|
||||
|
||||
synchronized( mCallResetLock )
|
||||
{
|
||||
if( mResetTimeout != -1 && now >= mResetTimeout )
|
||||
{
|
||||
mResetTimeout = -1;
|
||||
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum State
|
||||
{
|
||||
IDLE( "IDLE" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CALL( "CALL" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE;
|
||||
}
|
||||
},
|
||||
DATA( "DATA" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == State.ENCRYPTED ||
|
||||
state == FADE;
|
||||
}
|
||||
},
|
||||
ENCRYPTED( "ENCRYPTED" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == FADE;
|
||||
}
|
||||
},
|
||||
CONTROL( "CONTROL" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
FADE( "FADE" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != FADE; //All states except fade allowed
|
||||
}
|
||||
},
|
||||
END( "END" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != FADE; //Fade is only disallowed state
|
||||
}
|
||||
},
|
||||
NO_TUNER( "NO TUNER" ) {
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == FADE || state == END;
|
||||
}
|
||||
};
|
||||
|
||||
private String mDisplayValue;
|
||||
|
||||
private State( String displayValue )
|
||||
{
|
||||
mDisplayValue = displayValue;
|
||||
}
|
||||
|
||||
public abstract boolean canChangeTo( State state );
|
||||
|
||||
public String getDisplayValue()
|
||||
{
|
||||
return mDisplayValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to receive channel state attribute changes
|
||||
*/
|
||||
public void addListener( Listener<ChangedAttribute> listener )
|
||||
{
|
||||
mChangeBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from receiving channel state attribute changes
|
||||
*/
|
||||
public void removeListener( Listener<ChangedAttribute> listener )
|
||||
{
|
||||
mChangeBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
public enum ChangedAttribute
|
||||
{
|
||||
AUDIO_TYPE,
|
||||
CHANNEL_NUMBER,
|
||||
CHANNEL_NAME,
|
||||
CHANNEL_SITE_NUMBER,
|
||||
CHANNEL_SITE_NUMBER_ALIAS,
|
||||
CHANNEL_STATE,
|
||||
DESCRIPTION,
|
||||
FROM_TALKGROUP,
|
||||
FROM_TALKGROUP_ALIAS,
|
||||
FROM_TALKGROUP_TYPE,
|
||||
MESSAGE,
|
||||
MESSAGE_TYPE,
|
||||
NAC,
|
||||
SITE,
|
||||
SITE_ALIAS,
|
||||
SITE_NAME,
|
||||
SOURCE,
|
||||
SYSTEM,
|
||||
SYSTEM_NAME,
|
||||
SQUELCH,
|
||||
TO_TALKGROUP,
|
||||
TO_TALKGROUP_ALIAS,
|
||||
TO_TALKGROUP_TYPE,
|
||||
WACN;
|
||||
}
|
||||
}
|
||||
@@ -1,467 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.ColorSettingMenuItem;
|
||||
import settings.ColorSettingResetMenuItem;
|
||||
import settings.SettingsManager;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEventListener;
|
||||
import decode.am.AMChannelState;
|
||||
import decode.am.AMPanel;
|
||||
import decode.fleetsync2.FleetsyncChannelState;
|
||||
import decode.fleetsync2.FleetsyncPanel;
|
||||
import decode.lj1200.LJ1200ChannelState;
|
||||
import decode.lj1200.LJ1200Panel;
|
||||
import decode.ltrnet.LTRNetChannelState;
|
||||
import decode.ltrnet.LTRNetPanel;
|
||||
import decode.ltrstandard.LTRChannelState;
|
||||
import decode.ltrstandard.LTRPanel;
|
||||
import decode.mdc1200.MDCChannelState;
|
||||
import decode.mdc1200.MDCPanel;
|
||||
import decode.mpt1327.MPT1327ChannelState;
|
||||
import decode.mpt1327.MPT1327Panel;
|
||||
import decode.nbfm.NBFMChannelState;
|
||||
import decode.nbfm.NBFMPanel;
|
||||
import decode.p25.P25ChannelState;
|
||||
import decode.p25.P25Panel;
|
||||
import decode.passport.PassportChannelState;
|
||||
import decode.passport.PassportPanel;
|
||||
|
||||
public class ChannelStateList extends JPanel implements ChannelEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private HashMap<Channel,ChannelStatePanel> mDisplayedPanels =
|
||||
new HashMap<Channel,ChannelStatePanel>();
|
||||
|
||||
private SettingsManager mSettingsManager;
|
||||
|
||||
public ChannelStateList( SettingsManager settingsManager )
|
||||
{
|
||||
mSettingsManager = settingsManager;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") );
|
||||
setBackground( Color.BLACK );
|
||||
|
||||
addMouseListener( new ListSelectionListener() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelChanged( ChannelEvent event )
|
||||
{
|
||||
ChannelType type = event.getChannel().getChannelType();
|
||||
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case PROCESSING_STARTED:
|
||||
if( type == ChannelType.STANDARD )
|
||||
{
|
||||
channelStarted( event.getChannel() );
|
||||
}
|
||||
else if( type == ChannelType.TRAFFIC )
|
||||
{
|
||||
for( Channel parent: mDisplayedPanels.keySet() )
|
||||
{
|
||||
if( parent.hasTrafficChannel( event.getChannel() ) )
|
||||
{
|
||||
mDisplayedPanels.get( parent )
|
||||
.trafficChannelAdded( event.getChannel() );
|
||||
validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROCESSING_STOPPED:
|
||||
if( type == ChannelType.STANDARD )
|
||||
{
|
||||
channelStopped( event.getChannel() );
|
||||
}
|
||||
/* Since we don't have a reference to the parent channel that
|
||||
* owns the traffic channel, iterate all channels to find the
|
||||
* parent */
|
||||
else if( type == ChannelType.TRAFFIC )
|
||||
{
|
||||
for( Channel control: mDisplayedPanels.keySet() )
|
||||
{
|
||||
if( control.hasTrafficChannel( event.getChannel() ) )
|
||||
{
|
||||
control.removeTrafficChannel( event.getChannel() );
|
||||
|
||||
mDisplayedPanels.get( control )
|
||||
.trafficChannelDeleted( event.getChannel() );
|
||||
|
||||
validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void channelStarted( Channel channel )
|
||||
{
|
||||
if( !mDisplayedPanels.containsKey( channel ) )
|
||||
{
|
||||
ChannelState state = channel.getProcessingChain().getChannelState();
|
||||
|
||||
ChannelStatePanel panel = null;
|
||||
|
||||
if( state instanceof AMChannelState )
|
||||
{
|
||||
panel = new AMPanel( channel );
|
||||
}
|
||||
else if( state instanceof NBFMChannelState )
|
||||
{
|
||||
NBFMChannelState convState =
|
||||
(NBFMChannelState)state;
|
||||
|
||||
panel = new NBFMPanel( channel );
|
||||
|
||||
for( AuxChannelState aux: convState.getAuxChannelStates() )
|
||||
{
|
||||
if( aux instanceof FleetsyncChannelState )
|
||||
{
|
||||
FleetsyncPanel fsPanel =
|
||||
new FleetsyncPanel( mSettingsManager,
|
||||
(FleetsyncChannelState)aux );
|
||||
|
||||
panel.addPanel( fsPanel );
|
||||
}
|
||||
else if( aux instanceof MDCChannelState )
|
||||
{
|
||||
MDCPanel mdsPanel = new MDCPanel( mSettingsManager,
|
||||
(MDCChannelState)aux );
|
||||
|
||||
panel.addPanel( mdsPanel );
|
||||
}
|
||||
else if( aux instanceof LJ1200ChannelState )
|
||||
{
|
||||
LJ1200Panel ljPanel = new LJ1200Panel( mSettingsManager,
|
||||
(LJ1200ChannelState)aux );
|
||||
|
||||
panel.addPanel( ljPanel );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( state instanceof LTRChannelState )
|
||||
{
|
||||
LTRChannelState ltrState = (LTRChannelState)state;
|
||||
|
||||
panel = new LTRPanel( mSettingsManager, channel );
|
||||
|
||||
for( AuxChannelState aux: ltrState.getAuxChannelStates() )
|
||||
{
|
||||
if( aux instanceof FleetsyncChannelState )
|
||||
{
|
||||
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
|
||||
(FleetsyncChannelState)aux );
|
||||
|
||||
panel.addPanel( fsPanel );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( state instanceof LTRNetChannelState )
|
||||
{
|
||||
LTRNetChannelState ltrState = (LTRNetChannelState)state;
|
||||
|
||||
panel = new LTRNetPanel( channel );
|
||||
|
||||
for( AuxChannelState aux: ltrState.getAuxChannelStates() )
|
||||
{
|
||||
if( aux instanceof FleetsyncChannelState )
|
||||
{
|
||||
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
|
||||
(FleetsyncChannelState)aux );
|
||||
|
||||
panel.addPanel( fsPanel );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( state instanceof MPT1327ChannelState )
|
||||
{
|
||||
MPT1327ChannelState mptState = (MPT1327ChannelState)state;
|
||||
|
||||
panel = new MPT1327Panel( channel );
|
||||
}
|
||||
else if( state instanceof PassportChannelState )
|
||||
{
|
||||
PassportChannelState passportState = (PassportChannelState)state;
|
||||
|
||||
panel = new PassportPanel( mSettingsManager, channel );
|
||||
|
||||
for( AuxChannelState aux: passportState.getAuxChannelStates() )
|
||||
{
|
||||
if( aux instanceof FleetsyncChannelState )
|
||||
{
|
||||
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
|
||||
(FleetsyncChannelState)aux );
|
||||
|
||||
panel.addPanel( fsPanel );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( state instanceof P25ChannelState )
|
||||
{
|
||||
panel = new P25Panel( channel );
|
||||
}
|
||||
|
||||
if( panel != null )
|
||||
{
|
||||
add( panel, "wrap" );
|
||||
|
||||
mDisplayedPanels.put( channel, panel );
|
||||
|
||||
validate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void channelStopped( Channel channel )
|
||||
{
|
||||
if( mDisplayedPanels.containsKey( channel ) )
|
||||
{
|
||||
ChannelStatePanel panel = mDisplayedPanels.get( channel );
|
||||
|
||||
if( panel != null )
|
||||
{
|
||||
remove( panel );
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
mDisplayedPanels.remove( channel );
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedChannel( ChannelStatePanel selectedPanel )
|
||||
{
|
||||
//De-select any other selected panel
|
||||
for( ChannelStatePanel panel: mDisplayedPanels.values() )
|
||||
{
|
||||
if( panel != selectedPanel && panel.isSelected() )
|
||||
{
|
||||
panel.setSelected( false );
|
||||
}
|
||||
}
|
||||
|
||||
//Toggle selection state
|
||||
selectedPanel.setSelected( !selectedPanel.isSelected() );
|
||||
}
|
||||
|
||||
public class ListSelectionListener implements MouseListener
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked( MouseEvent e )
|
||||
{
|
||||
Component component = getComponentAt( e.getPoint() );
|
||||
|
||||
if( component instanceof ChannelStatePanel )
|
||||
{
|
||||
ChannelStatePanel panel = (ChannelStatePanel)component;
|
||||
|
||||
/* If the panel has child traffic channels, attempt to get
|
||||
* one of them for the mouse event */
|
||||
if( panel.getChannel().getTrafficChannels().size() > 0 )
|
||||
{
|
||||
for( ChannelStatePanel trafficPanel: panel.getTrafficPanels().values() )
|
||||
{
|
||||
Point translatedPoint = SwingUtilities
|
||||
.convertPoint( e.getComponent(), e.getPoint(),
|
||||
trafficPanel );
|
||||
|
||||
if( trafficPanel.contains( translatedPoint ) )
|
||||
{
|
||||
panel = trafficPanel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( e.getButton() == MouseEvent.BUTTON1 )
|
||||
{
|
||||
setSelectedChannel( panel );
|
||||
}
|
||||
else if( e.getButton() == MouseEvent.BUTTON3 )
|
||||
{
|
||||
JPopupMenu popupMenu = new JPopupMenu();
|
||||
JMenu menu = panel.getChannel().getContextMenu();
|
||||
|
||||
popupMenu.add( menu );
|
||||
|
||||
JMenu colorMenu = new JMenu( "Color" );
|
||||
popupMenu.add( colorMenu );
|
||||
|
||||
JMenu backgroundMenu = new JMenu( "Background" );
|
||||
colorMenu.add( backgroundMenu );
|
||||
|
||||
backgroundMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND ) );
|
||||
backgroundMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND ) );
|
||||
|
||||
JMenu channelsMenu = new JMenu( "Channel State" );
|
||||
colorMenu.add( channelsMenu );
|
||||
|
||||
JMenu callMenu = new JMenu( "Call" );
|
||||
channelsMenu.add( callMenu );
|
||||
|
||||
callMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ) );
|
||||
callMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ) );
|
||||
callMenu.add( new JSeparator() );
|
||||
|
||||
callMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ) );
|
||||
callMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ) );
|
||||
|
||||
JMenu controlMenu = new JMenu( "Control" );
|
||||
channelsMenu.add( controlMenu );
|
||||
|
||||
controlMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ) );
|
||||
controlMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ) );
|
||||
controlMenu.add( new JSeparator() );
|
||||
|
||||
controlMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ) );
|
||||
controlMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ) );
|
||||
|
||||
JMenu dataMenu = new JMenu( "Data" );
|
||||
channelsMenu.add( dataMenu );
|
||||
|
||||
dataMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ) );
|
||||
dataMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ) );
|
||||
dataMenu.add( new JSeparator() );
|
||||
|
||||
dataMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ) );
|
||||
dataMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ) );
|
||||
|
||||
JMenu fadeMenu = new JMenu( "Fade" );
|
||||
channelsMenu.add( fadeMenu );
|
||||
|
||||
fadeMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ) );
|
||||
fadeMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ) );
|
||||
fadeMenu.add( new JSeparator() );
|
||||
|
||||
fadeMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ) );
|
||||
fadeMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ) );
|
||||
|
||||
JMenu idleMenu = new JMenu( "Idle" );
|
||||
channelsMenu.add( idleMenu );
|
||||
|
||||
idleMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ) );
|
||||
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ) );
|
||||
idleMenu.add( new JSeparator() );
|
||||
|
||||
idleMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
|
||||
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
|
||||
|
||||
JMenu noTunerMenu = new JMenu( "No Tuner" );
|
||||
channelsMenu.add( noTunerMenu );
|
||||
|
||||
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
|
||||
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
|
||||
noTunerMenu.add( new JSeparator() );
|
||||
|
||||
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
|
||||
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
|
||||
|
||||
JMenu labelMenu = new JMenu( "Labels" );
|
||||
colorMenu.add( labelMenu );
|
||||
|
||||
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) );
|
||||
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) );
|
||||
labelMenu.add( new JSeparator() );
|
||||
|
||||
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) );
|
||||
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) );
|
||||
labelMenu.add( new JSeparator() );
|
||||
|
||||
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ) );
|
||||
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ) );
|
||||
|
||||
JMenu selectedMenu = new JMenu( "Selected" );
|
||||
colorMenu.add( selectedMenu );
|
||||
|
||||
selectedMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) );
|
||||
selectedMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) );
|
||||
|
||||
popupMenu.show( ChannelStateList.this, e.getX(), e.getY() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed( MouseEvent e ) {}
|
||||
public void mouseReleased( MouseEvent e ) {}
|
||||
public void mouseEntered( MouseEvent e ) {}
|
||||
public void mouseExited( MouseEvent e ) {}
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Font;
|
||||
import java.awt.GradientPaint;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Paint;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import sample.Listener;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingChangeListener;
|
||||
import settings.SettingsManager;
|
||||
import controller.channel.Channel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelState.State;
|
||||
|
||||
public abstract class ChannelStatePanel extends JPanel
|
||||
implements Listener<ChangedAttribute>,
|
||||
SettingChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
|
||||
protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
|
||||
protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
|
||||
|
||||
protected Color mColorChannelBackground;
|
||||
protected Color mColorChannelSelected;
|
||||
protected Color mColorTopCall;
|
||||
protected Color mColorMiddleCall;
|
||||
protected Color mColorTopControl;
|
||||
protected Color mColorMiddleControl;
|
||||
protected Color mColorTopData;
|
||||
protected Color mColorMiddleData;
|
||||
protected Color mColorTopFade;
|
||||
protected Color mColorMiddleFade;
|
||||
protected Color mColorTopIdle;
|
||||
protected Color mColorMiddleIdle;
|
||||
protected Color mColorTopNoTuner;
|
||||
protected Color mColorMiddleNoTuner;
|
||||
protected Color mColorLabelDetails;
|
||||
protected Color mColorLabelDecoder;
|
||||
protected Color mColorLabelAuxDecoder;
|
||||
|
||||
private HashMap<Channel,ChannelStatePanel> mTrafficPanels =
|
||||
new HashMap<Channel,ChannelStatePanel>();
|
||||
|
||||
protected Channel mChannel;
|
||||
|
||||
public ChannelStatePanel( Channel channel )
|
||||
{
|
||||
mChannel = channel;
|
||||
|
||||
/* Register to receive settings updates */
|
||||
getSettingsManager().addListener( this );
|
||||
|
||||
/* Register to receive channel updates */
|
||||
mChannel.getProcessingChain().getChannelState().addListener( this );
|
||||
|
||||
getColors();
|
||||
|
||||
setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") );
|
||||
}
|
||||
|
||||
public SettingsManager getSettingsManager()
|
||||
{
|
||||
return mChannel.getResourceManager().getSettingsManager();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mTrafficPanels.clear();
|
||||
mChannel = null;
|
||||
}
|
||||
|
||||
private void getColors()
|
||||
{
|
||||
mColorChannelBackground = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor();
|
||||
mColorChannelSelected = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor();
|
||||
mColorTopCall = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor();
|
||||
mColorMiddleCall = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor();
|
||||
mColorTopControl = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor();
|
||||
mColorMiddleControl = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor();
|
||||
mColorTopData = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor();
|
||||
mColorMiddleData = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor();
|
||||
mColorTopFade = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor();
|
||||
mColorMiddleFade = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor();
|
||||
mColorTopIdle = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor();
|
||||
mColorMiddleIdle = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor();
|
||||
mColorTopNoTuner = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor();
|
||||
mColorMiddleNoTuner = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor();
|
||||
|
||||
mColorLabelDetails = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor();
|
||||
mColorLabelDecoder = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor();
|
||||
mColorLabelAuxDecoder = getSettingsManager().getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor();
|
||||
}
|
||||
|
||||
public HashMap<Channel,ChannelStatePanel> getTrafficPanels()
|
||||
{
|
||||
return mTrafficPanels;
|
||||
}
|
||||
|
||||
public JMenu getContextMenu()
|
||||
{
|
||||
return mChannel.getContextMenu();
|
||||
}
|
||||
|
||||
public Channel getChannel()
|
||||
{
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
public ChannelState getChannelState()
|
||||
{
|
||||
if( mChannel.getProcessingChain() != null )
|
||||
{
|
||||
return mChannel.getProcessingChain().getChannelState();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSelected()
|
||||
{
|
||||
return mChannel.isSelected();
|
||||
}
|
||||
|
||||
public void addPanel( final JPanel panel )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
add( panel, "span,wrap" );
|
||||
validate();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public void removePanel( final JPanel panel )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
remove( panel );
|
||||
|
||||
validate();
|
||||
|
||||
if( getParent() != null )
|
||||
{
|
||||
getParent().validate();
|
||||
}
|
||||
|
||||
if( panel instanceof ChannelStatePanel )
|
||||
{
|
||||
((ChannelStatePanel)panel).dispose();
|
||||
}
|
||||
else if( panel instanceof AuxStatePanel )
|
||||
{
|
||||
((AuxStatePanel)panel).dispose();
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
public void trafficChannelAdded( Channel traffic )
|
||||
{
|
||||
//Disregard -- sub class can override and implement
|
||||
}
|
||||
|
||||
public void trafficChannelDeleted( Channel traffic )
|
||||
{
|
||||
ChannelStatePanel panelToRemove =
|
||||
getTrafficPanels().remove( traffic );
|
||||
|
||||
removePanel( panelToRemove );
|
||||
}
|
||||
|
||||
public void setSelected( boolean selected )
|
||||
{
|
||||
for( ChannelStatePanel traffic: getTrafficPanels().values() )
|
||||
{
|
||||
traffic.setSelected( false );
|
||||
}
|
||||
|
||||
mChannel.setSelected( selected );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private GradientPaint getGradient( Color top, Color middle )
|
||||
{
|
||||
return new GradientPaint( 0f, -50.0f, top,
|
||||
0f, (float)getHeight() / 2.2f, middle, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g)
|
||||
{
|
||||
super.paintComponent( g );
|
||||
|
||||
setBackground( mColorChannelBackground );
|
||||
|
||||
Graphics2D g2 = (Graphics2D)g.create();
|
||||
|
||||
Paint p = null;
|
||||
|
||||
ChannelState channelState = getChannelState();
|
||||
|
||||
if( channelState != null )
|
||||
{
|
||||
State state = channelState.getState();
|
||||
|
||||
if( state != null )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
case CALL:
|
||||
p = getGradient( mColorTopCall, mColorMiddleCall );
|
||||
break;
|
||||
case CONTROL:
|
||||
p = getGradient( mColorTopControl, mColorMiddleControl );
|
||||
break;
|
||||
case DATA:
|
||||
p = getGradient( mColorTopData, mColorMiddleData );
|
||||
break;
|
||||
case FADE:
|
||||
p = getGradient( mColorTopFade, mColorMiddleFade );
|
||||
break;
|
||||
case NO_TUNER:
|
||||
p = getGradient( mColorTopNoTuner, mColorMiddleNoTuner );
|
||||
break;
|
||||
case END:
|
||||
case IDLE:
|
||||
default:
|
||||
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g2.setPaint( p );
|
||||
g2.fillRect( 0, 0, getWidth(), getHeight() );
|
||||
|
||||
/* Draw bottom separator line */
|
||||
g2.setColor( Color.LIGHT_GRAY );
|
||||
g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 );
|
||||
|
||||
/* Draw channel selected box */
|
||||
if( mChannel.isSelected() )
|
||||
{
|
||||
g2.setColor( mColorChannelSelected );
|
||||
g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 );
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
switch( colorSetting.getColorSettingName() )
|
||||
{
|
||||
case CHANNEL_STATE_BACKGROUND:
|
||||
mColorChannelBackground = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_CALL:
|
||||
mColorMiddleCall = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_CALL:
|
||||
mColorTopCall = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL:
|
||||
mColorMiddleControl = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_CONTROL:
|
||||
mColorTopControl = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_DATA:
|
||||
mColorMiddleData = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_DATA:
|
||||
mColorTopData = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_FADE:
|
||||
mColorMiddleFade = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_FADE:
|
||||
mColorTopFade = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE:
|
||||
mColorMiddleIdle = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_IDLE:
|
||||
mColorTopIdle = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER:
|
||||
mColorMiddleNoTuner = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER:
|
||||
mColorTopNoTuner = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_AUX_DECODER:
|
||||
mColorLabelAuxDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DECODER:
|
||||
mColorLabelDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
mColorLabelDetails = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_SELECTED_CHANNEL:
|
||||
mColorChannelSelected = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted( Setting setting ) {}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 controller.state;
|
||||
|
||||
|
||||
public interface ChannelStateSelectionListener
|
||||
{
|
||||
public void selected( ChannelState channelState );
|
||||
}
|
||||
Reference in New Issue
Block a user