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 8126e97b..8200516f 100644 --- a/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java +++ b/src/main/java/io/github/dsheirer/channel/metadata/NowPlayingPanel.java @@ -25,26 +25,21 @@ import io.github.dsheirer.gui.power.ChannelPowerPanel; import io.github.dsheirer.icon.IconModel; import io.github.dsheirer.module.decode.event.DecodeEventPanel; import io.github.dsheirer.module.decode.event.MessageActivityPanel; -import io.github.dsheirer.module.decode.event.filter.lastheard.LastHeardPanel; -import io.github.dsheirer.module.decode.event.filter.lastseen.LastSeenPanel; import io.github.dsheirer.playlist.PlaylistManager; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.settings.SettingsManager; import java.awt.Color; import net.miginfocom.swing.MigLayout; -import javax.swing.*; -import java.awt.*; +import javax.swing.JPanel; public class NowPlayingPanel extends JPanel { - private ChannelMetadataPanel mChannelMetadataPanel; - private ChannelDetailPanel mChannelDetailPanel; - private DecodeEventPanel mDecodeEventPanel; - private LastSeenPanel mLastSeenPanel; - private LastHeardPanel mLastHeardPanel; - private MessageActivityPanel mMessageActivityPanel; - private ChannelPowerPanel mChannelPowerPanel; + private final ChannelMetadataPanel mChannelMetadataPanel; + private final ChannelDetailPanel mChannelDetailPanel; + private final DecodeEventPanel mDecodeEventPanel; + private final MessageActivityPanel mMessageActivityPanel; + private final ChannelPowerPanel mChannelPowerPanel; /** * GUI panel that combines the currently decoding channels metadata table and viewers for channel details, @@ -55,8 +50,6 @@ public class NowPlayingPanel extends JPanel { mChannelDetailPanel = new ChannelDetailPanel(playlistManager.getChannelProcessingManager()); mDecodeEventPanel = new DecodeEventPanel(iconModel, userPreferences, playlistManager.getAliasModel()); - mLastSeenPanel = new LastSeenPanel(iconModel, userPreferences, playlistManager.getAliasModel()); - mLastHeardPanel = new LastHeardPanel(iconModel, userPreferences, playlistManager.getAliasModel()); mMessageActivityPanel = new MessageActivityPanel(userPreferences); mChannelMetadataPanel = new ChannelMetadataPanel(playlistManager, iconModel, userPreferences); mChannelPowerPanel = new ChannelPowerPanel(playlistManager, settingsManager); @@ -71,13 +64,15 @@ public class NowPlayingPanel extends JPanel JideTabbedPane tabbedPane = new JideTabbedPane(); tabbedPane.addTab("Details", mChannelDetailPanel); tabbedPane.addTab("Events", mDecodeEventPanel); - tabbedPane.addTab("Last Seen", mLastSeenPanel); - tabbedPane.addTab("Last Heard", mLastHeardPanel); tabbedPane.addTab("Messages", mMessageActivityPanel); tabbedPane.addTab("Channel", mChannelPowerPanel); tabbedPane.setFont(this.getFont()); tabbedPane.setForeground(Color.BLACK); + //Register state change listener to toggle visibility state for channel tab to turn-on/off FFT processing + tabbedPane.addChangeListener(e -> mChannelPowerPanel.setPanelVisible(tabbedPane.getSelectedIndex() == tabbedPane + .indexOfComponent(mChannelPowerPanel))); + JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT); splitPane.setShowGripper(true); splitPane.add(mChannelMetadataPanel); @@ -86,8 +81,6 @@ public class NowPlayingPanel extends JPanel mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelDetailPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mDecodeEventPanel); - mChannelMetadataPanel.addProcessingChainSelectionListener(mLastSeenPanel); - mChannelMetadataPanel.addProcessingChainSelectionListener(mLastHeardPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mMessageActivityPanel); mChannelMetadataPanel.addProcessingChainSelectionListener(mChannelPowerPanel); } diff --git a/src/main/java/io/github/dsheirer/filter/AllPassFilter.java b/src/main/java/io/github/dsheirer/filter/AllPassFilter.java index 945c5828..f94f19ed 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() + private static final String KEY = "Other/Unlisted"; + private final AllPassKeyExtractor mKeyExtractor = new AllPassKeyExtractor(); + + /** + * Constructor + * @param name to display for this filter + */ + public AllPassFilter(String name) + { + super(name); + add(new FilterElement<>(KEY)); + } + + @Override + public Function getKeyExtractor() { - super( "Allow All Messages" ); + return mKeyExtractor; } - - @Override - public boolean passes( T t ) - { - return true; - } - @Override - public boolean canProcess( T t ) - { - return true; - } - - @Override - @SuppressWarnings( "unchecked" ) - public List getFilterElements() - { - return Collections.EMPTY_LIST; - } + /** + * Key extractor that always returns the same (String)key. + */ + public class AllPassKeyExtractor implements Function + { + @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 9e84bd54..6f99a05b 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; - } + private static final Logger LOG = LoggerFactory.getLogger(Filter.class); + private String mName; + private Map> mFilterElementMap = new HashMap<>(); + private IFilterChangeListener mFilterChangeListener; - /** - * Indicates if this filter is enabled or disabled - */ - public boolean isEnabled() - { - return mEnabled; - } + /** + * Constructs an instance + * @param name of this filter + */ + public Filter(String name) + { + mName = name; + } - /** - * Enables (true) or disables (false) this filter. An enabled filter will - * evaluate messages and a disabled filter will return false on all message - * test methods. - */ - public void setEnabled( boolean enabled ) - { - mEnabled = enabled; - } - - /** - * List of filter elements managed by this filter - */ - public abstract List> getFilterElements(); + /** + * Indicates if this filter contains a filter element matching the key value. + * @param key to check + * @return true if this filter has a matching filter element. + */ + protected boolean hasKey(K key) + { + return mFilterElementMap.containsKey(key); + } - /** - * Name of this filter - */ - public String getName() - { - return mName; - } - - public void setName( String name ) - { - mName = name; - } - - public String toString() - { - return mName; - } + /** + * Key extractor function provided by the subclass. + * @return key extractor function + */ + public abstract Function 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 00000000..2d5ffec6 --- /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 d3842102..a1260a7e 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); + DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(filter); + mModel.insertNodeInto(childNode, parent, parent.getChildCount()); - mModel.insertNodeInto(child, parent, parent.getChildCount()); - - if(filter instanceof FilterSet) + if(filter instanceof FilterSet childFilterSet) { - addFilterSet((FilterSet) 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,123 +131,39 @@ public class FilterEditorPanel extends JPanel for(FilterElement element : elements) { DefaultMutableTreeNode child = new DefaultMutableTreeNode(element); - mModel.insertNodeInto(child, parent, parent.getChildCount()); } } - public class MouseHandler implements MouseListener + /** + * Accesses the tree's root tree node. + * @return root node. + */ + private DefaultMutableTreeNode getRoot() { - public MouseHandler() + if(mTree.getModel().getRoot() instanceof DefaultMutableTreeNode root) { + return root; } - @Override - public void mouseClicked(MouseEvent event) - { - if(SwingUtilities.isRightMouseButton(event)) - { - int row = mTree.getRowForLocation(event.getX(), - event.getY()); - - if(row != -1) - { - mTree.setSelectionRow(row); - - Object selectedNode = - mTree.getLastSelectedPathComponent(); - - if(selectedNode instanceof DefaultMutableTreeNode) - { - final DefaultMutableTreeNode node = - (DefaultMutableTreeNode) selectedNode; - - if(node.getChildCount() > 0) - { - JPopupMenu selectionMenu = new JPopupMenu(); - - JMenuItem selectAll = new JMenuItem("Select All"); - selectAll.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - setFilterEnabled(mModel, node, true); - } - }); - - selectionMenu.add(selectAll); - - JMenuItem deselectAll = new JMenuItem("Deselect All"); - deselectAll.addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - setFilterEnabled(mModel, node, false); - } - }); - - selectionMenu.add(deselectAll); - - selectionMenu.show(mTree, - event.getX(), - event.getY()); - } - } - } - } - } - - @Override - public void mousePressed(MouseEvent e) - { - } - - @Override - public void mouseReleased(MouseEvent e) - { - } - - @Override - public void mouseEntered(MouseEvent e) - { - } - - @Override - public void mouseExited(MouseEvent e) - { - } + return null; } /** - * Recursively sets the enabled state on all filter node children below the - * specified parent node. + * Recursively sets the enabled state on all filter node children below the specified parent node. * * @param model - model containing the tree nodes * @param node - parent node * @param enabled - true or false to enable or disable the filters */ - private void setFilterEnabled(DefaultTreeModel model, - DefaultMutableTreeNode node, - boolean enabled) + private void setFilterEnabled(DefaultTreeModel model, DefaultMutableTreeNode node, boolean enabled) { Object obj = node.getUserObject(); - if(obj instanceof FilterSet) + if(obj instanceof FilterElement filterElement) { - ((FilterSet) obj).setEnabled(enabled); + filterElement.setEnabled(enabled); } - else if(obj instanceof Filter) - { - ((Filter) obj).setEnabled(enabled); - } - else if(obj instanceof FilterElement) - { - ((FilterElement) obj).setEnabled(enabled); - } - - model.nodeChanged(node); /* Recursively set the children of this node */ Enumeration children = node.children(); @@ -228,43 +172,123 @@ public class FilterEditorPanel extends JPanel { Object child = children.nextElement(); - if(child instanceof DefaultMutableTreeNode) + if(child instanceof DefaultMutableTreeNode childNode) { - setFilterEnabled(model, (DefaultMutableTreeNode) child, enabled); + setFilterEnabled(model, childNode, enabled); } } + + model.nodeChanged(node); + } + + /** + * Recursively searches the tree to find the tree node containing the specified filter element. + * + * @param node to start search + * @param element to find + */ + private DefaultMutableTreeNode findFilterElementNode(DefaultMutableTreeNode node, FilterElement element) + { + if(node != null) + { + if(node.getUserObject() instanceof FilterElement selfFilterElement && selfFilterElement.equals(element)) + { + return node; + } + + for(int x = 0; x < node.getChildCount(); x++) + { + if(node.getChildAt(x) instanceof DefaultMutableTreeNode child) + { + DefaultMutableTreeNode found = findFilterElementNode(child, element); + + if(found != null) + { + return found; + } + } + } + } + + return null; + } + + /** + * Updates the display value for the specified node and all parents in the nodes parentage. + * @param node to update + */ + private void updateParentage(DefaultMutableTreeNode node) + { + mModel.nodeChanged(node); + + TreeNode parent = node.getParent(); + + while(parent != null) + { + mModel.nodeChanged(parent); + parent = parent.getParent(); + } } + /** + * Recursively searches the tree to find the tree node containing the specified filter. + * + * @param node to start search + * @param filter to find + */ + private DefaultMutableTreeNode findFilterNode(DefaultMutableTreeNode node, IFilter filter) + { + if(node != null) + { + if(node.getUserObject() instanceof IFilter selfFilter && selfFilter.equals(filter)) + { + return node; + } + + for(int x = 0; x < node.getChildCount(); x++) + { + if(node.getChildAt(x) instanceof DefaultMutableTreeNode child) + { + DefaultMutableTreeNode found = findFilterNode(child, filter); + + if(found != null) + { + return found; + } + } + } + } + else + { + System.out.println("Can't evaluate - node is null"); + } + + return null; + } + + /** + * Custom cell renderer + */ public class EditorTreeCellRenderer extends DefaultTreeCellRenderer { private static final long serialVersionUID = 1L; - public Component getTreeCellRendererComponent(JTree tree, - Object treeNode, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) + public Component getTreeCellRendererComponent(JTree tree, Object treeNode, boolean selected, boolean expanded, + boolean leaf, int row, boolean hasFocus) { if(treeNode instanceof DefaultMutableTreeNode) { Object userObject = ((DefaultMutableTreeNode) treeNode).getUserObject(); - JCheckBox checkBox = null; - if(userObject instanceof IFilter) + if(userObject instanceof IFilter filter) { - IFilter filter = (IFilter) userObject; - - checkBox = new JCheckBox(filter.getName()); + checkBox = new FilterCheckBox(filter); checkBox.setSelected(filter.isEnabled()); } - else if(userObject instanceof FilterElement) + else if(userObject instanceof FilterElement element) { - FilterElement element = (FilterElement) userObject; - - checkBox = new JCheckBox(element.getName()); + checkBox = new FilterElementCheckBox(element); checkBox.setSelected(element.isEnabled()); } @@ -285,95 +309,110 @@ public class FilterEditorPanel extends JPanel } } - return super.getTreeCellRendererComponent(tree, treeNode, selected, - expanded, leaf, row, hasFocus); + return super.getTreeCellRendererComponent(tree, treeNode, selected, expanded, leaf, row, hasFocus); } } + /** + * Check box used in filter tree nodes + */ public class FilterCheckBox extends JCheckBox { private static final long serialVersionUID = 1L; + private IFilter mFilter; - private IFilter mFilter; - - public FilterCheckBox(IFilter filter) + /** + * Constructs an instance + * + * @param filter + */ + public FilterCheckBox(IFilter filter) { - super(filter.getName()); - mFilter = filter; - setSelected(mFilter.isEnabled()); + addItemListener(e -> { + DefaultMutableTreeNode selfNode = findFilterNode(getRoot(), mFilter); - addItemListener(new ItemListener() - { - @Override - public void itemStateChanged(ItemEvent e) + if(selfNode != null) { - mFilter.setEnabled(FilterCheckBox.this.isSelected()); + setFilterEnabled(mModel, selfNode, FilterCheckBox.this.isSelected()); + updateLabel(); + updateParentage(selfNode); } }); } + + @Override + public void setSelected(boolean b) + { + super.setSelected(b); + updateLabel(); + } + + public void updateLabel() + { + setText(mFilter.getName() + " (" + mFilter.getEnabledCount() + "/" + mFilter.getElementCount() + ")"); + } } + /** + * Check box used in filter element tree nodes + */ public class FilterElementCheckBox extends JCheckBox { private static final long serialVersionUID = 1L; - private FilterElement mFilter; + /** + * Constructs an instance + * + * @param filter element + */ public FilterElementCheckBox(FilterElement filter) { super(filter.getName()); - mFilter = filter; - setSelected(mFilter.isEnabled()); - - addItemListener(new ItemListener() - { - @Override - public void itemStateChanged(ItemEvent arg0) + addItemListener(arg0 -> { + mFilter.setEnabled(FilterElementCheckBox.this.isSelected()); + DefaultMutableTreeNode self = findFilterElementNode(getRoot(), mFilter); + if(self != null) { - mFilter.setEnabled(FilterElementCheckBox.this.isSelected()); + updateParentage(self); } }); } } - public class FilterTreeNodeEditor implements TreeCellEditor + /** + * Editor for filter tree nodes + */ + public class FilterTreeCellEditor implements TreeCellEditor { @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - public Component getTreeCellEditorComponent(JTree tree, Object node, - boolean isSelected, boolean expanded, boolean leaf, int row) + public Component getTreeCellEditorComponent(JTree tree, Object node, boolean isSelected, boolean expanded, boolean leaf, int row) { - if(node instanceof DefaultMutableTreeNode) + if(node instanceof DefaultMutableTreeNode mutableTreeNode) { - Object userObject = ((DefaultMutableTreeNode) node).getUserObject(); + Object userObject = mutableTreeNode.getUserObject(); - if(userObject instanceof IFilter) + if(userObject instanceof IFilter filter) { - return new FilterCheckBox((IFilter) userObject); + return new FilterCheckBox(filter); } - else if(userObject instanceof FilterElement) + else if(userObject instanceof FilterElement filterElement) { - return new FilterElementCheckBox((FilterElement) userObject); + return new FilterElementCheckBox(filterElement); } } - return new JLabel(node.toString()); } @Override - public void addCellEditorListener(CellEditorListener l) - { - } + public void addCellEditorListener(CellEditorListener l) {} @Override - public void cancelCellEditing() - { - - } + public void cancelCellEditing() {} @Override public Object getCellEditorValue() @@ -388,9 +427,7 @@ public class FilterEditorPanel extends JPanel } @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 394b793c..9f95e6db 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(); - } + private boolean mEnabled; + private final T mElement; + private IFilterChangeListener mFilterChangeListener; - @Override - public int compareTo( FilterElement other ) + /** + * Constructs an instance + * + * @param element to track + * @param enabled enabled state of this filter element + */ + public FilterElement(T element, boolean enabled) { - return toString().compareTo( other.toString() ); + mElement = element; + mEnabled = enabled; } + /** + * Constructs an instance with a default state of enabled (true). + * + * @param t element to track + */ + public FilterElement(T t) + { + this(t, true); + } + + /** + * Registers the listener with each child filter element. + * + * @param listener to register + */ + public void register(IFilterChangeListener listener) + { + mFilterChangeListener = listener; + } + + /** + * Element being filtered. + * + * @return element + */ + public T getElement() + { + return mElement; + } + + /** + * Name or display string for the element. + * + * @return string version of the element. + */ + public String toString() + { + return getName(); + } + + /** + * Indicates if this filter is enabled indicating that elements of this type will pass this filter. + * + * @return enabled state. + */ + public boolean isEnabled() + { + return mEnabled; + } + + /** + * Sets the enabled state of this filter. + * + * @param enabled true to allow this filter's element type to pass the filter. + */ + public void setEnabled(boolean enabled) + { + mEnabled = enabled; + + if(mFilterChangeListener != null) + { + mFilterChangeListener.filterChanged(); + } + } + + /** + * Name of the tracked element. + * + * @return name + */ + public String getName() + { + return mElement.toString(); + } + + /** + * Implements compareto interface for ordering of filter elements. + * + * @param other the object to be compared. + * @return comparison value + */ @Override - public boolean equals(Object o) { - if (!(o instanceof FilterElement)) return false; - return compareTo((FilterElement) o) == 0; + public int compareTo(FilterElement 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 ef8a1f63..8609c491 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; + 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; - public FilterSet( String name ) - { - mName = name; - } - - public FilterSet() - { - mName = "Message Filter"; - } - - public FilterSet(IFilter filter) + /** + * Constructs an instance of a filter set. + * + * @param name of this filter set. + */ + public FilterSet(String name) { + mName = name; + } + + /** + * Constructs an instance with a single filter using the default 'Filters' name. + * + * @param filter for this filter set. + */ + public FilterSet(IFilter filter) + { + this("Filters"); addFilter(filter); } - - public String getName() - { - return mName; - } - - public void setName( String name ) - { - mName = name; - } - - public String toString() - { - return getName(); - } - - @Override - public boolean passes( T t ) - { - if( mEnabled ) - { - for( IFilter filter: mFilters ) - { - // Make sure to loop through all filters. - // Only return if true or all filters have been evaluated. - if( filter.canProcess( t ) && filter.passes( t )) - { - return true; - } - } - } - return false; - } - @Override - public boolean canProcess( T t ) - { - if( mEnabled ) + /** + * Registers the listener with each child filter element. + * @param listener to register + */ + public void register(IFilterChangeListener listener) + { + mFilterChangeListener = listener; + + for(IFilter child: mFilters) { - for( IFilter filter: mFilters ) + child.register(listener); + } + } + + /** + * Name of this filter set. + * + * @return name + */ + public String getName() + { + return mName; + } + + /** + * Sets the name of this filter set. + * + * @param name to set + */ + public void setName(String name) + { + mName = name; + } + + /** + * Name for this filter set. + * + * @return name + */ + public String toString() + { + return getName(); + } + + /** + * Indicates if the element passes at least one of the child filters in this filter set. + * + * @param t to evaluate + * @return true if the argument passes a child filter. + */ + @Override + public boolean passes(T t) + { + for(IFilter filter : mFilters) + { + //Stop at the first filter that says that it can process the message type. + if(filter.canProcess(t)) { - if( filter.canProcess( t ) ) - { - return true; - } + return filter.passes(t); } } - + return false; - } - + } + + /** + * Indicates if this filter set contains at least one filter that can evaluate the type of argument that is presented. + * + * @param t element to evaluate + * @return true if this filter set can evaluate objects of the argument's type. + */ + @Override + public boolean canProcess(T t) + { + for(IFilter filter : mFilters) + { + if(filter.canProcess(t)) + { + return true; + } + } + + sLoggingSuppressor.info(t.getClass().getSimpleName(), 1, "FilterSet [" + + this.getClass().getSimpleName() + "] has no filter element for items of type [" + + t.getClass().getSimpleName() + "]"); + + return false; + } + + /** + * Child filters + * + * @return filters + */ public List> getFilters() { - return mFilters; - } - - public void addFilters( List> filters ) - { - mFilters.addAll( filters ); - } - - public void addFilter( IFilter filter ) - { - mFilters.add( filter ); - } - - public void removeFilter( IFilter filter ) - { - mFilters.remove( filter ); + return new ArrayList<>(mFilters); } - @Override - public boolean isEnabled() - { - return mEnabled; - } + /** + * Adds a list of child filters to this filter set. + * @param filters to add + */ + public void addFilters(List> filters) + { + for(IFilter filter: filters) + { + addFilter(filter); + } + } + /** + * Adds the child filter to this filter set. + * @param filter to add + */ + public void addFilter(IFilter filter) + { + if(filter instanceof Filter filterInstance && filterInstance.getKeyExtractor() == null) + { + LOGGER.warn("Filter [" + filter.getClass() + "] has a null key extractor"); + } + + mFilters.add(filter); + filter.register(mFilterChangeListener); + } + + /** + * Indicates if any of the child filters contain at least one filter element that is enabled. + * @return enabled state + */ @Override - public void setEnabled( boolean enabled ) - { - mEnabled = enabled; - } + public boolean isEnabled() + { + for(IFilter filter: mFilters) + { + if(filter.isEnabled()) + { + return true; + } + } + + return false; + } + + /** + * Recursive count of enabled child filter elements. + * @return count + */ + @Override + public int getEnabledCount() + { + int count = 0; + + for(IFilter filter: mFilters) + { + count += filter.getEnabledCount(); + } + + return count; + } + + /** + * Recursive count of child filter elements. + * @return count + */ + @Override + public int getElementCount() + { + int count = 0; + + for(IFilter filter: mFilters) + { + count += filter.getElementCount(); + } + + return count; + } } diff --git a/src/main/java/io/github/dsheirer/filter/IFilter.java b/src/main/java/io/github/dsheirer/filter/IFilter.java index b8596a29..1cba9320 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 ); + /** + * Generic filter method. + * + * @param t - message to filter + * @return - true if the message passes the filter + */ + boolean passes(T t); - /** - * Indicates if the filter can process (filter) the object - * - * @param message - candidate message for filtering - * - * @return - true if the filter is capable of filtering the message - */ - public boolean canProcess( T t ); + /** + * Indicates if the filter can process (filter) the object + * + * @param t - candidate message for filtering + * @return - true if the filter is capable of filtering the message + */ + boolean canProcess(T t); - /** - * Indicates if this filter is enabled to evaluate messages - */ - public boolean isEnabled(); + /** + * Indicates if this filter is enabled to evaluate messages + */ + boolean isEnabled(); - /** - * Sets the enabled state of the filter - */ - public void setEnabled( boolean enabled ); - - /** - * Display name for the filter - */ - public String getName(); + /** + * Display name for the filter + */ + String getName(); + + /** + * (Recursive) count of enabled child filter elements. + * + * @return total enable count. + */ + int getEnabledCount(); + + /** + * (Recursive) count of total child filter elements. + * + * @return total count of child filter elements. + */ + int getElementCount(); + + /** + * Registers the listener to be notified when the enabled state of any child filter elements is updated. + * + * @param listener to be notified + */ + void register(IFilterChangeListener listener); } diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java similarity index 66% rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java rename to src/main/java/io/github/dsheirer/filter/IFilterChangeListener.java index a9daa12e..db36f784 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryRequest.java +++ b/src/main/java/io/github/dsheirer/filter/IFilterChangeListener.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 @@ -17,19 +17,9 @@ * **************************************************************************** */ -package io.github.dsheirer.module.decode.event.filter.lastseen; +package io.github.dsheirer.filter; -import io.github.dsheirer.module.ModuleEventBusMessage; - -/** - * Request for decode event history from a processing chain for the specified channel. - */ -public class LastSeenHistoryRequest extends ModuleEventBusMessage +public interface IFilterChangeListener { - /** - * Constructs an instance - */ - public LastSeenHistoryRequest() - { - } + void filterChanged(); } 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 be973b5c..e33d5386 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 class SyncLossMessageFilter extends Filter 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 b79cb36b..accc47ad 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 @@ package io.github.dsheirer.module; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; - import java.util.ArrayList; import java.util.List; @@ -50,7 +49,7 @@ public abstract class HistoryModule extends Module implements Listener */ 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 cf9935a9..13b6d3e3 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 551f5994..20a6729d 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.DMRDecoderState; import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager; import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR; import io.github.dsheirer.module.decode.dmr.audio.DMRAudioModule; +import io.github.dsheirer.module.decode.dmr.message.filter.DmrMessageFilterSet; import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2Decoder; import io.github.dsheirer.module.decode.fleetsync2.Fleetsync2DecoderState; import io.github.dsheirer.module.decode.fleetsync2.FleetsyncMessageFilter; @@ -80,16 +81,18 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1; import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderC4FM; import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderLSM; import io.github.dsheirer.module.decode.p25.phase1.P25P1DecoderState; -import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25MessageFilterSet; +import io.github.dsheirer.module.decode.p25.phase1.message.filter.P25P1MessageFilterSet; import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2; import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderHDQPSK; import io.github.dsheirer.module.decode.p25.phase2.P25P2DecoderState; +import io.github.dsheirer.module.decode.p25.phase2.message.filter.P25P2MessageFilterSet; import io.github.dsheirer.module.decode.passport.DecodeConfigPassport; import io.github.dsheirer.module.decode.passport.PassportDecoder; import io.github.dsheirer.module.decode.passport.PassportDecoderState; import io.github.dsheirer.module.decode.passport.PassportMessageFilter; import io.github.dsheirer.module.decode.tait.Tait1200Decoder; import io.github.dsheirer.module.decode.tait.Tait1200DecoderState; +import io.github.dsheirer.module.decode.tait.Tait1200MessageFilter; import io.github.dsheirer.module.decode.traffic.TrafficChannelManager; import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule; import io.github.dsheirer.preference.UserPreferences; @@ -494,7 +497,7 @@ public class DecoderFactory */ public static FilterSet getMessageFilters(List modules) { - FilterSet filterSet = new FilterSet<>(); + FilterSet filterSet = new FilterSet<>("Message Filters"); for(Module module : modules) { @@ -504,28 +507,28 @@ public class DecoderFactory } } - /* If we don't have any filters, add an ALL-PASS filter */ - if(filterSet.getFilters().isEmpty()) - { - filterSet.addFilter(new AllPassFilter<>()); - } + //Add an all-others filter as a catch-all for anything that isn't handled by the decoder filters. + filterSet.addFilter(new AllPassFilter<>("All Other Messages Filter")); return filterSet; } /** * Returns a set of IMessageFilter objects (FilterSets or Filters) that - * can process all of the messages produced by the specified decoder type. + * can process all messages produced by the specified decoder type. */ public static List> 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 class DecoderFactory filters.add(new MPT1327MessageFilter()); break; case P25_PHASE1: - filters.add(new P25MessageFilterSet()); + filters.add(new P25P1MessageFilterSet()); + break; + case P25_PHASE2: + filters.add(new P25P2MessageFilterSet()); break; case PASSPORT: filters.add(new PassportMessageFilter()); break; - case DMR: - //filters.add(new DMR) //todo: not finished + case TAIT_1200: + filters.add(new Tait1200MessageFilter()); break; default: break; 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 b07fd1f4..c2bb750a 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.Identifier; import io.github.dsheirer.identifier.IdentifierCollection; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.p25.identifier.channel.StandardChannel; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.ISourceEventListener; @@ -100,9 +101,8 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource if(mDecodeEvent == null) { - mDecodeEvent = DecodeEvent.builder(System.currentTimeMillis()) + mDecodeEvent = DecodeEvent.builder(DecodeEventType.CALL, System.currentTimeMillis()) .channel(mChannelDescriptor) - .eventDescription("CALL") .details(getDecoderType().name()) .identifiers(new IdentifierCollection(getIdentifierCollection().getIdentifiers())) .build(); 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 ea5a64f6..af0812a8 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 @@ package io.github.dsheirer.module.decode.dcs; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.function.Function; /** * Message filter for Digital Coded Squelch (DCS) */ -public class DCSMessageFilter extends Filter +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)); - } - - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) - { - return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled(); - } - - return false; + super("DCS Messages"); + add(new FilterElement(DCS_KEY)); } + /** + * Indicates that this filter can process DCS messages. + * @param message to test + * @return true if the message can be processed + */ @Override public boolean canProcess(IMessage message) { - return message instanceof DCSMessage; + return message instanceof DCSMessage && super.canProcess(message); } + /** + * Key extractor that always returns the same key constant. + * @return extractor function. + */ @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @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 e28b4e7f..4bfd1b65 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 @@ public class DMRDecoderState extends TimeslotDecoderState { broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot())); - DecodeEvent smsEvent = DMRDecodeEvent.builder(sms.getTimestamp()) - .eventType(DecodeEventType.SMS) - .eventDescription(DecodeEventType.SMS.toString()) + DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp()) .details("MESSAGE: " + sms.getSMS()) .identifiers(new IdentifierCollection(sms.getIdentifiers())) .timeslot(sms.getTimeslot()) @@ -312,9 +310,7 @@ public class DMRDecoderState extends TimeslotDecoderState mic.update(hyteraSmsPacket.getSource()); mic.update(hyteraSmsPacket.getDestination()); - DecodeEvent smsEvent = DMRDecodeEvent.builder(packet.getTimestamp()) - .eventDescription(DecodeEventType.SMS.name()) - .eventType(DecodeEventType.DATA_PACKET) + DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp()) .identifiers(mic) .timeslot(packet.getTimeslot()) .details("SMS:" + hyteraSmsPacket.getSMS()) @@ -326,9 +322,7 @@ public class DMRDecoderState extends TimeslotDecoderState { MutableIdentifierCollection mic = new MutableIdentifierCollection(packet.getIdentifiers()); - DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(packet.getTimestamp()) - .eventDescription(DecodeEventType.UNKNOWN_PACKET.name()) - .eventType(DecodeEventType.DATA_PACKET) + DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp()) .identifiers(mic) .timeslot(packet.getTimeslot()) .details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString()) @@ -337,9 +331,7 @@ public class DMRDecoderState extends TimeslotDecoderState } else { - DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp()) - .eventDescription(DecodeEventType.DATA_PACKET.name()) - .eventType(DecodeEventType.DATA_PACKET) + DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp()) .identifiers(getMergedIdentifierCollection(packet.getIdentifiers())) .timeslot(packet.getTimeslot()) .details(packet.toString()) @@ -350,9 +342,8 @@ public class DMRDecoderState extends TimeslotDecoderState GeoPosition geoPosition = PacketUtil.extractGeoPosition(packet.getPacket()); if (geoPosition != null) { - PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(packet.getTimestamp()) + PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, packet.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.GPS.toString()) .identifiers(getMergedIdentifierCollection(packet.getIdentifiers())) .protocol(Protocol.LRRP) .location(geoPosition) @@ -784,9 +775,8 @@ public class DMRDecoderState extends TimeslotDecoderState broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot())); break; case MOTOROLA_CONPLUS_REGISTRATION_REQUEST: - DecodeEvent event = DMRDecodeEvent.builder(csbk.getTimestamp()) + DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp()) .details("Registration Request") - .eventDescription(DecodeEventType.REGISTER.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .build(); @@ -794,9 +784,8 @@ public class DMRDecoderState extends TimeslotDecoderState broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot())); break; case MOTOROLA_CONPLUS_REGISTRATION_RESPONSE: - DecodeEvent regRespEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) + DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp()) .details("Registration Response") - .eventDescription(DecodeEventType.REGISTER.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .build(); @@ -837,9 +826,8 @@ public class DMRDecoderState extends TimeslotDecoderState broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot())); break; case MOTOROLA_CONPLUS_TALKGROUP_AFFILIATION: - DecodeEvent affiliateEvent = DMRDecodeEvent.builder(csbk.getTimestamp()) + DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp()) .details("TALKGROUP AFFILIATION") - .eventDescription(DecodeEventType.AFFILIATE.toString()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .build(); @@ -853,20 +841,16 @@ public class DMRDecoderState extends TimeslotDecoderState } private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) { - return DMRDecodeEvent.builder(csbk.getTimestamp()) + return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp()) .channel(channel) - .eventType(decodeEventType) .details(csbk.getOpcode().getLabel()) - .eventDescription(decodeEventType.toString()) .identifiers(mergedIdentifiers) .timeslot(channel.getTimeslot()) .build(); } private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) { - return DMRDecodeEvent.builder(csbk.getTimestamp()) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) + return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp()) .identifiers(getMergedIdentifierCollection(csbk.getIdentifiers())) .timeslot(csbk.getTimeslot()) .details(details) @@ -1119,8 +1103,7 @@ public class DMRDecoderState extends TimeslotDecoderState MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); ic.update(gpsInformation.getGPSLocation()); - DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp()) - .eventDescription(DecodeEventType.GPS.toString()) + DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp()) .identifiers(ic) .timeslot(message.getTimeslot()) .details("LOCATION:" + gpsInformation.getGPSLocation()) @@ -1151,9 +1134,8 @@ public class DMRDecoderState extends TimeslotDecoderState if(mCurrentCallEvent == null) { - mCurrentCallEvent = DMRDecodeEvent.builder(timestamp) + mCurrentCallEvent = DMRDecodeEvent.builder(type, timestamp) .channel(getCurrentChannel()) - .eventDescription(type.toString()) .details(details) .identifiers(getIdentifierCollection().copyOf()) .build(); @@ -1164,7 +1146,6 @@ public class DMRDecoderState extends TimeslotDecoderState { if(type != DecodeEventType.CALL) { - mCurrentCallEvent.setEventDescription(type.toString()); mCurrentCallEvent.setDetails(details); } 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 7d322ea3..eea406ff 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 @@ -329,10 +329,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I if(isStale(event, timestamp, identifierCollection)) //Create new event { - event = DMRChannelGrantEvent.channelGrantBuilder(timestamp) + event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp) .channel(channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : "")) .identifiers(identifierCollection) .build(); @@ -351,11 +349,9 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I { event.end(timestamp); - event = DMRChannelGrantEvent.channelGrantBuilder(timestamp) + event = DMRChannelGrantEvent.channelGrantBuilder(decodeEventType, timestamp) .channel(channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString() + " - Continue") - .details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : "")) + .details("CONTINUE - CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : "")) .identifiers(identifierCollection) .build(); @@ -392,15 +388,6 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I { if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode()) { - if(event.getEventDescription() == null) - { - event.setEventDescription(decodeEventType + IGNORED); - } - else if(!event.getEventDescription().endsWith(IGNORED)) - { - event.setEventDescription(event.getEventDescription() + IGNORED); - } - if(event.getDetails() == null) { event.setDetails(DATA_CALL_IGNORED); 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 94bf729a..c9ecddc1 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,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.dmr.event; @@ -34,11 +33,12 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent /** * Constructs an instance + * @param decodeEventType for the event * @param timestamp for the event */ - public DMRChannelGrantEvent(long timestamp) + public DMRChannelGrantEvent(DecodeEventType decodeEventType, long timestamp) { - super(timestamp); + super(decodeEventType, timestamp); } /** @@ -63,9 +63,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent * @param timeStart for the event * @return builder */ - public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(long timeStart) + public static DMRChannelGrantDecodeEventBuilder channelGrantBuilder(DecodeEventType decodeEventType, long timeStart) { - return new DMRChannelGrantDecodeEventBuilder(timeStart); + return new DMRChannelGrantDecodeEventBuilder(decodeEventType, timeStart); } /** @@ -76,7 +76,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent protected long mTimeStart; protected long mDuration; protected DecodeEventType mDecodeEventType; - protected String mEventDescription; protected IdentifierCollection mIdentifierCollection; protected DMRChannel mChannel; protected String mDetails; @@ -87,8 +86,9 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent * * @param timeStart */ - public DMRChannelGrantDecodeEventBuilder(long timeStart) + public DMRChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart) { + mDecodeEventType = decodeEventType; mTimeStart = timeStart; } @@ -131,25 +131,6 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent return this; } - /** - * Sets the Decode Event type for this event. - * @param eventType - */ - public DMRChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) { - mDecodeEventType = eventType; - return this; - } - - /** - * Sets the event description text - * @param description of the event - */ - public DMRChannelGrantDecodeEventBuilder eventDescription(String description) - { - mEventDescription = description; - return this; - } - /** * Sets the identifier collection. * @param identifierCollection containing optional identifiers like TO, FROM, frequency and @@ -176,13 +157,11 @@ public class DMRChannelGrantEvent extends DMRDecodeEvent */ public DMRChannelGrantEvent build() { - DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mTimeStart); + DMRChannelGrantEvent decodeEvent = new DMRChannelGrantEvent(mDecodeEventType, mTimeStart); decodeEvent.setChannelDescriptor(mChannel); decodeEvent.setTimeslot(mChannel.getTimeslot()); decodeEvent.setDetails(mDetails); decodeEvent.setDuration(mDuration); - decodeEvent.setEventType(mDecodeEventType); - decodeEvent.setEventDescription(mEventDescription); decodeEvent.setIdentifierCollection(mIdentifierCollection); return decodeEvent; } 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 a69f0ab1..218fd5dd 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 class DMRDecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.DMR); return decodeEventBuilder; } 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 cdccf419..ec8a601c 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 enum Opcode */ public boolean isDataChannelGrantOpcode() { - return DATA_CHANNEL_GRANT_OPCODES.contains(this); + return DATA_CHANNEL_GRANTS.contains(this); } /** - * Data Channel Grant opcodes + * Data channel grant opcodes */ - public static final EnumSet 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 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 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 ee1a51f5..a35dab54 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 667661f7..cb444b37 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 class PacketSequenceMessageFactory 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 00000000..b0eec20c --- /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 00000000..fb8b7ccb --- /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 00000000..928ab8a0 --- /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/event/filter/lastheard/LastHeardHistoryResponse.java b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java similarity index 53% rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryResponse.java rename to src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.java index cfedfdbd..dc852e0c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryResponse.java +++ b/src/main/java/io/github/dsheirer/module/decode/dmr/message/filter/DmrMessageFilterSet.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 @@ -17,31 +17,29 @@ * **************************************************************************** */ -package io.github.dsheirer.module.decode.event.filter.lastheard; +package io.github.dsheirer.module.decode.dmr.message.filter; -import io.github.dsheirer.module.ModuleEventBusMessage; +import io.github.dsheirer.filter.FilterSet; +import io.github.dsheirer.filter.SyncLossMessageFilter; +import io.github.dsheirer.message.IMessage; /** - * Response to decode event history request from a processing chain for the specified channel. + * Filter set for DMR messages */ -public class LastHeardHistoryResponse extends ModuleEventBusMessage +public class DmrMessageFilterSet extends FilterSet { - private LastHeardHistory mLastHeardHistory; - /** - * Constructs an instance - * @param history that has a decode event history + * Constructor */ - public LastHeardHistoryResponse(LastHeardHistory history) + public DmrMessageFilterSet() { - mLastHeardHistory = history; - } - - /** - * Decode event history module - */ - public LastHeardHistory getDecodeEventHistory() - { - return mLastHeardHistory; + super("DMR Messages"); + addFilter(new ControlMessageFilterSet()); + addFilter(new VoiceMessageFilter()); + addFilter(new DataMessageFilter()); + addFilter(new DmrPacketSequenceFilter()); + addFilter(new LinkControlMessageFilterSet()); + addFilter(new SyncLossMessageFilter()); + addFilter(new DmrOtherMessageFilter()); } } 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 00000000..339edc3e --- /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 00000000..83cfc096 --- /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 00000000..708dc8e2 --- /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 00000000..20191f12 --- /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 00000000..8eab4622 --- /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 29eac064..9ee71138 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 00000000..c1a2af37 --- /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 130d879b..4cc9cbf9 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 class DecodeEvent implements IDecodeEvent mTimeEnd = mTimeStart + duration; } - /** - * Event description - */ - @Override - public String getEventDescription() - { - return mEventDescription; - } - - /** - * Sets the event description text - */ - public void setEventDescription(String description) - { - mEventDescription = description; - } - /** * {@link DecodeEventType} */ @@ -140,13 +129,6 @@ public class DecodeEvent implements IDecodeEvent return mDecodeEventType; } - /** - * Sets the {@link DecodeEventType} - */ - public void setEventType(DecodeEventType eventType) { - this.mDecodeEventType = eventType; - } - /** * Identifier collection for this event. */ @@ -222,24 +204,24 @@ public class DecodeEvent implements IDecodeEvent * @return timeslot or default value of 0 */ @Override - public Integer getTimeslot() + public int getTimeslot() { return mTimeslot; } /** - * Indicates if this event specifies a timeslot + * Indicates if this event specifies a timeslot that is not negative */ public boolean hasTimeslot() { - return mTimeslot != null; + return mTimeslot >= 0; } /** * Sets the timeslot for this event * @param timeslot of the event */ - public void setTimeslot(Integer timeslot) + public void setTimeslot(int timeslot) { mTimeslot = timeslot; } @@ -249,7 +231,7 @@ public class DecodeEvent implements IDecodeEvent { StringBuilder sb = new StringBuilder(); sb.append(mProtocol); - sb.append(" DECODE EVENT: ").append(getEventDescription()); + sb.append(" DECODE EVENT: ").append(getEventType()); sb.append(" DETAILS:").append(getDetails()); if(mIdentifierCollection != null) { @@ -267,19 +249,19 @@ public class DecodeEvent implements IDecodeEvent { protected long mTimeStart; protected long mDuration; - protected String mEventDescription; protected DecodeEventType mDecodeEventType; protected IdentifierCollection mIdentifierCollection; protected IChannelDescriptor mChannelDescriptor; protected String mDetails; protected Protocol mProtocol = Protocol.UNKNOWN; - protected Integer mTimeslot; + protected int mTimeslot = -1; /** * Constructs a builder instance with the specified start time in milliseconds */ - public DecodeEventBuilder(long timeStart) + public DecodeEventBuilder(DecodeEventType decodeEventType, long timeStart) { + mDecodeEventType = decodeEventType; mTimeStart = timeStart; } @@ -313,25 +295,6 @@ public class DecodeEvent implements IDecodeEvent return this; } - /** - * Sets the Decode Event type for this event. - * @param eventType - */ - public DecodeEventBuilder eventType(DecodeEventType eventType) { - mDecodeEventType = eventType; - return this; - } - - /** - * Sets the event description text - * @param description of the event - */ - public DecodeEventBuilder eventDescription(String description) - { - mEventDescription = description; - return this; - } - /** * Sets the identifier collection. * @param identifierCollection containing optional identifiers like TO, FROM, frequency and @@ -363,7 +326,12 @@ public class DecodeEvent implements IDecodeEvent return this; } - public DecodeEventBuilder timeslot(Integer timeslot) + /** + * Sets the timeslot for this event + * @param timeslot + * @return + */ + public DecodeEventBuilder timeslot(int timeslot) { mTimeslot = timeslot; return this; @@ -374,12 +342,10 @@ public class DecodeEvent implements IDecodeEvent */ public DecodeEvent build() { - DecodeEvent decodeEvent = new DecodeEvent(mTimeStart); + DecodeEvent decodeEvent = new DecodeEvent(mDecodeEventType, mTimeStart); decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setDetails(mDetails); decodeEvent.setDuration(mDuration); - decodeEvent.setEventType(mDecodeEventType); - decodeEvent.setEventDescription(mEventDescription); decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setProtocol(mProtocol); decodeEvent.setTimeslot(mTimeslot); 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 697391dc..b677a125 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 @@ package io.github.dsheirer.module.decode.event; import com.google.common.eventbus.Subscribe; import io.github.dsheirer.channel.IChannelDescriptor; import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.filter.FilterSet; import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.module.decode.event.filter.DecodeEventFilterSet; -import io.github.dsheirer.module.decode.event.filter.EventFilterProvider; import io.github.dsheirer.preference.PreferenceType; import io.github.dsheirer.sample.Listener; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; +import java.awt.EventQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.table.AbstractTableModel; - -public class DecodeEventModel extends AbstractTableModel implements Listener, 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,72 +59,10 @@ public class DecodeEventModel extends AbstractTableModel implements Listener getEvents() - { - return new ArrayList<>(mEvents); - } - - public void dispose() - { - MyEventBus.getGlobalEventBus().unregister(this); - Iterator it = mEvents.iterator(); - - while(it.hasNext()) - { - it.remove(); - } - } - - public void clear() - { - mEvents.clear(); - fireTableDataChanged(); - } - - /** - * Clears all events from this model and loads the events argument - */ - public void clearAndSet(List events) - { - mEvents.clear(); - mEvents.addAll(events); - fireTableDataChanged(); - } - - public void reset() - { - dispose(); - fireTableDataChanged(); - } - - public int getMaxMessageCount() - { - return mMaxMessages; - } - - public void setMaxMessageCount(int count) - { - mMaxMessages = count; } /** @@ -146,49 +73,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener 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 class DecodeEventModel extends AbstractTableModel 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 class DecodeEventPanel extends JPanel implements Listener(mEventModel); + mTableRowSorter.setRowFilter(new EventRowFilter()); + mTable.setRowSorter(mTableRowSorter); mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - mEventManagementPanel = new EventManagementPanel(); mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); updateCellRenderers(); - - //Disabled until #1368 decode event is fully implemented -// add(mEventManagementPanel, "span,growx"); - + mHistoryManagementPanel = new HistoryManagementPanel<>(mEventModel, "Event Filter Editor"); + mHistoryManagementPanel.updateFilterSet(mFilterSet); + add(mHistoryManagementPanel, "span,growx"); mEmptyScroller = new JScrollPane(mTable); add(mEmptyScroller); + + //Register filter change listener to refresh the table any time the event filters are changed. + mFilterSet.register(() -> mEventModel.fireTableDataChanged()); } public void dispose() @@ -124,22 +129,14 @@ public class DecodeEventPanel extends JPanel implements Listener 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 class DecodeEventPanel extends JPanel implements Listener + { + @Override + public boolean include(Entry 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 3eb72bbd..51b61733 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 @@ -14,11 +14,17 @@ * * 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.util.Arrays; +import java.util.EnumSet; + +/** + * Enumeration of event types for decoded events. + */ public enum DecodeEventType { AFFILIATE("Affiliate"), @@ -43,6 +49,7 @@ public enum DecodeEventType CALL_UNIT_TO_UNIT_ENCRYPTED("Encrypted Unit To Unit Call"), CALL_NO_TUNER("Call - No Tuner"), CALL_TIMEOUT("Call Timeout"), + CELLOCATOR("Cellocator"), COMMAND("Command"), DATA_CALL("Data Call"), DATA_CALL_ENCRYPTED("Encrypted Data Call"), @@ -55,6 +62,7 @@ public enum DecodeEventType ID_ANI("ANI"), ID_UNIQUE("Unique ID"), IP_PACKET("IP Packet"), + LRRP("Motorola LRRP"), NOTIFICATION("Notification"), PAGE("Page"), QUERY("Query"), @@ -74,16 +82,82 @@ public enum DecodeEventType private String mLabel; + /** + * Encrypted voice call event types for filtering + */ + public static final EnumSet 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 00000000..ad76b60d --- /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 ea3da208..7fcf66c5 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 615b3a45..f5ecd4f2 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<>()); - + /** + * Constructor + */ public MessageActivityModel() { } /** - * Applies the filter set + * Implements the listener interface and wraps the IMessage in table-compatible message item wrapper. + * @param message to add to the model */ - public void setFilters(FilterSet filterSet) - { - mMessageFilterSet = filterSet; - } - - public void clearFilters() - { - mMessageFilterSet = new FilterSet<>(new AllPassFilter<>()); - } - - /** - * Clears all messages from history - */ - public void clear() - { - EventQueue.invokeLater(() -> { - mMessageItems.clear(); - fireTableDataChanged(); - }); - } - - /** - * Clears the current messages and loads the messages argument - */ - public void clearAndSet(List 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 class MessageActivityModel extends AbstractTableModel implements Listener return; } - if(mMessageFilterSet.passes(message)) - { - final MessageItem messageItem = new MessageItem(message); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mMessageItems.addFirst(messageItem); - - MessageActivityModel.this.fireTableRowsInserted(0, 0); - - prune(); - } - }); - } - } - - private void prune() - { - while(mMessageItems.size() > mMaxMessages) - { - MessageItem removed = mMessageItems.removeLast(); - removed.dispose(); - super.fireTableRowsDeleted(mMessageItems.size() - 1, mMessageItems.size() - 1); - } - } - - @Override - public int getRowCount() - { - return mMessageItems.size(); + EventQueue.invokeLater(() -> add(new MessageItem(message))); } @Override @@ -184,20 +74,20 @@ public class MessageActivityModel extends AbstractTableModel implements Listener @Override public Object getValueAt(int rowIndex, int columnIndex) { - if(0 <= rowIndex && rowIndex < mMessageItems.size()) - { - MessageItem messageItem = mMessageItems.get(rowIndex); + MessageItem item = getItem(rowIndex); + if(item != null) + { switch(columnIndex) { case TIME: - return messageItem.getTimestamp(mSDFTime); + return item.getTimestamp(mSDFTime); case PROTOCOL: - return messageItem.getProtocol(); + return item.getProtocol(); case TIMESLOT: - return messageItem.getTimeslot(); + return item.getTimeslot(); case MESSAGE: - return messageItem.getText(); + return item.getText(); default: break; } 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 4e07d9fe..479c1d79 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.module.decode.DecoderFactory; import io.github.dsheirer.preference.UserPreferences; import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; import io.github.dsheirer.sample.Listener; +import java.util.ArrayList; +import java.util.List; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JSlider; import javax.swing.JTable; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; +import javax.swing.RowFilter; +import javax.swing.table.TableModel; +import javax.swing.table.TableRowSorter; +/** + * Panel to display decoded messages/activity. + */ public class MessageActivityPanel extends JPanel implements Listener { 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]")); + mHistoryManagementPanel = new HistoryManagementPanel<>(mMessageModel, "Message Filter Editor"); + add(mHistoryManagementPanel, "span,growx"); + add(new JScrollPane(mTable), "span,grow"); } /** @@ -89,182 +84,62 @@ public class MessageActivityPanel extends JPanel implements Listener 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(); + mHistoryManagementPanel.setEnabled(false); } } - public class MessageManagementPanel extends JPanel + /** + * Row visibility filter for messages + */ + public class MessageRowFilter extends RowFilter { - private static final long serialVersionUID = 1L; - - private MessageHistoryButton mHistoryButton = new MessageHistoryButton(); - private MessageFilterButton mFilterButton = new MessageFilterButton(); - - public MessageManagementPanel() + @Override + public boolean include(Entry entry) { - setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", "")); - - disableButtons(); - - add(mFilterButton); - add(mHistoryButton); - } - - public void enableButtons() - { - mHistoryButton.setEnabled(true); - mFilterButton.setEnabled(true); - } - - public void disableButtons() - { - mHistoryButton.setEnabled(false); - mFilterButton.setEnabled(false); - } - } - - public class MessageHistoryButton extends JideSplitButton - { - private static final long serialVersionUID = 1L; - - public MessageHistoryButton() - { - super("Clear"); - - JPanel historyPanel = new JPanel(); - - historyPanel.add(new JLabel("Message History:")); - - final JSlider slider = new JSlider(); - slider.setMinimum(0); - slider.setMaximum(2000); - slider.setMajorTickSpacing(500); - slider.setPaintTicks(true); - slider.setPaintLabels(true); - - slider.addMouseListener(new MouseListener() + if(entry.getModel() instanceof MessageActivityModel model) { - @Override - public void mouseClicked(MouseEvent arg0) + MessageItem item = model.getItem(entry.getIdentifier()); + + if(mMessageFilterSet != null && item != null && item.getMessage() != null) { - if(SwingUtilities.isLeftMouseButton(arg0) && arg0.getClickCount() == 2) - { - slider.setValue(500); //default - } + IMessage message = item.getMessage(); + return mMessageFilterSet.canProcess(message) && mMessageFilterSet.passes(message); } + } - public void mouseEntered(MouseEvent arg0) - { - } - - public void mouseExited(MouseEvent arg0) - { - } - - public void mousePressed(MouseEvent arg0) - { - } - - public void mouseReleased(MouseEvent arg0) - { - } - }); - - slider.setValue(((MessageActivityModel) mTable.getModel()).getMaxMessageCount()); - slider.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent arg0) - { - ((MessageActivityModel) mTable.getModel()).setMaxMessageCount(slider.getValue()); - } - }); - - historyPanel.add(slider); - - add(historyPanel); - - /* Clear messages */ - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - ((MessageActivityModel) mTable.getModel()).clear(); - } - }); - } - } - - public class MessageFilterButton extends JideButton - { - private static final long serialVersionUID = 1L; - - public MessageFilterButton() - { - super("Filter"); - - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent arg0) - { - final JFrame editor = new JFrame(); - - editor.setTitle("Message Filter Editor"); - editor.setSize(600, 400); - editor.setLocationRelativeTo(MessageFilterButton.this); - editor.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - editor.setLayout(new MigLayout("", "[grow,fill]", - "[grow,fill][][]")); - - @SuppressWarnings("unchecked") - FilterSet 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 99a1faec..719cf3b7 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,6 @@ public class PlottableDecodeEvent extends DecodeEvent { private long mTimeStart; private long mDuration; - private String mEventDescription; private DecodeEventType mDecodeEventType; private IdentifierCollection mIdentifierCollection; private IChannelDescriptor mChannelDescriptor; @@ -115,8 +114,9 @@ public class PlottableDecodeEvent extends DecodeEvent /** * Constructs a builder instance with the specified start time in milliseconds */ - public PlottableDecodeEventBuilder(long timeStart) + public PlottableDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart) { + mDecodeEventType = decodeEventType; mTimeStart = timeStart; } @@ -150,16 +150,6 @@ public class PlottableDecodeEvent extends DecodeEvent return this; } - /** - * Sets the event description text - * @param description of the event - */ - public PlottableDecodeEventBuilder eventDescription(String description) - { - mEventDescription = description; - return this; - } - /** * Sets the identifier collection. * @param identifierCollection containing optional identifiers like TO, FROM, frequency and @@ -191,15 +181,6 @@ public class PlottableDecodeEvent extends DecodeEvent return this; } - /** - * Sets the Decode Event type for this event. - * @param eventType - */ - public PlottableDecodeEventBuilder eventType(DecodeEventType eventType) { - mDecodeEventType = eventType; - return this; - } - /** * Sets the location for the plottable event */ @@ -232,12 +213,10 @@ public class PlottableDecodeEvent extends DecodeEvent */ public PlottableDecodeEvent build() { - PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mTimeStart); + PlottableDecodeEvent decodeEvent = new PlottableDecodeEvent(mDecodeEventType, mTimeStart); decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setDetails(mDetails); decodeEvent.setDuration(mDuration); - decodeEvent.setEventType(mDecodeEventType); - decodeEvent.setEventDescription(mEventDescription); decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setProtocol(mProtocol); decodeEvent.setLocation(mGeoPosition); @@ -246,5 +225,4 @@ public class PlottableDecodeEvent extends DecodeEvent return decodeEvent; } } - } diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java similarity index 58% rename from src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java rename to src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.java index ac1ad974..88d24c7b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistoryRequest.java +++ b/src/main/java/io/github/dsheirer/module/decode/event/filter/AllOtherEventFilter.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 @@ -17,19 +17,23 @@ * **************************************************************************** */ -package io.github.dsheirer.module.decode.event.filter.lastheard; +package io.github.dsheirer.module.decode.event.filter; -import io.github.dsheirer.module.ModuleEventBusMessage; +import io.github.dsheirer.module.decode.event.DecodeEventType; /** - * Request for decode event history from a processing chain for the specified channel. + * Event filter for decode event types that are not specified in other event filters. + * + * This is intended as a catch-all for any elements of the DecodeEventType enumeration that may get added in the + * future and are not deliberately added to the various event filter groupings listed in the enumeration. */ -public class LastHeardHistoryRequest extends ModuleEventBusMessage +public class AllOtherEventFilter extends EventFilter { /** - * Constructs an instance + * Constructor */ - public LastHeardHistoryRequest() + public AllOtherEventFilter() { + super("Other", DecodeEventType.OTHERS); } } 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 ed2a8302..a2505521 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 3afc588a..27e79bbf 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 930e52a2..039963c3 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 9c36fe28..2b0d5e7f 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,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2022 Dennis Sheirer + * Copyright (C) 2014-2023 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,24 +20,17 @@ package io.github.dsheirer.module.decode.event.filter; import io.github.dsheirer.module.decode.event.DecodeEventType; -import java.util.Arrays; +/** + * Event filter for network commands. + */ public class DecodedCommandEventFilter extends EventFilter { + /** + * Constructs an instance + */ public DecodedCommandEventFilter() { - super("Commands", Arrays.asList( - DecodeEventType.ANNOUNCEMENT, - DecodeEventType.STATION_ID, - DecodeEventType.ACKNOWLEDGE, - DecodeEventType.PAGE, - DecodeEventType.QUERY, - DecodeEventType.RADIO_CHECK, - DecodeEventType.STATUS, - DecodeEventType.COMMAND, - DecodeEventType.EMERGENCY, - DecodeEventType.NOTIFICATION, - DecodeEventType.FUNCTION - )); + super("Commands", DecodeEventType.COMMANDS); } } 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 5b8ea00d..58ae94ed 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 d316b671..d78cafbe 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 8dba214e..e6bc6432 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 class EventClearButton extends JideSplitButton JPanel historyPanel = new JPanel(); - historyPanel.add(new JLabel("History Entries:")); - + historyPanel.add(new JLabel("History Size:")); final JSlider historySlider = initializeHistorySlider(); + JLabel valueLabel = new JLabel(String.valueOf(maxHistoryCount)); historySlider.setValue(maxHistoryCount); - historySlider.addChangeListener(new ChangeListener() - { - @Override - public void stateChanged(ChangeEvent arg0) + historySlider.addChangeListener(arg0 -> { + if (mEventClearHandler != null) { - if (mEventClearHandler != null) - { - mEventClearHandler.onHistoryLimitChanged(historySlider.getValue()); - } + mEventClearHandler.onHistoryLimitChanged(historySlider.getValue()); } + + valueLabel.setText(String.valueOf(historySlider.getValue())); }); historyPanel.add(historySlider); - + historyPanel.add(valueLabel); add(historyPanel); /* This handles the click action on the main button. Clear messages */ - addActionListener(new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) + addActionListener(e -> { + if (mEventClearHandler != null) { - if (mEventClearHandler != null) - { - mEventClearHandler.onClearHistoryClicked(); - } + mEventClearHandler.onClearHistoryClicked(); } }); } 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 badfd817..60c664ad 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 53508930..32588e32 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 235aa1be..2b338c47 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; + private String mTitle; + private FilterSet mFilterSet; - public EventFilterActionHandler( String title, FilterSet filterSet) + /** + * 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 d0981010..00000000 --- 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/event/filter/lastheard/LastHeardFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardFilterSet.java deleted file mode 100644 index b03135e2..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardFilterSet.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.dsheirer.module.decode.event.filter.lastheard; - -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.module.decode.event.IDecodeEvent; - -public class LastHeardFilterSet extends FilterSet -{ - - public LastHeardFilterSet(AliasModel aliasModel, IconModel iconModel) - { - super("Last Heard To Channel"); - - addFilter(new LastHeardIconFilter("Icon", aliasModel, iconModel)); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java deleted file mode 100644 index d2f37c7c..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardHistory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ - -package io.github.dsheirer.module.decode.event.filter.lastheard; - -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.module.HistoryModule; -import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.IDecodeEventListener; -import io.github.dsheirer.sample.Listener; - -/** - * Decode event history module. Maintains a history of decode events and constrains the total history size. - */ -public class LastHeardHistory extends HistoryModule implements IDecodeEventListener -{ - /** - * Constructs an instance - */ - public LastHeardHistory(int historySize) - { - super(historySize); - } - - /** - * Implements the IDecodeEventListener interface - delegates to receive(event) method. - */ - @Override - public Listener getDecodeEventListener() - { - return this; - } - - /** - * Process preload data - */ - @Subscribe - public void process(DecodeEventHistoryPreloadData preloadData) - { - for(IDecodeEvent decodeEvent: preloadData.getData()) - { - receive(decodeEvent); - } - } - - /** - * Processes a request for decode event history and posts the response back - * to the processing chain event bus so that any of the modules can receive that history. - * - * Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode - * event history from a traffic channel conversion to the new rest channel. - * - * @param request for decode event history - */ - @Subscribe - public void process(LastHeardHistoryRequest request) - { - getInterModuleEventBus().post(new LastHeardHistoryResponse(this)); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java deleted file mode 100644 index 37b66203..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardIconFilter.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.dsheirer.module.decode.event.filter.lastheard; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.filter.Filter; -import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.icon.Icon; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.Form; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.identifier.Role; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import javafx.collections.ObservableList; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class LastHeardIconFilter extends Filter -{ - private AliasModel mAliasModel; - private Map> mElements = new HashMap<>(); - - public LastHeardIconFilter(String name, AliasModel aliasModel, IconModel iconModel) - { - super(name); - this.mAliasModel = aliasModel; - - ObservableList icons = iconModel.iconsProperty(); - for (Icon icon : icons) { - mElements.put(icon.getName(), new FilterElement<>(icon.getName())); - } - } - - @Override - public boolean passes(IDecodeEvent iDecodeEvent) - { - if (mEnabled && canProcess(iDecodeEvent)) - { - return matchAliasToFilter(iDecodeEvent) - .map(FilterElement::isEnabled) - .orElse(false); - } - return false; - } - - public Optional> matchAliasToFilter(IDecodeEvent iDecodeEvent) - { - IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection(); - List identifiers = identifierCollection.getIdentifiers(Role.TO); - if (iDecodeEvent.getDuration() > 0 && identifiers != null && !identifiers.isEmpty()) - { - AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - if (aliasList != null) - { - for(Identifier identifier: identifiers) - { - if (identifier.getForm() == Form.TALKGROUP) - { - List aliases = aliasList.getAliases(identifier); - if (!aliases.isEmpty()) - { - for (Alias alias : aliases) { - if (alias.getIconName() == null) - { - // Return any to match aliases with no icon defined. - return Optional.of(mElements.get(IconModel.DEFAULT_ICON)); - } - else - { - return Optional.of(mElements.get(alias.getIconName())); - } - } - } - } - } - } - } - return Optional.empty(); - } - - @Override - public boolean canProcess(IDecodeEvent iDecodeEvent) - { - return true; - } - - @Override - public List> getFilterElements() - { - return new ArrayList<>(mElements.values()); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java deleted file mode 100644 index f0a7e8d8..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardModel.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.module.decode.event.filter.lastheard; - -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.IChannelDescriptor; -import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.filter.EventFilterProvider; -import io.github.dsheirer.preference.PreferenceType; -import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class LastHeardModel extends AbstractTableModel implements Listener, EventFilterProvider -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(LastHeardModel.class); - - public static final int COLUMN_TIME = 0; - public static final int COLUMN_DURATION = 1; - public static final int COLUMN_EVENT = 2; - public static final int COLUMN_FROM_ID = 3; - public static final int COLUMN_FROM_ALIAS = 4; - public static final int COLUMN_TO_ID = 5; - public static final int COLUMN_TO_ALIAS = 6; - public static final int COLUMN_CHANNEL = 7; - public static final int COLUMN_FREQUENCY = 8; - public static final int COLUMN_DETAILS = 9; - - protected int mMaxMessages = 500; - - protected List mEvents = new ArrayList<>(); - protected FilterSet mLastSeenFilterSet; - - protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"}; - - public LastHeardModel(AliasModel aliasModel, IconModel iconModel) - { - mLastSeenFilterSet = new LastHeardFilterSet(aliasModel, iconModel); - MyEventBus.getGlobalEventBus().register(this); - } - - /** - * Receives preference update notifications via the event bus - * @param preferenceType that was updated - */ - @Subscribe - public void preferenceUpdated(PreferenceType preferenceType) - { - if(preferenceType == PreferenceType.DECODE_EVENT) - { - for(int row = 0; row < mEvents.size(); row++) - { - fireTableCellUpdated(row, COLUMN_TIME); - } - } - else if(preferenceType == PreferenceType.TALKGROUP_FORMAT) - { - for(int row = 0; row < mEvents.size(); row++) - { - fireTableCellUpdated(row, COLUMN_FROM_ID); - fireTableCellUpdated(row, COLUMN_TO_ID); - } - } - } - - /** - * Access the complete list of events managed by this model. - */ - public List getEvents() - { - return new ArrayList<>(mEvents); - } - - public void dispose() - { - MyEventBus.getGlobalEventBus().unregister(this); - Iterator it = mEvents.iterator(); - - while(it.hasNext()) - { - it.remove(); - } - } - - public void clear() - { - mEvents.clear(); - fireTableDataChanged(); - } - - /** - * Clears all events from this model and loads the events argument - */ - public void clearAndSet(List events) - { - mEvents.clear(); - mEvents.addAll(events); - fireTableDataChanged(); - } - - public void reset() - { - dispose(); - fireTableDataChanged(); - } - - public int getMaxMessageCount() - { - return mMaxMessages; - } - - public void setMaxMessageCount(int count) - { - mMaxMessages = count; - } - - /** - * Adds, updates or deletes the event from the model. Producers can send - * the same call event multiple times to indicate that information in the - * event is updated. Producers can also mark the event as invalid and the - * event will be removed from the model. - */ - public void receive(final IDecodeEvent event) - { - if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event)) - { - return; - } - if(!mEvents.contains(event)) - { - mEvents.add(0, event); - fireTableRowsInserted(0, 0); - prune(); - } - else - { - int row = mEvents.indexOf(event); - fireTableRowsUpdated(row, row); - } - } - - private void prune() - { - while(mEvents.size() > mMaxMessages) - { - int index = mEvents.size() - 1; - mEvents.remove(index); - fireTableRowsDeleted(index, index); - } - } - - @Override - public FilterSet getFilterSet() { - return mLastSeenFilterSet; - } - - @Override - public void setFilterSet(FilterSet filterSet) { - this.mLastSeenFilterSet = filterSet; - } - - @Override - public int getRowCount() - { - return mEvents.size(); - } - - @Override - public int getColumnCount() - { - return mHeaders.length; - } - - public String getColumnName(int column) - { - return mHeaders[column]; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - synchronized(mEvents) - { - IDecodeEvent event = mEvents.get(rowIndex); - - if(event != null) - { - switch(columnIndex) - { - case COLUMN_TIME: - return event.getTimeStart(); - case COLUMN_DURATION: - return event.getDuration(); - case COLUMN_EVENT: - return event.getEventDescription(); - case COLUMN_FROM_ID: - return event.getIdentifierCollection(); - case COLUMN_FROM_ALIAS: - return event.getIdentifierCollection(); - case COLUMN_TO_ID: - return event.getIdentifierCollection(); - case COLUMN_TO_ALIAS: - return event.getIdentifierCollection(); - case COLUMN_CHANNEL: - IChannelDescriptor channelDescriptor = event.getChannelDescriptor(); - - if(channelDescriptor != null) - { - if(event.hasTimeslot()) - { - return channelDescriptor.toString() + " TS:" + event.getTimeslot(); - } - else - { - return channelDescriptor.toString(); - } - } - else - { - if(event.hasTimeslot()) - { - return "TS:" + event.getTimeslot(); - } - else - { - return null; - } - } - case COLUMN_FREQUENCY: - return event.getChannelDescriptor(); - case COLUMN_DETAILS: - return event.getDetails(); - } - } - } - - return null; - } - - @Override - public Class getColumnClass(int columnIndex) - { - switch(columnIndex) - { - case COLUMN_DURATION: - case COLUMN_TIME: - return Long.class; - case COLUMN_DETAILS: - case COLUMN_EVENT: - return String.class; - case COLUMN_FREQUENCY: - case COLUMN_FROM_ALIAS: - case COLUMN_FROM_ID: - case COLUMN_TO_ALIAS: - case COLUMN_TO_ID: - return IdentifierCollection.class; - case COLUMN_CHANNEL: - return String.class; - } - return super.getColumnClass(columnIndex); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java deleted file mode 100644 index ff043fab..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastheard/LastHeardPanel.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.module.decode.event.filter.lastheard; - -import com.google.common.base.Joiner; -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.IChannelDescriptor; -import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.filter.AllPassFilter; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.Form; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.identifier.Role; -import io.github.dsheirer.module.ProcessingChain; -import io.github.dsheirer.module.decode.event.DecodeEventHistory; -import io.github.dsheirer.module.decode.event.DecodeEventModel; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.filter.EventClearButton; -import io.github.dsheirer.module.decode.event.filter.EventClearHandler; -import io.github.dsheirer.module.decode.event.filter.EventFilterButton; -import io.github.dsheirer.module.decode.event.filter.EventFilterProvider; -import io.github.dsheirer.preference.PreferenceType; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; -import io.github.dsheirer.sample.Listener; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -public class LastHeardPanel extends JPanel implements Listener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(LastHeardPanel.class); - private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel"; - - private JTable mTable; - private JTableColumnWidthMonitor mTableColumnWidthMonitor; - private LastHeardModel mLastHeardModel; - private DecodeEventHistory mCurrentEventHistory; - private JScrollPane mEmptyScroller; - private IconModel mIconModel; - private AliasModel mAliasModel; - private UserPreferences mUserPreferences; - private TimestampCellRenderer mTimestampCellRenderer; - private LastSeenManagementPanel mLastSeenManagementPanel; - - /** - * View for call event table - * @param iconModel to display alias icons in table rows - */ - public LastHeardPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel) - { - MyEventBus.getGlobalEventBus().register(this); - - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - mIconModel = iconModel; - mAliasModel = aliasModel; - mLastHeardModel = new LastHeardModel(aliasModel, iconModel); - mUserPreferences = userPreferences; - mTimestampCellRenderer = new TimestampCellRenderer(); - mTable = new JTable(mLastHeardModel); - mTable.setAutoCreateRowSorter(true); - mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - mLastSeenManagementPanel = new LastSeenManagementPanel(); - mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); - updateCellRenderers(); - - add(mLastSeenManagementPanel, "span,growx"); - - mEmptyScroller = new JScrollPane(mTable); - add(mEmptyScroller); - } - - public void dispose() - { - MyEventBus.getGlobalEventBus().unregister(this); - } - - /** - * Receives preference update notifications via the event bus - * @param preferenceType that was updated - */ - @Subscribe - public void preferenceUpdated(PreferenceType preferenceType) - { - if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT) - { - EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences()); - } - } - - private void updateCellRenderers() - { - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME) - .setCellRenderer(mTimestampCellRenderer); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION) - .setCellRenderer(new DurationCellRenderer()); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID) - .setCellRenderer(new IdentifierCellRenderer(Role.FROM)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS) - .setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID) - .setCellRenderer(new IdentifierCellRenderer(Role.TO)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS) - .setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL) - .setCellRenderer(new ChannelDescriptorCellRenderer()); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY) - .setCellRenderer(new FrequencyCellRenderer()); - } - - @Override - public void receive(final ProcessingChain processingChain) - { - if(mCurrentEventHistory != null) - { - mCurrentEventHistory.removeListener(mLastHeardModel); - } - - EventQueue.invokeLater(() -> { - if(processingChain != null) - { - mCurrentEventHistory = processingChain.getDecodeEventHistory(); - mLastHeardModel.clearAndSet(mCurrentEventHistory.getItems()); - processingChain.getDecodeEventHistory().addListener(mLastHeardModel); - mLastSeenManagementPanel.enableButtons(); - } - else - { - mCurrentEventHistory = null; - mLastHeardModel.clearAndSet(Collections.emptyList()); - mLastSeenManagementPanel.disableButtons(); - } - }); - } - - public class LastSeenManagementPanel extends JPanel - { - private static final long serialVersionUID = 1L; - - private EventFilterButton mFilterButton; - private EventClearButton mEventClearButton; - - public LastSeenManagementPanel() - { - setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", "")); - - initializeFilterButton(); - initializeClearButton(); - disableButtons(); - - add(mFilterButton); - add(mEventClearButton); - } - - public void enableButtons() - { - mFilterButton.setEnabled(true); - mEventClearButton.setEnabled(true); - } - - public void disableButtons() - { - mFilterButton.setEnabled(false); - mEventClearButton.setEnabled(false); - } - - private void initializeFilterButton() - { - EventFilterProvider filterProvider = (EventFilterProvider) mTable.getModel(); - FilterSet filterSet = new FilterSet<>(new AllPassFilter<>()); - if (filterProvider != null) { - filterSet = filterProvider.getFilterSet(); - } - mFilterButton = new EventFilterButton<>("Last Heard To Channel Filter Editor", filterSet); - } - - private void initializeClearButton() { - mEventClearButton = new EventClearButton( - ((LastHeardModel) mTable.getModel()).getMaxMessageCount() - ); - mEventClearButton.setEventClearHandler(new EventClearHandler() { - @Override - public void onHistoryLimitChanged(int newHistoryLimit) { - ((LastHeardModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit); - } - - @Override - public void onClearHistoryClicked() { - ((LastHeardModel) mTable.getModel()).clear(); - } - }); - } - } - - /** - * Custom cell renderer for displaying identifiers from an identifier collection - */ - public class IdentifierCellRenderer extends DefaultTableCellRenderer - { - protected Role mRole; - - /** - * Constructs an instance of the cell renderer. - * - * @param role of the identifier - */ - public IdentifierCellRenderer(Role role) - { - mRole = role; - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if(value instanceof IdentifierCollection) - { - List identifiers = ((IdentifierCollection)value).getIdentifiers(mRole); - label.setText(format(identifiers)); - } - else - { - label.setText(null); - } - - return label; - } - - /** - * Formats a list of identifiers as a comma separated list of values - * @param identifiers to format - * @return formatted list or null - */ - protected String format(List identifiers) - { - if(identifiers == null || identifiers.isEmpty()) - { - return null; - } - - StringBuilder sb = new StringBuilder(); - - for(Identifier identifier: identifiers) - { - if(sb.length() > 0) - { - sb.append(","); - } - - if(identifier.getForm() == Form.TALKGROUP) - { - sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier)); - } - else - { - sb.append(identifier); - } - - } - - return sb.toString(); - } - } - - /** - * Cell renderer for identifier aliases - */ - public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer - { - private Role mRole; - - /** - * Constructs an instance of the cell renderer. - * - * @param role of the identifier - */ - public AliasedIdentifierCellRenderer(Role role) - { - mRole = role; - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - Color color = mTable.getForeground(); - ImageIcon icon = null; - String text = null; - - if(value instanceof IdentifierCollection) - { - IdentifierCollection identifierCollection = (IdentifierCollection)value; - List identifiers = identifierCollection.getIdentifiers(mRole); - - if(identifiers != null && !identifiers.isEmpty()) - { - AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - - if(aliasList != null) - { - StringBuilder sb = new StringBuilder(); - - for(Identifier identifier: identifiers) - { - List aliases = aliasList.getAliases(identifier); - - if(!aliases.isEmpty()) - { - if(sb.length() > 0) - { - sb.append(","); - } - sb.append(Joiner.on(", ").skipNulls().join(aliases)); - color = aliases.get(0).getDisplayColor(); - icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE); - } - } - - text = sb.toString(); - } - } - } - - label.setText(text); - label.setForeground(color); - label.setIcon(icon); - - return label; - } - } - - public class TimestampCellRenderer extends DefaultTableCellRenderer - { - private SimpleDateFormat mTimestampFormatter; - - public TimestampCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - updatePreferences(); - } - - public void updatePreferences() - { - mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter(); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if(value instanceof Long) - { - label.setText(mTimestampFormatter.format(new Date((long)value))); - } - else - { - label.setText(null); - } - - return label; - } - } - - public class DurationCellRenderer extends DefaultTableCellRenderer - { - private DecimalFormat mDecimalFormat = new DecimalFormat("0.0"); - - public DurationCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - String formatted = null; - - if(value instanceof Long) - { - long duration = (long)value; - - if(duration > 0) - { - formatted = mDecimalFormat.format((double)duration / 1e3d); - } - } - - label.setText(formatted); - - return label; - } - } - - /** - * Frequency value cell renderer - */ - public class FrequencyCellRenderer extends DefaultTableCellRenderer - { - private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000"); - - public FrequencyCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - String formatted = null; - - if(value instanceof IChannelDescriptor) - { - IChannelDescriptor channelDescriptor = (IChannelDescriptor)value; - - long frequency = channelDescriptor.getDownlinkFrequency(); - - if(frequency > 0) - { - formatted = mFrequencyFormatter.format(frequency / 1e6d); - } - } - - label.setText(formatted); - - return label; - } - } - - /** - * Channel descriptor value cell renderer - */ - public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer - { - public ChannelDescriptorCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java deleted file mode 100644 index 871fd567..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenFilterSet.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.module.decode.event.IDecodeEvent; - -public class LastSeenFilterSet extends FilterSet -{ - - public LastSeenFilterSet(AliasModel aliasModel, IconModel iconModel) - { - super("Last Seen From Radio"); - - addFilter(new LastSeenIconFilter("Icon", aliasModel, iconModel)); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java deleted file mode 100644 index a3d0d424..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistory.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ - -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.module.HistoryModule; -import io.github.dsheirer.module.decode.event.DecodeEventHistoryPreloadData; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.IDecodeEventListener; -import io.github.dsheirer.sample.Listener; - -/** - * Decode event history module. Maintains a history of decode events and constrains the total history size. - */ -public class LastSeenHistory extends HistoryModule implements IDecodeEventListener -{ - /** - * Constructs an instance - */ - public LastSeenHistory(int historySize) - { - super(historySize); - } - - /** - * Implements the IDecodeEventListener interface - delegates to receive(event) method. - */ - @Override - public Listener getDecodeEventListener() - { - return this; - } - - /** - * Process preload data - */ - @Subscribe - public void process(DecodeEventHistoryPreloadData preloadData) - { - for(IDecodeEvent decodeEvent: preloadData.getData()) - { - receive(decodeEvent); - } - } - - /** - * Processes a request for decode event history and posts the response back - * to the processing chain event bus so that any of the modules can receive that history. - * - * Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode - * event history from a traffic channel conversion to the new rest channel. - * - * @param request for decode event history - */ - @Subscribe - public void process(LastSeenHistoryRequest request) - { - getInterModuleEventBus().post(new LastSeenHistoryResponse(this)); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java deleted file mode 100644 index 8d85fa05..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenHistoryResponse.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2020 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * **************************************************************************** - */ - -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import io.github.dsheirer.module.ModuleEventBusMessage; - -/** - * Response to decode event history request from a processing chain for the specified channel. - */ -public class LastSeenHistoryResponse extends ModuleEventBusMessage -{ - private LastSeenHistory mLastSeenHistory; - - /** - * Constructs an instance - * @param history that has a decode event history - */ - public LastSeenHistoryResponse(LastSeenHistory history) - { - mLastSeenHistory = history; - } - - /** - * Decode event history module - */ - public LastSeenHistory getDecodeEventHistory() - { - return mLastSeenHistory; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java deleted file mode 100644 index 3089d4b0..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenIconFilter.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.filter.Filter; -import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.icon.Icon; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.Form; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import javafx.collections.ObservableList; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -public class LastSeenIconFilter extends Filter -{ - private AliasModel mAliasModel; - private Map> mElements = new HashMap<>(); - - public LastSeenIconFilter(String name, AliasModel aliasModel, IconModel iconModel) - { - super(name); - this.mAliasModel = aliasModel; - - ObservableList icons = iconModel.iconsProperty(); - for (Icon icon : icons) { - mElements.put(icon.getName(), new FilterElement<>(icon.getName())); - } - } - - @Override - public boolean passes(IDecodeEvent iDecodeEvent) - { - if (mEnabled && canProcess(iDecodeEvent)) - { - return matchAliasToFilter(iDecodeEvent) - .map(FilterElement::isEnabled) - .orElse(false); - } - return false; - } - - public Optional> matchAliasToFilter(IDecodeEvent iDecodeEvent) - { - IdentifierCollection identifierCollection = iDecodeEvent.getIdentifierCollection(); - List identifiers = identifierCollection.getIdentifiers(); - if (identifiers != null && !identifiers.isEmpty()) - { - AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - if (aliasList != null) - { - for(Identifier identifier: identifiers) - { - if (identifier.getForm() == Form.RADIO) - { - List aliases = aliasList.getAliases(identifier); - if (!aliases.isEmpty()) - { - for (Alias alias : aliases) { - if (alias.getIconName() == null) - { - // Return any to match aliases with no icon defined. - return Optional.of(mElements.get(IconModel.DEFAULT_ICON)); - } - else - { - return Optional.of(mElements.get(alias.getIconName())); - } - } - } - } - } - } - } - return Optional.empty(); - } - - @Override - public boolean canProcess(IDecodeEvent iDecodeEvent) - { - return true; - } - - @Override - public List> getFilterElements() - { - return new ArrayList<>(mElements.values()); - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java deleted file mode 100644 index 4b2506d7..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenModel.java +++ /dev/null @@ -1,289 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.IChannelDescriptor; -import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.filter.EventFilterProvider; -import io.github.dsheirer.preference.PreferenceType; -import io.github.dsheirer.sample.Listener; - -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class LastSeenModel extends AbstractTableModel implements Listener, EventFilterProvider -{ - private static final long serialVersionUID = 1L; - - public static final int COLUMN_TIME = 0; - public static final int COLUMN_DURATION = 1; - public static final int COLUMN_EVENT = 2; - public static final int COLUMN_FROM_ID = 3; - public static final int COLUMN_FROM_ALIAS = 4; - public static final int COLUMN_TO_ID = 5; - public static final int COLUMN_TO_ALIAS = 6; - public static final int COLUMN_CHANNEL = 7; - public static final int COLUMN_FREQUENCY = 8; - public static final int COLUMN_DETAILS = 9; - - protected int mMaxMessages = 500; - - protected List mEvents = new ArrayList<>(); - protected FilterSet mLastSeenFilterSet; - - protected String[] mHeaders = new String[]{"Time", "Duration", "Event", "From", "Alias", "To", "Alias", "Channel", "Frequency", "Details"}; - - public LastSeenModel(AliasModel aliasModel, IconModel iconModel) - { - mLastSeenFilterSet = new LastSeenFilterSet(aliasModel, iconModel); - MyEventBus.getGlobalEventBus().register(this); - } - - /** - * Receives preference update notifications via the event bus - * @param preferenceType that was updated - */ - @Subscribe - public void preferenceUpdated(PreferenceType preferenceType) - { - if(preferenceType == PreferenceType.DECODE_EVENT) - { - for(int row = 0; row < mEvents.size(); row++) - { - fireTableCellUpdated(row, COLUMN_TIME); - } - } - else if(preferenceType == PreferenceType.TALKGROUP_FORMAT) - { - for(int row = 0; row < mEvents.size(); row++) - { - fireTableCellUpdated(row, COLUMN_FROM_ID); - fireTableCellUpdated(row, COLUMN_TO_ID); - } - } - } - - /** - * Access the complete list of events managed by this model. - */ - public List getEvents() - { - return new ArrayList<>(mEvents); - } - - public void dispose() - { - MyEventBus.getGlobalEventBus().unregister(this); - Iterator it = mEvents.iterator(); - - while(it.hasNext()) - { - it.remove(); - } - } - - public void clear() - { - mEvents.clear(); - fireTableDataChanged(); - } - - /** - * Clears all events from this model and loads the events argument - */ - public void clearAndSet(List events) - { - mEvents.clear(); - mEvents.addAll(events); - fireTableDataChanged(); - } - - public void reset() - { - dispose(); - fireTableDataChanged(); - } - - public int getMaxMessageCount() - { - return mMaxMessages; - } - - public void setMaxMessageCount(int count) - { - mMaxMessages = count; - } - - /** - * Adds, updates or deletes the event from the model. Producers can send - * the same call event multiple times to indicate that information in the - * event is updated. Producers can also mark the event as invalid and the - * event will be removed from the model. - */ - public void receive(final IDecodeEvent event) - { - if (mLastSeenFilterSet != null && !mLastSeenFilterSet.passes(event)) - { - return; - } - if(!mEvents.contains(event)) - { - mEvents.add(0, event); - fireTableRowsInserted(0, 0); - prune(); - } - else - { - int row = mEvents.indexOf(event); - fireTableRowsUpdated(row, row); - } - } - - private void prune() - { - while(mEvents.size() > mMaxMessages) - { - int index = mEvents.size() - 1; - mEvents.remove(index); - fireTableRowsDeleted(index, index); - } - } - - @Override - public FilterSet getFilterSet() { - return mLastSeenFilterSet; - } - - @Override - public void setFilterSet(FilterSet filterSet) { - this.mLastSeenFilterSet = filterSet; - } - - @Override - public int getRowCount() - { - return mEvents.size(); - } - - @Override - public int getColumnCount() - { - return mHeaders.length; - } - - public String getColumnName(int column) - { - return mHeaders[column]; - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) - { - synchronized(mEvents) - { - IDecodeEvent event = mEvents.get(rowIndex); - - if(event != null) - { - switch(columnIndex) - { - case COLUMN_TIME: - return event.getTimeStart(); - case COLUMN_DURATION: - return event.getDuration(); - case COLUMN_EVENT: - return event.getEventDescription(); - case COLUMN_FROM_ID: - return event.getIdentifierCollection(); - case COLUMN_FROM_ALIAS: - return event.getIdentifierCollection(); - case COLUMN_TO_ID: - return event.getIdentifierCollection(); - case COLUMN_TO_ALIAS: - return event.getIdentifierCollection(); - case COLUMN_CHANNEL: - IChannelDescriptor channelDescriptor = event.getChannelDescriptor(); - - if(channelDescriptor != null) - { - if(event.hasTimeslot()) - { - return channelDescriptor.toString() + " TS:" + event.getTimeslot(); - } - else - { - return channelDescriptor.toString(); - } - } - else - { - if(event.hasTimeslot()) - { - return "TS:" + event.getTimeslot(); - } - else - { - return null; - } - } - case COLUMN_FREQUENCY: - return event.getChannelDescriptor(); - case COLUMN_DETAILS: - return event.getDetails(); - } - } - } - - return null; - } - - @Override - public Class getColumnClass(int columnIndex) - { - switch(columnIndex) - { - case COLUMN_DURATION: - case COLUMN_TIME: - return Long.class; - case COLUMN_DETAILS: - case COLUMN_EVENT: - return String.class; - case COLUMN_FREQUENCY: - case COLUMN_FROM_ALIAS: - case COLUMN_FROM_ID: - case COLUMN_TO_ALIAS: - case COLUMN_TO_ID: - return IdentifierCollection.class; - case COLUMN_CHANNEL: - return String.class; - } - return super.getColumnClass(columnIndex); - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java b/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java deleted file mode 100644 index 47057d13..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/event/filter/lastseen/LastSeenPanel.java +++ /dev/null @@ -1,482 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ -package io.github.dsheirer.module.decode.event.filter.lastseen; - -import com.google.common.base.Joiner; -import com.google.common.eventbus.Subscribe; -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.IChannelDescriptor; -import io.github.dsheirer.eventbus.MyEventBus; -import io.github.dsheirer.filter.AllPassFilter; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.icon.IconModel; -import io.github.dsheirer.identifier.Form; -import io.github.dsheirer.identifier.Identifier; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.identifier.Role; -import io.github.dsheirer.module.ProcessingChain; -import io.github.dsheirer.module.decode.event.DecodeEventHistory; -import io.github.dsheirer.module.decode.event.DecodeEventModel; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.event.filter.EventClearButton; -import io.github.dsheirer.module.decode.event.filter.EventClearHandler; -import io.github.dsheirer.module.decode.event.filter.EventFilterButton; -import io.github.dsheirer.module.decode.event.filter.EventFilterProvider; -import io.github.dsheirer.preference.PreferenceType; -import io.github.dsheirer.preference.UserPreferences; -import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor; -import io.github.dsheirer.sample.Listener; -import net.miginfocom.swing.MigLayout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -public class LastSeenPanel extends JPanel implements Listener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger(LastSeenPanel.class); - private static final String TABLE_PREFERENCE_KEY = "decode.lastseen.panel"; - - private JTable mTable; - private JTableColumnWidthMonitor mTableColumnWidthMonitor; - private LastSeenModel mLastSeenModel; - private DecodeEventHistory mCurrentEventHistory; - private JScrollPane mEmptyScroller; - private IconModel mIconModel; - private AliasModel mAliasModel; - private UserPreferences mUserPreferences; - private TimestampCellRenderer mTimestampCellRenderer; - private LastSeenManagementPanel mLastSeenManagementPanel; - - /** - * View for call event table - * @param iconModel to display alias icons in table rows - */ - public LastSeenPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel) - { - MyEventBus.getGlobalEventBus().register(this); - - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); - mIconModel = iconModel; - mAliasModel = aliasModel; - mLastSeenModel = new LastSeenModel(aliasModel, iconModel); - mUserPreferences = userPreferences; - mTimestampCellRenderer = new TimestampCellRenderer(); - mTable = new JTable(mLastSeenModel); - mTable.setAutoCreateRowSorter(true); - mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); - mLastSeenManagementPanel = new LastSeenManagementPanel(); - mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY); - updateCellRenderers(); - - add(mLastSeenManagementPanel, "span,growx"); - - mEmptyScroller = new JScrollPane(mTable); - add(mEmptyScroller); - } - - public void dispose() - { - MyEventBus.getGlobalEventBus().unregister(this); - } - - /** - * Receives preference update notifications via the event bus - * @param preferenceType that was updated - */ - @Subscribe - public void preferenceUpdated(PreferenceType preferenceType) - { - if(preferenceType == PreferenceType.DECODE_EVENT || preferenceType == PreferenceType.TALKGROUP_FORMAT) - { - EventQueue.invokeLater(() -> mTimestampCellRenderer.updatePreferences()); - } - } - - private void updateCellRenderers() - { - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TIME) - .setCellRenderer(mTimestampCellRenderer); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_DURATION) - .setCellRenderer(new DurationCellRenderer()); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ID) - .setCellRenderer(new IdentifierCellRenderer(Role.FROM)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FROM_ALIAS) - .setCellRenderer(new AliasedIdentifierCellRenderer(Role.FROM)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ID) - .setCellRenderer(new IdentifierCellRenderer(Role.TO)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_TO_ALIAS) - .setCellRenderer(new AliasedIdentifierCellRenderer(Role.TO)); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_CHANNEL) - .setCellRenderer(new ChannelDescriptorCellRenderer()); - mTable.getColumnModel().getColumn(DecodeEventModel.COLUMN_FREQUENCY) - .setCellRenderer(new FrequencyCellRenderer()); - } - - @Override - public void receive(final ProcessingChain processingChain) - { - if(mCurrentEventHistory != null) - { - mCurrentEventHistory.removeListener(mLastSeenModel); - } - - EventQueue.invokeLater(() -> { - if(processingChain != null) - { - mCurrentEventHistory = processingChain.getDecodeEventHistory(); - mLastSeenModel.clearAndSet(mCurrentEventHistory.getItems()); - processingChain.getDecodeEventHistory().addListener(mLastSeenModel); - mLastSeenManagementPanel.enableButtons(); - } - else - { - mCurrentEventHistory = null; - mLastSeenModel.clearAndSet(Collections.emptyList()); - mLastSeenManagementPanel.disableButtons(); - } - }); - } - - public class LastSeenManagementPanel extends JPanel - { - private static final long serialVersionUID = 1L; - - private EventFilterButton mFilterButton; - private EventClearButton mEventClearButton; - - public LastSeenManagementPanel() - { - setLayout(new MigLayout("insets 2 2 5 5", "[]5[left,grow]", "")); - - initializeFilterButton(); - initializeClearButton(); - disableButtons(); - - add(mFilterButton); - add(mEventClearButton); - } - - public void enableButtons() - { - mFilterButton.setEnabled(true); - mEventClearButton.setEnabled(true); - } - - public void disableButtons() - { - mFilterButton.setEnabled(false); - mEventClearButton.setEnabled(false); - } - - private void initializeFilterButton() - { - EventFilterProvider filterProvider = (EventFilterProvider) mTable.getModel(); - FilterSet filterSet = new FilterSet<>(new AllPassFilter<>()); - if (filterProvider != null) { - filterSet = filterProvider.getFilterSet(); - } - mFilterButton = new EventFilterButton<>("Last Seen From Radio Filter Editor", filterSet); - } - - private void initializeClearButton() { - mEventClearButton = new EventClearButton( - ((LastSeenModel) mTable.getModel()).getMaxMessageCount() - ); - mEventClearButton.setEventClearHandler(new EventClearHandler() { - @Override - public void onHistoryLimitChanged(int newHistoryLimit) { - ((LastSeenModel) mTable.getModel()).setMaxMessageCount(newHistoryLimit); - } - - @Override - public void onClearHistoryClicked() { - ((LastSeenModel) mTable.getModel()).clear(); - } - }); - } - } - - /** - * Custom cell renderer for displaying identifiers from an identifier collection - */ - public class IdentifierCellRenderer extends DefaultTableCellRenderer - { - protected Role mRole; - - /** - * Constructs an instance of the cell renderer. - * - * @param role of the identifier - */ - public IdentifierCellRenderer(Role role) - { - mRole = role; - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if(value instanceof IdentifierCollection) - { - List identifiers = ((IdentifierCollection)value).getIdentifiers(mRole); - label.setText(format(identifiers)); - } - else - { - label.setText(null); - } - - return label; - } - - /** - * Formats a list of identifiers as a comma separated list of values - * @param identifiers to format - * @return formatted list or null - */ - protected String format(List identifiers) - { - if(identifiers == null || identifiers.isEmpty()) - { - return null; - } - - StringBuilder sb = new StringBuilder(); - - for(Identifier identifier: identifiers) - { - if(sb.length() > 0) - { - sb.append(","); - } - - if(identifier.getForm() == Form.TALKGROUP) - { - sb.append(mUserPreferences.getTalkgroupFormatPreference().format(identifier)); - } - else - { - sb.append(identifier); - } - - } - - return sb.toString(); - } - } - - /** - * Cell renderer for identifier aliases - */ - public class AliasedIdentifierCellRenderer extends DefaultTableCellRenderer - { - private Role mRole; - - /** - * Constructs an instance of the cell renderer. - * - * @param role of the identifier - */ - public AliasedIdentifierCellRenderer(Role role) - { - mRole = role; - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - Color color = mTable.getForeground(); - ImageIcon icon = null; - String text = null; - - if(value instanceof IdentifierCollection) - { - IdentifierCollection identifierCollection = (IdentifierCollection)value; - List identifiers = identifierCollection.getIdentifiers(mRole); - - if(identifiers != null && !identifiers.isEmpty()) - { - AliasList aliasList = mAliasModel.getAliasList(identifierCollection); - - if(aliasList != null) - { - StringBuilder sb = new StringBuilder(); - - for(Identifier identifier: identifiers) - { - List aliases = aliasList.getAliases(identifier); - - if(!aliases.isEmpty()) - { - if(sb.length() > 0) - { - sb.append(","); - } - sb.append(Joiner.on(", ").skipNulls().join(aliases)); - color = aliases.get(0).getDisplayColor(); - icon = mIconModel.getIcon(aliases.get(0).getIconName(), IconModel.DEFAULT_ICON_SIZE); - } - } - - text = sb.toString(); - } - } - } - - label.setText(text); - label.setForeground(color); - label.setIcon(icon); - - return label; - } - } - - public class TimestampCellRenderer extends DefaultTableCellRenderer - { - private SimpleDateFormat mTimestampFormatter; - - public TimestampCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - updatePreferences(); - } - - public void updatePreferences() - { - mTimestampFormatter = mUserPreferences.getDecodeEventPreference().getTimestampFormat().getFormatter(); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if(value instanceof Long) - { - label.setText(mTimestampFormatter.format(new Date((long)value))); - } - else - { - label.setText(null); - } - - return label; - } - } - - public class DurationCellRenderer extends DefaultTableCellRenderer - { - private DecimalFormat mDecimalFormat = new DecimalFormat("0.0"); - - public DurationCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - String formatted = null; - - if(value instanceof Long) - { - long duration = (long)value; - - if(duration > 0) - { - formatted = mDecimalFormat.format((double)duration / 1e3d); - } - } - - label.setText(formatted); - - return label; - } - } - - /** - * Frequency value cell renderer - */ - public class FrequencyCellRenderer extends DefaultTableCellRenderer - { - private DecimalFormat mFrequencyFormatter = new DecimalFormat("0.00000"); - - public FrequencyCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) - { - JLabel label = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - String formatted = null; - - if(value instanceof IChannelDescriptor) - { - IChannelDescriptor channelDescriptor = (IChannelDescriptor)value; - - long frequency = channelDescriptor.getDownlinkFrequency(); - - if(frequency > 0) - { - formatted = mFrequencyFormatter.format(frequency / 1e6d); - } - } - - label.setText(formatted); - - return label; - } - } - - /** - * Channel descriptor value cell renderer - */ - public class ChannelDescriptorCellRenderer extends DefaultTableCellRenderer - { - public ChannelDescriptorCellRenderer() - { - setHorizontalAlignment(JLabel.CENTER); - } - } -} 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 ffd3fb8a..70817ff1 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 @@ -2,24 +2,19 @@ * ***************************************************************************** * Copyright (C) 2014-2023 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** + * 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.fleetsync2; @@ -125,10 +120,8 @@ public class Fleetsync2DecoderState extends DecoderState broadcast(new DecoderStateEvent(this, DecoderStateEvent.Event.DECODE, State.DATA)); break; case GPS: - PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(fleetsync.getTimestamp()) + PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, fleetsync.getTimestamp()) .channel(getCurrentChannel()) - .eventType(DecodeEventType.GPS) - .eventDescription(DecodeEventType.GPS.toString()) .details(fleetsync.toString()) .identifiers(getIdentifierCollection().copyOf()) .protocol(Protocol.FLEETSYNC) @@ -143,11 +136,9 @@ public class Fleetsync2DecoderState extends DecoderState } private DecodeEvent getDecodeEvent(Fleetsync2Message fleetsync, DecodeEventType eventType) { - return DecodeEvent.builder(fleetsync.getTimestamp()) + return DecodeEvent.builder(eventType, fleetsync.getTimestamp()) .channel(getCurrentChannel()) - .eventType(eventType) - .eventDescription(fleetsync.getMessageType().toString()) - .details(fleetsync.toString()) + .details(fleetsync.getMessageType() + " " + fleetsync) .identifiers(getIdentifierCollection().copyOf()) .protocol(Protocol.FLEETSYNC) .build(); 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 c2b75b0b..10b9b8b3 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.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message; -import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType; +import java.util.function.Function; -import java.util.*; - -public class FleetsyncMessageFilter extends Filter +/** + * 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)); } } - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) - { - Fleetsync2Message fleet = (Fleetsync2Message)message; - - if(mElements.containsKey(fleet.getMessageType())) - { - return mElements.get(fleet.getMessageType()).isEnabled(); - } - } - - return false; - } - + /** + * Limits processing to only fleetsync messages. + * @param message to test + * @return true if message type is correct. + */ @Override public boolean canProcess(IMessage message) { - return message instanceof Fleetsync2Message; + return message instanceof Fleetsync2Message && super.canProcess(message); } + /** + * Key extractor for fleetsync messages + * @return key extractor + */ @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor for Fleetsync 2 Messages. + */ + private class KeyExtractor implements Function + { + @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 258970fa..bc54188e 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; @@ -31,14 +28,14 @@ import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEventType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import io.github.dsheirer.protocol.Protocol; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LJ1200DecoderState extends DecoderState { @@ -73,12 +70,11 @@ public class LJ1200DecoderState extends DecoderState ic.remove(IdentifierClass.USER); ic.update(lj.getIdentifiers()); - DecodeEvent event = DecodeEvent.builder(System.currentTimeMillis()) + DecodeEvent event = DecodeEvent.builder(DecodeEventType.DATA_PACKET, System.currentTimeMillis()) + .protocol(Protocol.LOJACK) .identifiers(ic) - .eventType(DecodeEventType.DATA_PACKET) .channel(getCurrentChannel()) - .details("LOJACK") - .eventDescription(lj.toString()) + .details("LOJACK " + lj) .build(); broadcast(event); @@ -93,12 +89,11 @@ public class LJ1200DecoderState extends DecoderState ic.remove(IdentifierClass.USER); ic.update(transponder.getIdentifiers()); - DecodeEvent transponderEvent = DecodeEvent.builder(System.currentTimeMillis()) + DecodeEvent transponderEvent = DecodeEvent.builder(DecodeEventType.GPS, System.currentTimeMillis()) + .protocol(Protocol.LOJACK) .identifiers(ic) - .eventType(DecodeEventType.GPS) .channel(getCurrentChannel()) - .details("LOJACK TRANSPONDER") - .eventDescription(transponder.toString()) + .details("LOJACK TRANSPONDER " + transponder) .build(); broadcast(transponderEvent); @@ -117,7 +112,7 @@ public class LJ1200DecoderState extends DecoderState { sb.append("Transponder Addresses:\n"); - List 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 96c4916f..85a7467a 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"); - } - - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); + super("LJ-1200 Messages"); + add(new FilterElement<>(LJ1200_KEY)); } @Override public boolean canProcess(IMessage message) { - return message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage; + return (message instanceof LJ1200Message || message instanceof LJ1200TransponderMessage); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return Collections.EMPTY_LIST; + return mKeyExtractor; + } + + /** + * Key extractor + */ + public class LJ1200KeyExtractor implements Function + { + @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 index 5a7c6b79..dfe3c4e0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecodeEvent.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,27 @@ * * 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(long start) + public LTRNetDecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.LTR_NET); } @@ -40,9 +43,9 @@ public class LTRNetDecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.LTR_NET); return decodeEventBuilder; } 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 45c26187..e3bba893 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; @@ -61,9 +58,6 @@ import io.github.dsheirer.module.decode.ltrnet.message.osw.SystemIdle; import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyHigh; import io.github.dsheirer.module.decode.ltrnet.message.osw.TransmitFrequencyLow; import io.github.dsheirer.protocol.Protocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -72,6 +66,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LTRNetDecoderState extends DecoderState { @@ -167,9 +163,24 @@ public class LTRNetDecoderState extends DecoderState if(mCurrentCallTalkgroup == null || !mCurrentCallTalkgroup.equals(talkgroup)) { + DecodeEventType decodeEventType = null; + + if(talkgroup.getTalkgroup() == 253) + { + decodeEventType = DecodeEventType.REGISTER; + } + else if(talkgroup.getTalkgroup() == 254) + { + decodeEventType = DecodeEventType.STATION_ID; + } + else + { + decodeEventType = DecodeEventType.CALL; + } + getIdentifierCollection().remove(IdentifierClass.USER); getIdentifierCollection().update(talkgroup); - mCurrentCallEvent = LTRNetDecodeEvent.builder(timestamp) + mCurrentCallEvent = LTRNetDecodeEvent.builder(decodeEventType, timestamp) .channel(getCurrentChannel()) .identifiers(getIdentifierCollection().copyOf()) .build(); @@ -181,20 +192,14 @@ public class LTRNetDecoderState extends DecoderState if(talkgroup.getTalkgroup() == 253) { - mCurrentCallEvent.setEventType(DecodeEventType.REGISTER); - mCurrentCallEvent.setEventDescription("Register"); broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); } else if(talkgroup.getTalkgroup() == 254) { - mCurrentCallEvent.setEventType(DecodeEventType.STATION_ID); - mCurrentCallEvent.setEventDescription("FCC CWID"); broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); } else { - mCurrentCallEvent.setEventType(DecodeEventType.CALL); - mCurrentCallEvent.setEventDescription("Call"); broadcast(new DecoderStateEvent(this, Event.START, State.CALL)); } @@ -292,77 +297,59 @@ public class LTRNetDecoderState extends DecoderState switch(((LtrNetMessage)message).getLtrNetMessageType()) { case ISW_CALL_END: - if(message instanceof IswCallEnd) + if(message instanceof IswCallEnd callEnd) { - IswCallEnd callEnd = (IswCallEnd)message; mTalkgroups.add(callEnd.getTalkgroup()); processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp()); } break; case ISW_CALL_START: - if(message instanceof IswCallStart) + if(message instanceof IswCallStart callStart) { - IswCallStart callStart = (IswCallStart)message; mTalkgroups.add(callStart.getTalkgroup()); processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(), MessageDirection.ISW); } break; case ISW_REGISTRATION_REQUEST_ESN_HIGH: - if(message instanceof RegistrationRequestEsnHigh) + if(message instanceof RegistrationRequestEsnHigh registrationRequestEsn) { - RegistrationRequestEsnHigh registrationRequestEsn = (RegistrationRequestEsnHigh)message; - if(registrationRequestEsn.isCompleteEsn()) { getIdentifierCollection().update(registrationRequestEsn.getESN()); mESNIdentifiers.add(registrationRequestEsn.getESN()); } - broadcast(getDecodeEvent( - message, DecodeEventType.REQUEST, - "Registration Request", registrationRequestEsn.toString())); + broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); } break; case ISW_REGISTRATION_REQUEST_ESN_LOW: - if(message instanceof RegistrationRequestEsnLow) + if(message instanceof RegistrationRequestEsnLow registrationRequestEsn) { - RegistrationRequestEsnLow registrationRequestEsn = (RegistrationRequestEsnLow)message; - if(registrationRequestEsn.isCompleteEsn()) { getIdentifierCollection().update(registrationRequestEsn.getESN()); mESNIdentifiers.add(registrationRequestEsn.getESN()); } - broadcast(getDecodeEvent( - message, DecodeEventType.REQUEST, - "Registration Request", registrationRequestEsn.toString())); - + broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "REGISTER: " + registrationRequestEsn)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); } break; case ISW_REQUEST_ACCESS: - if(message instanceof RequestAccess) + if(message instanceof RequestAccess requestAccess) { - RequestAccess requestAccess = (RequestAccess)message; - getIdentifierCollection().update(requestAccess.getTalkgroup()); mTalkgroups.add(requestAccess.getTalkgroup()); - - broadcast(getDecodeEvent( - message, DecodeEventType.REQUEST, - "Access Request", requestAccess.toString())); - + broadcast(getDecodeEvent(message, DecodeEventType.REQUEST, "ACCESS: " + requestAccess)); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA)); } break; case ISW_UNIQUE_ID: - if(message instanceof IswUniqueId) + if(message instanceof IswUniqueId iswUniqueId) { - IswUniqueId iswUniqueId = (IswUniqueId)message; getIdentifierCollection().update(iswUniqueId.getUniqueID()); mLtrNetRadioIdentifiers.add(iswUniqueId.getUniqueID()); updateCurrentCallIdentifiers(); @@ -373,99 +360,90 @@ public class LTRNetDecoderState extends DecoderState broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_CALL_END: - if(message instanceof OswCallEnd) + if(message instanceof OswCallEnd callEnd) { - OswCallEnd callEnd = (OswCallEnd)message; mTalkgroups.add(callEnd.getTalkgroup()); processCallEnd(callEnd.getChannel(), callEnd.getTalkgroup(), callEnd.getTimestamp()); } break; case OSW_CALL_START: - if(message instanceof OswCallStart) + if(message instanceof OswCallStart callStart) { - OswCallStart callStart = (OswCallStart)message; mTalkgroups.add(callStart.getTalkgroup()); processCallStart(callStart.getChannel(), callStart.getTalkgroup(), callStart.getTimestamp(), MessageDirection.OSW); } break; case OSW_CHANNEL_MAP_HIGH: - if(message instanceof ChannelMapHigh) + if(message instanceof ChannelMapHigh channelMapHigh) { - mChannelMapHigh = (ChannelMapHigh)message; + mChannelMapHigh = channelMapHigh; } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_CHANNEL_MAP_LOW: - if(message instanceof ChannelMapLow) + if(message instanceof ChannelMapLow channelMapLow) { - mChannelMapLow = (ChannelMapLow)message; + mChannelMapLow = channelMapLow; } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_SYSTEM_IDLE: - if(message instanceof SystemIdle) + if(message instanceof SystemIdle systemIdle) { - setCurrentChannelNumber(((SystemIdle)message).getChannel()); + setCurrentChannelNumber(systemIdle.getChannel()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_NEIGHBOR_ID: - if(message instanceof NeighborId) + if(message instanceof NeighborId neighborId) { - NeighborId neighborId = (NeighborId)message; mNeighborMap.put(neighborId.getNeighborRank(), neighborId); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_RECEIVE_FREQUENCY_HIGH: - if(message instanceof ReceiveFrequencyHigh) + if(message instanceof ReceiveFrequencyHigh receiveFrequency) { - ReceiveFrequencyHigh receiveFrequency = (ReceiveFrequencyHigh)message; updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_RECEIVE_FREQUENCY_LOW: - if(message instanceof ReceiveFrequencyLow) + if(message instanceof ReceiveFrequencyLow receiveFrequency) { - ReceiveFrequencyLow receiveFrequency = (ReceiveFrequencyLow)message; updateReceiveFrequency(receiveFrequency.getChannel(), receiveFrequency.getFrequency()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_REGISTRATION_ACCEPT: - if(message instanceof RegistrationAccept) + if(message instanceof RegistrationAccept registrationAccept) { - RegistrationAccept registrationAccept = (RegistrationAccept)message; mLtrNetRadioIdentifiers.add(registrationAccept.getUniqueID()); MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); ic.remove(IdentifierClass.USER); ic.update(message.getIdentifiers()); - broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE, - "Registration Accept", registrationAccept.toString())); + broadcast(getDecodeEvent(message, DecodeEventType.RESPONSE, "REGISTRATION ACCEPT: " + registrationAccept)); } break; case OSW_SITE_ID: - if(message instanceof SiteId) + if(message instanceof SiteId siteId) { - mCurrentSite = (SiteId)message; + mCurrentSite = siteId; } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_TRANSMIT_FREQUENCY_HIGH: - if(message instanceof TransmitFrequencyHigh) + if(message instanceof TransmitFrequencyHigh transmitFrequency) { - TransmitFrequencyHigh transmitFrequency = (TransmitFrequencyHigh)message; updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); break; case OSW_TRANSMIT_FREQUENCY_LOW: - if(message instanceof TransmitFrequencyLow) + if(message instanceof TransmitFrequencyLow transmitFrequency) { - TransmitFrequencyLow transmitFrequency = (TransmitFrequencyLow)message; updateTransmitFrequency(transmitFrequency.getChannel(), transmitFrequency.getFrequency()); } broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.IDLE)); @@ -477,24 +455,22 @@ public class LTRNetDecoderState extends DecoderState } } - private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String description, String details) { - return LTRNetDecodeEvent.builder(message.getTimestamp()) - .eventType(decodeEventType) - .eventDescription(description) + private DecodeEvent getDecodeEvent(IMessage message, DecodeEventType decodeEventType, String details) + { + return LTRNetDecodeEvent.builder(decodeEventType, message.getTimestamp()) .details(details) .identifiers(getIdentifierCollection().copyOf()) .channel(getCurrentChannel()) .build(); } - private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType) { + private DecodeEvent getDecodeEvent(LTRTalkgroup talkgroup, int channel, long timestamp, DecodeEventType decodeEventType) + { MutableIdentifierCollection ic = new MutableIdentifierCollection(getIdentifierCollection().getIdentifiers()); ic.remove(IdentifierClass.USER); ic.update(talkgroup); - return LTRNetDecodeEvent.builder(timestamp) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) + return LTRNetDecodeEvent.builder(decodeEventType, timestamp) .channel(mChannelMap.get(channel)) .identifiers(ic) .build(); 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 ab9182bc..0b122661 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.function.Function; -import java.util.*; - -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)); } } - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) - { - LtrNetMessage ltr = (LtrNetMessage) message; - - if(mElements.containsKey(ltr.getLtrNetMessageType())) - { - return mElements.get(ltr.getLtrNetMessageType()).isEnabled(); - } - } - - return false; - } - + /** + * Tests for instance of LTR-Net message + * @param message to test + * @return true if correct type. + */ @Override public boolean canProcess(IMessage message) { - return message instanceof LtrNetMessage; + return message instanceof LtrNetMessage && super.canProcess(message); } + /** + * Key extractor for LTR-Net messages + * @return extractor + */ @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @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 index 67a07504..b98ad009 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecodeEvent.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,27 @@ * * 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(long start) + public LTRStandardDecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.LTR); } @@ -40,9 +43,9 @@ public class LTRStandardDecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.LTR); return decodeEventBuilder; } 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 7d8e2b08..a28aa6da 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; @@ -42,9 +39,6 @@ import io.github.dsheirer.module.decode.ltrstandard.message.CallEnd; import io.github.dsheirer.module.decode.ltrstandard.message.Idle; import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage; import io.github.dsheirer.protocol.Protocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +46,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LTRStandardDecoderState extends DecoderState { @@ -82,10 +78,8 @@ public class LTRStandardDecoderState extends DecoderState switch(((LTRMessage)message).getMessageType()) { case CALL: - if(message instanceof Call) + if(message instanceof Call start) { - Call start = (Call)message; - int channel = start.getChannel(); setChannelNumber(channel); mLCNTracker.processFreeChannel(start.getFree()); @@ -98,11 +92,9 @@ public class LTRStandardDecoderState extends DecoderState mCurrentTalkgroup = start.getTalkgroup(); getIdentifierCollection().remove(IdentifierClass.USER); getIdentifierCollection().update(start.getTalkgroup()); - mCurrentCallEvent = LTRStandardDecodeEvent.builder(start.getTimestamp()) + mCurrentCallEvent = LTRStandardDecodeEvent.builder(DecodeEventType.CALL, start.getTimestamp()) .identifiers(getIdentifierCollection().copyOf()) .channel(getCurrentChannel()) - .eventType(DecodeEventType.CALL) - .eventDescription("Call") .build(); } else @@ -115,10 +107,8 @@ public class LTRStandardDecoderState extends DecoderState } break; case CALL_END: - if(message instanceof CallEnd) + if(message instanceof CallEnd end) { - CallEnd end = (CallEnd)message; - mCurrentTalkgroup = null; //Home channel is 31 for call end -- use the free channel as the call end channel @@ -136,10 +126,10 @@ public class LTRStandardDecoderState extends DecoderState } break; case IDLE: - if(message instanceof Idle) + if(message instanceof Idle idle) { mCurrentTalkgroup = null; - mLCNTracker.processCallChannel(((Idle)message).getChannel()); + mLCNTracker.processCallChannel(idle.getChannel()); } break; default: 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 af409e5e..7d27e835 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.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.ltrstandard.message.LTRMessage; +import java.util.function.Function; -import java.util.*; - -public class LTRStandardMessageFilter extends Filter +/** + * 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"); + super("LTR Messages"); - mElements.put(LtrStandardMessageType.CALL, new FilterElement<>(LtrStandardMessageType.CALL)); - mElements.put(LtrStandardMessageType.CALL_END, new FilterElement<>(LtrStandardMessageType.CALL_END)); - mElements.put(LtrStandardMessageType.IDLE, new FilterElement<>(LtrStandardMessageType.IDLE)); - mElements.put(LtrStandardMessageType.UNKNOWN, new FilterElement<>(LtrStandardMessageType.UNKNOWN)); - } - - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) + for(LtrStandardMessageType type: LtrStandardMessageType.values()) { - LTRMessage ltr = (LTRMessage)message; - - if(mElements.containsKey(ltr.getMessageType())) - { - return mElements.get(ltr.getMessageType()).isEnabled(); - } + add(new FilterElement<>(type)); } - - return false; } public boolean canProcess(IMessage message) { - return message instanceof LTRMessage; + return message instanceof LTRMessage && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @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 index bd7514f8..8187c717 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/mdc1200/MDCDecodeEvent.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,28 @@ * * 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 a P25 decode event - * @param start + * Constucts an MDC1200 decode event + * @param decodeEventType for the event + * @param start for the event */ - public MDCDecodeEvent(long start) + public MDCDecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.MDC1200); } @@ -40,9 +44,9 @@ public class MDCDecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.MDC1200); return decodeEventBuilder; } 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 f4254e2f..f9a07905 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; @@ -31,7 +28,6 @@ import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.mdc1200.identifier.MDC1200Identifier; - import java.util.Iterator; import java.util.Set; import java.util.TreeSet; @@ -114,9 +110,7 @@ public class MDCDecoderState extends DecoderState ic.remove(IdentifierClass.USER); ic.update(message.getIdentifiers()); - broadcast(MDCDecodeEvent.builder(mdc.getTimestamp()) - .eventType(getDecodeEventType(type)) - .eventDescription(type.getLabel()) + broadcast(MDCDecodeEvent.builder(getDecodeEventType(type), mdc.getTimestamp()) .details(mdc.toString()) .identifiers(ic) .build()); 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 45b4d3af..795f5a46 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.function.Function; -import java.util.*; - -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) - { - if(mEnabled && canProcess(message)) - { - MDCMessage mdc = (MDCMessage) message; - - if(mElements.containsKey(mdc.getMessageType())) - { - return mElements.get(mdc.getMessageType()).isEnabled(); - } - } - - return false; - } - @Override public boolean canProcess(IMessage message) { - return message instanceof MDCMessage; + return message instanceof MDCMessage && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @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 e65fe219..7e249d07 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,21 +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.event.DecodeEventType; -import io.github.dsheirer.module.decode.p25.P25DecodeEvent; import io.github.dsheirer.protocol.Protocol; -public class MPT1327ChannelGrantEvent extends P25DecodeEvent +/** + * MPT1327 Channel Grant Event + */ +public class MPT1327ChannelGrantEvent extends MPT1327DecodeEvent { - public MPT1327ChannelGrantEvent(long timestamp) + public MPT1327ChannelGrantEvent(DecodeEventType decodeEventType, long timestamp) { - super(timestamp); + super(decodeEventType, timestamp); } /** @@ -37,9 +38,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent * @param timeStart for the event * @return builder */ - public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(long timeStart) + public static MPT1327ChannelGrantDecodeEventBuilder mpt1327Builder(DecodeEventType decodeEventType, long timeStart) { - return new MPT1327ChannelGrantDecodeEventBuilder(timeStart); + return new MPT1327ChannelGrantDecodeEventBuilder(decodeEventType, timeStart); } /** @@ -49,7 +50,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent { protected long mTimeStart; protected long mDuration; - protected String mEventDescription; protected DecodeEventType mDecodeEventType; protected IdentifierCollection mIdentifierCollection; protected IChannelDescriptor mChannelDescriptor; @@ -60,8 +60,9 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent * * @param timeStart */ - public MPT1327ChannelGrantDecodeEventBuilder(long timeStart) + public MPT1327ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart) { + mDecodeEventType = decodeEventType; mTimeStart = timeStart; } @@ -95,25 +96,6 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent return this; } - /** - * Sets the Decode Event type for this event. - * @param eventType - */ - public MPT1327ChannelGrantDecodeEventBuilder eventType(DecodeEventType eventType) { - mDecodeEventType = eventType; - return this; - } - - /** - * Sets the event description text - * @param description of the event - */ - public MPT1327ChannelGrantDecodeEventBuilder eventDescription(String description) - { - mEventDescription = description; - return this; - } - /** * Sets the identifier collection. * @param identifierCollection containing optional identifiers like TO, FROM, frequency and @@ -140,13 +122,11 @@ public class MPT1327ChannelGrantEvent extends P25DecodeEvent */ public MPT1327ChannelGrantEvent build() { - MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mTimeStart); + MPT1327ChannelGrantEvent decodeEvent = new MPT1327ChannelGrantEvent(mDecodeEventType, mTimeStart); decodeEvent.setProtocol(Protocol.MPT1327); decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setDetails(mDetails); decodeEvent.setDuration(mDuration); - decodeEvent.setEventType(mDecodeEventType); - decodeEvent.setEventDescription(mEventDescription); decodeEvent.setIdentifierCollection(mIdentifierCollection); return decodeEvent; } 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 index 3a662e4e..57afa590 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecodeEvent.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,27 @@ * * 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(long start) + public MPT1327DecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.MPT1327); } @@ -40,9 +43,9 @@ public class MPT1327DecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.MPT1327); return decodeEventBuilder; } 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 59fd1d9e..95b1fc63 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 @@ -33,14 +33,13 @@ import io.github.dsheirer.module.decode.event.DecodeEvent; import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.mpt1327.channel.MPT1327Channel; import io.github.dsheirer.util.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.TreeSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MPT1327DecoderState extends DecoderState { @@ -198,9 +197,7 @@ public class MPT1327DecoderState extends DecoderState private DecodeEvent getDecodeEvent(IMessage message, MPT1327Message mpt, DecodeEventType decodeEventType, String details) { MutableIdentifierCollection ic = getUpdatedMutableIdentifierCollection(mpt); - return MPT1327DecodeEvent.builder(message.getTimestamp()) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) + return MPT1327DecodeEvent.builder(decodeEventType, message.getTimestamp()) .details(details) .identifiers(ic) .build(); 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 bfd596aa..159f57ee 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 @@ package io.github.dsheirer.module.decode.mpt1327; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; -import io.github.dsheirer.message.MessageType; +import java.util.function.Function; -import java.util.*; - -public class MPT1327MessageFilter extends Filter +/** + * 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) - { - if(mEnabled && canProcess(message)) - { - MPT1327Message mpt = (MPT1327Message) message; - - FilterElement element = - mFilterElements.get(mpt.getMessageType()); - - if(element != null) - { - return element.isEnabled(); - } - } - - return false; - } - @Override public boolean canProcess(IMessage message) { - return message instanceof MPT1327Message; + return message instanceof MPT1327Message && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mFilterElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @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 b59ef8b4..9e1f45ce 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 @@ -104,10 +104,9 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen } } - MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent.mpt1327Builder(mpt1327Message.getTimestamp()) + MPT1327ChannelGrantEvent channelGrantEvent = MPT1327ChannelGrantEvent + .mpt1327Builder(DecodeEventType.CALL, mpt1327Message.getTimestamp()) .channel(mpt1327Channel) - .eventType(DecodeEventType.CALL) - .eventDescription("Call") .details("Traffic Channel Grant") .identifiers(identifierCollection) .build(); @@ -125,7 +124,7 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen if(trafficChannel == null) { channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED); - channelGrantEvent.setEventDescription("Detect:" + channelGrantEvent.getEventDescription()); + channelGrantEvent.setDetails("Detect:" + channelGrantEvent.getDetails()); return; } @@ -152,10 +151,8 @@ public class MPT1327TrafficChannelManager extends TrafficChannelManager implemen DecodeConfiguration decodeConfiguration = parentChannel.getDecodeConfiguration(); List 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 d6f55655..4b15afad 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 class P25ChannelGrantEvent extends P25DecodeEvent * @param timeStart for the event * @return builder */ - public static P25ChannelGrantDecodeEventBuilder builder(long timeStart, ServiceOptions serviceOptions) + public static P25ChannelGrantDecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions) { - return new P25ChannelGrantDecodeEventBuilder(timeStart, serviceOptions); + return new P25ChannelGrantDecodeEventBuilder(decodeEventType, timeStart, serviceOptions); } /** @@ -76,7 +75,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent { protected long mTimeStart; protected long mDuration; - protected String mEventDescription; protected DecodeEventType mDecodeEventType; protected IdentifierCollection mIdentifierCollection; protected IChannelDescriptor mChannelDescriptor; @@ -88,8 +86,9 @@ public class P25ChannelGrantEvent extends P25DecodeEvent * * @param timeStart */ - public P25ChannelGrantDecodeEventBuilder(long timeStart, ServiceOptions serviceOptions) + public P25ChannelGrantDecodeEventBuilder(DecodeEventType decodeEventType, long timeStart, ServiceOptions serviceOptions) { + mDecodeEventType = decodeEventType; mTimeStart = timeStart; mServiceOptions = serviceOptions; } @@ -124,26 +123,6 @@ public class P25ChannelGrantEvent extends P25DecodeEvent return this; } - /** - * Sets the event description text - * @param description of the event - */ - public P25ChannelGrantDecodeEventBuilder eventDescription(String description) - { - mEventDescription = description; - return this; - } - - /** - * Sets the {@link DecodeEventType} - * @param decodeEventType of the event - */ - public P25ChannelGrantDecodeEventBuilder eventType(DecodeEventType decodeEventType) - { - mDecodeEventType = decodeEventType; - return this; - } - /** * Sets the identifier collection. * @param identifierCollection containing optional identifiers like TO, FROM, frequency and @@ -170,13 +149,11 @@ public class P25ChannelGrantEvent extends P25DecodeEvent */ public P25ChannelGrantEvent build() { - P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mTimeStart); + P25ChannelGrantEvent decodeEvent = new P25ChannelGrantEvent(mDecodeEventType, mTimeStart); decodeEvent.setProtocol(Protocol.APCO25); decodeEvent.setChannelDescriptor(mChannelDescriptor); decodeEvent.setDetails(mDetails); decodeEvent.setDuration(mDuration); - decodeEvent.setEventType(mDecodeEventType); - decodeEvent.setEventDescription(mEventDescription); decodeEvent.setIdentifierCollection(mIdentifierCollection); decodeEvent.setServiceOptions(mServiceOptions); return decodeEvent; 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 6ce0090e..d22aeb4e 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,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.p25; import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.protocol.Protocol; public class P25DecodeEvent extends DecodeEvent @@ -29,9 +29,9 @@ public class P25DecodeEvent extends DecodeEvent * Constructs a P25 decode event * @param start */ - public P25DecodeEvent(long start) + public P25DecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.APCO25); } @@ -40,9 +40,9 @@ public class P25DecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.APCO25); return decodeEventBuilder; } 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 ff3bcb84..5018f079 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 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I { event.end(timestamp); - P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString() + " - Continue") - .details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) + .details("CONTINUE - PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -306,7 +304,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(trafficChannel != null) { - event.setEventDescription(decodeEventType.toString()); event.setDetails("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")); event.setChannelDescriptor(apco25Channel); broadcast(event); @@ -327,10 +324,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(mIgnoreDataCalls && opcode.isDataChannelGrant()) { - P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString() + " - Ignored") .details("DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -340,10 +335,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I return; } - P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) .details("PHASE 1 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -357,8 +350,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(trafficChannel == null) { - channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED); - channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored"); + channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED"); return; } @@ -427,11 +419,9 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I { event.end(timestamp); - P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent continuationGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString() + " - Continue") - .details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) + .details("CONTINUE - PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -460,7 +450,6 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(trafficChannel != null) { - event.setEventDescription(decodeEventType.toString()); event.setDetails("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")); event.setChannelDescriptor(apco25Channel); broadcast(event); @@ -488,10 +477,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(mIgnoreDataCalls && opcode.isDataChannelGrant()) { - P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString() + " - Ignored") .details("PHASE 2 DATA CALL IGNORED: " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -502,10 +489,8 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I return; } - P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(timestamp, serviceOptions) + P25ChannelGrantEvent channelGrantEvent = P25ChannelGrantEvent.builder(decodeEventType, timestamp, serviceOptions) .channel(apco25Channel) - .eventType(decodeEventType) - .eventDescription(decodeEventType.toString()) .details("PHASE 2 CHANNEL GRANT " + (serviceOptions != null ? serviceOptions : "")) .identifiers(identifierCollection) .build(); @@ -526,8 +511,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if(trafficChannel == null) { - channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED); - channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored"); + channelGrantEvent.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED + " - IGNORED"); return; } @@ -836,17 +820,7 @@ public class P25TrafficChannelManager extends TrafficChannelManager implements I if (event != null) { - event.setEventDescription(event.getEventDescription() + " - Rejected"); - - if (channelEvent.getDescription() != null) - { - event.setDetails(channelEvent.getDescription() + " - " + event.getDetails()); - } - else - { - event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails()); - } - + event.setDetails(CHANNEL_START_REJECTED + " - " + event.getDetails()); broadcast(event); } }); 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 28bbfb51..1b4019bc 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 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList 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 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList /** * Updates or creates a current call event. * - * @param type of call that will be used as an event description + * @param decodeEventType of call that will be used as an event description * @param details of the call (optional) * @param timestamp of the message indicating a call or continuation */ - private void updateCurrentCall(DecodeEventType type, String details, long timestamp) + private void updateCurrentCall(DecodeEventType decodeEventType, String details, long timestamp) { if(mCurrentCallEvent == null) { - mCurrentCallEvent = P25DecodeEvent.builder(timestamp) + mCurrentCallEvent = P25DecodeEvent.builder(DecodeEventType.CALL, timestamp) .channel(getCurrentChannel()) - .eventDescription(type.toString()) .details(details) .identifiers(getIdentifierCollection().copyOf()) .build(); @@ -791,9 +788,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList mCurrentCallEvent.end(timestamp); broadcast(mCurrentCallEvent); - if(type == DecodeEventType.CALL_ENCRYPTED) + if(decodeEventType == DecodeEventType.CALL_ENCRYPTED) { - mCurrentCallEvent.setEventDescription(type.toString()); mCurrentCallEvent.setDetails(details); broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED)); } @@ -838,12 +834,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList */ private void processPDU(P25Message message) { - if(message.isValid() && message instanceof PDUMessage) + if(message.isValid() && message instanceof PDUMessage pdu) { - PDUMessage pdu = (PDUMessage)message; - processBroadcast(pdu.getIdentifiers(), message.getTimestamp(), DecodeEventType.DATA_PACKET, pdu.toString()); - } broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); @@ -856,10 +849,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList */ private void processUMBTC(P25Message message) { - if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing) + if(message.isValid() && message instanceof UMBTCTelephoneInterconnectRequestExplicitDialing tired) { - UMBTCTelephoneInterconnectRequestExplicitDialing tired = (UMBTCTelephoneInterconnectRequestExplicitDialing)message; - processBroadcast(tired.getIdentifiers(), tired.getTimestamp(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT:" + tired.getTelephoneNumber()); } @@ -910,11 +901,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.AUTOMATIC_REGISTRATION_SERVICE.toString()) .identifiers(ic) - .details(arsPacket.toString() + " " + ipv4.toString()) + .details(arsPacket + " " + ipv4) .build(); broadcast(packetEvent); @@ -929,11 +919,10 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent cellocatorEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent cellocatorEvent = P25DecodeEvent.builder(DecodeEventType.CELLOCATOR, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription("Cellocator") .identifiers(ic) - .details(mcgpPacket.toString() + " " + ipv4.toString()) + .details(mcgpPacket + " " + ipv4) .build(); broadcast(cellocatorEvent); @@ -942,10 +931,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList { MutableIdentifierCollection ic = new MutableIdentifierCollection(packet.getIdentifiers()); - DecodeEvent lrrpEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent lrrpEvent = P25DecodeEvent.builder(DecodeEventType.LRRP, message.getTimestamp()) .channel(getCurrentChannel()) .details(lrrpPacket + " " + ipv4) - .eventDescription("LRRP") .identifiers(ic) .protocol(Protocol.LRRP) .build(); @@ -956,9 +944,9 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList if(geoPosition != null) { - PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent.plottableBuilder(message.getTimestamp()) + PlottableDecodeEvent plottableDecodeEvent = PlottableDecodeEvent + .plottableBuilder(DecodeEventType.GPS, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.GPS.toString()) .identifiers(ic) .protocol(Protocol.LRRP) .location(geoPosition) @@ -975,9 +963,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UDP_PACKET, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.UDP_PACKET.toString()) .identifiers(ic) .details(ipv4.toString()) .build(); @@ -993,9 +980,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.ICMP_PACKET, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.ICMP_PACKET.toString()) .identifiers(ic) .details(ipv4.toString()) .build(); @@ -1010,9 +996,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.IP_PACKET, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.IP_PACKET.toString()) .identifiers(ic) .details(ipv4.toString()) .build(); @@ -1028,9 +1013,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList ic.update(identifier); } - DecodeEvent packetEvent = P25DecodeEvent.builder(message.getTimestamp()) + DecodeEvent packetEvent = P25DecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, message.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.UNKNOWN_PACKET.toString()) .identifiers(ic) .details(packet.toString()) .build(); @@ -1376,9 +1360,8 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList private void processTSBKRoamingAddressRequest(TSBKMessage tsbk) { //TODO: not sure if this should be used or not. - broadcast(P25DecodeEvent.builder(tsbk.getTimestamp()) + broadcast(P25DecodeEvent.builder(DecodeEventType.REQUEST, tsbk.getTimestamp()) .channel(getCurrentChannel()) - .eventDescription(DecodeEventType.REQUEST.toString()) .details("ROAMING ADDRESS") // TODO: This identifierCollection is different from all the others. .identifiers(new IdentifierCollection(tsbk.getIdentifiers())) 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 ccc83701..7e91bd05 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.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.phase1.message.hdu.HDUMessage; +import java.util.function.Function; -import java.util.Collections; -import java.util.List; - -public class HDUMessageFilter extends Filter +/** + * Filter for HDU header messages + */ +public class HeaderMessageFilter extends Filter { - public HDUMessageFilter() - { - super("Header Data Unit"); - } + private static final String HDU_KEY = "Header Data Unit (HDU)"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); - @Override - public boolean passes(IMessage message) + /** + * Constructor + */ + public HeaderMessageFilter() { - return mEnabled && canProcess(message); + super("Header Messages"); + add(new FilterElement<>(HDU_KEY)); } @Override public boolean canProcess(IMessage message) { - return message instanceof HDUMessage; + return message instanceof HDUMessage && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return Collections.EMPTY_LIST; + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public String apply(IMessage message) + { + if(message instanceof HDUMessage) + { + return HDU_KEY; + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/IPPacketMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/IPPacketMessageFilter.java deleted file mode 100644 index f22d0c85..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/IPPacketMessageFilter.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.packet.PacketMessage; - -import java.util.Collections; -import java.util.List; - -public class IPPacketMessageFilter extends Filter -{ - public IPPacketMessageFilter() - { - super("Packet Message"); - } - - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); - } - - @Override - public boolean canProcess(IMessage message) - { - return message instanceof PacketMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/LDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25OtherMessageFilter.java similarity index 51% rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/LDUMessageFilter.java rename to src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25OtherMessageFilter.java index 6f7fb6cd..d130370e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/LDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25OtherMessageFilter.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; @@ -23,33 +22,52 @@ package io.github.dsheirer.module.decode.p25.phase1.message.filter; import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; -import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.P25Message; +import java.util.function.Function; -import java.util.Collections; -import java.util.List; - -public class LDUMessageFilter extends Filter +/** + * Filter for unknown messages, meaning messages not handled by any other filter. + */ +public class P25OtherMessageFilter extends Filter { - public LDUMessageFilter() + private static final String OTHER_KEY = "Other/Unknown Message"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ + public P25OtherMessageFilter() { - super("Link Data Unit"); + super("Other P25 messages"); + add(new FilterElement<>(OTHER_KEY)); } @Override - public boolean passes(IMessage message) + public Function getKeyExtractor() { - return mEnabled && canProcess(message); + return mKeyExtractor; } @Override public boolean canProcess(IMessage message) { - return message instanceof LDUMessage; + return message instanceof P25Message && super.canProcess(message); } - @Override - public List> getFilterElements() + /** + * Key extractor + */ + private class KeyExtractor implements Function { - return Collections.EMPTY_LIST; + @Override + public String apply(IMessage message) + { + if(message instanceof P25Message) + { + return OTHER_KEY; + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25MessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25P1MessageFilterSet.java similarity index 66% rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25MessageFilterSet.java rename to src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25P1MessageFilterSet.java index 28dd12c2..faa5f842 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25MessageFilterSet.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/P25P1MessageFilterSet.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,35 +14,42 @@ * * 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.FilterSet; +import io.github.dsheirer.filter.SyncLossMessageFilter; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.message.SyncLossMessage; import io.github.dsheirer.module.decode.p25.phase1.message.P25Message; -public class P25MessageFilterSet extends FilterSet +/** + * Filter set for P25 messages + */ +public class P25P1MessageFilterSet extends FilterSet { - public P25MessageFilterSet() + public P25P1MessageFilterSet() { super("P25 Message Filter"); - addFilter(new AMBTCMessageFilter()); - addFilter(new HDUMessageFilter()); - addFilter(new IPPacketMessageFilter()); - addFilter(new LDUMessageFilter()); + addFilter(new HeaderMessageFilter()); + addFilter(new PacketMessageFilter()); addFilter(new PDUMessageFilter()); addFilter(new SNDCPMessageFilter()); addFilter(new SyncLossMessageFilter()); - addFilter(new TDUMessageFilter()); - addFilter(new TDULCMessageFilter()); - addFilter(new TSBKMessageFilterSet()); - addFilter(new UMBTCMessageFilter()); + addFilter(new TerminatorMessageFilterSet()); + addFilter(new TrunkingOpcodeMessageFilterSet()); + addFilter(new VoiceMessageFilter()); + addFilter(new P25OtherMessageFilter()); } + /** + * Override default to descope handling to P25 or sync-loss messages. + * @param message to test + * @return true if the message can be processed + */ @Override public boolean canProcess(IMessage message) { diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PDUMessageFilter.java index 1f447070..7a0eaec9 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PDUMessageFilter.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,49 +23,52 @@ import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessage; -import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode; -import io.github.dsheirer.protocol.Protocol; +import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage; +import java.util.function.Function; -import java.util.*; - -public class PDUMessageFilter extends Filter +/** + * Filter for PDU packet data unit messages + */ +public class PDUMessageFilter extends Filter { - private Map> mOpcodeFilterElements = new EnumMap<>(Opcode.class); + private static final String PDU_KEY = "Packet Data Unit (PDU)"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + /** + * Constructor + */ public PDUMessageFilter() { - super("PDU - Packet Data Unit"); - - for(Opcode opcode : Opcode.values()) - { - mOpcodeFilterElements.put(opcode, new FilterElement(opcode)); - } + super("Packet Data Unit messages"); + add(new FilterElement<>(PDU_KEY)); } @Override - public boolean passes(IMessage message) + public Function getKeyExtractor() { - if(mEnabled && message instanceof PDUMessage) - { - PDUMessage pdu = (PDUMessage) message; - - Opcode opcode = pdu.getOpcode(); - - return mOpcodeFilterElements.get(opcode).isEnabled(); - } - - return false; + return mKeyExtractor; } @Override public boolean canProcess(IMessage message) { - return message instanceof PDUMessage; + return message instanceof TDUMessage && super.canProcess(message); } - @Override - public List> getFilterElements() + /** + * Key extractor + */ + private class KeyExtractor implements Function { - return new ArrayList>(mOpcodeFilterElements.values()); + @Override + public String apply(IMessage message) + { + if(message instanceof PDUMessage) + { + return PDU_KEY; + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PacketMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PacketMessageFilter.java new file mode 100644 index 00000000..0e603473 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/PacketMessageFilter.java @@ -0,0 +1,135 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.phase1.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.ip.IPacket; +import io.github.dsheirer.module.decode.ip.cellocator.MCGPPacket; +import io.github.dsheirer.module.decode.ip.icmp.ICMPPacket; +import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet; +import io.github.dsheirer.module.decode.ip.mototrbo.ars.ARSPacket; +import io.github.dsheirer.module.decode.ip.mototrbo.lrrp.LRRPPacket; +import io.github.dsheirer.module.decode.ip.mototrbo.xcmp.XCMPPacket; +import io.github.dsheirer.module.decode.ip.udp.UDPPacket; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.PacketMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPPacketMessage; +import java.util.function.Function; + +/** + * Filter for packet messages + */ +public class PacketMessageFilter extends Filter +{ + private static final String KEY_CELLOCATOR = "Cellocator Packet"; + private static final String KEY_IPV4_OTHER = "IPV4 Other Packet"; + private static final String KEY_IPV4_UDP_ICMP = "IPV4 UDP/IP ICMP Packet"; + private static final String KEY_IPV4_UDP_OTHER = "IPV4 UDP/IP Other Packet"; + private static final String KEY_MOTOROLA_ARS = "Motorola Registration (ARS) Packet"; + private static final String KEY_MOTOROLA_LRRP = "Motorola Location Report (LRRP) Packet"; + private static final String KEY_MOTOROLA_XCMP = "Motorola Extensible (XCMP) Packet"; + private static final String KEY_SNDCP = "SNDCP Data Packet"; + private static final String KEY_UNKNOWN = "Unknown Packet"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ + public PacketMessageFilter() + { + super("Packet Messages"); + add(new FilterElement<>(KEY_CELLOCATOR)); + add(new FilterElement<>(KEY_IPV4_OTHER)); + add(new FilterElement<>(KEY_IPV4_UDP_ICMP)); + add(new FilterElement<>(KEY_IPV4_UDP_OTHER)); + add(new FilterElement<>(KEY_MOTOROLA_ARS)); + add(new FilterElement<>(KEY_MOTOROLA_LRRP)); + add(new FilterElement<>(KEY_MOTOROLA_XCMP)); + add(new FilterElement<>(KEY_SNDCP)); + add(new FilterElement<>(KEY_UNKNOWN)); + } + + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof PacketMessage && super.canProcess(message); + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public String apply(IMessage message) + { + if(message instanceof PacketMessage packetMessage) + { + IPacket packet = packetMessage.getPacket(); + + if(packet instanceof MCGPPacket) + { + return KEY_CELLOCATOR; + } + else if(packet instanceof ICMPPacket) + { + return KEY_IPV4_UDP_ICMP; + } + else if(packet instanceof UDPPacket) + { + return KEY_IPV4_UDP_OTHER; + } + else if(packet instanceof IPV4Packet) + { + return KEY_IPV4_OTHER; + } + else if(packet instanceof SNDCPPacketMessage) + { + return KEY_SNDCP; + } + else if(packet instanceof ARSPacket) + { + return KEY_MOTOROLA_ARS; + } + else if(packet instanceof LRRPPacket) + { + return KEY_MOTOROLA_LRRP; + } + else if(packet instanceof XCMPPacket) + { + return KEY_MOTOROLA_XCMP; + } + + else + { + return KEY_UNKNOWN; + } + } + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SNDCPMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SNDCPMessageFilter.java index 00bb3469..ecae7ce1 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SNDCPMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/SNDCPMessageFilter.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,55 @@ import io.github.dsheirer.filter.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.phase1.message.pdu.packet.sndcp.SNDCPMessage; +import io.github.dsheirer.module.decode.p25.reference.PDUType; +import java.util.function.Function; -import java.util.Collections; -import java.util.List; - -public class SNDCPMessageFilter extends Filter +/** + * Filter for SNDCP messages + */ +public class SNDCPMessageFilter extends Filter { + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ public SNDCPMessageFilter() { - super("Sub-Network Dependent Convergence Protocol"); - } + super("Sub-Network Dependent Convergence Protocol (SNDCP) Messages"); - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); + for(PDUType pduType: PDUType.values()) + { + add(new FilterElement<>(pduType)); + } } @Override public boolean canProcess(IMessage message) { - return message instanceof SNDCPMessage; + return message instanceof SNDCPMessage && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return Collections.EMPTY_LIST; + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public PDUType apply(IMessage message) + { + if(message instanceof SNDCPMessage sndcp) + { + return sndcp.getPDUType(); + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDULCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDULCMessageFilter.java deleted file mode 100644 index c1c6b04f..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDULCMessageFilter.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.tdu.TDULinkControlMessage; - -import java.util.Collections; -import java.util.List; - -public class TDULCMessageFilter extends Filter -{ - public TDULCMessageFilter() - { - super("TDU Terminator Data Unit with Link Control"); - } - - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); - } - - @Override - public boolean canProcess(IMessage message) - { - return message instanceof TDULinkControlMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDUMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDUMessageFilter.java index f47bf304..44794e83 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDUMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TDUMessageFilter.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.Filter; import io.github.dsheirer.filter.FilterElement; import io.github.dsheirer.message.IMessage; import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage; +import java.util.function.Function; -import java.util.Collections; -import java.util.List; - -public class TDUMessageFilter extends Filter +/** + * Filter for TDU terminator (no link control) messages + */ +public class TDUMessageFilter extends Filter { + private static final String TDU_KEY = "Terminator Data Unit (TDU)"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ public TDUMessageFilter() { - super("TDU Terminator Data Unit"); + super("Terminator/No Link Control messages"); + add(new FilterElement<>(TDU_KEY)); } @Override - public boolean passes(IMessage message) + public Function getKeyExtractor() { - return mEnabled && canProcess(message); + return mKeyExtractor; } @Override public boolean canProcess(IMessage message) { - return message instanceof TDUMessage; + return message instanceof TDUMessage && super.canProcess(message); } - @Override - public List> getFilterElements() + /** + * Key extractor + */ + private class KeyExtractor implements Function { - return Collections.EMPTY_LIST; + @Override + public String apply(IMessage message) + { + if(message instanceof TDUMessage) + { + return TDU_KEY; + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TSBKMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TSBKMessageFilterSet.java deleted file mode 100644 index 2efe15ce..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TSBKMessageFilterSet.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * ****************************************************************************** - * sdrtrunk - * Copyright (C) 2014-2019 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - * ***************************************************************************** - */ - -package io.github.dsheirer.module.decode.p25.phase1.message.filter; - -import io.github.dsheirer.filter.Filter; -import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.filter.FilterSet; -import io.github.dsheirer.message.IMessage; -import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode; -import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage; -import io.github.dsheirer.module.decode.p25.reference.Vendor; - -import java.util.*; - -public class TSBKMessageFilterSet extends FilterSet -{ - public TSBKMessageFilterSet() - { - super("TSBK Trunking Signalling Block"); - - addFilter(new StandardOpcodeFilter()); - } - -// @Override -// public boolean passes( Message message ) -// { -// if( mEnabled && canProcess( message ) ) -// { -// TSBKMessage tsbk = (TSBKMessage) message; -// -// switch( tsbk.getVendor() ) -// { -// case STANDARD: -// return -// } -// Opcode opcode = tsbk.getOpcode(); -// -// return mFilterElements.get( opcode ).isEnabled(); -// } -// -// return false; -// } - - @Override - public boolean canProcess(IMessage message) - { - return message instanceof TSBKMessage; - } - - public class StandardOpcodeFilter extends Filter - { - private Map> mStandardElements = new EnumMap<>(Opcode.class); - - public StandardOpcodeFilter() - { - super("Standard Opcode Filter"); - - for(Opcode opcode : Opcode.values()) - { - if(opcode != Opcode.OSP_UNKNOWN) - { - mStandardElements.put(opcode, new FilterElement(opcode)); - } - } - } - - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) - { - Opcode opcode = ((TSBKMessage) message).getOpcode(); - - if(mStandardElements.containsKey(opcode)) - { - return mStandardElements.get(opcode).isEnabled(); - } - } - - return false; - } - - @Override - public boolean canProcess(IMessage message) - { - return ((TSBKMessage) message).getVendor() == Vendor.STANDARD; - } - - @Override - public List> getFilterElements() - { - return new ArrayList>(mStandardElements.values()); - } - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilter.java new file mode 100644 index 00000000..be1df793 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilter.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.p25.phase1.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode; +import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage; +import java.util.Collection; +import java.util.function.Function; + +/** + * Filter for TDU terminator and TDULC terminator with link control messages + */ +public class TerminatorMessageFilter extends Filter +{ + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructs an instance + * + * @param name of this filter + */ + public TerminatorMessageFilter(String name, Collection opcodes) + { + super(name); + + for(LinkControlOpcode opcode: opcodes) + { + add(new FilterElement<>(opcode)); + } + } + + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public LinkControlOpcode apply(IMessage message) + { + if(message instanceof TDULinkControlMessage tdulc) + { + return tdulc.getLinkControlWord().getOpcode(); + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilterSet.java new file mode 100644 index 00000000..b87a9a35 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TerminatorMessageFilterSet.java @@ -0,0 +1,63 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.phase1.message.filter; + +import io.github.dsheirer.filter.FilterSet; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.lc.LinkControlOpcode; +import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDULinkControlMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.tdu.TDUMessage; +import java.util.ArrayList; +import java.util.List; + +/** + * Filter set for terminator/link control messages + */ +public class TerminatorMessageFilterSet extends FilterSet +{ + /** + * Constructor + */ + public TerminatorMessageFilterSet() + { + super("Terminator Messages"); + addFilter(new TDUMessageFilter()); + addFilter(new TerminatorMessageFilter("Command/Status", LinkControlOpcode.COMMAND_STATUS_OPCODES)); + addFilter(new TerminatorMessageFilter("Network/Channel", LinkControlOpcode.NETWORK_OPCODES)); + addFilter(new TerminatorMessageFilter("Voice", LinkControlOpcode.VOICE_OPCODES)); + addFilter(new TerminatorMessageFilter("Motorola", LinkControlOpcode.MOTOROLA_OPCODES)); + + List others = new ArrayList<>(); + for(LinkControlOpcode opcode: LinkControlOpcode.values()) + { + if(!opcode.isGrouped()) + { + others.add(opcode); + } + } + addFilter(new TerminatorMessageFilter("Other/Reserved", others)); + } + + @Override + public boolean canProcess(IMessage message) + { + return (message instanceof TDUMessage || message instanceof TDULinkControlMessage) && super.canProcess(message); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilter.java new file mode 100644 index 00000000..bee5291b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilter.java @@ -0,0 +1,91 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2023 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package io.github.dsheirer.module.decode.p25.phase1.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage; +import java.util.Collection; +import java.util.function.Function; + +/** + * Message filter for P25 TSBK, AMBTC and UMBTC trunking control messages. + */ +public class TrunkingOpcodeMessageFilter extends Filter +{ + private KeyExtractor mKeyExtractor = new KeyExtractor(); + private Collection mOpcodes; + + /** + * Constructs an instance + * + * @param name of this filter + */ + public TrunkingOpcodeMessageFilter(String name, Collection opcodes) + { + super(name); + + mOpcodes = opcodes; + + for(Opcode opcode: opcodes) + { + add(new FilterElement<>(opcode)); + } + } + + /** + * Key extractor + * @return opcode extractor + */ + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + /** + * Key extractor for Opcode values from TSBK, AMBTC and UMBTC messages. + */ + private class KeyExtractor implements Function + { + @Override + public Opcode apply(IMessage message) + { + if(message instanceof TSBKMessage tsbk) + { + return tsbk.getOpcode(); + } + else if(message instanceof AMBTCMessage ambtc) + { + return ambtc.getHeader().getOpcode(); + } + else if(message instanceof UMBTCMessage umbtc) + { + return umbtc.getOpcode(); + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilterSet.java new file mode 100644 index 00000000..ed7be3ba --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/TrunkingOpcodeMessageFilterSet.java @@ -0,0 +1,65 @@ +/* + * ***************************************************************************** + * 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.p25.phase1.message.filter; + +import io.github.dsheirer.filter.FilterSet; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.pdu.umbtc.UMBTCMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.Opcode; +import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage; +import java.util.List; + +/** + * Filter set for trunking messages + */ +public class TrunkingOpcodeMessageFilterSet extends FilterSet +{ + /** + * Constructor + */ + public TrunkingOpcodeMessageFilterSet() + { + super("Trunking Messages"); + addFilter(new TrunkingOpcodeMessageFilter("Mobile Data Requests", Opcode.MOBILE_DATA_REQUESTS)); + addFilter(new TrunkingOpcodeMessageFilter("Mobile Request/Response", Opcode.MOBILE_REQUEST_RESPONSE)); + addFilter(new TrunkingOpcodeMessageFilter("Mobile Voice Requests", Opcode.MOBILE_VOICE_REQUESTS)); + addFilter(new TrunkingOpcodeMessageFilter("Network/Channel Status", Opcode.NETWORK_CHANNEL_STATUS)); + addFilter(new TrunkingOpcodeMessageFilter("Network Data Grants", Opcode.NETWORK_DATA_GRANTS)); + addFilter(new TrunkingOpcodeMessageFilter("Network Command/Request/Response", Opcode.NETWORK_COMMAND_REQUEST_RESPONSE)); + addFilter(new TrunkingOpcodeMessageFilter("Network Voice Grants", Opcode.NETWORK_VOICE_GRANTS)); + addFilter(new TrunkingOpcodeMessageFilter("Vendor-Harris Messages", Opcode.HARRIS)); + addFilter(new TrunkingOpcodeMessageFilter("Vendor-Motorola Messages", Opcode.MOTOROLA)); + addFilter(new TrunkingOpcodeMessageFilter("Vendor-Unknown Messages", Opcode.UNKNOWN)); + + List ungrouped = Opcode.getUngrouped(); + if(!ungrouped.isEmpty()) + { + addFilter(new TrunkingOpcodeMessageFilter("Unknown Opcode Messages", ungrouped)); + } + } + + @Override + public boolean canProcess(IMessage message) + { + return (message instanceof TSBKMessage || message instanceof AMBTCMessage || message instanceof UMBTCMessage) && + super.canProcess(message); + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/UMBTCMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/UMBTCMessageFilter.java deleted file mode 100644 index e355a9ce..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/UMBTCMessageFilter.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.umbtc.UMBTCMessage; - -import java.util.Collections; -import java.util.List; - -public class UMBTCMessageFilter extends Filter -{ - public UMBTCMessageFilter() - { - super("Unconfirmed Multi-Block Trunking Control"); - } - - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); - } - - @Override - public boolean canProcess(IMessage message) - { - return message instanceof UMBTCMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/VoiceMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/VoiceMessageFilter.java new file mode 100644 index 00000000..70788207 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/VoiceMessageFilter.java @@ -0,0 +1,83 @@ +/* + * ***************************************************************************** + * 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.p25.phase1.message.filter; + +import io.github.dsheirer.filter.Filter; +import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU1Message; +import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDU2Message; +import io.github.dsheirer.module.decode.p25.phase1.message.ldu.LDUMessage; +import java.util.function.Function; + +/** + * Filter for LDU1 and LDU2 voice messages + */ +public class VoiceMessageFilter extends Filter +{ + private static final String LDU1_KEY = "LDU1 Voice Message"; + private static final String LDU2_KEY = "LDU2 Voice Message"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructs an instance + * + * @param name of this filter + */ + public VoiceMessageFilter() + { + super("Voice Messages"); + add(new FilterElement<>(LDU1_KEY)); + add(new FilterElement<>(LDU2_KEY)); + } + + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof LDUMessage && super.canProcess(message); + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public String apply(IMessage message) + { + if(message instanceof LDU1Message) + { + return LDU1_KEY; + } + else if(message instanceof LDU2Message) + { + return LDU2_KEY; + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java index 6a8fdddd..0d039e78 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/lc/LinkControlOpcode.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,17 @@ * * 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.lc; import io.github.dsheirer.module.decode.p25.reference.Vendor; +import java.util.EnumSet; +/** + * Opcodes used for Link Control Words. + */ public enum LinkControlOpcode { GROUP_VOICE_CHANNEL_USER("GROUP VOICE CHANNEL USER", 0), @@ -101,22 +104,82 @@ public enum LinkControlOpcode private String mLabel; private int mCode; + /** + * Constructor + * @param label to display for the opcode + * @param code value. + */ LinkControlOpcode(String label, int code) { mLabel = label; mCode = code; } + /** + * Command, response and status opcodes + */ + public static final EnumSet COMMAND_STATUS_OPCODES = EnumSet.of(UNIT_TO_UNIT_ANSWER_REQUEST, + TELEPHONE_INTERCONNECT_ANSWER_REQUEST, CALL_TERMINATION_OR_CANCELLATION, GROUP_AFFILIATION_QUERY, + UNIT_AUTHENTICATION_COMMAND, UNIT_REGISTRATION_COMMAND, STATUS_QUERY, STATUS_UPDATE, MESSAGE_UPDATE, + EXTENDED_FUNCTION_COMMAND, CALL_ALERT); + + /** + * Motorola Opcodes + */ + public static final EnumSet MOTOROLA_OPCODES = + EnumSet.range(MOTOROLA_PATCH_GROUP_VOICE_CHANNEL_USER, MOTOROLA_UNKNOWN); + + + /** + * Network/channel related opcodes + */ + public static final EnumSet NETWORK_OPCODES = EnumSet.of(CHANNEL_IDENTIFIER_UPDATE, + CHANNEL_IDENTIFIER_UPDATE_EXPLICIT, SYSTEM_SERVICE_BROADCAST, SECONDARY_CONTROL_CHANNEL_BROADCAST, + SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, ADJACENT_SITE_STATUS_BROADCAST, + ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT, RFSS_STATUS_BROADCAST, RFSS_STATUS_BROADCAST_EXPLICIT, + NETWORK_STATUS_BROADCAST, NETWORK_STATUS_BROADCAST_EXPLICIT, PROTECTION_PARAMETER_BROADCAST); + + /** + * Voice/call opcodes + */ + public static final EnumSet VOICE_OPCODES = EnumSet.of(GROUP_VOICE_CHANNEL_USER, + GROUP_VOICE_CHANNEL_UPDATE, GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT, UNIT_TO_UNIT_VOICE_CHANNEL_USER, + TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER); + + /** + * 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 COMMAND_STATUS_OPCODES.contains(this) || MOTOROLA_OPCODES.contains(this) || + NETWORK_OPCODES.contains(this) || VOICE_OPCODES.contains(this); + } + + /** + * Pretty label/string for the element + * @return label + */ public String getLabel() { return mLabel; } + /** + * Numeric value of the opcode. + * @return code value. + */ public int getCode() { return mCode; } + /** + * Lookup method for finding an opcode for a given vendor and value combination. + * @param value of the opcode + * @param vendor for the opcode + * @return matching element or UNKNOWN. + */ public static LinkControlOpcode fromValue(int value, Vendor vendor) { switch(vendor) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java index 09ccf88d..a2f2f446 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/tsbk/Opcode.java @@ -1,6 +1,6 @@ /* * ***************************************************************************** - * Copyright (C) 2014-2021 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,8 +21,9 @@ package io.github.dsheirer.module.decode.p25.phase1.message.tsbk; import io.github.dsheirer.module.decode.p25.reference.Direction; import io.github.dsheirer.module.decode.p25.reference.Vendor; - +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; public enum Opcode { @@ -192,13 +193,12 @@ public enum Opcode private String mLabel; private String mDescription; - public static final EnumSet STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT, - OSP_PROTECTION_PARAMETER_UPDATE); - public static final EnumSet STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST, - ISP_RESERVED_3F); - public static final EnumSet DATA_CHANNEL_GRANT_OPCODES = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT, - OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT); - + /** + * Constructor + * @param code value + * @param label or pretty value. + * @param description of the opcode + */ Opcode(int code, String label, String description) { mCode = code; @@ -206,6 +206,135 @@ public enum Opcode mDescription = description; } + /** + * OSP standard opcodes + */ + public static final EnumSet STANDARD_OUTBOUND_OPCODES = EnumSet.range(OSP_GROUP_VOICE_CHANNEL_GRANT, + OSP_PROTECTION_PARAMETER_UPDATE); + + /** + * ISP standard opcodes + */ + public static final EnumSet STANDARD_INBOUND_OPCODES = EnumSet.range(ISP_GROUP_VOICE_SERVICE_REQUEST, + ISP_RESERVED_3F); + + /** + * ISP mobile data requests + */ + public static final EnumSet MOBILE_DATA_REQUESTS = EnumSet.of(ISP_INDIVIDUAL_DATA_SERVICE_REQUEST, + ISP_GROUP_DATA_SERVICE_REQUEST, ISP_SNDCP_DATA_CHANNEL_REQUEST, ISP_SNDCP_RECONNECT_REQUEST); + + /** + * ISP mobile voice requests + */ + public static final EnumSet MOBILE_VOICE_REQUESTS = EnumSet.of(ISP_GROUP_VOICE_SERVICE_REQUEST, + ISP_UNIT_TO_UNIT_VOICE_SERVICE_REQUEST, ISP_TELEPHONE_INTERCONNECT_EXPLICIT_DIAL_REQUEST, + ISP_TELEPHONE_INTERCONNECT_PSTN_REQUEST); + + /** + * ISP mobile request, response and status. + */ + public static final EnumSet MOBILE_REQUEST_RESPONSE = EnumSet.of(ISP_UNIT_TO_UNIT_ANSWER_RESPONSE, + ISP_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE, ISP_SNDCP_DATA_PAGE_RESPONSE, ISP_STATUS_UPDATE_REQUEST, + ISP_STATUS_QUERY_REQUEST, ISP_STATUS_QUERY_RESPONSE, ISP_MESSAGE_UPDATE_REQUEST, + ISP_RADIO_UNIT_MONITOR_REQUEST, ISP_CALL_ALERT_REQUEST, ISP_UNIT_ACKNOWLEDGE_RESPONSE, + ISP_CANCEL_SERVICE_REQUEST, ISP_EXTENDED_FUNCTION_RESPONSE, ISP_EMERGENCY_ALARM_REQUEST, + ISP_GROUP_AFFILIATION_QUERY_RESPONSE, ISP_GROUP_AFFILIATION_REQUEST, ISP_UNIT_DE_REGISTRATION_REQUEST, + ISP_UNIT_REGISTRATION_REQUEST, ISP_LOCATION_REGISTRATION_REQUEST, ISP_AUTHENTICATION_QUERY_OBSOLETE, + ISP_AUTHENTICATION_RESPONSE_OBSOLETE, ISP_PROTECTION_PARAMETER_REQUEST, ISP_IDENTIFIER_UPDATE_REQUEST, + ISP_ROAMING_ADDRESS_REQUEST, ISP_ROAMING_ADDRESS_RESPONSE, ISP_AUTHENTICATION_RESPONSE, + ISP_AUTHENTICATION_RESPONSE_MUTUAL, ISP_AUTHENTICATION_FNE_RESULT, ISP_AUTHENTICATION_SU_DEMAND); + + /** + * OSP network data channel grants. + */ + public static final EnumSet NETWORK_DATA_GRANTS = EnumSet.of(OSP_SNDCP_DATA_CHANNEL_GRANT, + OSP_INDIVIDUAL_DATA_CHANNEL_GRANT, OSP_GROUP_DATA_CHANNEL_GRANT); + + /** + * OSP network status + */ + public static final EnumSet NETWORK_CHANNEL_STATUS = EnumSet.of(OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT, + OSP_GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT, OSP_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT, + OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, OSP_TDMA_SYNC_BROADCAST, OSP_IDENTIFIER_UPDATE_TDMA, + OSP_IDENTIFIER_UPDATE_VHF_UHF_BANDS, OSP_TIME_DATE_ANNOUNCEMENT, OSP_SYSTEM_SERVICE_BROADCAST, + OSP_SECONDARY_CONTROL_CHANNEL_BROADCAST, OSP_RFSS_STATUS_BROADCAST, OSP_NETWORK_STATUS_BROADCAST, + OSP_ADJACENT_STATUS_BROADCAST, OSP_IDENTIFIER_UPDATE, OSP_PROTECTION_PARAMETER_BROADCAST, + OSP_PROTECTION_PARAMETER_UPDATE); + + /** + * OSP network command, request and response + */ + public static final EnumSet NETWORK_COMMAND_REQUEST_RESPONSE = EnumSet.of(OSP_UNIT_TO_UNIT_ANSWER_REQUEST, + OSP_TELEPHONE_INTERCONNECT_ANSWER_REQUEST, OSP_SNDCP_DATA_PAGE_REQUEST, OSP_STATUS_UPDATE, + OSP_STATUS_QUERY, OSP_MESSAGE_UPDATE, OSP_RADIO_UNIT_MONITOR_COMMAND, OSP_CALL_ALERT, + OSP_ACKNOWLEDGE_RESPONSE, OSP_QUEUED_RESPONSE, OSP_EXTENDED_FUNCTION_COMMAND, OSP_DENY_RESPONSE, + OSP_GROUP_AFFILIATION_QUERY, OSP_GROUP_AFFILIATION_RESPONSE, OSP_LOCATION_REGISTRATION_RESPONSE, + OSP_UNIT_REGISTRATION_COMMAND, OSP_UNIT_REGISTRATION_RESPONSE, OSP_AUTHENTICATION_COMMAND, + OSP_UNIT_DEREGISTRATION_ACKNOWLEDGE, OSP_AUTHENTICATION_DEMAND, OSP_AUTHENTICATION_FNE_RESPONSE, + OSP_ROAMING_ADDRESS_COMMAND, OSP_ROAMING_ADDRESS_UPDATE); + + /** + * OSP network voice channel grants. + */ + public static final EnumSet NETWORK_VOICE_GRANTS = EnumSet.of(OSP_GROUP_VOICE_CHANNEL_GRANT, + OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE, OSP_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT, + OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT, OSP_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE, + OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT, OSP_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE); + + /** + * Motorola opcodes + */ + public static final EnumSet MOTOROLA = EnumSet.of(MOTOROLA_ISP_UNKNOWN, MOTOROLA_OSP_PATCH_GROUP_ADD, + MOTOROLA_OSP_PATCH_GROUP_DELETE, MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT, + MOTOROLA_OSP_PATCH_GROUP_CHANNEL_GRANT_UPDATE, MOTOROLA_OSP_TRAFFIC_CHANNEL_ID, + MOTOROLA_OSP_DENY_RESPONSE, MOTOROLA_OSP_SYSTEM_LOADING, MOTOROLA_OSP_BASE_STATION_ID, + MOTOROLA_OSP_CONTROL_CHANNEL_PLANNED_SHUTDOWN, MOTOROLA_OSP_UNKNOWN); + + /** + * Harris opcodes + */ + public static final EnumSet HARRIS = EnumSet.of(HARRIS_ISP_UNKNOWN, HARRIS_OSP_TDMA_SYNC, + HARRIS_OSP_UNKNOWN); + + /** + * Unknown vendor ISP/OSP opcodes + */ + public static final EnumSet UNKNOWN = EnumSet.of(UNKNOWN_VENDOR_ISP, UNKNOWN_VENDOR_OSP); + + /** + * 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 MOBILE_DATA_REQUESTS.contains(this) || MOBILE_VOICE_REQUESTS.contains(this) || + MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_DATA_GRANTS.contains(this) || + NETWORK_CHANNEL_STATUS.contains(this) || NETWORK_COMMAND_REQUEST_RESPONSE.contains(this) || + NETWORK_VOICE_GRANTS.contains(this) || MOTOROLA.contains(this) || HARRIS.contains(this) || + UNKNOWN.contains(this); + } + + /** + * List of opcodes that are not grouped. This will catch any opcodes that are added to this enumeration where + * the opcode is not added to one of the existing groups. This is used for message filtering to ensure that all + * opcodes and handled by the filter/filterset. + * @return list of ungrouped opcodes. + */ + public static List getUngrouped() + { + List ungrouped = new ArrayList<>(); + for(Opcode opcode: ungrouped) + { + if(!opcode.isGrouped()) + { + ungrouped.add(opcode); + } + } + + return ungrouped; + } + /** * Formatted, fixed-length label */ @@ -235,7 +364,7 @@ public enum Opcode */ public boolean isDataChannelGrant() { - return DATA_CHANNEL_GRANT_OPCODES.contains(this); + return NETWORK_DATA_GRANTS.contains(this); } @Override @@ -244,17 +373,13 @@ public enum Opcode return mLabel; } - @Deprecated //use the ISP/OSP version of this method instead - public static Opcode fromValue(int value) - { - if(0 <= value && value <= 63) - { - return values()[value]; - } - - return OSP_UNKNOWN; - } - + /** + * Lookup an opcode from a numeric value, vendor, and inbound/outbound direction. + * @param value for the opcode + * @param direction indicator - inbound (mobile) or outbound (tower) + * @param vendor identifier + * @return opcode or UNKNOWN + */ public static Opcode fromValue(int value, Direction direction, Vendor vendor) { switch(vendor) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java index f735294c..c7e25244 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/P25P2DecoderState.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 @@ -169,9 +169,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie { if(message.isValid() && message.getTimeslot() == getTimeslot()) { - if(message instanceof MacMessage) + if(message instanceof MacMessage macMessage) { - MacMessage macMessage = (MacMessage)message; processMacMessage(macMessage); MacPduType macPduType = macMessage.getMacPduType(); @@ -315,31 +314,26 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie processMacRelease(message, mac); break; case PHASE1_65_GROUP_VOICE_SERVICE_REQUEST: - if(mac instanceof GroupVoiceServiceRequest) + if(mac instanceof GroupVoiceServiceRequest gvsr) { - GroupVoiceServiceRequest gvsr = (GroupVoiceServiceRequest)mac; - broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "GROUP VOICE SERVICE " + gvsr.getServiceOptions()); } break; case PHASE1_68_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_ABBREVIATED: - if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated) + if(mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga) { - UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac; broadcast(message, mac, uuvcga.getChannel(), DecodeEventType.CALL_UNIT_TO_UNIT, "VOICE CHANNEL GRANT"); } break; case PHASE1_69_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED: - if(mac instanceof UnitToUnitAnswerRequestAbbreviated) + if(mac instanceof UnitToUnitAnswerRequestAbbreviated uuara) { - UnitToUnitAnswerRequestAbbreviated uuara = (UnitToUnitAnswerRequestAbbreviated)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST - " + uuara.getServiceOptions()); } break; case PHASE1_74_TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if(mac instanceof TelephoneInterconnectAnswerRequest) + if(mac instanceof TelephoneInterconnectAnswerRequest tiar) { - TelephoneInterconnectAnswerRequest tiar = (TelephoneInterconnectAnswerRequest)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "TELEPHONE INTERCONNECT ANSWER REQUEST"); } break; @@ -350,9 +344,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie processSDCG(message, mac); break; case PHASE1_85_SNDCP_DATA_PAGE_REQUEST: - if(mac instanceof SNDCPDataPageRequest) + if(mac instanceof SNDCPDataPageRequest sdpr) { - SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.PAGE, "SNDCP DATA PAGE " + sdpr.getServiceOptions()); } break; @@ -366,9 +359,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } break; case PHASE1_92_MESSAGE_UPDATE_ABBREVIATED: - if(mac instanceof MessageUpdateAbbreviated) + if(mac instanceof MessageUpdateAbbreviated mua) { - MessageUpdateAbbreviated mua = (MessageUpdateAbbreviated)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mua.getShortDataMessage()); } break; @@ -379,9 +371,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie processCallAlert(message, mac); break; case PHASE1_96_ACK_RESPONSE: - if(mac instanceof AcknowledgeResponse) + if(mac instanceof AcknowledgeResponse ar) { - AcknowledgeResponse ar = (AcknowledgeResponse)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.RESPONSE, "ACKNOWLEDGE: " + ar.getServiceType()); } break; @@ -401,10 +392,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie broadcast(message, mac, getCurrentChannel(), DecodeEventType.COMMAND, "UNIT REGISTRATION"); break; case PHASE1_197_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED: - if(mac instanceof UnitToUnitAnswerRequestExtended) + if(mac instanceof UnitToUnitAnswerRequestExtended uuare) { - UnitToUnitAnswerRequestExtended uuare = (UnitToUnitAnswerRequestExtended)mac; - broadcast(message, mac, getCurrentChannel(), DecodeEventType.REQUEST, "UNIT-TO-UNIT ANSWER REQUEST " + uuare.getServiceOptions()); } break; @@ -418,9 +407,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie broadcast(message, mac, getCurrentChannel(), DecodeEventType.STATUS, "STATUS QUERY"); break; case PHASE1_220_MESSAGE_UPDATE_EXTENDED: - if(mac instanceof MessageUpdateExtended) + if(mac instanceof MessageUpdateExtended mue) { - MessageUpdateExtended mue = (MessageUpdateExtended)mac; broadcast(message, mac, getCurrentChannel(), DecodeEventType.SDM, "MESSAGE UPDATE - " + mue.getShortDataMessage()); } break; @@ -468,9 +456,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processQueuedResponse(MacMessage message, MacStructure mac) { - if(mac instanceof QueuedResponse) + if(mac instanceof QueuedResponse qr) { - QueuedResponse qr = (QueuedResponse)mac; broadcast(message, mac, DecodeEventType.RESPONSE, "QUEUED - " + qr.getQueuedResponseServiceType() + " REASON:" + qr.getQueuedResponseReason() + " ADDL:" + qr.getAdditionalInfo()); @@ -479,27 +466,24 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie private void processEFCA(MacMessage message, MacStructure mac) { - if(mac instanceof ExtendedFunctionCommand) + if(mac instanceof ExtendedFunctionCommand efc) { - ExtendedFunctionCommand efc = (ExtendedFunctionCommand)mac; broadcast(message, mac, DecodeEventType.COMMAND, "EXTENDED FUNCTION: " + efc.getExtendedFunction() + " ARGUMENTS:" + efc.getArguments()); } } private void processDenyResponse(MacMessage message, MacStructure mac) { - if(mac instanceof DenyResponse) + if(mac instanceof DenyResponse dr) { - DenyResponse dr = (DenyResponse)mac; broadcast(message, mac, DecodeEventType.RESPONSE, "DENY: " + dr.getDeniedServiceType() + " REASON:" + dr.getDenyReason() + " ADDL:" + dr.getAdditionalInfo()); } } private void processRUMCExtended(MacMessage message, MacStructure mac) { - if(mac instanceof RadioUnitMonitorCommandExtended) + if(mac instanceof RadioUnitMonitorCommandExtended rumce) { - RadioUnitMonitorCommandExtended rumce = (RadioUnitMonitorCommandExtended)mac; broadcast(message, mac, DecodeEventType.COMMAND, "RADIO UNIT MONITOR" + (rumce.isSilentMonitor() ? " (STEALTH)" : "") + " TIME:" + rumce.getTransmitTime() + "MULTIPLIER:" + rumce.getTransmitMultiplier()); @@ -507,18 +491,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processSUE(MacMessage message, MacStructure mac) { - if(mac instanceof StatusUpdateExtended) + if(mac instanceof StatusUpdateExtended sue) { - StatusUpdateExtended sue = (StatusUpdateExtended)mac; broadcast(message, mac, DecodeEventType.STATUS, "STATUS UPDATE - UNIT:" + sue.getUnitStatus() + " USER:" + sue.getUserStatus()); } } private void processEFCE(MacMessage message, MacStructure mac) { - if(mac instanceof ExtendedFunctionCommandExtended) + if(mac instanceof ExtendedFunctionCommandExtended efce) { - ExtendedFunctionCommandExtended efce = (ExtendedFunctionCommandExtended)mac; broadcast(message, mac, DecodeEventType.COMMAND, "EXTENDED FUNCTION: " + efce.getExtendedFunction() + " ARGUMENTS:" + efce.getArguments()); } @@ -529,9 +511,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processRUMCE(MacMessage message, MacStructure mac) { - if(mac instanceof RadioUnitMonitorCommandEnhanced) + if(mac instanceof RadioUnitMonitorCommandEnhanced rumc) { - RadioUnitMonitorCommandEnhanced rumc = (RadioUnitMonitorCommandEnhanced)mac; broadcast(message, mac, DecodeEventType.COMMAND, "RADIO UNIT MONITOR" + (rumc.isStealthMode() ? " (STEALTH)" : "") + " ENCRYPTION:" + rumc.getEncryption() + @@ -540,18 +521,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processSUA(MacMessage message, MacStructure mac) { - if(mac instanceof StatusUpdateAbbreviated) + if(mac instanceof StatusUpdateAbbreviated sua) { - StatusUpdateAbbreviated sua = (StatusUpdateAbbreviated)mac; broadcast(message, mac, DecodeEventType.STATUS, "STATUS UPDATE - UNIT:" + sua.getUnitStatus() + " USER:" + sua.getUserStatus()); } } private void processSDCG(MacMessage message, MacStructure mac) { - if(mac instanceof SNDCPDataChannelGrant) + if(mac instanceof SNDCPDataChannelGrant sdcg) { - SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)mac; broadcast(message, mac, sdcg.getServiceOptions().isEncrypted() ? DecodeEventType.DATA_CALL_ENCRYPTED : DecodeEventType.DATA_CALL, "SNDCP CHANNEL GRANT " + sdcg.getServiceOptions()); @@ -559,9 +538,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processRUMCA(MacMessage message, MacStructure mac) { - if(mac instanceof RadioUnitMonitorCommand) + if(mac instanceof RadioUnitMonitorCommand rumc) { - RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)mac; broadcast(message, mac, DecodeEventType.COMMAND, "RADIO UNIT MONITOR" + (rumc.isSilentMonitor() ? " (STEALTH)" : "") + " TIME:" + rumc.getTransmitTime() + " MULTIPLIER:" + rumc.getTransmitMultiplier()); @@ -569,9 +547,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processMacRelease(MacMessage message, MacStructure mac) { - if(mac instanceof MacRelease) + if(mac instanceof MacRelease mr) { - MacRelease mr = (MacRelease)mac; closeCurrentCallEvent(message.getTimestamp(), true, message.getMacPduType()); broadcast(message, mac, DecodeEventType.COMMAND, (mr.isForcedPreemption() ? "FORCED " : "") + "CALL PREEMPTION" + (mr.isTalkerPreemption() ? " BY USER" : "")); @@ -579,18 +556,16 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processPCSQ(MacMessage message, MacStructure mac) { - if(mac instanceof PowerControlSignalQuality) + if(mac instanceof PowerControlSignalQuality pcsq) { - PowerControlSignalQuality pcsq = (PowerControlSignalQuality)mac; broadcast(message, mac, DecodeEventType.COMMAND, "ADJUST TRANSMIT POWER - RF:" + pcsq.getRFLevel() + " BER:" + pcsq.getBitErrorRate()); } } private void processIPMWP(MacMessage message, MacStructure mac) { - if(mac instanceof IndividualPagingMessage) + if(mac instanceof IndividualPagingMessage ipm) { - IndividualPagingMessage ipm = (IndividualPagingMessage)mac; boolean priority = ipm.isTalkgroupPriority1() || ipm.isTalkgroupPriority2() || ipm.isTalkgroupPriority3() || ipm.isTalkgroupPriority4(); broadcast(message, mac, DecodeEventType.PAGE, @@ -601,10 +576,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie private void broadcast(MacMessage message, MacStructure mac, IChannelDescriptor currentChannel, DecodeEventType eventType, String details) { MutableIdentifierCollection collection = getUpdatedMutableIdentifierCollection(mac); - broadcast(P25DecodeEvent.builder(message.getTimestamp()) + broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp()) .channel(currentChannel) - .eventType(eventType) - .eventDescription(eventType.toString()) .details(details) .identifiers(collection) .build()); @@ -613,10 +586,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie private void broadcast(MacMessage message, MacStructure structure, DecodeEventType eventType, String details) { MutableIdentifierCollection icQueuedResponse = getUpdatedMutableIdentifierCollection(structure); - broadcast(P25DecodeEvent.builder(message.getTimestamp()) + broadcast(P25DecodeEvent.builder(eventType, message.getTimestamp()) .channel(getCurrentChannel()) - .eventType(eventType) - .eventDescription(eventType.toString()) .details(details) .identifiers(icQueuedResponse) .build()); @@ -630,10 +601,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processUTUVCGUExtended(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended) + if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue) { - UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = (UnitToUnitVoiceChannelGrantUpdateExtended)mac; - if(isCurrentGroup(uuvcgue.getTargetAddress())) { broadcastCurrentChannel(uuvcgue.getChannel()); @@ -642,10 +611,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processGVCGUExplicit(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit) + if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateExplicit gvcgue) { - GroupVoiceChannelGrantUpdateExplicit gvcgue = (GroupVoiceChannelGrantUpdateExplicit)mac; - if(isCurrentGroup(gvcgue.getGroupAddress())) { broadcastCurrentChannel(gvcgue.getChannel()); @@ -654,10 +621,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processUTUVCGUAbbreviated(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated) + if(getCurrentChannel() == null && mac instanceof UnitToUnitVoiceChannelGrantAbbreviated uuvcga) { - UnitToUnitVoiceChannelGrantAbbreviated uuvcga = (UnitToUnitVoiceChannelGrantAbbreviated)mac; - if(isCurrentGroup(uuvcga.getSourceAddress()) || isCurrentGroup(uuvcga.getTargetAddress())) { broadcastCurrentChannel(uuvcga.getChannel()); @@ -666,10 +631,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processGVCGUpdate(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate) + if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdate gvcgu) { - GroupVoiceChannelGrantUpdate gvcgu = (GroupVoiceChannelGrantUpdate)mac; - if(isCurrentGroup(gvcgu.getGroupAddressA())) { broadcastCurrentChannel(gvcgu.getChannelA()); @@ -683,10 +646,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processGVCGAbbreviated(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated) + if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantAbbreviated gvcga) { - GroupVoiceChannelGrantAbbreviated gvcga = (GroupVoiceChannelGrantAbbreviated)mac; - if(isCurrentGroup(gvcga.getGroupAddress())) { broadcastCurrentChannel(gvcga.getChannel()); @@ -695,10 +656,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processGVCGUMExplicit(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit) + if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume) { - GroupVoiceChannelGrantUpdateMultipleExplicit gvcgume = (GroupVoiceChannelGrantUpdateMultipleExplicit)mac; - if(isCurrentGroup(gvcgume.getGroupAddressA())) { broadcastCurrentChannel(gvcgume.getChannelA()); @@ -712,10 +671,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } private void processGVCGUM(MacStructure mac) { - if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple) + if(getCurrentChannel() == null && mac instanceof GroupVoiceChannelGrantUpdateMultiple gvcgum) { - GroupVoiceChannelGrantUpdateMultiple gvcgum = (GroupVoiceChannelGrantUpdateMultiple)mac; - if(isCurrentGroup(gvcgum.getGroupAddressA())) { broadcastCurrentChannel(gvcgum.getChannelA()); @@ -752,10 +709,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie getIdentifierCollection().update(mPatchGroupManager.update(identifier)); } - if(mac instanceof TelephoneInterconnectVoiceChannelUser) + if(mac instanceof TelephoneInterconnectVoiceChannelUser tivcu) { - TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)mac; - if(tivcu.getServiceOptions().isEncrypted()) { updateCurrentCall(DecodeEventType.CALL_INTERCONNECT_ENCRYPTED, null, message.getTimestamp()); @@ -787,10 +742,8 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie getIdentifierCollection().update(mPatchGroupManager.update(identifier)); } - if(mac instanceof UnitToUnitVoiceChannelUserExtended) + if(mac instanceof UnitToUnitVoiceChannelUserExtended uuvcue) { - UnitToUnitVoiceChannelUserExtended uuvcue = (UnitToUnitVoiceChannelUserExtended)mac; - if(uuvcue.getServiceOptions().isEncrypted()) { updateCurrentCall(DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED, null, message.getTimestamp()); @@ -1046,9 +999,13 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie { if(mCurrentCallEvent == null) { - mCurrentCallEvent = P25DecodeEvent.builder(timestamp) + if(type == null) + { + type = DecodeEventType.CALL; + } + + mCurrentCallEvent = P25DecodeEvent.builder(type, timestamp) .channel(getCurrentChannel()) - .eventDescription(type != null ? type.toString() : DecodeEventType.CALL.toString()) .details(details) .identifiers(getIdentifierCollection().copyOf()) .build(); @@ -1066,11 +1023,6 @@ public class P25P2DecoderState extends TimeslotDecoderState implements Identifie } else { - if(type != null) - { - mCurrentCallEvent.setEventDescription(type.toString()); - } - if(details != null) { mCurrentCallEvent.setDetails(details); diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/P25MessageFactory.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/P25MessageFactory.java deleted file mode 100644 index 134ee59f..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/P25MessageFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 Dennis Sheirer - * * - * * This program is free software: you can redistribute it and/or modify - * * it under the terms of the GNU General Public License as published by - * * the Free Software Foundation, either version 3 of the License, or - * * (at your option) any later version. - * * - * * This program is distributed in the hope that it will be useful, - * * but WITHOUT ANY WARRANTY; without even the implied warranty of - * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * * GNU General Public License for more details. - * * - * * You should have received a copy of the GNU General Public License - * * along with this program. If not, see - * * ***************************************************************************** - * - * - */ - -package io.github.dsheirer.module.decode.p25.phase2.message; - -public class P25MessageFactory -{ - -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilter.java new file mode 100644 index 00000000..f878583f --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilter.java @@ -0,0 +1,81 @@ +/* + * ***************************************************************************** + * 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.p25.phase2.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.phase2.message.mac.MacMessage; +import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode; +import java.util.Collection; +import java.util.function.Function; + +/** + * Message filter for P25 Phase 2 MAC opcode messages. + */ +public class MacOpcodeMessageFilter extends Filter +{ + private KeyExtractor mKeyExtractor = new KeyExtractor(); + private Collection mOpcodes; + + /** + * Constructs an instance + * + * @param name of this filter + */ + public MacOpcodeMessageFilter(String name, Collection opcodes) + { + super(name); + + mOpcodes = opcodes; + + for(MacOpcode opcode: opcodes) + { + add(new FilterElement<>(opcode)); + } + } + + /** + * Key extractor + * @return opcode extractor + */ + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + /** + * Key extractor for MacOpcode values from MacMessage. + */ + private class KeyExtractor implements Function + { + @Override + public MacOpcode apply(IMessage message) + { + if(message instanceof MacMessage mac) + { + return mac.getMacStructure().getOpcode(); + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilterSet.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilterSet.java new file mode 100644 index 00000000..a2d2f82b --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/MacOpcodeMessageFilterSet.java @@ -0,0 +1,64 @@ +/* + * ***************************************************************************** + * 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.p25.phase2.message.filter; + +import io.github.dsheirer.filter.FilterSet; +import io.github.dsheirer.message.IMessage; +import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage; +import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacOpcode; +import java.util.ArrayList; +import java.util.List; + +/** + * Filter set for P25 Phase 2 MAC messages + */ +public class MacOpcodeMessageFilterSet extends FilterSet +{ + /** + * Constructor + */ + public MacOpcodeMessageFilterSet() + { + super("Trunking Messages"); + + addFilter(new MacOpcodeMessageFilter("Call Maintenance", MacOpcode.CALL_MAINTENANCE)); + addFilter(new MacOpcodeMessageFilter("Data Channel Grants", MacOpcode.DATA_GRANTS)); + addFilter(new MacOpcodeMessageFilter("Mobile Request & Response", MacOpcode.MOBILE_REQUEST_RESPONSE)); + addFilter(new MacOpcodeMessageFilter("Network Request & Response", MacOpcode.NETWORK_REQUEST_RESPONSE)); + addFilter(new MacOpcodeMessageFilter("Network Status & Announcements", MacOpcode.NETWORK_STATUS)); + addFilter(new MacOpcodeMessageFilter("Voice Channel Grants", MacOpcode.VOICE_GRANTS)); + + List ungrouped = new ArrayList<>(); + for(MacOpcode opcode: ungrouped) + { + if(!opcode.isGrouped()) + { + ungrouped.add(opcode); + } + } + addFilter(new MacOpcodeMessageFilter("Other/Unknown", ungrouped)); + } + + @Override + public boolean canProcess(IMessage message) + { + return (message instanceof MacMessage) && super.canProcess(message); + } +} 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/phase2/message/filter/P25P2MessageFilterSet.java similarity index 51% rename from src/main/java/io/github/dsheirer/module/decode/p25/phase1/message/filter/AMBTCMessageFilter.java rename to src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/P25P2MessageFilterSet.java index fe065b3c..112267d4 100644 --- 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/phase2/message/filter/P25P2MessageFilterSet.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,41 +14,38 @@ * * 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.module.decode.p25.phase2.message.filter; -import io.github.dsheirer.filter.Filter; -import io.github.dsheirer.filter.FilterElement; +import io.github.dsheirer.filter.FilterSet; import io.github.dsheirer.message.IMessage; -import io.github.dsheirer.module.decode.p25.phase1.message.pdu.ambtc.AMBTCMessage; +import io.github.dsheirer.message.SyncLossMessage; +import io.github.dsheirer.module.decode.p25.phase2.message.P25P2Message; -import java.util.Collections; -import java.util.List; - -public class AMBTCMessageFilter extends Filter +/** + * Filter set for P25 Phase 2 messages + */ +public class P25P2MessageFilterSet extends FilterSet { - public AMBTCMessageFilter() + public P25P2MessageFilterSet() { - super("Alternate Multi-Block Trunking Control"); - } - - @Override - public boolean passes(IMessage message) - { - return mEnabled && canProcess(message); + super("P25 Phase 2 Message Filter"); + + addFilter(new MacOpcodeMessageFilterSet()); + addFilter(new VoiceMessageFilter()); + addFilter(new P25P2OtherMessageFilter()); } + /** + * Override default to descope handling to P25P2 or sync-loss messages. + * @param message to test + * @return true if the message can be processed + */ @Override public boolean canProcess(IMessage message) { - return message instanceof AMBTCMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; + return message instanceof P25P2Message || message instanceof SyncLossMessage; } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/P25P2OtherMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/P25P2OtherMessageFilter.java new file mode 100644 index 00000000..8dacb354 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/P25P2OtherMessageFilter.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.p25.phase2.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.phase2.message.P25P2Message; +import java.util.function.Function; + +/** + * Filter for unknown messages, meaning messages not handled by any other filter. + */ +public class P25P2OtherMessageFilter extends Filter +{ + private static final String OTHER_KEY = "Other/Unknown Message"; + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ + public P25P2OtherMessageFilter() + { + super("Other P25 messages"); + add(new FilterElement<>(OTHER_KEY)); + } + + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof P25P2Message && super.canProcess(message); + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public String apply(IMessage message) + { + if(message instanceof P25P2Message) + { + return OTHER_KEY; + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/VoiceMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/VoiceMessageFilter.java new file mode 100644 index 00000000..164f5beb --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/filter/VoiceMessageFilter.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.p25.phase2.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.phase2.enumeration.DataUnitID; +import io.github.dsheirer.module.decode.p25.phase2.timeslot.AbstractVoiceTimeslot; +import java.util.function.Function; + +/** + * Filter for P25 Phase 2 Voice (2/4) messages + */ +public class VoiceMessageFilter extends Filter +{ + private KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructs an instance + * + * @param name of this filter + */ + public VoiceMessageFilter() + { + super("Voice Timeslot Messages"); + add(new FilterElement<>(DataUnitID.VOICE_2)); + add(new FilterElement<>(DataUnitID.VOICE_4)); + } + + @Override + public Function getKeyExtractor() + { + return mKeyExtractor; + } + + @Override + public boolean canProcess(IMessage message) + { + return message instanceof AbstractVoiceTimeslot && super.canProcess(message); + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public DataUnitID apply(IMessage message) + { + if(message instanceof AbstractVoiceTimeslot voice) + { + return voice.getDataUnitID(); + } + + return null; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacOpcode.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacOpcode.java index e80c927a..d267395c 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacOpcode.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/message/mac/MacOpcode.java @@ -1,27 +1,25 @@ /* + * ***************************************************************************** + * 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.p25.phase2.message.mac; +import java.util.EnumSet; import java.util.Map; import java.util.TreeMap; @@ -120,6 +118,12 @@ public enum MacOpcode private String mLabel; private int mLength; + /** + * Constructor + * @param value of the opcode + * @param label for display + * @param length of the message in bytes. + */ MacOpcode(int value, String label, int length) { mValue = value; @@ -127,6 +131,75 @@ public enum MacOpcode mLength = length; } + /** + * Call maintenance + */ + public static EnumSet CALL_MAINTENANCE = EnumSet.of(PUSH_TO_TALK, END_PUSH_TO_TALK, + TDMA_1_GROUP_VOICE_CHANNEL_USER_ABBREVIATED, TDMA_2_UNIT_TO_UNIT_VOICE_CHANNEL_USER, + TDMA_3_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER, TDMA_5_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE, + TDMA_33_GROUP_VOICE_CHANNEL_USER_EXTENDED, TDMA_34_UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED, + TDMA_37_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_EXPLICIT); + + /** + * Data channel grants + */ + public static EnumSet DATA_GRANTS = EnumSet.of(PHASE1_84_SNDCP_DATA_CHANNEL_GRANT); + + /** + * Mobile requests and responses + */ + public static EnumSet MOBILE_REQUEST_RESPONSE = EnumSet.of(PHASE1_65_GROUP_VOICE_SERVICE_REQUEST, + PHASE1_88_STATUS_UPDATE_ABBREVIATED, PHASE1_92_MESSAGE_UPDATE_ABBREVIATED, + PHASE1_216_STATUS_UPDATE_EXTENDED, PHASE1_220_MESSAGE_UPDATE_EXTENDED); + + /** + * Network requests and responses + */ + public static EnumSet NETWORK_REQUEST_RESPONSE = EnumSet.of(TDMA_0_NULL_INFORMATION_MESSAGE, + TDMA_17_INDIRECT_GROUP_PAGING, TDMA_18_INDIVIDUAL_PAGING_MESSAGE_WITH_PRIORITY, + TDMA_48_POWER_CONTROL_SIGNAL_QUALITY, TDMA_49_MAC_RELEASE, PHASE1_69_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED, + PHASE1_74_TELEPHONE_INTERCONNECT_ANSWER_REQUEST, PHASE1_76_RADIO_UNIT_MONITOR_COMMAND_ABBREVIATED, + PHASE1_85_SNDCP_DATA_PAGE_REQUEST, PHASE1_90_STATUS_QUERY_ABBREVIATED, + OBSOLETE_PHASE1_93_RADIO_UNIT_MONITOR_COMMAND, PHASE1_94_RADIO_UNIT_MONITOR_COMMAND_ENHANCED, + PHASE1_95_CALL_ALERT_ABBREVIATED, PHASE1_96_ACK_RESPONSE, PHASE1_97_QUEUED_RESPONSE, + PHASE1_100_EXTENDED_FUNCTION_COMMAND_ABBREVIATED, PHASE1_103_DENY_RESPONSE, + PHASE1_106_GROUP_AFFILIATION_QUERY_ABBREVIATED, PHASE1_109_UNIT_REGISTRATION_COMMAND_ABBREVIATED, + PHASE1_197_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED, PHASE1_204_RADIO_UNIT_MONITOR_COMMAND_EXTENDED, + PHASE1_218_STATUS_QUERY_EXTENDED, PHASE1_223_CALL_ALERT_EXTENDED, + PHASE1_228_EXTENDED_FUNCTION_COMMAND_EXTENDED, PHASE1_234_GROUP_AFFILIATION_QUERY_EXTENDED); + + /** + * Network and channel status and announcements + */ + public static EnumSet NETWORK_STATUS = EnumSet.of(PHASE1_115_IDENTIFIER_UPDATE_TDMA, + PHASE1_116_IDENTIFIER_UPDATE_V_UHF, PHASE1_117_TIME_AND_DATE_ANNOUNCEMENT, + PHASE1_120_SYSTEM_SERVICE_BROADCAST, PHASE1_121_SECONDARY_CONTROL_CHANNEL_BROADCAST_ABBREVIATED, + PHASE1_122_RFSS_STATUS_BROADCAST_ABBREVIATED, PHASE1_123_NETWORK_STATUS_BROADCAST_ABBREVIATED, + PHASE1_124_ADJACENT_STATUS_BROADCAST_ABBREVIATED, PHASE1_125_IDENTIFIER_UPDATE, + PHASE1_214_SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT, PHASE1_233_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT, + PHASE1_250_RFSS_STATUS_BROADCAST_EXTENDED, PHASE1_251_NETWORK_STATUS_BROADCAST_EXTENDED, + PHASE1_252_ADJACENT_STATUS_BROADCAST_EXTENDED); + + /** + * Voice channel grants + */ + public static EnumSet VOICE_GRANTS = EnumSet.of(PHASE1_64_GROUP_VOICE_CHANNEL_GRANT_ABBREVIATED, + PHASE1_66_GROUP_VOICE_CHANNEL_GRANT_UPDATE, PHASE1_68_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_ABBREVIATED, + PHASE1_70_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_ABBREVIATED, PHASE1_192_GROUP_VOICE_CHANNEL_GRANT_EXTENDED, + PHASE1_195_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT, PHASE1_196_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_EXTENDED, + PHASE1_198_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_EXTENDED); + + /** + * 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 CALL_MAINTENANCE.contains(this) || DATA_GRANTS.contains(this) || + MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) || + NETWORK_STATUS.contains(this) || VOICE_GRANTS.contains(this); + } + @Override public String toString() { diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/timeslot/FacchTimeslot.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/timeslot/FacchTimeslot.java index a959d08b..5320b011 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/phase2/timeslot/FacchTimeslot.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase2/timeslot/FacchTimeslot.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.p25.phase2.timeslot; @@ -29,11 +26,10 @@ import io.github.dsheirer.module.decode.p25.phase2.enumeration.DataUnitID; import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage; import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessageFactory; import io.github.dsheirer.module.decode.p25.phase2.message.mac.UnknownMacMessage; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; - /** * Fast Associated Control CHannel (FACCH) timeslot carrying a S-OEMI Message */ @@ -257,33 +253,4 @@ public class FacchTimeslot extends AbstractSignalingTimeslot return mMacMessages; } - - public static void main(String[] args) - { -// String raw = "575D57F7FFD8000000000000000030000000000000003FD55DDF5F500082919EB24B903F6A5B0BF9831985D29B"; - String raw = "575D57F7FFD8010020000000000030000000000000003FD55DDF5F500082919EB24B903F6A5B0BF9831985D29B"; - CorrectedBinaryMessage m = new CorrectedBinaryMessage(360); - - int pointer = 0; - - for(int x = 0; x < raw.length(); x += 2) - { - String braw = raw.substring(x, x + 2); - int parsed = Integer.parseInt(braw, 16); - m.load(pointer, 8, parsed); - pointer += 8; - } - - mLog.debug(" IN:" + raw); - mLog.debug("OUT:" + m.toHexString()); - - FacchTimeslot facch = new FacchTimeslot(m, 0, System.currentTimeMillis()); - - List macs = facch.getMacMessages(); - - for(MacMessage mac: macs) - { - mLog.debug(mac.toString()); - } - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecodeEvent.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecodeEvent.java index 49063945..3328657d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecodeEvent.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecodeEvent.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,27 @@ * * 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.passport; import io.github.dsheirer.module.decode.event.DecodeEvent; +import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.protocol.Protocol; +/** + * Passport decode event + */ public class PassportDecodeEvent extends DecodeEvent { /** * Constructs a Passport decode event * @param start */ - public PassportDecodeEvent(long start) + public PassportDecodeEvent(DecodeEventType decodeEventType, long start) { - super(start); + super(decodeEventType, start); setProtocol(Protocol.PASSPORT); } @@ -40,9 +43,9 @@ public class PassportDecodeEvent extends DecodeEvent * @param timeStart for the event * @return builder */ - public static DecodeEventBuilder builder(long timeStart) + public static DecodeEventBuilder builder(DecodeEventType decodeEventType, long timeStart) { - DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(timeStart); + DecodeEventBuilder decodeEventBuilder = new DecodeEventBuilder(decodeEventType, timeStart); decodeEventBuilder.protocol(Protocol.PASSPORT); return decodeEventBuilder; } diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java index a7bb9dd8..d792d398 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoderState.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.passport; @@ -38,9 +35,6 @@ import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.passport.identifier.PassportRadioId; import io.github.dsheirer.module.decode.passport.identifier.PassportTalkgroup; import io.github.dsheirer.protocol.Protocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -48,6 +42,8 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PassportDecoderState extends DecoderState { @@ -159,9 +155,10 @@ public class PassportDecoderState extends DecoderState } } - mCurrentDecodeEvent = PassportDecodeEvent.builder(passport.getTimestamp()) - .eventType(passport.getMessageType() == MessageType.CA_STRT ? DecodeEventType.CALL : DecodeEventType.DATA_CALL) - .eventDescription(passport.getMessageType() == MessageType.CA_STRT ? "Call" : "Data") + DecodeEventType decodeEventType = passport.getMessageType() == MessageType.CA_STRT ? + DecodeEventType.CALL : DecodeEventType.DATA_CALL; + + mCurrentDecodeEvent = PassportDecodeEvent.builder(decodeEventType, passport.getTimestamp()) .identifiers(getIdentifierCollection().copyOf()) .details(passport.toString()) .build(); @@ -179,9 +176,7 @@ public class PassportDecoderState extends DecoderState !isSameTalkgroup(to, getToIdentifier(callDetect.getIdentifierCollection())) || callDetect.getTimeStart() < (passport.getTimestamp() - 45000)) { - callDetect = PassportDecodeEvent.builder(passport.getTimestamp()) - .eventType(DecodeEventType.CALL_DETECT) - .eventDescription("Call Detect") + callDetect = PassportDecodeEvent.builder(DecodeEventType.CALL_DETECT, passport.getTimestamp()) .identifiers(new IdentifierCollection(passport.getIdentifiers())) // .channel(...) .build(); @@ -221,9 +216,7 @@ public class PassportDecoderState extends DecoderState } break; case RA_REGI: - broadcast(PassportDecodeEvent.builder(passport.getTimestamp()) - .eventType(DecodeEventType.REGISTER) - .eventDescription("Register") + broadcast(PassportDecodeEvent.builder(DecodeEventType.REGISTER, passport.getTimestamp()) .identifiers(new IdentifierCollection(passport.getIdentifiers())) .build()); diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java index f4ea669b..e81bbfd6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessageFilter.java @@ -1,57 +1,79 @@ +/* + * ***************************************************************************** + * 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.passport; 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 io.github.dsheirer.protocol.Protocol; +import java.util.function.Function; -import java.util.*; - -public class PassportMessageFilter extends Filter +/** + * Filter for Passport messages + */ +public class PassportMessageFilter extends Filter { - private Map> mElements = new EnumMap<>(MessageType.class); + private KeyExtractor mKeyExtractor = new KeyExtractor(); + /** + * Constructor + */ public PassportMessageFilter() { - super("Passport Message Filter"); - - mElements.put(MessageType.CA_STRT, new FilterElement(MessageType.CA_STRT)); - mElements.put(MessageType.CA_ENDD, new FilterElement(MessageType.CA_ENDD)); - mElements.put(MessageType.SY_IDLE, new FilterElement(MessageType.SY_IDLE)); - mElements.put(MessageType.ID_TGAS, new FilterElement(MessageType.ID_TGAS)); - mElements.put(MessageType.ID_ESNH, new FilterElement(MessageType.ID_ESNH)); - mElements.put(MessageType.CA_PAGE, new FilterElement(MessageType.CA_PAGE)); - mElements.put(MessageType.ID_RDIO, new FilterElement(MessageType.ID_RDIO)); - mElements.put(MessageType.DA_STRT, new FilterElement(MessageType.DA_STRT)); - mElements.put(MessageType.RA_REGI, new FilterElement(MessageType.RA_REGI)); - } - - @Override - public boolean passes(IMessage message) - { - if(mEnabled && canProcess(message)) - { - PassportMessage passport = (PassportMessage) message; - - if(mElements.containsKey(passport.getMessageType())) - { - return mElements.get(passport.getMessageType()).isEnabled(); - } - } - - return false; + super("Passport Messages"); + add(new FilterElement<>(MessageType.CA_STRT)); + add(new FilterElement<>(MessageType.SY_IDLE)); + add(new FilterElement<>(MessageType.ID_TGAS)); + add(new FilterElement<>(MessageType.ID_ESNH)); + add(new FilterElement<>(MessageType.CA_PAGE)); + add(new FilterElement<>(MessageType.ID_RDIO)); + add(new FilterElement<>(MessageType.DA_STRT)); + add(new FilterElement<>(MessageType.RA_REGI)); } @Override public boolean canProcess(IMessage message) { - return message instanceof PassportMessage; + return message instanceof PassportMessage && super.canProcess(message); } @Override - public List> getFilterElements() + public Function getKeyExtractor() { - return new ArrayList>(mElements.values()); + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public MessageType apply(IMessage message) + { + if(message instanceof PassportMessage passport) + { + return passport.getMessageType(); + } + + return null; + } } } diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java index 4c26f35c..6bdddec0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200DecoderState.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.tait; @@ -34,9 +31,8 @@ import io.github.dsheirer.module.decode.event.DecodeEventType; import io.github.dsheirer.module.decode.event.PlottableDecodeEvent; import io.github.dsheirer.module.decode.tait.identifier.TaitIdentifier; import io.github.dsheirer.protocol.Protocol; -import org.jdesktop.swingx.mapviewer.GeoPosition; - import java.util.TreeSet; +import org.jdesktop.swingx.mapviewer.GeoPosition; public class Tait1200DecoderState extends DecoderState { @@ -97,10 +93,8 @@ public class Tait1200DecoderState extends DecoderState ic.remove(IdentifierClass.USER); ic.update(message.getIdentifiers()); - PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(gps.getTimestamp()) + PlottableDecodeEvent event = PlottableDecodeEvent.plottableBuilder(DecodeEventType.GPS, gps.getTimestamp()) .protocol(PROTOCOL_TAIT_1200) - .eventType(DecodeEventType.GPS) - .eventDescription("GPS") .identifiers(ic) .location(position) .speed(gps.getSpeed()) @@ -111,9 +105,8 @@ public class Tait1200DecoderState extends DecoderState broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); } - else if(message instanceof Tait1200ANIMessage) + else if(message instanceof Tait1200ANIMessage ani) { - Tait1200ANIMessage ani = (Tait1200ANIMessage)message; mIdents.add(ani.getFromIdentifier()); mIdents.add(ani.getToIdentifier()); @@ -121,10 +114,8 @@ public class Tait1200DecoderState extends DecoderState ic.remove(IdentifierClass.USER); ic.update(message.getIdentifiers()); - broadcast(DecodeEvent.builder(ani.getTimestamp()) + broadcast(DecodeEvent.builder(DecodeEventType.ID_ANI, ani.getTimestamp()) .protocol(PROTOCOL_TAIT_1200) - .eventType(DecodeEventType.ID_ANI) - .eventDescription("ANI") .identifiers(ic) .details("Automatic Number Identification") .build()); diff --git a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200MessageFilter.java b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200MessageFilter.java index 72677dac..2da9bdfc 100644 --- a/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200MessageFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/tait/Tait1200MessageFilter.java @@ -1,52 +1,64 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 - ******************************************************************************/ +/* + * ***************************************************************************** + * 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.tait; import io.github.dsheirer.filter.Filter; -import io.github.dsheirer.filter.FilterElement; -import io.github.dsheirer.message.Message; +import io.github.dsheirer.message.IMessage; +import java.util.function.Function; -import java.util.Collections; -import java.util.List; - -public class Tait1200MessageFilter extends Filter +/** + * Filter for Tait1200 messages + */ +public class Tait1200MessageFilter extends Filter { - - public Tait1200MessageFilter() - { - super( "Tait-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) + private static final String TAIT1200_KEY = "Tait-1200"; + private final KeyExtractor mKeyExtractor = new KeyExtractor(); + + /** + * Constructor + */ + public Tait1200MessageFilter() { - return mEnabled && canProcess( message ); + super("Tait-1200 Messages"); } - @Override - public boolean canProcess( Message message ) + @Override + public boolean canProcess(IMessage message) { - return message instanceof Tait1200GPSMessage; + return message instanceof Tait1200GPSMessage; } - @Override - public List> getFilterElements() + @Override + public Function getKeyExtractor() { - return Collections.EMPTY_LIST; + return mKeyExtractor; + } + + /** + * Key extractor + */ + private class KeyExtractor implements Function + { + @Override + public String apply(IMessage message) + { + return TAIT1200_KEY; + } } } diff --git a/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java b/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java index 129dda56..b23f8a2f 100644 --- a/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java +++ b/src/main/java/io/github/dsheirer/module/log/DecodeEventLogger.java @@ -1,25 +1,23 @@ /* + * ***************************************************************************** + * 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.log; + import io.github.dsheirer.alias.AliasList; import io.github.dsheirer.alias.AliasModel; import io.github.dsheirer.channel.IChannelDescriptor; @@ -33,15 +31,14 @@ import io.github.dsheirer.module.decode.event.IDecodeEvent; import io.github.dsheirer.module.decode.event.IDecodeEventListener; import io.github.dsheirer.preference.TimestampFormat; import io.github.dsheirer.sample.Listener; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.QuoteMode; - import java.nio.file.Path; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.QuoteMode; public class DecodeEventLogger extends EventLogger implements IDecodeEventListener, Listener { @@ -101,9 +98,7 @@ public class DecodeEventLogger extends EventLogger implements IDecodeEventListen cells.add(mTimestampFormat.format(new Date(event.getTimeStart()))); cells.add(event.getDuration() > 0 ? event.getDuration() : ""); cells.add(event.getProtocol()); - - String description = event.getEventDescription(); - cells.add(description != null ? description : ""); + cells.add(event.getEventType()); List fromIdentifiers = event.getIdentifierCollection().getIdentifiers(Role.FROM); if(fromIdentifiers != null && !fromIdentifiers.isEmpty()) diff --git a/src/test/java/io/github/dsheirer/module/log/DecodeEventLoggerTest.java b/src/test/java/io/github/dsheirer/module/log/DecodeEventLoggerTest.java deleted file mode 100644 index 75fb76b1..00000000 --- a/src/test/java/io/github/dsheirer/module/log/DecodeEventLoggerTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ***************************************************************************** - * Copyright (C) 2014-2021 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.log; - -import io.github.dsheirer.alias.Alias; -import io.github.dsheirer.alias.AliasModel; -import io.github.dsheirer.channel.IChannelDescriptor; -import io.github.dsheirer.identifier.IdentifierCollection; -import io.github.dsheirer.identifier.Role; -import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier; -import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdentifier; -import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier; -import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.event.DecodeEvent; -import io.github.dsheirer.module.decode.event.IDecodeEvent; -import io.github.dsheirer.module.decode.p25.identifier.channel.APCO25Channel; -import io.github.dsheirer.module.decode.p25.identifier.talkgroup.APCO25Talkgroup; -import io.github.dsheirer.protocol.Protocol; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class DecodeEventLoggerTest { - IChannelDescriptor channelDescriptor = APCO25Channel.create(98765, 1); - - APCO25Talkgroup fromIdentifier = new APCO25Talkgroup(123, Role.FROM); - APCO25Talkgroup toIdentifier = new APCO25Talkgroup(456, Role.TO); - FrequencyConfigurationIdentifier freqIdentifier = new FrequencyConfigurationIdentifier(859562500L); - DecoderTypeConfigurationIdentifier decoderIdentifier = new DecoderTypeConfigurationIdentifier(DecoderType.P25_PHASE1); - AliasListConfigurationIdentifier aliasListIdentifier = new AliasListConfigurationIdentifier("My Alias List"); - AliasModel aliasModel = new AliasModel(); - - Path logDirectory = Paths.get(System.getProperty("java.io.tmpdir"), "sdr_trunk_tests"); - - @BeforeEach - void setUp() { - aliasModel.addAliasList(aliasListIdentifier.getValue()); - } - - @AfterEach - void tearDown() { - } - - IdentifierCollection buildIdentifierCollection() { - return new IdentifierCollection(Arrays.asList( - fromIdentifier, - toIdentifier, - decoderIdentifier, - freqIdentifier, - aliasListIdentifier - )); - } - - DecodeEvent.DecodeEventBuilder decodeEventBuilder() { - return DecodeEvent.builder(1634428994000L) - .channel(channelDescriptor) - .identifiers(buildIdentifierCollection()) - .duration(111) - .protocol(Protocol.APCO25) - .eventDescription("DATA_PACKET") - .timeslot(2) - .details("Some details"); - } - -// @Test -// void test_receive_writesToCsv() { -// IDecodeEvent decodeEvent = decodeEventBuilder().build(); -// -// DecodeEventLogger decodeEventLogger = new DecodeEventLogger(aliasModel, logDirectory, "_foo.txt", 859562500); -// DecodeEventLogger spy = spy(decodeEventLogger); -// -// doNothing().when(spy).write(anyString()); -// -// spy.receive(decodeEvent); -// -// String expectedToCsvString = -// "\"2021:10:16:20:03:14\",\"111\",\"APCO-25\",\"DATA_PACKET\",\"123\",\" (456)\",\"98765-1\",\"859.562500\",\"TS:2\",\"Some details\""; -// verify(spy).write(expectedToCsvString); -// } - -// @Test -// void test_receive_withQuotesInDetails_writesToCsv() { -// IDecodeEvent decodeEvent = decodeEventBuilder() -// .details("Some details now with \"quotes\"!") -// .build(); -// -// DecodeEventLogger decodeEventLogger = new DecodeEventLogger(aliasModel, logDirectory, "_foo.txt", 859562500); -// DecodeEventLogger spy = spy(decodeEventLogger); -// -// doNothing().when(spy).write(anyString()); -// -// spy.receive(decodeEvent); -// -// String expectedToCsvString = -// "\"2021:10:16:20:03:14\",\"111\",\"APCO-25\",\"DATA_PACKET\",\"123\",\" (456)\",\"98765-1\",\"859.562500\",\"TS:2\",\"Some details now with \"\"quotes\"\"!\""; -// verify(spy).write(expectedToCsvString); -// } - -// @Test -// void test_receive_withCommasInDetails_writesToCsv() { -// IDecodeEvent decodeEvent = decodeEventBuilder() -// .details("Some details now with, to an extent, commas!") -// .build(); -// -// DecodeEventLogger decodeEventLogger = new DecodeEventLogger(aliasModel, logDirectory, "_foo.txt", 859562500); -// DecodeEventLogger spy = spy(decodeEventLogger); -// -// doNothing().when(spy).write(anyString()); -// -// spy.receive(decodeEvent); -// -// String expectedToCsvString = -// "\"2021:10:16:20:03:14\",\"111\",\"APCO-25\",\"DATA_PACKET\",\"123\",\" (456)\",\"98765-1\",\"859.562500\",\"TS:2\",\"Some details now with, to an extent, commas!\""; -// verify(spy).write(expectedToCsvString); -// } -} \ No newline at end of file