From 7115b4feb1d3f954f528dfe6a523caccbb78f32e Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 14 Jan 2017 11:21:53 -0500 Subject: [PATCH 01/10] #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. --- src/alias/Alias.java | 4 +- src/alias/AliasNameEditor.java | 4 +- src/alias/PatchGroupAlias.java | 11 + src/channel/metadata/AliasedIdentifier.java | 91 ++++ src/channel/metadata/Attribute.java | 62 +++ .../metadata/AttributeChangeRequest.java | 111 +++++ .../metadata/ChannelMetadataModel.java | 139 ++++++ .../metadata/ChannelMetadataPanel.java | 237 +++++++++ .../metadata/ChannelMetadataViewer.java | 61 +++ src/channel/metadata/Metadata.java | 463 ++++++++++++++++++ src/channel/metadata/MetadataChangeEvent.java | 53 ++ .../state/AlwaysUnsquelchedDecoderState.java | 4 +- .../state/ChangeChannelTimeoutEvent.java | 2 +- .../decode => channel}/state/ChannelList.java | 2 +- .../state/ChannelPanel.java | 9 +- .../state/ChannelState.java | 26 +- .../state/ChannelStateSelectionListener.java | 2 +- .../state/DecoderPanel.java | 5 +- .../state/DecoderState.java | 9 +- .../state/DecoderStateEvent.java | 2 +- .../state/IChangedAttributeProvider.java | 10 + .../state/IDecoderStateEventListener.java | 2 +- .../state/IDecoderStateEventProvider.java | 2 +- .../decode => channel}/state/State.java | 2 +- .../TrafficChannelAllocationEvent.java | 45 ++ .../traffic}/TrafficChannelManager.java | 22 +- src/controller/ControllerPanel.java | 2 +- src/controller/channel/ChannelUtils.java | 2 +- .../channel/TrafficChannelEvent.java | 2 +- src/map/MapService.java | 3 +- src/module/ProcessingChain.java | 14 +- src/module/decode/DecoderFactory.java | 8 +- src/module/decode/am/AMDecoderPanel.java | 8 +- .../fleetsync2/Fleetsync2DecoderPanel.java | 8 +- .../fleetsync2/Fleetsync2DecoderState.java | 22 +- .../decode/lj1200/LJ1200DecoderPanel.java | 8 +- .../decode/lj1200/LJ1200DecoderState.java | 18 +- .../decode/ltrnet/LTRNetDecoderPanel.java | 8 +- .../decode/ltrnet/LTRNetDecoderState.java | 44 +- .../ltrstandard/LTRStandardDecoderPanel.java | 6 +- .../ltrstandard/LTRStandardDecoderState.java | 24 +- .../decode/mdc1200/MDCDecoderPanel.java | 8 +- .../decode/mdc1200/MDCDecoderState.java | 22 +- .../decode/mpt1327/MPT1327DecoderPanel.java | 8 +- .../decode/mpt1327/MPT1327DecoderState.java | 34 +- .../decode/mpt1327/MPT1327TrafficPanel.java | 8 +- src/module/decode/nbfm/NBFMDecoderPanel.java | 8 +- src/module/decode/p25/P25DecoderPanel.java | 8 +- src/module/decode/p25/P25DecoderState.java | 50 +- .../decode/passport/PassportDecoderPanel.java | 8 +- .../decode/passport/PassportDecoderState.java | 20 +- src/module/decode/state/ChangedAttribute.java | 29 -- .../state/IChangedAttributeListener.java | 8 - .../state/IChangedAttributeProvider.java | 9 - .../state/TrafficChannelAllocationEvent.java | 25 - .../decode/tait/Tait1200DecoderPanel.java | 8 +- .../decode/tait/Tait1200DecoderState.java | 22 +- 57 files changed, 1534 insertions(+), 298 deletions(-) create mode 100644 src/channel/metadata/AliasedIdentifier.java create mode 100644 src/channel/metadata/Attribute.java create mode 100644 src/channel/metadata/AttributeChangeRequest.java create mode 100644 src/channel/metadata/ChannelMetadataModel.java create mode 100644 src/channel/metadata/ChannelMetadataPanel.java create mode 100644 src/channel/metadata/ChannelMetadataViewer.java create mode 100644 src/channel/metadata/Metadata.java create mode 100644 src/channel/metadata/MetadataChangeEvent.java rename src/{module/decode => channel}/state/AlwaysUnsquelchedDecoderState.java (96%) rename src/{module/decode => channel}/state/ChangeChannelTimeoutEvent.java (95%) rename src/{module/decode => channel}/state/ChannelList.java (99%) rename src/{module/decode => channel}/state/ChannelPanel.java (98%) rename src/{module/decode => channel}/state/ChannelState.java (96%) rename src/{module/decode => channel}/state/ChannelStateSelectionListener.java (97%) rename src/{module/decode => channel}/state/DecoderPanel.java (97%) rename src/{module/decode => channel}/state/DecoderState.java (95%) rename src/{module/decode => channel}/state/DecoderStateEvent.java (97%) create mode 100644 src/channel/state/IChangedAttributeProvider.java rename src/{module/decode => channel}/state/IDecoderStateEventListener.java (82%) rename src/{module/decode => channel}/state/IDecoderStateEventProvider.java (87%) rename src/{module/decode => channel}/state/State.java (99%) create mode 100644 src/channel/traffic/TrafficChannelAllocationEvent.java rename src/{module/decode/state => channel/traffic}/TrafficChannelManager.java (92%) delete mode 100644 src/module/decode/state/ChangedAttribute.java delete mode 100644 src/module/decode/state/IChangedAttributeListener.java delete mode 100644 src/module/decode/state/IChangedAttributeProvider.java delete mode 100644 src/module/decode/state/TrafficChannelAllocationEvent.java diff --git a/src/alias/Alias.java b/src/alias/Alias.java index fd9ca32a..0747e72d 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -117,9 +117,9 @@ public class Alias mColor = color; } - public Color getMapColor() + public Color getDisplayColor() { - return new Color( mColor ); + return new Color( getColor() ); } @XmlAttribute diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 70b5cd66..6c6ee06b 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -148,7 +148,7 @@ public class AliasNameEditor extends Editor Color newColor = JColorChooser.showDialog( AliasNameEditor.this, "Choose color for this alias", - (hasItem() ? getItem().getMapColor() : null)); + (hasItem() ? getItem().getDisplayColor() : null)); if(newColor != null) { @@ -264,7 +264,7 @@ public class AliasNameEditor extends Editor mGroupCombo.setSelectedItem(alias.getGroup()); - Color color = alias.getMapColor(); + Color color = alias.getDisplayColor(); mButtonColor.setBackground(color); mButtonColor.setForeground(color); diff --git a/src/alias/PatchGroupAlias.java b/src/alias/PatchGroupAlias.java index a83a6fd7..2e24f91f 100644 --- a/src/alias/PatchGroupAlias.java +++ b/src/alias/PatchGroupAlias.java @@ -47,6 +47,17 @@ public class PatchGroupAlias extends Alias { } + @Override + public int getColor() + { + if(mPatchGroupAlias != null) + { + return mPatchGroupAlias.getColor(); + } + + return super.getColor(); + } + /** * Sets an alias for this patch group ID */ diff --git a/src/channel/metadata/AliasedIdentifier.java b/src/channel/metadata/AliasedIdentifier.java new file mode 100644 index 00000000..57db3f0d --- /dev/null +++ b/src/channel/metadata/AliasedIdentifier.java @@ -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 + * + ******************************************************************************/ +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; + } +} diff --git a/src/channel/metadata/Attribute.java b/src/channel/metadata/Attribute.java new file mode 100644 index 00000000..d65a4138 --- /dev/null +++ b/src/channel/metadata/Attribute.java @@ -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 + * + ******************************************************************************/ +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; +} \ No newline at end of file diff --git a/src/channel/metadata/AttributeChangeRequest.java b/src/channel/metadata/AttributeChangeRequest.java new file mode 100644 index 00000000..26d7dfc2 --- /dev/null +++ b/src/channel/metadata/AttributeChangeRequest.java @@ -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 + * + ******************************************************************************/ +package channel.metadata; + +import alias.Alias; + +public class AttributeChangeRequest +{ + 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; + } +} diff --git a/src/channel/metadata/ChannelMetadataModel.java b/src/channel/metadata/ChannelMetadataModel.java new file mode 100644 index 00000000..2e6b7390 --- /dev/null +++ b/src/channel/metadata/ChannelMetadataModel.java @@ -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 + * + ******************************************************************************/ +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 +{ + 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 mChannelMetadata = new ArrayList(); + private Map 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; + } + } + } +} diff --git a/src/channel/metadata/ChannelMetadataPanel.java b/src/channel/metadata/ChannelMetadataPanel.java new file mode 100644 index 00000000..e3fa68aa --- /dev/null +++ b/src/channel/metadata/ChannelMetadataPanel.java @@ -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 + * + ******************************************************************************/ +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; + } + } + +} diff --git a/src/channel/metadata/ChannelMetadataViewer.java b/src/channel/metadata/ChannelMetadataViewer.java new file mode 100644 index 00000000..cd02fccf --- /dev/null +++ b/src/channel/metadata/ChannelMetadataViewer.java @@ -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 + * + ******************************************************************************/ +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); + } +} diff --git a/src/channel/metadata/Metadata.java b/src/channel/metadata/Metadata.java new file mode 100644 index 00000000..6540fe80 --- /dev/null +++ b/src/channel/metadata/Metadata.java @@ -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 + * + ******************************************************************************/ +package channel.metadata; + +import alias.Alias; +import channel.state.State; +import sample.Broadcaster; +import sample.Listener; + +public class Metadata implements Listener +{ + 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 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 listener) + { + mMetadataChangeEventBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving metadata change events for this channel metadata + */ + public void removeListener(Listener 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; + } +} diff --git a/src/channel/metadata/MetadataChangeEvent.java b/src/channel/metadata/MetadataChangeEvent.java new file mode 100644 index 00000000..123adb08 --- /dev/null +++ b/src/channel/metadata/MetadataChangeEvent.java @@ -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 + * + ******************************************************************************/ +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; + } +} diff --git a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java b/src/channel/state/AlwaysUnsquelchedDecoderState.java similarity index 96% rename from src/module/decode/state/AlwaysUnsquelchedDecoderState.java rename to src/channel/state/AlwaysUnsquelchedDecoderState.java index 3b97c6bb..3d65f1ed 100644 --- a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java +++ b/src/channel/state/AlwaysUnsquelchedDecoderState.java @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package module.decode.state; +package channel.state; import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.state.DecoderStateEvent.Event; +import channel.state.DecoderStateEvent.Event; import audio.metadata.Metadata; import audio.metadata.MetadataType; diff --git a/src/module/decode/state/ChangeChannelTimeoutEvent.java b/src/channel/state/ChangeChannelTimeoutEvent.java similarity index 95% rename from src/module/decode/state/ChangeChannelTimeoutEvent.java rename to src/channel/state/ChangeChannelTimeoutEvent.java index 9119e4c9..5fed00dc 100644 --- a/src/module/decode/state/ChangeChannelTimeoutEvent.java +++ b/src/channel/state/ChangeChannelTimeoutEvent.java @@ -1,4 +1,4 @@ -package module.decode.state; +package channel.state; import controller.channel.Channel.ChannelType; diff --git a/src/module/decode/state/ChannelList.java b/src/channel/state/ChannelList.java similarity index 99% rename from src/module/decode/state/ChannelList.java rename to src/channel/state/ChannelList.java index d9817009..091201ff 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/channel/state/ChannelList.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package module.decode.state; +package channel.state; import audio.AudioPanel; import controller.channel.Channel; diff --git a/src/module/decode/state/ChannelPanel.java b/src/channel/state/ChannelPanel.java similarity index 98% rename from src/module/decode/state/ChannelPanel.java rename to src/channel/state/ChannelPanel.java index b2a9acdc..40a98113 100644 --- a/src/module/decode/state/ChannelPanel.java +++ b/src/channel/state/ChannelPanel.java @@ -16,8 +16,9 @@ * along with this program. If not, see * ******************************************************************************/ -package module.decode.state; +package channel.state; +import channel.metadata.Attribute; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; @@ -46,7 +47,7 @@ import java.util.ArrayList; import java.util.List; public class ChannelPanel extends JPanel - implements Listener, + implements Listener, SettingChangeListener, ChannelEventListener { @@ -222,14 +223,14 @@ public class ChannelPanel extends JPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @Override public void run() { - switch(changedAttribute) + switch(attribute) { case CHANNEL_STATE: if(mChannelState != null && mChannelState.getState() != null) diff --git a/src/module/decode/state/ChannelState.java b/src/channel/state/ChannelState.java similarity index 96% rename from src/module/decode/state/ChannelState.java rename to src/channel/state/ChannelState.java index 1b5f89d1..1188c3ac 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/channel/state/ChannelState.java @@ -15,18 +15,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package module.decode.state; +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 module.decode.state.DecoderStateEvent.Event; +import channel.state.DecoderStateEvent.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,7 +54,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged private State mState = State.IDLE; private Listener mCallEventListener; - private Listener mChangedAttributeListener; + private Listener mChangedAttributeListener; private Listener mDecoderStateListener; private Listener mMetadataListener; private Listener mSquelchStateListener; @@ -116,7 +118,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged broadcast( new MetadataReset() ); mState = State.IDLE; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); } @Override @@ -312,7 +314,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged broadcast( SquelchState.SQUELCH ); updateFadeTimeout(); mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); } break; case DATA: @@ -320,13 +322,13 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged broadcast( SquelchState.SQUELCH ); updateFadeTimeout(); mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); break; case CALL: broadcast( SquelchState.UNSQUELCH ); updateFadeTimeout(); mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); break; case FADE: processFadeState(); @@ -360,7 +362,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged mState = State.FADE; broadcast( SquelchState.SQUELCH ); - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); } private void processIdleState() @@ -375,7 +377,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged mState = State.IDLE; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); } private void processTeardownState() @@ -386,7 +388,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged mState = State.TEARDOWN; - broadcast( ChangedAttribute.CHANNEL_STATE ); + broadcast( Attribute.CHANNEL_STATE ); if( mTrafficChannelEndListener != null ) { @@ -421,7 +423,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged * Broadcasts the channel state attribute change event to all registered * listeners */ - protected void broadcast( ChangedAttribute attribute ) + protected void broadcast( Attribute attribute ) { if( mChangedAttributeListener != null ) { @@ -433,7 +435,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged * Adds the listener to receive channel state attribute change events */ @Override - public void setChangedAttributeListener( Listener listener ) + public void setChangedAttributeListener( Listener listener ) { mChangedAttributeListener = listener; } diff --git a/src/module/decode/state/ChannelStateSelectionListener.java b/src/channel/state/ChannelStateSelectionListener.java similarity index 97% rename from src/module/decode/state/ChannelStateSelectionListener.java rename to src/channel/state/ChannelStateSelectionListener.java index c0496acf..7ddcdc4a 100644 --- a/src/module/decode/state/ChannelStateSelectionListener.java +++ b/src/channel/state/ChannelStateSelectionListener.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package module.decode.state; +package channel.state; public interface ChannelStateSelectionListener diff --git a/src/module/decode/state/DecoderPanel.java b/src/channel/state/DecoderPanel.java similarity index 97% rename from src/module/decode/state/DecoderPanel.java rename to src/channel/state/DecoderPanel.java index 01ea72e5..2ca2840e 100644 --- a/src/module/decode/state/DecoderPanel.java +++ b/src/channel/state/DecoderPanel.java @@ -16,9 +16,10 @@ * along with this program. If not, see * ******************************************************************************/ -package module.decode.state; +package channel.state; import alias.Alias; +import channel.metadata.Attribute; import icon.IconManager; import sample.Listener; import settings.ColorSetting; @@ -31,7 +32,7 @@ import javax.swing.*; import java.awt.*; public abstract class DecoderPanel extends JPanel - implements Listener, SettingChangeListener + implements Listener, SettingChangeListener { private static final long serialVersionUID = 1L; diff --git a/src/module/decode/state/DecoderState.java b/src/channel/state/DecoderState.java similarity index 95% rename from src/module/decode/state/DecoderState.java rename to src/channel/state/DecoderState.java index 469f4be6..de5b04b9 100644 --- a/src/module/decode/state/DecoderState.java +++ b/src/channel/state/DecoderState.java @@ -1,5 +1,6 @@ -package module.decode.state; +package channel.state; +import channel.metadata.Attribute; import message.IMessageListener; import message.Message; import module.Module; @@ -31,7 +32,7 @@ public abstract class DecoderState extends Module { /* This has to be a broadcaster in order for references to persist */ private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); - private Listener mChangedAttributeListener; + private Listener mChangedAttributeListener; private Listener mDecoderStateListener; private Listener mMetadataListener; @@ -141,7 +142,7 @@ public abstract class DecoderState extends Module * Broadcasts the channel state attribute change event to all registered * listeners */ - protected void broadcast( ChangedAttribute attribute ) + protected void broadcast( Attribute attribute ) { if( mChangedAttributeListener != null ) { @@ -153,7 +154,7 @@ public abstract class DecoderState extends Module * Adds the listener to receive channel state attribute change events */ @Override - public void setChangedAttributeListener( Listener listener ) + public void setChangedAttributeListener( Listener listener ) { mChangedAttributeListener = listener; } diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/channel/state/DecoderStateEvent.java similarity index 97% rename from src/module/decode/state/DecoderStateEvent.java rename to src/channel/state/DecoderStateEvent.java index a4ebd68f..02371c0d 100644 --- a/src/module/decode/state/DecoderStateEvent.java +++ b/src/channel/state/DecoderStateEvent.java @@ -1,4 +1,4 @@ -package module.decode.state; +package channel.state; public class DecoderStateEvent { diff --git a/src/channel/state/IChangedAttributeProvider.java b/src/channel/state/IChangedAttributeProvider.java new file mode 100644 index 00000000..85b7d926 --- /dev/null +++ b/src/channel/state/IChangedAttributeProvider.java @@ -0,0 +1,10 @@ +package channel.state; + +import channel.metadata.Attribute; +import sample.Listener; + +public interface IChangedAttributeProvider +{ + public void setChangedAttributeListener( Listener listener ); + public void removeChangedAttributeListener(); +} diff --git a/src/module/decode/state/IDecoderStateEventListener.java b/src/channel/state/IDecoderStateEventListener.java similarity index 82% rename from src/module/decode/state/IDecoderStateEventListener.java rename to src/channel/state/IDecoderStateEventListener.java index 72b9eb1c..5f1f1866 100644 --- a/src/module/decode/state/IDecoderStateEventListener.java +++ b/src/channel/state/IDecoderStateEventListener.java @@ -1,4 +1,4 @@ -package module.decode.state; +package channel.state; import sample.Listener; diff --git a/src/module/decode/state/IDecoderStateEventProvider.java b/src/channel/state/IDecoderStateEventProvider.java similarity index 87% rename from src/module/decode/state/IDecoderStateEventProvider.java rename to src/channel/state/IDecoderStateEventProvider.java index afd8c2d0..30a954ce 100644 --- a/src/module/decode/state/IDecoderStateEventProvider.java +++ b/src/channel/state/IDecoderStateEventProvider.java @@ -1,4 +1,4 @@ -package module.decode.state; +package channel.state; import sample.Listener; diff --git a/src/module/decode/state/State.java b/src/channel/state/State.java similarity index 99% rename from src/module/decode/state/State.java rename to src/channel/state/State.java index 3c9f5189..83336d2c 100644 --- a/src/module/decode/state/State.java +++ b/src/channel/state/State.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package module.decode.state; +package channel.state; import java.util.EnumSet; diff --git a/src/channel/traffic/TrafficChannelAllocationEvent.java b/src/channel/traffic/TrafficChannelAllocationEvent.java new file mode 100644 index 00000000..87b7b0b9 --- /dev/null +++ b/src/channel/traffic/TrafficChannelAllocationEvent.java @@ -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 + * + ******************************************************************************/ +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; + } +} diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/channel/traffic/TrafficChannelManager.java similarity index 92% rename from src/module/decode/state/TrafficChannelManager.java rename to src/channel/traffic/TrafficChannelManager.java index 37ebc3a7..2d9829c5 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/channel/traffic/TrafficChannelManager.java @@ -1,4 +1,22 @@ -package module.decode.state; +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package channel.traffic; import java.util.ArrayList; import java.util.List; @@ -6,6 +24,8 @@ 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; diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index f41a24a9..fdbf00c8 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -34,7 +34,7 @@ import map.MapPanel; import map.MapService; import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; -import module.decode.state.ChannelList; +import channel.state.ChannelList; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import source.SourceManager; diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java index 6434eb4c..c595dac3 100644 --- a/src/controller/channel/ChannelUtils.java +++ b/src/controller/channel/ChannelUtils.java @@ -11,7 +11,7 @@ import javax.swing.JSeparator; import module.ProcessingChain; import module.decode.event.ActivitySummaryFrame; -import module.decode.state.DecoderState; +import channel.state.DecoderState; import controller.channel.ChannelEvent.Event; public class ChannelUtils diff --git a/src/controller/channel/TrafficChannelEvent.java b/src/controller/channel/TrafficChannelEvent.java index 20c4aade..8679c549 100644 --- a/src/controller/channel/TrafficChannelEvent.java +++ b/src/controller/channel/TrafficChannelEvent.java @@ -1,7 +1,7 @@ package controller.channel; import module.decode.event.CallEvent; -import module.decode.state.TrafficChannelManager; +import channel.traffic.TrafficChannelManager; public class TrafficChannelEvent extends ChannelEvent { diff --git a/src/map/MapService.java b/src/map/MapService.java index 713e46b2..38991881 100644 --- a/src/map/MapService.java +++ b/src/map/MapService.java @@ -23,7 +23,6 @@ import controller.NamingThreadFactory; import icon.IconManager; import message.Message; import sample.Listener; -import settings.SettingsManager; import java.awt.*; import java.util.ArrayList; @@ -102,7 +101,7 @@ public class MapService implements Listener if(alias != null) { - color = alias.getMapColor(); + color = alias.getDisplayColor(); } if(color == null) diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 6d6f91e7..46e45623 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -39,13 +39,13 @@ import module.decode.event.CallEventModel; import module.decode.event.ICallEventListener; import module.decode.event.ICallEventProvider; import module.decode.event.MessageActivityModel; -import module.decode.state.ChannelState; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.IDecoderStateEventListener; -import module.decode.state.IDecoderStateEventProvider; -import module.decode.state.State; +import channel.state.ChannelState; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.IDecoderStateEventListener; +import channel.state.IDecoderStateEventProvider; +import channel.state.State; import module.log.EventLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 6db582f0..0214dee9 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -92,10 +92,10 @@ import module.decode.passport.PassportDecoderEditor; import module.decode.passport.PassportDecoderPanel; import module.decode.passport.PassportDecoderState; import module.decode.passport.PassportMessageFilter; -import module.decode.state.AlwaysUnsquelchedDecoderState; -import module.decode.state.DecoderPanel; -import module.decode.state.DecoderState; -import module.decode.state.TrafficChannelManager; +import channel.state.AlwaysUnsquelchedDecoderState; +import channel.state.DecoderPanel; +import channel.state.DecoderState; +import channel.traffic.TrafficChannelManager; import module.decode.tait.Tait1200Decoder; import module.decode.tait.Tait1200DecoderPanel; import module.decode.tait.Tait1200DecoderState; diff --git a/src/module/decode/am/AMDecoderPanel.java b/src/module/decode/am/AMDecoderPanel.java index 11d4fa25..2e344bca 100644 --- a/src/module/decode/am/AMDecoderPanel.java +++ b/src/module/decode/am/AMDecoderPanel.java @@ -19,9 +19,9 @@ package module.decode.am; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; -import module.decode.state.DecoderState; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; +import channel.state.DecoderState; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -55,7 +55,7 @@ public class AMDecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java index cccd09b6..612d9f6c 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java @@ -19,8 +19,8 @@ package module.decode.fleetsync2; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -116,14 +116,14 @@ public class Fleetsync2DecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @Override public void run() { - switch(changedAttribute) + switch(attribute) { case FROM_TALKGROUP: String from = getDecoderState().getFromID(); diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java index bb5d614d..0f343109 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -23,11 +23,11 @@ import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; import alias.Alias; import alias.AliasList; @@ -182,7 +182,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setFromID( String from ) { mFrom = from; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); } public Alias getFromIDAlias() @@ -193,7 +193,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setFromIDAlias( Alias alias ) { mFromAlias = alias; - broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + broadcast( Attribute.FROM_TALKGROUP_ALIAS ); } public String getToID() @@ -204,7 +204,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setToID( String to ) { mTo = to; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } public Alias getToIDAlias() @@ -215,7 +215,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setToIDAlias( Alias alias ) { mToAlias = alias; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } public String getMessage() @@ -226,7 +226,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setMessage( String message ) { mMessage = message; - broadcast( ChangedAttribute.MESSAGE ); + broadcast( Attribute.MESSAGE ); } public String getMessageType() @@ -237,7 +237,7 @@ public class Fleetsync2DecoderState extends DecoderState public void setMessageType( String type ) { mMessageType = type; - broadcast( ChangedAttribute.MESSAGE_TYPE ); + broadcast( Attribute.MESSAGE_TYPE ); } /** diff --git a/src/module/decode/lj1200/LJ1200DecoderPanel.java b/src/module/decode/lj1200/LJ1200DecoderPanel.java index 31ab0276..51143cdc 100644 --- a/src/module/decode/lj1200/LJ1200DecoderPanel.java +++ b/src/module/decode/lj1200/LJ1200DecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.lj1200; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -79,9 +79,9 @@ public class LJ1200DecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { - switch(changedAttribute) + switch(attribute) { case TO_TALKGROUP: mTo.setText(getDecoderState().getAddress()); diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java index 5c438647..2cc6cf42 100644 --- a/src/module/decode/lj1200/LJ1200DecoderState.java +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -26,11 +26,11 @@ import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -106,13 +106,13 @@ public class LJ1200DecoderState extends DecoderState private void setAddress( String address ) { mAddress = address; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } private void setAddressAlias( Alias alias ) { mAddressAlias = alias; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } @Override @@ -160,9 +160,9 @@ public class LJ1200DecoderState extends DecoderState private void resetState() { setAddress( null ); - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); setAddressAlias( null ); - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } @Override diff --git a/src/module/decode/ltrnet/LTRNetDecoderPanel.java b/src/module/decode/ltrnet/LTRNetDecoderPanel.java index 28e0dc28..5f5d9013 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderPanel.java +++ b/src/module/decode/ltrnet/LTRNetDecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.ltrnet; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -83,7 +83,7 @@ public class LTRNetDecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @@ -92,7 +92,7 @@ public class LTRNetDecoderPanel extends DecoderPanel { LTRNetDecoderState state = getDecoderState(); - switch(changedAttribute) + switch(attribute) { case TO_TALKGROUP: mTalkgroup.setText(state.getToTalkgroup()); diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java index 56b0df90..1f4d6f7f 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderState.java +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -27,11 +27,11 @@ import message.Message; import module.decode.DecoderType; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,7 +93,7 @@ public class LTRNetDecoderState extends DecoderState if (mChannelNumber == 0) { mChannelNumber = ltr.getChannel(); - broadcast(ChangedAttribute.CHANNEL_NUMBER); + broadcast(Attribute.CHANNEL_NUMBER); } /* Process FCC Station ID Events */ @@ -131,7 +131,7 @@ public class LTRNetDecoderState extends DecoderState if (mChannelNumber == 0) { mChannelNumber = ltr.getChannel(); - broadcast(ChangedAttribute.CHANNEL_NUMBER); + broadcast(Attribute.CHANNEL_NUMBER); } /* If the call event channel matches our current channel @@ -150,7 +150,7 @@ public class LTRNetDecoderState extends DecoderState if (mChannelNumber != ltr.getChannel()) { mChannelNumber = ltr.getChannel(); - broadcast(ChangedAttribute.CHANNEL_NUMBER); + broadcast(Attribute.CHANNEL_NUMBER); } break; case MA_CHNH: @@ -211,10 +211,10 @@ public class LTRNetDecoderState extends DecoderState } mTalkgroup = String.valueOf(uniqueID); - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); break; case ID_SITE: String siteID = ltr.getSiteID(); @@ -252,13 +252,13 @@ public class LTRNetDecoderState extends DecoderState } mDescription = "ESN"; - broadcast(ChangedAttribute.DESCRIPTION); + broadcast(Attribute.DESCRIPTION); mTalkgroup = ltr.getESN(); - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); mTalkgroupAlias = ltr.getESNAlias(); - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); broadcast(new DecoderStateEvent(this, Event.DECODE, State.DATA)); @@ -286,13 +286,13 @@ public class LTRNetDecoderState extends DecoderState mUniqueIDs.add(uniqueid); mDescription = "REGISTER UID"; - broadcast(ChangedAttribute.DESCRIPTION); + broadcast(Attribute.DESCRIPTION); mTalkgroup = String.valueOf(uniqueid); - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); if (getCurrentLTRCallEvent() == null) { @@ -620,7 +620,7 @@ public class LTRNetDecoderState extends DecoderState { broadcast(new DecoderStateEvent(this, Event.START, State.DATA)); mDescription = "REGISTER"; - broadcast(ChangedAttribute.DESCRIPTION); + broadcast(Attribute.DESCRIPTION); } /* Process call */ else @@ -643,7 +643,7 @@ public class LTRNetDecoderState extends DecoderState } mTalkgroup = message.getTalkgroupID(); - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); /* A talkgroup must be seen at least once before it will be added * to the mTalkgroups list that is used in the activity summary, @@ -658,7 +658,7 @@ public class LTRNetDecoderState extends DecoderState } mTalkgroupAlias = message.getTalkgroupIDAlias(); - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); CallEvent callEvent = new LTRCallEvent.Builder(DecoderType.LTR_NET, CallEventType.CALL) .aliasList(getAliasList()) @@ -729,13 +729,13 @@ public class LTRNetDecoderState extends DecoderState mCurrentCallEvent = null; mTalkgroup = null; - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); mTalkgroupAlias = null; - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); mDescription = null; - broadcast(ChangedAttribute.DESCRIPTION); + broadcast(Attribute.DESCRIPTION); } public String getToTalkgroup() diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java index 8fc815de..cde9cced 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.ltrstandard; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -84,7 +84,7 @@ public class LTRStandardDecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute attribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java index e4af71e8..8727d69d 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -34,11 +34,11 @@ import module.decode.ltrstandard.message.CallEndMessage; import module.decode.ltrstandard.message.CallMessage; import module.decode.ltrstandard.message.IdleMessage; import module.decode.ltrstandard.message.LTRStandardMessage; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -137,12 +137,12 @@ public class LTRStandardDecoderState extends DecoderState if( mLCNTracker.isCurrentChannel( channel ) ) { mTalkgroup = start.getToID(); - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); processTalkgroup( mTalkgroup ); mTalkgroupAlias = start.getToIDAlias(); - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); broadcast( new Metadata( MetadataType.TO, mTalkgroup, mTalkgroupAlias, true ) ); @@ -165,12 +165,12 @@ public class LTRStandardDecoderState extends DecoderState if( mLCNTracker.isCurrentChannel( repeater ) ) { mTalkgroup = end.getToID(); - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); processTalkgroup( mTalkgroup ); mTalkgroupAlias = end.getToIDAlias(); - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); LTRCallEvent event = mActiveCalls.remove( repeater ); @@ -242,10 +242,10 @@ public class LTRStandardDecoderState extends DecoderState mActiveCalls.clear(); mTalkgroup = null; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); mTalkgroupAlias = null; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } public String getTalkgroup() @@ -453,7 +453,7 @@ public class LTRStandardDecoderState extends DecoderState mCallHighestCount = mCallLCNCounts[ channel ]; mCurrentLCN = channel; - broadcast( ChangedAttribute.CHANNEL_NUMBER ); + broadcast( Attribute.CHANNEL_NUMBER ); } } } diff --git a/src/module/decode/mdc1200/MDCDecoderPanel.java b/src/module/decode/mdc1200/MDCDecoderPanel.java index b9702ce5..d13b4dba 100644 --- a/src/module/decode/mdc1200/MDCDecoderPanel.java +++ b/src/module/decode/mdc1200/MDCDecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.mdc1200; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -82,14 +82,14 @@ public class MDCDecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @Override public void run() { - switch(changedAttribute) + switch(attribute) { case FROM_TALKGROUP: String from = getDecoderState().getFrom(); diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java index 0a9d3a7c..a7a618f8 100644 --- a/src/module/decode/mdc1200/MDCDecoderState.java +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -23,11 +23,11 @@ import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.State; -import module.decode.state.DecoderStateEvent.Event; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.State; +import channel.state.DecoderStateEvent.Event; import alias.Alias; import alias.AliasList; @@ -150,7 +150,7 @@ public class MDCDecoderState extends DecoderState public void setFrom( String from ) { mFrom = from; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); } public Alias getFromAlias() @@ -161,7 +161,7 @@ public class MDCDecoderState extends DecoderState public void setFromAlias( Alias alias ) { mFromAlias = alias; - broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + broadcast( Attribute.FROM_TALKGROUP_ALIAS ); } public String getTo() @@ -172,7 +172,7 @@ public class MDCDecoderState extends DecoderState public void setTo( String to ) { mTo = to; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } public Alias getToAlias() @@ -183,7 +183,7 @@ public class MDCDecoderState extends DecoderState public void setToAlias( Alias alias ) { mToAlias = alias; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } public String getMessage() @@ -194,7 +194,7 @@ public class MDCDecoderState extends DecoderState public void setMessage( String message ) { mMessage = message; - broadcast( ChangedAttribute.MESSAGE ); + broadcast( Attribute.MESSAGE ); } public String getMessageType() @@ -205,7 +205,7 @@ public class MDCDecoderState extends DecoderState public void setMessageType( String type ) { mMessageType = type; - broadcast( ChangedAttribute.MESSAGE_TYPE ); + broadcast( Attribute.MESSAGE_TYPE ); } @Override diff --git a/src/module/decode/mpt1327/MPT1327DecoderPanel.java b/src/module/decode/mpt1327/MPT1327DecoderPanel.java index 27d9a44c..0aa40f98 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderPanel.java +++ b/src/module/decode/mpt1327/MPT1327DecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.mpt1327; import controller.channel.Channel.ChannelType; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -120,7 +120,7 @@ public class MPT1327DecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { final MPT1327DecoderState state = getDecoderState(); @@ -129,7 +129,7 @@ public class MPT1327DecoderPanel extends DecoderPanel @Override public void run() { - switch(changedAttribute) + switch(attribute) { case CHANNEL_SITE_NUMBER: mSiteOrToTalkgroup.setText(String.valueOf(state.getSite())); diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index 20858552..35c6428a 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -31,13 +31,13 @@ import module.decode.config.DecodeConfiguration; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; import module.decode.mpt1327.MPT1327Message.IdentType; -import module.decode.state.ChangeChannelTimeoutEvent; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; -import module.decode.state.TrafficChannelAllocationEvent; +import channel.state.ChangeChannelTimeoutEvent; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; +import channel.traffic.TrafficChannelAllocationEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -184,7 +184,7 @@ public class MPT1327DecoderState extends DecoderState if( site != null && ( mSite == null || !site.contentEquals( mSite ) ) ) { mSite = site; - broadcast( ChangedAttribute.CHANNEL_SITE_NUMBER ); + broadcast( Attribute.CHANNEL_SITE_NUMBER ); } mSite = mpt.getSiteID(); @@ -314,10 +314,10 @@ public class MPT1327DecoderState extends DecoderState private void resetState() { mFromTalkgroup = null; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); mToTalkgroup = null; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); /** * If this is a standard channel, reset the fade timeout to the default @@ -378,7 +378,7 @@ public class MPT1327DecoderState extends DecoderState { mFromTalkgroup = fromTalkgroup; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); } public Alias getFromTalkgroupAlias() @@ -405,7 +405,7 @@ public class MPT1327DecoderState extends DecoderState { mToTalkgroup = toTalkgroup; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } public Alias getToTalkgroupAlias() @@ -432,7 +432,7 @@ public class MPT1327DecoderState extends DecoderState { mChannelNumber = channel; - broadcast( ChangedAttribute.CHANNEL_NUMBER ); + broadcast( Attribute.CHANNEL_NUMBER ); } @Override @@ -590,7 +590,7 @@ public class MPT1327DecoderState extends DecoderState try { mChannelNumber = Integer.valueOf( channel ); - broadcast( ChangedAttribute.CHANNEL_NUMBER ); + broadcast( Attribute.CHANNEL_NUMBER ); } catch( Exception e ) { @@ -599,13 +599,13 @@ public class MPT1327DecoderState extends DecoderState } mFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast( ChangedAttribute.SOURCE ); + broadcast( Attribute.SOURCE ); mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); mToTalkgroup = allocationEvent.getCallEvent().getToID(); - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } } break; diff --git a/src/module/decode/mpt1327/MPT1327TrafficPanel.java b/src/module/decode/mpt1327/MPT1327TrafficPanel.java index aab5729d..5b799012 100644 --- a/src/module/decode/mpt1327/MPT1327TrafficPanel.java +++ b/src/module/decode/mpt1327/MPT1327TrafficPanel.java @@ -20,8 +20,8 @@ package module.decode.mpt1327; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; import settings.SettingsManager; @@ -95,7 +95,7 @@ public class MPT1327TrafficPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { final MPT1327DecoderState state = getDecoderState(); @@ -104,7 +104,7 @@ public class MPT1327TrafficPanel extends DecoderPanel @Override public void run() { - switch(changedAttribute) + switch(attribute) { case FROM_TALKGROUP: String talkgroup = state.getFromTalkgroup(); diff --git a/src/module/decode/nbfm/NBFMDecoderPanel.java b/src/module/decode/nbfm/NBFMDecoderPanel.java index 7030bf39..e25437ad 100644 --- a/src/module/decode/nbfm/NBFMDecoderPanel.java +++ b/src/module/decode/nbfm/NBFMDecoderPanel.java @@ -19,9 +19,9 @@ package module.decode.nbfm; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; -import module.decode.state.DecoderState; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; +import channel.state.DecoderState; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -56,7 +56,7 @@ public class NBFMDecoderPanel extends DecoderPanel } @Override - public void receive(ChangedAttribute t) + public void receive(Attribute t) { /* Not implemented */ } diff --git a/src/module/decode/p25/P25DecoderPanel.java b/src/module/decode/p25/P25DecoderPanel.java index 78faa3f3..d63ef9c2 100644 --- a/src/module/decode/p25/P25DecoderPanel.java +++ b/src/module/decode/p25/P25DecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.p25; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -108,7 +108,7 @@ public class P25DecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { final P25DecoderState state = getDecoderState(); @@ -118,7 +118,7 @@ public class P25DecoderPanel extends DecoderPanel public void run() { - switch(changedAttribute) + switch(attribute) { case NAC: mNAC.setText("NAC:" + state.getNAC()); diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 0b17c870..6c5b7c56 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -123,13 +123,13 @@ import module.decode.p25.reference.IPProtocol; import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Response; import module.decode.p25.reference.Vendor; -import module.decode.state.ChangeChannelTimeoutEvent; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; -import module.decode.state.TrafficChannelAllocationEvent; +import channel.state.ChangeChannelTimeoutEvent; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; +import channel.traffic.TrafficChannelAllocationEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -236,16 +236,16 @@ public class P25DecoderState extends DecoderState private void resetState() { mFromTalkgroup = null; - broadcast(ChangedAttribute.FROM_TALKGROUP); + broadcast(Attribute.FROM_TALKGROUP); mFromAlias = null; - broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS); + broadcast(Attribute.FROM_TALKGROUP_ALIAS); mToTalkgroup = null; - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); mToAlias = null; - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); mCallDetectTalkgroups.clear(); @@ -3372,7 +3372,7 @@ public class P25DecoderState extends DecoderState { mToTalkgroup = to; - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); if(hasAliasList()) { @@ -3383,7 +3383,7 @@ public class P25DecoderState extends DecoderState mToAlias = null; } - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); /* Send audio metadata update */ broadcast(new Metadata(MetadataType.TO, mToTalkgroup, mToAlias, true)); @@ -3409,7 +3409,7 @@ public class P25DecoderState extends DecoderState if(mFromTalkgroup == null || !mFromTalkgroup.contentEquals(from)) { mFromTalkgroup = from; - broadcast(ChangedAttribute.FROM_TALKGROUP); + broadcast(Attribute.FROM_TALKGROUP); if(hasAliasList()) { @@ -3420,7 +3420,7 @@ public class P25DecoderState extends DecoderState mFromAlias = null; } - broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS); + broadcast(Attribute.FROM_TALKGROUP_ALIAS); /* Send audio metadata update */ broadcast(new Metadata(MetadataType.FROM, mFromTalkgroup, mFromAlias, true)); @@ -3446,7 +3446,7 @@ public class P25DecoderState extends DecoderState { mNAC = nac; - broadcast(ChangedAttribute.NAC); + broadcast(Attribute.NAC); } } @@ -3459,7 +3459,7 @@ public class P25DecoderState extends DecoderState { mSite = site; - broadcast(ChangedAttribute.SITE); + broadcast(Attribute.SITE); if(hasAliasList()) { @@ -3475,7 +3475,7 @@ public class P25DecoderState extends DecoderState } } - broadcast(ChangedAttribute.SITE_ALIAS); + broadcast(Attribute.SITE_ALIAS); } } @@ -3485,7 +3485,7 @@ public class P25DecoderState extends DecoderState { mSystem = system; - broadcast(ChangedAttribute.SYSTEM); + broadcast(Attribute.SYSTEM); } } @@ -3816,26 +3816,26 @@ public class P25DecoderState extends DecoderState mCurrentCallEvent = (P25CallEvent) allocationEvent.getCallEvent(); mCurrentChannel = allocationEvent.getCallEvent().getChannel(); - broadcast(ChangedAttribute.CHANNEL_NUMBER); + broadcast(Attribute.CHANNEL_NUMBER); mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency(); - broadcast(ChangedAttribute.SOURCE); + broadcast(Attribute.SOURCE); mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); - broadcast(ChangedAttribute.FROM_TALKGROUP); + broadcast(Attribute.FROM_TALKGROUP); mToTalkgroup = allocationEvent.getCallEvent().getToID(); - broadcast(ChangedAttribute.TO_TALKGROUP); + broadcast(Attribute.TO_TALKGROUP); if(allocationEvent.getCallEvent().hasAliasList()) { AliasList aliasList = allocationEvent.getCallEvent().getAliasList(); mFromAlias = aliasList.getTalkgroupAlias(mFromTalkgroup); - broadcast(ChangedAttribute.FROM_TALKGROUP_ALIAS); + broadcast(Attribute.FROM_TALKGROUP_ALIAS); mToAlias = aliasList.getTalkgroupAlias(mToTalkgroup); - broadcast(ChangedAttribute.TO_TALKGROUP_ALIAS); + broadcast(Attribute.TO_TALKGROUP_ALIAS); } } } diff --git a/src/module/decode/passport/PassportDecoderPanel.java b/src/module/decode/passport/PassportDecoderPanel.java index 4bb29ebb..4d0aa4fd 100644 --- a/src/module/decode/passport/PassportDecoderPanel.java +++ b/src/module/decode/passport/PassportDecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.passport; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.Setting; @@ -93,7 +93,7 @@ public class PassportDecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @@ -102,7 +102,7 @@ public class PassportDecoderPanel extends DecoderPanel { PassportDecoderState state = getDecoderState(); - switch(changedAttribute) + switch(attribute) { case TO_TALKGROUP: String tg = state.getTalkgroup(); diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java index 99004efd..1b615c92 100644 --- a/src/module/decode/passport/PassportDecoderState.java +++ b/src/module/decode/passport/PassportDecoderState.java @@ -29,11 +29,11 @@ import message.Message; import module.decode.DecoderType; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.DecoderStateEvent.Event; -import module.decode.state.State; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.DecoderStateEvent.Event; +import channel.state.State; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -390,7 +390,7 @@ public class PassportDecoderState extends DecoderState mChannelNumber = mSiteBand.getChannel( mFrequency ); - broadcast( ChangedAttribute.CHANNEL_NUMBER ); + broadcast( Attribute.CHANNEL_NUMBER ); } break; default: @@ -570,7 +570,7 @@ public class PassportDecoderState extends DecoderState public void setTalkgroup( String talkgroup ) { mTalkgroup = talkgroup; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } public Alias getTalkgroupAlias() @@ -581,7 +581,7 @@ public class PassportDecoderState extends DecoderState public void setTalkgroupAlias( Alias alias ) { mTalkgroupAlias = alias; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } public String getMobileID() @@ -592,7 +592,7 @@ public class PassportDecoderState extends DecoderState public void setMobileID( String id ) { mMobileID = id; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); } public Alias getMobileIDAlias() @@ -603,7 +603,7 @@ public class PassportDecoderState extends DecoderState public void setMobileIDAlias( Alias alias ) { mMobileIDAlias = alias; - broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + broadcast( Attribute.FROM_TALKGROUP_ALIAS ); } @Override diff --git a/src/module/decode/state/ChangedAttribute.java b/src/module/decode/state/ChangedAttribute.java deleted file mode 100644 index 34a2881b..00000000 --- a/src/module/decode/state/ChangedAttribute.java +++ /dev/null @@ -1,29 +0,0 @@ -package module.decode.state; - -public enum ChangedAttribute -{ - AUDIO_TYPE, - CHANNEL_NUMBER, - CHANNEL_NAME, - CHANNEL_SITE_NUMBER, - CHANNEL_SITE_NUMBER_ALIAS, - CHANNEL_STATE, - DESCRIPTION, - FROM_TALKGROUP, - FROM_TALKGROUP_ALIAS, - FROM_TALKGROUP_TYPE, - MESSAGE, - MESSAGE_TYPE, - NAC, - SITE, - SITE_ALIAS, - SITE_NAME, - SOURCE, - SYSTEM, - SYSTEM_NAME, - SQUELCH, - TO_TALKGROUP, - TO_TALKGROUP_ALIAS, - TO_TALKGROUP_TYPE, - WACN; -} \ No newline at end of file diff --git a/src/module/decode/state/IChangedAttributeListener.java b/src/module/decode/state/IChangedAttributeListener.java deleted file mode 100644 index 04d3ce66..00000000 --- a/src/module/decode/state/IChangedAttributeListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package module.decode.state; - -import sample.Listener; - -public interface IChangedAttributeListener -{ - public Listener getChangedAttributeListener(); -} diff --git a/src/module/decode/state/IChangedAttributeProvider.java b/src/module/decode/state/IChangedAttributeProvider.java deleted file mode 100644 index 283c3e6b..00000000 --- a/src/module/decode/state/IChangedAttributeProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package module.decode.state; - -import sample.Listener; - -public interface IChangedAttributeProvider -{ - public void setChangedAttributeListener( Listener listener ); - public void removeChangedAttributeListener(); -} diff --git a/src/module/decode/state/TrafficChannelAllocationEvent.java b/src/module/decode/state/TrafficChannelAllocationEvent.java deleted file mode 100644 index 14441fde..00000000 --- a/src/module/decode/state/TrafficChannelAllocationEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -package module.decode.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; - } -} diff --git a/src/module/decode/tait/Tait1200DecoderPanel.java b/src/module/decode/tait/Tait1200DecoderPanel.java index 779399be..6a4672da 100644 --- a/src/module/decode/tait/Tait1200DecoderPanel.java +++ b/src/module/decode/tait/Tait1200DecoderPanel.java @@ -20,8 +20,8 @@ package module.decode.tait; import alias.Alias; import icon.IconManager; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderPanel; +import channel.metadata.Attribute; +import channel.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -87,14 +87,14 @@ public class Tait1200DecoderPanel extends DecoderPanel } @Override - public void receive(final ChangedAttribute changedAttribute) + public void receive(final Attribute attribute) { EventQueue.invokeLater(new Runnable() { @Override public void run() { - switch(changedAttribute) + switch(attribute) { case FROM_TALKGROUP: String from = getDecoderState().getFromID(); diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java index 569c6a36..ad6c78f7 100644 --- a/src/module/decode/tait/Tait1200DecoderState.java +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -28,11 +28,11 @@ import org.jdesktop.swingx.mapviewer.GeoPosition; import message.Message; import module.decode.DecoderType; import module.decode.event.CallEvent.CallEventType; -import module.decode.state.ChangedAttribute; -import module.decode.state.DecoderState; -import module.decode.state.DecoderStateEvent; -import module.decode.state.State; -import module.decode.state.DecoderStateEvent.Event; +import channel.metadata.Attribute; +import channel.state.DecoderState; +import channel.state.DecoderStateEvent; +import channel.state.State; +import channel.state.DecoderStateEvent.Event; import alias.Alias; import alias.AliasList; @@ -208,7 +208,7 @@ public class Tait1200DecoderState extends DecoderState public void setFromID( String from ) { mFrom = from; - broadcast( ChangedAttribute.FROM_TALKGROUP ); + broadcast( Attribute.FROM_TALKGROUP ); } public Alias getFromIDAlias() @@ -219,7 +219,7 @@ public class Tait1200DecoderState extends DecoderState public void setFromIDAlias( Alias alias ) { mFromAlias = alias; - broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + broadcast( Attribute.FROM_TALKGROUP_ALIAS ); } public String getToID() @@ -230,7 +230,7 @@ public class Tait1200DecoderState extends DecoderState public void setToID( String to ) { mTo = to; - broadcast( ChangedAttribute.TO_TALKGROUP ); + broadcast( Attribute.TO_TALKGROUP ); } public Alias getToIDAlias() @@ -241,7 +241,7 @@ public class Tait1200DecoderState extends DecoderState public void setToIDAlias( Alias alias ) { mToAlias = alias; - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( Attribute.TO_TALKGROUP_ALIAS ); } public String getMessage() @@ -252,7 +252,7 @@ public class Tait1200DecoderState extends DecoderState public void setMessage( String message ) { mMessage = message; - broadcast( ChangedAttribute.MESSAGE ); + broadcast( Attribute.MESSAGE ); } public String getMessageType() @@ -263,6 +263,6 @@ public class Tait1200DecoderState extends DecoderState public void setMessageType( String type ) { mMessageType = type; - broadcast( ChangedAttribute.MESSAGE_TYPE ); + broadcast( Attribute.MESSAGE_TYPE ); } } From 16b69d4aeff29afe99e67c397d09a816c6a933f0 Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 15 Jan 2017 08:42:13 -0500 Subject: [PATCH 02/10] #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. --- src/channel/metadata/AliasedIdentifier.java | 22 +- .../metadata/ChannelMetadataModel.java | 18 +- .../metadata/ChannelMetadataPanel.java | 9 +- .../IAttributeChangeRequestListener.java | 26 + .../IAttributeChangeRequestProvider.java | 28 + src/channel/metadata/Metadata.java | 529 +++++++++++----- src/channel/metadata/MutableMetadata.java | 251 ++++++++ ...t.java => MutableMetadataChangeEvent.java} | 8 +- .../state/AlwaysUnsquelchedDecoderState.java | 124 ++-- src/channel/state/ChannelState.java | 43 +- src/channel/state/DecoderState.java | 356 +++++------ .../fleetsync2/Fleetsync2DecoderState.java | 599 +++++++++--------- 12 files changed, 1256 insertions(+), 757 deletions(-) create mode 100644 src/channel/metadata/IAttributeChangeRequestListener.java create mode 100644 src/channel/metadata/IAttributeChangeRequestProvider.java create mode 100644 src/channel/metadata/MutableMetadata.java rename src/channel/metadata/{MetadataChangeEvent.java => MutableMetadataChangeEvent.java} (87%) diff --git a/src/channel/metadata/AliasedIdentifier.java b/src/channel/metadata/AliasedIdentifier.java index 57db3f0d..ef71adf2 100644 --- a/src/channel/metadata/AliasedIdentifier.java +++ b/src/channel/metadata/AliasedIdentifier.java @@ -34,8 +34,10 @@ public class AliasedIdentifier /** * Resets alias and value to null + * + * Package Private - should only be changed within MutableMetadata */ - public void reset() + void reset() { mIdentifier = null; mAlias = null; @@ -51,8 +53,10 @@ public class AliasedIdentifier /** * Sets the identifier to the argument value + * + * Package Private - should only be changed within MutableMetadata */ - public void setIdentifier(String identifier) + void setIdentifier(String identifier) { mIdentifier = identifier; } @@ -75,8 +79,10 @@ public class AliasedIdentifier /** * Sets the alias to the argument + * + * Package Private - should only be changed within MutableMetadata */ - public void setAlias(Alias alias) + void setAlias(Alias alias) { mAlias = alias; } @@ -88,4 +94,14 @@ public class AliasedIdentifier { return mAlias != null; } + + public AliasedIdentifier copyOf() + { + AliasedIdentifier copy = new AliasedIdentifier(); + + copy.setAlias(mAlias); + copy.setIdentifier(new String(mIdentifier)); + + return copy; + } } diff --git a/src/channel/metadata/ChannelMetadataModel.java b/src/channel/metadata/ChannelMetadataModel.java index 2e6b7390..8b890782 100644 --- a/src/channel/metadata/ChannelMetadataModel.java +++ b/src/channel/metadata/ChannelMetadataModel.java @@ -27,7 +27,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -public class ChannelMetadataModel extends AbstractTableModel implements Listener +public class ChannelMetadataModel extends AbstractTableModel implements Listener { public static final int COLUMN_STATE = 0; public static final int COLUMN_PRIMARY = 1; @@ -40,10 +40,10 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener private static final String[] COLUMNS = {"State", "Primary", "Secondary", "Message", "Network", "Frequency", "Configuration"}; - private List mChannelMetadata = new ArrayList(); - private Map mMetadataChannelMap = new HashMap(); + private List mChannelMetadata = new ArrayList(); + private Map mMetadataChannelMap = new HashMap(); - public void add(Metadata metadata, Channel channel) + public void add(MutableMetadata metadata, Channel channel) { mChannelMetadata.add(metadata); mMetadataChannelMap.put(metadata, channel); @@ -55,7 +55,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener metadata.addListener(this); } - public void remove(Metadata metadata) + public void remove(MutableMetadata metadata) { metadata.removeListener(this); @@ -88,7 +88,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener @Override public Class getColumnClass(int columnIndex) { - return Metadata.class; + return MutableMetadata.class; } @Override @@ -98,13 +98,13 @@ public class ChannelMetadataModel extends AbstractTableModel implements Listener } @Override - public void receive(MetadataChangeEvent metadataChangeEvent) + public void receive(MutableMetadataChangeEvent mutableMetadataChangeEvent) { - int rowIndex = mChannelMetadata.indexOf(metadataChangeEvent.getMetadata()); + int rowIndex = mChannelMetadata.indexOf(mutableMetadataChangeEvent.getMetadata()); if(rowIndex >= 0) { - switch(metadataChangeEvent.getAttribute()) + switch(mutableMetadataChangeEvent.getAttribute()) { case CHANNEL_CONFIGURATION_LABEL_1: case CHANNEL_CONFIGURATION_LABEL_2: diff --git a/src/channel/metadata/ChannelMetadataPanel.java b/src/channel/metadata/ChannelMetadataPanel.java index e3fa68aa..6b8d0b49 100644 --- a/src/channel/metadata/ChannelMetadataPanel.java +++ b/src/channel/metadata/ChannelMetadataPanel.java @@ -24,7 +24,6 @@ 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; @@ -120,7 +119,7 @@ public class ChannelMetadataPanel extends JPanel public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Metadata metadata = (Metadata)value; + MutableMetadata metadata = (MutableMetadata)value; if(mAttribute1 != null) { @@ -187,7 +186,7 @@ public class ChannelMetadataPanel extends JPanel public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Metadata metadata = (Metadata)value; + MutableMetadata metadata = (MutableMetadata)value; if(metadata.hasChannelFrequency()) { @@ -198,7 +197,7 @@ public class ChannelMetadataPanel extends JPanel mLabel1.setText(null); } - mLabel2.setText(metadata.getChannelID()); + mLabel2.setText(metadata.getChannelFrequencyLabel()); setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); @@ -224,7 +223,7 @@ public class ChannelMetadataPanel extends JPanel public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - Metadata metadata = (Metadata)value; + MutableMetadata metadata = (MutableMetadata)value; mLabel1.setText(metadata.getState().getDisplayValue()); diff --git a/src/channel/metadata/IAttributeChangeRequestListener.java b/src/channel/metadata/IAttributeChangeRequestListener.java new file mode 100644 index 00000000..b753569a --- /dev/null +++ b/src/channel/metadata/IAttributeChangeRequestListener.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package channel.metadata; + +import sample.Listener; + +public interface IAttributeChangeRequestListener +{ + public Listener getAttributeChangeRequestListener(); +} diff --git a/src/channel/metadata/IAttributeChangeRequestProvider.java b/src/channel/metadata/IAttributeChangeRequestProvider.java new file mode 100644 index 00000000..7be76f95 --- /dev/null +++ b/src/channel/metadata/IAttributeChangeRequestProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package channel.metadata; + +import sample.Listener; + +public interface IAttributeChangeRequestProvider +{ + public void setAttributeChangeRequestListener(Listener listener); + + public void removeAttributeChangeRequestListener(); +} diff --git a/src/channel/metadata/Metadata.java b/src/channel/metadata/Metadata.java index 6540fe80..5d268ec4 100644 --- a/src/channel/metadata/Metadata.java +++ b/src/channel/metadata/Metadata.java @@ -19,234 +19,187 @@ package channel.metadata; import alias.Alias; +import alias.id.broadcast.BroadcastChannel; +import alias.id.priority.Priority; import channel.state.State; -import sample.Broadcaster; -import sample.Listener; +import module.decode.DecoderType; -public class Metadata implements Listener +import java.util.ArrayList; +import java.util.List; + +public class Metadata { - 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(); + // Static unique metadata identifier tracking + private static int UNIQUE_METADATA_ID_GENERATOR = 0; - //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 int mMetadataID; - private Broadcaster mMetadataChangeEventBroadcaster = new Broadcaster<>(); + protected boolean mUpdated; + protected DecoderType mPrimaryDecoderType; + protected boolean mSelected; + protected State mState = State.IDLE; + protected String mChannelConfigurationLabel1; + protected String mChannelConfigurationLabel2; + protected String mChannelFrequencyLabel; + protected long mChannelFrequency; + protected AliasedIdentifier mNetworkID1 = new AliasedIdentifier(); + protected AliasedIdentifier mNetworkID2 = new AliasedIdentifier(); + + protected String mMessage; + protected String mMessageType; + protected AliasedIdentifier mPrimaryAddressFrom = new AliasedIdentifier(); + protected AliasedIdentifier mPrimaryAddressTo = new AliasedIdentifier(); + protected AliasedIdentifier mSecondaryAddressFrom = new AliasedIdentifier(); + protected AliasedIdentifier mSecondaryAddressTo = new AliasedIdentifier(); + + //Lazily constructed member variables. + private Integer mAudioPriority; + private Boolean mRecordable; + private List mBroadcastChannels; /** * 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. + * graphically convey the current state of a decoding channel and to provide audio metadata */ public Metadata() { + mMetadataID = UNIQUE_METADATA_ID_GENERATOR++; } /** - * Sets the state of this metadata + * Protected constructor. Primarily used by the copyOf() method to create a metadata copy with the same ID */ - public void setState(State state) + protected Metadata(int metadataID) { - mState = state; - broadcast(Attribute.CHANNEL_STATE); + mMetadataID = metadataID; } /** - * Request to change an attribute value for this metadata. This is the primary method for updating all attributes - * in this metadata set. + * Unique string identifier for this metadata that is comprised of the channel ID and the primary TO address. + * + * This identifier can be used to uniquely identify a channel audio stream and the primary communicant. */ - @Override - public void receive(AttributeChangeRequest request) + public String getUniqueIdentifier() { - 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()); - } + return "SRC:" + mMetadataID + + " ID:" + (mPrimaryAddressTo.hasIdentifier() ? mPrimaryAddressTo.getIdentifier() : "UNKNOWN"); } /** - * Broadcasts to registered listeners that an attribute has changed for this metadata + * Indicates if any of the fields of this metadata have been updated since the last time a copy was made from this + * metadata. This method is primarily used by downstream audio playback and audio recording to signal when changes + * are made to the metadata that requires the downstream component to reinspect the metadata. + * + * This flag is reset to false immediately after a copy is made of this metadata via the copyOf() method. */ - private void broadcast(Attribute attribute) + public boolean isUpdated() { - mMetadataChangeEventBroadcaster.broadcast(new MetadataChangeEvent(this, attribute)); + return mUpdated; } /** - * Adds the listener to receive metadata change events for this channel metadata + * Audio Priority as the highest audio priority value from across the primary and secondary identifier aliases. + * + * A default priority will be used if there are no aliased values. If there is a conflict between audio priority + * and 'Do Not Monitor' among the aliases, the highest audio priority will be used (prefer to monitor). + * + * -1 Do Not Monitor + * 0 Channel Selected for immediate monitor + * 1 Highest Priority + * 100 Lowest Priority (default) + * + * @return audio priority */ - public void addListener(Listener listener) + public int getAudioPriority() { - mMetadataChangeEventBroadcaster.addListener(listener); + if(isSelected()) + { + return Priority.SELECTED_PRIORITY; + } + + if(mAudioPriority == null) + { + determineAudioPriority(); + } + + return mAudioPriority; } /** - * Removes the listener from receiving metadata change events for this channel metadata + * Indicates if the audio priority is set to 'Do Not Monitor' */ - public void removeListener(Listener listener) + public boolean isDoNotMonitor() { - mMetadataChangeEventBroadcaster.removeListener(listener); + return getAudioPriority() == Priority.DO_NOT_MONITOR; } /** - * 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. + * Indicates if any of the primary or secondary TO/FROM aliases are identified as recordable */ - public void resetTemporalAttributes() + public boolean isRecordable() { - if(hasMessage()) + if(mRecordable == null) { - mMessage = null; - broadcast(Attribute.MESSAGE); + determineRecordable(); } - 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); - } + return mRecordable; } /** - * Resets all attributes to empty values + * Indicates that this metadata has been selected by the user and any audio produced by this channel will be set + * to the highest playback priority (immediate monitor). */ - public void resetAllAttributes() + public boolean isSelected() { - resetTemporalAttributes(); + return mSelected; + } - if(hasChannelConfigurationLabel1()) + /** + * List of de-duplicated broadcast channels aggregated from the primary and secondary TO/FROM aliases. + */ + public List getBroadcastChannels() + { + if(mBroadcastChannels == null) { - mChannelConfigurationLabel1 = null; - broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_1); + determineBroadcastChannels(); } - if(hasChannelConfigurationLabel2()) - { - mChannelConfigurationLabel2 = null; - broadcast(Attribute.CHANNEL_CONFIGURATION_LABEL_2); - } + return mBroadcastChannels; + } - if(hasChannelID()) - { - mChannelID = null; - broadcast(Attribute.CHANNEL_ID); - } + /** + * Indicates if this metadata is streamable, meaning that it contains one ore more broadcast channels + */ + public boolean isStreamable() + { + return !getBroadcastChannels().isEmpty(); + } - if(hasChannelFrequency()) - { - mChannelFrequency = 0; - broadcast(Attribute.CHANNEL_FREQUENCY); - } + /** + * Unique identifier for the source channel that produces this metadata + */ + public int getMetadataID() + { + return mMetadataID; + } - if(mNetworkID1.hasIdentifier()) - { - mNetworkID1.reset(); - broadcast(Attribute.NETWORK_ID_1); - } + /** + * Decoder type (ie protocol) for the primary decoder + */ + public DecoderType getPrimaryDecoderType() + { + return mPrimaryDecoderType; + } - if(mNetworkID2.hasIdentifier()) - { - mNetworkID2.reset(); - broadcast(Attribute.NETWORK_ID_2); - } + /** + * Indicates if this metadata has a primary decoder type defined + */ + public boolean hasPrimaryDecoderType() + { + return mPrimaryDecoderType != null; } /** @@ -285,7 +238,7 @@ public class Metadata implements Listener case CHANNEL_CONFIGURATION_LABEL_2: return mChannelConfigurationLabel2; case CHANNEL_ID: - return mChannelID; + return mChannelFrequencyLabel; case CHANNEL_STATE: return mState.getDisplayValue(); case MESSAGE: @@ -352,17 +305,17 @@ public class Metadata implements Listener /** * Channel ID or Channel Number */ - public String getChannelID() + public String getChannelFrequencyLabel() { - return mChannelID; + return mChannelFrequencyLabel; } /** * Indicates if there is a non-null, non-empty channel identifier value */ - public boolean hasChannelID() + public boolean hasChannelFrequencyLabel() { - return mChannelID != null && !mChannelID.isEmpty(); + return mChannelFrequencyLabel != null && !mChannelFrequencyLabel.isEmpty(); } /** @@ -460,4 +413,232 @@ public class Metadata implements Listener { return mSecondaryAddressTo; } + + /** + * Determines the audio priority from across the primary and secondary TO/FROM aliases when they exist + */ + private void determineAudioPriority() + { + int priority = Priority.DEFAULT_PRIORITY; + + boolean doNotMonitorFound = false; + boolean audioPriorityFound = false; + + if(getPrimaryAddressTo().hasAlias()) + { + Alias primaryTo = getPrimaryAddressTo().getAlias(); + + if(primaryTo.hasCallPriority()) + { + if(primaryTo.getCallPriority() == Priority.DO_NOT_MONITOR) + { + doNotMonitorFound = true; + } + else + { + audioPriorityFound = true; + + if(primaryTo.getCallPriority() < priority) + { + priority = primaryTo.getCallPriority(); + } + } + } + } + + if(getPrimaryAddressFrom().hasAlias()) + { + Alias primaryFrom = getPrimaryAddressFrom().getAlias(); + + if(primaryFrom.hasCallPriority()) + { + if(primaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR) + { + doNotMonitorFound = true; + } + else + { + audioPriorityFound = true; + + if(primaryFrom.getCallPriority() < priority) + { + priority = primaryFrom.getCallPriority(); + } + } + } + } + + if(getSecondaryAddressTo().hasAlias()) + { + Alias secondaryTo = getSecondaryAddressTo().getAlias(); + + if(secondaryTo.hasCallPriority()) + { + if(secondaryTo.getCallPriority() == Priority.DO_NOT_MONITOR) + { + doNotMonitorFound = true; + } + else + { + audioPriorityFound = true; + + if(secondaryTo.getCallPriority() < priority) + { + priority = secondaryTo.getCallPriority(); + } + } + } + } + + if(getSecondaryAddressFrom().hasAlias()) + { + Alias secondaryFrom = getSecondaryAddressFrom().getAlias(); + + if(secondaryFrom.hasCallPriority()) + { + if(secondaryFrom.getCallPriority() == Priority.DO_NOT_MONITOR) + { + doNotMonitorFound = true; + } + else + { + audioPriorityFound = true; + + if(secondaryFrom.getCallPriority() < priority) + { + priority = secondaryFrom.getCallPriority(); + } + } + } + } + + //If we found a 'Do Not Monitor' and no other audio priority, then set to do not monitor + if(doNotMonitorFound && !audioPriorityFound) + { + mAudioPriority = Priority.DO_NOT_MONITOR; + } + else + { + mAudioPriority = priority; + } + } + + /** + * Determines if any of the primary or secondary to/from aliases are recordable. + */ + private void determineRecordable() + { + mRecordable = false; + + if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isRecordable()) + { + mRecordable = true; + return; + } + + if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isRecordable()) + { + mRecordable = true; + return; + } + + if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isRecordable()) + { + mRecordable = true; + return; + } + + if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isRecordable()) + { + mRecordable = true; + return; + } + } + + /** + * Aggregates a de-duplicated list of broadcast channels from among the primary/secondary to/from aliases + */ + private void determineBroadcastChannels() + { + mBroadcastChannels = new ArrayList<>(); + + if(mPrimaryAddressTo.hasAlias() && mPrimaryAddressTo.getAlias().isStreamable()) + { + for(BroadcastChannel broadcastChannel: mPrimaryAddressTo.getAlias().getBroadcastChannels()) + { + if(!mBroadcastChannels.contains(broadcastChannel)) + { + mBroadcastChannels.add(broadcastChannel); + } + } + } + + if(mPrimaryAddressFrom.hasAlias() && mPrimaryAddressFrom.getAlias().isStreamable()) + { + for(BroadcastChannel broadcastChannel: mPrimaryAddressFrom.getAlias().getBroadcastChannels()) + { + if(!mBroadcastChannels.contains(broadcastChannel)) + { + mBroadcastChannels.add(broadcastChannel); + } + } + } + + if(mSecondaryAddressTo.hasAlias() && mSecondaryAddressTo.getAlias().isStreamable()) + { + for(BroadcastChannel broadcastChannel: mSecondaryAddressTo.getAlias().getBroadcastChannels()) + { + if(!mBroadcastChannels.contains(broadcastChannel)) + { + mBroadcastChannels.add(broadcastChannel); + } + } + } + + if(mSecondaryAddressFrom.hasAlias() && mSecondaryAddressFrom.getAlias().isStreamable()) + { + for(BroadcastChannel broadcastChannel: mSecondaryAddressFrom.getAlias().getBroadcastChannels()) + { + if(!mBroadcastChannels.contains(broadcastChannel)) + { + mBroadcastChannels.add(broadcastChannel); + } + } + } + } + + /** + * Creates a deep/full snapshot copy of this metadata set. + * + * The streamable, recordable, audio priority and broadcast channels are not copied. These are lazily determined + * when initially requested. + */ + public Metadata copyOf() + { + Metadata copy = new Metadata(mMetadataID); + + copy.mState = mState; + copy.mPrimaryDecoderType = mPrimaryDecoderType; + copy.mChannelFrequency = mChannelFrequency; + + copy.mChannelConfigurationLabel1 = hasChannelConfigurationLabel1() ? new String(mChannelConfigurationLabel1) : null; + copy.mChannelConfigurationLabel2 = hasChannelConfigurationLabel2() ? new String(mChannelConfigurationLabel2) : null; + copy.mChannelFrequencyLabel = hasChannelFrequencyLabel() ? new String(mChannelFrequencyLabel) : null; + copy.mMessage = hasMessage() ? new String(mMessage) : null; + copy.mMessageType = hasMessageType() ? new String(mMessageType) : null; + + copy.mNetworkID1 = mNetworkID1.copyOf(); + copy.mNetworkID2 = mNetworkID2.copyOf(); + copy.mPrimaryAddressFrom = mPrimaryAddressFrom.copyOf(); + copy.mPrimaryAddressTo = mPrimaryAddressTo.copyOf(); + copy.mSecondaryAddressFrom = mSecondaryAddressFrom.copyOf(); + copy.mSecondaryAddressTo = mSecondaryAddressTo.copyOf(); + + copy.mUpdated = mUpdated; + + //Reset the updated flag + mUpdated = false; + + return copy; + } } diff --git a/src/channel/metadata/MutableMetadata.java b/src/channel/metadata/MutableMetadata.java new file mode 100644 index 00000000..7a4bb15d --- /dev/null +++ b/src/channel/metadata/MutableMetadata.java @@ -0,0 +1,251 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package channel.metadata; + +import channel.state.State; +import module.decode.DecoderType; +import sample.Broadcaster; +import sample.Listener; + +public class MutableMetadata extends Metadata implements Listener +{ + private Broadcaster mMetadataChangeEventBroadcaster = new Broadcaster<>(); + + /** + * Mutable 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 and to provide audio metadata + */ + public MutableMetadata() + { + super(); + } + + /** + * Sets the selected state for this metadata. Selected indicates that the user has selected this metadata for + * immediate monitor in the audio panel. When true, this overrides the audio priority for any audio produced + * by the channel and sets that audio to the highest priority. + * + * Any component that toggles the selected state for this metadata is also responsible for clearing the flag. + */ + public void setSelected(boolean selected) + { + mSelected = selected; + } + + /** + * Sets the primary decoder type + */ + public void setPrimaryDecoderType(DecoderType decoderType) + { + mPrimaryDecoderType = decoderType; + } + + /** + * 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: + mChannelFrequencyLabel = 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; + case CHANNEL_STATE: + mState = (State)request.getValue(); + broadcast(Attribute.CHANNEL_STATE); + break; + default: + throw new IllegalArgumentException("Unrecognized Metadata Attribute: " + + request.getAttribute().name()); + } + } + + /** + * 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(hasChannelFrequencyLabel()) + { + mChannelFrequencyLabel = 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); + } + } + + + /** + * Broadcasts to registered listeners that an attribute has changed for this metadata + */ + private void broadcast(Attribute attribute) + { + mMetadataChangeEventBroadcaster.broadcast(new MutableMetadataChangeEvent(this, attribute)); + } + + /** + * Adds the listener to receive metadata change events for this channel metadata + */ + public void addListener(Listener listener) + { + mMetadataChangeEventBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving metadata change events for this channel metadata + */ + public void removeListener(Listener listener) + { + mMetadataChangeEventBroadcaster.removeListener(listener); + } +} diff --git a/src/channel/metadata/MetadataChangeEvent.java b/src/channel/metadata/MutableMetadataChangeEvent.java similarity index 87% rename from src/channel/metadata/MetadataChangeEvent.java rename to src/channel/metadata/MutableMetadataChangeEvent.java index 123adb08..41d4e911 100644 --- a/src/channel/metadata/MetadataChangeEvent.java +++ b/src/channel/metadata/MutableMetadataChangeEvent.java @@ -18,9 +18,9 @@ ******************************************************************************/ package channel.metadata; -public class MetadataChangeEvent +public class MutableMetadataChangeEvent { - private Metadata mMetadata; + private MutableMetadata mMetadata; private Attribute mAttribute; /** @@ -29,7 +29,7 @@ public class MetadataChangeEvent * @param metadata where the change occurred * @param attribute that changed */ - public MetadataChangeEvent(Metadata metadata, Attribute attribute) + public MutableMetadataChangeEvent(MutableMetadata metadata, Attribute attribute) { mMetadata = metadata; mAttribute = attribute; @@ -38,7 +38,7 @@ public class MetadataChangeEvent /** * Channel metadata where the change occurred */ - public Metadata getMetadata() + public MutableMetadata getMetadata() { return mMetadata; } diff --git a/src/channel/state/AlwaysUnsquelchedDecoderState.java b/src/channel/state/AlwaysUnsquelchedDecoderState.java index 3d65f1ed..485e2cd1 100644 --- a/src/channel/state/AlwaysUnsquelchedDecoderState.java +++ b/src/channel/state/AlwaysUnsquelchedDecoderState.java @@ -1,29 +1,29 @@ /******************************************************************************* * 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 ******************************************************************************/ package channel.state; -import java.util.concurrent.ScheduledExecutorService; - +import channel.metadata.Attribute; +import channel.metadata.AttributeChangeRequest; +import channel.state.DecoderStateEvent.Event; import message.Message; import module.decode.DecoderType; -import channel.state.DecoderStateEvent.Event; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; + +import java.util.concurrent.ScheduledExecutorService; /** * Basic decoder channel state - provides the minimum channel state functionality @@ -31,68 +31,68 @@ import audio.metadata.MetadataType; */ 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 */ - } + private DecoderType mDecoderType; + private String mChannelName; - @Override - public String getActivitySummary() + public AlwaysUnsquelchedDecoderState(DecoderType decoderType, String channelName) { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "\tDecoder:\t" ); - sb.append( mDecoderType ); - sb.append( "\n\n" ); - - return sb.toString(); + super(null); + + mDecoderType = decoderType; + mChannelName = channelName; } - @Override - public void receive( Message t ) - { + @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 ) - { + @Override + public void receiveDecoderStateEvent(DecoderStateEvent event) + { /* Not implemented */ - } + } - @Override - public DecoderType getDecoderType() - { - return mDecoderType; - } + @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 start(ScheduledExecutorService executor) + { + broadcast(new AttributeChangeRequest(Attribute.PRIMARY_ADDRESS_TO, mChannelName)); + broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE)); + } - @Override - public void stop() - { - } + @Override + public void stop() + { + } } diff --git a/src/channel/state/ChannelState.java b/src/channel/state/ChannelState.java index 1188c3ac..56c57da4 100644 --- a/src/channel/state/ChannelState.java +++ b/src/channel/state/ChannelState.java @@ -23,6 +23,9 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import channel.metadata.Attribute; +import channel.metadata.AttributeChangeRequest; +import channel.metadata.IAttributeChangeRequestListener; +import channel.metadata.MutableMetadata; import channel.traffic.TrafficChannelManager; import module.Module; import module.decode.config.DecodeConfiguration; @@ -43,14 +46,16 @@ import audio.squelch.SquelchState; import controller.channel.Channel.ChannelType; public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider, - IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider + IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider, + IAttributeChangeRequestListener { 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 MutableMetadata mMutableMetadata = new MutableMetadata(); private State mState = State.IDLE; private Listener mCallEventListener; @@ -107,7 +112,19 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged { mChannelType = channelType; } - + + + /** + * Metadata for this channel containing channel details, primary and secondary entities for call events and any + * decoded messages. This metadata is primarily intended to be used by graphical components to convey the + * details of the current channel state and to provide metadata backing any decoded audio packets produced by + * any audio streams for this channel. + */ + public MutableMetadata getMutableMetadata() + { + return mMutableMetadata; + } + /** * Resets this channel state and prepares it for reuse. */ @@ -115,8 +132,9 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged public void reset() { broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); - broadcast( new MetadataReset() ); - + + mMutableMetadata.resetTemporalAttributes(); + mState = State.IDLE; broadcast( Attribute.CHANNEL_STATE ); } @@ -126,6 +144,8 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged { if( mStateMonitorFuture == null && mStateMonitor != null ) { + mMutableMetadata.resetAllAttributes(); + if( mTrafficChannelEndListener != null ) { setState( State.CALL ); @@ -419,7 +439,16 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged mCallEventListener = null; } - /** + + + //NEW: + @Override + public Listener getAttributeChangeRequestListener() + { + return mMutableMetadata; + } + + /** * Broadcasts the channel state attribute change event to all registered * listeners */ @@ -439,7 +468,7 @@ public class ChannelState extends Module implements ICallEventProvider, IChanged { mChangedAttributeListener = listener; } - + /** * Removes the listener to receive channel state attribute change events */ diff --git a/src/channel/state/DecoderState.java b/src/channel/state/DecoderState.java index de5b04b9..8aaa20e2 100644 --- a/src/channel/state/DecoderState.java +++ b/src/channel/state/DecoderState.java @@ -1,6 +1,11 @@ package channel.state; +import alias.AliasList; +import audio.metadata.IMetadataProvider; +import audio.metadata.Metadata; import channel.metadata.Attribute; +import channel.metadata.AttributeChangeRequest; +import channel.metadata.IAttributeChangeRequestProvider; import message.IMessageListener; import message.Message; import module.Module; @@ -10,219 +15,182 @@ 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 + * 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, - ICallEventProvider, - IChangedAttributeProvider, - IDecoderStateEventListener, - IDecoderStateEventProvider, - IMessageListener, - IMetadataProvider +public abstract class DecoderState extends Module implements ActivitySummaryProvider, Listener, + IAttributeChangeRequestProvider, ICallEventProvider, IDecoderStateEventListener, + IDecoderStateEventProvider, IMessageListener { - /* This has to be a broadcaster in order for references to persist */ - private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); - private Listener mChangedAttributeListener; - private Listener mDecoderStateListener; - private Listener 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(); + /* This has to be a broadcaster in order for references to persist */ + private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); + private Listener mAttributeChangeRequestListener; + private Listener mDecoderStateListener; - /** - * Allow the decoder to perform any setup actions - */ - public abstract void init(); + private DecoderStateEventListener mDecoderStateEventListener = new DecoderStateEventListener(); - /** - * 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; + protected CallEvent mCurrentCallEvent; - mAliasList = null; - } - - /** - * Activity Summary - textual summary of activity observed by the channel state. - */ - public abstract String getActivitySummary(); + private AliasList mAliasList; - /** - * Broadcasts a call event to any registered listeners - */ - protected void broadcast( CallEvent event ) - { - mCallEventBroadcaster.broadcast( event ); - } + public DecoderState(AliasList aliasList) + { + mAliasList = aliasList; + } - /** - * Adds a call event listener - */ - @Override - public void addCallEventListener( Listener listener ) - { - mCallEventBroadcaster.addListener( listener ); - } + public abstract DecoderType getDecoderType(); - /** - * Removes the call event listener - */ - @Override - public void removeCallEventListener( Listener listener ) - { - mCallEventBroadcaster.removeListener( listener ); - } + public AliasList getAliasList() + { + return mAliasList; + } - @Override - public Listener getDecoderStateListener() - { - return mDecoderStateEventListener; - } - - private class DecoderStateEventListener implements Listener - { - @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 ); - } - } + public boolean hasAliasList() + { + return mAliasList != null; + } - /** - * Adds the listener to receive channel state attribute change events - */ - @Override - public void setChangedAttributeListener( Listener listener ) - { - mChangedAttributeListener = listener; - } - - /** - * Removes the listener to receive channel state attribute change events - */ - @Override - public void removeChangedAttributeListener() - { - mChangedAttributeListener = null; - } + /** + * Reset the decoder state to prepare for processing a different sample + * source + */ + public abstract void reset(); - /** - * Broadcasts a channel state event to any registered listeners - */ - protected void broadcast( DecoderStateEvent event ) - { - if( mDecoderStateListener != null ) - { - mDecoderStateListener.receive( event ); - } - } + /** + * Allow the decoder to perform any setup actions + */ + public abstract void init(); - /** - * Adds a decoder state event listener - */ - @Override - public void setDecoderStateListener( Listener listener ) - { - mDecoderStateListener = listener; - } + /** + * Implements the IDecoderStateEventListener interface to receive state + * reset events. + */ + public abstract void receiveDecoderStateEvent(DecoderStateEvent event); - /** - * Removes the decoder state event listener - */ - @Override - public void removeDecoderStateListener() - { - mDecoderStateListener = null; - } + /** + * Disposes any resources or pointers held by this instance to prepare for + * garbage collection + */ + public void dispose() + { + mCallEventBroadcaster.dispose(); + mCallEventBroadcaster = null; + mDecoderStateListener = null; - /** - * Broadcasts metadata to a registered listener - */ - protected void broadcast( Metadata metadata ) - { - if( mMetadataListener != null ) - { - mMetadataListener.receive( metadata ); - } - } + mAliasList = null; + } - @Override - public void setMetadataListener( Listener listener ) - { - mMetadataListener = listener; - } + /** + * Activity Summary - textual summary of activity observed by the channel state. + */ + public abstract String getActivitySummary(); - @Override - public void removeMetadataListener() - { - mMetadataListener = null; - } + /** + * Broadcasts a call event to any registered listeners + */ + protected void broadcast(CallEvent event) + { + mCallEventBroadcaster.broadcast(event); + } - @Override - public Listener getMessageListener() - { - return this; - } + /** + * Adds a call event listener + */ + @Override + public void addCallEventListener(Listener listener) + { + mCallEventBroadcaster.addListener(listener); + } + + /** + * Removes the call event listener + */ + @Override + public void removeCallEventListener(Listener listener) + { + mCallEventBroadcaster.removeListener(listener); + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderStateEventListener; + } + + private class DecoderStateEventListener implements Listener + { + @Override + public void receive(DecoderStateEvent event) + { + receiveDecoderStateEvent(event); + } + } + + /** + * 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 listener) + { + mDecoderStateListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeDecoderStateListener() + { + mDecoderStateListener = null; + } + + @Override + public Listener getMessageListener() + { + return this; + } + + /** + * Broadcasts the attribute change request to the registered listener + */ + protected void broadcast(AttributeChangeRequest request) + { + if(mAttributeChangeRequestListener != null) + { + mAttributeChangeRequestListener.receive(request); + } + } + + /** + * Sets the listener to receive attribute change requests from this decoder state + */ + @Override + public void setAttributeChangeRequestListener(Listener listener) + { + mAttributeChangeRequestListener = listener; + } + + /** + * Removes any listener from receiving attribute change requests + */ + @Override + public void removeAttributeChangeRequestListener() + { + mAttributeChangeRequestListener = null; + } } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java index 0f343109..ee9df66b 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -1,353 +1,354 @@ /******************************************************************************* * 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 ******************************************************************************/ package module.decode.fleetsync2; -import java.util.Iterator; -import java.util.TreeSet; -import java.util.concurrent.ScheduledExecutorService; - -import message.Message; -import module.decode.DecoderType; +import alias.Alias; +import alias.AliasList; import channel.metadata.Attribute; import channel.state.DecoderState; import channel.state.DecoderStateEvent; import channel.state.DecoderStateEvent.Event; import channel.state.State; -import alias.Alias; -import alias.AliasList; +import message.Message; +import module.decode.DecoderType; + +import java.util.Iterator; +import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; public class Fleetsync2DecoderState extends DecoderState { - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); + private TreeSet mIdents = new TreeSet(); + private TreeSet mEmergencyIdents = new TreeSet(); - private String mFrom; - private Alias mFromAlias; + private String mFrom; + private Alias mFromAlias; - private String mTo; - private Alias mToAlias; + private String mTo; + private Alias mToAlias; - private String mMessage; - private String mMessageType; - - private long mFrequency; + private String mMessage; + private String mMessageType; - public Fleetsync2DecoderState( AliasList aliasList ) - { - super( aliasList ); - } - - @Override - public DecoderType getDecoderType() - { - return DecoderType.FLEETSYNC2; - } + private long mFrequency; - @Override - public void start( ScheduledExecutorService executor ) - { - } - - @Override - public void stop() - { - } - - @Override - public void dispose() - { - super.dispose(); - } - - @Override - public void init() - { - } - - /** - * Resets the overall decoder state and clears any accumulated event details - */ - @Override - public void reset() - { - mIdents.clear(); - mEmergencyIdents.clear(); - - resetState(); - } - - /** - * Resets this decoder state - */ - public void resetState() - { - setFromID( null ); - setFromIDAlias( null ); - setToID( null ); - setToIDAlias( null ); - setMessage( null ); - setMessageType( null ); - } - - @Override - public void receive( Message message ) + public Fleetsync2DecoderState(AliasList aliasList) { - if( message instanceof FleetsyncMessage ) - { - FleetsyncMessage fleetsync = (FleetsyncMessage)message; - - if( fleetsync.isValid() ) - { - State state = State.CALL; - - setFromID( fleetsync.getFromID() ); - setFromIDAlias( fleetsync.getFromIDAlias() ); - mIdents.add( fleetsync.getFromID() ); - - FleetsyncMessageType type = fleetsync.getMessageType(); + super(aliasList); + } - if( type != FleetsyncMessageType.ANI ) - { - setToID( fleetsync.getToID() ); - setToIDAlias( fleetsync.getToIDAlias() ); - mIdents.add( fleetsync.getToID() ); - } - - setMessageType( type.getLabel() ); + @Override + public DecoderType getDecoderType() + { + return DecoderType.FLEETSYNC2; + } - switch( type ) - { - case GPS: - setMessage( fleetsync.getGPSLocation() ); - state = State.DATA; - break; - case STATUS: - StringBuilder sb = new StringBuilder(); - sb.append( fleetsync.getStatus() ); - - Alias status = fleetsync.getStatusAlias(); - - if( status != null ) - { - sb.append( "/" ); - sb.append( status.getName() ); - } + @Override + public void start(ScheduledExecutorService executor) + { + } - setMessage( sb.toString() ); - state = State.DATA; - break; - case EMERGENCY: - case LONE_WORKER_EMERGENCY: - mEmergencyIdents.add( fleetsync.getFromID() ); - state = State.DATA; - break; - default: - break; - } + @Override + public void stop() + { + } - FleetsyncCallEvent fsCallEvent = - FleetsyncCallEvent.getFleetsync2Event( fleetsync, mFrequency ); + @Override + public void dispose() + { + super.dispose(); + } - fsCallEvent.setAliasList( getAliasList() ); + @Override + public void init() + { + } - broadcast( fsCallEvent ); + /** + * Resets the overall decoder state and clears any accumulated event details + */ + @Override + public void reset() + { + mIdents.clear(); + mEmergencyIdents.clear(); + + resetState(); + } + + /** + * Resets this decoder state + */ + public void resetState() + { + mFrom = null; + mFromAlias = null; + mTo = null; + mToAlias = null; + mMessage = null; + mMessageType = null; + } + + @Override + public void receive(Message message) + { + if(message instanceof FleetsyncMessage) + { + FleetsyncMessage fleetsync = (FleetsyncMessage) message; + + if(fleetsync.isValid()) + { + State state = State.CALL; + + setFromID(fleetsync.getFromID()); + setFromIDAlias(fleetsync.getFromIDAlias()); + mIdents.add(fleetsync.getFromID()); + + FleetsyncMessageType type = fleetsync.getMessageType(); + + if(type != FleetsyncMessageType.ANI) + { + setToID(fleetsync.getToID()); + setToIDAlias(fleetsync.getToIDAlias()); + mIdents.add(fleetsync.getToID()); + } + + setMessageType(type.getLabel()); + + switch(type) + { + case GPS: + setMessage(fleetsync.getGPSLocation()); + state = State.DATA; + break; + case STATUS: + StringBuilder sb = new StringBuilder(); + sb.append(fleetsync.getStatus()); + + Alias status = fleetsync.getStatusAlias(); + + if(status != null) + { + sb.append("/"); + sb.append(status.getName()); + } + + setMessage(sb.toString()); + state = State.DATA; + break; + case EMERGENCY: + case LONE_WORKER_EMERGENCY: + mEmergencyIdents.add(fleetsync.getFromID()); + state = State.DATA; + break; + default: + break; + } + + FleetsyncCallEvent fsCallEvent = + FleetsyncCallEvent.getFleetsync2Event(fleetsync, mFrequency); + + fsCallEvent.setAliasList(getAliasList()); + + broadcast(fsCallEvent); /* Broadcast decode event so that the channel state will - * kick in and reset everything after a short delay */ - broadcast( new DecoderStateEvent( this, Event.DECODE, state ) ); - } - } + * kick in and reset everything after a short delay */ + broadcast(new DecoderStateEvent(this, Event.DECODE, state)); + } + } } - - public String getFromID() - { - return mFrom; - } - - public void setFromID( String from ) - { - mFrom = from; - broadcast( Attribute.FROM_TALKGROUP ); - } - - public Alias getFromIDAlias() - { - return mFromAlias; - } - public void setFromIDAlias( Alias alias ) - { - mFromAlias = alias; - broadcast( Attribute.FROM_TALKGROUP_ALIAS ); - } - - public String getToID() - { - return mTo; - } - - public void setToID( String to ) - { - mTo = to; - broadcast( Attribute.TO_TALKGROUP ); - } - - public Alias getToIDAlias() - { - return mToAlias; - } - - public void setToIDAlias( Alias alias ) - { - mToAlias = alias; - broadcast( Attribute.TO_TALKGROUP_ALIAS ); - } - - public String getMessage() - { - return mMessage; - } + public String getFromID() + { + return mFrom; + } - public void setMessage( String message ) - { - mMessage = message; - broadcast( Attribute.MESSAGE ); - } - - public String getMessageType() - { - return mMessageType; - } + public void setFromID(String from) + { + mFrom = from; - public void setMessageType( String type ) - { - mMessageType = type; - broadcast( Attribute.MESSAGE_TYPE ); - } - - /** - * Responds to reset events issued by the channel state - */ - @Override - public void receiveDecoderStateEvent( DecoderStateEvent event ) - { - switch( event.getEvent() ) - { - case RESET: - resetState(); - break; - case SOURCE_FREQUENCY: - mFrequency = event.getFrequency(); - break; - default: - break; - } - } - - @Override + broadcast(Attribute.FROM_TALKGROUP); + } + + public Alias getFromIDAlias() + { + return mFromAlias; + } + + public void setFromIDAlias(Alias alias) + { + mFromAlias = alias; + broadcast(Attribute.FROM_TALKGROUP_ALIAS); + } + + public String getToID() + { + return mTo; + } + + public void setToID(String to) + { + mTo = to; + broadcast(Attribute.TO_TALKGROUP); + } + + public Alias getToIDAlias() + { + return mToAlias; + } + + public void setToIDAlias(Alias alias) + { + mToAlias = alias; + broadcast(Attribute.TO_TALKGROUP_ALIAS); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage(String message) + { + mMessage = message; + broadcast(Attribute.MESSAGE); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType(String type) + { + mMessageType = type; + broadcast(Attribute.MESSAGE_TYPE); + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent(DecoderStateEvent event) + { + switch(event.getEvent()) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + default: + break; + } + } + + @Override public String getActivitySummary() { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); - sb.append( "=============================\n" ); - sb.append( "Decoder:\tFleetsync II\n\n" ); - sb.append( "Fleetsync Idents\n" ); + sb.append("=============================\n"); + sb.append("Decoder:\tFleetsync II\n\n"); + sb.append("Fleetsync Idents\n"); - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( formatIdent( ident ) ); + if(mIdents.isEmpty()) + { + sb.append(" None\n"); + } + else + { + Iterator it = mIdents.iterator(); - if( hasAliasList() ) - { - Alias alias = getAliasList().getFleetsyncAlias( ident ); - - if( alias != null ) - { - sb.append( " " ); - sb.append( alias.getName() ); - } - } - - sb.append( "\n" ); - } - } - - sb.append( "\nFleetsync Emergency Activations\n" ); + while(it.hasNext()) + { + String ident = it.next(); - if( mEmergencyIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( formatIdent( ident ) ); + sb.append(" "); + sb.append(formatIdent(ident)); - if( hasAliasList() ) - { - Alias alias = getAliasList().getFleetsyncAlias( ident ); - - if( alias != null ) - { - sb.append( " " ); - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - } + if(hasAliasList()) + { + Alias alias = getAliasList().getFleetsyncAlias(ident); - return sb.toString(); + if(alias != null) + { + sb.append(" "); + sb.append(alias.getName()); + } + } + + sb.append("\n"); + } + } + + sb.append("\nFleetsync Emergency Activations\n"); + + if(mEmergencyIdents.isEmpty()) + { + sb.append(" None\n"); + } + else + { + Iterator it = mEmergencyIdents.iterator(); + + while(it.hasNext()) + { + String ident = it.next(); + + sb.append(" "); + sb.append(formatIdent(ident)); + + if(hasAliasList()) + { + Alias alias = getAliasList().getFleetsyncAlias(ident); + + if(alias != null) + { + sb.append(" "); + sb.append(alias.getName()); + } + + sb.append("\n"); + } + } + } + + return sb.toString(); } - public static String formatIdent( String ident ) - { - StringBuilder sb = new StringBuilder(); - - if( ident.length() == 7 ) - { - sb.append( ident.substring( 0, 3 ) ); - sb.append( "-" ); - sb.append( ident.substring( 3, 7 ) ); + public static String formatIdent(String ident) + { + StringBuilder sb = new StringBuilder(); - return sb.toString(); - } - else - { - return ident; - } - } + if(ident.length() == 7) + { + sb.append(ident.substring(0, 3)); + sb.append("-"); + sb.append(ident.substring(3, 7)); + + return sb.toString(); + } + else + { + return ident; + } + } } From bc8873de018206d53e4af8e490dd7ae88c74d8e3 Mon Sep 17 00:00:00 2001 From: Denny Date: Tue, 17 Jan 2017 19:24:54 -0500 Subject: [PATCH 03/10] #90 [WIP] merged metadata management for channel state, decoder states and audio metadata into a new metadata class. The new metadata class is maintained for each decoding audio channel, or in the future, a decoding TDMA channel. Audio playback, recording and streaming all use the same metadata object for the channel. The list of channel processing gui panels have been deleted and channel processing status will now reside in a table where each table row is a metadata instance from the processing channel. The channel processing manager is responsible for adding/removing the channel's metadata instance to/from the metadata table. This allows the channel processing table to be the controller for the messages, events and channel spectrum views, keyed to the selected table row metadata. I still have to clean up the gui components and wire in the table selection listeners. --- .idea/dictionaries/denny.xml | 1 + src/alias/AliasList.java | 207 ++- src/audio/AudioChannelPanel.java | 70 +- src/audio/AudioManager.java | 988 +++++++------- src/audio/AudioModule.java | 138 +- src/audio/AudioPacket.java | 139 +- src/audio/broadcast/AudioBroadcaster.java | 22 +- src/audio/broadcast/AudioRecording.java | 17 +- src/audio/broadcast/BroadcastModel.java | 51 +- .../broadcast/IBroadcastMetadataUpdater.java | 4 +- src/audio/broadcast/StreamManager.java | 46 +- .../IcecastBroadcastMetadataUpdater.java | 69 +- .../ShoutcastV1BroadcastMetadataUpdater.java | 66 +- .../v2/ShoutcastV2AudioBroadcaster.java | 66 +- src/audio/metadata/AudioMetadata.java | 357 ----- src/audio/metadata/IMetadataListener.java | 8 - src/audio/metadata/IMetadataProvider.java | 9 - src/audio/metadata/Metadata.java | 162 --- src/audio/metadata/MetadataReset.java | 14 - src/audio/metadata/MetadataType.java | 40 - src/audio/output/AudioOutput.java | 805 ++++++----- src/channel/metadata/Attribute.java | 29 +- .../metadata/AttributeChangeRequest.java | 20 + .../metadata/ChannelMetadataModel.java | 2 +- .../metadata/ChannelMetadataViewer.java | 66 +- src/channel/metadata/Metadata.java | 4 +- src/channel/metadata/MutableMetadata.java | 32 +- src/channel/state/ChannelList.java | 317 ----- src/channel/state/ChannelPanel.java | 466 ------- src/channel/state/ChannelState.java | 1199 ++++++++--------- src/channel/state/DecoderPanel.java | 163 --- src/channel/state/DecoderState.java | 4 +- .../state/IChangedAttributeProvider.java | 10 - src/controller/ControllerPanel.java | 139 +- .../channel/ChannelProcessingManager.java | 148 +- src/instrument/gui/DecoderViewFrame.java | 603 +++++---- src/module/ProcessingChain.java | 172 +-- src/module/decode/DecoderFactory.java | 141 +- src/module/decode/am/AMDecoderPanel.java | 84 -- src/module/decode/event/CallEvent.java | 597 ++++---- .../decode/event/MessageActivityPanel.java | 526 ++++---- .../fleetsync2/Fleetsync2DecoderPanel.java | 201 --- .../fleetsync2/Fleetsync2DecoderState.java | 78 +- .../decode/lj1200/LJ1200DecoderPanel.java | 127 -- .../decode/lj1200/LJ1200DecoderState.java | 290 ++-- .../decode/ltrnet/LTRNetDecoderPanel.java | 157 --- .../decode/ltrnet/LTRNetDecoderState.java | 436 +++--- .../ltrstandard/LTRStandardDecoderPanel.java | 164 --- .../ltrstandard/LTRStandardDecoderState.java | 908 +++++++------ .../decode/mdc1200/MDCDecoderPanel.java | 183 --- .../decode/mdc1200/MDCDecoderState.java | 581 ++++---- .../decode/mpt1327/MPT1327DecoderPanel.java | 200 --- .../decode/mpt1327/MPT1327DecoderState.java | 1132 ++++++++-------- .../decode/mpt1327/MPT1327TrafficPanel.java | 179 --- src/module/decode/nbfm/NBFMDecoderPanel.java | 80 -- src/module/decode/p25/P25DecoderPanel.java | 226 ---- src/module/decode/p25/P25DecoderState.java | 124 +- .../decode/p25/audio/P25AudioModule.java | 86 +- .../decode/passport/PassportDecoderPanel.java | 225 ---- .../decode/passport/PassportDecoderState.java | 1143 ++++++++-------- .../decode/tait/Tait1200DecoderPanel.java | 180 --- .../decode/tait/Tait1200DecoderState.java | 500 +++---- src/playlist/version1/AliasListOld.java | 1005 +++++++------- src/record/AudioRecorder.java | 34 +- src/record/RecorderManager.java | 44 +- .../wave/AudioPacketMonoWaveReader.java | 9 +- src/util/StringUtils.java | 41 + 67 files changed, 6207 insertions(+), 10127 deletions(-) delete mode 100644 src/audio/metadata/AudioMetadata.java delete mode 100644 src/audio/metadata/IMetadataListener.java delete mode 100644 src/audio/metadata/IMetadataProvider.java delete mode 100644 src/audio/metadata/Metadata.java delete mode 100644 src/audio/metadata/MetadataReset.java delete mode 100644 src/audio/metadata/MetadataType.java delete mode 100644 src/channel/state/ChannelList.java delete mode 100644 src/channel/state/ChannelPanel.java delete mode 100644 src/channel/state/DecoderPanel.java delete mode 100644 src/channel/state/IChangedAttributeProvider.java delete mode 100644 src/module/decode/am/AMDecoderPanel.java delete mode 100644 src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java delete mode 100644 src/module/decode/lj1200/LJ1200DecoderPanel.java delete mode 100644 src/module/decode/ltrnet/LTRNetDecoderPanel.java delete mode 100644 src/module/decode/ltrstandard/LTRStandardDecoderPanel.java delete mode 100644 src/module/decode/mdc1200/MDCDecoderPanel.java delete mode 100644 src/module/decode/mpt1327/MPT1327DecoderPanel.java delete mode 100644 src/module/decode/mpt1327/MPT1327TrafficPanel.java delete mode 100644 src/module/decode/nbfm/NBFMDecoderPanel.java delete mode 100644 src/module/decode/p25/P25DecoderPanel.java delete mode 100644 src/module/decode/passport/PassportDecoderPanel.java delete mode 100644 src/module/decode/tait/Tait1200DecoderPanel.java create mode 100644 src/util/StringUtils.java diff --git a/.idea/dictionaries/denny.xml b/.idea/dictionaries/denny.xml index 408050c7..24e59925 100644 --- a/.idea/dictionaries/denny.xml +++ b/.idea/dictionaries/denny.xml @@ -8,6 +8,7 @@ icecast ident polyphase + remez samplerate sdrtrunk sheirer diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index cc1c4662..10c3e214 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -1,6 +1,6 @@ /******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * 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 @@ -14,6 +14,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see + * ******************************************************************************/ package alias; @@ -29,7 +30,6 @@ import alias.id.siteID.SiteID; import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; -import audio.metadata.MetadataType; import module.decode.lj1200.LJ1200Message.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,16 +48,16 @@ public class AliasList implements Listener public static final String WILDCARD = "*"; public static final String REGEX_WILDCARD = "."; - private Map mESN = new HashMap<>(); - private Map mFleetsync = new HashMap<>(); - private Map mLoJack = new HashMap<>(); - private Map mMDC1200 = new HashMap<>(); - private Map mMobileID = new HashMap<>(); - private Map mMPT1327 = new HashMap<>(); - private Map mSiteID = new HashMap<>(); - private Map mStatus = new HashMap<>(); - private Map mTalkgroup = new HashMap<>(); - private Map mUniqueID = new HashMap<>(); + private Map mESN = new HashMap<>(); + private Map mFleetsync = new HashMap<>(); + private Map mLoJack = new HashMap<>(); + private Map mMDC1200 = new HashMap<>(); + private Map mMobileID = new HashMap<>(); + private Map mMPT1327 = new HashMap<>(); + private Map mSiteID = new HashMap<>(); + private Map mStatus = new HashMap<>(); + private Map mTalkgroup = new HashMap<>(); + private Map mUniqueID = new HashMap<>(); private List mESNWildcards = new ArrayList<>(); private List mMobileIDWildcards = new ArrayList<>(); @@ -85,9 +85,9 @@ public class AliasList implements Listener */ public void addAlias(Alias alias) { - if (alias != null) + if(alias != null) { - for (AliasID aliasID : alias.getId()) + for(AliasID aliasID : alias.getId()) { addAliasID(aliasID, alias); } @@ -99,18 +99,18 @@ public class AliasList implements Listener */ private void addAliasID(AliasID id, Alias alias) { - if (id.isValid()) + if(id.isValid()) { try { - switch (id.getType()) + switch(id.getType()) { case ESN: - String esn = ((Esn)id).getEsn(); + String esn = ((Esn) id).getEsn(); if(esn != null) { - if (esn.contains(WILDCARD)) + if(esn.contains(WILDCARD)) { mESNWildcards.add(new WildcardID(esn)); Collections.sort(mESNWildcards); @@ -120,11 +120,11 @@ public class AliasList implements Listener } break; case FLEETSYNC: - String fleetsync = ((FleetsyncID)id).getIdent(); + String fleetsync = ((FleetsyncID) id).getIdent(); if(fleetsync != null) { - if (fleetsync.contains(WILDCARD)) + if(fleetsync.contains(WILDCARD)) { mFleetsyncWildcards.add(new WildcardID(fleetsync)); Collections.sort(mFleetsyncWildcards); @@ -137,11 +137,11 @@ public class AliasList implements Listener mLoJack.put((LoJackFunctionAndID) id, alias); break; case MDC1200: - String mdc = ((MDC1200ID)id).getIdent(); + String mdc = ((MDC1200ID) id).getIdent(); if(mdc != null) { - if (mdc.contains(WILDCARD)) + if(mdc.contains(WILDCARD)) { mMDC1200Wildcards.add(new WildcardID(mdc)); Collections.sort(mMDC1200Wildcards); @@ -151,11 +151,11 @@ public class AliasList implements Listener } break; case MPT1327: - String mpt = ((MPT1327ID)id).getIdent(); + String mpt = ((MPT1327ID) id).getIdent(); if(mpt != null) { - if (mpt.contains(WILDCARD)) + if(mpt.contains(WILDCARD)) { mMPT1327Wildcards.add(new WildcardID(mpt)); Collections.sort(mMPT1327Wildcards); @@ -165,11 +165,11 @@ public class AliasList implements Listener } break; case MIN: - String min = ((Min)id).getMin(); + String min = ((Min) id).getMin(); if(min != null) { - if (min.contains(WILDCARD)) + if(min.contains(WILDCARD)) { mMobileIDWildcards.add(new WildcardID(min)); Collections.sort(mMobileIDWildcards); @@ -179,14 +179,14 @@ public class AliasList implements Listener } break; case LTR_NET_UID: - mUniqueID.put(((UniqueID)id).getUid(), alias); + mUniqueID.put(((UniqueID) id).getUid(), alias); break; case SITE: - String siteID = ((SiteID)id).getSite(); + String siteID = ((SiteID) id).getSite(); if(siteID != null) { - if (siteID.contains(WILDCARD)) + if(siteID.contains(WILDCARD)) { mSiteWildcards.add(new WildcardID(siteID)); Collections.sort(mSiteWildcards); @@ -199,11 +199,11 @@ public class AliasList implements Listener mStatus.put(((StatusID) id).getStatus(), alias); break; case TALKGROUP: - String tgid = ((TalkgroupID)id).getTalkgroup(); + String tgid = ((TalkgroupID) id).getTalkgroup(); if(tgid != null) { - if (tgid.contains(WILDCARD)) + if(tgid.contains(WILDCARD)) { mTalkgroupWildcards.add(new WildcardID(tgid)); Collections.sort(mTalkgroupWildcards); @@ -222,7 +222,7 @@ public class AliasList implements Listener break; } } - catch (Exception e) + catch(Exception e) { mLog.error("Couldn't add alias ID " + id + " for alias " + alias); } @@ -234,9 +234,9 @@ public class AliasList implements Listener */ public void removeAlias(Alias alias) { - if (alias != null) + if(alias != null) { - for (AliasID aliasID : alias.getId()) + for(AliasID aliasID : alias.getId()) { removeAliasID(aliasID, alias); } @@ -247,7 +247,7 @@ public class AliasList implements Listener { if(value != null) { - for(WildcardID wildcardID: wildcards) + for(WildcardID wildcardID : wildcards) { if(wildcardID.value().equals(value)) { @@ -262,12 +262,12 @@ public class AliasList implements Listener */ private void removeAliasID(AliasID id, Alias alias) { - if (id.isValid()) + if(id.isValid()) { - switch (id.getType()) + switch(id.getType()) { case ESN: - String esn = ((Esn)id).getEsn(); + String esn = ((Esn) id).getEsn(); if(esn != null) { @@ -280,13 +280,13 @@ public class AliasList implements Listener mESN.remove(esn); break; case FLEETSYNC: - String fleetsync = ((FleetsyncID)id).getIdent(); + String fleetsync = ((FleetsyncID) id).getIdent(); if(fleetsync != null) { if(fleetsync.contains(WILDCARD)) { - removeWildcard(fleetsync,mFleetsyncWildcards); + removeWildcard(fleetsync, mFleetsyncWildcards); } mFleetsync.remove(fleetsync); @@ -296,7 +296,7 @@ public class AliasList implements Listener mLoJack.remove((LoJackFunctionAndID) id); break; case MDC1200: - String mdc = ((MDC1200ID)id).getIdent(); + String mdc = ((MDC1200ID) id).getIdent(); if(mdc != null) { @@ -309,7 +309,7 @@ public class AliasList implements Listener } break; case MPT1327: - String mpt = ((MPT1327ID)id).getIdent(); + String mpt = ((MPT1327ID) id).getIdent(); if(mpt != null) { @@ -322,7 +322,7 @@ public class AliasList implements Listener } break; case MIN: - String min = ((Min)id).getMin(); + String min = ((Min) id).getMin(); if(min != null) { @@ -344,7 +344,7 @@ public class AliasList implements Listener mStatus.remove(((StatusID) id).getStatus()); break; case TALKGROUP: - String tgid = ((TalkgroupID)id).getTalkgroup(); + String tgid = ((TalkgroupID) id).getTalkgroup(); if(tgid != null) { @@ -378,11 +378,11 @@ public class AliasList implements Listener */ private String getWildcardMatch(String id, List wildcards) { - if (id != null) + if(id != null) { - for (WildcardID wildcard : wildcards) + for(WildcardID wildcard : wildcards) { - if (wildcard.matches(id)) + if(wildcard.matches(id)) { return wildcard.value(); } @@ -399,15 +399,15 @@ public class AliasList implements Listener { Alias alias = null; - if (siteID != null) + if(siteID != null) { alias = mSiteID.get(siteID); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(siteID, mSiteWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mSiteID.get(wildcard); } @@ -440,15 +440,15 @@ public class AliasList implements Listener { Alias alias = null; - if (esn != null) + if(esn != null) { alias = mESN.get(esn); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(esn, mESNWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mESN.get(wildcard); } @@ -465,15 +465,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mFleetsync.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mFleetsyncWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mFleetsync.get(wildcard); } @@ -488,11 +488,11 @@ public class AliasList implements Listener */ public Alias getLoJackAlias(Function function, String id) { - if (id != null) + if(id != null) { - for (LoJackFunctionAndID lojack : mLoJack.keySet()) + for(LoJackFunctionAndID lojack : mLoJack.keySet()) { - if (lojack.matches(function, id)) + if(lojack.matches(function, id)) { return mLoJack.get(lojack); } @@ -509,15 +509,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMDC1200.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMDC1200Wildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMDC1200.get(wildcard); } @@ -534,15 +534,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMPT1327.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMPT1327Wildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMPT1327.get(wildcard); } @@ -559,15 +559,15 @@ public class AliasList implements Listener { Alias alias = null; - if (ident != null) + if(ident != null) { alias = mMobileID.get(ident); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(ident, mMobileIDWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mMobileID.get(wildcard); } @@ -584,15 +584,15 @@ public class AliasList implements Listener { Alias alias = null; - if (tgid != null) + if(tgid != null) { alias = mTalkgroup.get(tgid); - if (alias == null) + if(alias == null) { String wildcard = getWildcardMatch(tgid, mTalkgroupWildcards); - if (wildcard != null) + if(wildcard != null) { alias = mTalkgroup.get(wildcard); } @@ -619,61 +619,6 @@ public class AliasList implements Listener return mName; } - /** - * Lookup an alias by metadata type and string value - */ - public Alias getAlias(String value, MetadataType type) - { - switch (type) - { - case ESN: - return getESNAlias(value); - case FLEETSYNC: - return getFleetsyncAlias(value); - case MDC1200: - return getMDC1200Alias(value); - case MOBILE_ID: - return getMobileIDNumberAlias(value); - case MPT1327: - return getMPT1327Alias(value); - case SITE_ID: - return getSiteID(value); - case STATUS: - if (value != null) - { - try - { - return getStatus(Integer.valueOf(value)); - } - catch (Exception e) - { - //do nothing, we couldn't parse the int value - } - } - break; - case FROM: - case TO: - return getTalkgroupAlias(value); - case UNIQUE_ID: - if (value != null) - { - try - { - return getUniqueID(Integer.valueOf(value)); - } - catch (Exception e) - { - //do nothing, we couldn't parse the int value - } - } - break; - default: - break; - } - - return null; - } - /** * Receive alias change event notifications and modify this list accordingly */ @@ -682,22 +627,22 @@ public class AliasList implements Listener { Alias alias = event.getAlias(); - switch (event.getEvent()) + switch(event.getEvent()) { case ADD: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { addAlias(alias); } break; case CHANGE: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { addAlias(alias); } break; case DELETE: - if (alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) + if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList())) { removeAlias(alias); } diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java index 72f4a091..5532fdb7 100644 --- a/src/audio/AudioChannelPanel.java +++ b/src/audio/AudioChannelPanel.java @@ -19,10 +19,8 @@ package audio; import alias.Alias; -import audio.metadata.AudioMetadata; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; import audio.output.AudioOutput; +import channel.metadata.Metadata; import icon.IconManager; import net.miginfocom.swing.MigLayout; import sample.Listener; @@ -50,12 +48,12 @@ public class AudioChannelPanel extends JPanel private JLabel mChannelName = new JLabel(" "); private JLabel mToLabel = new JLabel("TO:"); - private JLabel mTo = new JLabel(""); + private JLabel mTo = new JLabel("-----"); private JLabel mToAlias = new JLabel(""); private JLabel mMutedLabel = new JLabel(" "); private JLabel mFromLabel = new JLabel("FROM:"); - private JLabel mFrom = new JLabel(""); + private JLabel mFrom = new JLabel("-----"); private JLabel mFromAlias = new JLabel(""); private boolean mConfigured = false; @@ -71,7 +69,7 @@ public class AudioChannelPanel extends JPanel if(mAudioOutput != null) { mAudioOutput.addAudioEventListener(this); - mAudioOutput.setAudioMetadataListener(new AudioMetadataProcessor()); + mAudioOutput.setMetadataListener(new AudioMetadataProcessor()); } mLabelColor = mSettingsManager.getColorSetting( @@ -173,11 +171,8 @@ public class AudioChannelPanel extends JPanel */ private void resetLabels() { - mFrom.setText(""); - updateAlias(mFromAlias, null); - - mTo.setText(""); - updateAlias(mToAlias, null); + updateLabel(mFromLabel, null, mFromAlias, null); + updateLabel(mToLabel, null, mToAlias, null); mConfigured = false; } @@ -187,27 +182,25 @@ public class AudioChannelPanel extends JPanel * does not occur on the Swing event thread -- wrap any calls to this * method with an event thread call. */ - private void updateAlias(JLabel label, Alias alias) + private void updateLabel(JLabel valueLabel, String value, JLabel aliasLabel, Alias alias) { - if(alias != null) + if(value != null) { - label.setText(alias.getName()); + valueLabel.setText(value); - String icon = alias.getIconName(); - - if(icon != null) + if(alias != null) { - label.setIcon(mIconManager.getIcon(icon, IconManager.DEFAULT_ICON_SIZE)); + aliasLabel.setIcon(mIconManager.getIcon(alias.getIconName(), IconManager.DEFAULT_ICON_SIZE)); } else { - label.setIcon(null); + aliasLabel.setIcon(null); } } else { - label.setText(""); - label.setIcon(null); + valueLabel.setText("-----"); + aliasLabel.setIcon(null); } } @@ -215,45 +208,28 @@ public class AudioChannelPanel extends JPanel /** * Processes audio metadata to update this panel's display values */ - public class AudioMetadataProcessor implements Listener + public class AudioMetadataProcessor implements Listener { @Override - public void receive(AudioMetadata audioMetadata) + public void receive(final Metadata metadata) { - if(!mConfigured || audioMetadata.isUpdated()) + if(metadata.isUpdated() || !mConfigured) { - final Metadata from = audioMetadata.getMetadata(MetadataType.FROM); - - final Metadata to = audioMetadata.getMetadata(MetadataType.TO); - EventQueue.invokeLater(new Runnable() { @Override public void run() { - if(from != null) - { - mFrom.setText(from.getValue()); - updateAlias(mFromAlias, from.getAlias()); - } - else - { - mFrom.setText("-----"); - } + updateLabel(mFromLabel, metadata.getPrimaryAddressFrom().getIdentifier(), + mFromAlias, metadata.getPrimaryAddressFrom().getAlias()); - if(to != null) - { - mTo.setText(to.getValue()); - updateAlias(mToAlias, to.getAlias()); - } - else - { - mTo.setText("-----"); - } + updateLabel(mToLabel, metadata.getPrimaryAddressTo().getIdentifier(), + mToAlias, metadata.getPrimaryAddressTo().getAlias()); + + mConfigured = true; } }); - mConfigured = true; } } } diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index fbc7e7b1..6be25898 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -1,22 +1,40 @@ /******************************************************************************* - * 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 + * 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 + * ******************************************************************************/ package audio; +import audio.AudioEvent.Type; +import audio.output.AudioOutput; +import audio.output.MonoAudioOutput; +import audio.output.StereoAudioOutput; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import properties.SystemProperties; +import sample.Broadcaster; +import sample.Listener; +import source.mixer.MixerChannel; +import source.mixer.MixerChannelConfiguration; +import source.mixer.MixerManager; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Mixer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -27,523 +45,495 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Mixer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import properties.SystemProperties; -import sample.Broadcaster; -import sample.Listener; -import source.mixer.MixerChannel; -import source.mixer.MixerChannelConfiguration; -import source.mixer.MixerManager; -import audio.AudioEvent.Type; -import audio.output.AudioOutput; -import audio.output.MonoAudioOutput; -import audio.output.StereoAudioOutput; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; - public class AudioManager implements Listener, IAudioController { - private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); + private static final Logger mLog = LoggerFactory.getLogger(AudioManager.class); - public static final int AUDIO_TIMEOUT = 1000; //1 second - - public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; - public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; - - public static final AudioEvent CONFIGURATION_CHANGE_STARTED = - new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null ); - - public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE = - new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null ); + public static final int AUDIO_TIMEOUT = 1000; //1 second - private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); - private Map mChannelConnectionMap = new HashMap<>(); - private List mAudioOutputConnections = new ArrayList<>(); - private AudioOutputConnection mLowestPriorityConnection; - private int mAvailableConnectionCount; + public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; + public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; - private Map mAudioOutputMap = new HashMap<>(); - - private Broadcaster mControllerBroadcaster = new Broadcaster<>(); - - private ThreadPoolManager mThreadPoolManager; - private ScheduledFuture mProcessingTask; - private MixerManager mMixerManager; - private MixerChannelConfiguration mMixerChannelConfiguration; - + public static final AudioEvent CONFIGURATION_CHANGE_STARTED = + new AudioEvent(Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null); - /** - * Processes all audio produced by the decoding channels and routes audio - * packets to any combination of outputs based on any alias audio routing - * options specified by the user. - */ - public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) - { - mThreadPoolManager = manager; - mMixerManager = mixerManager; + public static final AudioEvent CONFIGURATION_CHANGE_COMPLETE = + new AudioEvent(Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null); - loadSettings(); - } - - private void loadSettings() - { - MixerChannelConfiguration configuration = null; - - SystemProperties properties = SystemProperties.getInstance(); - - Mixer defaultMixer = AudioSystem.getMixer( null ); + private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); + private Map mChannelConnectionMap = new HashMap<>(); + private List mAudioOutputConnections = new ArrayList<>(); + private AudioOutputConnection mLowestPriorityConnection; + private int mAvailableConnectionCount; - String mixer = properties.get( AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName() ); - - String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); - - MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); + private Map mAudioOutputMap = new HashMap<>(); - for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) - { - if( mixerConfig.matches( mixer, channels ) ) - { - configuration = mixerConfig; - } - } - - if( configuration == null ) - { - configuration = getDefaultConfiguration(); - } + private Broadcaster mControllerBroadcaster = new Broadcaster<>(); - try - { - setMixerChannelConfiguration( configuration ); - } - catch( Exception e ) - { - mLog.error( "Couldn't set stored audio mixer/channel configuration" - + " - using default", e ); - - try - { - setMixerChannelConfiguration( getDefaultConfiguration() ); - } - catch( Exception e2 ) - { - mLog.error( "Couldn't set default audio mixer/channel " - + "configuration - no audio will be available", e2 ); - } - } - } - - private MixerChannelConfiguration getDefaultConfiguration() - { - /* Use the system default mixer and mono channel as default startup */ - Mixer defaultMixer = AudioSystem.getMixer( null ); - - return new MixerChannelConfiguration( defaultMixer, MixerChannel.MONO ); - } - - public void dispose() - { - if( mThreadPoolManager != null && mProcessingTask != null ) - { - mThreadPoolManager.cancel( mProcessingTask ); - } - - mAudioPacketQueue.clear(); - - mThreadPoolManager = null; - mProcessingTask = null; + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mProcessingTask; + private MixerManager mMixerManager; + private MixerChannelConfiguration mMixerChannelConfiguration; - mChannelConnectionMap.clear(); - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - connection.dispose(); - } - - mAudioOutputConnections.clear(); - } - @Override - public synchronized void receive( AudioPacket packet ) - { - mAudioPacketQueue.add( packet ); - } - - /** - * Checks each audio channel assignment and disconnects any inactive connections - */ - private void disconnectInactiveChannelAssignments() - { - boolean changed = false; - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isInactive() && - mChannelConnectionMap.containsKey( connection.getSource() ) ) - { - mChannelConnectionMap.remove( connection.getSource() ); - connection.disconnect(); - changed = true; - } - } - - if( changed ) - { - updateLowestPriorityAssignment(); - } - } + /** + * Processes all audio produced by the decoding channels and routes audio + * packets to any combination of outputs based on any alias audio routing + * options specified by the user. + */ + public AudioManager(ThreadPoolManager manager, MixerManager mixerManager) + { + mThreadPoolManager = manager; + mMixerManager = mixerManager; - /** - * Identifies the lowest priority channel connection - */ - private void updateLowestPriorityAssignment() - { - mLowestPriorityConnection = null; - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isConnected() && - ( mLowestPriorityConnection == null || - mLowestPriorityConnection.getPriority() > connection.getPriority() ) ) - { - mLowestPriorityConnection = connection; - } - } - } - - @Override - public void setMixerChannelConfiguration( MixerChannelConfiguration entry ) throws AudioException - { - if( entry != null && - ( entry.getMixerChannel() == MixerChannel.MONO || - entry.getMixerChannel() == MixerChannel.STEREO ) ) - { - mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_STARTED ); - - if( mThreadPoolManager != null && mProcessingTask != null ) - { - mThreadPoolManager.cancel( mProcessingTask ); - } - - disposeCurrentConfiguration(); - - switch( entry.getMixerChannel() ) - { - case MONO: - AudioOutput mono = new MonoAudioOutput( mThreadPoolManager, - entry.getMixer() ); - mAudioOutputConnections.add( new AudioOutputConnection( mono ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( mono.getChannelName(), mono ); - break; - case STEREO: - AudioOutput left = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.LEFT ); - mAudioOutputConnections.add( new AudioOutputConnection( left ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( left.getChannelName(), left ); - - AudioOutput right = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.RIGHT ); - mAudioOutputConnections.add( new AudioOutputConnection( right ) ); - mAvailableConnectionCount++; - mAudioOutputMap.put( right.getChannelName(), right ); - break; - default: - throw new AudioException( "Unsupported mixer channel " - + "configuration: " + entry.getMixerChannel() ); - } + loadSettings(); + } - mProcessingTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, - TimeUnit.MILLISECONDS ); + private void loadSettings() + { + MixerChannelConfiguration configuration = null; - mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_COMPLETE ); - - SystemProperties properties = SystemProperties.getInstance(); - - properties.set( AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName() ); - properties.set( AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name() ); - properties.save(); - } - } + SystemProperties properties = SystemProperties.getInstance(); - /** - * Clears all channel assignments and terminates all audio outputs in - * preparation for complete shutdown or change to another mixer/channel - * configuration - */ - private void disposeCurrentConfiguration() - { - mChannelConnectionMap.clear(); + Mixer defaultMixer = AudioSystem.getMixer(null); - for( AudioOutputConnection connection : mAudioOutputConnections ) - { - connection.dispose(); - } - - mAvailableConnectionCount = 0; + String mixer = properties.get(AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName()); - mAudioOutputConnections.clear(); - - mAudioOutputMap.clear(); - - mLowestPriorityConnection = null; - } + String channels = properties.get(AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name()); - @Override - public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException - { - return mMixerChannelConfiguration; - } + MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); - @Override - public List getAudioOutputs() - { - List outputs = new ArrayList<>( mAudioOutputMap.values() ); + for(MixerChannelConfiguration mixerConfig : mixerConfigurations) + { + if(mixerConfig.matches(mixer, channels)) + { + configuration = mixerConfig; + } + } - Collections.sort( outputs, new Comparator() - { - @Override - public int compare( AudioOutput first, AudioOutput second ) - { - return first.getChannelName().compareTo( second.getChannelName() ); - } - } ); - - return outputs; - } + if(configuration == null) + { + configuration = getDefaultConfiguration(); + } - @Override - public void addControllerListener( Listener listener ) - { - mControllerBroadcaster.addListener( listener ); - } + try + { + setMixerChannelConfiguration(configuration); + } + catch(Exception e) + { + mLog.error("Couldn't set stored audio mixer/channel configuration" + + " - using default", e); - @Override - public void removeControllerListener( Listener listener ) - { - mControllerBroadcaster.removeListener( listener ); - } - - /** - * Returns an audio output connection for the packet if one is - * available, or overrides an existing lower priority connection. - * Returns null if no connection is available for the audio packet. - * - * @param packet from a source - * @return an audio output connection or null - */ - private AudioOutputConnection getConnection( AudioPacket packet ) - { - int source = packet.getAudioMetadata().getSource(); - - //Use an existing connection - if( mChannelConnectionMap.containsKey( source ) ) - { - return mChannelConnectionMap.get( source ); - } + try + { + setMixerChannelConfiguration(getDefaultConfiguration()); + } + catch(Exception e2) + { + mLog.error("Couldn't set default audio mixer/channel " + + "configuration - no audio will be available", e2); + } + } + } - //Connect to and use an available connection - if( mAvailableConnectionCount > 0 ) - { - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isDisconnected() ) - { - connection.connect( source ); - mChannelConnectionMap.put( source, connection ); - return connection; - } - } - } - //Preempt an existing connection and connect it to this higher priority source - else //Check for a channel priority override - { - int priority = packet.getAudioMetadata().getPriority(); + private MixerChannelConfiguration getDefaultConfiguration() + { + /* Use the system default mixer and mono channel as default startup */ + Mixer defaultMixer = AudioSystem.getMixer(null); - if( mLowestPriorityConnection != null && - priority < mLowestPriorityConnection.getPriority() ) - { - AudioOutputConnection connection = mLowestPriorityConnection; + return new MixerChannelConfiguration(defaultMixer, MixerChannel.MONO); + } - mChannelConnectionMap.remove( connection.getSource() ); - - connection.connect( source ); - - mChannelConnectionMap.put( source, connection ); - - updateLowestPriorityAssignment(); - - return connection; - } - } - - return null; - } - - public class AudioPacketProcessor implements Runnable - { - @Override - public void run() - { - try - { - disconnectInactiveChannelAssignments(); - - if( mAudioPacketQueue != null ) - { - List packets = new ArrayList(); + public void dispose() + { + if(mThreadPoolManager != null && mProcessingTask != null) + { + mThreadPoolManager.cancel(mProcessingTask); + } - mAudioPacketQueue.drainTo( packets ); - - for( AudioPacket packet: packets ) - { + mAudioPacketQueue.clear(); + + mThreadPoolManager = null; + mProcessingTask = null; + + mChannelConnectionMap.clear(); + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + connection.dispose(); + } + + mAudioOutputConnections.clear(); + } + + @Override + public synchronized void receive(AudioPacket packet) + { + mAudioPacketQueue.add(packet); + } + + /** + * Checks each audio channel assignment and disconnects any inactive connections + */ + private void disconnectInactiveChannelAssignments() + { + boolean changed = false; + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isInactive() && + mChannelConnectionMap.containsKey(connection.getChannelMetadataID())) + { + mChannelConnectionMap.remove(connection.getChannelMetadataID()); + connection.disconnect(); + changed = true; + } + } + + if(changed) + { + updateLowestPriorityAssignment(); + } + } + + /** + * Identifies the lowest priority channel connection + */ + private void updateLowestPriorityAssignment() + { + mLowestPriorityConnection = null; + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isConnected() && + (mLowestPriorityConnection == null || mLowestPriorityConnection.getPriority() > connection.getPriority())) + { + mLowestPriorityConnection = connection; + } + } + } + + @Override + public void setMixerChannelConfiguration(MixerChannelConfiguration entry) throws AudioException + { + if(entry != null && + (entry.getMixerChannel() == MixerChannel.MONO || entry.getMixerChannel() == MixerChannel.STEREO)) + { + mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_STARTED); + + if(mThreadPoolManager != null && mProcessingTask != null) + { + mThreadPoolManager.cancel(mProcessingTask); + } + + disposeCurrentConfiguration(); + + switch(entry.getMixerChannel()) + { + case MONO: + AudioOutput mono = new MonoAudioOutput(mThreadPoolManager, + entry.getMixer()); + mAudioOutputConnections.add(new AudioOutputConnection(mono)); + mAvailableConnectionCount++; + mAudioOutputMap.put(mono.getChannelName(), mono); + break; + case STEREO: + AudioOutput left = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.LEFT); + mAudioOutputConnections.add(new AudioOutputConnection(left)); + mAvailableConnectionCount++; + mAudioOutputMap.put(left.getChannelName(), left); + + AudioOutput right = new StereoAudioOutput(mThreadPoolManager, entry.getMixer(), MixerChannel.RIGHT); + mAudioOutputConnections.add(new AudioOutputConnection(right)); + mAvailableConnectionCount++; + mAudioOutputMap.put(right.getChannelName(), right); + break; + default: + throw new AudioException("Unsupported mixer channel " + + "configuration: " + entry.getMixerChannel()); + } + + mProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, + TimeUnit.MILLISECONDS); + + mControllerBroadcaster.broadcast(CONFIGURATION_CHANGE_COMPLETE); + + SystemProperties properties = SystemProperties.getInstance(); + + properties.set(AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName()); + properties.set(AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name()); + properties.save(); + } + } + + /** + * Clears all channel assignments and terminates all audio outputs in + * preparation for complete shutdown or change to another mixer/channel + * configuration + */ + private void disposeCurrentConfiguration() + { + mChannelConnectionMap.clear(); + + for(AudioOutputConnection connection : mAudioOutputConnections) + { + connection.dispose(); + } + + mAvailableConnectionCount = 0; + + mAudioOutputConnections.clear(); + + mAudioOutputMap.clear(); + + mLowestPriorityConnection = null; + } + + @Override + public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException + { + return mMixerChannelConfiguration; + } + + @Override + public List getAudioOutputs() + { + List outputs = new ArrayList<>(mAudioOutputMap.values()); + + Collections.sort(outputs, new Comparator() + { + @Override + public int compare(AudioOutput first, AudioOutput second) + { + return first.getChannelName().compareTo(second.getChannelName()); + } + }); + + return outputs; + } + + @Override + public void addControllerListener(Listener listener) + { + mControllerBroadcaster.addListener(listener); + } + + @Override + public void removeControllerListener(Listener listener) + { + mControllerBroadcaster.removeListener(listener); + } + + /** + * Returns an audio output connection for the packet if one is + * available, or overrides an existing lower priority connection. + * Returns null if no connection is available for the audio packet. + * + * @param packet from a source + * @return an audio output connection or null + */ + private AudioOutputConnection getConnection(AudioPacket packet) + { + int channelMetadataID = packet.getMetadata().getMetadataID(); + + //Use an existing connection + if(mChannelConnectionMap.containsKey(channelMetadataID)) + { + return mChannelConnectionMap.get(channelMetadataID); + } + + //Connect to and use an available connection + if(mAvailableConnectionCount > 0) + { + for(AudioOutputConnection connection : mAudioOutputConnections) + { + if(connection.isDisconnected()) + { + connection.connect(channelMetadataID); + mChannelConnectionMap.put(channelMetadataID, connection); + return connection; + } + } + } + //Preempt an existing connection and connect it to this higher priority source + else //Check for a channel priority override + { + int priority = packet.getMetadata().getAudioPriority(); + + if(mLowestPriorityConnection != null && + priority < mLowestPriorityConnection.getPriority()) + { + AudioOutputConnection connection = mLowestPriorityConnection; + + mChannelConnectionMap.remove(connection.getChannelMetadataID()); + + connection.connect(channelMetadataID); + + mChannelConnectionMap.put(channelMetadataID, connection); + + updateLowestPriorityAssignment(); + + return connection; + } + } + + return null; + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + try + { + disconnectInactiveChannelAssignments(); + + if(mAudioPacketQueue != null) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo(packets); + + for(AudioPacket packet : packets) + { /* Don't process any packet's marked as do not monitor */ - if( !packet.getAudioMetadata().isDoNotMonitor() && - packet.getType() == AudioPacket.Type.AUDIO ) - { - AudioOutputConnection connection = getConnection( packet ); + if(!packet.getMetadata().isDoNotMonitor() && packet.getType() == AudioPacket.Type.AUDIO) + { + AudioOutputConnection connection = getConnection(packet); - if( connection != null ) - { - connection.receive( packet ); - } - } - } - } - } - catch( Exception e ) - { - mLog.error( "Encountered error while processing audio packets", e ); - } - } - } - - /** - * Audio output connection manages a connection between a source and an audio - * output and maintains current state information about the audio activity - * received form the source. - */ - public class AudioOutputConnection - { - private static final int DISCONNECTED = -1; - - private AudioOutput mAudioOutput; - private int mPriority = 0; - private int mSource = DISCONNECTED; - - public AudioOutputConnection( AudioOutput audioOutput ) - { - mAudioOutput = audioOutput; - } - - public void receive( AudioPacket packet ) - { - if( packet.hasAudioMetadata() && - packet.getAudioMetadata().getSource() == mSource ) - { - int priority = packet.getAudioMetadata().getPriority(); + if(connection != null) + { + connection.receive(packet); + } + } + } + } + } + catch(Exception e) + { + mLog.error("Encountered error while processing audio packets", e); + } + } + } - if( mPriority != priority ) - { - mPriority = priority; - updateLowestPriorityAssignment(); - } - - if( mAudioOutput != null ) - { - mAudioOutput.receive( packet ); - } - } - else - { - if( packet.hasAudioMetadata() ) - { - mLog.error( "Received audio packet from channel source [" + - packet.getAudioMetadata().getSource() + - "] however this assignment is currently connected " - + "to channel source [" + mSource + "]" ); - } - else - { - mLog.error( "Received audio packet with no metadata - " - + "cannot route audio packet" ); - } - } - } - - /** - * Terminates the audio output and prepares this connection for disposal - */ - public void dispose() - { - mAudioOutput.dispose(); - mAudioOutput = null; - } + /** + * Audio output connection manages a connection between a source and an audio + * output and maintains current state information about the audio activity + * received form the source. + */ + public class AudioOutputConnection + { + private static final int DISCONNECTED = -1; - /** - * Indicates if this assignment is currently disconnected from a channel source - */ - public boolean isDisconnected() - { - return mSource == DISCONNECTED; - } - - /** - * Indicates if this assignment is currently connected to a channel source - */ - public boolean isConnected() - { - return !isDisconnected(); - } + private AudioOutput mAudioOutput; + private int mPriority = 0; + private int mChannelMetadataID = DISCONNECTED; - /** - * Connects this assignment to the indicated source so that audio - * packets from this source can be sent to the audio output - */ - public void connect( int source ) - { - mSource = source; - - mAudioOutput.updateTimestamp(); - } + public AudioOutputConnection(AudioOutput audioOutput) + { + mAudioOutput = audioOutput; + } - /** - * Currently connected source or -1 if disconnected - */ - public int getSource() - { - return mSource; - } + public void receive(AudioPacket packet) + { + if(packet.hasMetadata() && packet.getMetadata().getMetadataID() == mChannelMetadataID) + { + int priority = packet.getMetadata().getAudioPriority(); - /** - * Disconnects this assignment from the source and prevents any audio - * from being routed to the audio output until another source is assigned - */ - public void disconnect() - { - mSource = DISCONNECTED; - mPriority = 0; - } + if(mPriority != priority) + { + mPriority = priority; + updateLowestPriorityAssignment(); + } - /** - * Indicates if audio output is current inactive, meaning that the - * audio output hasn't recently processed any audio packets. - */ - public boolean isInactive() - { - return ( mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT ) < System.currentTimeMillis(); - } - - public int getPriority() - { - return mPriority; - } - } + if(mAudioOutput != null) + { + mAudioOutput.receive(packet); + } + } + else + { + if(packet.hasMetadata()) + { + mLog.error("Received audio packet from channel metadata [" + packet.getMetadata().getMetadataID() + + "] however this assignment is currently connected to metadata [" + mChannelMetadataID + "]"); + } + else + { + mLog.error("Received audio packet with no metadata - cannot route audio packet"); + } + } + } + + /** + * Terminates the audio output and prepares this connection for disposal + */ + public void dispose() + { + mAudioOutput.dispose(); + mAudioOutput = null; + } + + /** + * Indicates if this assignment is currently disconnected from a channel source + */ + public boolean isDisconnected() + { + return mChannelMetadataID == DISCONNECTED; + } + + /** + * Indicates if this assignment is currently connected to a channel source + */ + public boolean isConnected() + { + return !isDisconnected(); + } + + /** + * Connects this assignment to the indicated source so that audio + * packets from this source can be sent to the audio output + */ + public void connect(int source) + { + mChannelMetadataID = source; + + mAudioOutput.updateTimestamp(); + } + + /** + * Currently connected source or -1 if disconnected + */ + public int getChannelMetadataID() + { + return mChannelMetadataID; + } + + /** + * Disconnects this assignment from the source and prevents any audio + * from being routed to the audio output until another source is assigned + */ + public void disconnect() + { + mChannelMetadataID = DISCONNECTED; + mPriority = 0; + } + + /** + * Indicates if audio output is current inactive, meaning that the + * audio output hasn't recently processed any audio packets. + */ + public boolean isInactive() + { + return (mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT) < System.currentTimeMillis(); + } + + public int getPriority() + { + return mPriority; + } + } } diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 4a02298f..58b0be0f 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -1,29 +1,38 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ package audio; -import java.util.Arrays; -import java.util.concurrent.ScheduledExecutorService; - +import audio.squelch.ISquelchStateListener; +import audio.squelch.SquelchState; +import channel.metadata.Metadata; import dsp.filter.design.FilterDesignException; import dsp.filter.fir.FIRFilterSpecification; import dsp.filter.fir.remez.RemezFIRFilterDesigner; import dsp.filter.polyphase.PolyphaseFIRDecimatingFilter_RB; import module.Module; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import sample.Listener; import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; -import audio.metadata.AudioMetadata; -import audio.metadata.IMetadataListener; -import audio.metadata.Metadata; -import audio.squelch.ISquelchStateListener; -import audio.squelch.SquelchState; -import controller.channel.ChannelEvent; -import controller.channel.IChannelEventListener; -import dsp.filter.Filters; -import dsp.filter.fir.real.RealFIRFilter_R_R; + +import java.util.concurrent.ScheduledExecutorService; /** * Provides packaging of demodulated audio sample buffers into audio packets for @@ -33,12 +42,8 @@ import dsp.filter.fir.real.RealFIRFilter_R_R; * Incorporates audio squelch state listener to control if audio packets are * broadcast or ignored. */ -public class AudioModule extends Module implements IAudioPacketProvider, - IChannelEventListener, - IMetadataListener, - IFilteredRealBufferListener, - ISquelchStateListener, - Listener +public class AudioModule extends Module implements IAudioPacketProvider, IFilteredRealBufferListener, + ISquelchStateListener, Listener { protected static final Logger mLog = LoggerFactory.getLogger(AudioModule.class); @@ -48,48 +53,41 @@ public class AudioModule extends Module implements IAudioPacketProvider, { //Band-pass filter to both decimate (48kHz to 8kHz) and block 0 - 300 Hz (LTR signalling). FIRFilterSpecification specification = FIRFilterSpecification.bandPassBuilder() - .sampleRate( 48000 ) - .stopFrequency1( 150 ) - .passFrequencyBegin( 300 ) - .passFrequencyEnd( 3500 ) - .stopFrequency2( 4000 ) - .stopRipple( 0.0004 ) - .passRipple( 0.008) - .build(); + .sampleRate(48000) + .stopFrequency1(150) + .passFrequencyBegin(300) + .passFrequencyEnd(3500) + .stopFrequency2(4000) + .stopRipple(0.0004) + .passRipple(0.008) + .build(); RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification); try { - if (!designer.isValid()) + if(!designer.isValid()) { throw new FilterDesignException("Couldn't design the audio decimation filter"); } mDecimationCoefficients = designer.getImpulseResponse(); } - catch (FilterDesignException e) + catch(FilterDesignException e) { mLog.debug("Error designing filter", e); } } - /* Provides a unique identifier for this audio module instance to use as a - * source identifier for all audio packets */ - private static int UNIQUE_ID = 0; - private int mSourceID; - - private AudioMetadata mAudioMetadata; - private ChannelEventListener mChannelEventListener = new ChannelEventListener(); private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchState mSquelchState = SquelchState.SQUELCH; private Listener mAudioPacketListener; private PolyphaseFIRDecimatingFilter_RB mAudioDecimationFilter; + private Metadata mMetadata; - public AudioModule(boolean record) + public AudioModule(Metadata metadata) { - mSourceID = ++UNIQUE_ID; - mAudioMetadata = new AudioMetadata(mSourceID, record); + mMetadata = metadata; mAudioDecimationFilter = new PolyphaseFIRDecimatingFilter_RB(mDecimationCoefficients, 6, 2.0f); mAudioDecimationFilter.setListener(new Listener() @@ -97,9 +95,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(RealBuffer realBuffer) { - if (mAudioPacketListener != null) + if(mAudioPacketListener != null) { - AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mAudioMetadata.copyOf()); + AudioPacket packet = new AudioPacket(realBuffer.getSamples(), mMetadata.copyOf()); mAudioPacketListener.receive(packet); } } @@ -116,7 +114,6 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void reset() { - mAudioMetadata.reset(); } @Override @@ -128,11 +125,10 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void stop() { - /* Issue an end audio packet in case a recorder is still rolling */ - if (mAudioPacketListener != null) + /* Issue an end audio packet in case a recorder is still rolling */ + if(mAudioPacketListener != null) { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, - mAudioMetadata.copyOf())); + mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); } } @@ -143,15 +139,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(RealBuffer buffer) { - if (mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH) + if(mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH) { mAudioDecimationFilter.receive(buffer); - -// /* We make a copy of the samples, so that we don't affect any other -// * processes that might be concurrently processing the same buffer */ -// float[] audio = Arrays.copyOf( buffer.getSamples(), buffer.getSamples().length ); -// -// mAudioFilter.filter( audio ); } } @@ -161,12 +151,6 @@ public class AudioModule extends Module implements IAudioPacketProvider, return this; } - @Override - public Listener getMetadataListener() - { - return mAudioMetadata; - } - @Override public void setAudioPacketListener(Listener listener) { @@ -179,41 +163,12 @@ public class AudioModule extends Module implements IAudioPacketProvider, mAudioPacketListener = null; } - @Override - public Listener getChannelEventListener() - { - return mChannelEventListener; - } - @Override public Listener getSquelchStateListener() { return mSquelchStateListener; } - /** - * Wrapper for channel event listener. Responds to channel state reset - * events to remove/cleanup current audio metadata - */ - public class ChannelEventListener implements Listener - { - @Override - public void receive(ChannelEvent event) - { - switch (event.getEvent()) - { - case NOTIFICATION_STATE_RESET: - mAudioMetadata.reset(); - break; - case NOTIFICATION_SELECTION_CHANGE: - mAudioMetadata.setSelected(event.getChannel().isSelected()); - break; - default: - break; - } - } - } - /** * Wrapper for squelch state listener */ @@ -222,10 +177,9 @@ public class AudioModule extends Module implements IAudioPacketProvider, @Override public void receive(SquelchState state) { - if (state == SquelchState.SQUELCH && mAudioPacketListener != null) + if(state == SquelchState.SQUELCH && mAudioPacketListener != null) { - mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, - mAudioMetadata.copyOf())); + mAudioPacketListener.receive(new AudioPacket(AudioPacket.Type.END, mMetadata.copyOf())); } mSquelchState = state; diff --git a/src/audio/AudioPacket.java b/src/audio/AudioPacket.java index 2936557e..aa655975 100644 --- a/src/audio/AudioPacket.java +++ b/src/audio/AudioPacket.java @@ -1,77 +1,78 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ package audio; +import channel.metadata.Metadata; import sample.real.RealBuffer; -import audio.metadata.AudioMetadata; -/******************************************************************************* - * 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 - ******************************************************************************/ public class AudioPacket { - private Type mType; - private RealBuffer mAudioData; - private AudioMetadata mAudioMetadata; - - public AudioPacket( Type type, AudioMetadata metadata ) - { - mType = type; - mAudioMetadata = metadata; - } - - public AudioPacket( float[] audio, AudioMetadata metadata ) - { - this( Type.AUDIO, metadata ); + private Type mType; + private RealBuffer mAudioData; + private Metadata mMetadata; - mAudioData = new RealBuffer( audio ); - } - - public boolean hasAudioMetadata() - { - return mAudioMetadata != null; - } - - public AudioMetadata getAudioMetadata() - { - return mAudioMetadata; - } - - public void setMetadata( AudioMetadata metadata ) - { - mAudioMetadata = metadata; - } - - public Type getType() - { - return mType; - } - - public RealBuffer getAudioBuffer() - { - return mAudioData; - } - - public boolean hasAudioBuffer() - { - return mAudioData != null; - } - - public enum Type - { - AUDIO, - END; - } + public AudioPacket(Type type, Metadata metadata) + { + mType = type; + mMetadata = metadata; + } + + public AudioPacket(float[] audio, Metadata metadata) + { + this(Type.AUDIO, metadata); + + mAudioData = new RealBuffer(audio); + } + + public boolean hasMetadata() + { + return mMetadata != null; + } + + public Metadata getMetadata() + { + return mMetadata; + } + + public void setMetadata(Metadata metadata) + { + mMetadata = metadata; + } + + public Type getType() + { + return mType; + } + + public RealBuffer getAudioBuffer() + { + return mAudioData; + } + + public boolean hasAudioBuffer() + { + return mAudioData != null; + } + + public enum Type + { + AUDIO, + END; + } } diff --git a/src/audio/broadcast/AudioBroadcaster.java b/src/audio/broadcast/AudioBroadcaster.java index 7467a2b6..2115480b 100644 --- a/src/audio/broadcast/AudioBroadcaster.java +++ b/src/audio/broadcast/AudioBroadcaster.java @@ -1,6 +1,6 @@ /******************************************************************************* * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * 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 @@ -18,14 +18,11 @@ ******************************************************************************/ package audio.broadcast; -import audio.AudioPacket; -import audio.IAudioPacketListener; import audio.convert.ISilenceGenerator; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import controller.ThreadPoolManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import properties.SystemProperties; import sample.Listener; import java.io.ByteArrayInputStream; @@ -34,14 +31,13 @@ import java.nio.file.Files; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public abstract class AudioBroadcaster implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( AudioBroadcaster.class ); + private final static Logger mLog = LoggerFactory.getLogger(AudioBroadcaster.class); public static final int PROCESSOR_RUN_INTERVAL_MS = 1000; @@ -107,9 +103,10 @@ public abstract class AudioBroadcaster implements Listener /** * Broadcasts the next song's audio metadata prior to streaming the next song. + * * @param metadata for the next recording that will be streamed */ - protected void broadcastMetadata(AudioMetadata metadata) + protected void broadcastMetadata(Metadata metadata) { IBroadcastMetadataUpdater metadataUpdater = getMetadataUpdater(); @@ -136,7 +133,7 @@ public abstract class AudioBroadcaster implements Listener if(mThreadPoolManager != null) { mScheduledTask = mThreadPoolManager.scheduleFixedRate(ThreadPoolManager.ThreadType.AUDIO_PROCESSING, - mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS ); + mRecordingQueueProcessor, PROCESSOR_RUN_INTERVAL_MS, TimeUnit.MILLISECONDS); } } } @@ -160,6 +157,7 @@ public abstract class AudioBroadcaster implements Listener /** * Stream name for the broadcast configuration for this broadcaster + * * @return stream name or null */ public String getStreamName() @@ -246,7 +244,7 @@ public abstract class AudioBroadcaster implements Listener { if(mBroadcastState != state) { - mLog.info("[" + getStreamName() + "] status: " + state); + mLog.info("[" + getStreamName() + "] status: " + state); mBroadcastState = state; broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE)); @@ -386,7 +384,7 @@ public abstract class AudioBroadcaster implements Listener { mStreamedAudioCount++; broadcast(new BroadcastEvent(AudioBroadcaster.this, - BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE)); + BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE)); metadataUpdateRequired = true; } @@ -431,7 +429,7 @@ public abstract class AudioBroadcaster implements Listener if(connected()) { - broadcastMetadata(nextRecording.getAudioMetadata()); + broadcastMetadata(nextRecording.getMetadata()); } metadataUpdateRequired = false; diff --git a/src/audio/broadcast/AudioRecording.java b/src/audio/broadcast/AudioRecording.java index 1d4c3f16..f80a0c66 100644 --- a/src/audio/broadcast/AudioRecording.java +++ b/src/audio/broadcast/AudioRecording.java @@ -1,6 +1,6 @@ /******************************************************************************* * sdrtrunk - * Copyright (C) 2014-2016 Dennis Sheirer + * 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 @@ -18,7 +18,7 @@ ******************************************************************************/ package audio.broadcast; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,22 +30,23 @@ public class AudioRecording implements Comparable private final static Logger mLog = LoggerFactory.getLogger(StreamManager.class); private Path mPath; - private AudioMetadata mAudioMetadata; private long mStartTime; private long mRecordingLength; private AtomicInteger mPendingReplayCount = new AtomicInteger(); + private Metadata mMetadata; /** * Audio recording that is ready to be streamed + * * @param path to the audio recording file - * @param audioMetadata associated with the recording + * @param metadata associated with the recording * @param start time of recording in milliseconds since epoch * @param recordingLength in milliseconds */ - public AudioRecording(Path path, AudioMetadata audioMetadata, long start, long recordingLength) + public AudioRecording(Path path, Metadata metadata, long start, long recordingLength) { mPath = path; - mAudioMetadata = audioMetadata; + mMetadata = metadata; mStartTime = start; mRecordingLength = recordingLength; } @@ -61,9 +62,9 @@ public class AudioRecording implements Comparable /** * Optional audio metadata for the recording. */ - public AudioMetadata getAudioMetadata() + public Metadata getMetadata() { - return mAudioMetadata; + return mMetadata; } /** diff --git a/src/audio/broadcast/BroadcastModel.java b/src/audio/broadcast/BroadcastModel.java index 911eaf39..f6e9b587 100644 --- a/src/audio/broadcast/BroadcastModel.java +++ b/src/audio/broadcast/BroadcastModel.java @@ -20,7 +20,7 @@ package audio.broadcast; import alias.id.broadcast.BroadcastChannel; import audio.AudioPacket; -import audio.metadata.AudioMetadata; +import channel.metadata.Metadata; import controller.ThreadPoolManager; import icon.IconManager; import org.slf4j.Logger; @@ -48,7 +48,7 @@ import java.util.regex.Pattern; public class BroadcastModel extends AbstractTableModel implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( BroadcastModel.class ); + private final static Logger mLog = LoggerFactory.getLogger(BroadcastModel.class); public static final String TEMPORARY_STREAM_DIRECTORY = "streaming"; public static final String TEMPORARY_STREAM_FILE_SUFFIX = "temporary_streaming_file_"; @@ -84,7 +84,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener