Adds message filtering, message history clear, and message history size features to the 'Messages' tab. Filter window dynamically constructs a list of filters from the primary filter and each of the auxiliary filters enabled within the currently selected/enabled processing channel.

This commit is contained in:
dennis.sheirer
2014-12-12 04:30:31 +00:00
parent 33d18b1186
commit 0d8da161e1
28 changed files with 1693 additions and 53 deletions

View File

@@ -52,7 +52,7 @@ public class ConfigurationTreePanel extends JPanel
mSystemTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION );
mSystemTree.setShowsRootHandles( true );
mSystemTree.addMouseListener( new Handler() );
mSystemTree.addMouseListener( new MouseHandler() );
mSystemTree.setCellRenderer( new ControllerTreeCellRenderer() );
//Give the model a reference to the tree, so that it can force newly
@@ -68,9 +68,9 @@ public class ConfigurationTreePanel extends JPanel
mSystemTree.addTreeSelectionListener( listener );
}
public class Handler implements MouseListener
public class MouseHandler implements MouseListener
{
public Handler()
public MouseHandler()
{
}

View File

@@ -21,6 +21,7 @@ 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;
@@ -131,7 +132,7 @@ public class ControllerPanel extends JPanel
MapPanel mapPanel = new MapPanel( mResourceManager );
mTabbedPane.addTab( "Map", mapPanel );
/* Channel state list */
mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() );

View File

@@ -18,8 +18,10 @@
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;
@@ -63,7 +65,13 @@ public class ActivitySummaryFrame extends JFrame
} );
add( close );
setVisible( true );
EventQueue.invokeLater( new Runnable()
{
public void run()
{
setVisible( true );
}
} );
}
}

View File

@@ -25,6 +25,7 @@ import javax.swing.table.AbstractTableModel;
import message.Message;
import sample.Listener;
import filter.FilterSet;
public class MessageActivityModel extends AbstractTableModel
implements Listener<Message>
@@ -51,10 +52,41 @@ public class MessageActivityModel extends AbstractTableModel
private boolean mNewMessagesFirst = true;
private FilterSet mMessageFilter;
public MessageActivityModel( FilterSet 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 getMessageFilter()
{
return mMessageFilter;
}
public void dispose()
{
mMessages.clear();
@@ -90,18 +122,21 @@ public class MessageActivityModel extends AbstractTableModel
public void receive( final Message message )
{
EventQueue.invokeLater( new Runnable()
if( mMessageFilter.passes( message ) )
{
@Override
public void run()
{
mMessages.addFirst( message );
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
mMessages.addFirst( message );
MessageActivityModel.this.fireTableRowsInserted( 0, 0 );
MessageActivityModel.this.fireTableRowsInserted( 0, 0 );
prune();
}
} );
prune();
}
} );
}
}
private void prune()

View File

@@ -17,11 +17,37 @@
******************************************************************************/
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;
@@ -30,23 +56,33 @@ 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 JScrollPane mEmptyScrollPane;
private Channel mDisplayedChannel;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
public MessageActivityPanel()
{
setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") );
setLayout( new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]") );
mEmptyScrollPane =
new JScrollPane( new JTable( new MessageActivityModel() ) );
add( mManagementPanel, "span,growx" );
mMessageTable = new JTable( EMPTY_MODEL );
add( mEmptyScrollPane );
add( new JScrollPane( mMessageTable ), "span,grow" );
}
@Override
public void channelChanged( ChannelEvent event )
{
boolean changed = false;
if( event.getEvent() == Event.CHANGE_SELECTED &&
event.getChannel().isSelected() )
{
@@ -54,18 +90,21 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener
( mDisplayedChannel != null &&
mDisplayedChannel != event.getChannel() ) )
{
removeAll();
JScrollPane scroll =
new JScrollPane( event.getChannel().getProcessingChain()
.getChannelState().getMessageActivityTable() );
add( scroll );
mDisplayedChannel = event.getChannel();
mMessageTable.setModel( event.getChannel().getProcessingChain()
.getChannelState().getMessageActivityModel() );
if( mDisplayedChannel != null )
{
mManagementPanel.enable();
}
else
{
mManagementPanel.disable();
}
revalidate();
repaint();
changed = true;
}
}
else if( event.getEvent() == Event.PROCESSING_STOPPED ||
@@ -75,12 +114,203 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener
mDisplayedChannel == event.getChannel() )
{
mDisplayedChannel = null;
removeAll();
add( mEmptyScrollPane );
revalidate();
repaint();
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 =
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 );
}
} );
}
}
} );
}
}
}

