mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 09:07:27 -05:00
#90 [WIP] on redesigning the channel processing gui components and backing model. Simplifies channel state by introducing a new Metadata class that tracks all metadata for all decoders.
This commit is contained in:
91
src/channel/metadata/AliasedIdentifier.java
Normal file
91
src/channel/metadata/AliasedIdentifier.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import alias.Alias;
|
||||
|
||||
public class AliasedIdentifier
|
||||
{
|
||||
private String mIdentifier;
|
||||
private Alias mAlias;
|
||||
|
||||
/**
|
||||
* Identifier and corresponding optional alias
|
||||
*/
|
||||
public AliasedIdentifier()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets alias and value to null
|
||||
*/
|
||||
public void reset()
|
||||
{
|
||||
mIdentifier = null;
|
||||
mAlias = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier
|
||||
*/
|
||||
public String getIdentifier()
|
||||
{
|
||||
return mIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier to the argument value
|
||||
*/
|
||||
public void setIdentifier(String identifier)
|
||||
{
|
||||
mIdentifier = identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty identifier
|
||||
*/
|
||||
public boolean hasIdentifier()
|
||||
{
|
||||
return mIdentifier != null && !mIdentifier.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for the identifier.
|
||||
*/
|
||||
public Alias getAlias()
|
||||
{
|
||||
return mAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the alias to the argument
|
||||
*/
|
||||
public void setAlias(Alias alias)
|
||||
{
|
||||
mAlias = alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is an alias corresponding to the identifier
|
||||
*/
|
||||
public boolean hasAlias()
|
||||
{
|
||||
return mAlias != null;
|
||||
}
|
||||
}
|
||||
62
src/channel/metadata/Attribute.java
Normal file
62
src/channel/metadata/Attribute.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
public enum Attribute
|
||||
{
|
||||
CHANNEL_CONFIGURATION_LABEL_1,
|
||||
CHANNEL_CONFIGURATION_LABEL_2,
|
||||
CHANNEL_FREQUENCY,
|
||||
CHANNEL_ID,
|
||||
CHANNEL_STATE,
|
||||
MESSAGE,
|
||||
MESSAGE_TYPE,
|
||||
NETWORK_ID_1,
|
||||
NETWORK_ID_2,
|
||||
PRIMARY_ADDRESS_FROM,
|
||||
PRIMARY_ADDRESS_TO,
|
||||
SECONDARY_ADDRESS_FROM,
|
||||
SECONDARY_ADDRESS_TO,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO: remove all attributes below here ********************
|
||||
|
||||
CHANNEL_NUMBER,
|
||||
CHANNEL_NAME,
|
||||
CHANNEL_SITE_NUMBER,
|
||||
CHANNEL_SITE_NUMBER_ALIAS,
|
||||
DESCRIPTION,
|
||||
FROM_TALKGROUP,
|
||||
FROM_TALKGROUP_ALIAS,
|
||||
FROM_TALKGROUP_TYPE,
|
||||
NAC,
|
||||
SITE,
|
||||
SITE_ALIAS,
|
||||
SITE_NAME,
|
||||
SOURCE,
|
||||
SYSTEM,
|
||||
SYSTEM_NAME,
|
||||
TO_TALKGROUP,
|
||||
TO_TALKGROUP_ALIAS,
|
||||
TO_TALKGROUP_TYPE;
|
||||
}
|
||||
111
src/channel/metadata/AttributeChangeRequest.java
Normal file
111
src/channel/metadata/AttributeChangeRequest.java
Normal file
@@ -0,0 +1,111 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import alias.Alias;
|
||||
|
||||
public class AttributeChangeRequest<T>
|
||||
{
|
||||
private Attribute mAttribute;
|
||||
private T mValue;
|
||||
private Alias mAlias;
|
||||
|
||||
/**
|
||||
* Request to change the value of an attribute
|
||||
* @param attribute to change
|
||||
* @param value to change for the attribute
|
||||
* @param alias (optional) for the value
|
||||
*/
|
||||
public AttributeChangeRequest(Attribute attribute, T value, Alias alias)
|
||||
{
|
||||
mAttribute = attribute;
|
||||
mValue = value;
|
||||
mAlias = alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to change the value of an attribute
|
||||
* @param attribute to change
|
||||
* @param value to change for the attribute
|
||||
*/
|
||||
public AttributeChangeRequest(Attribute attribute, T value)
|
||||
{
|
||||
this(attribute, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute to change
|
||||
*/
|
||||
public Attribute getAttribute()
|
||||
{
|
||||
return mAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value to set for the attribute
|
||||
*/
|
||||
public T getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this change request has a non-null value for the attribute
|
||||
*/
|
||||
public boolean hasValue()
|
||||
{
|
||||
return mValue != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for the corresponding attribute value
|
||||
*/
|
||||
public Alias getAlias()
|
||||
{
|
||||
return mAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this change request contains an alias that corresponds to the attribute value
|
||||
*/
|
||||
public boolean hasAlias()
|
||||
{
|
||||
return mAlias != null;
|
||||
}
|
||||
|
||||
public String getStringValue()
|
||||
{
|
||||
if(mValue instanceof String)
|
||||
{
|
||||
return (String)mValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLongValue()
|
||||
{
|
||||
if(mValue instanceof Long)
|
||||
{
|
||||
return (Long)mValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
139
src/channel/metadata/ChannelMetadataModel.java
Normal file
139
src/channel/metadata/ChannelMetadataModel.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import controller.channel.Channel;
|
||||
import sample.Listener;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChannelMetadataModel extends AbstractTableModel implements Listener<MetadataChangeEvent>
|
||||
{
|
||||
public static final int COLUMN_STATE = 0;
|
||||
public static final int COLUMN_PRIMARY = 1;
|
||||
public static final int COLUMN_SECONDARY = 2;
|
||||
public static final int COLUMN_MESSAGE = 3;
|
||||
public static final int COLUMN_NETWORK = 4;
|
||||
public static final int COLUMN_FREQUENCY = 5;
|
||||
public static final int COLUMN_CONFIGURATION = 6;
|
||||
|
||||
private static final String[] COLUMNS =
|
||||
{"State", "Primary", "Secondary", "Message", "Network", "Frequency", "Configuration"};
|
||||
|
||||
private List<Metadata> mChannelMetadata = new ArrayList();
|
||||
private Map<Metadata,Channel> mMetadataChannelMap = new HashMap();
|
||||
|
||||
public void add(Metadata metadata, Channel channel)
|
||||
{
|
||||
mChannelMetadata.add(metadata);
|
||||
mMetadataChannelMap.put(metadata, channel);
|
||||
|
||||
int index = mChannelMetadata.indexOf(metadata);
|
||||
|
||||
fireTableRowsInserted(index, index);
|
||||
|
||||
metadata.addListener(this);
|
||||
}
|
||||
|
||||
public void remove(Metadata metadata)
|
||||
{
|
||||
metadata.removeListener(this);
|
||||
|
||||
int index = mChannelMetadata.indexOf(metadata);
|
||||
|
||||
mChannelMetadata.remove(metadata);
|
||||
mMetadataChannelMap.remove(metadata);
|
||||
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mChannelMetadata.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return COLUMNS.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column)
|
||||
{
|
||||
return COLUMNS[column];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
return Metadata.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
return mChannelMetadata.get(rowIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(MetadataChangeEvent metadataChangeEvent)
|
||||
{
|
||||
int rowIndex = mChannelMetadata.indexOf(metadataChangeEvent.getMetadata());
|
||||
|
||||
if(rowIndex >= 0)
|
||||
{
|
||||
switch(metadataChangeEvent.getAttribute())
|
||||
{
|
||||
case CHANNEL_CONFIGURATION_LABEL_1:
|
||||
case CHANNEL_CONFIGURATION_LABEL_2:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_CONFIGURATION);
|
||||
break;
|
||||
case CHANNEL_FREQUENCY:
|
||||
case CHANNEL_ID:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_FREQUENCY);
|
||||
break;
|
||||
case CHANNEL_STATE:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_STATE);
|
||||
break;
|
||||
case MESSAGE:
|
||||
case MESSAGE_TYPE:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_MESSAGE);
|
||||
break;
|
||||
case NETWORK_ID_1:
|
||||
case NETWORK_ID_2:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_NETWORK);
|
||||
break;
|
||||
case PRIMARY_ADDRESS_FROM:
|
||||
case PRIMARY_ADDRESS_TO:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_PRIMARY);
|
||||
break;
|
||||
case SECONDARY_ADDRESS_FROM:
|
||||
case SECONDARY_ADDRESS_TO:
|
||||
fireTableCellUpdated(rowIndex, COLUMN_SECONDARY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
237
src/channel/metadata/ChannelMetadataPanel.java
Normal file
237
src/channel/metadata/ChannelMetadataPanel.java
Normal file
@@ -0,0 +1,237 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import alias.Alias;
|
||||
import icon.IconTableModel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import java.awt.*;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
public class ChannelMetadataPanel extends JPanel
|
||||
{
|
||||
private ChannelMetadataModel mChannelMetadataModel;
|
||||
private IconTableModel mIconTableModel;
|
||||
private JTable mTable;
|
||||
|
||||
public ChannelMetadataPanel(ChannelMetadataModel channelMetadataModel, IconTableModel iconTableModel)
|
||||
{
|
||||
mChannelMetadataModel = channelMetadataModel;
|
||||
mIconTableModel = iconTableModel;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the panel
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") );
|
||||
|
||||
mTable = new JTable(mChannelMetadataModel);
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_STATE).setCellRenderer(new StateCellRenderer());
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_PRIMARY)
|
||||
.setCellRenderer(new AliasedValueCellRenderer(Attribute.PRIMARY_ADDRESS_TO, Attribute.PRIMARY_ADDRESS_FROM));
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_SECONDARY)
|
||||
.setCellRenderer(new AliasedValueCellRenderer(Attribute.SECONDARY_ADDRESS_TO, Attribute.SECONDARY_ADDRESS_FROM));
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_MESSAGE)
|
||||
.setCellRenderer(new AliasedValueCellRenderer(Attribute.MESSAGE, Attribute.MESSAGE_TYPE));
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_NETWORK)
|
||||
.setCellRenderer(new AliasedValueCellRenderer(Attribute.NETWORK_ID_1, Attribute.NETWORK_ID_2));
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_FREQUENCY)
|
||||
.setCellRenderer(new FrequencyCellRenderer());
|
||||
|
||||
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_CONFIGURATION)
|
||||
.setCellRenderer(new AliasedValueCellRenderer(Attribute.CHANNEL_CONFIGURATION_LABEL_1,
|
||||
Attribute.CHANNEL_CONFIGURATION_LABEL_2));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||
|
||||
add(scrollPane);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a selection listener to the contained metadata JTable
|
||||
*/
|
||||
public void addListSelectionListener(ListSelectionListener listener)
|
||||
{
|
||||
mTable.getSelectionModel().addListSelectionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the selection listener from the contained metadata JTable
|
||||
*/
|
||||
public void removeListSelectionListener(ListSelectionListener listener)
|
||||
{
|
||||
mTable.getSelectionModel().removeListSelectionListener(listener);
|
||||
}
|
||||
|
||||
public class AliasedValueCellRenderer extends JPanel implements TableCellRenderer
|
||||
{
|
||||
private JLabel mLabel1;
|
||||
private JLabel mLabel2;
|
||||
|
||||
private Attribute mAttribute1;
|
||||
private Attribute mAttribute2;
|
||||
|
||||
public AliasedValueCellRenderer(Attribute attribute1, Attribute attribute2)
|
||||
{
|
||||
attribute1 = attribute1;
|
||||
attribute2 = attribute2;
|
||||
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill][grow,fill]") );
|
||||
|
||||
mLabel1 = new JLabel();
|
||||
add(mLabel1,"wrap");
|
||||
mLabel2 = new JLabel();
|
||||
add(mLabel2,"wrap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
|
||||
int row, int column)
|
||||
{
|
||||
Metadata metadata = (Metadata)value;
|
||||
|
||||
if(mAttribute1 != null)
|
||||
{
|
||||
String value1 = metadata.getValue(mAttribute1);
|
||||
mLabel1.setText(value1);
|
||||
|
||||
Alias alias1 = metadata.getAlias(mAttribute1);
|
||||
|
||||
if(alias1 != null)
|
||||
{
|
||||
mLabel1.setIcon(mIconTableModel.getIcon(alias1.getIconName()).getIcon());
|
||||
mLabel1.setForeground(alias1.getDisplayColor());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLabel1.setIcon(null);
|
||||
mLabel1.setForeground(mTable.getForeground());
|
||||
}
|
||||
}
|
||||
|
||||
if(mAttribute2 != null)
|
||||
{
|
||||
String value2 = metadata.getValue(mAttribute2);
|
||||
mLabel2.setText(value2);
|
||||
|
||||
Alias alias2 = metadata.getAlias(mAttribute2);
|
||||
|
||||
if(alias2 != null)
|
||||
{
|
||||
mLabel2.setIcon(mIconTableModel.getIcon(alias2.getIconName()).getIcon());
|
||||
mLabel2.setForeground(alias2.getDisplayColor());
|
||||
}
|
||||
else
|
||||
{
|
||||
mLabel2.setIcon(null);
|
||||
mLabel2.setForeground(mTable.getForeground());
|
||||
}
|
||||
}
|
||||
|
||||
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FrequencyCellRenderer extends JPanel implements TableCellRenderer
|
||||
{
|
||||
private static final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
|
||||
|
||||
private JLabel mLabel1;
|
||||
private JLabel mLabel2;
|
||||
|
||||
public FrequencyCellRenderer()
|
||||
{
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill][grow,fill]") );
|
||||
|
||||
mLabel1 = new JLabel();
|
||||
add(mLabel1,"wrap");
|
||||
mLabel2 = new JLabel();
|
||||
add(mLabel2,"wrap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
|
||||
int row, int column)
|
||||
{
|
||||
Metadata metadata = (Metadata)value;
|
||||
|
||||
if(metadata.hasChannelFrequency())
|
||||
{
|
||||
mLabel1.setText(FREQUENCY_FORMATTER.format((double)metadata.getChannelFrequency() / 1E6d));
|
||||
}
|
||||
else
|
||||
{
|
||||
mLabel1.setText(null);
|
||||
}
|
||||
|
||||
mLabel2.setText(metadata.getChannelID());
|
||||
|
||||
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class StateCellRenderer extends JPanel implements TableCellRenderer
|
||||
{
|
||||
private static final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
|
||||
|
||||
private JLabel mLabel1;
|
||||
|
||||
public StateCellRenderer()
|
||||
{
|
||||
setLayout( new MigLayout( "", "[grow,fill,align center]", "[grow,fill,align center]") );
|
||||
|
||||
mLabel1 = new JLabel();
|
||||
add(mLabel1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
|
||||
int row, int column)
|
||||
{
|
||||
Metadata metadata = (Metadata)value;
|
||||
|
||||
mLabel1.setText(metadata.getState().getDisplayValue());
|
||||
|
||||
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
61
src/channel/metadata/ChannelMetadataViewer.java
Normal file
61
src/channel/metadata/ChannelMetadataViewer.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
import icon.IconTableModel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class ChannelMetadataViewer extends JPanel
|
||||
{
|
||||
private ChannelMetadataModel mChannelMetadataModel;
|
||||
private IconTableModel mIconTableModel;
|
||||
|
||||
/**
|
||||
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
|
||||
* messages, events, and spectral view.
|
||||
*/
|
||||
public ChannelMetadataViewer(ChannelMetadataModel channelMetadataModel, IconTableModel iconTableModel)
|
||||
{
|
||||
mChannelMetadataModel = channelMetadataModel;
|
||||
mIconTableModel = iconTableModel;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") );
|
||||
|
||||
ChannelMetadataPanel channelMetadataPanel = new ChannelMetadataPanel(mChannelMetadataModel, mIconTableModel);
|
||||
|
||||
JideTabbedPane tabbedPane = new JideTabbedPane();
|
||||
tabbedPane.addTab("Details", new JPanel());
|
||||
tabbedPane.addTab("Events", new JPanel());
|
||||
tabbedPane.addTab("Messages", new JPanel());
|
||||
tabbedPane.addTab("Spectrum", new JPanel());
|
||||
|
||||
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.add(channelMetadataPanel);
|
||||
splitPane.add(tabbedPane);
|
||||
|
||||
add(splitPane);
|
||||
}
|
||||
}
|
||||
463
src/channel/metadata/Metadata.java
Normal file
463
src/channel/metadata/Metadata.java
Normal file
@@ -0,0 +1,463 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
import alias.Alias;
|
||||
import channel.state.State;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
|
||||
public class Metadata implements Listener<AttributeChangeRequest>
|
||||
{
|
||||
private State mState = State.IDLE;
|
||||
private String mChannelConfigurationLabel1;
|
||||
private String mChannelConfigurationLabel2;
|
||||
private String mChannelID;
|
||||
private long mChannelFrequency;
|
||||
private AliasedIdentifier mNetworkID1 = new AliasedIdentifier();
|
||||
private AliasedIdentifier mNetworkID2 = new AliasedIdentifier();
|
||||
|
||||
//Temporal attributes
|
||||
private String mMessage;
|
||||
private String mMessageType;
|
||||
private AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier();
|
||||
private AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier();
|
||||
private AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier();
|
||||
private AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier();
|
||||
|
||||
private Broadcaster<MetadataChangeEvent> mMetadataChangeEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
|
||||
/**
|
||||
* Channel metadata. Contains all attributes that reflect the state and current attribute values for a channel
|
||||
* that is currently decoding. This metadata is intended to support any decoding channel gui components to
|
||||
* graphically convey the current state of a decoding channel.
|
||||
*/
|
||||
public Metadata()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of this metadata
|
||||
*/
|
||||
public void setState(State state)
|
||||
{
|
||||
mState = state;
|
||||
broadcast(Attribute.CHANNEL_STATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to change an attribute value for this metadata. This is the primary method for updating all attributes
|
||||
* in this metadata set.
|
||||
*/
|
||||
@Override
|
||||
public void receive(AttributeChangeRequest request)
|
||||
{
|
||||
switch(request.getAttribute())
|
||||
{
|
||||
case CHANNEL_CONFIGURATION_LABEL_1:
|
||||
mChannelConfigurationLabel1 = request.getStringValue();
|
||||
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
|
||||
break;
|
||||
case CHANNEL_CONFIGURATION_LABEL_2:
|
||||
mChannelConfigurationLabel2 = request.getStringValue();
|
||||
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
|
||||
break;
|
||||
case CHANNEL_FREQUENCY:
|
||||
if(request.hasValue())
|
||||
{
|
||||
mChannelFrequency = request.getLongValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
mChannelFrequency = 0;
|
||||
}
|
||||
broadcast(Attribute.CHANNEL_FREQUENCY);
|
||||
break;
|
||||
case CHANNEL_ID:
|
||||
mChannelID = request.getStringValue();
|
||||
broadcast(Attribute.CHANNEL_ID);
|
||||
break;
|
||||
case MESSAGE:
|
||||
mMessage = request.getStringValue();
|
||||
broadcast(Attribute.MESSAGE);
|
||||
break;
|
||||
case MESSAGE_TYPE:
|
||||
mMessageType = request.getStringValue();
|
||||
broadcast(Attribute.MESSAGE_TYPE);
|
||||
break;
|
||||
case NETWORK_ID_1:
|
||||
mNetworkID1.setIdentifier(request.getStringValue());
|
||||
mNetworkID1.setAlias(request.getAlias());
|
||||
broadcast(Attribute.NETWORK_ID_1);
|
||||
break;
|
||||
case NETWORK_ID_2:
|
||||
mNetworkID2.setIdentifier(request.getStringValue());
|
||||
mNetworkID2.setAlias(request.getAlias());
|
||||
broadcast(Attribute.NETWORK_ID_2);
|
||||
break;
|
||||
case PRIMARY_ADDRESS_FROM:
|
||||
mPrimaryAddressFrom.setIdentifier(request.getStringValue());
|
||||
mPrimaryAddressFrom.setAlias(request.getAlias());
|
||||
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
|
||||
break;
|
||||
case PRIMARY_ADDRESS_TO:
|
||||
mPrimaryAddressTo.setIdentifier(request.getStringValue());
|
||||
mPrimaryAddressTo.setAlias(request.getAlias());
|
||||
broadcast(Attribute.PRIMARY_ADDRESS_TO);
|
||||
break;
|
||||
case SECONDARY_ADDRESS_FROM:
|
||||
mSecondaryAddressFrom.setIdentifier(request.getStringValue());
|
||||
mSecondaryAddressFrom.setAlias(request.getAlias());
|
||||
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
|
||||
break;
|
||||
case SECONDARY_ADDRESS_TO:
|
||||
mSecondaryAddressTo.setIdentifier(request.getStringValue());
|
||||
mSecondaryAddressTo.setAlias(request.getAlias());
|
||||
broadcast(Attribute.SECONDARY_ADDRESS_TO);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Metadata Attribute: " +
|
||||
request.getAttribute().name());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts to registered listeners that an attribute has changed for this metadata
|
||||
*/
|
||||
private void broadcast(Attribute attribute)
|
||||
{
|
||||
mMetadataChangeEventBroadcaster.broadcast(new MetadataChangeEvent(this, attribute));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the listener to receive metadata change events for this channel metadata
|
||||
*/
|
||||
public void addListener(Listener<MetadataChangeEvent> listener)
|
||||
{
|
||||
mMetadataChangeEventBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from receiving metadata change events for this channel metadata
|
||||
*/
|
||||
public void removeListener(Listener<MetadataChangeEvent> listener)
|
||||
{
|
||||
mMetadataChangeEventBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets temporal attributes to null value. This only resets attributes that have a non-ull value in order to
|
||||
* limit the number of change events that are produced.
|
||||
*/
|
||||
public void resetTemporalAttributes()
|
||||
{
|
||||
if(hasMessage())
|
||||
{
|
||||
mMessage = null;
|
||||
broadcast(Attribute.MESSAGE);
|
||||
}
|
||||
|
||||
if(hasMessageType())
|
||||
{
|
||||
mMessageType = null;
|
||||
broadcast(Attribute.MESSAGE_TYPE);
|
||||
}
|
||||
|
||||
if(mPrimaryAddressFrom.hasIdentifier())
|
||||
{
|
||||
mPrimaryAddressFrom.reset();
|
||||
broadcast(Attribute.PRIMARY_ADDRESS_FROM);
|
||||
}
|
||||
|
||||
if(mPrimaryAddressTo.hasIdentifier())
|
||||
{
|
||||
mPrimaryAddressTo.reset();
|
||||
broadcast(Attribute.PRIMARY_ADDRESS_TO);
|
||||
}
|
||||
|
||||
if(mSecondaryAddressFrom.hasIdentifier())
|
||||
{
|
||||
mSecondaryAddressFrom.reset();
|
||||
broadcast(Attribute.SECONDARY_ADDRESS_FROM);
|
||||
}
|
||||
|
||||
if(mSecondaryAddressTo.hasIdentifier())
|
||||
{
|
||||
mSecondaryAddressTo.reset();
|
||||
broadcast(Attribute.SECONDARY_ADDRESS_TO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all attributes to empty values
|
||||
*/
|
||||
public void resetAllAttributes()
|
||||
{
|
||||
resetTemporalAttributes();
|
||||
|
||||
if(hasChannelConfigurationLabel1())
|
||||
{
|
||||
mChannelConfigurationLabel1 = null;
|
||||
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1);
|
||||
}
|
||||
|
||||
if(hasChannelConfigurationLabel2())
|
||||
{
|
||||
mChannelConfigurationLabel2 = null;
|
||||
broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2);
|
||||
}
|
||||
|
||||
if(hasChannelID())
|
||||
{
|
||||
mChannelID = null;
|
||||
broadcast(Attribute.CHANNEL_ID);
|
||||
}
|
||||
|
||||
if(hasChannelFrequency())
|
||||
{
|
||||
mChannelFrequency = 0;
|
||||
broadcast(Attribute.CHANNEL_FREQUENCY);
|
||||
}
|
||||
|
||||
if(mNetworkID1.hasIdentifier())
|
||||
{
|
||||
mNetworkID1.reset();
|
||||
broadcast(Attribute.NETWORK_ID_1);
|
||||
}
|
||||
|
||||
if(mNetworkID2.hasIdentifier())
|
||||
{
|
||||
mNetworkID2.reset();
|
||||
broadcast(Attribute.NETWORK_ID_2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the alias associated with the attribute or null
|
||||
*/
|
||||
public Alias getAlias(Attribute attribute)
|
||||
{
|
||||
switch(attribute)
|
||||
{
|
||||
case NETWORK_ID_1:
|
||||
return getNetworkID1().getAlias();
|
||||
case NETWORK_ID_2:
|
||||
return getNetworkID2().getAlias();
|
||||
case PRIMARY_ADDRESS_FROM:
|
||||
return getPrimaryAddressFrom().getAlias();
|
||||
case PRIMARY_ADDRESS_TO:
|
||||
return getPrimaryAddressTo().getAlias();
|
||||
case SECONDARY_ADDRESS_FROM:
|
||||
return getSecondaryAddressFrom().getAlias();
|
||||
case SECONDARY_ADDRESS_TO:
|
||||
return getSecondaryAddressTo().getAlias();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value for the specified attribute or null
|
||||
*/
|
||||
public String getValue(Attribute attribute)
|
||||
{
|
||||
switch(attribute)
|
||||
{
|
||||
case CHANNEL_CONFIGURATION_LABEL_1:
|
||||
return mChannelConfigurationLabel1;
|
||||
case CHANNEL_CONFIGURATION_LABEL_2:
|
||||
return mChannelConfigurationLabel2;
|
||||
case CHANNEL_ID:
|
||||
return mChannelID;
|
||||
case CHANNEL_STATE:
|
||||
return mState.getDisplayValue();
|
||||
case MESSAGE:
|
||||
return mMessage;
|
||||
case MESSAGE_TYPE:
|
||||
return mMessageType;
|
||||
case NETWORK_ID_1:
|
||||
return mNetworkID1.getIdentifier();
|
||||
case NETWORK_ID_2:
|
||||
return mNetworkID2.getIdentifier();
|
||||
case PRIMARY_ADDRESS_FROM:
|
||||
return mPrimaryAddressFrom.getIdentifier();
|
||||
case PRIMARY_ADDRESS_TO:
|
||||
return mPrimaryAddressTo.getIdentifier();
|
||||
case SECONDARY_ADDRESS_FROM:
|
||||
return mSecondaryAddressFrom.getIdentifier();
|
||||
case SECONDARY_ADDRESS_TO:
|
||||
return mPrimaryAddressTo.getIdentifier();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current channel state
|
||||
*/
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
/**
|
||||
* System name from the channel configuration
|
||||
*/
|
||||
public String getChannelConfigurationLabel1()
|
||||
{
|
||||
return mChannelConfigurationLabel1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty channel configuration system value
|
||||
*/
|
||||
public boolean hasChannelConfigurationLabel1()
|
||||
{
|
||||
return mChannelConfigurationLabel1 != null && !mChannelConfigurationLabel1.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Site name from the channel configuration
|
||||
*/
|
||||
public String getChannelConfigurationLabel2()
|
||||
{
|
||||
return mChannelConfigurationLabel2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty channel configuration site value
|
||||
*/
|
||||
public boolean hasChannelConfigurationLabel2()
|
||||
{
|
||||
return mChannelConfigurationLabel2 != null && !mChannelConfigurationLabel2.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel ID or Channel Number
|
||||
*/
|
||||
public String getChannelID()
|
||||
{
|
||||
return mChannelID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty channel identifier value
|
||||
*/
|
||||
public boolean hasChannelID()
|
||||
{
|
||||
return mChannelID != null && !mChannelID.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel frequency
|
||||
*/
|
||||
public long getChannelFrequency()
|
||||
{
|
||||
return mChannelFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-zero channel frequency value
|
||||
*/
|
||||
public boolean hasChannelFrequency()
|
||||
{
|
||||
return mChannelFrequency > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network Identifier 1
|
||||
*/
|
||||
public AliasedIdentifier getNetworkID1()
|
||||
{
|
||||
return mNetworkID1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Network Identifier 2
|
||||
*/
|
||||
public AliasedIdentifier getNetworkID2()
|
||||
{
|
||||
return mNetworkID2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
public String getMessage()
|
||||
{
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty message value
|
||||
*/
|
||||
public boolean hasMessage()
|
||||
{
|
||||
return mMessage != null && !mMessage.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Message and Decoder type
|
||||
*/
|
||||
public String getMessageType()
|
||||
{
|
||||
return mMessageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if there is a non-null, non-empty message and decoder type value
|
||||
*/
|
||||
public boolean hasMessageType()
|
||||
{
|
||||
return mMessageType != null && !mMessageType.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary FROM Address
|
||||
*/
|
||||
public AliasedIdentifier getPrimaryAddressFrom()
|
||||
{
|
||||
return mPrimaryAddressFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary TO Address
|
||||
*/
|
||||
public AliasedIdentifier getPrimaryAddressTo()
|
||||
{
|
||||
return mPrimaryAddressTo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Secondary FROM Address
|
||||
*/
|
||||
public AliasedIdentifier getSecondaryAddressFrom()
|
||||
{
|
||||
return mSecondaryAddressFrom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Secondary TO Address
|
||||
*/
|
||||
public AliasedIdentifier getSecondaryAddressTo()
|
||||
{
|
||||
return mSecondaryAddressTo;
|
||||
}
|
||||
}
|
||||
53
src/channel/metadata/MetadataChangeEvent.java
Normal file
53
src/channel/metadata/MetadataChangeEvent.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.metadata;
|
||||
|
||||
public class MetadataChangeEvent
|
||||
{
|
||||
private Metadata mMetadata;
|
||||
private Attribute mAttribute;
|
||||
|
||||
/**
|
||||
* Channel metadata changed attribute event
|
||||
*
|
||||
* @param metadata where the change occurred
|
||||
* @param attribute that changed
|
||||
*/
|
||||
public MetadataChangeEvent(Metadata metadata, Attribute attribute)
|
||||
{
|
||||
mMetadata = metadata;
|
||||
mAttribute = attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel metadata where the change occurred
|
||||
*/
|
||||
public Metadata getMetadata()
|
||||
{
|
||||
return mMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel metadata attribute that has changed
|
||||
*/
|
||||
public Attribute getAttribute()
|
||||
{
|
||||
return mAttribute;
|
||||
}
|
||||
}
|
||||
98
src/channel/state/AlwaysUnsquelchedDecoderState.java
Normal file
98
src/channel/state/AlwaysUnsquelchedDecoderState.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*******************************************************************************
|
||||
* 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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package channel.state;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import message.Message;
|
||||
import module.decode.DecoderType;
|
||||
import channel.state.DecoderStateEvent.Event;
|
||||
import audio.metadata.Metadata;
|
||||
import audio.metadata.MetadataType;
|
||||
|
||||
/**
|
||||
* Basic decoder channel state - provides the minimum channel state functionality
|
||||
* to support an always un-squelched audio decoder.
|
||||
*/
|
||||
public class AlwaysUnsquelchedDecoderState extends DecoderState
|
||||
{
|
||||
private DecoderType mDecoderType;
|
||||
private String mChannelName;
|
||||
|
||||
public AlwaysUnsquelchedDecoderState( DecoderType decoderType, String channelName )
|
||||
{
|
||||
super( null );
|
||||
|
||||
mDecoderType = decoderType;
|
||||
mChannelName = channelName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
/* No reset actions needed */
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivitySummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Activity Summary\n" );
|
||||
sb.append( "\tDecoder:\t" );
|
||||
sb.append( mDecoderType );
|
||||
sb.append( "\n\n" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message t )
|
||||
{
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveDecoderStateEvent( DecoderStateEvent event )
|
||||
{
|
||||
/* Not implemented */
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getDecoderType()
|
||||
{
|
||||
return mDecoderType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start( ScheduledExecutorService executor )
|
||||
{
|
||||
broadcast( new Metadata( MetadataType.TO, mChannelName, false ) );
|
||||
broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
}
|
||||
29
src/channel/state/ChangeChannelTimeoutEvent.java
Normal file
29
src/channel/state/ChangeChannelTimeoutEvent.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package channel.state;
|
||||
|
||||
import controller.channel.Channel.ChannelType;
|
||||
|
||||
/**
|
||||
* Allows decoders to change the call timeout setting in the channel state
|
||||
*/
|
||||
public class ChangeChannelTimeoutEvent extends DecoderStateEvent
|
||||
{
|
||||
private ChannelType mChannelType;
|
||||
private long mCallTimeout;
|
||||
|
||||
public ChangeChannelTimeoutEvent( Object source, ChannelType channelType, long timeout )
|
||||
{
|
||||
super( source, Event.CHANGE_CALL_TIMEOUT, State.IDLE );
|
||||
mChannelType = channelType;
|
||||
mCallTimeout = timeout;
|
||||
}
|
||||
|
||||
public ChannelType getChannelType()
|
||||
{
|
||||
return mChannelType;
|
||||
}
|
||||
|
||||
public long getCallTimeout()
|
||||
{
|
||||
return mCallTimeout;
|
||||
}
|
||||
}
|
||||
317
src/channel/state/ChannelList.java
Normal file
317
src/channel/state/ChannelList.java
Normal file
@@ -0,0 +1,317 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.state;
|
||||
|
||||
import audio.AudioPanel;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.channel.ChannelEventListener;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.ChannelProcessingManager;
|
||||
import icon.IconManager;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.ColorSettingMenuItem;
|
||||
import settings.ColorSettingResetMenuItem;
|
||||
import settings.SettingsManager;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Gui wrapper for the list of currently processing primary channels
|
||||
*/
|
||||
public class ChannelList extends JPanel implements ChannelEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Map<Channel, ChannelPanel> mDisplayedPanels = new ConcurrentHashMap<>();
|
||||
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private IconManager mIconManager;
|
||||
private SettingsManager mSettingsManager;
|
||||
private AudioPanel mAudioPanel;
|
||||
|
||||
public ChannelList(ChannelModel channelModel,
|
||||
ChannelProcessingManager channelProcessingManager,
|
||||
IconManager iconManager,
|
||||
SettingsManager settingsManager,
|
||||
AudioPanel audioPanel)
|
||||
{
|
||||
mChannelModel = channelModel;
|
||||
mChannelProcessingManager = channelProcessingManager;
|
||||
mIconManager = iconManager;
|
||||
mSettingsManager = settingsManager;
|
||||
mAudioPanel = audioPanel;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0,wrap 1", "[grow,fill]", "[]0[]"));
|
||||
setBackground(Color.BLACK);
|
||||
|
||||
addMouseListener(new ListSelectionListener());
|
||||
|
||||
add(mAudioPanel);
|
||||
|
||||
JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
|
||||
add(separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelChanged(ChannelEvent event)
|
||||
{
|
||||
switch(event.getEvent())
|
||||
{
|
||||
case NOTIFICATION_PROCESSING_START:
|
||||
addChannelPanel(event.getChannel());
|
||||
break;
|
||||
case NOTIFICATION_PROCESSING_STOP:
|
||||
removeChannelPanel(event.getChannel());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void addChannelPanel(Channel channel)
|
||||
{
|
||||
if(!mDisplayedPanels.containsKey(channel))
|
||||
{
|
||||
ChannelPanel channelPanel = new ChannelPanel(mChannelModel,
|
||||
mChannelProcessingManager, mIconManager, mSettingsManager, channel);
|
||||
|
||||
add(channelPanel);
|
||||
|
||||
mDisplayedPanels.put(channel, channelPanel);
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeChannelPanel(Channel channel)
|
||||
{
|
||||
if(mDisplayedPanels.containsKey(channel))
|
||||
{
|
||||
ChannelPanel panel = mDisplayedPanels.remove(channel);
|
||||
|
||||
if(panel != null)
|
||||
{
|
||||
remove(panel);
|
||||
|
||||
panel.dispose();
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
public class ListSelectionListener implements MouseListener
|
||||
{
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e)
|
||||
{
|
||||
Component component = getComponentAt(e.getPoint());
|
||||
|
||||
if(component instanceof ChannelPanel)
|
||||
{
|
||||
ChannelPanel panel = (ChannelPanel) component;
|
||||
|
||||
Channel channel = panel.getChannel();
|
||||
|
||||
if(e.getButton() == MouseEvent.BUTTON1)
|
||||
{
|
||||
if(channel.isSelected())
|
||||
{
|
||||
mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_DESELECT));
|
||||
}
|
||||
else
|
||||
{
|
||||
mChannelModel.broadcast(new ChannelEvent(channel, Event.REQUEST_SELECT));
|
||||
}
|
||||
}
|
||||
else if(e.getButton() == MouseEvent.BUTTON3)
|
||||
{
|
||||
JPopupMenu popupMenu = new JPopupMenu();
|
||||
|
||||
JMenu menu = panel.getContextMenu();
|
||||
|
||||
if(menu != null)
|
||||
{
|
||||
popupMenu.add(menu);
|
||||
}
|
||||
|
||||
JMenu colorMenu = new JMenu("Color");
|
||||
popupMenu.add(colorMenu);
|
||||
|
||||
JMenu backgroundMenu = new JMenu("Background");
|
||||
colorMenu.add(backgroundMenu);
|
||||
|
||||
backgroundMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND));
|
||||
backgroundMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND));
|
||||
|
||||
JMenu channelsMenu = new JMenu("Channel State");
|
||||
colorMenu.add(channelsMenu);
|
||||
|
||||
JMenu callMenu = new JMenu("Call");
|
||||
channelsMenu.add(callMenu);
|
||||
|
||||
callMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL));
|
||||
callMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL));
|
||||
callMenu.add(new JSeparator());
|
||||
|
||||
callMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL));
|
||||
callMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL));
|
||||
|
||||
JMenu controlMenu = new JMenu("Control");
|
||||
channelsMenu.add(controlMenu);
|
||||
|
||||
controlMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL));
|
||||
controlMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL));
|
||||
controlMenu.add(new JSeparator());
|
||||
|
||||
controlMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL));
|
||||
controlMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL));
|
||||
|
||||
JMenu dataMenu = new JMenu("Data");
|
||||
channelsMenu.add(dataMenu);
|
||||
|
||||
dataMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA));
|
||||
dataMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA));
|
||||
dataMenu.add(new JSeparator());
|
||||
|
||||
dataMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA));
|
||||
dataMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA));
|
||||
|
||||
JMenu fadeMenu = new JMenu("Fade");
|
||||
channelsMenu.add(fadeMenu);
|
||||
|
||||
fadeMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE));
|
||||
fadeMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE));
|
||||
fadeMenu.add(new JSeparator());
|
||||
|
||||
fadeMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE));
|
||||
fadeMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE));
|
||||
|
||||
JMenu idleMenu = new JMenu("Idle");
|
||||
channelsMenu.add(idleMenu);
|
||||
|
||||
idleMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE));
|
||||
idleMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE));
|
||||
idleMenu.add(new JSeparator());
|
||||
|
||||
idleMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE));
|
||||
idleMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE));
|
||||
|
||||
JMenu noTunerMenu = new JMenu("No Tuner");
|
||||
channelsMenu.add(noTunerMenu);
|
||||
|
||||
noTunerMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER));
|
||||
noTunerMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER));
|
||||
noTunerMenu.add(new JSeparator());
|
||||
|
||||
noTunerMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER));
|
||||
noTunerMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER));
|
||||
|
||||
JMenu labelMenu = new JMenu("Labels");
|
||||
colorMenu.add(labelMenu);
|
||||
|
||||
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER));
|
||||
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER));
|
||||
labelMenu.add(new JSeparator());
|
||||
|
||||
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER));
|
||||
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER));
|
||||
labelMenu.add(new JSeparator());
|
||||
|
||||
labelMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS));
|
||||
labelMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS));
|
||||
|
||||
JMenu selectedMenu = new JMenu("Selected");
|
||||
colorMenu.add(selectedMenu);
|
||||
|
||||
selectedMenu.add(new ColorSettingMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL));
|
||||
selectedMenu.add(new ColorSettingResetMenuItem(mSettingsManager,
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL));
|
||||
|
||||
popupMenu.show(ChannelList.this, e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mousePressed(MouseEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseReleased(MouseEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
466
src/channel/state/ChannelPanel.java
Normal file
466
src/channel/state/ChannelPanel.java
Normal file
@@ -0,0 +1,466 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.state;
|
||||
|
||||
import channel.metadata.Attribute;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.channel.ChannelEventListener;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.ChannelProcessingManager;
|
||||
import controller.channel.ChannelUtils;
|
||||
import icon.IconManager;
|
||||
import module.ProcessingChain;
|
||||
import module.decode.DecoderFactory;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import record.RecorderType;
|
||||
import record.config.RecordConfiguration;
|
||||
import sample.Listener;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingChangeListener;
|
||||
import settings.SettingsManager;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelPanel extends JPanel
|
||||
implements Listener<Attribute>,
|
||||
SettingChangeListener,
|
||||
ChannelEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelPanel.class);
|
||||
|
||||
private Font mFontDetails = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
private Font mFontDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
|
||||
protected Color mColorChannelBackground;
|
||||
protected Color mColorChannelSelected;
|
||||
protected Color mColorTopCall;
|
||||
protected Color mColorMiddleCall;
|
||||
protected Color mColorTopControl;
|
||||
protected Color mColorMiddleControl;
|
||||
protected Color mColorTopData;
|
||||
protected Color mColorMiddleData;
|
||||
protected Color mColorTopFade;
|
||||
protected Color mColorMiddleFade;
|
||||
protected Color mColorTopIdle;
|
||||
protected Color mColorMiddleIdle;
|
||||
protected Color mColorTopNoTuner;
|
||||
protected Color mColorMiddleNoTuner;
|
||||
protected Color mColorLabelDetails;
|
||||
protected Color mColorLabelDecoder;
|
||||
protected Color mColorLabelAuxDecoder;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mSourceLabel;
|
||||
private JLabel mOptionsLabel;
|
||||
|
||||
private JLabel mSystemLabel;
|
||||
private JLabel mSiteLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
protected SettingsManager mSettingsManager;
|
||||
protected IconManager mIconManager;
|
||||
|
||||
private Channel mChannel;
|
||||
private ChannelState mChannelState;
|
||||
private List<DecoderPanel> mDecoderPanels = new ArrayList<>();
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
|
||||
/**
|
||||
* Gui component to a channel state. Provides System, Site and channel
|
||||
* name, source and channel state visualization.
|
||||
*
|
||||
* Updates color background according to current channel state.
|
||||
*/
|
||||
public ChannelPanel(ChannelModel channelModel,
|
||||
ChannelProcessingManager channelProcessingManager,
|
||||
IconManager iconManager,
|
||||
SettingsManager settingsManager,
|
||||
Channel channel)
|
||||
{
|
||||
mChannelModel = channelModel;
|
||||
mChannelProcessingManager = channelProcessingManager;
|
||||
mIconManager = iconManager;
|
||||
mSettingsManager = settingsManager;
|
||||
mChannel = channel;
|
||||
|
||||
ProcessingChain processingChain =
|
||||
channelProcessingManager.getProcessingChain(mChannel);
|
||||
|
||||
if(processingChain != null)
|
||||
{
|
||||
mChannelState = processingChain.getChannelState();
|
||||
|
||||
init();
|
||||
|
||||
for(DecoderState decoderState : processingChain.getDecoderStates())
|
||||
{
|
||||
DecoderPanel panel = DecoderFactory.getDecoderPanel(mIconManager, mSettingsManager, decoderState);
|
||||
mDecoderPanels.add(panel);
|
||||
add(panel, "grow,span");
|
||||
}
|
||||
|
||||
mChannelState.setChangedAttributeListener(this);
|
||||
mChannelModel.addListener(this);
|
||||
mSettingsManager.addListener(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Processing Chain was null!");
|
||||
}
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
getColors();
|
||||
|
||||
setLayout(new MigLayout("insets 1 1 1 1", "[grow,fill]", "[]0[]0[]"));
|
||||
|
||||
mSystemLabel = new JLabel(mChannel.getSystem());
|
||||
mSystemLabel.setFont(mFontDetails);
|
||||
mSystemLabel.setForeground(mColorLabelDetails);
|
||||
add(mSystemLabel);
|
||||
|
||||
mSiteLabel = new JLabel(mChannel.getSite());
|
||||
mSiteLabel.setFont(mFontDetails);
|
||||
mSiteLabel.setForeground(mColorLabelDetails);
|
||||
add(mSiteLabel);
|
||||
|
||||
mChannelLabel = new JLabel(mChannel.getName());
|
||||
mChannelLabel.setFont(mFontDetails);
|
||||
mChannelLabel.setForeground(mColorLabelDetails);
|
||||
add(mChannelLabel, "wrap");
|
||||
|
||||
mStateLabel = new JLabel(mChannelState.getState().getDisplayValue());
|
||||
mStateLabel.setFont(mFontDecoder);
|
||||
mStateLabel.setForeground(mColorLabelDecoder);
|
||||
add(mStateLabel);
|
||||
|
||||
mSourceLabel = new JLabel(mChannel.getSourceConfiguration().getDescription());
|
||||
mSourceLabel.setFont(mFontDetails);
|
||||
mSourceLabel.setForeground(mColorLabelDetails);
|
||||
add(mSourceLabel);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(!mChannel.getEventLogConfiguration().getLoggers().isEmpty())
|
||||
{
|
||||
sb.append("LOG ");
|
||||
}
|
||||
|
||||
RecordConfiguration recordConfig = mChannel.getRecordConfiguration();
|
||||
|
||||
if(recordConfig.getRecorders().contains(RecorderType.AUDIO))
|
||||
{
|
||||
sb.append("AUDIO ");
|
||||
}
|
||||
|
||||
if(recordConfig.getRecorders().contains(RecorderType.BASEBAND) ||
|
||||
recordConfig.getRecorders().contains(RecorderType.TRAFFIC_BASEBAND))
|
||||
{
|
||||
sb.append("BASEBAND");
|
||||
}
|
||||
|
||||
mOptionsLabel = new JLabel(sb.toString());
|
||||
mOptionsLabel.setFont(mFontDetails);
|
||||
mOptionsLabel.setForeground(mColorLabelAuxDecoder);
|
||||
add(mOptionsLabel, "wrap");
|
||||
}
|
||||
|
||||
public Channel getChannel()
|
||||
{
|
||||
return mChannel;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mChannelModel.removeListener(this);
|
||||
mChannelModel = null;
|
||||
|
||||
mChannelProcessingManager = null;
|
||||
|
||||
mSettingsManager.removeListener(this);
|
||||
mSettingsManager = null;
|
||||
|
||||
mChannelState.removeChangedAttributeListener();
|
||||
mChannelState = null;
|
||||
mChannel = null;
|
||||
|
||||
for(DecoderPanel panel : mDecoderPanels)
|
||||
{
|
||||
panel.dispose();
|
||||
remove(panel);
|
||||
}
|
||||
|
||||
mDecoderPanels.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(final Attribute attribute)
|
||||
{
|
||||
EventQueue.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
switch(attribute)
|
||||
{
|
||||
case CHANNEL_STATE:
|
||||
if(mChannelState != null && mChannelState.getState() != null)
|
||||
{
|
||||
mStateLabel.setText(mChannelState.getState().getDisplayValue());
|
||||
}
|
||||
break;
|
||||
case SOURCE:
|
||||
mSourceLabel.setText(mChannel.getSourceConfiguration()
|
||||
.getDescription());
|
||||
break;
|
||||
case CHANNEL_NAME:
|
||||
case SITE_NAME:
|
||||
case SYSTEM_NAME:
|
||||
mChannelLabel.setText(mChannel.toString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g)
|
||||
{
|
||||
super.paintComponent(g);
|
||||
|
||||
setBackground(mColorChannelBackground);
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
|
||||
Paint p = null;
|
||||
|
||||
final Channel channel = mChannel;
|
||||
|
||||
if(mChannelState != null)
|
||||
{
|
||||
switch(mChannelState.getState())
|
||||
{
|
||||
case CALL:
|
||||
p = getGradient(mColorTopCall, mColorMiddleCall);
|
||||
break;
|
||||
case CONTROL:
|
||||
p = getGradient(mColorTopControl, mColorMiddleControl);
|
||||
break;
|
||||
case DATA:
|
||||
p = getGradient(mColorTopData, mColorMiddleData);
|
||||
break;
|
||||
case FADE:
|
||||
p = getGradient(mColorTopFade, mColorMiddleFade);
|
||||
break;
|
||||
case IDLE:
|
||||
default:
|
||||
p = getGradient(mColorTopIdle, mColorMiddleIdle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(p == null)
|
||||
{
|
||||
p = getGradient(mColorTopIdle, mColorMiddleIdle);
|
||||
}
|
||||
|
||||
g2.setPaint(p);
|
||||
g2.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
/* Draw bottom separator line */
|
||||
g2.setColor(Color.LIGHT_GRAY);
|
||||
g2.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);
|
||||
|
||||
/* Draw channel selected box */
|
||||
if(getChannel() != null && getChannel().isSelected())
|
||||
{
|
||||
g2.setColor(mColorChannelSelected);
|
||||
g2.drawRect(1, 1, getWidth() - 2, getHeight() - 2);
|
||||
}
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
|
||||
private void getColors()
|
||||
{
|
||||
mColorChannelBackground = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_BACKGROUND).getColor();
|
||||
mColorChannelSelected = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL).getColor();
|
||||
mColorTopCall = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL).getColor();
|
||||
mColorMiddleCall = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL).getColor();
|
||||
mColorTopControl = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL).getColor();
|
||||
mColorMiddleControl = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL).getColor();
|
||||
mColorTopData = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA).getColor();
|
||||
mColorMiddleData = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA).getColor();
|
||||
mColorTopFade = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE).getColor();
|
||||
mColorMiddleFade = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE).getColor();
|
||||
mColorTopIdle = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE).getColor();
|
||||
mColorMiddleIdle = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE).getColor();
|
||||
mColorTopNoTuner = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER).getColor();
|
||||
mColorMiddleNoTuner = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER).getColor();
|
||||
|
||||
mColorLabelDetails = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS).getColor();
|
||||
mColorLabelDecoder = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER).getColor();
|
||||
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER).getColor();
|
||||
}
|
||||
|
||||
private GradientPaint getGradient(Color top, Color middle)
|
||||
{
|
||||
return new GradientPaint(0f, -50.0f, top,
|
||||
0f, (float) getHeight() / 2.2f, middle, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged(Setting setting)
|
||||
{
|
||||
if(setting instanceof ColorSetting)
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting) setting;
|
||||
|
||||
switch(colorSetting.getColorSettingName())
|
||||
{
|
||||
case CHANNEL_STATE_BACKGROUND:
|
||||
mColorChannelBackground = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_CALL:
|
||||
mColorMiddleCall = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_CALL:
|
||||
mColorTopCall = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL:
|
||||
mColorMiddleControl = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_CONTROL:
|
||||
mColorTopControl = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_DATA:
|
||||
mColorMiddleData = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_DATA:
|
||||
mColorTopData = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_FADE:
|
||||
mColorMiddleFade = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_FADE:
|
||||
mColorTopFade = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE:
|
||||
mColorMiddleIdle = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_IDLE:
|
||||
mColorTopIdle = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER:
|
||||
mColorMiddleNoTuner = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER:
|
||||
mColorTopNoTuner = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_AUX_DECODER:
|
||||
mColorLabelAuxDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DECODER:
|
||||
mColorLabelDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
mColorLabelDetails = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_SELECTED_CHANNEL:
|
||||
mColorChannelSelected = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted(Setting setting)
|
||||
{
|
||||
}
|
||||
|
||||
public JMenu getContextMenu()
|
||||
{
|
||||
return ChannelUtils.getContextMenu(mChannelModel, mChannelProcessingManager,
|
||||
mChannel, ChannelPanel.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelChanged(ChannelEvent event)
|
||||
{
|
||||
if(event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE &&
|
||||
event.getChannel() == mChannel)
|
||||
{
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
678
src/channel/state/ChannelState.java
Normal file
678
src/channel/state/ChannelState.java
Normal file
@@ -0,0 +1,678 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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 channel.state;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import channel.metadata.Attribute;
|
||||
import channel.traffic.TrafficChannelManager;
|
||||
import module.Module;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.ICallEventProvider;
|
||||
import channel.state.DecoderStateEvent.Event;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import audio.metadata.IMetadataProvider;
|
||||
import audio.metadata.Metadata;
|
||||
import audio.metadata.MetadataReset;
|
||||
import audio.metadata.MetadataType;
|
||||
import audio.squelch.ISquelchStateProvider;
|
||||
import audio.squelch.SquelchState;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
|
||||
public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider,
|
||||
IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ChannelState.class );
|
||||
|
||||
public static final long FADE_TIMEOUT_DELAY = 1200;
|
||||
public static final long RESET_TIMEOUT_DELAY = 2000;
|
||||
|
||||
private State mState = State.IDLE;
|
||||
|
||||
private Listener<CallEvent> mCallEventListener;
|
||||
private Listener<Attribute> mChangedAttributeListener;
|
||||
private Listener<DecoderStateEvent> mDecoderStateListener;
|
||||
private Listener<Metadata> mMetadataListener;
|
||||
private Listener<SquelchState> mSquelchStateListener;
|
||||
private DecoderStateEventReceiver mDecoderStateEventReceiver =
|
||||
new DecoderStateEventReceiver();
|
||||
|
||||
private boolean mSquelchLocked = false;
|
||||
private boolean mSelected = false;
|
||||
|
||||
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
||||
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
||||
private long mFadeTimeout;
|
||||
private long mEndTimeout;
|
||||
|
||||
private StateMonitor mStateMonitor = new StateMonitor();
|
||||
private ScheduledFuture<?> mStateMonitorFuture;
|
||||
|
||||
private ChannelType mChannelType;
|
||||
|
||||
private TrafficChannelManager mTrafficChannelEndListener;
|
||||
private CallEvent mTrafficChannelCallEvent;
|
||||
|
||||
/**
|
||||
* Channel state tracks the overall state of all processing modules and decoders
|
||||
* configured for the channel and provides squelch control and decoder state
|
||||
* reset events.
|
||||
*
|
||||
* Uses a state enumeration that defines allowable channel state transitions in
|
||||
* order to track a call or data decode event from start to finish. Uses a
|
||||
* timer to monitor for inactivity and to provide a FADE period that indicates
|
||||
* to the user that the activity has stopped while continuing to provide details
|
||||
* about the call, before the state is reset to IDLE.
|
||||
*
|
||||
* Since this class is multi-threaded between decoder events and the internal
|
||||
* timer that monitors for inactivity, this class uses the mState object for
|
||||
* thread synchronization.
|
||||
*
|
||||
* State Descriptions:
|
||||
* IDLE Normal state. No voice or data call activity
|
||||
* CALL/DATA/ENCRYPTED/CONTROL
|
||||
* Decoding states.
|
||||
* FADE The phase after a voice or data call when either an explicit
|
||||
* call end has been received, or when no new signalling updates
|
||||
* have been received, and the fade timer has expired. This phase
|
||||
* allows for gui updates to signal to the user that the call is
|
||||
* ended, while continuing to display the call details for the user
|
||||
* TEARDOWN Indicates a traffic channel that will be torn down for reuse.
|
||||
*/
|
||||
public ChannelState( ChannelType channelType )
|
||||
{
|
||||
mChannelType = channelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this channel state and prepares it for reuse.
|
||||
*/
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||
broadcast( new MetadataReset() );
|
||||
|
||||
mState = State.IDLE;
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start( ScheduledExecutorService executor )
|
||||
{
|
||||
if( mStateMonitorFuture == null && mStateMonitor != null )
|
||||
{
|
||||
if( mTrafficChannelEndListener != null )
|
||||
{
|
||||
setState( State.CALL );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mStateMonitorFuture = executor.scheduleAtFixedRate( mStateMonitor, 0, 20, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
mLog.error( "state monitor scheduled task rejected", ree );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if( mStateMonitorFuture != null )
|
||||
{
|
||||
boolean success = mStateMonitorFuture.cancel( true );
|
||||
|
||||
if( !success )
|
||||
{
|
||||
mLog.error("Couldn't stop monitoring scheduled future" );
|
||||
}
|
||||
}
|
||||
|
||||
mTrafficChannelEndListener = null;
|
||||
|
||||
if( mTrafficChannelCallEvent != null )
|
||||
{
|
||||
mTrafficChannelCallEvent.end();
|
||||
broadcast( mTrafficChannelCallEvent );
|
||||
}
|
||||
|
||||
mTrafficChannelCallEvent = null;
|
||||
|
||||
mStateMonitorFuture = null;
|
||||
|
||||
mSquelchLocked = false;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mCallEventListener = null;
|
||||
mChangedAttributeListener = null;
|
||||
mDecoderStateListener = null;
|
||||
mSquelchStateListener = null;
|
||||
mStateMonitor = null;
|
||||
}
|
||||
|
||||
private boolean isStandardChannel()
|
||||
{
|
||||
return mChannelType == ChannelType.STANDARD;
|
||||
}
|
||||
|
||||
private boolean isTrafficChannel()
|
||||
{
|
||||
return mChannelType == ChannelType.TRAFFIC;
|
||||
}
|
||||
|
||||
public void setStandardChannelTimeout( long milliseconds )
|
||||
{
|
||||
mStandardChannelFadeTimeout = milliseconds;
|
||||
|
||||
if( mChannelType == ChannelType.STANDARD )
|
||||
{
|
||||
mFadeTimeout = mStandardChannelFadeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrafficChannelTimeout( long milliseconds )
|
||||
{
|
||||
mTrafficChannelFadeTimeout = milliseconds;
|
||||
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
{
|
||||
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelected( boolean selected )
|
||||
{
|
||||
mSelected = selected;
|
||||
}
|
||||
|
||||
public boolean isSelected()
|
||||
{
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the fade timeout threshold to the current time plus delay
|
||||
*/
|
||||
private void updateFadeTimeout()
|
||||
{
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the reset timeout threshold to the current time plus delay
|
||||
*/
|
||||
private void updateResetTimeout()
|
||||
{
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
mEndTimeout = System.currentTimeMillis();
|
||||
}
|
||||
else
|
||||
{
|
||||
mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the squelch state to the registered listener
|
||||
*/
|
||||
protected void broadcast( SquelchState state )
|
||||
{
|
||||
if( mSquelchStateListener != null && !mSquelchLocked )
|
||||
{
|
||||
mSquelchStateListener.receive( state );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the squelch state listener
|
||||
*/
|
||||
@Override
|
||||
public void setSquelchStateListener( Listener<SquelchState> listener )
|
||||
{
|
||||
mSquelchStateListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the squelch state listener
|
||||
*/
|
||||
@Override
|
||||
public void removeSquelchStateListener()
|
||||
{
|
||||
mSquelchStateListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel state to the specified state, or updates the timeout
|
||||
* values so that the state monitor will not change state. Broadcasts a
|
||||
* squelch event when the state changes and the audio squelch state should
|
||||
* change. Also broadcasts changed attribute and decoder state events so
|
||||
* that external processes can maintain sync with this channel state.
|
||||
*/
|
||||
protected void setState( State state )
|
||||
{
|
||||
synchronized( mState )
|
||||
{
|
||||
if( state == mState )
|
||||
{
|
||||
if( State.CALL_STATES.contains( state ) )
|
||||
{
|
||||
updateFadeTimeout();
|
||||
}
|
||||
}
|
||||
else if( state != mState && mState.canChangeTo( state ) )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
case CONTROL:
|
||||
//Don't allow traffic channels to be control channels,
|
||||
//otherwise they can't transition to call tear down
|
||||
if( isStandardChannel() )
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateFadeTimeout();
|
||||
mState = state;
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
}
|
||||
break;
|
||||
case DATA:
|
||||
case ENCRYPTED:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateFadeTimeout();
|
||||
mState = state;
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
break;
|
||||
case CALL:
|
||||
broadcast( SquelchState.UNSQUELCH );
|
||||
updateFadeTimeout();
|
||||
mState = state;
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
break;
|
||||
case FADE:
|
||||
processFadeState();
|
||||
break;
|
||||
case IDLE:
|
||||
processIdleState();
|
||||
break;
|
||||
case TEARDOWN:
|
||||
processTeardownState();
|
||||
break;
|
||||
case RESET:
|
||||
mState = State.IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state and processes related actions
|
||||
*/
|
||||
private void processFadeState()
|
||||
{
|
||||
updateResetTimeout();
|
||||
mState = State.FADE;
|
||||
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
private void processIdleState()
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
|
||||
if( mState == State.FADE )
|
||||
{
|
||||
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||
broadcast( new MetadataReset() );
|
||||
}
|
||||
|
||||
mState = State.IDLE;
|
||||
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
private void processTeardownState()
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
|
||||
broadcast( new MetadataReset() );
|
||||
|
||||
mState = State.TEARDOWN;
|
||||
|
||||
broadcast( Attribute.CHANNEL_STATE );
|
||||
|
||||
if( mTrafficChannelEndListener != null )
|
||||
{
|
||||
mTrafficChannelEndListener.callEnd( mTrafficChannelCallEvent.getChannel() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the call event to the registered listener
|
||||
*/
|
||||
protected void broadcast( CallEvent event )
|
||||
{
|
||||
if( mCallEventListener != null )
|
||||
{
|
||||
mCallEventListener.receive( event );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the channel state attribute change event to all registered
|
||||
* listeners
|
||||
*/
|
||||
protected void broadcast( Attribute attribute )
|
||||
{
|
||||
if( mChangedAttributeListener != null )
|
||||
{
|
||||
mChangedAttributeListener.receive( attribute );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the listener to receive channel state attribute change events
|
||||
*/
|
||||
@Override
|
||||
public void setChangedAttributeListener( Listener<Attribute> listener )
|
||||
{
|
||||
mChangedAttributeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener to receive channel state attribute change events
|
||||
*/
|
||||
@Override
|
||||
public void removeChangedAttributeListener()
|
||||
{
|
||||
mChangedAttributeListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a channel state event to any registered listeners
|
||||
*/
|
||||
protected void broadcast( DecoderStateEvent event )
|
||||
{
|
||||
if( mDecoderStateListener != null )
|
||||
{
|
||||
mDecoderStateListener.receive( event );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void setDecoderStateListener( Listener<DecoderStateEvent> listener )
|
||||
{
|
||||
mDecoderStateListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeDecoderStateListener()
|
||||
{
|
||||
mDecoderStateListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<DecoderStateEvent> getDecoderStateListener()
|
||||
{
|
||||
return mDecoderStateEventReceiver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts metadata to the registered listener
|
||||
*/
|
||||
protected void broadcast( Metadata metadata )
|
||||
{
|
||||
if( mMetadataListener != null )
|
||||
{
|
||||
mMetadataListener.receive( metadata );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a metadata listener
|
||||
*/
|
||||
@Override
|
||||
public void setMetadataListener( Listener<Metadata> listener )
|
||||
{
|
||||
mMetadataListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeMetadataListener()
|
||||
{
|
||||
mMetadataListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to be notified when a traffic channel call event is
|
||||
* completed, so that the listener can perform call tear-down
|
||||
*/
|
||||
public void configureAsTrafficChannel( TrafficChannelManager manager, CallEvent callEvent )
|
||||
{
|
||||
mTrafficChannelEndListener = manager;
|
||||
|
||||
mTrafficChannelCallEvent = callEvent;
|
||||
|
||||
/* Broadcast the call event details as metadata for the audio manager */
|
||||
String channel = mTrafficChannelCallEvent.getChannel();
|
||||
|
||||
if( channel != null )
|
||||
{
|
||||
broadcast( new Metadata( MetadataType.CHANNEL_NUMBER, channel, true ) );
|
||||
}
|
||||
|
||||
broadcast( new Metadata( MetadataType.PROTOCOL,
|
||||
mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) );
|
||||
|
||||
String details = mTrafficChannelCallEvent.getDetails();
|
||||
|
||||
if( details != null )
|
||||
{
|
||||
broadcast( new Metadata( MetadataType.DETAILS, details, true ) );
|
||||
}
|
||||
|
||||
String from = mTrafficChannelCallEvent.getFromID();
|
||||
|
||||
if( from != null )
|
||||
{
|
||||
broadcast( new Metadata( mTrafficChannelCallEvent.getFromIDType(),
|
||||
from, mTrafficChannelCallEvent.getFromIDAlias(), true ) );
|
||||
}
|
||||
|
||||
String to = mTrafficChannelCallEvent.getToID();
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
broadcast( new Metadata( mTrafficChannelCallEvent.getToIDType(), to,
|
||||
mTrafficChannelCallEvent.getToIDAlias(), true ) );
|
||||
}
|
||||
|
||||
long frequency = mTrafficChannelCallEvent.getFrequency();
|
||||
|
||||
if( frequency > 0 )
|
||||
{
|
||||
broadcast( new Metadata( MetadataType.FREQUENCY, String.valueOf( frequency ), true ) );
|
||||
}
|
||||
|
||||
/* Rebroadcast the allocation event so that the internal decoder states
|
||||
* can self-configure with the call event details */
|
||||
broadcast( mTrafficChannelCallEvent );
|
||||
}
|
||||
|
||||
/**
|
||||
* DecoderStateEvent receiver wrapper
|
||||
*/
|
||||
public class DecoderStateEventReceiver implements Listener<DecoderStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive( DecoderStateEvent event )
|
||||
{
|
||||
if( event.getSource() != this )
|
||||
{
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case ALWAYS_UNSQUELCH:
|
||||
broadcast( SquelchState.UNSQUELCH );
|
||||
mSquelchLocked = true;
|
||||
break;
|
||||
case CHANGE_CALL_TIMEOUT:
|
||||
if( event instanceof ChangeChannelTimeoutEvent )
|
||||
{
|
||||
ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event;
|
||||
|
||||
if( timeout.getChannelType() == ChannelType.STANDARD )
|
||||
{
|
||||
setStandardChannelTimeout( timeout.getCallTimeout() );
|
||||
}
|
||||
else
|
||||
{
|
||||
setTrafficChannelTimeout( timeout.getCallTimeout() );
|
||||
}
|
||||
}
|
||||
case CONTINUATION:
|
||||
case DECODE:
|
||||
case START:
|
||||
if( State.CALL_STATES.contains( event.getState() ) )
|
||||
{
|
||||
setState( event.getState() );
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
setState( State.TEARDOWN );
|
||||
}
|
||||
else
|
||||
{
|
||||
setState( State.FADE );
|
||||
}
|
||||
break;
|
||||
case RESET:
|
||||
/* Channel State does not respond to reset events */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors decoder state events to automatically transition the channel
|
||||
* state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
|
||||
* decoding stops or the monitored channel returns to a no signal state.
|
||||
*
|
||||
* Provides a FADE transition state to allow for momentary decoding dropouts
|
||||
* and to allow the user access to call details for a fade period upon
|
||||
* call end.
|
||||
*/
|
||||
public class StateMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized( mState )
|
||||
{
|
||||
if( State.CALL_STATES.contains( mState ) &&
|
||||
mFadeTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
processFadeState();
|
||||
}
|
||||
else if( mState == State.FADE &&
|
||||
mEndTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
processTeardownState();
|
||||
}
|
||||
else
|
||||
{
|
||||
processIdleState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Exception thrown while state monitor is running " +
|
||||
"- state [" + getState() +
|
||||
"] current [" + System.currentTimeMillis() +
|
||||
"] mResetTimeout [" + mEndTimeout +
|
||||
"] mFadeTimeout [" + mFadeTimeout +
|
||||
"]", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/channel/state/ChannelStateSelectionListener.java
Normal file
24
src/channel/state/ChannelStateSelectionListener.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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 channel.state;
|
||||
|
||||
|
||||
public interface ChannelStateSelectionListener
|
||||
{
|
||||
public void selected( ChannelState channelState );
|
||||
}
|
||||
163
src/channel/state/DecoderPanel.java
Normal file
163
src/channel/state/DecoderPanel.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.state;
|
||||
|
||||
import alias.Alias;
|
||||
import channel.metadata.Attribute;
|
||||
import icon.IconManager;
|
||||
import sample.Listener;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingChangeListener;
|
||||
import settings.SettingsManager;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public abstract class DecoderPanel extends JPanel
|
||||
implements Listener<Attribute>, SettingChangeListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected Font mFontDetails = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
protected Font mFontDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||
protected Font mFontAuxDecoder = new Font(Font.MONOSPACED, Font.PLAIN, 10);
|
||||
public static final int ICON_SIZE = 12;
|
||||
|
||||
protected Color mColorLabelDetails;
|
||||
protected Color mColorLabelDecoder;
|
||||
protected Color mColorLabelAuxDecoder;
|
||||
|
||||
protected IconManager mIconManager;
|
||||
protected SettingsManager mSettingsManager;
|
||||
protected DecoderState mDecoderState;
|
||||
|
||||
public DecoderPanel(IconManager iconManager, SettingsManager settingsManager, DecoderState decoderState)
|
||||
{
|
||||
mIconManager = iconManager;
|
||||
mSettingsManager = settingsManager;
|
||||
mSettingsManager.addListener(this);
|
||||
|
||||
mDecoderState = decoderState;
|
||||
|
||||
mDecoderState.setChangedAttributeListener(this);
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mSettingsManager.removeListener(this);
|
||||
mSettingsManager = null;
|
||||
|
||||
mDecoderState.removeChangedAttributeListener();
|
||||
mDecoderState = null;
|
||||
}
|
||||
|
||||
public DecoderState getDecoderState()
|
||||
{
|
||||
return mDecoderState;
|
||||
}
|
||||
|
||||
protected void init()
|
||||
{
|
||||
getColors();
|
||||
|
||||
setOpaque(false); //Use the parent panel's background color
|
||||
}
|
||||
|
||||
private void getColors()
|
||||
{
|
||||
mColorLabelDetails = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS).getColor();
|
||||
mColorLabelDecoder = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_DECODER).getColor();
|
||||
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER).getColor();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void settingChanged(Setting setting)
|
||||
{
|
||||
if(setting instanceof ColorSetting)
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting) setting;
|
||||
|
||||
switch(colorSetting.getColorSettingName())
|
||||
{
|
||||
case CHANNEL_STATE_LABEL_AUX_DECODER:
|
||||
mColorLabelAuxDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DECODER:
|
||||
mColorLabelDecoder = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
mColorLabelDetails = colorSetting.getColor();
|
||||
repaint();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted(Setting setting)
|
||||
{
|
||||
}
|
||||
|
||||
protected void setAliasLabel(final JLabel label, final Alias alias)
|
||||
{
|
||||
setAliasLabel(label, alias, true);
|
||||
}
|
||||
|
||||
protected void setAliasLabel(final JLabel label, final Alias alias, boolean includeIcon)
|
||||
{
|
||||
if(alias != null)
|
||||
{
|
||||
label.setText(alias.getName());
|
||||
|
||||
if(includeIcon)
|
||||
{
|
||||
String iconName = alias.getIconName();
|
||||
|
||||
if(iconName != null)
|
||||
{
|
||||
label.setIcon(mIconManager.getIcon(iconName, IconManager.DEFAULT_ICON_SIZE));
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setIcon(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setIcon(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
label.setText("");
|
||||
label.setIcon(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
228
src/channel/state/DecoderState.java
Normal file
228
src/channel/state/DecoderState.java
Normal file
@@ -0,0 +1,228 @@
|
||||
package channel.state;
|
||||
|
||||
import channel.metadata.Attribute;
|
||||
import message.IMessageListener;
|
||||
import message.Message;
|
||||
import module.Module;
|
||||
import module.decode.DecoderType;
|
||||
import module.decode.event.ActivitySummaryProvider;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.ICallEventProvider;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import audio.metadata.IMetadataProvider;
|
||||
import audio.metadata.Metadata;
|
||||
|
||||
/**
|
||||
* Channel state monitors the stream of decoded messages produced by the
|
||||
* decoder and broadcasts call events as they occur within the decoded message activity.
|
||||
*
|
||||
* Provides access to a textual activity summary of events observed.
|
||||
*/
|
||||
public abstract class DecoderState extends Module
|
||||
implements ActivitySummaryProvider,
|
||||
Listener<Message>,
|
||||
ICallEventProvider,
|
||||
IChangedAttributeProvider,
|
||||
IDecoderStateEventListener,
|
||||
IDecoderStateEventProvider,
|
||||
IMessageListener,
|
||||
IMetadataProvider
|
||||
{
|
||||
/* This has to be a broadcaster in order for references to persist */
|
||||
private Broadcaster<CallEvent> mCallEventBroadcaster = new Broadcaster<>();
|
||||
private Listener<Attribute> mChangedAttributeListener;
|
||||
private Listener<DecoderStateEvent> mDecoderStateListener;
|
||||
private Listener<Metadata> mMetadataListener;
|
||||
|
||||
private DecoderStateEventListener mDecoderStateEventListener =
|
||||
new DecoderStateEventListener();
|
||||
|
||||
protected CallEvent mCurrentCallEvent;
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public DecoderState( AliasList aliasList )
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
}
|
||||
|
||||
public abstract DecoderType getDecoderType();
|
||||
|
||||
public AliasList getAliasList()
|
||||
{
|
||||
return mAliasList;
|
||||
}
|
||||
|
||||
public boolean hasAliasList()
|
||||
{
|
||||
return mAliasList != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the decoder state to prepare for processing a different sample
|
||||
* source
|
||||
*/
|
||||
public abstract void reset();
|
||||
|
||||
/**
|
||||
* Allow the decoder to perform any setup actions
|
||||
*/
|
||||
public abstract void init();
|
||||
|
||||
/**
|
||||
* Implements the IDecoderStateEventListener interface to receive state
|
||||
* reset events.
|
||||
*/
|
||||
public abstract void receiveDecoderStateEvent( DecoderStateEvent event );
|
||||
|
||||
/**
|
||||
* Disposes any resources or pointers held by this instance to prepare for
|
||||
* garbage collection
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mCallEventBroadcaster.dispose();
|
||||
mCallEventBroadcaster = null;
|
||||
mChangedAttributeListener = null;
|
||||
mDecoderStateListener = null;
|
||||
mMetadataListener = null;
|
||||
|
||||
mAliasList = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity Summary - textual summary of activity observed by the channel state.
|
||||
*/
|
||||
public abstract String getActivitySummary();
|
||||
|
||||
/**
|
||||
* Broadcasts a call event to any registered listeners
|
||||
*/
|
||||
protected void broadcast( CallEvent event )
|
||||
{
|
||||
mCallEventBroadcaster.broadcast( event );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a call event listener
|
||||
*/
|
||||
@Override
|
||||
public void addCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the call event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<DecoderStateEvent> getDecoderStateListener()
|
||||
{
|
||||
return mDecoderStateEventListener;
|
||||
}
|
||||
|
||||
private class DecoderStateEventListener implements Listener<DecoderStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive( DecoderStateEvent event )
|
||||
{
|
||||
receiveDecoderStateEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the channel state attribute change event to all registered
|
||||
* listeners
|
||||
*/
|
||||
protected void broadcast( Attribute attribute )
|
||||
{
|
||||
if( mChangedAttributeListener != null )
|
||||
{
|
||||
mChangedAttributeListener.receive( attribute );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the listener to receive channel state attribute change events
|
||||
*/
|
||||
@Override
|
||||
public void setChangedAttributeListener( Listener<Attribute> listener )
|
||||
{
|
||||
mChangedAttributeListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener to receive channel state attribute change events
|
||||
*/
|
||||
@Override
|
||||
public void removeChangedAttributeListener()
|
||||
{
|
||||
mChangedAttributeListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a channel state event to any registered listeners
|
||||
*/
|
||||
protected void broadcast( DecoderStateEvent event )
|
||||
{
|
||||
if( mDecoderStateListener != null )
|
||||
{
|
||||
mDecoderStateListener.receive( event );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void setDecoderStateListener( Listener<DecoderStateEvent> listener )
|
||||
{
|
||||
mDecoderStateListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the decoder state event listener
|
||||
*/
|
||||
@Override
|
||||
public void removeDecoderStateListener()
|
||||
{
|
||||
mDecoderStateListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts metadata to a registered listener
|
||||
*/
|
||||
protected void broadcast( Metadata metadata )
|
||||
{
|
||||
if( mMetadataListener != null )
|
||||
{
|
||||
mMetadataListener.receive( metadata );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMetadataListener( Listener<Metadata> listener )
|
||||
{
|
||||
mMetadataListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMetadataListener()
|
||||
{
|
||||
mMetadataListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<Message> getMessageListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
68
src/channel/state/DecoderStateEvent.java
Normal file
68
src/channel/state/DecoderStateEvent.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package channel.state;
|
||||
|
||||
public class DecoderStateEvent
|
||||
{
|
||||
private Object mSource;
|
||||
private Event mEvent;
|
||||
private State mState;
|
||||
private long mFrequency;
|
||||
|
||||
public DecoderStateEvent( Object source, Event event, State state )
|
||||
{
|
||||
mSource = source;
|
||||
mEvent = event;
|
||||
mState = state;
|
||||
}
|
||||
|
||||
public DecoderStateEvent( Object source, Event event, State state, long frequency )
|
||||
{
|
||||
this( source, event, state );
|
||||
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Decoder State Event - source[" + mSource.getClass() +
|
||||
"] event[" + mEvent.toString() +
|
||||
"] state[" + mState.toString() +
|
||||
"] frequency [" + mFrequency + "]" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public Object getSource()
|
||||
{
|
||||
return mSource;
|
||||
}
|
||||
|
||||
public Event getEvent()
|
||||
{
|
||||
return mEvent;
|
||||
}
|
||||
|
||||
public State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
public long getFrequency()
|
||||
{
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
public enum Event
|
||||
{
|
||||
ALWAYS_UNSQUELCH,
|
||||
CHANGE_CALL_TIMEOUT,
|
||||
CONTINUATION,
|
||||
DECODE,
|
||||
END,
|
||||
RESET,
|
||||
SOURCE_FREQUENCY,
|
||||
START,
|
||||
TRAFFIC_CHANNEL_ALLOCATION;
|
||||
}
|
||||
}
|
||||
10
src/channel/state/IChangedAttributeProvider.java
Normal file
10
src/channel/state/IChangedAttributeProvider.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package channel.state;
|
||||
|
||||
import channel.metadata.Attribute;
|
||||
import sample.Listener;
|
||||
|
||||
public interface IChangedAttributeProvider
|
||||
{
|
||||
public void setChangedAttributeListener( Listener<Attribute> listener );
|
||||
public void removeChangedAttributeListener();
|
||||
}
|
||||
8
src/channel/state/IDecoderStateEventListener.java
Normal file
8
src/channel/state/IDecoderStateEventListener.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package channel.state;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IDecoderStateEventListener
|
||||
{
|
||||
public Listener<DecoderStateEvent> getDecoderStateListener();
|
||||
}
|
||||
9
src/channel/state/IDecoderStateEventProvider.java
Normal file
9
src/channel/state/IDecoderStateEventProvider.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package channel.state;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public interface IDecoderStateEventProvider
|
||||
{
|
||||
public void setDecoderStateListener( Listener<DecoderStateEvent> listener );
|
||||
public void removeDecoderStateListener();
|
||||
}
|
||||
120
src/channel/state/State.java
Normal file
120
src/channel/state/State.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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 channel.state;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Details the set of states for a channel and the allowable transition states
|
||||
*/
|
||||
public enum State
|
||||
{
|
||||
IDLE( "IDLE" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != TEARDOWN &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
CALL( "CALL" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
DATA( "DATA" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
ENCRYPTED( "ENCRYPTED" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == FADE || state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
CONTROL( "CONTROL" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == IDLE ||
|
||||
state == FADE;
|
||||
}
|
||||
},
|
||||
FADE( "FADE" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != FADE &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
RESET( "RESET" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == IDLE;
|
||||
}
|
||||
},
|
||||
|
||||
TEARDOWN( "TEARDOWN" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == RESET;
|
||||
}
|
||||
};
|
||||
|
||||
private String mDisplayValue;
|
||||
|
||||
public static final EnumSet<State> CALL_STATES = EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED );
|
||||
public static final EnumSet<State> IDLE_STATES = EnumSet.of( IDLE, FADE );
|
||||
|
||||
private State( String displayValue )
|
||||
{
|
||||
mDisplayValue = displayValue;
|
||||
}
|
||||
|
||||
public abstract boolean canChangeTo( State state );
|
||||
|
||||
public String getDisplayValue()
|
||||
{
|
||||
return mDisplayValue;
|
||||
}
|
||||
}
|
||||
45
src/channel/traffic/TrafficChannelAllocationEvent.java
Normal file
45
src/channel/traffic/TrafficChannelAllocationEvent.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.traffic;
|
||||
|
||||
import channel.state.DecoderStateEvent;
|
||||
import channel.state.State;
|
||||
import module.decode.event.CallEvent;
|
||||
|
||||
/**
|
||||
* Traffic channel allocation event wraps a call event indicating the channel
|
||||
* and frequency for a traffic channel allocation event, so that the traffic
|
||||
* channel manager can respond and allocate a decoder channel and source if
|
||||
* available, or change the wrapped call event to a call detect event and log it.
|
||||
*/
|
||||
public class TrafficChannelAllocationEvent extends DecoderStateEvent
|
||||
{
|
||||
private CallEvent mCallEvent;
|
||||
|
||||
public TrafficChannelAllocationEvent( Object source, CallEvent callEvent )
|
||||
{
|
||||
super( source, Event.TRAFFIC_CHANNEL_ALLOCATION, State.CALL );
|
||||
mCallEvent = callEvent;
|
||||
}
|
||||
|
||||
public CallEvent getCallEvent()
|
||||
{
|
||||
return mCallEvent;
|
||||
}
|
||||
}
|
||||
477
src/channel/traffic/TrafficChannelManager.java
Normal file
477
src/channel/traffic/TrafficChannelManager.java
Normal file
@@ -0,0 +1,477 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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 channel.traffic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import channel.state.DecoderStateEvent;
|
||||
import channel.state.IDecoderStateEventListener;
|
||||
import module.Module;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.CallEvent.CallEventType;
|
||||
import module.decode.event.ICallEventProvider;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import record.config.RecordConfiguration;
|
||||
import sample.Listener;
|
||||
import source.config.SourceConfigTuner;
|
||||
import source.tuner.TunerChannel;
|
||||
import source.tuner.TunerChannel.Type;
|
||||
import alias.Alias;
|
||||
import alias.id.priority.Priority;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ChannelEvent;
|
||||
import controller.channel.ChannelEvent.Event;
|
||||
import controller.channel.ChannelModel;
|
||||
import controller.channel.ChannelProcessingManager;
|
||||
import controller.channel.TrafficChannelEvent;
|
||||
|
||||
public class TrafficChannelManager extends Module
|
||||
implements ICallEventProvider, IDecoderStateEventListener
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( TrafficChannelManager.class );
|
||||
public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED";
|
||||
public static final String NO_TUNER_AVAILABLE = "NO TUNER AVAILABLE";
|
||||
public static final String UNKNOWN_FREQUENCY = "UNKNOWN FREQUENCY";
|
||||
|
||||
private int mTrafficChannelPoolMaximumSize =
|
||||
DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT;
|
||||
private List<Channel> mTrafficChannelPool = new ArrayList<Channel>();
|
||||
private Map<String,Channel> mTrafficChannelsInUse = new ConcurrentHashMap<String,Channel>();
|
||||
|
||||
private DecoderStateEventListener mEventListener = new DecoderStateEventListener();
|
||||
private Listener<CallEvent> mCallEventListener;
|
||||
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private DecodeConfiguration mDecodeConfiguration;
|
||||
private RecordConfiguration mRecordConfiguration;
|
||||
private String mSystem;
|
||||
private String mSite;
|
||||
private String mAliasListName;
|
||||
private CallEvent mPreviousDoNotMonitorCallEvent;
|
||||
|
||||
/**
|
||||
* Monitors call events and allocates traffic decoder channels in response
|
||||
* to traffic channel allocation call events. Manages a pool of reusable
|
||||
* traffic channel allocations.
|
||||
*
|
||||
* @param channelModel containing channels currently in use
|
||||
*
|
||||
* @param channelProcessingManager
|
||||
*
|
||||
* @param decodeConfiguration - decoder configuration to use for each
|
||||
* traffic channel allocation.
|
||||
*
|
||||
* @param recordConfiguration - recording options for each traffic channel
|
||||
*
|
||||
* @param system label to use to describe the system
|
||||
*
|
||||
* @param site label to use to describe the site
|
||||
*
|
||||
* @param aliasListName designated for the channel
|
||||
*
|
||||
* @param trafficChannelTimeout - millisecond call timer limit used when an
|
||||
* end of call signalling event is not decoded.
|
||||
*
|
||||
* @param trafficChannelPoolSize - maximum number of allocated traffic channels
|
||||
* in the pool
|
||||
*/
|
||||
public TrafficChannelManager( ChannelModel channelModel,
|
||||
ChannelProcessingManager channelProcessingManager,
|
||||
DecodeConfiguration decodeConfiguration,
|
||||
RecordConfiguration recordConfiguration,
|
||||
String system,
|
||||
String site,
|
||||
String aliasListName,
|
||||
long trafficChannelTimeout,
|
||||
int trafficChannelPoolSize )
|
||||
{
|
||||
mChannelModel = channelModel;
|
||||
mChannelProcessingManager = channelProcessingManager;
|
||||
mDecodeConfiguration = decodeConfiguration;
|
||||
mRecordConfiguration = recordConfiguration;
|
||||
mSystem = system;
|
||||
mSite = site;
|
||||
mAliasListName = aliasListName;
|
||||
mTrafficChannelPoolMaximumSize = trafficChannelPoolSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
for( Channel trafficChannel: mTrafficChannelPool )
|
||||
{
|
||||
mChannelModel.broadcast( new ChannelEvent( trafficChannel, Event.REQUEST_DISABLE ) );
|
||||
}
|
||||
|
||||
mTrafficChannelPool.clear();
|
||||
|
||||
mTrafficChannelsInUse.clear();
|
||||
|
||||
mCallEventListener = null;
|
||||
mDecodeConfiguration = null;
|
||||
mPreviousDoNotMonitorCallEvent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a channel by either reusing an existing channel or constructing
|
||||
* a new one, limited by the total number of constructed channels allowed.
|
||||
*
|
||||
* Note: you must enforce thread safety on the mTrafficChannelsInUse
|
||||
* external to this method.
|
||||
*
|
||||
* @param channelNumber
|
||||
* @param tunerChannel
|
||||
* @return
|
||||
*/
|
||||
private Channel getChannel( String channelNumber, TunerChannel tunerChannel )
|
||||
{
|
||||
Channel channel = null;
|
||||
|
||||
if( !mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||
{
|
||||
for( Channel configuredChannel: mTrafficChannelPool )
|
||||
{
|
||||
if( !configuredChannel.getEnabled() )
|
||||
{
|
||||
channel = configuredChannel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( channel == null && mTrafficChannelPool.size() < mTrafficChannelPoolMaximumSize )
|
||||
{
|
||||
channel = new Channel( "Traffic", ChannelType.TRAFFIC );
|
||||
|
||||
channel.setDecodeConfiguration( mDecodeConfiguration );
|
||||
|
||||
channel.setRecordConfiguration( mRecordConfiguration );
|
||||
|
||||
channel.setAliasListName( mAliasListName );
|
||||
|
||||
mChannelModel.addChannel( channel );
|
||||
|
||||
mTrafficChannelPool.add( channel );
|
||||
}
|
||||
|
||||
/* If we have a configured channel, update metadata */
|
||||
if( channel != null )
|
||||
{
|
||||
channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) );
|
||||
channel.setSystem( mSystem );
|
||||
channel.setSite( mSite );
|
||||
channel.setName( channelNumber );
|
||||
|
||||
mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_CONFIGURATION_CHANGE ) );
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the event and creates a traffic channel is resources are
|
||||
* available
|
||||
*/
|
||||
private void process( TrafficChannelAllocationEvent event )
|
||||
{
|
||||
CallEvent callEvent = event.getCallEvent();
|
||||
|
||||
/* Check for duplicate events and suppress */
|
||||
synchronized( mTrafficChannelsInUse )
|
||||
{
|
||||
if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long frequency = callEvent.getFrequency();
|
||||
|
||||
/* Check the from/to aliases for do not monitor priority */
|
||||
if( isDoNotMonitor( callEvent ) )
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DO_NOT_MONITOR );
|
||||
|
||||
if( isSameCallEvent( mPreviousDoNotMonitorCallEvent, callEvent ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPreviousDoNotMonitorCallEvent = callEvent;
|
||||
}
|
||||
}
|
||||
else if( frequency > 0 )
|
||||
{
|
||||
Channel channel = getChannel( callEvent.getChannel(), new TunerChannel( Type.TRAFFIC,
|
||||
frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth() ) );
|
||||
|
||||
if( channel != null )
|
||||
{
|
||||
TrafficChannelEvent trafficChannelEvent =
|
||||
new TrafficChannelEvent( this, channel, Event.REQUEST_ENABLE, callEvent );
|
||||
|
||||
//Request to enable the channel
|
||||
mChannelModel.broadcast( trafficChannelEvent );
|
||||
|
||||
if( channel.getEnabled() )
|
||||
{
|
||||
mTrafficChannelsInUse.put( callEvent.getChannel(), channel );
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
|
||||
String details = callEvent.getDetails();
|
||||
|
||||
if(details == null || details.isEmpty())
|
||||
{
|
||||
callEvent.setDetails(CHANNEL_START_REJECTED);
|
||||
}
|
||||
else if(!details.contains(CHANNEL_START_REJECTED))
|
||||
{
|
||||
callEvent.setDetails(new StringBuilder(CHANNEL_START_REJECTED)
|
||||
.append(" : ")
|
||||
.append(callEvent.getDetails()).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
|
||||
String details = callEvent.getDetails();
|
||||
|
||||
if(details == null || details.isEmpty())
|
||||
{
|
||||
callEvent.setDetails(NO_TUNER_AVAILABLE);
|
||||
}
|
||||
else if(!details.contains(NO_TUNER_AVAILABLE))
|
||||
{
|
||||
callEvent.setDetails(new StringBuilder(NO_TUNER_AVAILABLE)
|
||||
.append(" : ")
|
||||
.append(callEvent.getDetails()).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
|
||||
String details = callEvent.getDetails();
|
||||
|
||||
if(details == null || details.isEmpty())
|
||||
{
|
||||
callEvent.setDetails(UNKNOWN_FREQUENCY);
|
||||
}
|
||||
else if(!details.contains(UNKNOWN_FREQUENCY))
|
||||
{
|
||||
callEvent.setDetails(new StringBuilder(UNKNOWN_FREQUENCY)
|
||||
.append(" : ")
|
||||
.append(callEvent.getDetails()).toString());
|
||||
}
|
||||
}
|
||||
|
||||
final Listener<CallEvent> listener = mCallEventListener;
|
||||
|
||||
if( listener != null )
|
||||
{
|
||||
listener.receive( callEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the call type, channel and to fields for equivalence and the
|
||||
* from field for either both null, or equivalence.
|
||||
*
|
||||
* @param e1
|
||||
* @param e2
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSameCallEvent( CallEvent e1, CallEvent e2 )
|
||||
{
|
||||
if( e1 == null || e2 == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( e1.getCallEventType() != e2.getCallEventType() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( e1.getChannel() == null ||
|
||||
e2.getChannel() == null ||
|
||||
!e1.getChannel().contentEquals( e2.getChannel() ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( e1.getToID() == null ||
|
||||
e2.getToID() == null ||
|
||||
!e1.getToID().contentEquals( e2.getToID() ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( e1.getFromID() == null || e2.getFromID() == null )
|
||||
{
|
||||
if( e1.getFromID() == null && e2.getFromID() == null )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( e1.getFromID().contentEquals( e2.getFromID() ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for aliases that contain a do not follow/process priority.
|
||||
*
|
||||
* @param event
|
||||
* @return - true if there is an alias that has a do not process alias id
|
||||
*/
|
||||
private boolean isDoNotMonitor( CallEvent event )
|
||||
{
|
||||
Alias to = event.getToIDAlias();
|
||||
|
||||
if( to != null &&
|
||||
to.hasCallPriority() &&
|
||||
to.getCallPriority() == Priority.DO_NOT_MONITOR )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Alias from = event.getFromIDAlias();
|
||||
|
||||
if( from != null &&
|
||||
from.hasCallPriority() &&
|
||||
from.getCallPriority() == Priority.DO_NOT_MONITOR )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<DecoderStateEvent> getDecoderStateListener()
|
||||
{
|
||||
return mEventListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCallEventListener( Listener<CallEvent> listener )
|
||||
{
|
||||
mCallEventListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start( ScheduledExecutorService executor )
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if( !mTrafficChannelsInUse.isEmpty() )
|
||||
{
|
||||
List<String> channels = new ArrayList<>();
|
||||
|
||||
/* Copy the keyset so we don't get concurrent modification of the map */
|
||||
channels.addAll( mTrafficChannelsInUse.keySet() );
|
||||
|
||||
for( String channel: channels )
|
||||
{
|
||||
callEnd( channel );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used by the TrafficChannelStatusListener class to signal the
|
||||
* end of a an allocated traffic channel call event
|
||||
*
|
||||
* @param channelNumber - channel number from the call event that signaled
|
||||
* the start of a traffic channel allocation
|
||||
*/
|
||||
public void callEnd( String channelNumber )
|
||||
{
|
||||
synchronized( mTrafficChannelsInUse )
|
||||
{
|
||||
if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||
{
|
||||
Channel channel = mTrafficChannelsInUse.get( channelNumber );
|
||||
|
||||
mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DISABLE ) );
|
||||
|
||||
mTrafficChannelsInUse.remove( channelNumber );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper class for the decoder state event listener interface to catch
|
||||
* traffic channel allocation requests
|
||||
*/
|
||||
public class DecoderStateEventListener implements Listener<DecoderStateEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive( DecoderStateEvent event )
|
||||
{
|
||||
switch( event.getEvent() )
|
||||
{
|
||||
case TRAFFIC_CHANNEL_ALLOCATION:
|
||||
process( (TrafficChannelAllocationEvent)event );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user