#6 DMR Decoder (#947)

* #6 DMR decoder feature - work in progress

* #6 DMR Decoder.

* #6 DMR Decoder.  Code review updates.

Co-authored-by: Zhenyu Mao <maozhenyu@foxmail.com>
Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
Denny Sheirer
2020-10-12 04:57:59 -04:00
committed by GitHub
parent b061db1189
commit 7eec82deee
436 changed files with 36258 additions and 2409 deletions

View File

@@ -0,0 +1,74 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.module.HistoryModule;
import io.github.dsheirer.sample.Listener;
/**
* Decode event history module. Maintains a history of decode events and constrains the total history size.
*/
public class DecodeEventHistory extends HistoryModule<IDecodeEvent> implements IDecodeEventListener
{
/**
* Constructs an instance
*/
public DecodeEventHistory(int historySize)
{
super(historySize);
}
/**
* Implements the IDecodeEventListener interface - delegates to receive(event) method.
*/
@Override
public Listener<IDecodeEvent> getDecodeEventListener()
{
return this;
}
/**
* Process preload data
*/
@Subscribe
public void process(DecodeEventHistoryPreloadData preloadData)
{
for(IDecodeEvent decodeEvent: preloadData.getData())
{
receive(decodeEvent);
}
}
/**
* Processes a request for decode event history and posts the response back
* to the processing chain event bus so that any of the modules can receive that history.
*
* Note: this is principally used by the DMR decoder for Capacity+ REST channel rotation to transfer the decode
* event history from a traffic channel conversion to the new rest channel.
*
* @param request for decode event history
*/
@Subscribe
public void process(DecodeEventHistoryRequest request)
{
getInterModuleEventBus().post(new DecodeEventHistoryResponse(this));
}
}

View File

@@ -0,0 +1,40 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.controller.channel.event.PreloadDataContent;
import java.util.List;
/**
* Decode event history for pre-loading into a processing chain.
*/
public class DecodeEventHistoryPreloadData extends PreloadDataContent<List<IDecodeEvent>>
{
/**
* Constructs an instance
*
* @param data to preload
*/
public DecodeEventHistoryPreloadData(List<IDecodeEvent> data)
{
super(data);
}
}

View File

@@ -0,0 +1,35 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Request for decode event history from a processing chain for the specified channel.
*/
public class DecodeEventHistoryRequest extends ModuleEventBusMessage
{
/**
* Constructs an instance
*/
public DecodeEventHistoryRequest()
{
}
}

View File

@@ -0,0 +1,47 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.module.ModuleEventBusMessage;
/**
* Response to decode event history request from a processing chain for the specified channel.
*/
public class DecodeEventHistoryResponse extends ModuleEventBusMessage
{
private DecodeEventHistory mDecodeEventHistory;
/**
* Constructs an instance
* @param history that has a decode event history
*/
public DecodeEventHistoryResponse(DecodeEventHistory history)
{
mDecodeEventHistory = history;
}
/**
* Decode event history module
*/
public DecodeEventHistory getDecodeEventHistory()
{
return mDecodeEventHistory;
}
}

View File