View File

@@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
import javax.swing.JTable;
import message.Message;
import message.MessageFilterFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,6 +46,7 @@ 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
@@ -108,8 +110,7 @@ public abstract class ChannelState implements Listener<Message>
protected JTable mCallEventTable;
protected MessageActivityModel mMessageActivityModel =
new MessageActivityModel();
protected MessageActivityModel mMessageActivityModel;
protected JTable mMessageActivityTable;
protected ProcessingChain mProcessingChain;
@@ -130,6 +131,12 @@ public abstract class ChannelState implements Listener<Message>
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 )
@@ -240,20 +247,11 @@ public abstract class ChannelState implements Listener<Message>
}
/**
* Returns the message activity model wrapped in a JTable. Although this
* may seem to violate separation between model and view, it simplifies
* retaining the JTable state for each channel by keeping a local reference
* to reuse
* Returns the message activity model.
*/
public JTable getMessageActivityTable()
public MessageActivityModel getMessageActivityModel()
{
if( mMessageActivityTable == null )
{
mMessageActivityTable = new JTable( mMessageActivityModel );
mMessageActivityTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
}
return mMessageActivityTable;
return mMessageActivityModel;
}
public void addAuxChannelState( AuxChannelState state )

View File

@@ -67,7 +67,7 @@ public enum DecoderType
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
DecoderType.P25_PHASE1,
// DecoderType.P25_PHASE1,
DecoderType.PASSPORT );
}

View File

