diff --git a/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
index 7108c0e76..8200516fb 100644
--- a/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
+++ b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java
@@ -35,11 +35,11 @@
public class NowPlayingPanel extends JPanel
{
- private ChannelMetadataPanel mChannelMetadataPanel;
- private ChannelDetailPanel mChannelDetailPanel;
- private DecodeEventPanel mDecodeEventPanel;
- 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,
diff --git a/src/main/java/io/github/dsheirer/filter/AllPassFilter.java b/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
index 945c5828c..f94f19edf 100644
--- a/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/AllPassFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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 extends Filter
+public class AllPassFilter extends Filter
{
- public AllPassFilter()
- {
- super( "Allow All Messages" );
- }
-
- @Override
- public boolean passes( T t )
+ 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)
{
- return true;
+ super(name);
+ add(new FilterElement<>(KEY));
}
@Override
- public boolean canProcess( T t )
- {
- return true;
- }
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
- @Override
- @SuppressWarnings( "unchecked" )
- public List getFilterElements()
- {
- return Collections.EMPTY_LIST;
- }
+ /**
+ * Key extractor that always returns the same (String)key.
+ */
+ public class AllPassKeyExtractor implements Function
+ {
+ @Override
+ public String apply(T t)
+ {
+ return KEY;
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/Filter.java b/src/main/java/io/github/dsheirer/filter/Filter.java
index 9e84bd54b..6f99a05b8 100644
--- a/src/main/java/io/github/dsheirer/filter/Filter.java
+++ b/src/main/java/io/github/dsheirer/filter/Filter.java
@@ -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
+ * ****************************************************************************
+ */
+
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 implements IFilter
+/**
+ * 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 object type that this filter can process.
+ * @param key type used to lookup filter elements
+ */
+public abstract class Filter implements IFilter
{
- protected String mName;
- protected boolean mEnabled = true;
-
- public Filter( String name )
- {
- mName = name;
- }
-
- /**
- * Indicates if this filter is enabled or disabled
- */
- public boolean isEnabled()
- {
- return mEnabled;
- }
-
- /**
- * Enables (true) or disables (false) this filter. An enabled filter will
- * evaluate messages and a disabled filter will return false on all message
- * test methods.
- */
- public void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
-
- /**
- * List of filter elements managed by this filter
- */
- public abstract List> getFilterElements();
-
- /**
- * Name of this filter
- */
- public String getName()
- {
- return mName;
- }
-
- public void setName( String name )
- {
- mName = name;
- }
-
- public String toString()
- {
- return mName;
- }
+ private static final Logger LOG = LoggerFactory.getLogger(Filter.class);
+ private String mName;
+ private Map> mFilterElementMap = new HashMap<>();
+ private IFilterChangeListener mFilterChangeListener;
+
+ /**
+ * Constructs an instance
+ * @param name of this filter
+ */
+ public Filter(String name)
+ {
+ mName = name;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Key extractor function provided by the subclass.
+ * @return key extractor function
+ */
+ public abstract Function 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 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> 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);
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterEditor.java b/src/main/java/io/github/dsheirer/filter/FilterEditor.java
new file mode 100644
index 000000000..2d5ffec60
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/filter/FilterEditor.java
@@ -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
+ * ****************************************************************************
+ */
+
+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 item type for editing
+ */
+public class FilterEditor extends JFrame
+{
+ private FilterEditorPanel 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 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 filterSet)
+ {
+ mEditorPanel.updateFilterSet(filterSet);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
index d3842102e..a1260a7e3 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterEditorPanel.java
@@ -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
+ * ****************************************************************************
+ */
+
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 element type for the filter set.
+ */
public class FilterEditorPanel extends JPanel
{
private static final long serialVersionUID = 1L;
-
private JTree mTree;
private DefaultTreeModel mModel;
private FilterSet mFilterSet;
+ /**
+ * Constructs an instance
+ * @param filterSet to manage
+ */
public FilterEditorPanel(FilterSet 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 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 filterSet, DefaultMutableTreeNode parent)
{
List> filters = filterSet.getFilters();
- /* sort the filters in alphabetical order by name */
- filters.sort(Comparator.comparing(IFilter::getName));
-
for(IFilter filter : filters)
{
- DefaultMutableTreeNode child = new DefaultMutableTreeNode(filter);
-
- mModel.insertNodeInto(child, parent, parent.getChildCount());
+ DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(filter);
+ mModel.insertNodeInto(childNode, parent, parent.getChildCount());
- if(filter instanceof FilterSet)
+ if(filter instanceof FilterSet childFilterSet)
{
- addFilterSet((FilterSet) filter, child);
+ addFilterSet(childFilterSet, childNode);
}
- else if(filter instanceof Filter)
+ else if(filter instanceof Filter childFilter)
{
- addFilter((Filter) filter, child);
+ addFilter(childFilter, childNode);
}
}
}
- private void addFilter(Filter 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> elements = filter.getFilterElements();
@@ -103,168 +131,164 @@ private void addFilter(Filter filter, DefaultMutableTreeNode parent)
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)
+ return null;
+ }
+
+ /**
+ * Recursively sets the enabled state on all filter node children below the specified parent node.
+ *
+ * @param model - model containing the tree nodes
+ * @param node - parent node
+ * @param enabled - true or false to enable or disable the filters
+ */
+ private void setFilterEnabled(DefaultTreeModel model, DefaultMutableTreeNode node, boolean enabled)
+ {
+ Object obj = node.getUserObject();
+
+ if(obj instanceof FilterElement filterElement)
+ {
+ filterElement.setEnabled(enabled);
+ }
+
+ /* Recursively set the children of this node */
+ Enumeration> children = node.children();
+
+ while(children.hasMoreElements())
{
- if(SwingUtilities.isRightMouseButton(event))
+ Object child = children.nextElement();
+
+ if(child instanceof DefaultMutableTreeNode childNode)
{
- int row = mTree.getRowForLocation(event.getX(),
- event.getY());
+ setFilterEnabled(model, childNode, enabled);
+ }
+ }
- if(row != -1)
- {
- mTree.setSelectionRow(row);
+ model.nodeChanged(node);
+ }
- Object selectedNode =
- mTree.getLastSelectedPathComponent();
+ /**
+ * 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(selectedNode instanceof DefaultMutableTreeNode)
+ if(found != null)
{
- 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());
- }
+ return found;
}
}
}
}
- @Override
- public void mousePressed(MouseEvent e)
- {
- }
+ return null;
+ }
- @Override
- public void mouseReleased(MouseEvent e)
- {
- }
+ /**
+ * 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);
- @Override
- public void mouseEntered(MouseEvent e)
- {
- }
+ TreeNode parent = node.getParent();
- @Override
- public void mouseExited(MouseEvent e)
+ while(parent != null)
{
+ mModel.nodeChanged(parent);
+ parent = parent.getParent();
}
}
/**
- * Recursively sets the enabled state on all filter node children below the
- * specified parent node.
+ * Recursively searches the tree to find the tree node containing the specified filter.
*
- * @param model - model containing the tree nodes
- * @param node - parent node
- * @param enabled - true or false to enable or disable the filters
+ * @param node to start search
+ * @param filter to find
*/
- private void setFilterEnabled(DefaultTreeModel model,
- DefaultMutableTreeNode node,
- boolean enabled)
+ private DefaultMutableTreeNode findFilterNode(DefaultMutableTreeNode node, IFilter> filter)
{
- Object obj = node.getUserObject();
-
- if(obj instanceof FilterSet>)
- {
- ((FilterSet>) obj).setEnabled(enabled);
- }
- else if(obj instanceof Filter)
- {
- ((Filter>) obj).setEnabled(enabled);
- }
- else if(obj instanceof FilterElement)
- {
- ((FilterElement>) obj).setEnabled(enabled);
- }
-
- model.nodeChanged(node);
-
- /* Recursively set the children of this node */
- Enumeration> children = node.children();
-
- while(children.hasMoreElements())
+ if(node != null)
{
- Object child = children.nextElement();
+ if(node.getUserObject() instanceof IFilter> selfFilter && selfFilter.equals(filter))
+ {
+ return node;
+ }
- if(child instanceof DefaultMutableTreeNode)
+ for(int x = 0; x < node.getChildCount(); x++)
{
- setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled);
+ 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 @@ else if(userObject instanceof FilterElement)
}
}
- 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;
-
- public FilterCheckBox(IFilter filter)
+ private IFilter> mFilter;
+
+ /**
+ * 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 boolean isCellEditable(EventObject anEvent)
}
@Override
- public void removeCellEditorListener(CellEditorListener l)
- {
- }
+ public void removeCellEditorListener(CellEditorListener l) {}
@Override
public boolean shouldSelectCell(EventObject anEvent)
diff --git a/src/main/java/io/github/dsheirer/filter/FilterElement.java b/src/main/java/io/github/dsheirer/filter/FilterElement.java
index 394b793c9..9f95e6db9 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterElement.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterElement.java
@@ -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
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+/**
+ * Filter element provides filtering and enabled state tracking for an individual filterable element.
+ *
+ * @param element to track and filter.
+ */
public class FilterElement implements Comparable>
{
- private boolean mEnabled;
- private T mElement;
-
- public FilterElement( T element, boolean enabled )
- {
- mElement = element;
- mEnabled = enabled;
- }
-
- public FilterElement( T t )
- {
- this( t, true );
- }
-
- public T getElement()
- {
- return mElement;
- }
-
- public String toString()
- {
- return getName();
- }
-
- public boolean isEnabled()
- {
- return mEnabled;
- }
-
- public void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
-
- public String getName()
- {
- return mElement.toString();
- }
-
- @Override
- public int compareTo( FilterElement other )
- {
- return toString().compareTo( other.toString() );
+ private boolean mEnabled;
+ private final T mElement;
+ private IFilterChangeListener mFilterChangeListener;
+
+ /**
+ * Constructs an instance
+ *
+ * @param element to track
+ * @param enabled enabled state of this filter element
+ */
+ public FilterElement(T element, boolean enabled)
+ {
+ 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 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;
}
}
diff --git a/src/main/java/io/github/dsheirer/filter/FilterSet.java b/src/main/java/io/github/dsheirer/filter/FilterSet.java
index ef8a1f639..8609c491f 100644
--- a/src/main/java/io/github/dsheirer/filter/FilterSet.java
+++ b/src/main/java/io/github/dsheirer/filter/FilterSet.java
@@ -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
+ * ****************************************************************************
+ */
+
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 type of element tracked/filtered by this filter set.
+ */
public class FilterSet implements IFilter
{
- protected List> mFilters = new ArrayList>();
- protected boolean mEnabled = true;
- protected String mName;
-
- public FilterSet( String name )
- {
- mName = name;
- }
-
- public FilterSet()
- {
- mName = "Message Filter";
- }
-
- public FilterSet(IFilter filter)
+ private static final Logger LOGGER = LoggerFactory.getLogger(FilterSet.class);
+ private static LoggingSuppressor sLoggingSuppressor = new LoggingSuppressor(LOGGER);
+ private List> mFilters = new ArrayList<>();
+ private String mName;
+ private IFilterChangeListener mFilterChangeListener;
+
+ /**
+ * 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 filter)
+ {
+ this("Filters");
addFilter(filter);
}
-
- public String getName()
- {
- return mName;
- }
-
- public void setName( String name )
- {
- mName = name;
- }
-
- public String toString()
- {
- return getName();
- }
-
+
+ /**
+ * Registers the listener with each child filter element.
+ * @param listener to register
+ */
+ public void register(IFilterChangeListener listener)
+ {
+ mFilterChangeListener = listener;
+
+ for(IFilter child: 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 )
- {
- if( mEnabled )
+ public boolean passes(T t)
+ {
+ for(IFilter filter : mFilters)
{
- for( IFilter filter: mFilters )
+ //Stop at the first filter that says that it can process the message type.
+ if(filter.canProcess(t))
{
- // 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 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 )
- {
- if( mEnabled )
+ public boolean canProcess(T t)
+ {
+ for(IFilter filter : mFilters)
{
- for( IFilter filter: mFilters )
+ if(filter.canProcess(t))
{
- if( filter.canProcess( t ) )
- {
- return true;
- }
+ 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> getFilters()
{
- return mFilters;
+ return new ArrayList<>(mFilters);
}
-
- public void addFilters( List> filters )
+
+ /**
+ * Adds a list of child filters to this filter set.
+ * @param filters to add
+ */
+ public void addFilters(List> filters)
{
- mFilters.addAll( filters );
+ for(IFilter filter: filters)
+ {
+ addFilter(filter);
+ }
}
-
- public void addFilter( IFilter filter )
+
+ /**
+ * Adds the child filter to this filter set.
+ * @param filter to add
+ */
+ public void addFilter(IFilter filter)
{
- mFilters.add( 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);
}
-
- public void removeFilter( IFilter filter )
+
+ /**
+ * Indicates if any of the child filters contain at least one filter element that is enabled.
+ * @return enabled state
+ */
+ @Override
+ public boolean isEnabled()
{
- mFilters.remove( filter );
+ for(IFilter filter: mFilters)
+ {
+ if(filter.isEnabled())
+ {
+ return true;
+ }
+ }
+
+ return false;
}
+ /**
+ * Recursive count of enabled child filter elements.
+ * @return count
+ */
@Override
- public boolean isEnabled()
- {
- return mEnabled;
- }
+ 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 void setEnabled( boolean enabled )
- {
- mEnabled = enabled;
- }
+ public int getElementCount()
+ {
+ int count = 0;
+
+ for(IFilter filter: mFilters)
+ {
+ count += filter.getElementCount();
+ }
+
+ return count;
+ }
}
diff --git a/src/main/java/io/github/dsheirer/filter/IFilter.java b/src/main/java/io/github/dsheirer/filter/IFilter.java
index b8596a291..1cba93205 100644
--- a/src/main/java/io/github/dsheirer/filter/IFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/IFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
package io.github.dsheirer.filter;
+/**
+ * Filter interface
+ *
+ * @param type of filter
+ */
public interface IFilter
{
- /**
- * Generic filter method.
- *
- * @param message - message to filter
- * @return - true if the message passes the filter
- */
- public boolean passes( T t );
-
- /**
- * Indicates if the filter can process (filter) the object
- *
- * @param message - candidate message for filtering
- *
- * @return - true if the filter is capable of filtering the message
- */
- public boolean canProcess( T t );
-
- /**
- * Indicates if this filter is enabled to evaluate messages
- */
- public boolean isEnabled();
-
- /**
- * Sets the enabled state of the filter
- */
- public void setEnabled( boolean enabled );
-
- /**
- * Display name for the filter
- */
- public String getName();
+ /**
+ * 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 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
+ */
+ boolean isEnabled();
+
+ /**
+ * 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);
}
diff --git a/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java
new file mode 100644
index 000000000..db36f7846
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java
@@ -0,0 +1,25 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.filter;
+
+public interface IFilterChangeListener
+{
+ void filterChanged();
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java b/src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
similarity index 55%
rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java
rename to src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
index be973b5ca..e33d53860 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SyncLossMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/filter/SyncLossMessageFilter.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
-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
+/**
+ * Filter for sync-loss messages
+ */
+public class SyncLossMessageFilter extends Filter
{
+ 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 getKeyExtractor()
{
- return mEnabled && canProcess(message);
+ return mKeyExtractor;
}
@Override
@@ -47,9 +52,20 @@ public boolean canProcess(IMessage message)
return message instanceof SyncLossMessage;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return Collections.EMPTY_LIST;
+ @Override
+ public String apply(IMessage message)
+ {
+ if(message instanceof SyncLossMessage)
+ {
+ return SYNC_LOSS_KEY;
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/HistoryModule.java b/src/main/java/io/github/dsheirer/module/HistoryModule.java
index b79cb36b1..accc47ad5 100644
--- a/src/main/java/io/github/dsheirer/module/HistoryModule.java
+++ b/src/main/java/io/github/dsheirer/module/HistoryModule.java
@@ -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 @@
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 HistoryModule(int maximumHistorySize)
*/
public List getItems()
{
- return new ArrayList<>(mItems);
+ return new ArrayList<>(mItems);
}
@Override
diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
index cf9935a98..13b6d3e36 100644
--- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java
+++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java
@@ -117,8 +117,8 @@ public class ProcessingChain implements Listener
private Broadcaster mMessageBroadcaster = new Broadcaster<>();
private Broadcaster 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;
diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
index 551f59946..20a6729de 100644
--- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
@@ -47,6 +47,7 @@
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.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 static List getAuxiliaryDecoders(AuxDecodeConfiguration config)
*/
public static FilterSet getMessageFilters(List modules)
{
- FilterSet filterSet = new FilterSet<>();
+ FilterSet filterSet = new FilterSet<>("Message Filters");
for(Module module : modules)
{
@@ -504,28 +507,28 @@ public static FilterSet getMessageFilters(List modules)
}
}
- /* 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> getMessageFilter(DecoderType decoder)
{
- ArrayList> filters = new ArrayList<>();
+ List> 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 static List> getMessageFilter(DecoderType decoder)
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;
diff --git a/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
index b07fd1f42..c2bb750a0 100644
--- a/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/analog/AnalogDecoderState.java
@@ -26,6 +26,7 @@
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 @@ private void startCallEvent()
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();
diff --git a/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
index ea5a64f6d..af0812a8e 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dcs/DCSMessageFilter.java
@@ -22,45 +22,55 @@
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
+public class DCSMessageFilter extends Filter
{
- private Map> 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));
+ 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 passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- if(mEnabled && canProcess(message))
- {
- return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
- }
-
- return false;
+ return message instanceof DCSMessage && super.canProcess(message);
}
+ /**
+ * Key extractor that always returns the same key constant.
+ * @return extractor function.
+ */
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof DCSMessage;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mElements.values());
+ @Override
+ public String apply(IMessage message)
+ {
+ return DCS_KEY;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
index 57b4fe2c3..4bfd1b65a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRDecoderState.java
@@ -286,9 +286,7 @@ private void processSMS(UDTShortMessageService sms)
{
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,8 +310,7 @@ private void processPacket(DMRPacketMessage packet)
mic.update(hyteraSmsPacket.getSource());
mic.update(hyteraSmsPacket.getDestination());
- DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.SMS.name())
+ DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("SMS:" + hyteraSmsPacket.getSMS())
@@ -325,8 +322,7 @@ else if(packet.getPacket() instanceof HyteraUnknownPacket hyteraUnknownPacket)
{
MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers());
- DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.UNKNOWN_PACKET.name())
+ DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
.identifiers(mic)
.timeslot(packet.getTimeslot())
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
@@ -335,8 +331,7 @@ else if(packet.getPacket() instanceof HyteraUnknownPacket hyteraUnknownPacket)
}
else
{
- DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
- .eventDescription(DecodeEventType.DATA_PACKET.name())
+ DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
.timeslot(packet.getTimeslot())
.details(packet.toString())
@@ -347,9 +342,8 @@ else if(packet.getPacket() instanceof HyteraUnknownPacket hyteraUnknownPacket)
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)
@@ -505,14 +499,8 @@ private void processCSBK(CSBKMessage csbk)
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_PAYLOAD:
if(csbk instanceof Acknowledge)
{
- DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.RESPONSE.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details(((Acknowledge)csbk).getReason().toString())
- .build();
-
- broadcast(ackEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
+ ((Acknowledge) csbk).getReason().toString()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot()));
break;
@@ -520,14 +508,8 @@ private void processCSBK(CSBKMessage csbk)
case STANDARD_ACKNOWLEDGE_RESPONSE_OUTBOUND_TSCC:
if(csbk instanceof Acknowledge)
{
- DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.RESPONSE.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details(((Acknowledge)csbk).getReason().toString())
- .build();
-
- broadcast(ackEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE,
+ ((Acknowledge) csbk).getReason().toString()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
break;
@@ -537,33 +519,16 @@ private void processCSBK(CSBKMessage csbk)
switch(((Ahoy)csbk).getServiceKind())
{
case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE:
- DecodeEvent registerEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.COMMAND.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details(DecodeEventType.REGISTER.toString())
- .build();
- broadcast(registerEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, DecodeEventType.REGISTER.toString()));
break;
case CANCEL_CALL_SERVICE:
- DecodeEvent cancelEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.COMMAND.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("CANCEL CALL")
- .build();
- broadcast(cancelEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND, "CANCEL CALL"));
break;
case SUPPLEMENTARY_SERVICE:
if(csbk instanceof StunReviveKill)
{
- DecodeEvent stunEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.COMMAND.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details(((StunReviveKill)csbk).getCommand() + " RADIO")
- .build();
- broadcast(stunEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
+ ((StunReviveKill)csbk).getCommand() + " RADIO"));
}
break;
case FULL_DUPLEX_MS_TO_MS_PACKET_CALL_SERVICE:
@@ -577,15 +542,9 @@ private void processCSBK(CSBKMessage csbk)
if(csbk instanceof ServiceRadioCheck)
{
ServiceRadioCheck src = (ServiceRadioCheck)csbk;
-
- DecodeEvent checkEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.RADIO_CHECK.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details(src.getServiceDescription() + " SERVICE FOR " +
- (src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO"))
- .build();
- broadcast(checkEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.RADIO_CHECK,
+ src.getServiceDescription() + " SERVICE FOR " +
+ (src.isTalkgroupTarget() ? "TALKGROUP" : "RADIO")));
}
break;
}
@@ -599,14 +558,7 @@ private void processCSBK(CSBKMessage csbk)
if(aloha.hasRadioIdentifier())
{
- DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.RESPONSE.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("Aloha Acknowledge")
- .build();
-
- broadcast(ackEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
resetState();
}
}
@@ -618,24 +570,13 @@ private void processCSBK(CSBKMessage csbk)
switch(((Announcement)csbk).getAnnouncementType())
{
case MASS_REGISTRATION:
- DecodeEvent massEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.REGISTER.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("MASS REGISTRATION")
- .build();
- broadcast(massEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.REGISTER, "MASS REGISTRATION"));
break;
case VOTE_NOW_ADVICE:
if(csbk instanceof VoteNowAdvice)
{
- DecodeEvent voteEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.COMMAND.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode())
- .build();
- broadcast(voteEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
+ "VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode()));
}
break;
}
@@ -653,13 +594,8 @@ private void processCSBK(CSBKMessage csbk)
case STANDARD_PROTECT:
if(csbk instanceof Protect)
{
- DecodeEvent protectEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.COMMAND.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("PROTECT: " + ((Protect)csbk).getProtectKind())
- .build();
- broadcast(protectEvent);
+ broadcast(getDecodeEvent(csbk, DecodeEventType.COMMAND,
+ "PROTECT: " + ((Protect)csbk).getProtectKind()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot()));
break;
@@ -714,13 +650,7 @@ private void processCSBK(CSBKMessage csbk)
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
- event = DMRDecodeEvent.builder(csbk.getTimestamp())
- .channel(channel)
- .details(csbk.getOpcode().getLabel())
- .eventDescription(DecodeEventType.DATA_CALL.toString())
- .identifiers(mergedIdentifiers)
- .timeslot(channel.getTimeslot())
- .build();
+ event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -753,13 +683,7 @@ private void processCSBK(CSBKMessage csbk)
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
- event = DMRDecodeEvent.builder(csbk.getTimestamp())
- .channel(channel)
- .details(csbk.getOpcode().getLabel())
- .eventDescription(DecodeEventType.CALL_GROUP.toString())
- .identifiers(mergedIdentifiers)
- .timeslot(channel.getTimeslot())
- .build();
+ event = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -791,13 +715,7 @@ private void processCSBK(CSBKMessage csbk)
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
- event = DMRDecodeEvent.builder(csbk.getTimestamp())
- .channel(channel)
- .details(csbk.getOpcode().getLabel())
- .eventDescription(DecodeEventType.CALL_UNIT_TO_UNIT.toString())
- .identifiers(mergedIdentifiers)
- .timeslot(channel.getTimeslot())
- .build();
+ event = getDecodeEvent(csbk, DecodeEventType.CALL_UNIT_TO_UNIT, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -817,15 +735,7 @@ private void processCSBK(CSBKMessage csbk)
if(cmAloha.hasRadioIdentifier())
{
- DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .eventDescription(DecodeEventType.RESPONSE.toString())
- .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
- .timeslot(csbk.getTimeslot())
- .details("Aloha Acknowledge")
- .build();
-
- broadcast(ackEvent);
-
+ broadcast(getDecodeEvent(csbk, DecodeEventType.RESPONSE, "Aloha Acknowledge"));
resetState();
}
}
@@ -850,13 +760,7 @@ private void processCSBK(CSBKMessage csbk)
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
{
- event = DMRDecodeEvent.builder(csbk.getTimestamp())
- .channel(channel)
- .details(csbk.getOpcode().getLabel())
- .eventDescription(DecodeEventType.DATA_CALL.toString())
- .identifiers(mergedIdentifiers)
- .timeslot(channel.getTimeslot())
- .build();
+ event = getDecodeEvent(csbk, DecodeEventType.DATA_CALL, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
}
else
@@ -871,9 +775,8 @@ private void processCSBK(CSBKMessage csbk)
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();
@@ -881,9 +784,8 @@ private void processCSBK(CSBKMessage csbk)
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();
@@ -909,13 +811,7 @@ private void processCSBK(CSBKMessage csbk)
if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers()))
{
- detectedEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
- .channel(channel)
- .details(csbk.getOpcode().getLabel())
- .eventDescription(DecodeEventType.CALL_GROUP.toString())
- .identifiers(mergedIdentifiers)
- .timeslot(channel.getTimeslot())
- .build();
+ detectedEvent = getDecodeEvent(csbk, DecodeEventType.CALL_GROUP, channel, mergedIdentifiers);
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent);
}
else
@@ -930,9 +826,8 @@ private void processCSBK(CSBKMessage csbk)
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();
@@ -945,6 +840,23 @@ private void processCSBK(CSBKMessage csbk)
}
}
+ private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
+ return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
+ .channel(channel)
+ .details(csbk.getOpcode().getLabel())
+ .identifiers(mergedIdentifiers)
+ .timeslot(channel.getTimeslot())
+ .build();
+ }
+
+ private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
+ return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
+ .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
+ .timeslot(csbk.getTimeslot())
+ .details(details)
+ .build();
+ }
+
/**
* Creates a copy of the current identifier collection, removes any USER class identifiers and loads the identifiers
* argument values into the collection.
@@ -1191,8 +1103,7 @@ private void processLinkControl(LCMessage message, boolean isTerminator)
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())
@@ -1223,9 +1134,8 @@ private void updateCurrentCall(DecodeEventType type, String details, long timest
if(mCurrentCallEvent == null)
{
- mCurrentCallEvent = DMRDecodeEvent.builder(timestamp)
+ mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp)
.channel(getCurrentChannel())
- .eventDescription(type.toString())
.details(details)
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -1236,7 +1146,6 @@ private void updateCurrentCall(DecodeEventType type, String details, long timest
{
if(type != DecodeEventType.CALL)
{
- mCurrentCallEvent.setEventDescription(type.toString());
mCurrentCallEvent.setDetails(details);
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
index ec6577f8b..eea406ff8 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/DMRTrafficChannelManager.java
@@ -325,12 +325,12 @@ public void processChannelGrant(DMRChannel channel, IdentifierCollection identif
int lsn = channel.getLogicalSlotNumber();
DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn);
+ DecodeEventType decodeEventType = getEventType(opcode, identifierCollection, encrypted);
if(isStale(event, timestamp, identifierCollection)) //Create new event
{
- event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
+ event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
- .eventDescription(getEventType(opcode, identifierCollection, encrypted).toString())
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -349,10 +349,9 @@ public void processChannelGrant(DMRChannel channel, IdentifierCollection identif
{
event.end(timestamp);
- event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
+ event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp)
.channel(channel)
- .eventDescription(getEventType(opcode, identifierCollection, encrypted).toString() + " - Continue")
- .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
+ .details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
.identifiers(identifierCollection)
.build();
@@ -389,15 +388,6 @@ else if(!event.getDetails().endsWith(NO_FREQUENCY))
{
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
{
- if(event.getEventDescription() == null)
- {
- event.setEventDescription(getEventType(opcode, identifierCollection, encrypted) + IGNORED);
- }
- else if(!event.getEventDescription().endsWith(IGNORED))
- {
- event.setEventDescription(event.getEventDescription() + IGNORED);
- }
-
if(event.getDetails() == null)
{
event.setDetails(DATA_CALL_IGNORED);
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
index 5d1d1e772..c9ecddc11 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRChannelGrantEvent.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.event;
import io.github.dsheirer.identifier.IdentifierCollection;
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
/**
* DMR Channel Grant Event.
@@ -33,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);
}
/**
@@ -62,9 +63,9 @@ public void setEncrypted(boolean encrypted)
* @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);
}
/**
@@ -74,7 +75,7 @@ public static class DMRChannelGrantDecodeEventBuilder
{
protected long mTimeStart;
protected long mDuration;
- protected String mEventDescription;
+ protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected DMRChannel mChannel;
protected String mDetails;
@@ -85,8 +86,9 @@ public static class DMRChannelGrantDecodeEventBuilder
*
* @param timeStart
*/
- public DMRChannelGrantDecodeEventBuilder(long timeStart)
+ public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -121,7 +123,7 @@ public DMRChannelGrantDecodeEventBuilder end(long timestamp)
/**
* Sets the channel descriptor for this event
- * @param channelDescriptor
+ * @param channel
*/
public DMRChannelGrantDecodeEventBuilder channel(DMRChannel channel)
{
@@ -129,16 +131,6 @@ public DMRChannelGrantDecodeEventBuilder channel(DMRChannel channel)
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
@@ -165,12 +157,11 @@ public DMRChannelGrantDecodeEventBuilder details(String details)
*/
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.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
index a69f0ab10..218fd5dde 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/event/DMRDecodeEvent.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
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 DMRDecodeEvent(long start)
* @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;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
index cdccf4193..ec8a601c9 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/csbk/Opcode.java
@@ -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 String toString()
*/
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 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 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 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 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 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 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 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 MOTOROLA_CAPACITY_MAX = EnumSet.of(MOTOROLA_CAPMAX_ALOHA);
+
+ /**
+ * Motorola Capacity Plus opcodes
+ */
+ public static final EnumSet 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 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
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
index ee1a51f5a..a35dab54c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/lc/LCOpcode.java
@@ -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 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);
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
index 667661f71..cb444b37d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/data/packet/PacketSequenceMessageFactory.java
@@ -63,7 +63,6 @@ public static IMessage create(PacketSequence packetSequence)
if(packetSequence.hasPacketSequenceHeader())
{
PacketSequenceHeader primaryHeader = packetSequence.getPacketSequenceHeader();
- boolean confirmed = primaryHeader.isConfirmedData();
CorrectedBinaryMessage packet = getPacket(packetSequence, primaryHeader.isConfirmedData());
if(packet != null)
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java
new file mode 100644
index 000000000..b0eec20ce
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ 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 opcodes)
+ {
+ super(name);
+
+ for(Opcode opcode: opcodes)
+ {
+ add(new FilterElement<>(opcode));
+ }
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public Opcode apply(IMessage message)
+ {
+ if(message instanceof CSBKMessage csbk)
+ {
+ return csbk.getOpcode();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java
new file mode 100644
index 000000000..fb8b7ccb2
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/ControlMessageFilterSet.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ /**
+ * 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 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);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java
new file mode 100644
index 000000000..928ab8a04
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DataMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ 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 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
+ {
+ @Override
+ public DataType apply(IMessage message)
+ {
+ if(message instanceof DataMessage dataMessage)
+ {
+ return dataMessage.getSlotType().getDataType();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java
new file mode 100644
index 000000000..dc852e0c6
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java
@@ -0,0 +1,45 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.dmr.message.filter;
+
+import io.github.dsheirer.filter.FilterSet;
+import io.github.dsheirer.filter.SyncLossMessageFilter;
+import io.github.dsheirer.message.IMessage;
+
+/**
+ * Filter set for DMR messages
+ */
+public class DmrMessageFilterSet extends FilterSet
+{
+ /**
+ * Constructor
+ */
+ public DmrMessageFilterSet()
+ {
+ super("DMR Messages");
+ addFilter(new ControlMessageFilterSet());
+ addFilter(new VoiceMessageFilter());
+ addFilter(new DataMessageFilter());
+ addFilter(new DmrPacketSequenceFilter());
+ addFilter(new LinkControlMessageFilterSet());
+ addFilter(new SyncLossMessageFilter());
+ addFilter(new DmrOtherMessageFilter());
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java
new file mode 100644
index 000000000..339edc3e4
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrOtherMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ 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 getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ @Override
+ public boolean canProcess(IMessage message)
+ {
+ return message instanceof DMRMessage && super.canProcess(message);
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public String apply(IMessage message)
+ {
+ if(message instanceof DMRMessage)
+ {
+ return OTHER_KEY;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java
new file mode 100644
index 000000000..83cfc096e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrPacketSequenceFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ 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 getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ private class KeyExtractor implements Function
+ {
+ @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;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java
new file mode 100644
index 000000000..708dc8e2b
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ private KeyExtractor mKeyExtractor = new KeyExtractor();
+
+ /**
+ * Constructs an instance
+ *
+ * @param name of this filter
+ */
+ public LinkControlMessageFilter(String name, Collection opcodes)
+ {
+ super(name);
+
+ for(LCOpcode opcode: opcodes)
+ {
+ add(new FilterElement<>(opcode));
+ }
+ }
+
+ @Override
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public LCOpcode apply(IMessage message)
+ {
+ if(message instanceof LCMessage lcMessage)
+ {
+ return lcMessage.getOpcode();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java
new file mode 100644
index 000000000..20191f127
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/LinkControlMessageFilterSet.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ /**
+ * Constructor
+ */
+ public LinkControlMessageFilterSet()
+ {
+ super("Short/Voice-Fragment Link Control Messages");
+
+ List fullLinkControl = new ArrayList<>();
+ List 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);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java
new file mode 100644
index 000000000..8eab4622e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/VoiceMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
+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
+{
+ 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 getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
+ {
+ @Override
+ public DMRSyncPattern apply(IMessage message)
+ {
+ if(message instanceof VoiceMessage voice)
+ {
+ return voice.getSyncPattern();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
index 29eac064f..9ee71138b 100644
--- a/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
+++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/type/DataType.java
@@ -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;
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java b/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java
new file mode 100644
index 000000000..c1a2af372
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/event/ClearableHistoryModel.java
@@ -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
+ * ****************************************************************************
+ */
+
+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 extends AbstractTableModel
+{
+ public static final int DEFAULT_HISTORY_SIZE = 200;
+ private LinkedList 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 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();
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
index ae4fa9ed6..4cc9cbf92 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEvent.java
@@ -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 void setDuration(long duration)
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 DecodeEventType getEventType() {
return mDecodeEventType;
}
- /**
- * Sets the {@link DecodeEventType}
- */
- public void setEventType(DecodeEventType eventType) {
- this.mDecodeEventType = eventType;
- }
-
/**
* Identifier collection for this event.
*/
@@ -222,24 +204,24 @@ public void setProtocol(Protocol protocol)
* @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 String toString()
{
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 static class DecodeEventBuilder
{
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,21 +295,6 @@ public DecodeEventBuilder channel(IChannelDescriptor channelDescriptor)
return this;
}
- 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
@@ -359,7 +326,12 @@ public DecodeEventBuilder protocol(Protocol protocol)
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;
@@ -370,11 +342,10 @@ public DecodeEventBuilder timeslot(Integer timeslot)
*/
public DecodeEvent build()
{
- DecodeEvent decodeEvent = new DecodeEvent(mTimeStart);
+ DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
- decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setTimeslot(mTimeslot);
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
index 697391dcd..b677a1254 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventModel.java
@@ -21,25 +21,20 @@
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, EventFilterProvider
+/**
+ * Decode event table model
+ */
+public class DecodeEventModel extends ClearableHistoryModel implements Listener
{
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 mEvents = new ArrayList<>();
- protected FilterSet mEventFilterSet = new DecodeEventFilterSet();
-
protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"};
public DecodeEventModel()
@@ -70,74 +59,12 @@ public DecodeEventModel()
@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 getEvents()
- {
- return new ArrayList<>(mEvents);
- }
-
- public void dispose()
- {
- MyEventBus.getGlobalEventBus().unregister(this);
- Iterator it = mEvents.iterator();
-
- while(it.hasNext())
+ if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT)
{
- it.remove();
+ fireTableDataChanged();
}
}
- public void clear()
- {
- mEvents.clear();
- fireTableDataChanged();
- }
-
- /**
- * Clears all events from this model and loads the events argument
- */
- public void clearAndSet(List 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
@@ -146,49 +73,7 @@ public void setMaxMessageCount(int count)
*/
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 getFilterSet() {
- return mEventFilterSet;
- }
-
- @Override
- public void setFilterSet(FilterSet filterSet) {
- this.mEventFilterSet = filterSet;
- }
-
- @Override
- public int getRowCount()
- {
- return mEvents.size();
+ EventQueue.invokeLater(() -> add(event));
}
@Override
@@ -205,58 +90,55 @@ public String getColumnName(int column)
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
- synchronized(mEvents)
- {
- IDecodeEvent event = mEvents.get(rowIndex);
+ IDecodeEvent event = getItem(rowIndex);
- if(event != null)
+ 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();
-
- if(channelDescriptor != null)
+ 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(event.hasTimeslot())
+ {
+ return channelDescriptor + " TS:" + event.getTimeslot();
+ }
+ else
+ {
+ return channelDescriptor.toString();
+ }
+ }
+ else
+ {
+ if(event.hasTimeslot())
{
- if(event.hasTimeslot())
- {
- return channelDescriptor.toString() + " TS:" + event.getTimeslot();
- }
- else
- {
- return channelDescriptor.toString();
- }
+ return "TS:" + event.getTimeslot();
}
else
{
- if(event.hasTimeslot())
- {
- return "TS:" + event.getTimeslot();
- }
- else
- {
- return null;
- }
+ return null;
}
- case COLUMN_FREQUENCY:
- return event.getChannelDescriptor();
- case COLUMN_DETAILS:
- return event.getDetails();
- }
+ }
+ case COLUMN_FREQUENCY:
+ return event.getChannelDescriptor();
+ case COLUMN_DETAILS:
+ return event.getDetails();
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java
index 1f543eaf6..bfd1fae32 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventPanel.java
@@ -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.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.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.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
{
@@ -75,7 +74,10 @@ public class DecodeEventPanel extends JPanel implements Listener mFilterSet = new DecodeEventFilterSet();
+ private TableRowSorter mTableRowSorter;
+ private HistoryManagementPanel mHistoryManagementPanel;
+
/**
* View for call event table
@@ -91,17 +93,20 @@ public DecodeEventPanel(IconModel iconModel, UserPreferences userPreferences, Al
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 void preferenceUpdated(PreferenceType preferenceType)
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 void receive(final ProcessingChain 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 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 filterProvider = (EventFilterProvider) mTable.getModel();
- FilterSet 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 ChannelDescriptorCellRenderer()
setHorizontalAlignment(JLabel.CENTER);
}
}
+
+ /**
+ * Row filter for decode events
+ */
+ public class EventRowFilter extends RowFilter
+ {
+ @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;
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
index a7b1f47ec..51b61733e 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/DecodeEventType.java
@@ -19,10 +19,17 @@
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"),
ANNOUNCEMENT("Announcement"),
+ ACKNOWLEDGE("Acknowledge"),
AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"),
CALL("Call"),
CALL_ENCRYPTED("Encrypted Call"),
@@ -32,6 +39,7 @@ public enum DecodeEventType
CALL_PATCH_GROUP_ENCRYPTED("Encrypted Patch Call"),
CALL_ALERT("Call Alert"),
CALL_DETECT("Call Detect"),
+ CALL_IN_PROGRESS("Call In Progress"),
CALL_DO_NOT_MONITOR("Call-Do Not Monitor"),
CALL_END("Call End"),
CALL_INTERCONNECT("Telephone Call"),
@@ -41,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"),
@@ -53,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"),
@@ -72,16 +82,82 @@ public enum DecodeEventType
private String mLabel;
+ /**
+ * Encrypted voice call event types for filtering
+ */
+ public static final EnumSet 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 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 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 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 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 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;
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java
new file mode 100644
index 000000000..ad76b60da
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/event/HistoryManagementPanel.java
@@ -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
+ * ****************************************************************************
+ */
+
+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 extends JPanel
+{
+ private ClearableHistoryModel mModel;
+ private FilterSet mFilterSet;
+ private FilterEditor 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 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 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;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
index ea3da208a..7fcf66c50 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/IDecodeEvent.java
@@ -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
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
index 615b3a45e..f5ecd4f27 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
@@ -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
+/**
+ * Table Model for decoded IMessages.
+ */
+public class MessageActivityModel extends ClearableHistoryModel implements Listener
{
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 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 mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
-
- public MessageActivityModel()
- {
- }
-
- /**
- * Applies the filter set
- */
- public void setFilters(FilterSet filterSet)
- {
- mMessageFilterSet = filterSet;
- }
-
- public void clearFilters()
- {
- mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
- }
-
/**
- * Clears all messages from history
+ * Constructor
*/
- public void clear()
+ public MessageActivityModel()
{
- EventQueue.invokeLater(() -> {
- mMessageItems.clear();
- fireTableDataChanged();
- });
}
/**
- * Clears the current messages and loads the messages argument
+ * Implements the listener interface and wraps the IMessage in table-compatible message item wrapper.
+ * @param message to add to the model
*/
- public void clearAndSet(List messages)
- {
- EventQueue.invokeLater(() -> {
- mMessageItems.clear();
- fireTableDataChanged();
- for(IMessage message: messages)
- {
- receive(message);
- }
- });
- }
-
- public FilterSet 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 void receive(final IMessage message)
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 String getColumnName(int column)
@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;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
index 4e07d9fe1..479c1d79c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityPanel.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
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.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
{
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 mTableRowSorter;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
- private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
+ private FilterSet mMessageFilterSet;
+ private HistoryManagementPanel mHistoryManagementPanel;
/**
* Constructs an instance
@@ -69,13 +63,14 @@ public class MessageActivityPanel extends JPanel implements Listener(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]"));
-
- add(mManagementPanel, "span,growx");
-
+ mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor");
+ add(mHistoryManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
- mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
}
/**
@@ -89,182 +84,62 @@ public void receive(ProcessingChain processingChain)
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 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();
- }
- }
-
- public class MessageManagementPanel extends JPanel
- {
- private static final long serialVersionUID = 1L;
-
- private MessageHistoryButton mHistoryButton = new MessageHistoryButton();
- private MessageFilterButton mFilterButton = new MessageFilterButton();
-
- public MessageManagementPanel()
- {
- setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", ""));
-
- disableButtons();
-
- add(mFilterButton);
- add(mHistoryButton);
- }
-
- public void enableButtons()
- {
- mHistoryButton.setEnabled(true);
- mFilterButton.setEnabled(true);
- }
-
- public void disableButtons()
- {
- mHistoryButton.setEnabled(false);
- mFilterButton.setEnabled(false);
+ mHistoryManagementPanel.setEnabled(false);
}
}
- public class MessageHistoryButton extends JideSplitButton
+ /**
+ * Row visibility filter for messages
+ */
+ public class MessageRowFilter extends RowFilter
{
- private static final long serialVersionUID = 1L;
-
- public MessageHistoryButton()
+ @Override
+ public boolean include(Entry extends TableModel, ? extends Integer> entry)
{
- super("Clear");
-
- JPanel historyPanel = new JPanel();
-
- historyPanel.add(new JLabel("Message History:"));
-
- final JSlider slider = new JSlider();
- slider.setMinimum(0);
- slider.setMaximum(2000);
- slider.setMajorTickSpacing(500);
- slider.setPaintTicks(true);
- slider.setPaintLabels(true);
-
- slider.addMouseListener(new MouseListener()
- {
- @Override
- public void mouseClicked(MouseEvent arg0)
- {
- if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2)
- {
- slider.setValue(500); //default
- }
- }
-
- public void mouseEntered(MouseEvent arg0)
- {
- }
-
- public void mouseExited(MouseEvent arg0)
- {
- }
-
- public void mousePressed(MouseEvent arg0)
- {
- }
-
- public void mouseReleased(MouseEvent arg0)
- {
- }
- });
-
- slider.setValue(((MessageActivityModel) mTable.getModel()).getMaxMessageCount());
- slider.addChangeListener(new ChangeListener()
+ if(entry.getModel() instanceof MessageActivityModel model)
{
- @Override
- public void stateChanged(ChangeEvent arg0)
- {
- ((MessageActivityModel) mTable.getModel()).setMaxMessageCount(slider.getValue());
- }
- });
-
- historyPanel.add(slider);
-
- add(historyPanel);
+ MessageItem item = model.getItem(entry.getIdentifier());
- /* Clear messages */
- addActionListener(new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
+ if(mMessageFilterSet != null && item != null && item.getMessage() != null)
{
- ((MessageActivityModel) mTable.getModel()).clear();
+ IMessage message = item.getMessage();
+ return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message);
}
- });
- }
- }
-
- 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 filter = (FilterSet) ((MessageActivityModel) mTable.getModel())
- .getMessageFilterSet();
-
- FilterEditorPanel 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;
}
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
index 87fca1235..719cf3b7c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/PlottableDecodeEvent.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
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,7 @@ public static class PlottableDecodeEventBuilder
{
private long mTimeStart;
private long mDuration;
- private String mEventDescription;
+ private DecodeEventType mDecodeEventType;
private IdentifierCollection mIdentifierCollection;
private IChannelDescriptor mChannelDescriptor;
private String mDetails;
@@ -114,8 +114,9 @@ public static class PlottableDecodeEventBuilder
/**
* 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;
}
@@ -149,16 +150,6 @@ public PlottableDecodeEventBuilder channel(IChannelDescriptor channelDescriptor)
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
@@ -222,11 +213,10 @@ public PlottableDecodeEventBuilder heading(double heading)
*/
public PlottableDecodeEvent build()
{
- PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart);
+ PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart);
decodeEvent.setChannelDescriptor(mChannelDescriptor);
decodeEvent.setDetails(mDetails);
decodeEvent.setDuration(mDuration);
- decodeEvent.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
decodeEvent.setProtocol(mProtocol);
decodeEvent.setLocation(mGeoPosition);
@@ -235,5 +225,4 @@ public PlottableDecodeEvent build()
return decodeEvent;
}
}
-
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java
new file mode 100644
index 000000000..88d24c7b4
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java
@@ -0,0 +1,39 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.module.decode.event.filter;
+
+import io.github.dsheirer.module.decode.event.DecodeEventType;
+
+/**
+ * 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 AllOtherEventFilter extends EventFilter
+{
+ /**
+ * Constructor
+ */
+ public AllOtherEventFilter()
+ {
+ super("Other", DecodeEventType.OTHERS);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
index ed2a8302b..a25055216 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodeEventFilterSet.java
@@ -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
+ * ****************************************************************************
+ */
+
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 {
- public DecodeEventFilterSet() {
- super ("All Messages");
-
+/**
+ * Top-level decode event filter set.
+ */
+public class DecodeEventFilterSet extends FilterSet
+{
+ public DecodeEventFilterSet()
+ {
+ super ("All Events");
addFilter(new DecodedCallEventFilter());
addFilter(new DecodedCallEncryptedEventFilter());
addFilter(new DecodedDataEventFilter());
addFilter(new DecodedCommandEventFilter());
addFilter(new DecodedRegistrationEventFilter());
+ addFilter(new AllOtherEventFilter());
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
index 3afc588a3..27e79bbf7 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEncryptedEventFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
index 930e52a2c..039963c3a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCallEventFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
index 8eb385e2c..2b0d5e7f6 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedCommandEventFilter.java
@@ -1,24 +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
+ * ****************************************************************************
+ */
+
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.PAGE,
- DecodeEventType.QUERY,
- DecodeEventType.RADIO_CHECK,
- DecodeEventType.STATUS,
- DecodeEventType.COMMAND,
- DecodeEventType.EMERGENCY,
- DecodeEventType.NOTIFICATION,
- DecodeEventType.FUNCTION
- ));
+ super("Commands", DecodeEventType.COMMANDS);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
index 5b8ea00dc..58ae94ed8 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedDataEventFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
index d316b6714..d78cafbe5 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/DecodedRegistrationEventFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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);
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
index 8dba214ea..e6bc6432a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearButton.java
@@ -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
+ * ****************************************************************************
+ */
+
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 EventClearButton(int maxHistoryCount)
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();
}
});
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
index badfd817b..60c664ade 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventClearHandler.java
@@ -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
+ * ****************************************************************************
+ */
+
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();
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
index 53508930d..32588e327 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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
+public class EventFilter extends Filter
{
- private Map> mElements = new EnumMap<>(DecodeEventType.class);
+ private EventKeyExtractor mKeyExtractor = new EventKeyExtractor();
- public EventFilter(String name, List eventTypes)
+ /**
+ * Constructs an instance
+ * @param name of this filter
+ * @param decodeEventTypes to filter against
+ */
+ public EventFilter(String name, Collection 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 getKeyExtractor()
{
- return true;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor function for decode events to extract the decode event type
+ */
+ public static class EventKeyExtractor implements Function
{
- return new ArrayList<>(mElements.values());
+ @Override
+ public DecodeEventType apply(IDecodeEvent iDecodeEvent)
+ {
+ return iDecodeEvent.getEventType();
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
index 235aa1be0..2b338c472 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterButton.java
@@ -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
+ * ****************************************************************************
+ */
+
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 type of filter.
+ */
public class EventFilterButton 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 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 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 filterSet;
-
- public EventFilterActionHandler( String title, FilterSet filterSet)
+ private String mTitle;
+ private FilterSet mFilterSet;
+
+ /**
+ * Constructs an instance
+ * @param title for this panel
+ * @param filterSet to edit
+ */
+ public EventFilterActionHandler(String title, FilterSet 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 panel = new FilterEditorPanel(filterSet);
-
+ editor.setLayout(new MigLayout("", "[grow,fill]", "[grow,fill][][]"));
+ FilterEditorPanel panel = new FilterEditorPanel(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));
}
}
}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java
deleted file mode 100644
index d09810103..000000000
--- a/src/main/java/io/github/dsheirer/module/decode/event/filter/EventFilterProvider.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.github.dsheirer.module.decode.event.filter;
-
-import io.github.dsheirer.filter.FilterSet;
-
-public interface EventFilterProvider {
- FilterSet getFilterSet();
- void setFilterSet(FilterSet filterSet);
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
index 2a6ba768b..70817ff13 100644
--- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2DecoderState.java
@@ -107,13 +107,7 @@ public void receive(IMessage message)
case ANI:
case EMERGENCY:
case LONE_WORKER_EMERGENCY:
- DecodeEvent aniEvent = DecodeEvent.builder(fleetsync.getTimestamp())
- .channel(getCurrentChannel())
- .eventDescription(fleetsync.getMessageType().toString())
- .details(fleetsync.toString())
- .identifiers(getIdentifierCollection().copyOf())
- .build();
-
+ DecodeEvent aniEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
broadcast(aniEvent);
broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.CALL));
break;
@@ -121,19 +115,13 @@ public void receive(IMessage message)
case PAGING:
case STATUS:
case UNKNOWN:
- DecodeEvent statusEvent = DecodeEvent.builder(fleetsync.getTimestamp())
- .channel(getCurrentChannel())
- .eventDescription(fleetsync.getMessageType().toString())
- .details(fleetsync.toString())
- .identifiers(getIdentifierCollection().copyOf())
- .build();
+ DecodeEvent statusEvent = getDecodeEvent(fleetsync, getDecodeEventType(fleetsync.getMessageType()));
broadcast(statusEvent);
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())
- .eventDescription(DecodeEventType.GPS.toString())
.details(fleetsync.toString())
.identifiers(getIdentifierCollection().copyOf())
.protocol(Protocol.FLEETSYNC)
@@ -147,6 +135,35 @@ public void receive(IMessage message)
}
}
+ private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) {
+ return DecodeEvent.builder(eventType, fleetsync.getTimestamp())
+ .channel(getCurrentChannel())
+ .details(fleetsync.getMessageType() + " " + fleetsync)
+ .identifiers(getIdentifierCollection().copyOf())
+ .protocol(Protocol.FLEETSYNC)
+ .build();
+ }
+
+ private DecodeEventType getDecodeEventType(FleetsyncMessageType fleetsyncMessageType) {
+ switch (fleetsyncMessageType) {
+ case ANI:
+ return DecodeEventType.ID_ANI;
+ case EMERGENCY:
+ case LONE_WORKER_EMERGENCY:
+ return DecodeEventType.EMERGENCY;
+ case ACKNOWLEDGE:
+ return DecodeEventType.ACKNOWLEDGE;
+ case PAGING:
+ return DecodeEventType.PAGE;
+ case STATUS:
+ return DecodeEventType.STATUS;
+ case GPS:
+ return DecodeEventType.GPS;
+ default:
+ return DecodeEventType.UNKNOWN;
+ }
+ }
+
/**
* Responds to reset events issued by the channel state
*/
diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java
index c2b75b0bd..10b9b8b35 100644
--- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/FleetsyncMessageFilter.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.fleetsync2;
@@ -24,52 +23,63 @@
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.*;
+import java.util.function.Function;
-public class FleetsyncMessageFilter extends Filter
+/**
+ * Filter for Fleetsync messages.
+ */
+public class FleetsyncMessageFilter extends Filter
{
- private Map> 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(type));
- }
+ add(new FilterElement<>(type));
}
}
+ /**
+ * Limits processing to only fleetsync messages.
+ * @param message to test
+ * @return true if message type is correct.
+ */
@Override
- public boolean passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- if(mEnabled && canProcess(message))
- {
- Fleetsync2Message fleet = (Fleetsync2Message)message;
-
- if(mElements.containsKey(fleet.getMessageType()))
- {
- return mElements.get(fleet.getMessageType()).isEnabled();
- }
- }
-
- return false;
+ return message instanceof Fleetsync2Message && super.canProcess(message);
}
+ /**
+ * Key extractor for fleetsync messages
+ * @return key extractor
+ */
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof Fleetsync2Message;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor for Fleetsync 2 Messages.
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mElements.values());
+ @Override
+ public FleetsyncMessageType apply(IMessage message)
+ {
+ if(message instanceof Fleetsync2Message f2m)
+ {
+ return f2m.getMessageType();
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
index 9763f4637..bc54188eb 100644
--- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200DecoderState.java
@@ -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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.lj1200;
@@ -30,14 +27,15 @@
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import io.github.dsheirer.module.decode.event.DecodeEventType;
+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
{
@@ -72,11 +70,11 @@ public void receive(IMessage message)
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)
.channel(getCurrentChannel())
- .details("LOJACK")
- .eventDescription(lj.toString())
+ .details("LOJACK " + lj)
.build();
broadcast(event);
@@ -91,11 +89,11 @@ else if(message instanceof LJ1200TransponderMessage)
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)
.channel(getCurrentChannel())
- .details("LOJACK TRANSPONDER")
- .eventDescription(transponder.toString())
+ .details("LOJACK TRANSPONDER " + transponder)
.build();
broadcast(transponderEvent);
@@ -114,7 +112,7 @@ public String getActivitySummary()
{
sb.append("Transponder Addresses:\n");
- List addresses = new ArrayList(mAddresses);
+ List addresses = new ArrayList<>(mAddresses);
Collections.sort(addresses);
diff --git a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java
index 96c4916f1..85a7467a0 100644
--- a/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/lj1200/LJ1200MessageFilter.java
@@ -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
- ******************************************************************************/
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
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
+/**
+ * Filter for LJ1200 messages
+ */
+public class LJ1200MessageFilter extends Filter
{
+ private static final String LJ1200_KEY = "LJ-1200";
+ private final LJ1200KeyExtractor mKeyExtractor = new LJ1200KeyExtractor();
+ /**
+ * Constructor
+ */
public LJ1200MessageFilter()
{
- super("LJ-1200 Message Filter");
+ super("LJ-1200 Messages");
+ add(new FilterElement<>(LJ1200_KEY));
}
@Override
- public boolean passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- return mEnabled && canProcess(message);
+ return (message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage);
}
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ public class LJ1200KeyExtractor implements Function
{
- return Collections.EMPTY_LIST;
+ @Override
+ public String apply(IMessage message)
+ {
+ return LJ1200_KEY;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecodeEvent.java
new file mode 100644
index 000000000..dfe3c4e0e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecodeEvent.java
@@ -0,0 +1,52 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+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(DecodeEventType decodeEventType, long start)
+ {
+ super(decodeEventType, start);
+ setProtocol(Protocol.LTR_NET);
+ }
+
+ /**
+ * Creates a new decode event builder with the specified start timestamp.
+ * @param timeStart for the event
+ * @return builder
+ */
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
+ {
+ DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
+ decodeEventBuilder.protocol(Protocol.LTR_NET);
+ return decodeEventBuilder;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
index c29402ae1..e3bba893d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderState.java
@@ -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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.ltrnet;
@@ -38,6 +35,7 @@
import io.github.dsheirer.message.MessageDirection;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.event.DecodeEvent;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.ltrnet.channel.LtrNetChannel;
import io.github.dsheirer.module.decode.ltrnet.identifier.LtrNetRadioIdentifier;
import io.github.dsheirer.module.decode.ltrnet.message.LtrNetMessage;
@@ -60,9 +58,6 @@
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;
@@ -71,6 +66,8 @@
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
{
@@ -166,10 +163,24 @@ private void processCallStart(int channel, LTRTalkgroup talkgroup, long timestam
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 = DecodeEvent.builder(timestamp)
- .protocol(Protocol.LTR_NET)
+ mCurrentCallEvent = LTRNetDecodeEvent.builder(decodeEventType, timestamp)
.channel(getCurrentChannel())
.identifiers(getIdentifierCollection().copyOf())
.build();
@@ -181,17 +192,14 @@ private void processCallStart(int channel, LTRTalkgroup talkgroup, long timestam
if(talkgroup.getTalkgroup() == 253)
{
- mCurrentCallEvent.setEventDescription("Register");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else if(talkgroup.getTalkgroup() == 254)
{
- mCurrentCallEvent.setEventDescription("FCC CWID");
broadcast(new DecoderStateEvent(this, Event.START, State.DATA));
}
else
{
- mCurrentCallEvent.setEventDescription("Call");
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
@@ -203,16 +211,7 @@ else if(talkgroup.getTalkgroup() == 254)
if(decodeEvent == null)
{
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(talkgroup);
-
- decodeEvent = DecodeEvent.builder(timestamp)
- .eventDescription("Call Detect")
- .channel(mChannelMap.get(channel))
- .protocol(Protocol.LTR_NET)
- .identifiers(ic)
- .build();
+ decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
mCallDetectMap.put(channel, decodeEvent);
}
else
@@ -223,16 +222,7 @@ else if(talkgroup.getTalkgroup() == 254)
if(eventTalkgroup == null || !eventTalkgroup.equals(talkgroup) ||
(timestamp - decodeEvent.getTimeStart() - decodeEvent.getDuration() > 2000))
{
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(talkgroup);
-
- decodeEvent = DecodeEvent.builder(timestamp)
- .eventDescription("Call Detect")
- .channel(mChannelMap.get(channel))
- .protocol(Protocol.LTR_NET)
- .identifiers(ic)
- .build();
+ decodeEvent = getDecodeEvent(talkgroup, channel, timestamp, DecodeEventType.CALL_DETECT);
mCallDetectMap.put(channel, decodeEvent);
}
}
@@ -307,89 +297,59 @@ public void receive(IMessage message)
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(DecodeEvent.builder(message.getTimestamp())
- .eventDescription("Registration Request")
- .details(registrationRequestEsn.toString())
- .identifiers(getIdentifierCollection().copyOf())
- .channel(getCurrentChannel())
- .protocol(Protocol.LTR_NET)
- .build());
+ 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(DecodeEvent.builder(message.getTimestamp())
- .eventDescription("Registration Request")
- .details(registrationRequestEsn.toString())
- .identifiers(getIdentifierCollection().copyOf())
- .channel(getCurrentChannel())
- .protocol(Protocol.LTR_NET)
- .build());
-
+ 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(DecodeEvent.builder(message.getTimestamp())
- .eventDescription("Access Request")
- .details(requestAccess.toString())
- .identifiers(getIdentifierCollection().copyOf())
- .channel(getCurrentChannel())
- .protocol(Protocol.LTR_NET)
- .build());
-
+ 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();
@@ -400,104 +360,90 @@ public void receive(IMessage message)
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(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.LTR_NET)
- .channel(getCurrentChannel())
- .identifiers(ic)
- .eventDescription("Registration Accept")
- .details(registrationAccept.toString())
- .build());
+ 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));
@@ -509,6 +455,27 @@ public void receive(IMessage message)
}
}
+ 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)
+ {
+ MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
+ ic.remove(IdentifierClass.USER);
+ ic.update(talkgroup);
+
+ return LTRNetDecodeEvent.builder(decodeEventType, timestamp)
+ .channel(mChannelMap.get(channel))
+ .identifiers(ic)
+ .build();
+ }
+
@Override
public String getActivitySummary()
{
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageFilter.java
index ab9182bc6..0b1226619 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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.*;
+import java.util.function.Function;
-public class LTRNetMessageFilter extends Filter
+/**
+ * Filter for LTR-Net messages
+ */
+public class LTRNetMessageFilter extends Filter
{
- private Map> 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));
}
}
+ /**
+ * Tests for instance of LTR-Net message
+ * @param message to test
+ * @return true if correct type.
+ */
@Override
- public boolean passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- if(mEnabled && canProcess(message))
- {
- LtrNetMessage ltr = (LtrNetMessage) message;
-
- if(mElements.containsKey(ltr.getLtrNetMessageType()))
- {
- return mElements.get(ltr.getLtrNetMessageType()).isEnabled();
- }
- }
-
- return false;
+ return message instanceof LtrNetMessage && super.canProcess(message);
}
+ /**
+ * Key extractor for LTR-Net messages
+ * @return extractor
+ */
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof LtrNetMessage;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mElements.values());
+ @Override
+ public LtrNetMessageType apply(IMessage message)
+ {
+ if(message instanceof LtrNetMessage ltr)
+ {
+ return ltr.getLtrNetMessageType();
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecodeEvent.java
new file mode 100644
index 000000000..b98ad009e
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecodeEvent.java
@@ -0,0 +1,52 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+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(DecodeEventType decodeEventType, long start)
+ {
+ super(decodeEventType, start);
+ setProtocol(Protocol.LTR);
+ }
+
+ /**
+ * Creates a new decode event builder with the specified start timestamp.
+ * @param timeStart for the event
+ * @return builder
+ */
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
+ {
+ DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
+ decodeEventBuilder.protocol(Protocol.LTR);
+ return decodeEventBuilder;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
index f5539d212..a28aa6dae 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java
@@ -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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.ltrstandard;
@@ -35,15 +32,13 @@
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 io.github.dsheirer.module.decode.ltrstandard.channel.LtrChannel;
import io.github.dsheirer.module.decode.ltrstandard.message.Call;
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;
@@ -51,6 +46,8 @@
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
{
@@ -81,10 +78,8 @@ public void receive(IMessage message)
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());
@@ -97,11 +92,9 @@ public void receive(IMessage message)
mCurrentTalkgroup = start.getTalkgroup();
getIdentifierCollection().remove(IdentifierClass.USER);
getIdentifierCollection().update(start.getTalkgroup());
- mCurrentCallEvent = DecodeEvent.builder(start.getTimestamp())
- .protocol(Protocol.LTR)
+ mCurrentCallEvent = LTRStandardDecodeEvent.builder(DecodeEventType.CALL, start.getTimestamp())
.identifiers(getIdentifierCollection().copyOf())
.channel(getCurrentChannel())
- .eventDescription("Call")
.build();
}
else
@@ -114,10 +107,8 @@ public void receive(IMessage message)
}
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
@@ -135,10 +126,10 @@ public void receive(IMessage message)
}
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:
diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java
index af409e5e7..7d27e8357 100644
--- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageFilter.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.ltrstandard;
@@ -24,47 +23,53 @@
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
+/**
+ * Filter for LTR standard messages.
+ */
+public class LTRStandardMessageFilter extends Filter
{
- private Map> mElements = new EnumMap<>(LtrStandardMessageType.class);
+ private final KeyExtractor mKeyExtractor = new KeyExtractor();
+ /**
+ * Constructor
+ */
public LTRStandardMessageFilter()
{
- super("LTR Message Filter");
-
- 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));
- }
+ super("LTR Messages");
- @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> getFilterElements()
+ public Function getKeyExtractor()
+ {
+ return mKeyExtractor;
+ }
+
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mElements.values());
+ @Override
+ public LtrStandardMessageType apply(IMessage message)
+ {
+ if(message instanceof LTRMessage ltr)
+ {
+ return ltr.getMessageType();
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecodeEvent.java
new file mode 100644
index 000000000..8187c7178
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecodeEvent.java
@@ -0,0 +1,53 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+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 an MDC1200 decode event
+ * @param decodeEventType for the event
+ * @param start for the event
+ */
+ public MDCDecodeEvent(DecodeEventType decodeEventType, long start)
+ {
+ super(decodeEventType, start);
+ setProtocol(Protocol.MDC1200);
+ }
+
+ /**
+ * Creates a new decode event builder with the specified start timestamp.
+ * @param timeStart for the event
+ * @return builder
+ */
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
+ {
+ DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
+ decodeEventBuilder.protocol(Protocol.MDC1200);
+ return decodeEventBuilder;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
index f79fefd8c..f9a079053 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecoderState.java
@@ -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
- * * *****************************************************************************
+ * 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
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.mdc1200;
@@ -29,9 +26,8 @@
import io.github.dsheirer.identifier.MutableIdentifierCollection;
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 io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier;
-
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
@@ -114,8 +110,7 @@ public void receive(IMessage message)
ic.remove(IdentifierClass.USER);
ic.update(message.getIdentifiers());
- broadcast(DecodeEvent.builder(mdc.getTimestamp())
- .eventDescription(type.getLabel())
+ broadcast(MDCDecodeEvent.builder(getDecodeEventType(type), mdc.getTimestamp())
.details(mdc.toString())
.identifiers(ic)
.build());
@@ -195,4 +190,22 @@ public void receiveDecoderStateEvent(DecoderStateEvent event)
break;
}
}
+
+ private DecodeEventType getDecodeEventType(MDCMessageType messageType) {
+ switch(messageType)
+ {
+ case ANI:
+ return DecodeEventType.ID_ANI;
+ case ACKNOWLEDGE:
+ return DecodeEventType.RESPONSE;
+ case EMERGENCY:
+ return DecodeEventType.EMERGENCY;
+ case PAGING:
+ return DecodeEventType.PAGE;
+ case STATUS:
+ return DecodeEventType.STATUS;
+ default:
+ return DecodeEventType.UNKNOWN;
+ }
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java
index 45b4d3afe..795f5a46a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCMessageFilter.java
@@ -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
+ * ****************************************************************************
+ */
+
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.*;
+import java.util.function.Function;
-public class MDCMessageFilter extends Filter
+/**
+ * Message filter for MDC-1200 messages
+ */
+public class MDCMessageFilter extends Filter
{
- private Map> 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(type));
- }
+ add(new FilterElement<>(type));
}
}
@Override
- public boolean passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- if(mEnabled && canProcess(message))
- {
- MDCMessage mdc = (MDCMessage) message;
-
- if(mElements.containsKey(mdc.getMessageType()))
- {
- return mElements.get(mdc.getMessageType()).isEnabled();
- }
- }
-
- return false;
+ return message instanceof MDCMessage && super.canProcess(message);
}
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof MDCMessage;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mElements.values());
+ @Override
+ public MDCMessageType apply(IMessage message)
+ {
+ if(message instanceof MDCMessage mdc)
+ {
+ return mdc.getMessageType();
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java
index 18506fcc3..7e249d071 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327ChannelGrantEvent.java
@@ -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,20 +14,23 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
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.p25.P25DecodeEvent;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
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);
}
/**
@@ -36,9 +38,9 @@ public MPT1327ChannelGrantEvent(long timestamp)
* @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);
}
/**
@@ -48,7 +50,7 @@ public static class MPT1327ChannelGrantDecodeEventBuilder
{
protected long mTimeStart;
protected long mDuration;
- protected String mEventDescription;
+ protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
protected String mDetails;
@@ -58,8 +60,9 @@ public static class MPT1327ChannelGrantDecodeEventBuilder
*
* @param timeStart
*/
- public MPT1327ChannelGrantDecodeEventBuilder(long timeStart)
+ public MPT1327ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart)
{
+ mDecodeEventType = decodeEventType;
mTimeStart = timeStart;
}
@@ -93,16 +96,6 @@ public MPT1327ChannelGrantDecodeEventBuilder channel(IChannelDescriptor channelD
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
@@ -129,12 +122,11 @@ public MPT1327ChannelGrantDecodeEventBuilder details(String details)
*/
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.setEventDescription(mEventDescription);
decodeEvent.setIdentifierCollection(mIdentifierCollection);
return decodeEvent;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecodeEvent.java
new file mode 100644
index 000000000..57afa5905
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecodeEvent.java
@@ -0,0 +1,52 @@
+/*
+ * *****************************************************************************
+ * 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
+ * ****************************************************************************
+ */
+
+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(DecodeEventType decodeEventType, long start)
+ {
+ super(decodeEventType, start);
+ setProtocol(Protocol.MPT1327);
+ }
+
+ /**
+ * Creates a new decode event builder with the specified start timestamp.
+ * @param timeStart for the event
+ * @return builder
+ */
+ public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart)
+ {
+ DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart);
+ decodeEventBuilder.protocol(Protocol.MPT1327);
+ return decodeEventBuilder;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
index 6cdf80184..95b1fc63b 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderState.java
@@ -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
@@ -30,17 +30,16 @@
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
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.protocol.Protocol;
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
{
@@ -110,28 +109,11 @@ public void receive(IMessage message)
if(identType == MPT1327Message.IdentType.REGI)
{
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Register")
- .identifiers(ic)
- .build());
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.REGISTER, null));
}
else
{
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Response")
- .details("ACK " + identType.getLabel())
- .identifiers(ic)
- .build());
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.RESPONSE, "ACK " + identType.getLabel()));
}
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
@@ -143,45 +125,15 @@ public void receive(IMessage message)
case ACKT:
case ACKV:
case ACKX:
- MutableIdentifierCollection icACK = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- icACK.remove(IdentifierClass.USER);
- icACK.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Acknowledge")
- .details(mpt.getMessageType().getDescription())
- .identifiers(icACK)
- .build());
-
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.ACKNOWLEDGE, mpt.getMessageType().getDescription()));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case AHYC:
- MutableIdentifierCollection icAHY = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- icAHY.remove(IdentifierClass.USER);
- icAHY.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Command")
- .details("Send Short Data Message")
- .identifiers(icAHY)
- .build());
-
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Short Data Message"));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case AHYQ:
- MutableIdentifierCollection icAHYQ = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- icAHYQ.remove(IdentifierClass.USER);
- icAHYQ.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Command")
- .details("Send Status Message")
- .identifiers(icAHYQ)
- .build());
-
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.COMMAND, "Send Status Message"));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
case ALH:
@@ -197,41 +149,22 @@ public void receive(IMessage message)
case GTC:
if(mMPT1327TrafficChannelManager != null)
{
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(mpt.getIdentifiers());
+ MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
mMPT1327TrafficChannelManager.processChannelGrant(mpt, ic);
}
else
{
MPT1327Channel channel = MPT1327Channel.create(mpt.getChannel());
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(mpt.getTimestamp())
- .eventDescription("Call Detect")
- .details(mpt.getMessage())
- .channel(channel)
- .identifiers(ic)
- .build());
+ DecodeEvent decodeEvent = getDecodeEvent(message, mpt, DecodeEventType.CALL_DETECT, mpt.getMessage());
+ decodeEvent.setChannelDescriptor(channel);
+ broadcast(decodeEvent);
}
break;
case HEAD_PLUS1:
case HEAD_PLUS2:
case HEAD_PLUS3:
case HEAD_PLUS4:
- MutableIdentifierCollection icHEAD = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- icHEAD.remove(IdentifierClass.USER);
- icHEAD.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(message.getTimestamp())
- .protocol(Protocol.MPT1327)
- .eventDescription("Short Data Message")
- .details(mpt.getMessage())
- .identifiers(icHEAD)
- .build());
-
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.SDM, mpt.getMessage()));
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL));
break;
@@ -250,14 +183,7 @@ public void receive(IMessage message)
// timeout specified by the user. Otherwise we'll be using the shorter default call timeout
broadcast(new ChangeChannelTimeoutEvent(this, mChannelType, mCallTimeoutMilliseconds));
- MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
- ic.remove(IdentifierClass.USER);
- ic.update(mpt.getIdentifiers());
-
- broadcast(DecodeEvent.builder(mpt.getTimestamp())
- .identifiers(ic)
- .eventDescription("Call In Progress")
- .build());
+ broadcast(getDecodeEvent(message, mpt, DecodeEventType.CALL_IN_PROGRESS, null));
broadcast(new DecoderStateEvent(this, Event.START, State.CALL));
}
break;
@@ -268,6 +194,22 @@ public void receive(IMessage message)
}
}
+ private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details)
+ {
+ MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt);
+ return MPT1327DecodeEvent.builder(decodeEventType, message.getTimestamp())
+ .details(details)
+ .identifiers(ic)
+ .build();
+ }
+
+ private MutableIdentifierCollection getUpdatedMutableIdentifierCollection(MPT1327Message mpt) {
+ MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers());
+ ic.remove(IdentifierClass.USER);
+ ic.update(mpt.getIdentifiers());
+ return ic;
+ }
+
public void reset()
{
super.reset();
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java
index bfd596aa4..159f57eed 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327MessageFilter.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.mpt1327;
@@ -23,56 +22,55 @@
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.*;
+import java.util.function.Function;
-public class MPT1327MessageFilter extends Filter
+/**
+ * Filter for MPT1327 messages
+ */
+public class MPT1327MessageFilter extends Filter
{
- private Map> 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(type));
- }
+ add(new FilterElement<>(type));
}
}
@Override
- public boolean passes(IMessage message)
+ public boolean canProcess(IMessage message)
{
- if(mEnabled && canProcess(message))
- {
- MPT1327Message mpt = (MPT1327Message) message;
-
- FilterElement element =
- mFilterElements.get(mpt.getMessageType());
-
- if(element != null)
- {
- return element.isEnabled();
- }
- }
-
- return false;
+ return message instanceof MPT1327Message && super.canProcess(message);
}
@Override
- public boolean canProcess(IMessage message)
+ public Function getKeyExtractor()
{
- return message instanceof MPT1327Message;
+ return mKeyExtractor;
}
- @Override
- public List> getFilterElements()
+ /**
+ * Key extractor
+ */
+ private class KeyExtractor implements Function
{
- return new ArrayList>(mFilterElements.values());
+ @Override
+ public MPT1327Message.MPTMessageType apply(IMessage message)
+ {
+ if(message instanceof MPT1327Message mpt)
+ {
+ return mpt.getMessageType();
+ }
+
+ return null;
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java
index 713600f02..9e1f45ce2 100644
--- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java
+++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327TrafficChannelManager.java
@@ -30,6 +30,7 @@
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.event.DecodeEvent;
+import io.github.dsheirer.module.decode.event.DecodeEventType;
import io.github.dsheirer.module.decode.event.IDecodeEvent;
import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel;
@@ -103,9 +104,9 @@ else if(mAllocatedTrafficChannelMap.containsKey(mpt1327Channel))
}
}
- MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp())
+ MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent
+ .mpt1327Builder(DecodeEventType.CALL, mpt1327Message.getTimestamp())
.channel(mpt1327Channel)
- .eventDescription("Call")
.details("Traffic Channel Grant")
.identifiers(identifierCollection)
.build();
@@ -123,7 +124,7 @@ else if(mAllocatedTrafficChannelMap.containsKey(mpt1327Channel))
if(trafficChannel == null)
{
channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
- channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription());
+ channelGrantEvent.setDetails("Detect:" + channelGrantEvent.getDetails());
return;
}
@@ -150,10 +151,8 @@ private void createTrafficChannels(Channel parentChannel)
DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration();
List 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++)
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java
index d6f55655a..4b15afadb 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25ChannelGrantEvent.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
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 P25ChannelGrantEvent(long timestamp)
* @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 static class P25ChannelGrantDecodeEventBuilder
{
protected long mTimeStart;
protected long mDuration;
- protected String mEventDescription;
protected DecodeEventType mDecodeEventType;
protected IdentifierCollection mIdentifierCollection;
protected IChannelDescriptor mChannelDescriptor;
@@ -88,8 +86,9 @@ public static class P25ChannelGrantDecodeEventBuilder
*
* @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 P25ChannelGrantDecodeEventBuilder channel(IChannelDescriptor channelDescr
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 P25ChannelGrantDecodeEventBuilder details(String details)
*/
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;
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java
index 8189e25d0..d22aeb4e5 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecodeEvent.java
@@ -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,24 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
- * *****************************************************************************
+ * ****************************************************************************
*/
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
{
/**
- * Constucts a P25 decode event
+ * 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 P25DecodeEvent(long start)
* @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;
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java
index ff3bcb849..5018f079d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25TrafficChannelManager.java
@@ -281,11 +281,9 @@ private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptio
{
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 @@ private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptio
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 @@ private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptio
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 @@ private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptio
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 @@ private void processPhase1ChannelGrant(APCO25Channel apco25Channel, ServiceOptio
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 @@ else if(timeslot == 1)
{
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 @@ else if(timeslot == 1)
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 @@ else if(timeslot == 1)
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 @@ else if(timeslot == 1)
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 @@ else if(timeslot == 1)
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 synchronized void receive(ChannelEvent channelEvent)
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);
}
});
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java
index 28bbfb512..1b4019bcb 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1DecoderState.java
@@ -654,10 +654,8 @@ private void processBroadcast(TSBKMessage tsbkMessage, DecodeEventType request,
private void processBroadcast(List 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 @@ private void processTDULC(P25Message message)
/**
* 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 @@ private void updateCurrentCall(DecodeEventType type, String details, long timest
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 @@ private void processTDU(P25Message message)
*/
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 @@ private void processPDU(P25Message message)
*/
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 @@ else if(message instanceof PacketMessage)
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 @@ else if(udpPayload instanceof MCGPPacket)
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 @@ else if(udpPayload instanceof LRRPPacket lrrpPacket)
{
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 @@ else if(udpPayload instanceof LRRPPacket lrrpPacket)
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 @@ else if(udpPayload instanceof LRRPPacket lrrpPacket)
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 @@ else if(ipPayload instanceof ICMPPacket)
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 @@ else if(ipPayload instanceof ICMPPacket)
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 @@ else if(packet instanceof UnknownPacket)
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 @@ private void processTSBKMotorolaOspDenyResponse(TSBKMessage tsbk) {
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()))
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/AMBTCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/AMBTCMessageFilter.java
deleted file mode 100644
index fe065b3cb..000000000
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/AMBTCMessageFilter.java
+++ /dev/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
- * *****************************************************************************
- */
-
-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 java.util.Collections;
-import java.util.List;
-
-public class AMBTCMessageFilter extends Filter
-{
- public AMBTCMessageFilter()
- {
- super("Alternate Multi-Block Trunking Control");
- }
-
- @Override
- public boolean passes(IMessage message)
- {
- return mEnabled && canProcess(message);
- }
-
- @Override
- public boolean canProcess(IMessage message)
- {
- return message instanceof AMBTCMessage;
- }
-
- @Override
- public List> getFilterElements()
- {
- return Collections.EMPTY_LIST;
- }
-}
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HeaderMessageFilter.java
similarity index 55%
rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HDUMessageFilter.java
rename to src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HeaderMessageFilter.java
index ccc837014..7e91bd051 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HDUMessageFilter.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/HeaderMessageFilter.java
@@ -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
- * *****************************************************************************
+ * ****************************************************************************
*/
package io.github.dsheirer.module.decode.p25.phase1.message.filter;
@@ -24,32 +23,51 @@
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
+/**
+ * Filter for HDU header messages
+ */
+public class HeaderMessageFilter extends Filter