@@ -59,7 +59,7 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
public DecodeEventModel()
{
MyEventBus.getEventBus().register(this);
MyEventBus.getGlobalEventBus().register(this);
}
/**
@@ -86,10 +86,17 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
}
}
/**
* Access the complete list of events managed by this model.
*/
public List<IDecodeEvent> getEvents()
{
return new ArrayList<>(mEvents);
}
public void dispose()
{
MyEventBus.getEventBus().unregister(this);
MyEventBus.getGlobalEventBus().unregister(this);
Iterator<IDecodeEvent> it = mEvents.iterator();
while(it.hasNext())
@@ -98,6 +105,16 @@ public class DecodeEventModel extends AbstractTableModel implements Listener<IDe
}
}
/**
* Clears all events from this model and loads the events argument
*/
public void clearAndSet(List<IDecodeEvent> events)
{
mEvents.clear();
mEvents.addAll(events);
fireTableDataChanged();
}
public void reset()
{
dispose();

View File

@@ -53,6 +53,7 @@ import java.awt.Component;
import java.awt.EventQueue;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -64,7 +65,8 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private DecodeEventModel mEmptyDecodeEventModel;
private DecodeEventModel mEventModel = new DecodeEventModel();
private DecodeEventHistory mCurrentEventHistory;
private JScrollPane mEmptyScroller;
private IconModel mIconModel;
private AliasModel mAliasModel;
@@ -77,15 +79,14 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
*/
public DecodeEventPanel(IconModel iconModel, UserPreferences userPreferences, AliasModel aliasModel)
{
MyEventBus.getEventBus().register(this);
MyEventBus.getGlobalEventBus().register(this);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mIconModel = iconModel;
mAliasModel = aliasModel;
mUserPreferences = userPreferences;
mTimestampCellRenderer = new TimestampCellRenderer();
mEmptyDecodeEventModel = new DecodeEventModel();
mTable = new JTable(mEmptyDecodeEventModel);
mTable = new JTable(mEventModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
@@ -95,6 +96,11 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
add(mEmptyScroller);
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
/**
* Receives preference update notifications via the event bus
* @param preferenceType that was updated
@@ -129,20 +135,25 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
}
@Override
public void receive(ProcessingChain processingChain)
public void receive(final ProcessingChain processingChain)
{
if(mCurrentEventHistory != null)
{
mCurrentEventHistory.addListener(null);
}
EventQueue.invokeLater(() -> {
//Dispose the current column width monitor and recreate after we swap out the table model
mTableColumnWidthMonitor.dispose();
mTable.setModel(processingChain != null ? processingChain.getDecodeEventModel() : mEmptyDecodeEventModel);
if(processingChain != null)
{
updateCellRenderers();
mCurrentEventHistory = processingChain.getDecodeEventHistory();
mEventModel.clearAndSet(mCurrentEventHistory.getItems());
processingChain.getDecodeEventHistory().addListener(mEventModel);
}
else
{
mCurrentEventHistory = null;
mEventModel.clearAndSet(Collections.emptyList());
}
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
});
}

View File

@@ -22,6 +22,7 @@ package io.github.dsheirer.module.decode.event;
public enum DecodeEventType
{
AFFILIATE("Affiliate"),
ANNOUNCEMENT("Announcement"),
AUTOMATIC_REGISTRATION_SERVICE("Motorola ARS"),
CALL("Call"),
@@ -55,6 +56,7 @@ public enum DecodeEventType
NOTIFICATION("Notification"),
PAGE("Page"),
QUERY("Query"),
RADIO_CHECK("Radio Check"),
REGISTER("Register"),
REGISTER_ESN("ESN"),
REQUEST("Request"),

View File

@@ -21,6 +21,7 @@
*/
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;
@@ -30,6 +31,7 @@ import javax.swing.table.AbstractTableModel;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
public class MessageActivityModel extends AbstractTableModel implements Listener<IMessage>
{
@@ -46,39 +48,49 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
private SimpleDateFormat mSDFTime = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
private FilterSet<IMessage> mMessageFilter;
public MessageActivityModel(FilterSet<IMessage> messageFilter)
{
mMessageFilter = messageFilter;
}
private FilterSet<IMessage> mMessageFilterSet = new FilterSet<>(new AllPassFilter<>());
public MessageActivityModel()
{
}
/**
* Applies the filter set
*/
public void setFilters(FilterSet filterSet)
{
mMessageFilterSet = filterSet;
}
/**
* Clears all messages from history
*/
public void clear()
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
});
}
/**
* Clears the current messages and loads the messages argument
*/
public void clearAndSet(List<IMessage> messages)
{
EventQueue.invokeLater(() -> {
mMessageItems.clear();
fireTableDataChanged();
for(IMessage message: messages)
{
int messageCount = mMessageItems.size();
mMessageItems.clear();
fireTableRowsDeleted(0, messageCount - 1);
receive(message);
}
});
}
public FilterSet<IMessage> getMessageFilter()
public FilterSet<IMessage> getMessageFilterSet()
{
return mMessageFilter;
return mMessageFilterSet;
}
public void dispose()
@@ -121,7 +133,7 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
return;
}
if(mMessageFilter.passes(message))
if(mMessageFilterSet.passes(message))
{
final MessageItem messageItem = new MessageItem(message);

View File

@@ -24,6 +24,7 @@ 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;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
@@ -53,12 +54,17 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
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 EMPTY_MODEL = new MessageActivityModel();
private JTable mTable = new JTable(EMPTY_MODEL);
private static MessageActivityModel mMessageModel = new MessageActivityModel();
private MessageHistory mCurrentMessageHistory;
private JTable mTable = new JTable(mMessageModel);
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
/**
* Constructs an instance
* @param userPreferences
*/
public MessageActivityPanel(UserPreferences userPreferences)
{
mUserPreferences = userPreferences;
@@ -71,33 +77,30 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
}
/**
* Updates the message activity model with message history from the specified processing chain
*/
@Override
public void receive(ProcessingChain processingChain)
{
EventQueue.invokeLater(new Runnable()
if(mCurrentMessageHistory != null)
{
@Override
public void run()
{
//Remove the existing table column width monitor and replace after swapping the table model
mTableColumnWidthMonitor.dispose();
mTable.setModel(processingChain != null ? processingChain.getMessageActivityModel() : EMPTY_MODEL);
mCurrentMessageHistory.addListener(null);
}
mTable.getColumnModel().getColumn(0).setPreferredWidth(18);
mTable.getColumnModel().getColumn(1).setPreferredWidth(15);
mTable.getColumnModel().getColumn(2).setPreferredWidth(600);
if(processingChain != null)
{
mManagementPanel.enableButtons();
}
else
{
mManagementPanel.disableButtons();
}
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
}
});
if(processingChain != null)
{
mCurrentMessageHistory = processingChain.getMessageHistory();
mMessageModel.clearAndSet(mCurrentMessageHistory.getItems());
mCurrentMessageHistory.addListener(mMessageModel);
mManagementPanel.enableButtons();
}
else
{
mCurrentMessageHistory = null;
mMessageModel.clear();
mManagementPanel.disableButtons();
}
}
public class MessageManagementPanel extends JPanel
@@ -227,7 +230,7 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
@SuppressWarnings("unchecked")
FilterSet<IMessage> filter = (FilterSet<IMessage>) ((MessageActivityModel) mTable.getModel())
.getMessageFilter();
.getMessageFilterSet();
FilterEditorPanel<IMessage> panel = new FilterEditorPanel<>(filter);