@@ -0,0 +1,56 @@
package decode.fleetsync2;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import filter.Filter;
import filter.FilterElement;
public class FleetsyncMessageFilter extends Filter<Message>
{
private HashMap<FleetsyncMessageType, FilterElement<FleetsyncMessageType>> mElements =
new HashMap<FleetsyncMessageType, FilterElement<FleetsyncMessageType>>();
public FleetsyncMessageFilter()
{
super( "Fleetsync Message Filter" );
for( FleetsyncMessageType type: FleetsyncMessageType.values() )
{
if( type != FleetsyncMessageType.UNKNOWN )
{
mElements.put( type, new FilterElement<FleetsyncMessageType>( type ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
FleetsyncMessage fleet = (FleetsyncMessage)message;
if( mElements.containsKey( fleet.getMessageType() ) )
{
return mElements.get( fleet.getMessageType() ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof FleetsyncMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mElements.values() );
}
}

View File

@@ -0,0 +1,35 @@
package decode.lj1200;
import java.util.Collections;
import java.util.List;
import message.Message;
import filter.Filter;
import filter.FilterElement;
public class LJ1200MessageFilter extends Filter<Message>
{
public LJ1200MessageFilter()
{
super( "LJ-1200 Message Filter" );
}
@Override
public boolean passes( Message message )
{
return mEnabled && canProcess( message );
}
@Override
public boolean canProcess( Message message )
{
return message instanceof LJ1200Message;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -0,0 +1,80 @@
package decode.ltrnet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import message.MessageType;
import filter.Filter;
import filter.FilterElement;
public class LTRNetMessageFilter extends Filter<Message>
{
private HashMap<MessageType, FilterElement<MessageType>> mElements =
new HashMap<MessageType, FilterElement<MessageType>>();
public LTRNetMessageFilter()
{
super( "LTR-Net Message Filter" );
mElements.put( MessageType.CA_STRT,
new FilterElement<MessageType>( MessageType.CA_STRT ) );
mElements.put( MessageType.CA_ENDD,
new FilterElement<MessageType>( MessageType.CA_ENDD ) );
mElements.put( MessageType.SY_IDLE,
new FilterElement<MessageType>( MessageType.SY_IDLE ) );
mElements.put( MessageType.ID_UNIQ,
new FilterElement<MessageType>( MessageType.ID_UNIQ ) );
mElements.put( MessageType.ID_ESNH,
new FilterElement<MessageType>( MessageType.ID_ESNH ) );
mElements.put( MessageType.ID_ESNL,
new FilterElement<MessageType>( MessageType.ID_ESNL ) );
mElements.put( MessageType.RQ_ACCE,
new FilterElement<MessageType>( MessageType.RQ_ACCE ) );
mElements.put( MessageType.ID_SITE,
new FilterElement<MessageType>( MessageType.ID_SITE ) );
mElements.put( MessageType.FQ_RXHI,
new FilterElement<MessageType>( MessageType.FQ_RXHI ) );
mElements.put( MessageType.FQ_RXLO,
new FilterElement<MessageType>( MessageType.FQ_RXLO ) );
mElements.put( MessageType.FQ_TXHI,
new FilterElement<MessageType>( MessageType.FQ_TXHI ) );
mElements.put( MessageType.FQ_TXLO,
new FilterElement<MessageType>( MessageType.FQ_TXLO ) );
mElements.put( MessageType.ID_NBOR,
new FilterElement<MessageType>( MessageType.ID_NBOR ) );
mElements.put( MessageType.MA_CHNH,
new FilterElement<MessageType>( MessageType.MA_CHNH ) );
mElements.put( MessageType.MA_CHNL,
new FilterElement<MessageType>( MessageType.MA_CHNL ) );
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
LTRNetMessage ltr = (LTRNetMessage)message;
if( mElements.containsKey( ltr.getMessageType() ) )
{
return mElements.get( ltr.getMessageType() ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof LTRNetMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mElements.values() );
}
}

View File

@@ -0,0 +1,56 @@
package decode.ltrstandard;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import message.MessageType;
import filter.Filter;
import filter.FilterElement;
public class LTRStandardMessageFilter extends Filter<Message>
{
private HashMap<MessageType, FilterElement<MessageType>> mElements =
new HashMap<MessageType, FilterElement<MessageType>>();
public LTRStandardMessageFilter()
{
super( "LTR Message Filter" );
mElements.put( MessageType.CA_STRT,
new FilterElement<MessageType>( MessageType.CA_STRT ) );
mElements.put( MessageType.CA_ENDD,
new FilterElement<MessageType>( MessageType.CA_ENDD ) );
mElements.put( MessageType.SY_IDLE,
new FilterElement<MessageType>( MessageType.SY_IDLE ) );
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
LTRStandardMessage ltr = (LTRStandardMessage)message;
if( mElements.containsKey( ltr.getMessageType() ) )
{
return mElements.get( ltr.getMessageType() ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof LTRStandardMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mElements.values() );
}
}

View File

@@ -0,0 +1,56 @@
package decode.mdc1200;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import filter.Filter;
import filter.FilterElement;
public class MDCMessageFilter extends Filter<Message>
{
private HashMap<MDCMessageType, FilterElement<MDCMessageType>> mElements =
new HashMap<MDCMessageType, FilterElement<MDCMessageType>>();
public MDCMessageFilter()
{
super( "MDC-1200 Message Filter" );
for( MDCMessageType type: MDCMessageType.values() )
{
if( type != MDCMessageType.UNKNOWN )
{
mElements.put( type, new FilterElement<MDCMessageType>( type ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MDCMessage mdc = (MDCMessage)message;
if( mElements.containsKey( mdc.getMessageType() ) )
{
return mElements.get( mdc.getMessageType() ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof MDCMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mElements.values() );
}
}

View File

@@ -1930,6 +1930,11 @@ public class MPT1327Message extends Message
{
return mDescription;
}
public String toString()
{
return getDescription();
}
public static MPTMessageType fromNumber( int number )
{

View File

@@ -0,0 +1,62 @@
package decode.mpt1327;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.mpt1327.MPT1327Message.MPTMessageType;
import filter.Filter;
import filter.FilterElement;
public class MPT1327MessageFilter extends Filter<Message>
{
private HashMap<MPTMessageType,FilterElement<MPTMessageType>> mFilterElements =
new HashMap<MPTMessageType,FilterElement<MPTMessageType>>();
public MPT1327MessageFilter()
{
super( "MPT1327 Message Type Filter" );
for( MPTMessageType type: MPT1327Message.MPTMessageType.values() )
{
if( type != MPTMessageType.UNKN )
{
mFilterElements.put( type,
new FilterElement<MPTMessageType>( type ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MPT1327Message mpt = (MPT1327Message)message;
FilterElement<MPTMessageType> element =
mFilterElements.get( mpt.getMessageType() );
if( element != null )
{
return element.isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof MPT1327Message;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mFilterElements.values() );
}
}

View File

@@ -1,6 +1,5 @@
package decode.p25;
import java.util.Arrays;
import java.util.HashMap;
import message.Message;
@@ -8,12 +7,9 @@ import message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BitSetBuffer;
import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
import decode.mpt1327.MPT1327Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver;

View File

@@ -0,0 +1,23 @@
package decode.p25.message.filter;
import message.Message;
import decode.p25.message.P25Message;
import filter.FilterSet;
public class P25MessageFilterSet extends FilterSet<Message>
{
public P25MessageFilterSet()
{
super( "P25 Message Filter" );
addFilter( new PDUMessageFilter() );
addFilter( new TDUMessageFilter() );
addFilter( new TSBKMessageFilter() );
}
@Override
public boolean canProcess( Message message )
{
return message instanceof P25Message;
}
}

View File

@@ -0,0 +1,54 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.pdu.PDUMessage;
import decode.p25.reference.Opcode;
import filter.Filter;
import filter.FilterElement;
public class PDUMessageFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mOpcodeFilterElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public PDUMessageFilter()
{
super( "PDU - Packet Data Unit" );
for( Opcode opcode: Opcode.values() )
{
mOpcodeFilterElements.put( opcode, new FilterElement<Opcode>( opcode ) );
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && message instanceof PDUMessage )
{
PDUMessage pdu = (PDUMessage)message;
Opcode opcode = pdu.getOpcode();
return mOpcodeFilterElements.get( opcode ).isEnabled();
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof PDUMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mOpcodeFilterElements.values() );
}
}

View File

@@ -0,0 +1,35 @@
package decode.p25.message.filter;
import java.util.Collections;
import java.util.List;
import message.Message;
import decode.p25.message.tdu.TDUMessage;
import filter.Filter;
import filter.FilterElement;
public class TDUMessageFilter extends Filter<Message>
{
public TDUMessageFilter()
{
super( "TDU Terminator Data Unit" );
}
@Override
public boolean passes( Message message )
{
return mEnabled && canProcess( message );
}
@Override
public boolean canProcess( Message message )
{
return message instanceof TDUMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

View File

@@ -0,0 +1,54 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.Opcode;
import filter.Filter;
import filter.FilterElement;
public class TSBKMessageFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mFilterElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public TSBKMessageFilter()
{
super( "TSBK Trunking Signalling Block" );
for( Opcode opcode: Opcode.values() )
{
mFilterElements.put( opcode, new FilterElement<Opcode>( opcode ) );
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
TSBKMessage tsbk = (TSBKMessage)message;
Opcode opcode = tsbk.getOpcode();
return mFilterElements.get( opcode ).isEnabled();
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof TSBKMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mFilterElements.values() );
}
}

View File

@@ -0,0 +1,68 @@
package decode.passport;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import message.MessageType;
import filter.Filter;
import filter.FilterElement;
public class PassportMessageFilter extends Filter<Message>
{
private HashMap<MessageType, FilterElement<MessageType>> mElements =
new HashMap<MessageType, FilterElement<MessageType>>();
public PassportMessageFilter()
{
super( "Passport Message Filter" );
mElements.put( MessageType.CA_STRT,
new FilterElement<MessageType>( MessageType.CA_STRT ) );
mElements.put( MessageType.CA_ENDD,
new FilterElement<MessageType>( MessageType.CA_ENDD ) );
mElements.put( MessageType.SY_IDLE,
new FilterElement<MessageType>( MessageType.SY_IDLE ) );
mElements.put( MessageType.ID_TGAS,
new FilterElement<MessageType>( MessageType.ID_TGAS ) );
mElements.put( MessageType.ID_ESNH,
new FilterElement<MessageType>( MessageType.ID_ESNH ) );
mElements.put( MessageType.CA_PAGE,
new FilterElement<MessageType>( MessageType.CA_PAGE ) );
mElements.put( MessageType.ID_RDIO,
new FilterElement<MessageType>( MessageType.ID_RDIO ) );
mElements.put( MessageType.DA_STRT,
new FilterElement<MessageType>( MessageType.DA_STRT ) );
mElements.put( MessageType.RA_REGI,
new FilterElement<MessageType>( MessageType.RA_REGI ) );
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
PassportMessage passport = (PassportMessage)message;
if( mElements.containsKey( passport.getMessageType() ) )
{
return mElements.get( passport.getMessageType() ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof PassportMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mElements.values() );
}
}

View File

@@ -0,0 +1,35 @@
package filter;
import java.util.Collections;
import java.util.List;
/**
* Implements a message filter that passes (true) all messages
*/
@SuppressWarnings( "rawtypes" )
public class AllPassFilter<T> extends Filter<T>
{
public AllPassFilter()
{
super( "Allow All Messages" );
}
@Override
public boolean passes( T t )
{
return true;
}
@Override
public boolean canProcess( T t )
{
return true;
}
@Override
@SuppressWarnings( "unchecked" )
public List getFilterElements()
{
return Collections.EMPTY_LIST;
}
}

55
src/filter/Filter.java Normal file
View File

@@ -0,0 +1,55 @@
package filter;
import java.util.List;
public abstract class Filter<T> implements IFilter<T>
{
protected String mName;
protected boolean mEnabled = true;
public Filter( String name )
{
mName = name;
}
/**
* Indicates if this filter is enabled or disabled
*/
public boolean isEnabled()
{
return mEnabled;
}
/**
* Enables (true) or disables (false) this filter. An enabled filter will
* evaluate messages and a disabled filter will return false on all message
* test methods.
*/
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
/**
* List of filter elements managed by this filter
*/
public abstract List<FilterElement<?>> getFilterElements();
/**
* Name of this filter
*/
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public String toString()
{
return mName;
}
}

View File

@@ -0,0 +1,400 @@
package filter;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.List;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeSelectionModel;
import net.miginfocom.swing.MigLayout;
public class FilterEditorPanel<T> extends JPanel
{
private static final long serialVersionUID = 1L;
private JTree mTree;
private DefaultTreeModel mModel;
private FilterSet<T> mFilterSet;
public FilterEditorPanel( FilterSet<T> filterSet )
{
setLayout( new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]") );
mFilterSet = filterSet;
init();
}
private void init()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode( mFilterSet );
mModel = new DefaultTreeModel( root );
addFilterSet( mFilterSet, root );
mTree = new JTree( mModel );
mTree.setShowsRootHandles( true );
mTree.addMouseListener( new MouseHandler() );
mTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION );
mTree.setCellRenderer( new EditorTreeCellRenderer() );
mTree.setCellEditor( new FilterTreeNodeEditor() );
mTree.setEditable( true );
add( mTree );
}
private void addFilterSet( FilterSet<T> filterSet, DefaultMutableTreeNode parent )
{
List<IFilter<T>> filters = filterSet.getFilters();
/* sort the filters in alphabetical order by name */
Collections.sort( filters, new Comparator<IFilter<?>>()
{
@Override
public int compare( IFilter<?> first, IFilter<?> second )
{
return first.getName().compareTo( second.getName() );
}
});
for( IFilter<T> filter: filters )
{
DefaultMutableTreeNode child = new DefaultMutableTreeNode( filter );
mModel.insertNodeInto( child, parent, parent.getChildCount() );
if( filter instanceof FilterSet )
{
addFilterSet( (FilterSet<T>)filter, child );
}
else if( filter instanceof Filter )
{
addFilter( (Filter<T>)filter, child );
}
}
}
private void addFilter( Filter<T> filter, DefaultMutableTreeNode parent )
{
List<FilterElement<?>> elements = filter.getFilterElements();
Collections.sort( elements );
for( FilterElement<?> element: elements )
{
DefaultMutableTreeNode child = new DefaultMutableTreeNode( element );
mModel.insertNodeInto( child, parent, parent.getChildCount() );
}
}
public class MouseHandler implements MouseListener
{
public MouseHandler()
{
}
@Override
public void mouseClicked( MouseEvent event )
{
if( SwingUtilities.isRightMouseButton( event ) )
{
int row = mTree.getRowForLocation( event.getX(),
event.getY() );
if( row != -1 )
{
mTree.setSelectionRow( row );
Object selectedNode =
mTree.getLastSelectedPathComponent();
if( selectedNode instanceof DefaultMutableTreeNode )
{
final DefaultMutableTreeNode node =
(DefaultMutableTreeNode)selectedNode;
if( node.getChildCount() > 0 )
{
JPopupMenu selectionMenu = new JPopupMenu();
JMenuItem selectAll = new JMenuItem( "Select All" );
selectAll.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
setFilterEnabled( mModel, node, true );
}
});
selectionMenu.add( selectAll );
JMenuItem deselectAll = new JMenuItem( "Deselect All" );
deselectAll.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
setFilterEnabled( mModel, node, false );
}
});
selectionMenu.add( deselectAll );
selectionMenu.show( mTree,
event.getX(),
event.getY() );
}
}
}
}
}
@Override
public void mousePressed( MouseEvent e ){}
@Override
public void mouseReleased( MouseEvent e ){}
@Override
public void mouseEntered( MouseEvent e ){}
@Override
public void mouseExited( MouseEvent e ){}
}
/**
* Recursively sets the enabled state on all filter node children below the
* specified parent node.
*
* @param model - model containing the tree nodes
* @param node - parent node
* @param enabled - true or false to enable or disable the filters
*/
private void setFilterEnabled( DefaultTreeModel model,
DefaultMutableTreeNode node,
boolean enabled )
{
Object obj = node.getUserObject();
if( obj instanceof FilterSet<?> )
{
((FilterSet<?>)obj).setEnabled( enabled );
}
else if( obj instanceof Filter )
{
((Filter<?>)obj).setEnabled( enabled );
}
else if( obj instanceof FilterElement )
{
((FilterElement<?>)obj).setEnabled( enabled );
}
model.nodeChanged( node );
/* Recursively set the children of this node */
Enumeration<?> children = node.children();
while( children.hasMoreElements() )
{
Object child = children.nextElement();
if( child instanceof DefaultMutableTreeNode )
{
setFilterEnabled( model, (DefaultMutableTreeNode)child, enabled );
}
}
}
public class EditorTreeCellRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
public Component getTreeCellRendererComponent( JTree tree,
Object treeNode,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus )
{
if( treeNode instanceof DefaultMutableTreeNode )
{
Object userObject = ((DefaultMutableTreeNode)treeNode).getUserObject();
JCheckBox checkBox = null;
if( userObject instanceof IFilter )
{
IFilter<?> filter = (IFilter<?>)userObject;
checkBox = new JCheckBox( filter.getName() );
checkBox.setSelected( filter.isEnabled() );
}
else if( userObject instanceof FilterElement )
{
FilterElement<?> element = (FilterElement<?>)userObject;
checkBox = new JCheckBox( element.getName() );
checkBox.setSelected( element.isEnabled() );
}
if( checkBox != null )
{
if( selected )
{
checkBox.setForeground( getTextSelectionColor() );
checkBox.setBackground( getBackgroundSelectionColor() );
}
else
{
checkBox.setForeground( getTextNonSelectionColor() );
checkBox.setBackground( getBackgroundNonSelectionColor() );
}
return checkBox;
}
}
return super.getTreeCellRendererComponent( tree, treeNode, selected,
expanded, leaf, row, hasFocus );
}
}
public class FilterCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
private IFilter<T> mFilter;
public FilterCheckBox( IFilter<T> filter )
{
super( filter.getName() );
mFilter = filter;
setSelected( mFilter.isEnabled() );
addItemListener( new ItemListener()
{
@Override
public void itemStateChanged( ItemEvent e )
{
mFilter.setEnabled( FilterCheckBox.this.isSelected() );
}
} );
}
}
public class FilterElementCheckBox extends JCheckBox
{
private static final long serialVersionUID = 1L;
private FilterElement<?> mFilter;
public FilterElementCheckBox( FilterElement<?> filter )
{
super( filter.getName() );
mFilter = filter;
setSelected( mFilter.isEnabled() );
addItemListener( new ItemListener()
{
@Override
public void itemStateChanged( ItemEvent arg0 )
{
mFilter.setEnabled( FilterElementCheckBox.this.isSelected() );
}
} );
}
}
public class FilterTreeNodeEditor implements TreeCellEditor
{
@Override
@SuppressWarnings( { "rawtypes", "unchecked" } )
public Component getTreeCellEditorComponent( JTree tree, Object node,
boolean isSelected, boolean expanded, boolean leaf, int row )
{
if( node instanceof DefaultMutableTreeNode )
{
Object userObject = ((DefaultMutableTreeNode)node).getUserObject();
if( userObject instanceof IFilter )
{
return new FilterCheckBox( (IFilter)userObject );
}
else if( userObject instanceof FilterElement )
{
return new FilterElementCheckBox( (FilterElement<?>)userObject );
}
}
return new JLabel( node.toString() );
}
@Override
public void addCellEditorListener( CellEditorListener l ) {}
@Override
public void cancelCellEditing()
{
}
@Override
public Object getCellEditorValue()
{
return null;
}
@Override
public boolean isCellEditable( EventObject anEvent )
{
return true;
}
@Override
public void removeCellEditorListener( CellEditorListener l ) {}
@Override
public boolean shouldSelectCell( EventObject anEvent )
{
return false;
}
@Override
public boolean stopCellEditing()
{
return false;
}
}
}

View File

@@ -0,0 +1,49 @@
package filter;
public class FilterElement<T> implements Comparable<FilterElement<T>>
{
private boolean mEnabled;
private T mElement;
public FilterElement( T element, boolean enabled )
{
mElement = element;
mEnabled = enabled;
}
public FilterElement( T t )
{
this( t, true );
}
public T getElement()
{
return mElement;
}
public String toString()
{
return getName();
}
public boolean isEnabled()
{
return mEnabled;
}
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
public String getName()
{
return mElement.toString();
}
@Override
public int compareTo( FilterElement<T> other )
{
return toString().compareTo( other.toString() );
}
}

97
src/filter/FilterSet.java Normal file
View File

@@ -0,0 +1,97 @@
package filter;
import java.util.ArrayList;
import java.util.List;
public class FilterSet<T> implements IFilter<T>
{
protected List<IFilter<T>> mFilters = new ArrayList<IFilter<T>>();
protected boolean mEnabled = true;
protected String mName;
public FilterSet( String name )
{
mName = name;
}
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public String toString()
{
return getName();
}
@Override
public boolean passes( T t )
{
if( mEnabled )
{
for( IFilter<T> filter: mFilters )
{
if( filter.canProcess( t ) )
{
return filter.passes( t );
}
}
}
return false;
}
@Override
public boolean canProcess( T t )
{
if( mEnabled )
{
for( IFilter<T> filter: mFilters )
{
if( filter.canProcess( t ) )
{
return true;
}
}
}
return false;
}
public List<IFilter<T>> getFilters()
{
return mFilters;
}
public void addFilters( List<IFilter<T>> filters )
{
mFilters.addAll( filters );
}
public void addFilter( IFilter<T> filter )
{
mFilters.add( filter );
}
public void removeFilter( IFilter<T> filter )
{
mFilters.remove( filter );
}
@Override
public boolean isEnabled()
{
return mEnabled;
}
@Override
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
}
}

36
src/filter/IFilter.java Normal file
View File

@@ -0,0 +1,36 @@
package filter;
public interface IFilter<T>
{
/**
* Generic filter method.
*
* @param message - message to filter
* @return - true if the message passes the filter
*/
public boolean passes( T t );
/**
* Indicates if the filter can process (filter) the object
*
* @param message - candidate message for filtering
*
* @return - true if the filter is capable of filtering the message
*/
public boolean canProcess( T t );
/**
* Indicates if this filter is enabled to evaluate messages
*/
public boolean isEnabled();
/**
* Sets the enabled state of the filter
*/
public void setEnabled( boolean enabled );
/**
* Display name for the filter
*/
public String getName();
}

View File

@@ -0,0 +1,111 @@
package message;
import java.util.ArrayList;
import java.util.List;
import controller.channel.Channel;
import decode.DecoderType;
import decode.config.AuxDecodeConfiguration;
import decode.fleetsync2.FleetsyncMessageFilter;
import decode.lj1200.LJ1200MessageFilter;
import decode.ltrnet.LTRNetMessageFilter;
import decode.ltrstandard.LTRStandardMessageFilter;
import decode.mdc1200.MDCMessageFilter;
import decode.mpt1327.MPT1327MessageFilter;
import decode.p25.message.filter.P25MessageFilterSet;
import decode.passport.PassportMessageFilter;
import filter.AllPassFilter;
import filter.FilterSet;
import filter.IFilter;
/**
* Constructs message filters for each of the decoder types
*/
public class MessageFilterFactory
{
/**
* Assembles a filter set containing filters for the primary channel
* decoder and each of the auxiliary decoders
*/
public static FilterSet<Message> getMessageFilter( Channel channel )
{
FilterSet<Message> filterSet = new FilterSet<Message>(
"Message Filter: " + channel.getChannelDisplayName() );
filterSet.addFilters( getMessageFilters(
channel.getDecodeConfiguration().getDecoderType() ) );
AuxDecodeConfiguration auxConfig = channel.getAuxDecodeConfiguration();
for( DecoderType decoderType: auxConfig.getAuxDecoders() )
{
filterSet.addFilters( getMessageFilters( decoderType ) );
}
/* If we don't have any filters, add an ALL-PASS filter */
if( filterSet.getFilters().isEmpty() )
{
filterSet.addFilter( new AllPassFilter<Message>() );
}
return filterSet;
}
/**
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
* can process all of the messages produced by the specified decoder type.
*/
public static List<IFilter<Message>> getMessageFilters( DecoderType decoder )
{
ArrayList<IFilter<Message>> filters = new ArrayList<IFilter<Message>>();
switch( decoder )
{
case ACARS:
break;
case AIS:
break;
case AM:
break;
case APRS:
break;
case DMR:
break;
case FLEETSYNC2:
filters.add( new FleetsyncMessageFilter() );
break;
case LJ_1200:
filters.add( new LJ1200MessageFilter() );
break;
case LTR_NET:
filters.add( new LTRNetMessageFilter() );
break;
case LTR_STANDARD:
filters.add( new LTRStandardMessageFilter() );
break;
case MDC1200:
filters.add( new MDCMessageFilter() );
break;
case MOTOROLA_TYPE_2:
break;
case MPT1327:
filters.add( new MPT1327MessageFilter() );
break;
case NBFM:
break;
case NXDN:
break;
case P25_PHASE1:
case P25_PHASE2:
filters.add( new P25MessageFilterSet() );
break;
case PASSPORT:
filters.add( new PassportMessageFilter() );
break;
default:
break;
}
return filters;
}
}

View File

@@ -71,4 +71,9 @@ public enum MessageType
{
return mDisplayText;
}
public String toString()
{
return getDisplayText();
}
}