mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#1368 Restructures Message and Event filter framework.
This commit is contained in:
@@ -25,26 +25,21 @@ import io.github.dsheirer.gui.power.ChannelPowerPanel;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventPanel;
|
||||
import io.github.dsheirer.module.decode.event.MessageActivityPanel;
|
||||
import io.github.dsheirer.module.decode.event.filter.lastheard.LastHeardPanel;
|
||||
import io.github.dsheirer.module.decode.event.filter.lastseen.LastSeenPanel;
|
||||
import io.github.dsheirer.playlist.PlaylistManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.settings.SettingsManager;
|
||||
import java.awt.Color;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class NowPlayingPanel extends JPanel
|
||||
{
|
||||
private ChannelMetadataPanel mChannelMetadataPanel;
|
||||
private ChannelDetailPanel mChannelDetailPanel;
|
||||
private DecodeEventPanel mDecodeEventPanel;
|
||||
private LastSeenPanel mLastSeenPanel;
|
||||
private LastHeardPanel mLastHeardPanel;
|
||||
private MessageActivityPanel mMessageActivityPanel;
|
||||
private ChannelPowerPanel mChannelPowerPanel;
|
||||
private final ChannelMetadataPanel mChannelMetadataPanel;
|
||||
private final ChannelDetailPanel mChannelDetailPanel;
|
||||
private final DecodeEventPanel mDecodeEventPanel;
|
||||
private final MessageActivityPanel mMessageActivityPanel;
|
||||
private final ChannelPowerPanel mChannelPowerPanel;
|
||||
|
||||
/**
|
||||
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
|
||||
@@ -55,8 +50,6 @@ public class NowPlayingPanel extends JPanel
|
||||
{
|
||||
mChannelDetailPanel = new ChannelDetailPanel(playlistManager.getChannelProcessingManager());
|
||||
mDecodeEventPanel = new DecodeEventPanel(iconModel, userPreferences, playlistManager.getAliasModel());
|
||||
mLastSeenPanel = new LastSeenPanel(iconModel, userPreferences, playlistManager.getAliasModel());
|
||||
mLastHeardPanel = new LastHeardPanel(iconModel, userPreferences, playlistManager.getAliasModel());
|
||||
mMessageActivityPanel = new MessageActivityPanel(userPreferences);
|
||||
mChannelMetadataPanel = new ChannelMetadataPanel(playlistManager, iconModel, userPreferences);
|
||||
mChannelPowerPanel = new ChannelPowerPanel(playlistManager, settingsManager);
|
||||
@@ -71,13 +64,15 @@ public class NowPlayingPanel extends JPanel
|
||||
JideTabbedPane tabbedPane = new JideTabbedPane();
|
||||
tabbedPane.addTab("Details", mChannelDetailPanel);
|
||||
tabbedPane.addTab("Events", mDecodeEventPanel);
|
||||
tabbedPane.addTab("Last Seen", mLastSeenPanel);
|
||||
tabbedPane.addTab("Last Heard", mLastHeardPanel);
|
||||
tabbedPane.addTab("Messages", mMessageActivityPanel);
|
||||
tabbedPane.addTab("Channel", mChannelPowerPanel);
|
||||
tabbedPane.setFont(this.getFont());
|
||||
tabbedPane.setForeground(Color.BLACK);
|
||||
|
||||
//Register state change listener to toggle visibility state for channel tab to turn-on/off FFT processing
|
||||
tabbedPane.addChangeListener(e -> mChannelPowerPanel.setPanelVisible(tabbedPane.getSelectedIndex() == tabbedPane
|
||||
.indexOfComponent(mChannelPowerPanel)));
|
||||
|
||||
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.setShowGripper(true);
|
||||
splitPane.add(mChannelMetadataPanel);
|
||||
@@ -86,8 +81,6 @@ public class NowPlayingPanel extends JPanel
|
||||
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel);
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel);
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mLastSeenPanel);
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mLastHeardPanel);
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel);
|
||||
mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelPowerPanel);
|
||||
}
|
||||
|
||||
@@ -1,35 +1,61 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Implements a message filter that passes (true) all messages
|
||||
* Implements an all-pass filter that automatically passes/allows any presented object.
|
||||
*
|
||||
* A key type of String is used, but that makes no difference in how this filter functions.
|
||||
*/
|
||||
@SuppressWarnings( "rawtypes" )
|
||||
public class AllPassFilter<T> extends Filter<T>
|
||||
public class AllPassFilter<T> extends Filter<T, String>
|
||||
{
|
||||
public AllPassFilter()
|
||||
private static final String KEY = "Other/Unlisted";
|
||||
private final AllPassKeyExtractor mKeyExtractor = new AllPassKeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param name to display for this filter
|
||||
*/
|
||||
public AllPassFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
add(new FilterElement<>(KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function getKeyExtractor()
|
||||
{
|
||||
super( "Allow All Messages" );
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
/**
|
||||
* Key extractor that always returns the same (String)key.
|
||||
*/
|
||||
public class AllPassKeyExtractor implements Function<T,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(T t)
|
||||
{
|
||||
return KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,206 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public abstract class Filter<T> implements IFilter<T>
|
||||
/**
|
||||
* Base filter class that uses an extractor function to extract a filter key (K) from an object of type T and then
|
||||
* lookup the filter element (rule) indicating if objects of this type are enabled.
|
||||
*
|
||||
* @param <T> object type that this filter can process.
|
||||
* @param <K> key type used to lookup filter elements
|
||||
*/
|
||||
public abstract class Filter<T,K> implements IFilter<T>
|
||||
{
|
||||
protected String mName;
|
||||
protected boolean mEnabled = true;
|
||||
|
||||
public Filter( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Filter.class);
|
||||
private String mName;
|
||||
private Map<K, FilterElement<K>> mFilterElementMap = new HashMap<>();
|
||||
private IFilterChangeListener mFilterChangeListener;
|
||||
|
||||
/**
|
||||
* Indicates if this filter is enabled or disabled
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param name of this filter
|
||||
*/
|
||||
public Filter(String name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
/**
|
||||
* Indicates if this filter contains a filter element matching the key value.
|
||||
* @param key to check
|
||||
* @return true if this filter has a matching filter element.
|
||||
*/
|
||||
protected boolean hasKey(K key)
|
||||
{
|
||||
return mFilterElementMap.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of this filter
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
/**
|
||||
* Key extractor function provided by the subclass.
|
||||
* @return key extractor function
|
||||
*/
|
||||
public abstract Function<T,K> getKeyExtractor();
|
||||
|
||||
@Override
|
||||
public boolean passes(T t)
|
||||
{
|
||||
K key = getKeyExtractor().apply(t);
|
||||
|
||||
if(mFilterElementMap.containsKey(key))
|
||||
{
|
||||
return mFilterElementMap.get(key).isEnabled();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this filter can process the item T, meaning that it has a FilterElement that matches the key
|
||||
* value for items of type T.
|
||||
* @param t to test
|
||||
* @return true if this filter can process item t
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(T t)
|
||||
{
|
||||
return hasKey(getKeyExtractor().apply(t));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled()
|
||||
{
|
||||
for(FilterElement filterElement: mFilterElementMap.values())
|
||||
{
|
||||
if(filterElement.isEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the child filter element to this filter
|
||||
* @param filterElement to add
|
||||
*/
|
||||
public void add(FilterElement<K> filterElement)
|
||||
{
|
||||
if(mFilterElementMap.containsKey(filterElement.getElement()))
|
||||
{
|
||||
LOG.warn("Filter element for key [" + filterElement.getElement() + "] named [" + filterElement.getName() +
|
||||
"] already exists - overwriting existing value");
|
||||
}
|
||||
|
||||
mFilterElementMap.put(filterElement.getElement(), filterElement);
|
||||
filterElement.register(mFilterChangeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* List of filter elements managed by this filter
|
||||
*/
|
||||
public final List<FilterElement<K>> getFilterElements()
|
||||
{
|
||||
return new ArrayList<>(mFilterElementMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of this filter
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this filter
|
||||
*
|
||||
* @param name to set
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty version or name of this filter.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of enabled child filter elements.
|
||||
*
|
||||
* @return count
|
||||
*/
|
||||
@Override
|
||||
public int getEnabledCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(FilterElement filterElement : mFilterElementMap.values())
|
||||
{
|
||||
if(filterElement.isEnabled())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of child filter elements
|
||||
*
|
||||
* @return count
|
||||
*/
|
||||
@Override
|
||||
public int getElementCount()
|
||||
{
|
||||
return mFilterElementMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a filter change listener on this filter and all filter elements.
|
||||
* @param listener to be notified of any filter changes.
|
||||
*/
|
||||
@Override
|
||||
public void register(IFilterChangeListener listener)
|
||||
{
|
||||
mFilterChangeListener = listener;
|
||||
|
||||
for(FilterElement child: mFilterElementMap.values())
|
||||
{
|
||||
child.register(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
71
src/main/java/io/github/dsheirer/filter/FilterEditor.java
Normal file
71
src/main/java/io/github/dsheirer/filter/FilterEditor.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import java.awt.Component;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
/**
|
||||
* Filter editor
|
||||
* @param <T> item type for editing
|
||||
*/
|
||||
public class FilterEditor<T> extends JFrame
|
||||
{
|
||||
private FilterEditorPanel<T> mEditorPanel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param title for the editor window frame
|
||||
* @param owner to register the popup location
|
||||
* @param filterSet to use initially
|
||||
*/
|
||||
public FilterEditor(String title, Component owner, FilterSet<T> filterSet)
|
||||
{
|
||||
if(filterSet == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Unable to construct FilterEditor - FilterSet cannot be null");
|
||||
}
|
||||
setTitle(title);
|
||||
setSize(600, 400);
|
||||
setLocationRelativeTo(owner);
|
||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][]"));
|
||||
mEditorPanel = new FilterEditorPanel<>(filterSet);
|
||||
JScrollPane scroller = new JScrollPane(mEditorPanel);
|
||||
scroller.setViewportView(mEditorPanel);
|
||||
add(scroller, "wrap");
|
||||
JButton close = new JButton("Close");
|
||||
close.addActionListener(e -> dispose());
|
||||
add(close);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this editor with the filterset.
|
||||
* @param filterSet to use in this editor.
|
||||
*/
|
||||
public void updateFilterSet(FilterSet<T> filterSet)
|
||||
{
|
||||
mEditorPanel.updateFilterSet(filterSet);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,128 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.EventObject;
|
||||
import java.util.List;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
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.TreeNode;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Editor panel for managing the state of a filter set.
|
||||
*
|
||||
* @param <T> element type for the filter set.
|
||||
*/
|
||||
public class FilterEditorPanel<T> extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JTree mTree;
|
||||
private DefaultTreeModel mModel;
|
||||
private FilterSet<T> mFilterSet;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param filterSet to manage
|
||||
*/
|
||||
public FilterEditorPanel(FilterSet<T> filterSet)
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
|
||||
mFilterSet = filterSet;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the panel and the tree model.
|
||||
*/
|
||||
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.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
||||
mTree.setCellRenderer(new EditorTreeCellRenderer());
|
||||
|
||||
mTree.setCellEditor(new FilterTreeNodeEditor());
|
||||
|
||||
mTree.setCellEditor(new FilterTreeCellEditor());
|
||||
mTree.setEditable(true);
|
||||
|
||||
add(mTree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the filter set for this editor panel
|
||||
* @param filterSet to use
|
||||
*/
|
||||
public void updateFilterSet(FilterSet<T> filterSet)
|
||||
{
|
||||
remove(mTree);
|
||||
mFilterSet = filterSet;
|
||||
init();
|
||||
revalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filter set as a child tree node to the parent.
|
||||
* @param filterSet to add to the tree
|
||||
* @param parent node for the filter set child tree node.
|
||||
*/
|
||||
private void addFilterSet(FilterSet<T> filterSet, DefaultMutableTreeNode parent)
|
||||
{
|
||||
List<IFilter<T>> filters = filterSet.getFilters();
|
||||
|
||||
/* sort the filters in alphabetical order by name */
|
||||
filters.sort(Comparator.comparing(IFilter::getName));
|
||||
|
||||
for(IFilter<T> filter : filters)
|
||||
{
|
||||
DefaultMutableTreeNode child = new DefaultMutableTreeNode(filter);
|
||||
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(filter);
|
||||
mModel.insertNodeInto(childNode, parent, parent.getChildCount());
|
||||
|
||||
mModel.insertNodeInto(child, parent, parent.getChildCount());
|
||||
|
||||
if(filter instanceof FilterSet)
|
||||
if(filter instanceof FilterSet childFilterSet)
|
||||
{
|
||||
addFilterSet((FilterSet<T>) filter, child);
|
||||
addFilterSet(childFilterSet, childNode);
|
||||
}
|
||||
else if(filter instanceof Filter)
|
||||
else if(filter instanceof Filter childFilter)
|
||||
{
|
||||
addFilter((Filter<T>) filter, child);
|
||||
addFilter(childFilter, childNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addFilter(Filter<T> filter, DefaultMutableTreeNode parent)
|
||||
/**
|
||||
* Adds the filter as a child tree node to the parent.
|
||||
*
|
||||
* @param filter to add
|
||||
* @param parent tree node
|
||||
*/
|
||||
private void addFilter(Filter filter, DefaultMutableTreeNode parent)
|
||||
{
|
||||
List<FilterElement<?>> elements = filter.getFilterElements();
|
||||
|
||||
@@ -103,123 +131,39 @@ public class FilterEditorPanel<T> extends JPanel
|
||||
for(FilterElement<?> element : elements)
|
||||
{
|
||||
DefaultMutableTreeNode child = new DefaultMutableTreeNode(element);
|
||||
|
||||
mModel.insertNodeInto(child, parent, parent.getChildCount());
|
||||
}
|
||||
}
|
||||
|
||||
public class MouseHandler implements MouseListener
|
||||
/**
|
||||
* Accesses the tree's root tree node.
|
||||
* @return root node.
|
||||
*/
|
||||
private DefaultMutableTreeNode getRoot()
|
||||
{
|
||||
public MouseHandler()
|
||||
if(mTree.getModel().getRoot() instanceof DefaultMutableTreeNode root)
|
||||
{
|
||||
return root;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively sets the enabled state on all filter node children below the
|
||||
* specified parent node.
|
||||
* 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)
|
||||
private void setFilterEnabled(DefaultTreeModel model, DefaultMutableTreeNode node, boolean enabled)
|
||||
{
|
||||
Object obj = node.getUserObject();
|
||||
|
||||
if(obj instanceof FilterSet<?>)
|
||||
if(obj instanceof FilterElement filterElement)
|
||||
{
|
||||
((FilterSet<?>) obj).setEnabled(enabled);
|
||||
filterElement.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();
|
||||
@@ -228,43 +172,123 @@ public class FilterEditorPanel<T> extends JPanel
|
||||
{
|
||||
Object child = children.nextElement();
|
||||
|
||||
if(child instanceof DefaultMutableTreeNode)
|
||||
if(child instanceof DefaultMutableTreeNode childNode)
|
||||
{
|
||||
setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled);
|
||||
setFilterEnabled(model, childNode, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
model.nodeChanged(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches the tree to find the tree node containing the specified filter element.
|
||||
*
|
||||
* @param node to start search
|
||||
* @param element to find
|
||||
*/
|
||||
private DefaultMutableTreeNode findFilterElementNode(DefaultMutableTreeNode node, FilterElement<?> element)
|
||||
{
|
||||
if(node != null)
|
||||
{
|
||||
if(node.getUserObject() instanceof FilterElement<?> selfFilterElement && selfFilterElement.equals(element))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
for(int x = 0; x < node.getChildCount(); x++)
|
||||
{
|
||||
if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
|
||||
{
|
||||
DefaultMutableTreeNode found = findFilterElementNode(child, element);
|
||||
|
||||
if(found != null)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the display value for the specified node and all parents in the nodes parentage.
|
||||
* @param node to update
|
||||
*/
|
||||
private void updateParentage(DefaultMutableTreeNode node)
|
||||
{
|
||||
mModel.nodeChanged(node);
|
||||
|
||||
TreeNode parent = node.getParent();
|
||||
|
||||
while(parent != null)
|
||||
{
|
||||
mModel.nodeChanged(parent);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches the tree to find the tree node containing the specified filter.
|
||||
*
|
||||
* @param node to start search
|
||||
* @param filter to find
|
||||
*/
|
||||
private DefaultMutableTreeNode findFilterNode(DefaultMutableTreeNode node, IFilter<?> filter)
|
||||
{
|
||||
if(node != null)
|
||||
{
|
||||
if(node.getUserObject() instanceof IFilter<?> selfFilter && selfFilter.equals(filter))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
for(int x = 0; x < node.getChildCount(); x++)
|
||||
{
|
||||
if(node.getChildAt(x) instanceof DefaultMutableTreeNode child)
|
||||
{
|
||||
DefaultMutableTreeNode found = findFilterNode(child, filter);
|
||||
|
||||
if(found != null)
|
||||
{
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Can't evaluate - node is null");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom cell renderer
|
||||
*/
|
||||
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)
|
||||
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)
|
||||
if(userObject instanceof IFilter filter)
|
||||
{
|
||||
IFilter<?> filter = (IFilter<?>) userObject;
|
||||
|
||||
checkBox = new JCheckBox(filter.getName());
|
||||
checkBox = new FilterCheckBox(filter);
|
||||
checkBox.setSelected(filter.isEnabled());
|
||||
}
|
||||
else if(userObject instanceof FilterElement)
|
||||
else if(userObject instanceof FilterElement element)
|
||||
{
|
||||
FilterElement<?> element = (FilterElement<?>) userObject;
|
||||
|
||||
checkBox = new JCheckBox(element.getName());
|
||||
checkBox = new FilterElementCheckBox(element);
|
||||
checkBox.setSelected(element.isEnabled());
|
||||
}
|
||||
|
||||
@@ -285,95 +309,110 @@ public class FilterEditorPanel<T> extends JPanel
|
||||
}
|
||||
}
|
||||
|
||||
return super.getTreeCellRendererComponent(tree, treeNode, selected,
|
||||
expanded, leaf, row, hasFocus);
|
||||
return super.getTreeCellRendererComponent(tree, treeNode, selected, expanded, leaf, row, hasFocus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check box used in filter tree nodes
|
||||
*/
|
||||
public class FilterCheckBox extends JCheckBox
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private IFilter<?> mFilter;
|
||||
|
||||
private IFilter<T> mFilter;
|
||||
|
||||
public FilterCheckBox(IFilter<T> filter)
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param filter
|
||||
*/
|
||||
public FilterCheckBox(IFilter<?> filter)
|
||||
{
|
||||
super(filter.getName());
|
||||
|
||||
mFilter = filter;
|
||||
|
||||
setSelected(mFilter.isEnabled());
|
||||
addItemListener(e -> {
|
||||
DefaultMutableTreeNode selfNode = findFilterNode(getRoot(), mFilter);
|
||||
|
||||
addItemListener(new ItemListener()
|
||||
{
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e)
|
||||
if(selfNode != null)
|
||||
{
|
||||
mFilter.setEnabled(FilterCheckBox.this.isSelected());
|
||||
setFilterEnabled(mModel, selfNode, FilterCheckBox.this.isSelected());
|
||||
updateLabel();
|
||||
updateParentage(selfNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelected(boolean b)
|
||||
{
|
||||
super.setSelected(b);
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
public void updateLabel()
|
||||
{
|
||||
setText(mFilter.getName() + " (" + mFilter.getEnabledCount() + "/" + mFilter.getElementCount() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check box used in filter element tree nodes
|
||||
*/
|
||||
public class FilterElementCheckBox extends JCheckBox
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private FilterElement<?> mFilter;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param filter element
|
||||
*/
|
||||
public FilterElementCheckBox(FilterElement<?> filter)
|
||||
{
|
||||
super(filter.getName());
|
||||
|
||||
mFilter = filter;
|
||||
|
||||
setSelected(mFilter.isEnabled());
|
||||
|
||||
addItemListener(new ItemListener()
|
||||
{
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent arg0)
|
||||
addItemListener(arg0 -> {
|
||||
mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
|
||||
DefaultMutableTreeNode self = findFilterElementNode(getRoot(), mFilter);
|
||||
if(self != null)
|
||||
{
|
||||
mFilter.setEnabled(FilterElementCheckBox.this.isSelected());
|
||||
updateParentage(self);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class FilterTreeNodeEditor implements TreeCellEditor
|
||||
/**
|
||||
* Editor for filter tree nodes
|
||||
*/
|
||||
public class FilterTreeCellEditor implements TreeCellEditor
|
||||
{
|
||||
@Override
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public Component getTreeCellEditorComponent(JTree tree, Object node,
|
||||
boolean isSelected, boolean expanded, boolean leaf, int row)
|
||||
public Component getTreeCellEditorComponent(JTree tree, Object node, boolean isSelected, boolean expanded, boolean leaf, int row)
|
||||
{
|
||||
if(node instanceof DefaultMutableTreeNode)
|
||||
if(node instanceof DefaultMutableTreeNode mutableTreeNode)
|
||||
{
|
||||
Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
|
||||
Object userObject = mutableTreeNode.getUserObject();
|
||||
|
||||
if(userObject instanceof IFilter)
|
||||
if(userObject instanceof IFilter filter)
|
||||
{
|
||||
return new FilterCheckBox((IFilter) userObject);
|
||||
return new FilterCheckBox(filter);
|
||||
}
|
||||
else if(userObject instanceof FilterElement)
|
||||
else if(userObject instanceof FilterElement filterElement)
|
||||
{
|
||||
return new FilterElementCheckBox((FilterElement<?>) userObject);
|
||||
return new FilterElementCheckBox(filterElement);
|
||||
}
|
||||
}
|
||||
|
||||
return new JLabel(node.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCellEditorListener(CellEditorListener l)
|
||||
{
|
||||
}
|
||||
public void addCellEditorListener(CellEditorListener l) {}
|
||||
|
||||
@Override
|
||||
public void cancelCellEditing()
|
||||
{
|
||||
|
||||
}
|
||||
public void cancelCellEditing() {}
|
||||
|
||||
@Override
|
||||
public Object getCellEditorValue()
|
||||
@@ -388,9 +427,7 @@ public class FilterEditorPanel<T> extends JPanel
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCellEditorListener(CellEditorListener l)
|
||||
{
|
||||
}
|
||||
public void removeCellEditorListener(CellEditorListener l) {}
|
||||
|
||||
@Override
|
||||
public boolean shouldSelectCell(EventObject anEvent)
|
||||
|
||||
@@ -1,55 +1,148 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
/**
|
||||
* Filter element provides filtering and enabled state tracking for an individual filterable element.
|
||||
*
|
||||
* @param <T> element to track and 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();
|
||||
}
|
||||
private boolean mEnabled;
|
||||
private final T mElement;
|
||||
private IFilterChangeListener mFilterChangeListener;
|
||||
|
||||
@Override
|
||||
public int compareTo( FilterElement<T> other )
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param element to track
|
||||
* @param enabled enabled state of this filter element
|
||||
*/
|
||||
public FilterElement(T element, boolean enabled)
|
||||
{
|
||||
return toString().compareTo( other.toString() );
|
||||
mElement = element;
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with a default state of enabled (true).
|
||||
*
|
||||
* @param t element to track
|
||||
*/
|
||||
public FilterElement(T t)
|
||||
{
|
||||
this(t, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener with each child filter element.
|
||||
*
|
||||
* @param listener to register
|
||||
*/
|
||||
public void register(IFilterChangeListener listener)
|
||||
{
|
||||
mFilterChangeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Element being filtered.
|
||||
*
|
||||
* @return element
|
||||
*/
|
||||
public T getElement()
|
||||
{
|
||||
return mElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name or display string for the element.
|
||||
*
|
||||
* @return string version of the element.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this filter is enabled indicating that elements of this type will pass this filter.
|
||||
*
|
||||
* @return enabled state.
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled state of this filter.
|
||||
*
|
||||
* @param enabled true to allow this filter's element type to pass the filter.
|
||||
*/
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
mEnabled = enabled;
|
||||
|
||||
if(mFilterChangeListener != null)
|
||||
{
|
||||
mFilterChangeListener.filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of the tracked element.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return mElement.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements compareto interface for ordering of filter elements.
|
||||
*
|
||||
* @param other the object to be compared.
|
||||
* @return comparison value
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof FilterElement)) return false;
|
||||
return compareTo((FilterElement) o) == 0;
|
||||
public int compareTo(FilterElement<T> other)
|
||||
{
|
||||
return toString().compareTo(other.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement equals interface for comparison of filter elements.
|
||||
*
|
||||
* @param o to compare
|
||||
* @return comparison
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if(o instanceof FilterElement fe)
|
||||
{
|
||||
return compareTo(fe) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,108 +1,239 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import io.github.dsheirer.log.LoggingSuppressor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Filter set is a parent container for filters.
|
||||
*
|
||||
* @param <T> type of element tracked/filtered by this filter set.
|
||||
*/
|
||||
public class FilterSet<T> implements IFilter<T>
|
||||
{
|
||||
protected List<IFilter<T>> mFilters = new ArrayList<IFilter<T>>();
|
||||
protected boolean mEnabled = true;
|
||||
protected String mName;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FilterSet.class);
|
||||
private static LoggingSuppressor sLoggingSuppressor = new LoggingSuppressor(LOGGER);
|
||||
private List<IFilter<T>> mFilters = new ArrayList<>();
|
||||
private String mName;
|
||||
private IFilterChangeListener mFilterChangeListener;
|
||||
|
||||
public FilterSet( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public FilterSet()
|
||||
{
|
||||
mName = "Message Filter";
|
||||
}
|
||||
|
||||
public FilterSet(IFilter<T> filter)
|
||||
/**
|
||||
* Constructs an instance of a filter set.
|
||||
*
|
||||
* @param name of this filter set.
|
||||
*/
|
||||
public FilterSet(String name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with a single filter using the default 'Filters' name.
|
||||
*
|
||||
* @param filter for this filter set.
|
||||
*/
|
||||
public FilterSet(IFilter<T> filter)
|
||||
{
|
||||
this("Filters");
|
||||
addFilter(filter);
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
// Make sure to loop through all filters.
|
||||
// Only return if true or all filters have been evaluated.
|
||||
if( filter.canProcess( t ) && filter.passes( t ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess( T t )
|
||||
{
|
||||
if( mEnabled )
|
||||
/**
|
||||
* Registers the listener with each child filter element.
|
||||
* @param listener to register
|
||||
*/
|
||||
public void register(IFilterChangeListener listener)
|
||||
{
|
||||
mFilterChangeListener = listener;
|
||||
|
||||
for(IFilter child: mFilters)
|
||||
{
|
||||
for( IFilter<T> filter: mFilters )
|
||||
child.register(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Name of this filter set.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this filter set.
|
||||
*
|
||||
* @param name to set
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name for this filter set.
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the element passes at least one of the child filters in this filter set.
|
||||
*
|
||||
* @param t to evaluate
|
||||
* @return true if the argument passes a child filter.
|
||||
*/
|
||||
@Override
|
||||
public boolean passes(T t)
|
||||
{
|
||||
for(IFilter<T> filter : mFilters)
|
||||
{
|
||||
//Stop at the first filter that says that it can process the message type.
|
||||
if(filter.canProcess(t))
|
||||
{
|
||||
if( filter.canProcess( t ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return filter.passes(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this filter set contains at least one filter that can evaluate the type of argument that is presented.
|
||||
*
|
||||
* @param t element to evaluate
|
||||
* @return true if this filter set can evaluate objects of the argument's type.
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(T t)
|
||||
{
|
||||
for(IFilter<T> filter : mFilters)
|
||||
{
|
||||
if(filter.canProcess(t))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
sLoggingSuppressor.info(t.getClass().getSimpleName(), 1, "FilterSet [" +
|
||||
this.getClass().getSimpleName() + "] has no filter element for items of type [" +
|
||||
t.getClass().getSimpleName() + "]");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Child filters
|
||||
*
|
||||
* @return filters
|
||||
*/
|
||||
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 );
|
||||
return new ArrayList<>(mFilters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mEnabled;
|
||||
}
|
||||
/**
|
||||
* Adds a list of child filters to this filter set.
|
||||
* @param filters to add
|
||||
*/
|
||||
public void addFilters(List<IFilter<T>> filters)
|
||||
{
|
||||
for(IFilter<T> filter: filters)
|
||||
{
|
||||
addFilter(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the child filter to this filter set.
|
||||
* @param filter to add
|
||||
*/
|
||||
public void addFilter(IFilter<T> filter)
|
||||
{
|
||||
if(filter instanceof Filter<?,?> filterInstance && filterInstance.getKeyExtractor() == null)
|
||||
{
|
||||
LOGGER.warn("Filter [" + filter.getClass() + "] has a null key extractor");
|
||||
}
|
||||
|
||||
mFilters.add(filter);
|
||||
filter.register(mFilterChangeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if any of the child filters contain at least one filter element that is enabled.
|
||||
* @return enabled state
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled( boolean enabled )
|
||||
{
|
||||
mEnabled = enabled;
|
||||
}
|
||||
public boolean isEnabled()
|
||||
{
|
||||
for(IFilter filter: mFilters)
|
||||
{
|
||||
if(filter.isEnabled())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive count of enabled child filter elements.
|
||||
* @return count
|
||||
*/
|
||||
@Override
|
||||
public int getEnabledCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(IFilter filter: mFilters)
|
||||
{
|
||||
count += filter.getEnabledCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive count of child filter elements.
|
||||
* @return count
|
||||
*/
|
||||
@Override
|
||||
public int getElementCount()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(IFilter filter: mFilters)
|
||||
{
|
||||
count += filter.getElementCount();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,75 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
/**
|
||||
* Filter interface
|
||||
*
|
||||
* @param <T> type of 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 );
|
||||
/**
|
||||
* Generic filter method.
|
||||
*
|
||||
* @param t - message to filter
|
||||
* @return - true if the message passes the filter
|
||||
*/
|
||||
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 the filter can process (filter) the object
|
||||
*
|
||||
* @param t - candidate message for filtering
|
||||
* @return - true if the filter is capable of filtering the message
|
||||
*/
|
||||
boolean canProcess(T t);
|
||||
|
||||
/**
|
||||
* Indicates if this filter is enabled to evaluate messages
|
||||
*/
|
||||
public boolean isEnabled();
|
||||
/**
|
||||
* Indicates if this filter is enabled to evaluate messages
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Sets the enabled state of the filter
|
||||
*/
|
||||
public void setEnabled( boolean enabled );
|
||||
|
||||
/**
|
||||
* Display name for the filter
|
||||
*/
|
||||
public String getName();
|
||||
/**
|
||||
* Display name for the filter
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* (Recursive) count of enabled child filter elements.
|
||||
*
|
||||
* @return total enable count.
|
||||
*/
|
||||
int getEnabledCount();
|
||||
|
||||
/**
|
||||
* (Recursive) count of total child filter elements.
|
||||
*
|
||||
* @return total count of child filter elements.
|
||||
*/
|
||||
int getElementCount();
|
||||
|
||||
/**
|
||||
* Registers the listener to be notified when the enabled state of any child filter elements is updated.
|
||||
*
|
||||
* @param listener to be notified
|
||||
*/
|
||||
void register(IFilterChangeListener listener);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,19 +17,9 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
|
||||
/**
|
||||
* Request for decode event history from a processing chain for the specified channel.
|
||||
*/
|
||||
public class LastSeenHistoryRequest extends ModuleEventBusMessage
|
||||
public interface IFilterChangeListener
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public LastSeenHistoryRequest()
|
||||
{
|
||||
}
|
||||
void filterChanged();
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,30 +14,36 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
package io.github.dsheirer.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.SyncLossMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SyncLossMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for sync-loss messages
|
||||
*/
|
||||
public class SyncLossMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String SYNC_LOSS_KEY = "Sync-Loss";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SyncLossMessageFilter()
|
||||
{
|
||||
super("Sync Loss");
|
||||
super("Sync Loss Messages");
|
||||
add(new FilterElement<>(SYNC_LOSS_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,9 +52,20 @@ public class SyncLossMessageFilter extends Filter<IMessage>
|
||||
return message instanceof SyncLossMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof SyncLossMessage)
|
||||
{
|
||||
return SYNC_LOSS_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,6 @@ package io.github.dsheirer.module;
|
||||
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -50,7 +49,7 @@ public abstract class HistoryModule<T> extends Module implements Listener<T>
|
||||
*/
|
||||
public List<T> getItems()
|
||||
{
|
||||
return new ArrayList<>(mItems);
|
||||
return new ArrayList<>(mItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -117,8 +117,8 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
private Broadcaster<IMessage> mMessageBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<SquelchStateEvent> mSquelchStateEventBroadcaster = new Broadcaster<>();
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(500);
|
||||
private MessageHistory mMessageHistory = new MessageHistory(500);
|
||||
private DecodeEventHistory mDecodeEventHistory = new DecodeEventHistory(200);
|
||||
private MessageHistory mMessageHistory = new MessageHistory(200);
|
||||
private AbstractChannelState mChannelState;
|
||||
private EventBus mEventBus;
|
||||
protected Source mSource;
|
||||
|
||||
@@ -47,6 +47,7 @@ import io.github.dsheirer.module.decode.dmr.DMRDecoderState;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager;
|
||||
import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
|
||||
import io.github.dsheirer.module.decode.dmr.audio.DMRAudioModule;
|
||||
import io.github.dsheirer.module.decode.dmr.message.filter.DmrMessageFilterSet;
|
||||
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2Decoder;
|
||||
import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2DecoderState;
|
||||
import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageFilter;
|
||||
@@ -80,16 +81,18 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderC4FM;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderLSM;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderState;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25MessageFilterSet;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25P1MessageFilterSet;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderHDQPSK;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.filter.P25P2MessageFilterSet;
|
||||
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
|
||||
import io.github.dsheirer.module.decode.passport.PassportDecoder;
|
||||
import io.github.dsheirer.module.decode.passport.PassportDecoderState;
|
||||
import io.github.dsheirer.module.decode.passport.PassportMessageFilter;
|
||||
import io.github.dsheirer.module.decode.tait.Tait1200Decoder;
|
||||
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
|
||||
import io.github.dsheirer.module.decode.tait.Tait1200MessageFilter;
|
||||
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
|
||||
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
@@ -494,7 +497,7 @@ public class DecoderFactory
|
||||
*/
|
||||
public static FilterSet<IMessage> getMessageFilters(List<Module> modules)
|
||||
{
|
||||
FilterSet<IMessage> filterSet = new FilterSet<>();
|
||||
FilterSet<IMessage> filterSet = new FilterSet<>("Message Filters");
|
||||
|
||||
for(Module module : modules)
|
||||
{
|
||||
@@ -504,28 +507,28 @@ public class DecoderFactory
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't have any filters, add an ALL-PASS filter */
|
||||
if(filterSet.getFilters().isEmpty())
|
||||
{
|
||||
filterSet.addFilter(new AllPassFilter<>());
|
||||
}
|
||||
//Add an all-others filter as a catch-all for anything that isn't handled by the decoder filters.
|
||||
filterSet.addFilter(new AllPassFilter<>("All Other Messages Filter"));
|
||||
|
||||
return filterSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of IMessageFilter objects (FilterSets or Filters) that
|
||||
* can process all of the messages produced by the specified decoder type.
|
||||
* can process all messages produced by the specified decoder type.
|
||||
*/
|
||||
public static List<IFilter<IMessage>> getMessageFilter(DecoderType decoder)
|
||||
{
|
||||
ArrayList<IFilter<IMessage>> filters = new ArrayList<>();
|
||||
List<IFilter<IMessage>> filters = new ArrayList<>();
|
||||
|
||||
switch(decoder)
|
||||
{
|
||||
case DCS:
|
||||
filters.add(new DCSMessageFilter());
|
||||
break;
|
||||
case DMR:
|
||||
filters.add(new DmrMessageFilterSet());
|
||||
break;
|
||||
case FLEETSYNC2:
|
||||
filters.add(new FleetsyncMessageFilter());
|
||||
break;
|
||||
@@ -545,13 +548,16 @@ public class DecoderFactory
|
||||
filters.add(new MPT1327MessageFilter());
|
||||
break;
|
||||
case P25_PHASE1:
|
||||
filters.add(new P25MessageFilterSet());
|
||||
filters.add(new P25P1MessageFilterSet());
|
||||
break;
|
||||
case P25_PHASE2:
|
||||
filters.add(new P25P2MessageFilterSet());
|
||||
break;
|
||||
case PASSPORT:
|
||||
filters.add(new PassportMessageFilter());
|
||||
break;
|
||||
case DMR:
|
||||
//filters.add(new DMR) //todo: not finished
|
||||
case TAIT_1200:
|
||||
filters.add(new Tait1200MessageFilter());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.p25.identifier.channel.StandardChannel;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
@@ -100,9 +101,8 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource
|
||||
|
||||
if(mDecodeEvent == null)
|
||||
{
|
||||
mDecodeEvent = DecodeEvent.builder(System.currentTimeMillis())
|
||||
mDecodeEvent = DecodeEvent.builder(DecodeEventType.CALL, System.currentTimeMillis())
|
||||
.channel(mChannelDescriptor)
|
||||
.eventDescription("CALL")
|
||||
.details(getDecoderType().name())
|
||||
.identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers()))
|
||||
.build();
|
||||
|
||||
@@ -22,45 +22,55 @@ package io.github.dsheirer.module.decode.dcs;
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Message filter for Digital Coded Squelch (DCS)
|
||||
*/
|
||||
public class DCSMessageFilter extends Filter<IMessage>
|
||||
public class DCSMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private Map<String,FilterElement<String>> mElements = new HashMap<>();
|
||||
private static final String DCS_MESSAGE_FILTER_TAG = "DCS";
|
||||
private static final String DCS_KEY = "DCS";
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DCSMessageFilter()
|
||||
{
|
||||
super("DCS Message Filter");
|
||||
mElements.put(DCS_MESSAGE_FILTER_TAG, new FilterElement<>("DCS Code", true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
{
|
||||
return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
|
||||
}
|
||||
|
||||
return false;
|
||||
super("DCS Messages");
|
||||
add(new FilterElement(DCS_KEY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this filter can process DCS messages.
|
||||
* @param message to test
|
||||
* @return true if the message can be processed
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof DCSMessage;
|
||||
return message instanceof DCSMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor that always returns the same key constant.
|
||||
* @return extractor function.
|
||||
*/
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
return DCS_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,9 +286,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
|
||||
|
||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp())
|
||||
.eventType(DecodeEventType.SMS)
|
||||
.eventDescription(DecodeEventType.SMS.toString())
|
||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
|
||||
.details("MESSAGE: " + sms.getSMS())
|
||||
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
|
||||
.timeslot(sms.getTimeslot())
|
||||
@@ -312,9 +310,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
mic.update(hyteraSmsPacket.getSource());
|
||||
mic.update(hyteraSmsPacket.getDestination());
|
||||
|
||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
|
||||
.eventDescription(DecodeEventType.SMS.name())
|
||||
.eventType(DecodeEventType.DATA_PACKET)
|
||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
|
||||
.identifiers(mic)
|
||||
.timeslot(packet.getTimeslot())
|
||||
.details("SMS:" + hyteraSmsPacket.getSMS())
|
||||
@@ -326,9 +322,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
|
||||
|
||||
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
|
||||
.eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
|
||||
.eventType(DecodeEventType.DATA_PACKET)
|
||||
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
|
||||
.identifiers(mic)
|
||||
.timeslot(packet.getTimeslot())
|
||||
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
|
||||
@@ -337,9 +331,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
|
||||
.eventDescription(DecodeEventType.DATA_PACKET.name())
|
||||
.eventType(DecodeEventType.DATA_PACKET)
|
||||
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
|
||||
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
|
||||
.timeslot(packet.getTimeslot())
|
||||
.details(packet.toString())
|
||||
@@ -350,9 +342,8 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket());
|
||||
|
||||
if (geoPosition != null) {
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp())
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, packet.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.GPS.toString())
|
||||
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
|
||||
.protocol(Protocol.LRRP)
|
||||
.location(geoPosition)
|
||||
@@ -784,9 +775,8 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
case MOTOROLA_CONPLUS_REGISTRATION_REQUEST:
|
||||
DecodeEvent event = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
|
||||
.details("Registration Request")
|
||||
.eventDescription(DecodeEventType.REGISTER.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.build();
|
||||
@@ -794,9 +784,8 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
case MOTOROLA_CONPLUS_REGISTRATION_RESPONSE:
|
||||
DecodeEvent regRespEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
|
||||
.details("Registration Response")
|
||||
.eventDescription(DecodeEventType.REGISTER.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.build();
|
||||
@@ -837,9 +826,8 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
case MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION:
|
||||
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
|
||||
.details("TALKGROUP AFFILIATION")
|
||||
.eventDescription(DecodeEventType.AFFILIATE.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.build();
|
||||
@@ -853,20 +841,16 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
}
|
||||
|
||||
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
|
||||
return DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.eventType(decodeEventType)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(decodeEventType.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
}
|
||||
|
||||
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
|
||||
return DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(details)
|
||||
@@ -1119,8 +1103,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
ic.update(gpsInformation.getGPSLocation());
|
||||
|
||||
DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp())
|
||||
.eventDescription(DecodeEventType.GPS.toString())
|
||||
DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
|
||||
.identifiers(ic)
|
||||
.timeslot(message.getTimeslot())
|
||||
.details("LOCATION:" + gpsInformation.getGPSLocation())
|
||||
@@ -1151,9 +1134,8 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
|
||||
if(mCurrentCallEvent == null)
|
||||
{
|
||||
mCurrentCallEvent = DMRDecodeEvent.builder(timestamp)
|
||||
mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp)
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(type.toString())
|
||||
.details(details)
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.build();
|
||||
@@ -1164,7 +1146,6 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
if(type != DecodeEventType.CALL)
|
||||
{
|
||||
mCurrentCallEvent.setEventDescription(type.toString());
|
||||
mCurrentCallEvent.setDetails(details);
|
||||
}
|
||||
|
||||
|
||||
@@ -329,10 +329,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(isStale(event, timestamp, identifierCollection)) //Create new event
|
||||
{
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
|
||||
.channel(channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -351,11 +349,9 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
event.end(timestamp);
|
||||
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
|
||||
.channel(channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString() + " - Continue")
|
||||
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
|
||||
.details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
@@ -392,15 +388,6 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
|
||||
{
|
||||
if(event.getEventDescription() == null)
|
||||
{
|
||||
event.setEventDescription(decodeEventType + IGNORED);
|
||||
}
|
||||
else if(!event.getEventDescription().endsWith(IGNORED))
|
||||
{
|
||||
event.setEventDescription(event.getEventDescription() + IGNORED);
|
||||
}
|
||||
|
||||
if(event.getDetails() == null)
|
||||
{
|
||||
event.setDetails(DATA_CALL_IGNORED);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.dmr.event;
|
||||
|
||||
@@ -34,11 +33,12 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param decodeEventType for the event
|
||||
* @param timestamp for the event
|
||||
*/
|
||||
public DMRChannelGrantEvent(long timestamp)
|
||||
public DMRChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
|
||||
{
|
||||
super(timestamp);
|
||||
super(decodeEventType, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,9 +63,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(long timeStart)
|
||||
public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
return new DMRChannelGrantDecodeEventBuilder(timeStart);
|
||||
return new DMRChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +76,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
protected long mTimeStart;
|
||||
protected long mDuration;
|
||||
protected DecodeEventType mDecodeEventType;
|
||||
protected String mEventDescription;
|
||||
protected IdentifierCollection mIdentifierCollection;
|
||||
protected DMRChannel mChannel;
|
||||
protected String mDetails;
|
||||
@@ -87,8 +86,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
*
|
||||
* @param timeStart
|
||||
*/
|
||||
public DMRChannelGrantDecodeEventBuilder(long timeStart)
|
||||
public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = timeStart;
|
||||
}
|
||||
|
||||
@@ -131,25 +131,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Decode Event type for this event.
|
||||
* @param eventType
|
||||
*/
|
||||
public DMRChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
|
||||
mDecodeEventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
* @param description of the event
|
||||
*/
|
||||
public DMRChannelGrantDecodeEventBuilder eventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier collection.
|
||||
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
|
||||
@@ -176,13 +157,11 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent
|
||||
*/
|
||||
public DMRChannelGrantEvent build()
|
||||
{
|
||||
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mTimeStart);
|
||||
DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mDecodeEventType, mTimeStart);
|
||||
decodeEvent.setChannelDescriptor(mChannel);
|
||||
decodeEvent.setTimeslot(mChannel.getTimeslot());
|
||||
decodeEvent.setDetails(mDetails);
|
||||
decodeEvent.setDuration(mDuration);
|
||||
decodeEvent.setEventType(mDecodeEventType);
|
||||
decodeEvent.setEventDescription(mEventDescription);
|
||||
decodeEvent.setIdentifierCollection(mIdentifierCollection);
|
||||
return decodeEvent;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,12 +14,13 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.event;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
@@ -32,9 +32,9 @@ public class DMRDecodeEvent extends DecodeEvent
|
||||
* Constucts a decode event
|
||||
* @param start
|
||||
*/
|
||||
public DMRDecodeEvent(long start)
|
||||
public DMRDecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.DMR);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ public class DMRDecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.DMR);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public enum Opcode
|
||||
STANDARD_CHANNEL_TIMING(Vendor.STANDARD, 7, "CHANNEL TIMING"),
|
||||
STANDARD_ALOHA(Vendor.STANDARD, 25, "ALOHA"),
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER(Vendor.STANDARD, 26, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT OUTBOUND HEADER"),
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER(Vendor.STANDARD, 27, "UNIFIED DATA TRANSPORT INBOUND HEADER"),
|
||||
STANDARD_AHOY(Vendor.STANDARD, 28, "AHOY"),
|
||||
STANDARD_ACTIVATION(Vendor.STANDARD, 30, "ACTIVATION"),
|
||||
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST(Vendor.STANDARD, 31, "RANDOM ACCESS SERVICE REQUEST"),
|
||||
@@ -102,13 +102,18 @@ public enum Opcode
|
||||
HYTERA_68_XPT_PREAMBLE(Vendor.HYTERA_68, 61, "HYTERA 68 XPT PREAMBLE"),
|
||||
HYTERA_68_CSBKO_62(Vendor.HYTERA_68, 62, "HYTERA 68 CSBKO 62"),
|
||||
|
||||
|
||||
UNKNOWN(Vendor.UNKNOWN, -1, "UNKNOWN");
|
||||
|
||||
private Vendor mVendor;
|
||||
private int mValue;
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param vendor for the opcode
|
||||
* @param value for the opcode
|
||||
* @param label to display for the opcode
|
||||
*/
|
||||
Opcode(Vendor vendor, int value, String label)
|
||||
{
|
||||
mVendor = vendor;
|
||||
@@ -151,19 +156,86 @@ public enum Opcode
|
||||
*/
|
||||
public boolean isDataChannelGrantOpcode()
|
||||
{
|
||||
return DATA_CHANNEL_GRANT_OPCODES.contains(this);
|
||||
return DATA_CHANNEL_GRANTS.contains(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Channel Grant opcodes
|
||||
* Data channel grant opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(
|
||||
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
|
||||
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM,
|
||||
STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
|
||||
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM,
|
||||
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
|
||||
public static final EnumSet<Opcode> DATA_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_DATA_CHANNEL_GRANT_MULTI_ITEM,
|
||||
STANDARD_PRIVATE_DATA_CHANNEL_GRANT_SINGLE_ITEM, STANDARD_DUPLEX_PRIVATE_DATA_CHANNEL_GRANT,
|
||||
STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_MULTI_ITEM, STANDARD_TALKGROUP_DATA_CHANNEL_GRANT_SINGLE_ITEM);
|
||||
|
||||
/**
|
||||
* Data opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> DATA_OPCODES = EnumSet.of(STANDARD_UNIFIED_DATA_TRANSPORT_OUTBOUND_HEADER,
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_INBOUND_HEADER, STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_OUTBOUND_HEADER,
|
||||
STANDARD_UNIFIED_DATA_TRANSPORT_FOR_DGNA_INBOUND_HEADER, STANDARD_PREAMBLE);
|
||||
|
||||
/**
|
||||
* Mobile request and response opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST,
|
||||
STANDARD_RANDOM_ACCESS_SERVICE_REQUEST);
|
||||
|
||||
/**
|
||||
* Network request and response and announcement opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> NETWORK_REQUEST_RESPONSE = EnumSet.of(STANDARD_FEATURE_NOT_SUPPORTED,
|
||||
STANDARD_UNIT_TO_UNIT_VOICE_SERVICE_RESPONSE, STANDARD_AHOY, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC,
|
||||
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_TSCC, STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD,
|
||||
STANDARD_ACKNOWLEDGE_RESPONSE_INBOUND_PAYLOAD, STANDARD_NEGATIVE_ACKNOWLEDGE_RESPONSE, STANDARD_CLEAR,
|
||||
STANDARD_MOVE_TSCC, STANDARD_CHANNEL_TIMING,
|
||||
STANDARD_ALOHA, STANDARD_ACTIVATION, STANDARD_ANNOUNCEMENT, STANDARD_MAINTENANCE, STANDARD_PROTECT);
|
||||
|
||||
/**
|
||||
* Voice channel grant opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> VOICE_CHANNEL_GRANTS = EnumSet.of(STANDARD_PRIVATE_VOICE_CHANNEL_GRANT,
|
||||
STANDARD_TALKGROUP_VOICE_CHANNEL_GRANT, STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT,
|
||||
STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT);
|
||||
|
||||
/**
|
||||
* Hytera opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> HYTERA = EnumSet.of(HYTERA_08_ACKNOWLEDGE, HYTERA_08_ANNOUNCEMENT,
|
||||
HYTERA_08_CSBKO_44, HYTERA_08_CSBKO_47, HYTERA_68_XPT_SITE_STATE, HYTERA_68_ALOHA,
|
||||
HYTERA_68_ACKNOWLEDGE, HYTERA_68_ANNOUNCEMENT, HYTERA_68_XPT_PREAMBLE, HYTERA_68_CSBKO_62);
|
||||
|
||||
/**
|
||||
* Motorola Capacity Max opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_MAX = EnumSet.of(MOTOROLA_CAPMAX_ALOHA);
|
||||
|
||||
/**
|
||||
* Motorola Capacity Plus opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> MOTOROLA_CAPACITY_PLUS = EnumSet.of(MOTOROLA_CAPPLUS_CALL_ALERT,
|
||||
MOTOROLA_CAPPLUS_CALL_ALERT_ACK, MOTOROLA_CAPPLUS_DATA_WINDOW_ANNOUNCEMENT,
|
||||
MOTOROLA_CAPPLUS_DATA_WINDOW_GRANT, MOTOROLA_CAPPLUS_NEIGHBOR_REPORT, MOTOROLA_CAPPLUS_CSBKO_60,
|
||||
MOTOROLA_CAPPLUS_PREAMBLE, MOTOROLA_CAPPLUS_SITE_STATUS);
|
||||
|
||||
/**
|
||||
* Motorola Connect Plus opcodes
|
||||
*/
|
||||
public static final EnumSet<Opcode> MOTOROLA_CONNECT_PLUS = EnumSet.of(MOTOROLA_CONPLUS_NEIGHBOR_REPORT,
|
||||
MOTOROLA_CONPLUS_VOICE_CHANNEL_USER, MOTOROLA_CONPLUS_DATA_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_10,
|
||||
MOTOROLA_CONPLUS_TERMINATE_CHANNEL_GRANT, MOTOROLA_CONPLUS_CSBKO_16, MOTOROLA_CONPLUS_REGISTRATION_REQUEST,
|
||||
MOTOROLA_CONPLUS_REGISTRATION_RESPONSE, MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION,
|
||||
MOTOROLA_CONPLUS_DATA_WINDOW_ANNOUNCEMENT, MOTOROLA_CONPLUS_DATA_WINDOW_GRANT);
|
||||
|
||||
/**
|
||||
* Indicates if the opcode is included in one of the enumset groupings above
|
||||
* @return
|
||||
*/
|
||||
public boolean isGrouped()
|
||||
{
|
||||
return DATA_CHANNEL_GRANTS.contains(this) || DATA_OPCODES.contains(this) ||
|
||||
MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) ||
|
||||
VOICE_CHANNEL_GRANTS.contains(this) || HYTERA.contains(this) || MOTOROLA_CAPACITY_MAX.contains(this) ||
|
||||
MOTOROLA_CAPACITY_PLUS.contains(this) || MOTOROLA_CONNECT_PLUS.contains(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup map of vendors and opcode value to opcodes
|
||||
|
||||
@@ -67,14 +67,14 @@ public enum LCOpcode
|
||||
//CAP+ SLCO 15 = Rest Channel https://forums.radioreference.com/threads/understanding-capacity-plus-trunking.209318/page-7
|
||||
SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION(Vendor.MOTOROLA_CAPACITY_PLUS, false,15, "REST CHANNEL NOTIFICATION"),
|
||||
|
||||
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "HYTERA XPT CHANNEL"),
|
||||
SHORT_STANDARD_XPT_CHANNEL(Vendor.STANDARD, false, 8, "STANDARD XPT CHANNEL"),
|
||||
SHORT_HYTERA_XPT_CHANNEL(Vendor.HYTERA_68, false, 8, "HYTERA XPT CHANNEL"),
|
||||
|
||||
SHORT_CONNECT_PLUS_TRAFFIC_CHANNEL(Vendor.STANDARD, false, 9, "TRAFFIC CHANNEL INFO"),
|
||||
SHORT_CONNECT_PLUS_CONTROL_CHANNEL(Vendor.STANDARD, false, 10, "CONTROL CHANNEL INFO"),
|
||||
|
||||
|
||||
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "SHORT UNKNOWN");
|
||||
SHORT_STANDARD_UNKNOWN(Vendor.STANDARD,false,-1, "UNKNOWN");
|
||||
|
||||
private static final EnumSet<LCOpcode> TALKER_ALIAS_OPCODES = EnumSet.of(FULL_STANDARD_TALKER_ALIAS_HEADER,
|
||||
FULL_STANDARD_TALKER_ALIAS_BLOCK_1, FULL_STANDARD_TALKER_ALIAS_BLOCK_2, FULL_STANDARD_TALKER_ALIAS_BLOCK_3);
|
||||
|
||||
@@ -63,7 +63,6 @@ public class PacketSequenceMessageFactory
|
||||
if(packetSequence.hasPacketSequenceHeader())
|
||||
{
|
||||
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
|
||||
boolean confirmed = primaryHeader.isConfirmedData();
|
||||
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
|
||||
|
||||
if(packet != null)
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for opcode based control (CSBK) messages.
|
||||
*/
|
||||
public class ControlMessageFilter extends Filter<IMessage, Opcode>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param name of this control message filter
|
||||
* @param opcodes to use with this filter
|
||||
*/
|
||||
public ControlMessageFilter(String name, Collection<Opcode> opcodes)
|
||||
{
|
||||
super(name);
|
||||
|
||||
for(Opcode opcode: opcodes)
|
||||
{
|
||||
add(new FilterElement<>(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, Opcode> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,Opcode>
|
||||
{
|
||||
@Override
|
||||
public Opcode apply(IMessage message)
|
||||
{
|
||||
if(message instanceof CSBKMessage csbk)
|
||||
{
|
||||
return csbk.getOpcode();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Filter set for DMR control (CSBK) messages using Opcodes for filter elements.
|
||||
*/
|
||||
public class ControlMessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ControlMessageFilterSet()
|
||||
{
|
||||
super("Control (CSBK) Messages");
|
||||
addFilter(new ControlMessageFilter("Data Channel Grants", Opcode.DATA_CHANNEL_GRANTS));
|
||||
addFilter(new ControlMessageFilter("Data-Related", Opcode.DATA_OPCODES));
|
||||
addFilter(new ControlMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE));
|
||||
addFilter(new ControlMessageFilter("Network Request/Response", Opcode.NETWORK_REQUEST_RESPONSE));
|
||||
addFilter(new ControlMessageFilter("Voice Channel Grants", Opcode.VOICE_CHANNEL_GRANTS));
|
||||
addFilter(new ControlMessageFilter("Hytera", Opcode.HYTERA));
|
||||
addFilter(new ControlMessageFilter("Motorola Capacity Max", Opcode.MOTOROLA_CAPACITY_MAX));
|
||||
addFilter(new ControlMessageFilter("Motorola Capacity Plus", Opcode.MOTOROLA_CAPACITY_PLUS));
|
||||
addFilter(new ControlMessageFilter("Motorola Connect Plus", Opcode.MOTOROLA_CONNECT_PLUS));
|
||||
|
||||
//Add all remaining opcodes that are not grouped into one of the above enumsets.
|
||||
List<Opcode> otherOpcodes = new ArrayList<>();
|
||||
for(Opcode opcode: Opcode.values())
|
||||
{
|
||||
if(!opcode.isGrouped())
|
||||
{
|
||||
otherOpcodes.add(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
addFilter(new ControlMessageFilter("Other/Unknown", otherOpcodes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof CSBKMessage && super.canProcess(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.DataMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.CSBKMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.DataType;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for data messages, excluding CSBK messages.
|
||||
*/
|
||||
public class DataMessageFilter extends Filter<IMessage, DataType>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param name of this filter
|
||||
*/
|
||||
public DataMessageFilter()
|
||||
{
|
||||
super("Data Messages");
|
||||
|
||||
for(DataType dataType: DataType.values())
|
||||
{
|
||||
add(new FilterElement<>(dataType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, DataType> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof DataMessage && !(message instanceof CSBKMessage) && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,DataType>
|
||||
{
|
||||
@Override
|
||||
public DataType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof DataMessage dataMessage)
|
||||
{
|
||||
return dataMessage.getSlotType().getDataType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,31 +17,29 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.filter.SyncLossMessageFilter;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
|
||||
/**
|
||||
* Response to decode event history request from a processing chain for the specified channel.
|
||||
* Filter set for DMR messages
|
||||
*/
|
||||
public class LastHeardHistoryResponse extends ModuleEventBusMessage
|
||||
public class DmrMessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
private LastHeardHistory mLastHeardHistory;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param history that has a decode event history
|
||||
* Constructor
|
||||
*/
|
||||
public LastHeardHistoryResponse(LastHeardHistory history)
|
||||
public DmrMessageFilterSet()
|
||||
{
|
||||
mLastHeardHistory = history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode event history module
|
||||
*/
|
||||
public LastHeardHistory getDecodeEventHistory()
|
||||
{
|
||||
return mLastHeardHistory;
|
||||
super("DMR Messages");
|
||||
addFilter(new ControlMessageFilterSet());
|
||||
addFilter(new VoiceMessageFilter());
|
||||
addFilter(new DataMessageFilter());
|
||||
addFilter(new DmrPacketSequenceFilter());
|
||||
addFilter(new LinkControlMessageFilterSet());
|
||||
addFilter(new SyncLossMessageFilter());
|
||||
addFilter(new DmrOtherMessageFilter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.DMRMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for unknown messages, meaning messages not handled by any other filter.
|
||||
*/
|
||||
public class DmrOtherMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String OTHER_KEY = "Other/Unknown Message";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DmrOtherMessageFilter()
|
||||
{
|
||||
super("Other DMR messages");
|
||||
add(new FilterElement<>(OTHER_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof DMRMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof DMRMessage)
|
||||
{
|
||||
return OTHER_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
|
||||
import io.github.dsheirer.module.decode.ip.hytera.sds.HyteraUnknownPacket;
|
||||
import io.github.dsheirer.module.decode.ip.hytera.sms.HyteraSmsPacket;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for DMR packet sequence messages
|
||||
*/
|
||||
public class DmrPacketSequenceFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String KEY_ARS = "Automatic Registration Service (ARS)";
|
||||
private static final String KEY_HYTERA_SMS = "Hytera Short Message Service (SMS)";
|
||||
private static final String KEY_HYTERA_UNKNOWN = "Hytera Unknown";
|
||||
private static final String KEY_LRRP = "Location Request/Response Protocol (LRRP)";
|
||||
private static final String KEY_UDT_SMS = "Unified Data Transport - Short Message Service";
|
||||
private static final String KEY_XCMP = "Extensible Command Message Protocol (XCMP)";
|
||||
private static final String KEY_UNKNOWN = "Other/Unknown";
|
||||
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DmrPacketSequenceFilter()
|
||||
{
|
||||
super("Packet Sequence Messages");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof DMRPacketMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof UDTShortMessageService)
|
||||
{
|
||||
return KEY_UDT_SMS;
|
||||
}
|
||||
else if(message instanceof DMRPacketMessage packetMessage)
|
||||
{
|
||||
if(packetMessage.getPacket() instanceof ARSPacket)
|
||||
{
|
||||
return KEY_ARS;
|
||||
}
|
||||
else if(packetMessage.getPacket() instanceof LRRPPacket)
|
||||
{
|
||||
return KEY_LRRP;
|
||||
}
|
||||
else if(packetMessage.getPacket() instanceof XCMPPacket)
|
||||
{
|
||||
return KEY_XCMP;
|
||||
}
|
||||
else if(packetMessage.getPacket() instanceof HyteraSmsPacket)
|
||||
{
|
||||
return KEY_HYTERA_SMS;
|
||||
}
|
||||
else if(packetMessage.getPacket() instanceof HyteraUnknownPacket)
|
||||
{
|
||||
return KEY_HYTERA_UNKNOWN;
|
||||
}
|
||||
|
||||
//TODO: finish this
|
||||
}
|
||||
|
||||
return KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Message filter for link control opcodes.
|
||||
*
|
||||
* Note: this does not include link control messages carried by data messages with embedded link control.
|
||||
*/
|
||||
public class LinkControlMessageFilter extends Filter<IMessage, LCOpcode>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param name of this filter
|
||||
*/
|
||||
public LinkControlMessageFilter(String name, Collection<LCOpcode> opcodes)
|
||||
{
|
||||
super(name);
|
||||
|
||||
for(LCOpcode opcode: opcodes)
|
||||
{
|
||||
add(new FilterElement<>(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, LCOpcode> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,LCOpcode>
|
||||
{
|
||||
@Override
|
||||
public LCOpcode apply(IMessage message)
|
||||
{
|
||||
if(message instanceof LCMessage lcMessage)
|
||||
{
|
||||
return lcMessage.getOpcode();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.LCOpcode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Filter set for short & voice-fragment link control messages
|
||||
*
|
||||
* Note: this filter set does NOT work for link control messages in Data Message with Link Control.
|
||||
*/
|
||||
public class LinkControlMessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public LinkControlMessageFilterSet()
|
||||
{
|
||||
super("Short/Voice-Fragment Link Control Messages");
|
||||
|
||||
List<LCOpcode> fullLinkControl = new ArrayList<>();
|
||||
List<LCOpcode> shortLinkControl = new ArrayList<>();
|
||||
for(LCOpcode opcode: LCOpcode.values())
|
||||
{
|
||||
if(opcode.isFull())
|
||||
{
|
||||
fullLinkControl.add(opcode);
|
||||
}
|
||||
else
|
||||
{
|
||||
shortLinkControl.add(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
addFilter(new LinkControlMessageFilter("Full Link Control", fullLinkControl));
|
||||
addFilter(new LinkControlMessageFilter("Short Link Control", shortLinkControl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof LCMessage && super.canProcess(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.dmr.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for voice messages
|
||||
*/
|
||||
public class VoiceMessageFilter extends Filter<IMessage, DMRSyncPattern>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public VoiceMessageFilter()
|
||||
{
|
||||
super("Voice Messages");
|
||||
add(new FilterElement<>(DMRSyncPattern.BASE_STATION_VOICE));
|
||||
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_B));
|
||||
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_C));
|
||||
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_D));
|
||||
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_E));
|
||||
add(new FilterElement<>(DMRSyncPattern.BS_VOICE_FRAME_F));
|
||||
add(new FilterElement<>(DMRSyncPattern.MOBILE_STATION_VOICE));
|
||||
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_B));
|
||||
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_C));
|
||||
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_D));
|
||||
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_E));
|
||||
add(new FilterElement<>(DMRSyncPattern.MS_VOICE_FRAME_F));
|
||||
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_1));
|
||||
add(new FilterElement<>(DMRSyncPattern.DIRECT_MODE_VOICE_TIMESLOT_2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof VoiceMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage,DMRSyncPattern> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,DMRSyncPattern>
|
||||
{
|
||||
@Override
|
||||
public DMRSyncPattern apply(IMessage message)
|
||||
{
|
||||
if(message instanceof VoiceMessage voice)
|
||||
{
|
||||
return voice.getSyncPattern();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -27,18 +27,18 @@ public enum DataType
|
||||
VOICE_HEADER(1, 288, "VOICE HEADER"),
|
||||
TLC(2, 288, "TERMINATOR"),
|
||||
CSBK(3, 288, "CSBK"),
|
||||
MBC_HEADER(4, 288, "MBC HEADER"),
|
||||
MBC_BLOCK(5, 276, "MBC"),
|
||||
MBC_HEADER(4, 288, "MULTI-BLOCK CONTROL HEADER"),
|
||||
MBC_BLOCK(5, 276, "MULTI-BLOCK CONTROL BLOCK"),
|
||||
DATA_HEADER(6, 288, "DATA HEADER"),
|
||||
RATE_1_OF_2_DATA(7, 276, "RATE 1/2 PACKET"),
|
||||
RATE_3_OF_4_DATA(8, 276, "RATE 3/4 PACKET"),
|
||||
SLOT_IDLE(9, 276, "IDLE"),
|
||||
RATE_1_DATA(10, 276, "RATE 1/1 PACKET"),
|
||||
USB_DATA(11, 276, "UNIFIED SINGLE BLOCK DATA"),
|
||||
MBC_ENC_HEADER(12, 276, "MBC ENCRYPTED HEADER"),
|
||||
MBC_ENC_HEADER(12, 276, "MULTI-BLOCK CONTROL ENCRYPTED HEADER"),
|
||||
DATA_ENC_HEADER(13, 276, "DATA ENCRYPTED HEADER"),
|
||||
CHANNEL_CONTROL_ENC_HEADER(14, 276, "CONTROL CHANNEL ENCRYPTED HEADER"),
|
||||
RESERVED_15(15, -1, "RESERVED"),
|
||||
RESERVED_15(15, -1, "RESERVED 15"),
|
||||
UNKNOWN(-1, -1, "UNKNOWN");
|
||||
|
||||
private int mValue;
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
/**
|
||||
* AbstractTableModel implementation supporting clearable method options.
|
||||
*/
|
||||
public abstract class ClearableHistoryModel<T> extends AbstractTableModel
|
||||
{
|
||||
public static final int DEFAULT_HISTORY_SIZE = 200;
|
||||
private LinkedList<T> mItems = new LinkedList<>();
|
||||
private int mHistorySize = DEFAULT_HISTORY_SIZE;
|
||||
|
||||
/**
|
||||
* Access an item/row by the model index value.
|
||||
* @param index to retrieve
|
||||
* @return item or null
|
||||
*/
|
||||
public T getItem(int index)
|
||||
{
|
||||
if(index < mItems.size())
|
||||
{
|
||||
return mItems.get(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the item to the top of the item list and removes any tail items while the item list size exceeds the
|
||||
* maximum history size for this model.
|
||||
* @param item to add
|
||||
*/
|
||||
public void add(T item)
|
||||
{
|
||||
mItems.addFirst(item);
|
||||
ClearableHistoryModel.this.fireTableRowsInserted(0, 0);
|
||||
while(mItems.size() > mHistorySize)
|
||||
{
|
||||
mItems.removeLast();
|
||||
super.fireTableRowsDeleted(mItems.size() - 1, mItems.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all messages from history
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
EventQueue.invokeLater(() -> {
|
||||
mItems.clear();
|
||||
fireTableDataChanged();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current messages and loads the messages argument
|
||||
*/
|
||||
public void clearAndSet(List<T> items)
|
||||
{
|
||||
EventQueue.invokeLater(() -> {
|
||||
mItems.clear();
|
||||
fireTableDataChanged();
|
||||
for(T item: items)
|
||||
{
|
||||
add(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Current history size
|
||||
* @return history size
|
||||
*/
|
||||
public int getHistorySize()
|
||||
{
|
||||
return mHistorySize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the history size to the default value.
|
||||
*/
|
||||
public void resetHistorySize()
|
||||
{
|
||||
setHistorySize(DEFAULT_HISTORY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the history size
|
||||
* @param historySize
|
||||
*/
|
||||
public void setHistorySize(int historySize)
|
||||
{
|
||||
mHistorySize = historySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mItems.size();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -32,27 +32,33 @@ public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
private long mTimeStart;
|
||||
private long mTimeEnd;
|
||||
private String mEventDescription;
|
||||
private DecodeEventType mDecodeEventType;
|
||||
private IdentifierCollection mIdentifierCollection;
|
||||
private IChannelDescriptor mChannelDescriptor;
|
||||
private String mDetails;
|
||||
private Protocol mProtocol;
|
||||
private Integer mTimeslot;
|
||||
private int mTimeslot = -1;
|
||||
|
||||
public DecodeEvent(long start)
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param decodeEventType is mandatory to support event filtering by event type.
|
||||
* @param start time of the event.
|
||||
*/
|
||||
public DecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decode event builder with the specified start timestamp.
|
||||
* @param decodeEventType for the event
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
return new DecodeEventBuilder(timeStart);
|
||||
return new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,23 +121,6 @@ public class DecodeEvent implements IDecodeEvent
|
||||
mTimeEnd = mTimeStart + duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event description
|
||||
*/
|
||||
@Override
|
||||
public String getEventDescription()
|
||||
{
|
||||
return mEventDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
*/
|
||||
public void setEventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link DecodeEventType}
|
||||
*/
|
||||
@@ -140,13 +129,6 @@ public class DecodeEvent implements IDecodeEvent
|
||||
return mDecodeEventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link DecodeEventType}
|
||||
*/
|
||||
public void setEventType(DecodeEventType eventType) {
|
||||
this.mDecodeEventType = eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier collection for this event.
|
||||
*/
|
||||
@@ -222,24 +204,24 @@ public class DecodeEvent implements IDecodeEvent
|
||||
* @return timeslot or default value of 0
|
||||
*/
|
||||
@Override
|
||||
public Integer getTimeslot()
|
||||
public int getTimeslot()
|
||||
{
|
||||
return mTimeslot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this event specifies a timeslot
|
||||
* Indicates if this event specifies a timeslot that is not negative
|
||||
*/
|
||||
public boolean hasTimeslot()
|
||||
{
|
||||
return mTimeslot != null;
|
||||
return mTimeslot >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timeslot for this event
|
||||
* @param timeslot of the event
|
||||
*/
|
||||
public void setTimeslot(Integer timeslot)
|
||||
public void setTimeslot(int timeslot)
|
||||
{
|
||||
mTimeslot = timeslot;
|
||||
}
|
||||
@@ -249,7 +231,7 @@ public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(mProtocol);
|
||||
sb.append(" DECODE EVENT: ").append(getEventDescription());
|
||||
sb.append(" DECODE EVENT: ").append(getEventType());
|
||||
sb.append(" DETAILS:").append(getDetails());
|
||||
if(mIdentifierCollection != null)
|
||||
{
|
||||
@@ -267,19 +249,19 @@ public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
protected long mTimeStart;
|
||||
protected long mDuration;
|
||||
protected String mEventDescription;
|
||||
protected DecodeEventType mDecodeEventType;
|
||||
protected IdentifierCollection mIdentifierCollection;
|
||||
protected IChannelDescriptor mChannelDescriptor;
|
||||
protected String mDetails;
|
||||
protected Protocol mProtocol = Protocol.UNKNOWN;
|
||||
protected Integer mTimeslot;
|
||||
protected int mTimeslot = -1;
|
||||
|
||||
/**
|
||||
* Constructs a builder instance with the specified start time in milliseconds
|
||||
*/
|
||||
public DecodeEventBuilder(long timeStart)
|
||||
public DecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = timeStart;
|
||||
}
|
||||
|
||||
@@ -313,25 +295,6 @@ public class DecodeEvent implements IDecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Decode Event type for this event.
|
||||
* @param eventType
|
||||
*/
|
||||
public DecodeEventBuilder eventType(DecodeEventType eventType) {
|
||||
mDecodeEventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
* @param description of the event
|
||||
*/
|
||||
public DecodeEventBuilder eventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier collection.
|
||||
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
|
||||
@@ -363,7 +326,12 @@ public class DecodeEvent implements IDecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
public DecodeEventBuilder timeslot(Integer timeslot)
|
||||
/**
|
||||
* Sets the timeslot for this event
|
||||
* @param timeslot
|
||||
* @return
|
||||
*/
|
||||
public DecodeEventBuilder timeslot(int timeslot)
|
||||
{
|
||||
mTimeslot = timeslot;
|
||||
return this;
|
||||
@@ -374,12 +342,10 @@ public class DecodeEvent implements IDecodeEvent
|
||||
*/
|
||||
public DecodeEvent build()
|
||||
{
|
||||
DecodeEvent decodeEvent = new DecodeEvent(mTimeStart);
|
||||
DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart);
|
||||
decodeEvent.setChannelDescriptor(mChannelDescriptor);
|
||||
decodeEvent.setDetails(mDetails);
|
||||
decodeEvent.setDuration(mDuration);
|
||||
decodeEvent.setEventType(mDecodeEventType);
|
||||
decodeEvent.setEventDescription(mEventDescription);
|
||||
decodeEvent.setIdentifierCollection(mIdentifierCollection);
|
||||
decodeEvent.setProtocol(mProtocol);
|
||||
decodeEvent.setTimeslot(mTimeslot);
|
||||
|
||||
@@ -21,25 +21,20 @@ package io.github.dsheirer.module.decode.event;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.awt.EventQueue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
public class DecodeEventModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
|
||||
/**
|
||||
* Decode event table model
|
||||
*/
|
||||
public class DecodeEventModel extends ClearableHistoryModel<IDecodeEvent> implements Listener<IDecodeEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventModel.class);
|
||||
|
||||
public static final int COLUMN_TIME = 0;
|
||||
public static final int COLUMN_DURATION = 1;
|
||||
public static final int COLUMN_EVENT = 2;
|
||||
@@ -50,12 +45,6 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
|
||||
public static final int COLUMN_CHANNEL = 7;
|
||||
public static final int COLUMN_FREQUENCY = 8;
|
||||
public static final int COLUMN_DETAILS = 9;
|
||||
|
||||
protected int mMaxMessages = 200;
|
||||
|
||||
protected List<IDecodeEvent> mEvents = new ArrayList<>();
|
||||
protected FilterSet<IDecodeEvent> mEventFilterSet = new DecodeEventFilterSet();
|
||||
|
||||
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
|
||||
|
||||
public DecodeEventModel()
|
||||
@@ -70,72 +59,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT)
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_TIME);
|
||||
}
|
||||
fireTableDataChanged();
|
||||
}
|
||||
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_FROM_ID);
|
||||
fireTableCellUpdated(row, COLUMN_TO_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the complete list of events managed by this model.
|
||||
*/
|
||||
public List<IDecodeEvent> getEvents()
|
||||
{
|
||||
return new ArrayList<>(mEvents);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().unregister(this);
|
||||
Iterator<IDecodeEvent> it = mEvents.iterator();
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
mEvents.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all events from this model and loads the events argument
|
||||
*/
|
||||
public void clearAndSet(List<IDecodeEvent> events)
|
||||
{
|
||||
mEvents.clear();
|
||||
mEvents.addAll(events);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
dispose();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount(int count)
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,49 +73,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
|
||||
*/
|
||||
public void receive(final IDecodeEvent event)
|
||||
{
|
||||
//Disabled until #1368 decode event is fully implemented
|
||||
// if (!mEventFilterSet.passes(event))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if(!mEvents.contains(event))
|
||||
{
|
||||
mEvents.add(0, event);
|
||||
fireTableRowsInserted(0, 0);
|
||||
prune();
|
||||
}
|
||||
else
|
||||
{
|
||||
int row = mEvents.indexOf(event);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
while(mEvents.size() > mMaxMessages)
|
||||
{
|
||||
int index = mEvents.size() - 1;
|
||||
mEvents.remove(index);
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterSet<IDecodeEvent> getFilterSet() {
|
||||
return mEventFilterSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
|
||||
this.mEventFilterSet = filterSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mEvents.size();
|
||||
EventQueue.invokeLater(() -> add(event));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -205,58 +90,55 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
synchronized(mEvents)
|
||||
IDecodeEvent event = getItem(rowIndex);
|
||||
|
||||
if(event != null)
|
||||
{
|
||||
IDecodeEvent event = mEvents.get(rowIndex);
|
||||
|
||||
if(event != null)
|
||||
switch(columnIndex)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_TIME:
|
||||
return event.getTimeStart();
|
||||
case COLUMN_DURATION:
|
||||
return event.getDuration();
|
||||
case COLUMN_EVENT:
|
||||
return event.getEventDescription();
|
||||
case COLUMN_FROM_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_FROM_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_CHANNEL:
|
||||
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
|
||||
case COLUMN_TIME:
|
||||
return event.getTimeStart();
|
||||
case COLUMN_DURATION:
|
||||
return event.getDuration();
|
||||
case COLUMN_EVENT:
|
||||
return event.getEventType().getLabel();
|
||||
case COLUMN_FROM_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_FROM_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_CHANNEL:
|
||||
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
|
||||
|
||||
if(channelDescriptor != null)
|
||||
if(channelDescriptor != null)
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return channelDescriptor.toString();
|
||||
}
|
||||
return channelDescriptor + " TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return "TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return channelDescriptor.toString();
|
||||
}
|
||||
case COLUMN_FREQUENCY:
|
||||
return event.getChannelDescriptor();
|
||||
case COLUMN_DETAILS:
|
||||
return event.getDetails();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return "TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case COLUMN_FREQUENCY:
|
||||
return event.getChannelDescriptor();
|
||||
case COLUMN_DETAILS:
|
||||
return event.getDetails();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -25,7 +25,6 @@ import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.AllPassFilter;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
@@ -33,10 +32,7 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
@@ -58,7 +54,10 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
|
||||
public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain>
|
||||
{
|
||||
@@ -75,7 +74,10 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
|
||||
private AliasModel mAliasModel;
|
||||
private UserPreferences mUserPreferences;
|
||||
private TimestampCellRenderer mTimestampCellRenderer;
|
||||
private EventManagementPanel mEventManagementPanel;
|
||||
private FilterSet<IDecodeEvent> mFilterSet = new DecodeEventFilterSet();
|
||||
private TableRowSorter<TableModel> mTableRowSorter;
|
||||
private HistoryManagementPanel<IDecodeEvent> mHistoryManagementPanel;
|
||||
|
||||
|
||||
/**
|
||||
* View for call event table
|
||||
@@ -91,17 +93,20 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
|
||||
mUserPreferences = userPreferences;
|
||||
mTimestampCellRenderer = new TimestampCellRenderer();
|
||||
mTable = new JTable(mEventModel);
|
||||
mTable.setAutoCreateRowSorter(true);
|
||||
mTableRowSorter = new TableRowSorter<>(mEventModel);
|
||||
mTableRowSorter.setRowFilter(new EventRowFilter());
|
||||
mTable.setRowSorter(mTableRowSorter);
|
||||
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
||||
mEventManagementPanel = new EventManagementPanel();
|
||||
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
|
||||
updateCellRenderers();
|
||||
|
||||
//Disabled until #1368 decode event is fully implemented
|
||||
// add(mEventManagementPanel, "span,growx");
|
||||
|
||||
mHistoryManagementPanel = new HistoryManagementPanel<>(mEventModel, "Event Filter Editor");
|
||||
mHistoryManagementPanel.updateFilterSet(mFilterSet);
|
||||
add(mHistoryManagementPanel, "span,growx");
|
||||
mEmptyScroller = new JScrollPane(mTable);
|
||||
add(mEmptyScroller);
|
||||
|
||||
//Register filter change listener to refresh the table any time the event filters are changed.
|
||||
mFilterSet.register(() -> mEventModel.fireTableDataChanged());
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
@@ -124,22 +129,14 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
|
||||
|
||||
private void updateCellRenderers()
|
||||
{
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
|
||||
.setCellRenderer(mTimestampCellRenderer);
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
|
||||
.setCellRenderer(new DurationCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
|
||||
.setCellRenderer(new ChannelDescriptorCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
|
||||
.setCellRenderer(new FrequencyCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME).setCellRenderer(mTimestampCellRenderer);
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION).setCellRenderer(new DurationCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID).setCellRenderer(new IdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID).setCellRenderer(new IdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS).setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL).setCellRenderer(new ChannelDescriptorCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY).setCellRenderer(new FrequencyCellRenderer());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,76 +153,17 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
|
||||
mCurrentEventHistory = processingChain.getDecodeEventHistory();
|
||||
mEventModel.clearAndSet(mCurrentEventHistory.getItems());
|
||||
processingChain.getDecodeEventHistory().addListener(mEventModel);
|
||||
mEventManagementPanel.enableButtons();
|
||||
mHistoryManagementPanel.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentEventHistory = null;
|
||||
mEventModel.clearAndSet(Collections.emptyList());
|
||||
mEventManagementPanel.disableButtons();
|
||||
mHistoryManagementPanel.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class EventManagementPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EventFilterButton<IDecodeEvent> mFilterButton;
|
||||
private EventClearButton mEventClearButton;
|
||||
|
||||
public EventManagementPanel()
|
||||
{
|
||||
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
|
||||
|
||||
initializeFilterButton();
|
||||
initializeClearButton();
|
||||
disableButtons();
|
||||
|
||||
add(mFilterButton);
|
||||
add(mEventClearButton);
|
||||
}
|
||||
|
||||
public void enableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(true);
|
||||
mEventClearButton.setEnabled(true);
|
||||
}
|
||||
|
||||
public void disableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(false);
|
||||
mEventClearButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private void initializeFilterButton()
|
||||
{
|
||||
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
|
||||
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
|
||||
if (filterProvider != null) {
|
||||
filterSet = filterProvider.getFilterSet();
|
||||
}
|
||||
mFilterButton = new EventFilterButton<>("Message Filter Editor", filterSet);
|
||||
}
|
||||
|
||||
private void initializeClearButton() {
|
||||
mEventClearButton = new EventClearButton(
|
||||
((DecodeEventModel) mTable.getModel()).getMaxMessageCount()
|
||||
);
|
||||
mEventClearButton.setEventClearHandler(new EventClearHandler() {
|
||||
@Override
|
||||
public void onHistoryLimitChanged(int newHistoryLimit) {
|
||||
((DecodeEventModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearHistoryClicked() {
|
||||
((DecodeEventModel) mTable.getModel()).clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom cell renderer for displaying identifiers from an identifier collection
|
||||
*/
|
||||
@@ -479,4 +417,26 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Row filter for decode events
|
||||
*/
|
||||
public class EventRowFilter extends RowFilter<TableModel, Integer>
|
||||
{
|
||||
@Override
|
||||
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
|
||||
{
|
||||
if(entry.getModel() instanceof DecodeEventModel model)
|
||||
{
|
||||
IDecodeEvent event = model.getItem(entry.getIdentifier());
|
||||
|
||||
if(event != null)
|
||||
{
|
||||
return mFilterSet.canProcess(event) && mFilterSet.passes(event);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,17 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Enumeration of event types for decoded events.
|
||||
*/
|
||||
public enum DecodeEventType
|
||||
{
|
||||
AFFILIATE("Affiliate"),
|
||||
@@ -43,6 +49,7 @@ public enum DecodeEventType
|
||||
CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"),
|
||||
CALL_NO_TUNER("Call - No Tuner"),
|
||||
CALL_TIMEOUT("Call Timeout"),
|
||||
CELLOCATOR("Cellocator"),
|
||||
COMMAND("Command"),
|
||||
DATA_CALL("Data Call"),
|
||||
DATA_CALL_ENCRYPTED("Encrypted Data Call"),
|
||||
@@ -55,6 +62,7 @@ public enum DecodeEventType
|
||||
ID_ANI("ANI"),
|
||||
ID_UNIQUE("Unique ID"),
|
||||
IP_PACKET("IP Packet"),
|
||||
LRRP("Motorola LRRP"),
|
||||
NOTIFICATION("Notification"),
|
||||
PAGE("Page"),
|
||||
QUERY("Query"),
|
||||
@@ -74,16 +82,82 @@ public enum DecodeEventType
|
||||
|
||||
private String mLabel;
|
||||
|
||||
/**
|
||||
* Encrypted voice call event types for filtering
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> VOICE_CALLS_ENCRYPTED = EnumSet.of(DecodeEventType.CALL_ENCRYPTED,
|
||||
DecodeEventType.CALL_GROUP_ENCRYPTED, DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
|
||||
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED);
|
||||
|
||||
/**
|
||||
* Voice call event types for filtering
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> VOICE_CALLS = EnumSet.of(DecodeEventType.CALL_GROUP,
|
||||
DecodeEventType.CALL_PATCH_GROUP, DecodeEventType.CALL_ALERT, DecodeEventType.CALL_DETECT,
|
||||
DecodeEventType.CALL_DO_NOT_MONITOR, DecodeEventType.CALL_END, DecodeEventType.CALL_INTERCONNECT,
|
||||
DecodeEventType.CALL_UNIQUE_ID, DecodeEventType.CALL_UNIT_TO_UNIT, DecodeEventType.CALL_NO_TUNER,
|
||||
DecodeEventType.CALL_TIMEOUT);
|
||||
|
||||
/**
|
||||
* Command event types for filtering
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> COMMANDS = EnumSet.of(DecodeEventType.ANNOUNCEMENT,
|
||||
DecodeEventType.STATION_ID, DecodeEventType.ACKNOWLEDGE, DecodeEventType.PAGE, DecodeEventType.QUERY,
|
||||
DecodeEventType.RADIO_CHECK, DecodeEventType.STATUS, DecodeEventType.COMMAND, DecodeEventType.EMERGENCY,
|
||||
DecodeEventType.NOTIFICATION, DecodeEventType.FUNCTION);
|
||||
|
||||
/**
|
||||
* Data call event types for filtering
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> DATA_CALLS = EnumSet.of(DecodeEventType.DATA_CALL,
|
||||
DecodeEventType.DATA_CALL_ENCRYPTED, DecodeEventType.DATA_PACKET, DecodeEventType.GPS,
|
||||
DecodeEventType.IP_PACKET, DecodeEventType.UDP_PACKET, DecodeEventType.SDM, DecodeEventType.ID_ANI,
|
||||
DecodeEventType.ID_UNIQUE);
|
||||
|
||||
/**
|
||||
* Registration event types for filtering
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> REGISTRATION = EnumSet.of(DecodeEventType.AFFILIATE,
|
||||
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, DecodeEventType.REGISTER, DecodeEventType.REGISTER_ESN,
|
||||
DecodeEventType.DEREGISTER, DecodeEventType.REQUEST, DecodeEventType.RESPONSE, DecodeEventType.RESPONSE_PACKET);
|
||||
|
||||
/**
|
||||
* All other event types of this enumeration that are not included in the groupings above.
|
||||
*/
|
||||
public static final EnumSet<DecodeEventType> OTHERS = EnumSet.copyOf(Arrays.stream(DecodeEventType.values())
|
||||
.filter(decodeEventType -> !decodeEventType.isGrouped()).toList());
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param label for the element
|
||||
*/
|
||||
DecodeEventType(String label)
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the enumeration element is contained in one of the enumset groupings above.
|
||||
* @return true if the element is grouped.
|
||||
*/
|
||||
public boolean isGrouped()
|
||||
{
|
||||
return VOICE_CALLS.contains(this) || VOICE_CALLS_ENCRYPTED.contains(this) || COMMANDS.contains(this) ||
|
||||
DATA_CALLS.contains(this) || REGISTRATION.contains(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Label or pretty value for the element
|
||||
* @return label
|
||||
*/
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses label as the default string value.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
return mLabel;
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event;
|
||||
|
||||
import io.github.dsheirer.filter.FilterEditor;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* History management panel with controls for managing item histories.
|
||||
*/
|
||||
public class HistoryManagementPanel<T> extends JPanel
|
||||
{
|
||||
private ClearableHistoryModel mModel;
|
||||
private FilterSet<T> mFilterSet;
|
||||
private FilterEditor<T> mFilterEditor;
|
||||
private JButton mClearButton;
|
||||
private JButton mFilterButton;
|
||||
private JSlider mHistorySlider;
|
||||
private JLabel mHistoryTitleLabel;
|
||||
private JLabel mHistoryValueLabel;
|
||||
private String mFilterEditorTitle;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param model to manage
|
||||
*/
|
||||
public HistoryManagementPanel(ClearableHistoryModel model, String filterEditorTitle)
|
||||
{
|
||||
mModel = model;
|
||||
mFilterEditorTitle = filterEditorTitle;
|
||||
setLayout(new MigLayout("insets 6 1 5 5", "[]5[]10[]5[]5[][grow]", ""));
|
||||
add(getFilterButton());
|
||||
add(getClearButton());
|
||||
add(getHistoryTitleLabel());
|
||||
add(getHistorySlider());
|
||||
add(getHistoryValueLabel());
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the filter set
|
||||
* @param filterSet to use
|
||||
*/
|
||||
public void updateFilterSet(FilterSet<T> filterSet)
|
||||
{
|
||||
mFilterSet = filterSet;
|
||||
|
||||
if(mFilterEditor != null)
|
||||
{
|
||||
getFilterEditor().updateFilterSet(filterSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the panel method to also set the enabled state for the child controls.
|
||||
* @param enabled true if this component should be enabled, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
super.setEnabled(enabled);
|
||||
getClearButton().setEnabled(enabled);
|
||||
getFilterButton().setEnabled(enabled);
|
||||
getHistoryValueLabel().setEnabled(enabled);
|
||||
getHistoryTitleLabel().setEnabled(enabled);
|
||||
getHistorySlider().setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter editor
|
||||
* @return editor lazily constructed
|
||||
*/
|
||||
private FilterEditor<T> getFilterEditor()
|
||||
{
|
||||
if(mFilterEditor == null)
|
||||
{
|
||||
mFilterEditor = new FilterEditor<>(mFilterEditorTitle, getFilterButton(), mFilterSet);
|
||||
}
|
||||
|
||||
return mFilterEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter button for accessing the filter editor
|
||||
* @return filter button
|
||||
*/
|
||||
private JButton getFilterButton()
|
||||
{
|
||||
if(mFilterButton == null)
|
||||
{
|
||||
mFilterButton = new JButton("Filters");
|
||||
mFilterButton.setToolTipText("Edit filters");
|
||||
mFilterButton.addActionListener(arg0 -> EventQueue.invokeLater(() -> getFilterEditor().setVisible(true)));
|
||||
}
|
||||
|
||||
return mFilterButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear button
|
||||
* @return clear button
|
||||
*/
|
||||
private JButton getClearButton()
|
||||
{
|
||||
if(mClearButton == null)
|
||||
{
|
||||
mClearButton = new JButton("Clear");
|
||||
mClearButton.addActionListener(e -> mModel.clear());
|
||||
mClearButton.setToolTipText("Clears the history");
|
||||
}
|
||||
|
||||
return mClearButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* History value label.
|
||||
* @return label
|
||||
*/
|
||||
private JLabel getHistoryValueLabel()
|
||||
{
|
||||
if(mHistoryValueLabel == null)
|
||||
{
|
||||
mHistoryValueLabel = new JLabel(String.valueOf(ClearableHistoryModel.DEFAULT_HISTORY_SIZE));
|
||||
}
|
||||
|
||||
return mHistoryValueLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* History title label
|
||||
* @return label
|
||||
*/
|
||||
private JLabel getHistoryTitleLabel()
|
||||
{
|
||||
if(mHistoryTitleLabel == null)
|
||||
{
|
||||
mHistoryTitleLabel = new JLabel("History:");
|
||||
}
|
||||
|
||||
return mHistoryTitleLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* History value slider control
|
||||
* @return slider
|
||||
*/
|
||||
private JSlider getHistorySlider()
|
||||
{
|
||||
if(mHistorySlider == null)
|
||||
{
|
||||
mHistorySlider = new JSlider();
|
||||
mHistorySlider.setToolTipText("Adjust history size. Double-click to reset to default 200");
|
||||
mHistorySlider.setMinimum(0);
|
||||
mHistorySlider.setMaximum(2000);
|
||||
mHistorySlider.setMinorTickSpacing(25);
|
||||
mHistorySlider.setMajorTickSpacing(500);
|
||||
mHistorySlider.setPaintTicks(false);
|
||||
mHistorySlider.setPaintLabels(false);
|
||||
mHistorySlider.addMouseListener(new MouseListener()
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent arg0)
|
||||
{
|
||||
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
|
||||
{
|
||||
mHistorySlider.setValue(ClearableHistoryModel.DEFAULT_HISTORY_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent arg0) {}
|
||||
public void mouseExited(MouseEvent arg0) {}
|
||||
public void mousePressed(MouseEvent arg0) {}
|
||||
public void mouseReleased(MouseEvent arg0) {}
|
||||
});
|
||||
|
||||
mHistorySlider.setValue(mModel.getHistorySize());
|
||||
mHistorySlider.addChangeListener(e -> {
|
||||
mModel.setHistorySize(mHistorySlider.getValue());
|
||||
getHistoryValueLabel().setText(String.valueOf(mHistorySlider.getValue()));
|
||||
});
|
||||
}
|
||||
|
||||
return mHistorySlider;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -48,11 +48,6 @@ public interface IDecodeEvent
|
||||
*/
|
||||
long getDuration();
|
||||
|
||||
/**
|
||||
* A description of the event or event type
|
||||
*/
|
||||
String getEventDescription();
|
||||
|
||||
/**
|
||||
* Collection of identifiers associated with the event. This collection should contain a
|
||||
* Role.FROM and a Role.TO identifier, a Decoder Type identifier, and (optionally) an Alias List
|
||||
@@ -86,7 +81,7 @@ public interface IDecodeEvent
|
||||
* Timeslot for the event.
|
||||
* @return timeslot or default of 0
|
||||
*/
|
||||
Integer getTimeslot();
|
||||
int getTimeslot();
|
||||
|
||||
/**
|
||||
* Indicates if the event has a timeslot specified
|
||||
|
||||
@@ -18,19 +18,16 @@
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event;
|
||||
|
||||
import io.github.dsheirer.filter.AllPassFilter;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.StuffBitsMessage;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.awt.EventQueue;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
public class MessageActivityModel extends AbstractTableModel implements Listener<IMessage>
|
||||
/**
|
||||
* Table Model for decoded IMessages.
|
||||
*/
|
||||
public class MessageActivityModel extends ClearableHistoryModel<MessageItem> implements Listener<IMessage>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final int TIME = 0;
|
||||
@@ -38,95 +35,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
|
||||
private static final int TIMESLOT = 2;
|
||||
private static final int MESSAGE = 3;
|
||||
|
||||
protected int mMaxMessages = 200;
|
||||
protected LinkedList<MessageItem> mMessageItems = new LinkedList<>();
|
||||
protected int[] mColumnWidths = {20, 20, 500};
|
||||
protected String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
|
||||
|
||||
private String[] mHeaders = new String[]{"Time", "Protocol", "Timeslot", "Message"};
|
||||
private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
|
||||
|
||||
private FilterSet<IMessage> mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public MessageActivityModel()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the filter set
|
||||
* Implements the listener interface and wraps the IMessage in table-compatible message item wrapper.
|
||||
* @param message to add to the model
|
||||
*/
|
||||
public void setFilters(FilterSet filterSet)
|
||||
{
|
||||
mMessageFilterSet = filterSet;
|
||||
}
|
||||
|
||||
public void clearFilters()
|
||||
{
|
||||
mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all messages from history
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
EventQueue.invokeLater(() -> {
|
||||
mMessageItems.clear();
|
||||
fireTableDataChanged();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the current messages and loads the messages argument
|
||||
*/
|
||||
public void clearAndSet(List<IMessage> messages)
|
||||
{
|
||||
EventQueue.invokeLater(() -> {
|
||||
mMessageItems.clear();
|
||||
fireTableDataChanged();
|
||||
for(IMessage message: messages)
|
||||
{
|
||||
receive(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public FilterSet<IMessage> getMessageFilterSet()
|
||||
{
|
||||
return mMessageFilterSet;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mMessageItems.clear();
|
||||
}
|
||||
|
||||
public int[] getColumnWidths()
|
||||
{
|
||||
return mColumnWidths;
|
||||
}
|
||||
|
||||
public void setColumnWidths(int[] widths)
|
||||
{
|
||||
if(widths.length != 3)
|
||||
{
|
||||
throw new IllegalArgumentException("MessageActivityModel - column widths array should have 3 elements");
|
||||
}
|
||||
else
|
||||
{
|
||||
mColumnWidths = widths;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount(int count)
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
public void receive(final IMessage message)
|
||||
{
|
||||
//Don't process tail bits or stuff bits message fragments
|
||||
@@ -135,39 +57,7 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
|
||||
return;
|
||||
}
|
||||
|
||||
if(mMessageFilterSet.passes(message))
|
||||
{
|
||||
final MessageItem messageItem = new MessageItem(message);
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mMessageItems.addFirst(messageItem);
|
||||
|
||||
MessageActivityModel.this.fireTableRowsInserted(0, 0);
|
||||
|
||||
prune();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
while(mMessageItems.size() > mMaxMessages)
|
||||
{
|
||||
MessageItem removed = mMessageItems.removeLast();
|
||||
removed.dispose();
|
||||
super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mMessageItems.size();
|
||||
EventQueue.invokeLater(() -> add(new MessageItem(message)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,20 +74,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
if(0 <= rowIndex && rowIndex < mMessageItems.size())
|
||||
{
|
||||
MessageItem messageItem = mMessageItems.get(rowIndex);
|
||||
MessageItem item = getItem(rowIndex);
|
||||
|
||||
if(item != null)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case TIME:
|
||||
return messageItem.getTimestamp(mSDFTime);
|
||||
return item.getTimestamp(mSDFTime);
|
||||
case PROTOCOL:
|
||||
return messageItem.getProtocol();
|
||||
return item.getProtocol();
|
||||
case TIMESLOT:
|
||||
return messageItem.getTimeslot();
|
||||
return item.getTimeslot();
|
||||
case MESSAGE:
|
||||
return messageItem.getText();
|
||||
return item.getText();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,13 +14,10 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event;
|
||||
|
||||
import com.jidesoft.swing.JideButton;
|
||||
import com.jidesoft.swing.JideSplitButton;
|
||||
import io.github.dsheirer.filter.FilterEditorPanel;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.MessageHistory;
|
||||
@@ -30,37 +26,35 @@ import io.github.dsheirer.module.decode.DecoderFactory;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
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 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.RowFilter;
|
||||
import javax.swing.table.TableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
|
||||
/**
|
||||
* Panel to display decoded messages/activity.
|
||||
*/
|
||||
public class MessageActivityPanel extends JPanel implements Listener<ProcessingChain>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
|
||||
private static final String TABLE_PREFERENCE_KEY = "message.activity.panel";
|
||||
private static MessageActivityModel mMessageModel = new MessageActivityModel();
|
||||
private static final Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
|
||||
private final String TABLE_PREFERENCE_KEY = "message.activity.panel";
|
||||
private MessageActivityModel mMessageModel = new MessageActivityModel();
|
||||
private MessageHistory mCurrentMessageHistory;
|
||||
private JTable mTable = new JTable(mMessageModel);
|
||||
private TableRowSorter<TableModel> mTableRowSorter;
|
||||
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
|
||||
private UserPreferences mUserPreferences;
|
||||
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
|
||||
private FilterSet<IMessage> mMessageFilterSet;
|
||||
private HistoryManagementPanel<IMessage> mHistoryManagementPanel;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -69,13 +63,14 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
|
||||
public MessageActivityPanel(UserPreferences userPreferences)
|
||||
{
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
|
||||
|
||||
add(mManagementPanel, "span,growx");
|
||||
|
||||
add(new JScrollPane(mTable), "span,grow");
|
||||
mTableRowSorter = new TableRowSorter<>(mMessageModel);
|
||||
mTableRowSorter.setRowFilter(new MessageRowFilter());
|
||||
mTable.setRowSorter(mTableRowSorter);
|
||||
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
|
||||
mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor");
|
||||
add(mHistoryManagementPanel, "span,growx");
|
||||
add(new JScrollPane(mTable), "span,grow");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,182 +84,62 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
|
||||
mCurrentMessageHistory.removeListener(mMessageModel);
|
||||
}
|
||||
|
||||
//Unregister from changes made to the filter set
|
||||
if(mMessageFilterSet != null)
|
||||
{
|
||||
mMessageFilterSet.register(null);
|
||||
}
|
||||
|
||||
if(processingChain != null)
|
||||
{
|
||||
mCurrentMessageHistory = processingChain.getMessageHistory();
|
||||
mMessageModel.setFilters(DecoderFactory.getMessageFilters(processingChain.getModules()));
|
||||
mMessageModel.clearAndSet(mCurrentMessageHistory.getItems());
|
||||
mMessageFilterSet = DecoderFactory.getMessageFilters(processingChain.getModules());
|
||||
//Register filter change listener to refresh the table any time the event filters are changed.
|
||||
mMessageFilterSet.register(() -> mMessageModel.fireTableDataChanged());
|
||||
if(mHistoryManagementPanel != null)
|
||||
{
|
||||
mHistoryManagementPanel.updateFilterSet(mMessageFilterSet);
|
||||
}
|
||||
|
||||
List<MessageItem> currentHistory = new ArrayList<>();
|
||||
for(IMessage message: mCurrentMessageHistory.getItems())
|
||||
{
|
||||
currentHistory.add(new MessageItem(message));
|
||||
}
|
||||
|
||||
mMessageModel.clearAndSet(currentHistory);
|
||||
mCurrentMessageHistory.addListener(mMessageModel);
|
||||
mManagementPanel.enableButtons();
|
||||
mHistoryManagementPanel.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentMessageHistory = null;
|
||||
mMessageModel.clearFilters();
|
||||
mMessageFilterSet = null;
|
||||
mMessageModel.clear();
|
||||
mManagementPanel.disableButtons();
|
||||
mHistoryManagementPanel.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageManagementPanel extends JPanel
|
||||
/**
|
||||
* Row visibility filter for messages
|
||||
*/
|
||||
public class MessageRowFilter extends RowFilter<TableModel, Integer>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
|
||||
private MessageFilterButton mFilterButton = new MessageFilterButton();
|
||||
|
||||
public MessageManagementPanel()
|
||||
@Override
|
||||
public boolean include(Entry<? extends TableModel, ? extends Integer> entry)
|
||||
{
|
||||
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
|
||||
|
||||
disableButtons();
|
||||
|
||||
add(mFilterButton);
|
||||
add(mHistoryButton);
|
||||
}
|
||||
|
||||
public void enableButtons()
|
||||
{
|
||||
mHistoryButton.setEnabled(true);
|
||||
mFilterButton.setEnabled(true);
|
||||
}
|
||||
|
||||
public void disableButtons()
|
||||
{
|
||||
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()
|
||||
if(entry.getModel() instanceof MessageActivityModel model)
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent arg0)
|
||||
MessageItem item = model.getItem(entry.getIdentifier());
|
||||
|
||||
if(mMessageFilterSet != null && item != null && item.getMessage() != null)
|
||||
{
|
||||
if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
|
||||
{
|
||||
slider.setValue(500); //default
|
||||
}
|
||||
IMessage message = item.getMessage();
|
||||
return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent arg0)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent arg0)
|
||||
{
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent arg0)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent arg0)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
slider.setValue(((MessageActivityModel) mTable.getModel()).getMaxMessageCount());
|
||||
slider.addChangeListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent arg0)
|
||||
{
|
||||
((MessageActivityModel) mTable.getModel()).setMaxMessageCount(slider.getValue());
|
||||
}
|
||||
});
|
||||
|
||||
historyPanel.add(slider);
|
||||
|
||||
add(historyPanel);
|
||||
|
||||
/* Clear messages */
|
||||
addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
((MessageActivityModel) mTable.getModel()).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)
|
||||
{
|
||||
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<IMessage> filter = (FilterSet<IMessage>) ((MessageActivityModel) mTable.getModel())
|
||||
.getMessageFilterSet();
|
||||
|
||||
FilterEditorPanel<IMessage> panel = new FilterEditorPanel<>(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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.event;
|
||||
@@ -31,19 +30,20 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
private double mHeading;
|
||||
private double mSpeed;
|
||||
|
||||
public PlottableDecodeEvent(long start)
|
||||
public PlottableDecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decode event builder with the specified start timestamp.
|
||||
* @param decodeEventType for the event
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static PlottableDecodeEventBuilder plottableBuilder(long timeStart)
|
||||
public static PlottableDecodeEventBuilder plottableBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
return new PlottableDecodeEventBuilder(timeStart);
|
||||
return new PlottableDecodeEventBuilder(decodeEventType, timeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +102,6 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
{
|
||||
private long mTimeStart;
|
||||
private long mDuration;
|
||||
private String mEventDescription;
|
||||
private DecodeEventType mDecodeEventType;
|
||||
private IdentifierCollection mIdentifierCollection;
|
||||
private IChannelDescriptor mChannelDescriptor;
|
||||
@@ -115,8 +114,9 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
/**
|
||||
* Constructs a builder instance with the specified start time in milliseconds
|
||||
*/
|
||||
public PlottableDecodeEventBuilder(long timeStart)
|
||||
public PlottableDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = timeStart;
|
||||
}
|
||||
|
||||
@@ -150,16 +150,6 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
* @param description of the event
|
||||
*/
|
||||
public PlottableDecodeEventBuilder eventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier collection.
|
||||
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
|
||||
@@ -191,15 +181,6 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Decode Event type for this event.
|
||||
* @param eventType
|
||||
*/
|
||||
public PlottableDecodeEventBuilder eventType(DecodeEventType eventType) {
|
||||
mDecodeEventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location for the plottable event
|
||||
*/
|
||||
@@ -232,12 +213,10 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
*/
|
||||
public PlottableDecodeEvent build()
|
||||
{
|
||||
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart);
|
||||
PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart);
|
||||
decodeEvent.setChannelDescriptor(mChannelDescriptor);
|
||||
decodeEvent.setDetails(mDetails);
|
||||
decodeEvent.setDuration(mDuration);
|
||||
decodeEvent.setEventType(mDecodeEventType);
|
||||
decodeEvent.setEventDescription(mEventDescription);
|
||||
decodeEvent.setIdentifierCollection(mIdentifierCollection);
|
||||
decodeEvent.setProtocol(mProtocol);
|
||||
decodeEvent.setLocation(mGeoPosition);
|
||||
@@ -246,5 +225,4 @@ public class PlottableDecodeEvent extends DecodeEvent
|
||||
return decodeEvent;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,19 +17,23 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
|
||||
/**
|
||||
* Request for decode event history from a processing chain for the specified channel.
|
||||
* Event filter for decode event types that are not specified in other event filters.
|
||||
*
|
||||
* This is intended as a catch-all for any elements of the DecodeEventType enumeration that may get added in the
|
||||
* future and are not deliberately added to the various event filter groupings listed in the enumeration.
|
||||
*/
|
||||
public class LastHeardHistoryRequest extends ModuleEventBusMessage
|
||||
public class AllOtherEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
* Constructor
|
||||
*/
|
||||
public LastHeardHistoryRequest()
|
||||
public AllOtherEventFilter()
|
||||
{
|
||||
super("Other", DecodeEventType.OTHERS);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,40 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
|
||||
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent> {
|
||||
public DecodeEventFilterSet() {
|
||||
super ("All Messages");
|
||||
|
||||
/**
|
||||
* Top-level decode event filter set.
|
||||
*/
|
||||
public class DecodeEventFilterSet extends FilterSet<IDecodeEvent>
|
||||
{
|
||||
public DecodeEventFilterSet()
|
||||
{
|
||||
super ("All Events");
|
||||
addFilter(new DecodedCallEventFilter());
|
||||
addFilter(new DecodedCallEncryptedEventFilter());
|
||||
addFilter(new DecodedDataEventFilter());
|
||||
addFilter(new DecodedCommandEventFilter());
|
||||
addFilter(new DecodedRegistrationEventFilter());
|
||||
addFilter(new AllOtherEventFilter());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,36 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Encrypted call events filter
|
||||
*/
|
||||
public class DecodedCallEncryptedEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DecodedCallEncryptedEventFilter()
|
||||
{
|
||||
super("Voice Calls - Encrypted", Arrays.asList(
|
||||
DecodeEventType.CALL_ENCRYPTED,
|
||||
DecodeEventType.CALL_GROUP_ENCRYPTED,
|
||||
DecodeEventType.CALL_PATCH_GROUP_ENCRYPTED,
|
||||
DecodeEventType.CALL_INTERCONNECT_ENCRYPTED,
|
||||
DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED
|
||||
));
|
||||
super("Voice Calls - Encrypted", DecodeEventType.VOICE_CALLS_ENCRYPTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Call events filter
|
||||
*/
|
||||
public class DecodedCallEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DecodedCallEventFilter()
|
||||
{
|
||||
super("Voice Calls", Arrays.asList(
|
||||
DecodeEventType.CALL_GROUP,
|
||||
DecodeEventType.CALL_PATCH_GROUP,
|
||||
DecodeEventType.CALL_ALERT,
|
||||
DecodeEventType.CALL_DETECT,
|
||||
DecodeEventType.CALL_DO_NOT_MONITOR,
|
||||
DecodeEventType.CALL_END,
|
||||
DecodeEventType.CALL_INTERCONNECT,
|
||||
DecodeEventType.CALL_UNIQUE_ID,
|
||||
DecodeEventType.CALL_UNIT_TO_UNIT,
|
||||
DecodeEventType.CALL_NO_TUNER,
|
||||
DecodeEventType.CALL_TIMEOUT
|
||||
));
|
||||
super("Voice Calls", DecodeEventType.VOICE_CALLS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,24 +20,17 @@
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Event filter for network commands.
|
||||
*/
|
||||
public class DecodedCommandEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DecodedCommandEventFilter()
|
||||
{
|
||||
super("Commands", Arrays.asList(
|
||||
DecodeEventType.ANNOUNCEMENT,
|
||||
DecodeEventType.STATION_ID,
|
||||
DecodeEventType.ACKNOWLEDGE,
|
||||
DecodeEventType.PAGE,
|
||||
DecodeEventType.QUERY,
|
||||
DecodeEventType.RADIO_CHECK,
|
||||
DecodeEventType.STATUS,
|
||||
DecodeEventType.COMMAND,
|
||||
DecodeEventType.EMERGENCY,
|
||||
DecodeEventType.NOTIFICATION,
|
||||
DecodeEventType.FUNCTION
|
||||
));
|
||||
super("Commands", DecodeEventType.COMMANDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,36 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Event filter for data call event types
|
||||
*/
|
||||
public class DecodedDataEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public DecodedDataEventFilter()
|
||||
{
|
||||
super("Data Calls", Arrays.asList(
|
||||
DecodeEventType.DATA_CALL,
|
||||
DecodeEventType.DATA_CALL_ENCRYPTED,
|
||||
DecodeEventType.DATA_PACKET,
|
||||
DecodeEventType.GPS,
|
||||
DecodeEventType.IP_PACKET,
|
||||
DecodeEventType.UDP_PACKET,
|
||||
DecodeEventType.SDM,
|
||||
DecodeEventType.ID_ANI,
|
||||
DecodeEventType.ID_UNIQUE
|
||||
));
|
||||
super("Data Calls", DecodeEventType.DATA_CALLS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,36 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Event filter for registration related events
|
||||
*/
|
||||
public class DecodedRegistrationEventFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public DecodedRegistrationEventFilter()
|
||||
{
|
||||
super("Registrations", Arrays.asList(
|
||||
DecodeEventType.AFFILIATE,
|
||||
DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE,
|
||||
DecodeEventType.REGISTER,
|
||||
DecodeEventType.REGISTER_ESN,
|
||||
DecodeEventType.DEREGISTER,
|
||||
DecodeEventType.REQUEST,
|
||||
DecodeEventType.RESPONSE,
|
||||
DecodeEventType.RESPONSE_PACKET
|
||||
));
|
||||
super("Registrations", DecodeEventType.REGISTRATION);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import com.jidesoft.swing.JideSplitButton;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
public class EventClearButton extends JideSplitButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -21,37 +39,29 @@ public class EventClearButton extends JideSplitButton
|
||||
|
||||
JPanel historyPanel = new JPanel();
|
||||
|
||||
historyPanel.add(new JLabel("History Entries:"));
|
||||
|
||||
historyPanel.add(new JLabel("History Size:"));
|
||||
final JSlider historySlider = initializeHistorySlider();
|
||||
JLabel valueLabel = new JLabel(String.valueOf(maxHistoryCount));
|
||||
|
||||
historySlider.setValue(maxHistoryCount);
|
||||
historySlider.addChangeListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent arg0)
|
||||
historySlider.addChangeListener(arg0 -> {
|
||||
if (mEventClearHandler != null)
|
||||
{
|
||||
if (mEventClearHandler != null)
|
||||
{
|
||||
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
|
||||
}
|
||||
mEventClearHandler.onHistoryLimitChanged(historySlider.getValue());
|
||||
}
|
||||
|
||||
valueLabel.setText(String.valueOf(historySlider.getValue()));
|
||||
});
|
||||
|
||||
historyPanel.add(historySlider);
|
||||
|
||||
historyPanel.add(valueLabel);
|
||||
add(historyPanel);
|
||||
|
||||
/* This handles the click action on the main button. Clear messages */
|
||||
addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
addActionListener(e -> {
|
||||
if (mEventClearHandler != null)
|
||||
{
|
||||
if (mEventClearHandler != null)
|
||||
{
|
||||
mEventClearHandler.onClearHistoryClicked();
|
||||
}
|
||||
mEventClearHandler.onClearHistoryClicked();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,37 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
public interface EventClearHandler {
|
||||
/**
|
||||
* Handler interface for changes to the event history size or content.
|
||||
*/
|
||||
public interface EventClearHandler
|
||||
{
|
||||
/**
|
||||
* Invoked when the retained history size limit is changed
|
||||
* @param newHistoryLimit new size
|
||||
*/
|
||||
void onHistoryLimitChanged(int newHistoryLimit);
|
||||
|
||||
/**
|
||||
* Invoked when the history is cleared
|
||||
*/
|
||||
void onClearHistoryClicked();
|
||||
}
|
||||
|
||||
@@ -1,56 +1,74 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* This is used as a base for {@link IDecodeEvent} types.
|
||||
* This will take a list of {@link IDecodeEvent}s and generate filters for the list.
|
||||
* This will default canProcess to TRUE. Override if other value or evaluation is needed.
|
||||
*
|
||||
*/
|
||||
public class EventFilter extends Filter<IDecodeEvent>
|
||||
public class EventFilter extends Filter<IDecodeEvent,DecodeEventType>
|
||||
{
|
||||
private Map<DecodeEventType, FilterElement<DecodeEventType>> mElements = new EnumMap<>(DecodeEventType.class);
|
||||
private EventKeyExtractor mKeyExtractor = new EventKeyExtractor();
|
||||
|
||||
public EventFilter(String name, List<DecodeEventType> eventTypes)
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param name of this filter
|
||||
* @param decodeEventTypes to filter against
|
||||
*/
|
||||
public EventFilter(String name, Collection<DecodeEventType> decodeEventTypes)
|
||||
{
|
||||
super(name);
|
||||
|
||||
for (DecodeEventType eventType : eventTypes) {
|
||||
mElements.put(eventType, new FilterElement<>(eventType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
if (mEnabled && canProcess(iDecodeEvent))
|
||||
for (DecodeEventType decodeEventType : decodeEventTypes)
|
||||
{
|
||||
if (mElements.containsKey(iDecodeEvent.getEventType()))
|
||||
{
|
||||
return mElements.get(iDecodeEvent.getEventType()).isEnabled();
|
||||
}
|
||||
add(new FilterElement<>(decodeEventType));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor function
|
||||
* @return function.
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(IDecodeEvent iDecodeEvent)
|
||||
public Function<IDecodeEvent, DecodeEventType> getKeyExtractor()
|
||||
{
|
||||
return true;
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
/**
|
||||
* Key extractor function for decode events to extract the decode event type
|
||||
*/
|
||||
public static class EventKeyExtractor implements Function<IDecodeEvent,DecodeEventType>
|
||||
{
|
||||
return new ArrayList<>(mElements.values());
|
||||
@Override
|
||||
public DecodeEventType apply(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
return iDecodeEvent.getEventType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +1,103 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import com.jidesoft.swing.JideButton;
|
||||
import io.github.dsheirer.filter.FilterEditorPanel;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JScrollPane;
|
||||
|
||||
/**
|
||||
* Event filter button that includes split button functionality to allow user to select filter items.
|
||||
*
|
||||
* @param <T> type of filter.
|
||||
*/
|
||||
public class EventFilterButton<T> extends JideButton
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param dialogTitle for the dialog/panel that appears
|
||||
* @param filterSet to include in the editor panel.
|
||||
*/
|
||||
public EventFilterButton(String dialogTitle, FilterSet<T> filterSet)
|
||||
{
|
||||
this("Filter", dialogTitle, filterSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param buttonLabel to use on the button
|
||||
* @param dialogTitle for the dialog/panel that appears
|
||||
* @param filterSet to include in the editor panel.
|
||||
*/
|
||||
public EventFilterButton(String buttonLabel, String dialogTitle, FilterSet<T> filterSet)
|
||||
{
|
||||
super(buttonLabel);
|
||||
|
||||
addActionListener(
|
||||
new EventFilterActionHandler(dialogTitle, filterSet)
|
||||
);
|
||||
addActionListener(new EventFilterActionHandler(dialogTitle, filterSet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Action handler for the button
|
||||
*/
|
||||
public class EventFilterActionHandler implements ActionListener
|
||||
{
|
||||
private String title;
|
||||
private FilterSet<T> filterSet;
|
||||
private String mTitle;
|
||||
private FilterSet<T> mFilterSet;
|
||||
|
||||
public EventFilterActionHandler( String title, FilterSet<T> filterSet)
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param title for this panel
|
||||
* @param filterSet to edit
|
||||
*/
|
||||
public EventFilterActionHandler(String title, FilterSet<T> filterSet)
|
||||
{
|
||||
this.title = title;
|
||||
this.filterSet = filterSet;
|
||||
mTitle = title;
|
||||
mFilterSet = filterSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
final JFrame editor = new JFrame();
|
||||
|
||||
editor.setTitle(title);
|
||||
editor.setTitle(mTitle);
|
||||
editor.setLocationRelativeTo(EventFilterButton.this);
|
||||
editor.setSize(600, 400);
|
||||
editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
editor.setLayout(new MigLayout("", "[grow,fill]",
|
||||
"[grow,fill][][]"));
|
||||
|
||||
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(filterSet);
|
||||
|
||||
editor.setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][][]"));
|
||||
FilterEditorPanel<T> panel = new FilterEditorPanel<T>(mFilterSet);
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
close.addActionListener(e1 -> editor.dispose());
|
||||
editor.add(close);
|
||||
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
editor.setVisible(true);
|
||||
}
|
||||
});
|
||||
EventQueue.invokeLater(() -> editor.setVisible(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package io.github.dsheirer.module.decode.event.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
|
||||
public interface EventFilterProvider<T> {
|
||||
FilterSet<T> getFilterSet();
|
||||
void setFilterSet(FilterSet<T> filterSet);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
|
||||
public class LastHeardFilterSet extends FilterSet<IDecodeEvent>
|
||||
{
|
||||
|
||||
public LastHeardFilterSet(AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
super("Last Heard To Channel");
|
||||
|
||||
addFilter(new LastHeardIconFilter("Icon", aliasModel, iconModel));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.module.HistoryModule;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
/**
|
||||
* Decode event history module. Maintains a history of decode events and constrains the total history size.
|
||||
*/
|
||||
public class LastHeardHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public LastHeardHistory(int historySize)
|
||||
{
|
||||
super(historySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
|
||||
*/
|
||||
@Override
|
||||
public Listener<IDecodeEvent> getDecodeEventListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process preload data
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(DecodeEventHistoryPreloadData preloadData)
|
||||
{
|
||||
for(IDecodeEvent decodeEvent: preloadData.getData())
|
||||
{
|
||||
receive(decodeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a request for decode event history and posts the response back
|
||||
* to the processing chain event bus so that any of the modules can receive that history.
|
||||
*
|
||||
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
|
||||
* event history from a traffic channel conversion to the new rest channel.
|
||||
*
|
||||
* @param request for decode event history
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(LastHeardHistoryRequest request)
|
||||
{
|
||||
getInterModuleEventBus().post(new LastHeardHistoryResponse(this));
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.icon.Icon;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LastHeardIconFilter extends Filter<IDecodeEvent>
|
||||
{
|
||||
private AliasModel mAliasModel;
|
||||
private Map<String, FilterElement<String>> mElements = new HashMap<>();
|
||||
|
||||
public LastHeardIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
super(name);
|
||||
this.mAliasModel = aliasModel;
|
||||
|
||||
ObservableList<Icon> icons = iconModel.iconsProperty();
|
||||
for (Icon icon : icons) {
|
||||
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
if (mEnabled && canProcess(iDecodeEvent))
|
||||
{
|
||||
return matchAliasToFilter(iDecodeEvent)
|
||||
.map(FilterElement::isEnabled)
|
||||
.orElse(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
|
||||
List<Identifier> identifiers = identifierCollection.getIdentifiers(Role.TO);
|
||||
if (iDecodeEvent.getDuration() > 0 && identifiers != null && !identifiers.isEmpty())
|
||||
{
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
if (aliasList != null)
|
||||
{
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
if (identifier.getForm() == Form.TALKGROUP)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
if (!aliases.isEmpty())
|
||||
{
|
||||
for (Alias alias : aliases) {
|
||||
if (alias.getIconName() == null)
|
||||
{
|
||||
// Return any to match aliases with no icon defined.
|
||||
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Optional.of(mElements.get(alias.getIconName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
{
|
||||
return new ArrayList<>(mElements.values());
|
||||
}
|
||||
}
|
||||
@@ -1,292 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class LastHeardModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LastHeardModel.class);
|
||||
|
||||
public static final int COLUMN_TIME = 0;
|
||||
public static final int COLUMN_DURATION = 1;
|
||||
public static final int COLUMN_EVENT = 2;
|
||||
public static final int COLUMN_FROM_ID = 3;
|
||||
public static final int COLUMN_FROM_ALIAS = 4;
|
||||
public static final int COLUMN_TO_ID = 5;
|
||||
public static final int COLUMN_TO_ALIAS = 6;
|
||||
public static final int COLUMN_CHANNEL = 7;
|
||||
public static final int COLUMN_FREQUENCY = 8;
|
||||
public static final int COLUMN_DETAILS = 9;
|
||||
|
||||
protected int mMaxMessages = 500;
|
||||
|
||||
protected List<IDecodeEvent> mEvents = new ArrayList<>();
|
||||
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
|
||||
|
||||
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
|
||||
|
||||
public LastHeardModel(AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
mLastSeenFilterSet = new LastHeardFilterSet(aliasModel, iconModel);
|
||||
MyEventBus.getGlobalEventBus().register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives preference update notifications via the event bus
|
||||
* @param preferenceType that was updated
|
||||
*/
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_TIME);
|
||||
}
|
||||
}
|
||||
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_FROM_ID);
|
||||
fireTableCellUpdated(row, COLUMN_TO_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the complete list of events managed by this model.
|
||||
*/
|
||||
public List<IDecodeEvent> getEvents()
|
||||
{
|
||||
return new ArrayList<>(mEvents);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().unregister(this);
|
||||
Iterator<IDecodeEvent> it = mEvents.iterator();
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
mEvents.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all events from this model and loads the events argument
|
||||
*/
|
||||
public void clearAndSet(List<IDecodeEvent> events)
|
||||
{
|
||||
mEvents.clear();
|
||||
mEvents.addAll(events);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
dispose();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount(int count)
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds, updates or deletes the event from the model. Producers can send
|
||||
* the same call event multiple times to indicate that information in the
|
||||
* event is updated. Producers can also mark the event as invalid and the
|
||||
* event will be removed from the model.
|
||||
*/
|
||||
public void receive(final IDecodeEvent event)
|
||||
{
|
||||
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!mEvents.contains(event))
|
||||
{
|
||||
mEvents.add(0, event);
|
||||
fireTableRowsInserted(0, 0);
|
||||
prune();
|
||||
}
|
||||
else
|
||||
{
|
||||
int row = mEvents.indexOf(event);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
while(mEvents.size() > mMaxMessages)
|
||||
{
|
||||
int index = mEvents.size() - 1;
|
||||
mEvents.remove(index);
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterSet<IDecodeEvent> getFilterSet() {
|
||||
return mLastSeenFilterSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
|
||||
this.mLastSeenFilterSet = filterSet;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
IDecodeEvent event = mEvents.get(rowIndex);
|
||||
|
||||
if(event != null)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_TIME:
|
||||
return event.getTimeStart();
|
||||
case COLUMN_DURATION:
|
||||
return event.getDuration();
|
||||
case COLUMN_EVENT:
|
||||
return event.getEventDescription();
|
||||
case COLUMN_FROM_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_FROM_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_CHANNEL:
|
||||
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
|
||||
|
||||
if(channelDescriptor != null)
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return channelDescriptor.toString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return "TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case COLUMN_FREQUENCY:
|
||||
return event.getChannelDescriptor();
|
||||
case COLUMN_DETAILS:
|
||||
return event.getDetails();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_DURATION:
|
||||
case COLUMN_TIME:
|
||||
return Long.class;
|
||||
case COLUMN_DETAILS:
|
||||
case COLUMN_EVENT:
|
||||
return String.class;
|
||||
case COLUMN_FREQUENCY:
|
||||
case COLUMN_FROM_ALIAS:
|
||||
case COLUMN_FROM_ID:
|
||||
case COLUMN_TO_ALIAS:
|
||||
case COLUMN_TO_ID:
|
||||
return IdentifierCollection.class;
|
||||
case COLUMN_CHANNEL:
|
||||
return String.class;
|
||||
}
|
||||
return super.getColumnClass(columnIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event.filter.lastheard;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.AllPassFilter;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventModel;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class LastHeardPanel extends JPanel implements Listener<ProcessingChain>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LastHeardPanel.class);
|
||||
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
|
||||
|
||||
private JTable mTable;
|
||||
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
|
||||
private LastHeardModel mLastHeardModel;
|
||||
private DecodeEventHistory mCurrentEventHistory;
|
||||
private JScrollPane mEmptyScroller;
|
||||
private IconModel mIconModel;
|
||||
private AliasModel mAliasModel;
|
||||
private UserPreferences mUserPreferences;
|
||||
private TimestampCellRenderer mTimestampCellRenderer;
|
||||
private LastSeenManagementPanel mLastSeenManagementPanel;
|
||||
|
||||
/**
|
||||
* View for call event table
|
||||
* @param iconModel to display alias icons in table rows
|
||||
*/
|
||||
public LastHeardPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().register(this);
|
||||
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
mIconModel = iconModel;
|
||||
mAliasModel = aliasModel;
|
||||
mLastHeardModel = new LastHeardModel(aliasModel, iconModel);
|
||||
mUserPreferences = userPreferences;
|
||||
mTimestampCellRenderer = new TimestampCellRenderer();
|
||||
mTable = new JTable(mLastHeardModel);
|
||||
mTable.setAutoCreateRowSorter(true);
|
||||
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
||||
mLastSeenManagementPanel = new LastSeenManagementPanel();
|
||||
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
|
||||
updateCellRenderers();
|
||||
|
||||
add(mLastSeenManagementPanel, "span,growx");
|
||||
|
||||
mEmptyScroller = new JScrollPane(mTable);
|
||||
add(mEmptyScroller);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().unregister(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives preference update notifications via the event bus
|
||||
* @param preferenceType that was updated
|
||||
*/
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCellRenderers()
|
||||
{
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
|
||||
.setCellRenderer(mTimestampCellRenderer);
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
|
||||
.setCellRenderer(new DurationCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
|
||||
.setCellRenderer(new ChannelDescriptorCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
|
||||
.setCellRenderer(new FrequencyCellRenderer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(final ProcessingChain processingChain)
|
||||
{
|
||||
if(mCurrentEventHistory != null)
|
||||
{
|
||||
mCurrentEventHistory.removeListener(mLastHeardModel);
|
||||
}
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
if(processingChain != null)
|
||||
{
|
||||
mCurrentEventHistory = processingChain.getDecodeEventHistory();
|
||||
mLastHeardModel.clearAndSet(mCurrentEventHistory.getItems());
|
||||
processingChain.getDecodeEventHistory().addListener(mLastHeardModel);
|
||||
mLastSeenManagementPanel.enableButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentEventHistory = null;
|
||||
mLastHeardModel.clearAndSet(Collections.emptyList());
|
||||
mLastSeenManagementPanel.disableButtons();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class LastSeenManagementPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EventFilterButton<IDecodeEvent> mFilterButton;
|
||||
private EventClearButton mEventClearButton;
|
||||
|
||||
public LastSeenManagementPanel()
|
||||
{
|
||||
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
|
||||
|
||||
initializeFilterButton();
|
||||
initializeClearButton();
|
||||
disableButtons();
|
||||
|
||||
add(mFilterButton);
|
||||
add(mEventClearButton);
|
||||
}
|
||||
|
||||
public void enableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(true);
|
||||
mEventClearButton.setEnabled(true);
|
||||
}
|
||||
|
||||
public void disableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(false);
|
||||
mEventClearButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private void initializeFilterButton()
|
||||
{
|
||||
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
|
||||
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
|
||||
if (filterProvider != null) {
|
||||
filterSet = filterProvider.getFilterSet();
|
||||
}
|
||||
mFilterButton = new EventFilterButton<>("Last Heard To Channel Filter Editor", filterSet);
|
||||
}
|
||||
|
||||
private void initializeClearButton() {
|
||||
mEventClearButton = new EventClearButton(
|
||||
((LastHeardModel) mTable.getModel()).getMaxMessageCount()
|
||||
);
|
||||
mEventClearButton.setEventClearHandler(new EventClearHandler() {
|
||||
@Override
|
||||
public void onHistoryLimitChanged(int newHistoryLimit) {
|
||||
((LastHeardModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearHistoryClicked() {
|
||||
((LastHeardModel) mTable.getModel()).clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom cell renderer for displaying identifiers from an identifier collection
|
||||
*/
|
||||
public class IdentifierCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
protected Role mRole;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cell renderer.
|
||||
*
|
||||
* @param role of the identifier
|
||||
*/
|
||||
public IdentifierCellRenderer(Role role)
|
||||
{
|
||||
mRole = role;
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if(value instanceof IdentifierCollection)
|
||||
{
|
||||
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
|
||||
label.setText(format(identifiers));
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText(null);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a list of identifiers as a comma separated list of values
|
||||
* @param identifiers to format
|
||||
* @return formatted list or null
|
||||
*/
|
||||
protected String format(List<Identifier> identifiers)
|
||||
{
|
||||
if(identifiers == null || identifiers.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
if(sb.length() > 0)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
if(identifier.getForm() == Form.TALKGROUP)
|
||||
{
|
||||
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(identifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell renderer for identifier aliases
|
||||
*/
|
||||
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private Role mRole;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cell renderer.
|
||||
*
|
||||
* @param role of the identifier
|
||||
*/
|
||||
public AliasedIdentifierCellRenderer(Role role)
|
||||
{
|
||||
mRole = role;
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
Color color = mTable.getForeground();
|
||||
ImageIcon icon = null;
|
||||
String text = null;
|
||||
|
||||
if(value instanceof IdentifierCollection)
|
||||
{
|
||||
IdentifierCollection identifierCollection = (IdentifierCollection)value;
|
||||
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
|
||||
|
||||
if(identifiers != null && !identifiers.isEmpty())
|
||||
{
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
if(aliasList != null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
if(sb.length() > 0)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
color = aliases.get(0).getDisplayColor();
|
||||
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
text = sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(text);
|
||||
label.setForeground(color);
|
||||
label.setIcon(icon);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
public class TimestampCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private SimpleDateFormat mTimestampFormatter;
|
||||
|
||||
public TimestampCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
updatePreferences();
|
||||
}
|
||||
|
||||
public void updatePreferences()
|
||||
{
|
||||
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
label.setText(mTimestampFormatter.format(new Date((long)value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText(null);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
public class DurationCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
|
||||
|
||||
public DurationCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
String formatted = null;
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
long duration = (long)value;
|
||||
|
||||
if(duration > 0)
|
||||
{
|
||||
formatted = mDecimalFormat.format((double)duration / 1e3d);
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(formatted);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frequency value cell renderer
|
||||
*/
|
||||
public class FrequencyCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
|
||||
|
||||
public FrequencyCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
String formatted = null;
|
||||
|
||||
if(value instanceof IChannelDescriptor)
|
||||
{
|
||||
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
|
||||
|
||||
long frequency = channelDescriptor.getDownlinkFrequency();
|
||||
|
||||
if(frequency > 0)
|
||||
{
|
||||
formatted = mFrequencyFormatter.format(frequency / 1e6d);
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(formatted);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel descriptor value cell renderer
|
||||
*/
|
||||
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
public ChannelDescriptorCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
|
||||
public class LastSeenFilterSet extends FilterSet<IDecodeEvent>
|
||||
{
|
||||
|
||||
public LastSeenFilterSet(AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
super("Last Seen From Radio");
|
||||
|
||||
addFilter(new LastSeenIconFilter("Icon", aliasModel, iconModel));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.module.HistoryModule;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEventListener;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
/**
|
||||
* Decode event history module. Maintains a history of decode events and constrains the total history size.
|
||||
*/
|
||||
public class LastSeenHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public LastSeenHistory(int historySize)
|
||||
{
|
||||
super(historySize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
|
||||
*/
|
||||
@Override
|
||||
public Listener<IDecodeEvent> getDecodeEventListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process preload data
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(DecodeEventHistoryPreloadData preloadData)
|
||||
{
|
||||
for(IDecodeEvent decodeEvent: preloadData.getData())
|
||||
{
|
||||
receive(decodeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a request for decode event history and posts the response back
|
||||
* to the processing chain event bus so that any of the modules can receive that history.
|
||||
*
|
||||
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
|
||||
* event history from a traffic channel conversion to the new rest channel.
|
||||
*
|
||||
* @param request for decode event history
|
||||
*/
|
||||
@Subscribe
|
||||
public void process(LastSeenHistoryRequest request)
|
||||
{
|
||||
getInterModuleEventBus().post(new LastSeenHistoryResponse(this));
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import io.github.dsheirer.module.ModuleEventBusMessage;
|
||||
|
||||
/**
|
||||
* Response to decode event history request from a processing chain for the specified channel.
|
||||
*/
|
||||
public class LastSeenHistoryResponse extends ModuleEventBusMessage
|
||||
{
|
||||
private LastSeenHistory mLastSeenHistory;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param history that has a decode event history
|
||||
*/
|
||||
public LastSeenHistoryResponse(LastSeenHistory history)
|
||||
{
|
||||
mLastSeenHistory = history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode event history module
|
||||
*/
|
||||
public LastSeenHistory getDecodeEventHistory()
|
||||
{
|
||||
return mLastSeenHistory;
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.icon.Icon;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LastSeenIconFilter extends Filter<IDecodeEvent>
|
||||
{
|
||||
private AliasModel mAliasModel;
|
||||
private Map<String, FilterElement<String>> mElements = new HashMap<>();
|
||||
|
||||
public LastSeenIconFilter(String name, AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
super(name);
|
||||
this.mAliasModel = aliasModel;
|
||||
|
||||
ObservableList<Icon> icons = iconModel.iconsProperty();
|
||||
for (Icon icon : icons) {
|
||||
mElements.put(icon.getName(), new FilterElement<>(icon.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
if (mEnabled && canProcess(iDecodeEvent))
|
||||
{
|
||||
return matchAliasToFilter(iDecodeEvent)
|
||||
.map(FilterElement::isEnabled)
|
||||
.orElse(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Optional<FilterElement<String>> matchAliasToFilter(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection();
|
||||
List<Identifier> identifiers = identifierCollection.getIdentifiers();
|
||||
if (identifiers != null && !identifiers.isEmpty())
|
||||
{
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
if (aliasList != null)
|
||||
{
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
if (identifier.getForm() == Form.RADIO)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
if (!aliases.isEmpty())
|
||||
{
|
||||
for (Alias alias : aliases) {
|
||||
if (alias.getIconName() == null)
|
||||
{
|
||||
// Return any to match aliases with no icon defined.
|
||||
return Optional.of(mElements.get(IconModel.DEFAULT_ICON));
|
||||
}
|
||||
else
|
||||
{
|
||||
return Optional.of(mElements.get(alias.getIconName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IDecodeEvent iDecodeEvent)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
{
|
||||
return new ArrayList<>(mElements.values());
|
||||
}
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class LastSeenModel extends AbstractTableModel implements Listener<IDecodeEvent>, EventFilterProvider<IDecodeEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int COLUMN_TIME = 0;
|
||||
public static final int COLUMN_DURATION = 1;
|
||||
public static final int COLUMN_EVENT = 2;
|
||||
public static final int COLUMN_FROM_ID = 3;
|
||||
public static final int COLUMN_FROM_ALIAS = 4;
|
||||
public static final int COLUMN_TO_ID = 5;
|
||||
public static final int COLUMN_TO_ALIAS = 6;
|
||||
public static final int COLUMN_CHANNEL = 7;
|
||||
public static final int COLUMN_FREQUENCY = 8;
|
||||
public static final int COLUMN_DETAILS = 9;
|
||||
|
||||
protected int mMaxMessages = 500;
|
||||
|
||||
protected List<IDecodeEvent> mEvents = new ArrayList<>();
|
||||
protected FilterSet<IDecodeEvent> mLastSeenFilterSet;
|
||||
|
||||
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
|
||||
|
||||
public LastSeenModel(AliasModel aliasModel, IconModel iconModel)
|
||||
{
|
||||
mLastSeenFilterSet = new LastSeenFilterSet(aliasModel, iconModel);
|
||||
MyEventBus.getGlobalEventBus().register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives preference update notifications via the event bus
|
||||
* @param preferenceType that was updated
|
||||
*/
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_TIME);
|
||||
}
|
||||
}
|
||||
else if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
for(int row = 0; row < mEvents.size(); row++)
|
||||
{
|
||||
fireTableCellUpdated(row, COLUMN_FROM_ID);
|
||||
fireTableCellUpdated(row, COLUMN_TO_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the complete list of events managed by this model.
|
||||
*/
|
||||
public List<IDecodeEvent> getEvents()
|
||||
{
|
||||
return new ArrayList<>(mEvents);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().unregister(this);
|
||||
Iterator<IDecodeEvent> it = mEvents.iterator();
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
mEvents.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all events from this model and loads the events argument
|
||||
*/
|
||||
public void clearAndSet(List<IDecodeEvent> events)
|
||||
{
|
||||
mEvents.clear();
|
||||
mEvents.addAll(events);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
dispose();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public int getMaxMessageCount()
|
||||
{
|
||||
return mMaxMessages;
|
||||
}
|
||||
|
||||
public void setMaxMessageCount(int count)
|
||||
{
|
||||
mMaxMessages = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds, updates or deletes the event from the model. Producers can send
|
||||
* the same call event multiple times to indicate that information in the
|
||||
* event is updated. Producers can also mark the event as invalid and the
|
||||
* event will be removed from the model.
|
||||
*/
|
||||
public void receive(final IDecodeEvent event)
|
||||
{
|
||||
if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!mEvents.contains(event))
|
||||
{
|
||||
mEvents.add(0, event);
|
||||
fireTableRowsInserted(0, 0);
|
||||
prune();
|
||||
}
|
||||
else
|
||||
{
|
||||
int row = mEvents.indexOf(event);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
private void prune()
|
||||
{
|
||||
while(mEvents.size() > mMaxMessages)
|
||||
{
|
||||
int index = mEvents.size() - 1;
|
||||
mEvents.remove(index);
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FilterSet<IDecodeEvent> getFilterSet() {
|
||||
return mLastSeenFilterSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterSet(FilterSet<IDecodeEvent> filterSet) {
|
||||
this.mLastSeenFilterSet = filterSet;
|
||||
}
|
||||
|
||||
@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)
|
||||
{
|
||||
IDecodeEvent event = mEvents.get(rowIndex);
|
||||
|
||||
if(event != null)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_TIME:
|
||||
return event.getTimeStart();
|
||||
case COLUMN_DURATION:
|
||||
return event.getDuration();
|
||||
case COLUMN_EVENT:
|
||||
return event.getEventDescription();
|
||||
case COLUMN_FROM_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_FROM_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ID:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_TO_ALIAS:
|
||||
return event.getIdentifierCollection();
|
||||
case COLUMN_CHANNEL:
|
||||
IChannelDescriptor channelDescriptor = event.getChannelDescriptor();
|
||||
|
||||
if(channelDescriptor != null)
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return channelDescriptor.toString() + " TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return channelDescriptor.toString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.hasTimeslot())
|
||||
{
|
||||
return "TS:" + event.getTimeslot();
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
case COLUMN_FREQUENCY:
|
||||
return event.getChannelDescriptor();
|
||||
case COLUMN_DETAILS:
|
||||
return event.getDetails();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
switch(columnIndex)
|
||||
{
|
||||
case COLUMN_DURATION:
|
||||
case COLUMN_TIME:
|
||||
return Long.class;
|
||||
case COLUMN_DETAILS:
|
||||
case COLUMN_EVENT:
|
||||
return String.class;
|
||||
case COLUMN_FREQUENCY:
|
||||
case COLUMN_FROM_ALIAS:
|
||||
case COLUMN_FROM_ID:
|
||||
case COLUMN_TO_ALIAS:
|
||||
case COLUMN_TO_ID:
|
||||
return IdentifierCollection.class;
|
||||
case COLUMN_CHANNEL:
|
||||
return String.class;
|
||||
}
|
||||
return super.getColumnClass(columnIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *
|
||||
* * This program is free software: you can redistribute it and/or modify
|
||||
* * it under the terms of the GNU General Public License as published by
|
||||
* * the Free Software Foundation, either version 3 of the License, or
|
||||
* * (at your option) any later version.
|
||||
* *
|
||||
* * This program is distributed in the hope that it will be useful,
|
||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* * GNU General Public License for more details.
|
||||
* *
|
||||
* * You should have received a copy of the GNU General Public License
|
||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
*
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.event.filter.lastseen;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import io.github.dsheirer.alias.Alias;
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.filter.AllPassFilter;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.identifier.Role;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventModel;
|
||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventClearHandler;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterButton;
|
||||
import io.github.dsheirer.module.decode.event.filter.EventFilterProvider;
|
||||
import io.github.dsheirer.preference.PreferenceType;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class LastSeenPanel extends JPanel implements Listener<ProcessingChain>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LastSeenPanel.class);
|
||||
private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel";
|
||||
|
||||
private JTable mTable;
|
||||
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
|
||||
private LastSeenModel mLastSeenModel;
|
||||
private DecodeEventHistory mCurrentEventHistory;
|
||||
private JScrollPane mEmptyScroller;
|
||||
private IconModel mIconModel;
|
||||
private AliasModel mAliasModel;
|
||||
private UserPreferences mUserPreferences;
|
||||
private TimestampCellRenderer mTimestampCellRenderer;
|
||||
private LastSeenManagementPanel mLastSeenManagementPanel;
|
||||
|
||||
/**
|
||||
* View for call event table
|
||||
* @param iconModel to display alias icons in table rows
|
||||
*/
|
||||
public LastSeenPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().register(this);
|
||||
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
mIconModel = iconModel;
|
||||
mAliasModel = aliasModel;
|
||||
mLastSeenModel = new LastSeenModel(aliasModel, iconModel);
|
||||
mUserPreferences = userPreferences;
|
||||
mTimestampCellRenderer = new TimestampCellRenderer();
|
||||
mTable = new JTable(mLastSeenModel);
|
||||
mTable.setAutoCreateRowSorter(true);
|
||||
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
||||
mLastSeenManagementPanel = new LastSeenManagementPanel();
|
||||
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
|
||||
updateCellRenderers();
|
||||
|
||||
add(mLastSeenManagementPanel, "span,growx");
|
||||
|
||||
mEmptyScroller = new JScrollPane(mTable);
|
||||
add(mEmptyScroller);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
MyEventBus.getGlobalEventBus().unregister(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives preference update notifications via the event bus
|
||||
* @param preferenceType that was updated
|
||||
*/
|
||||
@Subscribe
|
||||
public void preferenceUpdated(PreferenceType preferenceType)
|
||||
{
|
||||
if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
|
||||
{
|
||||
EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCellRenderers()
|
||||
{
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME)
|
||||
.setCellRenderer(mTimestampCellRenderer);
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION)
|
||||
.setCellRenderer(new DurationCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID)
|
||||
.setCellRenderer(new IdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS)
|
||||
.setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO));
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL)
|
||||
.setCellRenderer(new ChannelDescriptorCellRenderer());
|
||||
mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY)
|
||||
.setCellRenderer(new FrequencyCellRenderer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(final ProcessingChain processingChain)
|
||||
{
|
||||
if(mCurrentEventHistory != null)
|
||||
{
|
||||
mCurrentEventHistory.removeListener(mLastSeenModel);
|
||||
}
|
||||
|
||||
EventQueue.invokeLater(() -> {
|
||||
if(processingChain != null)
|
||||
{
|
||||
mCurrentEventHistory = processingChain.getDecodeEventHistory();
|
||||
mLastSeenModel.clearAndSet(mCurrentEventHistory.getItems());
|
||||
processingChain.getDecodeEventHistory().addListener(mLastSeenModel);
|
||||
mLastSeenManagementPanel.enableButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentEventHistory = null;
|
||||
mLastSeenModel.clearAndSet(Collections.emptyList());
|
||||
mLastSeenManagementPanel.disableButtons();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class LastSeenManagementPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private EventFilterButton<IDecodeEvent> mFilterButton;
|
||||
private EventClearButton mEventClearButton;
|
||||
|
||||
public LastSeenManagementPanel()
|
||||
{
|
||||
setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
|
||||
|
||||
initializeFilterButton();
|
||||
initializeClearButton();
|
||||
disableButtons();
|
||||
|
||||
add(mFilterButton);
|
||||
add(mEventClearButton);
|
||||
}
|
||||
|
||||
public void enableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(true);
|
||||
mEventClearButton.setEnabled(true);
|
||||
}
|
||||
|
||||
public void disableButtons()
|
||||
{
|
||||
mFilterButton.setEnabled(false);
|
||||
mEventClearButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private void initializeFilterButton()
|
||||
{
|
||||
EventFilterProvider<IDecodeEvent> filterProvider = (EventFilterProvider<IDecodeEvent>) mTable.getModel();
|
||||
FilterSet<IDecodeEvent> filterSet = new FilterSet<>(new AllPassFilter<>());
|
||||
if (filterProvider != null) {
|
||||
filterSet = filterProvider.getFilterSet();
|
||||
}
|
||||
mFilterButton = new EventFilterButton<>("Last Seen From Radio Filter Editor", filterSet);
|
||||
}
|
||||
|
||||
private void initializeClearButton() {
|
||||
mEventClearButton = new EventClearButton(
|
||||
((LastSeenModel) mTable.getModel()).getMaxMessageCount()
|
||||
);
|
||||
mEventClearButton.setEventClearHandler(new EventClearHandler() {
|
||||
@Override
|
||||
public void onHistoryLimitChanged(int newHistoryLimit) {
|
||||
((LastSeenModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClearHistoryClicked() {
|
||||
((LastSeenModel) mTable.getModel()).clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom cell renderer for displaying identifiers from an identifier collection
|
||||
*/
|
||||
public class IdentifierCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
protected Role mRole;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cell renderer.
|
||||
*
|
||||
* @param role of the identifier
|
||||
*/
|
||||
public IdentifierCellRenderer(Role role)
|
||||
{
|
||||
mRole = role;
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if(value instanceof IdentifierCollection)
|
||||
{
|
||||
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
|
||||
label.setText(format(identifiers));
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText(null);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a list of identifiers as a comma separated list of values
|
||||
* @param identifiers to format
|
||||
* @return formatted list or null
|
||||
*/
|
||||
protected String format(List<Identifier> identifiers)
|
||||
{
|
||||
if(identifiers == null || identifiers.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
if(sb.length() > 0)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
|
||||
if(identifier.getForm() == Form.TALKGROUP)
|
||||
{
|
||||
sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(identifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell renderer for identifier aliases
|
||||
*/
|
||||
public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private Role mRole;
|
||||
|
||||
/**
|
||||
* Constructs an instance of the cell renderer.
|
||||
*
|
||||
* @param role of the identifier
|
||||
*/
|
||||
public AliasedIdentifierCellRenderer(Role role)
|
||||
{
|
||||
mRole = role;
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
Color color = mTable.getForeground();
|
||||
ImageIcon icon = null;
|
||||
String text = null;
|
||||
|
||||
if(value instanceof IdentifierCollection)
|
||||
{
|
||||
IdentifierCollection identifierCollection = (IdentifierCollection)value;
|
||||
List<Identifier> identifiers = identifierCollection.getIdentifiers(mRole);
|
||||
|
||||
if(identifiers != null && !identifiers.isEmpty())
|
||||
{
|
||||
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
|
||||
|
||||
if(aliasList != null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for(Identifier identifier: identifiers)
|
||||
{
|
||||
List<Alias> aliases = aliasList.getAliases(identifier);
|
||||
|
||||
if(!aliases.isEmpty())
|
||||
{
|
||||
if(sb.length() > 0)
|
||||
{
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(Joiner.on(", ").skipNulls().join(aliases));
|
||||
color = aliases.get(0).getDisplayColor();
|
||||
icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
text = sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(text);
|
||||
label.setForeground(color);
|
||||
label.setIcon(icon);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
public class TimestampCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private SimpleDateFormat mTimestampFormatter;
|
||||
|
||||
public TimestampCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
updatePreferences();
|
||||
}
|
||||
|
||||
public void updatePreferences()
|
||||
{
|
||||
mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
label.setText(mTimestampFormatter.format(new Date((long)value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText(null);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
public class DurationCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
|
||||
|
||||
public DurationCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
String formatted = null;
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
long duration = (long)value;
|
||||
|
||||
if(duration > 0)
|
||||
{
|
||||
formatted = mDecimalFormat.format((double)duration / 1e3d);
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(formatted);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frequency value cell renderer
|
||||
*/
|
||||
public class FrequencyCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000");
|
||||
|
||||
public FrequencyCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
String formatted = null;
|
||||
|
||||
if(value instanceof IChannelDescriptor)
|
||||
{
|
||||
IChannelDescriptor channelDescriptor = (IChannelDescriptor)value;
|
||||
|
||||
long frequency = channelDescriptor.getDownlinkFrequency();
|
||||
|
||||
if(frequency > 0)
|
||||
{
|
||||
formatted = mFrequencyFormatter.format(frequency / 1e6d);
|
||||
}
|
||||
}
|
||||
|
||||
label.setText(formatted);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel descriptor value cell renderer
|
||||
*/
|
||||
public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
public ChannelDescriptorCellRenderer()
|
||||
{
|
||||
setHorizontalAlignment(JLabel.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,19 @@
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.fleetsync2;
|
||||
|
||||
@@ -125,10 +120,8 @@ public class Fleetsync2DecoderState extends DecoderState
|
||||
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA));
|
||||
break;
|
||||
case GPS:
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp())
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, fleetsync.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventType(DecodeEventType.GPS)
|
||||
.eventDescription(DecodeEventType.GPS.toString())
|
||||
.details(fleetsync.toString())
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.protocol(Protocol.FLEETSYNC)
|
||||
@@ -143,11 +136,9 @@ public class Fleetsync2DecoderState extends DecoderState
|
||||
}
|
||||
|
||||
private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) {
|
||||
return DecodeEvent.builder(fleetsync.getTimestamp())
|
||||
return DecodeEvent.builder(eventType, fleetsync.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventType(eventType)
|
||||
.eventDescription(fleetsync.getMessageType().toString())
|
||||
.details(fleetsync.toString())
|
||||
.details(fleetsync.getMessageType() + " " + fleetsync)
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.protocol(Protocol.FLEETSYNC)
|
||||
.build();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.fleetsync2;
|
||||
@@ -24,52 +23,63 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class FleetsyncMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for Fleetsync messages.
|
||||
*/
|
||||
public class FleetsyncMessageFilter extends Filter<IMessage,FleetsyncMessageType>
|
||||
{
|
||||
private Map<FleetsyncMessageType,FilterElement<FleetsyncMessageType>> mElements = new EnumMap<>(FleetsyncMessageType.class);
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public FleetsyncMessageFilter()
|
||||
{
|
||||
super("Fleetsync Message Filter");
|
||||
super("Fleetsync Messages");
|
||||
|
||||
for(FleetsyncMessageType type : FleetsyncMessageType.values())
|
||||
{
|
||||
if(type != FleetsyncMessageType.UNKNOWN)
|
||||
{
|
||||
mElements.put(type, new FilterElement<FleetsyncMessageType>(type));
|
||||
}
|
||||
add(new FilterElement<>(type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
{
|
||||
Fleetsync2Message fleet = (Fleetsync2Message)message;
|
||||
|
||||
if(mElements.containsKey(fleet.getMessageType()))
|
||||
{
|
||||
return mElements.get(fleet.getMessageType()).isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits processing to only fleetsync messages.
|
||||
* @param message to test
|
||||
* @return true if message type is correct.
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof Fleetsync2Message;
|
||||
return message instanceof Fleetsync2Message && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor for fleetsync messages
|
||||
* @return key extractor
|
||||
*/
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, FleetsyncMessageType> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor for Fleetsync 2 Messages.
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage, FleetsyncMessageType>
|
||||
{
|
||||
@Override
|
||||
public FleetsyncMessageType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof Fleetsync2Message f2m)
|
||||
{
|
||||
return f2m.getMessageType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.lj1200;
|
||||
|
||||
@@ -31,14 +28,14 @@ import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LJ1200DecoderState extends DecoderState
|
||||
{
|
||||
@@ -73,12 +70,11 @@ public class LJ1200DecoderState extends DecoderState
|
||||
ic.remove(IdentifierClass.USER);
|
||||
ic.update(lj.getIdentifiers());
|
||||
|
||||
DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis())
|
||||
DecodeEvent event = DecodeEvent.builder(DecodeEventType.DATA_PACKET, System.currentTimeMillis())
|
||||
.protocol(Protocol.LOJACK)
|
||||
.identifiers(ic)
|
||||
.eventType(DecodeEventType.DATA_PACKET)
|
||||
.channel(getCurrentChannel())
|
||||
.details("LOJACK")
|
||||
.eventDescription(lj.toString())
|
||||
.details("LOJACK " + lj)
|
||||
.build();
|
||||
|
||||
broadcast(event);
|
||||
@@ -93,12 +89,11 @@ public class LJ1200DecoderState extends DecoderState
|
||||
ic.remove(IdentifierClass.USER);
|
||||
ic.update(transponder.getIdentifiers());
|
||||
|
||||
DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis())
|
||||
DecodeEvent transponderEvent = DecodeEvent.builder(DecodeEventType.GPS, System.currentTimeMillis())
|
||||
.protocol(Protocol.LOJACK)
|
||||
.identifiers(ic)
|
||||
.eventType(DecodeEventType.GPS)
|
||||
.channel(getCurrentChannel())
|
||||
.details("LOJACK TRANSPONDER")
|
||||
.eventDescription(transponder.toString())
|
||||
.details("LOJACK TRANSPONDER " + transponder)
|
||||
.build();
|
||||
|
||||
broadcast(transponderEvent);
|
||||
@@ -117,7 +112,7 @@ public class LJ1200DecoderState extends DecoderState
|
||||
{
|
||||
sb.append("Transponder Addresses:\n");
|
||||
|
||||
List<String> addresses = new ArrayList<String>(mAddresses);
|
||||
List<String> addresses = new ArrayList<>(mAddresses);
|
||||
|
||||
Collections.sort(addresses);
|
||||
|
||||
|
||||
@@ -1,52 +1,66 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014,2015 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.lj1200;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LJ1200MessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for LJ1200 messages
|
||||
*/
|
||||
public class LJ1200MessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String LJ1200_KEY = "LJ-1200";
|
||||
private final LJ1200KeyExtractor mKeyExtractor = new LJ1200KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public LJ1200MessageFilter()
|
||||
{
|
||||
super("LJ-1200 Message Filter");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
super("LJ-1200 Messages");
|
||||
add(new FilterElement<>(LJ1200_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage;
|
||||
return (message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
public class LJ1200KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
return LJ1200_KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,23 +14,27 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* LTRNet decode event
|
||||
*/
|
||||
public class LTRNetDecodeEvent extends DecodeEvent
|
||||
{
|
||||
/**
|
||||
* Constructs an LTR NET decode event
|
||||
* @param start
|
||||
*/
|
||||
public LTRNetDecodeEvent(long start)
|
||||
public LTRNetDecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.LTR_NET);
|
||||
}
|
||||
|
||||
@@ -40,9 +43,9 @@ public class LTRNetDecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.LTR_NET);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
@@ -61,9 +58,6 @@ import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle;
|
||||
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh;
|
||||
import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -72,6 +66,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LTRNetDecoderState extends DecoderState
|
||||
{
|
||||
@@ -167,9 +163,24 @@ public class LTRNetDecoderState extends DecoderState
|
||||
|
||||
if(mCurrentCallTalkgroup == null || !mCurrentCallTalkgroup.equals(talkgroup))
|
||||
{
|
||||
DecodeEventType decodeEventType = null;
|
||||
|
||||
if(talkgroup.getTalkgroup() == 253)
|
||||
{
|
||||
decodeEventType = DecodeEventType.REGISTER;
|
||||
}
|
||||
else if(talkgroup.getTalkgroup() == 254)
|
||||
{
|
||||
decodeEventType = DecodeEventType.STATION_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
decodeEventType = DecodeEventType.CALL;
|
||||
}
|
||||
|
||||
getIdentifierCollection().remove(IdentifierClass.USER);
|
||||
getIdentifierCollection().update(talkgroup);
|
||||
mCurrentCallEvent = LTRNetDecodeEvent.builder(timestamp)
|
||||
mCurrentCallEvent = LTRNetDecodeEvent.builder(decodeEventType, timestamp)
|
||||
.channel(getCurrentChannel())
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.build();
|
||||
@@ -181,20 +192,14 @@ public class LTRNetDecoderState extends DecoderState
|
||||
|
||||
if(talkgroup.getTalkgroup() == 253)
|
||||
{
|
||||
mCurrentCallEvent.setEventType(DecodeEventType.REGISTER);
|
||||
mCurrentCallEvent.setEventDescription("Register");
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
|
||||
}
|
||||
else if(talkgroup.getTalkgroup() == 254)
|
||||
{
|
||||
mCurrentCallEvent.setEventType(DecodeEventType.STATION_ID);
|
||||
mCurrentCallEvent.setEventDescription("FCC CWID");
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentCallEvent.setEventType(DecodeEventType.CALL);
|
||||
mCurrentCallEvent.setEventDescription("Call");
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
|
||||
}
|
||||
|
||||
@@ -292,77 +297,59 @@ public class LTRNetDecoderState extends DecoderState
|
||||
switch(((LtrNetMessage)message).getLtrNetMessageType())
|
||||
{
|
||||
case ISW_CALL_END:
|
||||
if(message instanceof IswCallEnd)
|
||||
if(message instanceof IswCallEnd callEnd)
|
||||
{
|
||||
IswCallEnd callEnd = (IswCallEnd)message;
|
||||
mTalkgroups.add(callEnd.getTalkgroup());
|
||||
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
|
||||
}
|
||||
break;
|
||||
case ISW_CALL_START:
|
||||
if(message instanceof IswCallStart)
|
||||
if(message instanceof IswCallStart callStart)
|
||||
{
|
||||
IswCallStart callStart = (IswCallStart)message;
|
||||
mTalkgroups.add(callStart.getTalkgroup());
|
||||
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
|
||||
MessageDirection.ISW);
|
||||
}
|
||||
break;
|
||||
case ISW_REGISTRATION_REQUEST_ESN_HIGH:
|
||||
if(message instanceof RegistrationRequestEsnHigh)
|
||||
if(message instanceof RegistrationRequestEsnHigh registrationRequestEsn)
|
||||
{
|
||||
RegistrationRequestEsnHigh registrationRequestEsn = (RegistrationRequestEsnHigh)message;
|
||||
|
||||
if(registrationRequestEsn.isCompleteEsn())
|
||||
{
|
||||
getIdentifierCollection().update(registrationRequestEsn.getESN());
|
||||
mESNIdentifiers.add(registrationRequestEsn.getESN());
|
||||
}
|
||||
|
||||
broadcast(getDecodeEvent(
|
||||
message, DecodeEventType.REQUEST,
|
||||
"Registration Request", registrationRequestEsn.toString()));
|
||||
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
|
||||
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
}
|
||||
break;
|
||||
case ISW_REGISTRATION_REQUEST_ESN_LOW:
|
||||
if(message instanceof RegistrationRequestEsnLow)
|
||||
if(message instanceof RegistrationRequestEsnLow registrationRequestEsn)
|
||||
{
|
||||
RegistrationRequestEsnLow registrationRequestEsn = (RegistrationRequestEsnLow)message;
|
||||
|
||||
if(registrationRequestEsn.isCompleteEsn())
|
||||
{
|
||||
getIdentifierCollection().update(registrationRequestEsn.getESN());
|
||||
mESNIdentifiers.add(registrationRequestEsn.getESN());
|
||||
}
|
||||
|
||||
broadcast(getDecodeEvent(
|
||||
message, DecodeEventType.REQUEST,
|
||||
"Registration Request", registrationRequestEsn.toString()));
|
||||
|
||||
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn));
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
}
|
||||
break;
|
||||
case ISW_REQUEST_ACCESS:
|
||||
if(message instanceof RequestAccess)
|
||||
if(message instanceof RequestAccess requestAccess)
|
||||
{
|
||||
RequestAccess requestAccess = (RequestAccess)message;
|
||||
|
||||
getIdentifierCollection().update(requestAccess.getTalkgroup());
|
||||
mTalkgroups.add(requestAccess.getTalkgroup());
|
||||
|
||||
broadcast(getDecodeEvent(
|
||||
message, DecodeEventType.REQUEST,
|
||||
"Access Request", requestAccess.toString()));
|
||||
|
||||
broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "ACCESS: " + requestAccess));
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA));
|
||||
}
|
||||
break;
|
||||
case ISW_UNIQUE_ID:
|
||||
if(message instanceof IswUniqueId)
|
||||
if(message instanceof IswUniqueId iswUniqueId)
|
||||
{
|
||||
IswUniqueId iswUniqueId = (IswUniqueId)message;
|
||||
getIdentifierCollection().update(iswUniqueId.getUniqueID());
|
||||
mLtrNetRadioIdentifiers.add(iswUniqueId.getUniqueID());
|
||||
updateCurrentCallIdentifiers();
|
||||
@@ -373,99 +360,90 @@ public class LTRNetDecoderState extends DecoderState
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_CALL_END:
|
||||
if(message instanceof OswCallEnd)
|
||||
if(message instanceof OswCallEnd callEnd)
|
||||
{
|
||||
OswCallEnd callEnd = (OswCallEnd)message;
|
||||
mTalkgroups.add(callEnd.getTalkgroup());
|
||||
processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp());
|
||||
}
|
||||
break;
|
||||
case OSW_CALL_START:
|
||||
if(message instanceof OswCallStart)
|
||||
if(message instanceof OswCallStart callStart)
|
||||
{
|
||||
OswCallStart callStart = (OswCallStart)message;
|
||||
mTalkgroups.add(callStart.getTalkgroup());
|
||||
processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(),
|
||||
MessageDirection.OSW);
|
||||
}
|
||||
break;
|
||||
case OSW_CHANNEL_MAP_HIGH:
|
||||
if(message instanceof ChannelMapHigh)
|
||||
if(message instanceof ChannelMapHigh channelMapHigh)
|
||||
{
|
||||
mChannelMapHigh = (ChannelMapHigh)message;
|
||||
mChannelMapHigh = channelMapHigh;
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_CHANNEL_MAP_LOW:
|
||||
if(message instanceof ChannelMapLow)
|
||||
if(message instanceof ChannelMapLow channelMapLow)
|
||||
{
|
||||
mChannelMapLow = (ChannelMapLow)message;
|
||||
mChannelMapLow = channelMapLow;
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_SYSTEM_IDLE:
|
||||
if(message instanceof SystemIdle)
|
||||
if(message instanceof SystemIdle systemIdle)
|
||||
{
|
||||
setCurrentChannelNumber(((SystemIdle)message).getChannel());
|
||||
setCurrentChannelNumber(systemIdle.getChannel());
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_NEIGHBOR_ID:
|
||||
if(message instanceof NeighborId)
|
||||
if(message instanceof NeighborId neighborId)
|
||||
{
|
||||
NeighborId neighborId = (NeighborId)message;
|
||||
mNeighborMap.put(neighborId.getNeighborRank(), neighborId);
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_RECEIVE_FREQUENCY_HIGH:
|
||||
if(message instanceof ReceiveFrequencyHigh)
|
||||
if(message instanceof ReceiveFrequencyHigh receiveFrequency)
|
||||
{
|
||||
ReceiveFrequencyHigh receiveFrequency = (ReceiveFrequencyHigh)message;
|
||||
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_RECEIVE_FREQUENCY_LOW:
|
||||
if(message instanceof ReceiveFrequencyLow)
|
||||
if(message instanceof ReceiveFrequencyLow receiveFrequency)
|
||||
{
|
||||
ReceiveFrequencyLow receiveFrequency = (ReceiveFrequencyLow)message;
|
||||
updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency());
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_REGISTRATION_ACCEPT:
|
||||
if(message instanceof RegistrationAccept)
|
||||
if(message instanceof RegistrationAccept registrationAccept)
|
||||
{
|
||||
RegistrationAccept registrationAccept = (RegistrationAccept)message;
|
||||
mLtrNetRadioIdentifiers.add(registrationAccept.getUniqueID());
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
ic.remove(IdentifierClass.USER);
|
||||
ic.update(message.getIdentifiers());
|
||||
|
||||
broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE,
|
||||
"Registration Accept", registrationAccept.toString()));
|
||||
broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE, "REGISTRATION ACCEPT: " + registrationAccept));
|
||||
}
|
||||
break;
|
||||
case OSW_SITE_ID:
|
||||
if(message instanceof SiteId)
|
||||
if(message instanceof SiteId siteId)
|
||||
{
|
||||
mCurrentSite = (SiteId)message;
|
||||
mCurrentSite = siteId;
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_TRANSMIT_FREQUENCY_HIGH:
|
||||
if(message instanceof TransmitFrequencyHigh)
|
||||
if(message instanceof TransmitFrequencyHigh transmitFrequency)
|
||||
{
|
||||
TransmitFrequencyHigh transmitFrequency = (TransmitFrequencyHigh)message;
|
||||
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
break;
|
||||
case OSW_TRANSMIT_FREQUENCY_LOW:
|
||||
if(message instanceof TransmitFrequencyLow)
|
||||
if(message instanceof TransmitFrequencyLow transmitFrequency)
|
||||
{
|
||||
TransmitFrequencyLow transmitFrequency = (TransmitFrequencyLow)message;
|
||||
updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency());
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE));
|
||||
@@ -477,24 +455,22 @@ public class LTRNetDecoderState extends DecoderState
|
||||
}
|
||||
}
|
||||
|
||||
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String description, String details) {
|
||||
return LTRNetDecodeEvent.builder(message.getTimestamp())
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(description)
|
||||
private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String details)
|
||||
{
|
||||
return LTRNetDecodeEvent.builder(decodeEventType, message.getTimestamp())
|
||||
.details(details)
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.channel(getCurrentChannel())
|
||||
.build();
|
||||
}
|
||||
|
||||
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType) {
|
||||
private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType)
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
|
||||
ic.remove(IdentifierClass.USER);
|
||||
ic.update(talkgroup);
|
||||
|
||||
return LTRNetDecodeEvent.builder(timestamp)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
return LTRNetDecodeEvent.builder(decodeEventType, timestamp)
|
||||
.channel(mChannelMap.get(channel))
|
||||
.identifiers(ic)
|
||||
.build();
|
||||
|
||||
@@ -1,52 +1,85 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
|
||||
import io.github.dsheirer.module.decode.ltrstandard.LtrStandardMessageType;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LTRNetMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for LTR-Net messages
|
||||
*/
|
||||
public class LTRNetMessageFilter extends Filter<IMessage,LtrNetMessageType>
|
||||
{
|
||||
private Map<LtrNetMessageType, FilterElement<LtrNetMessageType>> mElements = new EnumMap<>(LtrNetMessageType.class);
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public LTRNetMessageFilter()
|
||||
{
|
||||
super("LTR-Net Message Filter");
|
||||
super("LTR-Net Messages");
|
||||
|
||||
for(LtrNetMessageType type: LtrNetMessageType.values())
|
||||
{
|
||||
mElements.put(type, new FilterElement<>(type));
|
||||
add(new FilterElement<>(type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
{
|
||||
LtrNetMessage ltr = (LtrNetMessage) message;
|
||||
|
||||
if(mElements.containsKey(ltr.getLtrNetMessageType()))
|
||||
{
|
||||
return mElements.get(ltr.getLtrNetMessageType()).isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for instance of LTR-Net message
|
||||
* @param message to test
|
||||
* @return true if correct type.
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof LtrNetMessage;
|
||||
return message instanceof LtrNetMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor for LTR-Net messages
|
||||
* @return extractor
|
||||
*/
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, LtrNetMessageType> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,LtrNetMessageType>
|
||||
{
|
||||
@Override
|
||||
public LtrNetMessageType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof LtrNetMessage ltr)
|
||||
{
|
||||
return ltr.getLtrNetMessageType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,23 +14,27 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.ltrstandard;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* LTR decode event
|
||||
*/
|
||||
public class LTRStandardDecodeEvent extends DecodeEvent
|
||||
{
|
||||
/**
|
||||
* Constructs an LTR Standard decode event
|
||||
* @param start
|
||||
*/
|
||||
public LTRStandardDecodeEvent(long start)
|
||||
public LTRStandardDecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.LTR);
|
||||
}
|
||||
|
||||
@@ -40,9 +43,9 @@ public class LTRStandardDecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.LTR);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.ltrstandard;
|
||||
|
||||
@@ -42,9 +39,6 @@ import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd;
|
||||
import io.github.dsheirer.module.decode.ltrstandard.message.Idle;
|
||||
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -52,6 +46,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LTRStandardDecoderState extends DecoderState
|
||||
{
|
||||
@@ -82,10 +78,8 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
switch(((LTRMessage)message).getMessageType())
|
||||
{
|
||||
case CALL:
|
||||
if(message instanceof Call)
|
||||
if(message instanceof Call start)
|
||||
{
|
||||
Call start = (Call)message;
|
||||
|
||||
int channel = start.getChannel();
|
||||
setChannelNumber(channel);
|
||||
mLCNTracker.processFreeChannel(start.getFree());
|
||||
@@ -98,11 +92,9 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
mCurrentTalkgroup = start.getTalkgroup();
|
||||
getIdentifierCollection().remove(IdentifierClass.USER);
|
||||
getIdentifierCollection().update(start.getTalkgroup());
|
||||
mCurrentCallEvent = LTRStandardDecodeEvent.builder(start.getTimestamp())
|
||||
mCurrentCallEvent = LTRStandardDecodeEvent.builder(DecodeEventType.CALL, start.getTimestamp())
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.channel(getCurrentChannel())
|
||||
.eventType(DecodeEventType.CALL)
|
||||
.eventDescription("Call")
|
||||
.build();
|
||||
}
|
||||
else
|
||||
@@ -115,10 +107,8 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
}
|
||||
break;
|
||||
case CALL_END:
|
||||
if(message instanceof CallEnd)
|
||||
if(message instanceof CallEnd end)
|
||||
{
|
||||
CallEnd end = (CallEnd)message;
|
||||
|
||||
mCurrentTalkgroup = null;
|
||||
|
||||
//Home channel is 31 for call end -- use the free channel as the call end channel
|
||||
@@ -136,10 +126,10 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
}
|
||||
break;
|
||||
case IDLE:
|
||||
if(message instanceof Idle)
|
||||
if(message instanceof Idle idle)
|
||||
{
|
||||
mCurrentTalkgroup = null;
|
||||
mLCNTracker.processCallChannel(((Idle)message).getChannel());
|
||||
mLCNTracker.processCallChannel(idle.getChannel());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.ltrstandard;
|
||||
@@ -24,47 +23,53 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class LTRStandardMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for LTR standard messages.
|
||||
*/
|
||||
public class LTRStandardMessageFilter extends Filter<IMessage,LtrStandardMessageType>
|
||||
{
|
||||
private Map<LtrStandardMessageType,FilterElement<LtrStandardMessageType>> mElements = new EnumMap<>(LtrStandardMessageType.class);
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public LTRStandardMessageFilter()
|
||||
{
|
||||
super("LTR Message Filter");
|
||||
super("LTR Messages");
|
||||
|
||||
mElements.put(LtrStandardMessageType.CALL, new FilterElement<>(LtrStandardMessageType.CALL));
|
||||
mElements.put(LtrStandardMessageType.CALL_END, new FilterElement<>(LtrStandardMessageType.CALL_END));
|
||||
mElements.put(LtrStandardMessageType.IDLE, new FilterElement<>(LtrStandardMessageType.IDLE));
|
||||
mElements.put(LtrStandardMessageType.UNKNOWN, new FilterElement<>(LtrStandardMessageType.UNKNOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
for(LtrStandardMessageType type: LtrStandardMessageType.values())
|
||||
{
|
||||
LTRMessage ltr = (LTRMessage)message;
|
||||
|
||||
if(mElements.containsKey(ltr.getMessageType()))
|
||||
{
|
||||
return mElements.get(ltr.getMessageType()).isEnabled();
|
||||
}
|
||||
add(new FilterElement<>(type));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof LTRMessage;
|
||||
return message instanceof LTRMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, LtrStandardMessageType> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,LtrStandardMessageType>
|
||||
{
|
||||
@Override
|
||||
public LtrStandardMessageType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof LTRMessage ltr)
|
||||
{
|
||||
return ltr.getMessageType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,23 +14,28 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.mdc1200;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* MDC-1200 decode event
|
||||
*/
|
||||
public class MDCDecodeEvent extends DecodeEvent
|
||||
{
|
||||
/**
|
||||
* Constucts a P25 decode event
|
||||
* @param start
|
||||
* Constucts an MDC1200 decode event
|
||||
* @param decodeEventType for the event
|
||||
* @param start for the event
|
||||
*/
|
||||
public MDCDecodeEvent(long start)
|
||||
public MDCDecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.MDC1200);
|
||||
}
|
||||
|
||||
@@ -40,9 +44,9 @@ public class MDCDecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.MDC1200);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.mdc1200;
|
||||
|
||||
@@ -31,7 +28,6 @@ import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@@ -114,9 +110,7 @@ public class MDCDecoderState extends DecoderState
|
||||
ic.remove(IdentifierClass.USER);
|
||||
ic.update(message.getIdentifiers());
|
||||
|
||||
broadcast(MDCDecodeEvent.builder(mdc.getTimestamp())
|
||||
.eventType(getDecodeEventType(type))
|
||||
.eventDescription(type.getLabel())
|
||||
broadcast(MDCDecodeEvent.builder(getDecodeEventType(type), mdc.getTimestamp())
|
||||
.details(mdc.toString())
|
||||
.identifiers(ic)
|
||||
.build());
|
||||
|
||||
@@ -1,55 +1,75 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.mdc1200;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.module.decode.mpt1327.MPT1327Message;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MDCMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Message filter for MDC-1200 messages
|
||||
*/
|
||||
public class MDCMessageFilter extends Filter<IMessage,MDCMessageType>
|
||||
{
|
||||
private Map<MDCMessageType, FilterElement<MDCMessageType>> mElements = new EnumMap<>(MDCMessageType.class);
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public MDCMessageFilter()
|
||||
{
|
||||
super("MDC-1200 Message Filter");
|
||||
super("MDC-1200 Messages");
|
||||
|
||||
for(MDCMessageType type : MDCMessageType.values())
|
||||
{
|
||||
if(type != MDCMessageType.UNKNOWN)
|
||||
{
|
||||
mElements.put(type, new FilterElement<MDCMessageType>(type));
|
||||
}
|
||||
add(new FilterElement<>(type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage 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(IMessage message)
|
||||
{
|
||||
return message instanceof MDCMessage;
|
||||
return message instanceof MDCMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, MDCMessageType> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,MDCMessageType>
|
||||
{
|
||||
@Override
|
||||
public MDCMessageType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof MDCMessage mdc)
|
||||
{
|
||||
return mdc.getMessageType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,21 +14,23 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.mpt1327;
|
||||
|
||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.p25.P25DecodeEvent;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
/**
|
||||
* MPT1327 Channel Grant Event
|
||||
*/
|
||||
public class MPT1327ChannelGrantEvent extends MPT1327DecodeEvent
|
||||
{
|
||||
public MPT1327ChannelGrantEvent(long timestamp)
|
||||
public MPT1327ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
|
||||
{
|
||||
super(timestamp);
|
||||
super(decodeEventType, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,9 +38,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(long timeStart)
|
||||
public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
return new MPT1327ChannelGrantDecodeEventBuilder(timeStart);
|
||||
return new MPT1327ChannelGrantDecodeEventBuilder(decodeEventType, timeStart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +50,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
{
|
||||
protected long mTimeStart;
|
||||
protected long mDuration;
|
||||
protected String mEventDescription;
|
||||
protected DecodeEventType mDecodeEventType;
|
||||
protected IdentifierCollection mIdentifierCollection;
|
||||
protected IChannelDescriptor mChannelDescriptor;
|
||||
@@ -60,8 +60,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
*
|
||||
* @param timeStart
|
||||
*/
|
||||
public MPT1327ChannelGrantDecodeEventBuilder(long timeStart)
|
||||
public MPT1327ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = timeStart;
|
||||
}
|
||||
|
||||
@@ -95,25 +96,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Decode Event type for this event.
|
||||
* @param eventType
|
||||
*/
|
||||
public MPT1327ChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) {
|
||||
mDecodeEventType = eventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
* @param description of the event
|
||||
*/
|
||||
public MPT1327ChannelGrantDecodeEventBuilder eventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier collection.
|
||||
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
|
||||
@@ -140,13 +122,11 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent
|
||||
*/
|
||||
public MPT1327ChannelGrantEvent build()
|
||||
{
|
||||
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mTimeStart);
|
||||
MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mDecodeEventType, mTimeStart);
|
||||
decodeEvent.setProtocol(Protocol.MPT1327);
|
||||
decodeEvent.setChannelDescriptor(mChannelDescriptor);
|
||||
decodeEvent.setDetails(mDetails);
|
||||
decodeEvent.setDuration(mDuration);
|
||||
decodeEvent.setEventType(mDecodeEventType);
|
||||
decodeEvent.setEventDescription(mEventDescription);
|
||||
decodeEvent.setIdentifierCollection(mIdentifierCollection);
|
||||
return decodeEvent;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,23 +14,27 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.mpt1327;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
/**
|
||||
* MPT1327 Decode Event
|
||||
*/
|
||||
public class MPT1327DecodeEvent extends DecodeEvent
|
||||
{
|
||||
/**
|
||||
* Constructs a MPT1327 decode event
|
||||
* @param start
|
||||
*/
|
||||
public MPT1327DecodeEvent(long start)
|
||||
public MPT1327DecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.MPT1327);
|
||||
}
|
||||
|
||||
@@ -40,9 +43,9 @@ public class MPT1327DecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.MPT1327);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -33,14 +33,13 @@ import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
|
||||
import io.github.dsheirer.util.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class MPT1327DecoderState extends DecoderState
|
||||
{
|
||||
@@ -198,9 +197,7 @@ public class MPT1327DecoderState extends DecoderState
|
||||
private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details)
|
||||
{
|
||||
MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
|
||||
return MPT1327DecodeEvent.builder(message.getTimestamp())
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
return MPT1327DecodeEvent.builder(decodeEventType, message.getTimestamp())
|
||||
.details(details)
|
||||
.identifiers(ic)
|
||||
.build();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.mpt1327;
|
||||
@@ -23,56 +22,55 @@ package io.github.dsheirer.module.decode.mpt1327;
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.MessageType;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class MPT1327MessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for MPT1327 messages
|
||||
*/
|
||||
public class MPT1327MessageFilter extends Filter<IMessage, MPT1327Message.MPTMessageType>
|
||||
{
|
||||
private Map<MPT1327Message.MPTMessageType, FilterElement<MPT1327Message.MPTMessageType>> mFilterElements = new EnumMap<>(MPT1327Message.MPTMessageType.class);
|
||||
private final KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public MPT1327MessageFilter()
|
||||
{
|
||||
super("MPT1327 Message Type Filter");
|
||||
super("MPT1327 Messages");
|
||||
|
||||
for(MPT1327Message.MPTMessageType type : MPT1327Message.MPTMessageType.values())
|
||||
{
|
||||
if(type != MPT1327Message.MPTMessageType.UNKN)
|
||||
{
|
||||
mFilterElements.put(type, new FilterElement<MPT1327Message.MPTMessageType>(type));
|
||||
}
|
||||
add(new FilterElement<>(type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
{
|
||||
MPT1327Message mpt = (MPT1327Message) message;
|
||||
|
||||
FilterElement<MPT1327Message.MPTMessageType> element =
|
||||
mFilterElements.get(mpt.getMessageType());
|
||||
|
||||
if(element != null)
|
||||
{
|
||||
return element.isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof MPT1327Message;
|
||||
return message instanceof MPT1327Message && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, MPT1327Message.MPTMessageType> getKeyExtractor()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mFilterElements.values());
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage, MPT1327Message.MPTMessageType>
|
||||
{
|
||||
@Override
|
||||
public MPT1327Message.MPTMessageType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof MPT1327Message mpt)
|
||||
{
|
||||
return mpt.getMessageType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,10 +104,9 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
|
||||
}
|
||||
}
|
||||
|
||||
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp())
|
||||
MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent
|
||||
.mpt1327Builder(DecodeEventType.CALL, mpt1327Message.getTimestamp())
|
||||
.channel(mpt1327Channel)
|
||||
.eventType(DecodeEventType.CALL)
|
||||
.eventDescription("Call")
|
||||
.details("Traffic Channel Grant")
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -125,7 +124,7 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
|
||||
if(trafficChannel == null)
|
||||
{
|
||||
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
|
||||
channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription());
|
||||
channelGrantEvent.setDetails("Detect:" + channelGrantEvent.getDetails());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,10 +151,8 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen
|
||||
DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration();
|
||||
List<Channel> trafficChannelList = new ArrayList<>();
|
||||
|
||||
if(decodeConfiguration instanceof DecodeConfigMPT1327)
|
||||
if(decodeConfiguration instanceof DecodeConfigMPT1327 decodeConfigMPT1327)
|
||||
{
|
||||
DecodeConfigMPT1327 decodeConfigMPT1327 = (DecodeConfigMPT1327)decodeConfiguration;
|
||||
|
||||
int maxTrafficChannels = decodeConfigMPT1327.getTrafficChannelPoolSize();
|
||||
|
||||
for(int x = 0; x < maxTrafficChannels; x++)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25;
|
||||
|
||||
@@ -29,9 +28,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
{
|
||||
private ServiceOptions mServiceOptions;
|
||||
|
||||
public P25ChannelGrantEvent(long timestamp)
|
||||
public P25ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp)
|
||||
{
|
||||
super(timestamp);
|
||||
super(decodeEventType, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,9 +38,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static P25ChannelGrantDecodeEventBuilder builder(long timeStart, ServiceOptions serviceOptions)
|
||||
public static P25ChannelGrantDecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
|
||||
{
|
||||
return new P25ChannelGrantDecodeEventBuilder(timeStart, serviceOptions);
|
||||
return new P25ChannelGrantDecodeEventBuilder(decodeEventType, timeStart, serviceOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +75,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
{
|
||||
protected long mTimeStart;
|
||||
protected long mDuration;
|
||||
protected String mEventDescription;
|
||||
protected DecodeEventType mDecodeEventType;
|
||||
protected IdentifierCollection mIdentifierCollection;
|
||||
protected IChannelDescriptor mChannelDescriptor;
|
||||
@@ -88,8 +86,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
*
|
||||
* @param timeStart
|
||||
*/
|
||||
public P25ChannelGrantDecodeEventBuilder(long timeStart, ServiceOptions serviceOptions)
|
||||
public P25ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
mTimeStart = timeStart;
|
||||
mServiceOptions = serviceOptions;
|
||||
}
|
||||
@@ -124,26 +123,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event description text
|
||||
* @param description of the event
|
||||
*/
|
||||
public P25ChannelGrantDecodeEventBuilder eventDescription(String description)
|
||||
{
|
||||
mEventDescription = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link DecodeEventType}
|
||||
* @param decodeEventType of the event
|
||||
*/
|
||||
public P25ChannelGrantDecodeEventBuilder eventType(DecodeEventType decodeEventType)
|
||||
{
|
||||
mDecodeEventType = decodeEventType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier collection.
|
||||
* @param identifierCollection containing optional identifiers like TO, FROM, frequency and
|
||||
@@ -170,13 +149,11 @@ public class P25ChannelGrantEvent extends P25DecodeEvent
|
||||
*/
|
||||
public P25ChannelGrantEvent build()
|
||||
{
|
||||
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mTimeStart);
|
||||
P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mDecodeEventType, mTimeStart);
|
||||
decodeEvent.setProtocol(Protocol.APCO25);
|
||||
decodeEvent.setChannelDescriptor(mChannelDescriptor);
|
||||
decodeEvent.setDetails(mDetails);
|
||||
decodeEvent.setDuration(mDuration);
|
||||
decodeEvent.setEventType(mDecodeEventType);
|
||||
decodeEvent.setEventDescription(mEventDescription);
|
||||
decodeEvent.setIdentifierCollection(mIdentifierCollection);
|
||||
decodeEvent.setServiceOptions(mServiceOptions);
|
||||
return decodeEvent;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,12 +14,13 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25;
|
||||
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
|
||||
public class P25DecodeEvent extends DecodeEvent
|
||||
@@ -29,9 +29,9 @@ public class P25DecodeEvent extends DecodeEvent
|
||||
* Constructs a P25 decode event
|
||||
* @param start
|
||||
*/
|
||||
public P25DecodeEvent(long start)
|
||||
public P25DecodeEvent(DecodeEventType decodeEventType, long start)
|
||||
{
|
||||
super(start);
|
||||
super(decodeEventType, start);
|
||||
setProtocol(Protocol.APCO25);
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ public class P25DecodeEvent extends DecodeEvent
|
||||
* @param timeStart for the event
|
||||
* @return builder
|
||||
*/
|
||||
public static DecodeEventBuilder builder(long timeStart)
|
||||
public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
|
||||
{
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart);
|
||||
DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
|
||||
decodeEventBuilder.protocol(Protocol.APCO25);
|
||||
return decodeEventBuilder;
|
||||
}
|
||||
|
||||
@@ -281,11 +281,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
event.end(timestamp);
|
||||
|
||||
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString() + " - Continue")
|
||||
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.details("CONTINUE - PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
@@ -306,7 +304,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(trafficChannel != null)
|
||||
{
|
||||
event.setEventDescription(decodeEventType.toString());
|
||||
event.setDetails("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
|
||||
event.setChannelDescriptor(apco25Channel);
|
||||
broadcast(event);
|
||||
@@ -327,10 +324,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
|
||||
{
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString() + " - Ignored")
|
||||
.details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -340,10 +335,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
return;
|
||||
}
|
||||
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
.details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -357,8 +350,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(trafficChannel == null)
|
||||
{
|
||||
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
|
||||
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
|
||||
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -427,11 +419,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
event.end(timestamp);
|
||||
|
||||
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString() + " - Continue")
|
||||
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.details("CONTINUE - PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
@@ -460,7 +450,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(trafficChannel != null)
|
||||
{
|
||||
event.setEventDescription(decodeEventType.toString());
|
||||
event.setDetails("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""));
|
||||
event.setChannelDescriptor(apco25Channel);
|
||||
broadcast(event);
|
||||
@@ -488,10 +477,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(mIgnoreDataCalls && opcode.isDataChannelGrant())
|
||||
{
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString() + " - Ignored")
|
||||
.details("PHASE 2 DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -502,10 +489,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
return;
|
||||
}
|
||||
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions)
|
||||
P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions)
|
||||
.channel(apco25Channel)
|
||||
.eventType(decodeEventType)
|
||||
.eventDescription(decodeEventType.toString())
|
||||
.details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
@@ -526,8 +511,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if(trafficChannel == null)
|
||||
{
|
||||
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
|
||||
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
|
||||
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -836,17 +820,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
if (event != null)
|
||||
{
|
||||
event.setEventDescription(event.getEventDescription() + " - Rejected");
|
||||
|
||||
if (channelEvent.getDescription() != null)
|
||||
{
|
||||
event.setDetails(channelEvent.getDescription() + " - " + event.getDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
|
||||
}
|
||||
|
||||
event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails());
|
||||
broadcast(event);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -654,10 +654,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
private void processBroadcast(List<Identifier> identifiers, long timestamp, DecodeEventType request, String s) {
|
||||
MutableIdentifierCollection requestCollection = getMutableIdentifierCollection(identifiers);
|
||||
|
||||
broadcast(P25DecodeEvent.builder(timestamp)
|
||||
broadcast(P25DecodeEvent.builder(request, timestamp)
|
||||
.channel(getCurrentChannel())
|
||||
.eventType(request)
|
||||
.eventDescription(request.toString())
|
||||
.details(s)
|
||||
.identifiers(requestCollection)
|
||||
.build());
|
||||
@@ -767,17 +765,16 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
/**
|
||||
* Updates or creates a current call event.
|
||||
*
|
||||
* @param type of call that will be used as an event description
|
||||
* @param decodeEventType of call that will be used as an event description
|
||||
* @param details of the call (optional)
|
||||
* @param timestamp of the message indicating a call or continuation
|
||||
*/
|
||||
private void updateCurrentCall(DecodeEventType type, String details, long timestamp)
|
||||
private void updateCurrentCall(DecodeEventType decodeEventType, String details, long timestamp)
|
||||
{
|
||||
if(mCurrentCallEvent == null)
|
||||
{
|
||||
mCurrentCallEvent = P25DecodeEvent.builder(timestamp)
|
||||
mCurrentCallEvent = P25DecodeEvent.builder(DecodeEventType.CALL, timestamp)
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(type.toString())
|
||||
.details(details)
|
||||
.identifiers(getIdentifierCollection().copyOf())
|
||||
.build();
|
||||
@@ -791,9 +788,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
mCurrentCallEvent.end(timestamp);
|
||||
broadcast(mCurrentCallEvent);
|
||||
|
||||
if(type == DecodeEventType.CALL_ENCRYPTED)
|
||||
if(decodeEventType == DecodeEventType.CALL_ENCRYPTED)
|
||||
{
|
||||
mCurrentCallEvent.setEventDescription(type.toString());
|
||||
mCurrentCallEvent.setDetails(details);
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED));
|
||||
}
|
||||
@@ -838,12 +834,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
*/
|
||||
private void processPDU(P25Message message)
|
||||
{
|
||||
if(message.isValid() && message instanceof PDUMessage)
|
||||
if(message.isValid() && message instanceof PDUMessage pdu)
|
||||
{
|
||||
PDUMessage pdu = (PDUMessage)message;
|
||||
|
||||
processBroadcast(pdu.getIdentifiers(), message.getTimestamp(), DecodeEventType.DATA_PACKET, pdu.toString());
|
||||
|
||||
}
|
||||
|
||||
broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA));
|
||||
@@ -856,10 +849,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
*/
|
||||
private void processUMBTC(P25Message message)
|
||||
{
|
||||
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing)
|
||||
if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing tired)
|
||||
{
|
||||
UMBTCTelephoneInterconnectRequestExplicitDialing tired = (UMBTCTelephoneInterconnectRequestExplicitDialing)message;
|
||||
|
||||
processBroadcast(tired.getIdentifiers(), tired.getTimestamp(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT:" + tired.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@@ -910,11 +901,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString())
|
||||
.identifiers(ic)
|
||||
.details(arsPacket.toString() + " " + ipv4.toString())
|
||||
.details(arsPacket + " " + ipv4)
|
||||
.build();
|
||||
|
||||
broadcast(packetEvent);
|
||||
@@ -929,11 +919,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent cellocatorEvent = P25DecodeEvent.builder(DecodeEventType.CELLOCATOR, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription("Cellocator")
|
||||
.identifiers(ic)
|
||||
.details(mcgpPacket.toString() + " " + ipv4.toString())
|
||||
.details(mcgpPacket + " " + ipv4)
|
||||
.build();
|
||||
|
||||
broadcast(cellocatorEvent);
|
||||
@@ -942,10 +931,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
{
|
||||
MutableIdentifierCollection ic = new MutableIdentifierCollection(packet.getIdentifiers());
|
||||
|
||||
DecodeEvent lrrpEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent lrrpEvent = P25DecodeEvent.builder(DecodeEventType.LRRP, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.details(lrrpPacket + " " + ipv4)
|
||||
.eventDescription("LRRP")
|
||||
.identifiers(ic)
|
||||
.protocol(Protocol.LRRP)
|
||||
.build();
|
||||
@@ -956,9 +944,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
if(geoPosition != null)
|
||||
{
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(message.getTimestamp())
|
||||
PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent
|
||||
.plottableBuilder(DecodeEventType.GPS, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.GPS.toString())
|
||||
.identifiers(ic)
|
||||
.protocol(Protocol.LRRP)
|
||||
.location(geoPosition)
|
||||
@@ -975,9 +963,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UDP_PACKET, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.UDP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
@@ -993,9 +980,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.ICMP_PACKET, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.ICMP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
@@ -1010,9 +996,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.IP_PACKET, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.IP_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(ipv4.toString())
|
||||
.build();
|
||||
@@ -1028,9 +1013,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
ic.update(identifier);
|
||||
}
|
||||
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp())
|
||||
DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, message.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.UNKNOWN_PACKET.toString())
|
||||
.identifiers(ic)
|
||||
.details(packet.toString())
|
||||
.build();
|
||||
@@ -1376,9 +1360,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
|
||||
private void processTSBKRoamingAddressRequest(TSBKMessage tsbk) {
|
||||
//TODO: not sure if this should be used or not.
|
||||
broadcast(P25DecodeEvent.builder(tsbk.getTimestamp())
|
||||
broadcast(P25DecodeEvent.builder(DecodeEventType.REQUEST, tsbk.getTimestamp())
|
||||
.channel(getCurrentChannel())
|
||||
.eventDescription(DecodeEventType.REQUEST.toString())
|
||||
.details("ROAMING ADDRESS")
|
||||
// TODO: This identifierCollection is different from all the others.
|
||||
.identifiers(new IdentifierCollection(tsbk.getIdentifiers()))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class HDUMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for HDU header messages
|
||||
*/
|
||||
public class HeaderMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
public HDUMessageFilter()
|
||||
{
|
||||
super("Header Data Unit");
|
||||
}
|
||||
private static final String HDU_KEY = "Header Data Unit (HDU)";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public HeaderMessageFilter()
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
super("Header Messages");
|
||||
add(new FilterElement<>(HDU_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof HDUMessage;
|
||||
return message instanceof HDUMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof HDUMessage)
|
||||
{
|
||||
return HDU_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class IPPacketMessageFilter extends Filter<IMessage>
|
||||
{
|
||||
public IPPacketMessageFilter()
|
||||
{
|
||||
super("Packet Message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof PacketMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
@@ -23,33 +22,52 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LDUMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for unknown messages, meaning messages not handled by any other filter.
|
||||
*/
|
||||
public class P25OtherMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
public LDUMessageFilter()
|
||||
private static final String OTHER_KEY = "Other/Unknown Message";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public P25OtherMessageFilter()
|
||||
{
|
||||
super("Link Data Unit");
|
||||
super("Other P25 messages");
|
||||
add(new FilterElement<>(OTHER_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof LDUMessage;
|
||||
return message instanceof P25Message && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof P25Message)
|
||||
{
|
||||
return OTHER_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,35 +14,42 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.filter.SyncLossMessageFilter;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.SyncLossMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
|
||||
|
||||
public class P25MessageFilterSet extends FilterSet<IMessage>
|
||||
/**
|
||||
* Filter set for P25 messages
|
||||
*/
|
||||
public class P25P1MessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
public P25MessageFilterSet()
|
||||
public P25P1MessageFilterSet()
|
||||
{
|
||||
super("P25 Message Filter");
|
||||
|
||||
addFilter(new AMBTCMessageFilter());
|
||||
addFilter(new HDUMessageFilter());
|
||||
addFilter(new IPPacketMessageFilter());
|
||||
addFilter(new LDUMessageFilter());
|
||||
addFilter(new HeaderMessageFilter());
|
||||
addFilter(new PacketMessageFilter());
|
||||
addFilter(new PDUMessageFilter());
|
||||
addFilter(new SNDCPMessageFilter());
|
||||
addFilter(new SyncLossMessageFilter());
|
||||
addFilter(new TDUMessageFilter());
|
||||
addFilter(new TDULCMessageFilter());
|
||||
addFilter(new TSBKMessageFilterSet());
|
||||
addFilter(new UMBTCMessageFilter());
|
||||
addFilter(new TerminatorMessageFilterSet());
|
||||
addFilter(new TrunkingOpcodeMessageFilterSet());
|
||||
addFilter(new VoiceMessageFilter());
|
||||
addFilter(new P25OtherMessageFilter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Override default to descope handling to P25 or sync-loss messages.
|
||||
* @param message to test
|
||||
* @return true if the message can be processed
|
||||
*/
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
@@ -24,49 +23,52 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class PDUMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for PDU packet data unit messages
|
||||
*/
|
||||
public class PDUMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private Map<Opcode, FilterElement<Opcode>> mOpcodeFilterElements = new EnumMap<>(Opcode.class);
|
||||
private static final String PDU_KEY = "Packet Data Unit (PDU)";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PDUMessageFilter()
|
||||
{
|
||||
super("PDU - Packet Data Unit");
|
||||
|
||||
for(Opcode opcode : Opcode.values())
|
||||
{
|
||||
mOpcodeFilterElements.put(opcode, new FilterElement<Opcode>(opcode));
|
||||
}
|
||||
super("Packet Data Unit messages");
|
||||
add(new FilterElement<>(PDU_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
if(mEnabled && message instanceof PDUMessage)
|
||||
{
|
||||
PDUMessage pdu = (PDUMessage) message;
|
||||
|
||||
Opcode opcode = pdu.getOpcode();
|
||||
|
||||
return mOpcodeFilterElements.get(opcode).isEnabled();
|
||||
}
|
||||
|
||||
return false;
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof PDUMessage;
|
||||
return message instanceof TDUMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mOpcodeFilterElements.values());
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof PDUMessage)
|
||||
{
|
||||
return PDU_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for packet messages
|
||||
*/
|
||||
public class PacketMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String KEY_CELLOCATOR = "Cellocator Packet";
|
||||
private static final String KEY_IPV4_OTHER = "IPV4 Other Packet";
|
||||
private static final String KEY_IPV4_UDP_ICMP = "IPV4 UDP/IP ICMP Packet";
|
||||
private static final String KEY_IPV4_UDP_OTHER = "IPV4 UDP/IP Other Packet";
|
||||
private static final String KEY_MOTOROLA_ARS = "Motorola Registration (ARS) Packet";
|
||||
private static final String KEY_MOTOROLA_LRRP = "Motorola Location Report (LRRP) Packet";
|
||||
private static final String KEY_MOTOROLA_XCMP = "Motorola Extensible (XCMP) Packet";
|
||||
private static final String KEY_SNDCP = "SNDCP Data Packet";
|
||||
private static final String KEY_UNKNOWN = "Unknown Packet";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public PacketMessageFilter()
|
||||
{
|
||||
super("Packet Messages");
|
||||
add(new FilterElement<>(KEY_CELLOCATOR));
|
||||
add(new FilterElement<>(KEY_IPV4_OTHER));
|
||||
add(new FilterElement<>(KEY_IPV4_UDP_ICMP));
|
||||
add(new FilterElement<>(KEY_IPV4_UDP_OTHER));
|
||||
add(new FilterElement<>(KEY_MOTOROLA_ARS));
|
||||
add(new FilterElement<>(KEY_MOTOROLA_LRRP));
|
||||
add(new FilterElement<>(KEY_MOTOROLA_XCMP));
|
||||
add(new FilterElement<>(KEY_SNDCP));
|
||||
add(new FilterElement<>(KEY_UNKNOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof PacketMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof PacketMessage packetMessage)
|
||||
{
|
||||
IPacket packet = packetMessage.getPacket();
|
||||
|
||||
if(packet instanceof MCGPPacket)
|
||||
{
|
||||
return KEY_CELLOCATOR;
|
||||
}
|
||||
else if(packet instanceof ICMPPacket)
|
||||
{
|
||||
return KEY_IPV4_UDP_ICMP;
|
||||
}
|
||||
else if(packet instanceof UDPPacket)
|
||||
{
|
||||
return KEY_IPV4_UDP_OTHER;
|
||||
}
|
||||
else if(packet instanceof IPV4Packet)
|
||||
{
|
||||
return KEY_IPV4_OTHER;
|
||||
}
|
||||
else if(packet instanceof SNDCPPacketMessage)
|
||||
{
|
||||
return KEY_SNDCP;
|
||||
}
|
||||
else if(packet instanceof ARSPacket)
|
||||
{
|
||||
return KEY_MOTOROLA_ARS;
|
||||
}
|
||||
else if(packet instanceof LRRPPacket)
|
||||
{
|
||||
return KEY_MOTOROLA_LRRP;
|
||||
}
|
||||
else if(packet instanceof XCMPPacket)
|
||||
{
|
||||
return KEY_MOTOROLA_XCMP;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return KEY_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
@@ -24,32 +23,55 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPMessage;
|
||||
import io.github.dsheirer.module.decode.p25.reference.PDUType;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SNDCPMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for SNDCP messages
|
||||
*/
|
||||
public class SNDCPMessageFilter extends Filter<IMessage, PDUType>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SNDCPMessageFilter()
|
||||
{
|
||||
super("Sub-Network Dependent Convergence Protocol");
|
||||
}
|
||||
super("Sub-Network Dependent Convergence Protocol (SNDCP) Messages");
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
for(PDUType pduType: PDUType.values())
|
||||
{
|
||||
add(new FilterElement<>(pduType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof SNDCPMessage;
|
||||
return message instanceof SNDCPMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
public Function<IMessage, PDUType> getKeyExtractor()
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,PDUType>
|
||||
{
|
||||
@Override
|
||||
public PDUType apply(IMessage message)
|
||||
{
|
||||
if(message instanceof SNDCPMessage sndcp)
|
||||
{
|
||||
return sndcp.getPDUType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TDULCMessageFilter extends Filter<IMessage>
|
||||
{
|
||||
public TDULCMessageFilter()
|
||||
{
|
||||
super("TDU Terminator Data Unit with Link Control");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof TDULinkControlMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +14,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 io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
@@ -24,32 +23,51 @@ import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class TDUMessageFilter extends Filter<IMessage>
|
||||
/**
|
||||
* Filter for TDU terminator (no link control) messages
|
||||
*/
|
||||
public class TDUMessageFilter extends Filter<IMessage,String>
|
||||
{
|
||||
private static final String TDU_KEY = "Terminator Data Unit (TDU)";
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TDUMessageFilter()
|
||||
{
|
||||
super("TDU Terminator Data Unit");
|
||||
super("Terminator/No Link Control messages");
|
||||
add(new FilterElement<>(TDU_KEY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
public Function<IMessage, String> getKeyExtractor()
|
||||
{
|
||||
return mEnabled && canProcess(message);
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof TDUMessage;
|
||||
return message instanceof TDUMessage && super.canProcess(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,String>
|
||||
{
|
||||
return Collections.EMPTY_LIST;
|
||||
@Override
|
||||
public String apply(IMessage message)
|
||||
{
|
||||
if(message instanceof TDUMessage)
|
||||
{
|
||||
return TDU_KEY;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
|
||||
import io.github.dsheirer.module.decode.p25.reference.Vendor;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class TSBKMessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
public TSBKMessageFilterSet()
|
||||
{
|
||||
super("TSBK Trunking Signalling Block");
|
||||
|
||||
addFilter(new StandardOpcodeFilter());
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean passes( Message message )
|
||||
// {
|
||||
// if( mEnabled && canProcess( message ) )
|
||||
// {
|
||||
// TSBKMessage tsbk = (TSBKMessage) message;
|
||||
//
|
||||
// switch( tsbk.getVendor() )
|
||||
// {
|
||||
// case STANDARD:
|
||||
// return
|
||||
// }
|
||||
// Opcode opcode = tsbk.getOpcode();
|
||||
//
|
||||
// return mFilterElements.get( opcode ).isEnabled();
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return message instanceof TSBKMessage;
|
||||
}
|
||||
|
||||
public class StandardOpcodeFilter extends Filter<IMessage>
|
||||
{
|
||||
private Map<Opcode, FilterElement<Opcode>> mStandardElements = new EnumMap<>(Opcode.class);
|
||||
|
||||
public StandardOpcodeFilter()
|
||||
{
|
||||
super("Standard Opcode Filter");
|
||||
|
||||
for(Opcode opcode : Opcode.values())
|
||||
{
|
||||
if(opcode != Opcode.OSP_UNKNOWN)
|
||||
{
|
||||
mStandardElements.put(opcode, new FilterElement<Opcode>(opcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passes(IMessage message)
|
||||
{
|
||||
if(mEnabled && canProcess(message))
|
||||
{
|
||||
Opcode opcode = ((TSBKMessage) message).getOpcode();
|
||||
|
||||
if(mStandardElements.containsKey(opcode))
|
||||
{
|
||||
return mStandardElements.get(opcode).isEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return ((TSBKMessage) message).getVendor() == Vendor.STANDARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FilterElement<?>> getFilterElements()
|
||||
{
|
||||
return new ArrayList<FilterElement<?>>(mStandardElements.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Filter for TDU terminator and TDULC terminator with link control messages
|
||||
*/
|
||||
public class TerminatorMessageFilter extends Filter<IMessage, LinkControlOpcode>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param name of this filter
|
||||
*/
|
||||
public TerminatorMessageFilter(String name, Collection<LinkControlOpcode> opcodes)
|
||||
{
|
||||
super(name);
|
||||
|
||||
for(LinkControlOpcode opcode: opcodes)
|
||||
{
|
||||
add(new FilterElement<>(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<IMessage, LinkControlOpcode> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,LinkControlOpcode>
|
||||
{
|
||||
@Override
|
||||
public LinkControlOpcode apply(IMessage message)
|
||||
{
|
||||
if(message instanceof TDULinkControlMessage tdulc)
|
||||
{
|
||||
return tdulc.getLinkControlWord().getOpcode();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.FilterSet;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Filter set for terminator/link control messages
|
||||
*/
|
||||
public class TerminatorMessageFilterSet extends FilterSet<IMessage>
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public TerminatorMessageFilterSet()
|
||||
{
|
||||
super("Terminator Messages");
|
||||
addFilter(new TDUMessageFilter());
|
||||
addFilter(new TerminatorMessageFilter("Command/Status", LinkControlOpcode.COMMAND_STATUS_OPCODES));
|
||||
addFilter(new TerminatorMessageFilter("Network/Channel", LinkControlOpcode.NETWORK_OPCODES));
|
||||
addFilter(new TerminatorMessageFilter("Voice", LinkControlOpcode.VOICE_OPCODES));
|
||||
addFilter(new TerminatorMessageFilter("Motorola", LinkControlOpcode.MOTOROLA_OPCODES));
|
||||
|
||||
List<LinkControlOpcode> others = new ArrayList<>();
|
||||
for(LinkControlOpcode opcode: LinkControlOpcode.values())
|
||||
{
|
||||
if(!opcode.isGrouped())
|
||||
{
|
||||
others.add(opcode);
|
||||
}
|
||||
}
|
||||
addFilter(new TerminatorMessageFilter("Other/Reserved", others));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProcess(IMessage message)
|
||||
{
|
||||
return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
|
||||
|
||||
import io.github.dsheirer.filter.Filter;
|
||||
import io.github.dsheirer.filter.FilterElement;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode;
|
||||
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Message filter for P25 TSBK, AMBTC and UMBTC trunking control messages.
|
||||
*/
|
||||
public class TrunkingOpcodeMessageFilter extends Filter<IMessage, Opcode>
|
||||
{
|
||||
private KeyExtractor mKeyExtractor = new KeyExtractor();
|
||||
private Collection<Opcode> mOpcodes;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param name of this filter
|
||||
*/
|
||||
public TrunkingOpcodeMessageFilter(String name, Collection<Opcode> opcodes)
|
||||
{
|
||||
super(name);
|
||||
|
||||
mOpcodes = opcodes;
|
||||
|
||||
for(Opcode opcode: opcodes)
|
||||
{
|
||||
add(new FilterElement<>(opcode));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor
|
||||
* @return opcode extractor
|
||||
*/
|
||||
@Override
|
||||
public Function<IMessage, Opcode> getKeyExtractor()
|
||||
{
|
||||
return mKeyExtractor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key extractor for Opcode values from TSBK, AMBTC and UMBTC messages.
|
||||
*/
|
||||
private class KeyExtractor implements Function<IMessage,Opcode>
|
||||
{
|
||||
@Override
|
||||
public Opcode apply(IMessage message)
|
||||
{
|
||||
if(message instanceof TSBKMessage tsbk)
|
||||
{
|
||||
return tsbk.getOpcode();
|
||||
}
|
||||
else if(message instanceof AMBTCMessage ambtc)
|
||||
{
|
||||
return ambtc.getHeader().getOpcode();
|
||||
}
|
||||
else if(message instanceof UMBTCMessage umbtc)
|
||||
{
|
||||
return umbtc.getOpcode();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user