#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.
This commit is contained in:
Denny
2017-01-17 19:24:54 -05:00
parent 16b69d4aef
commit bc8873de01
67 changed files with 6207 additions and 10127 deletions

View File

@@ -1,58 +1,46 @@
/*******************************************************************************
* 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import alias.AliasController;
import alias.AliasModel;
import audio.AudioManager;
import audio.AudioPanel;
import audio.broadcast.BroadcastModel;
import audio.broadcast.BroadcastPanel;
import channel.metadata.ChannelMetadataViewer;
import com.jidesoft.swing.JideTabbedPane;
import controller.channel.ChannelController;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMapModel;
import icon.IconManager;
import map.MapPanel;
import map.MapService;
import module.decode.event.CallEventPanel;
import module.decode.event.MessageActivityPanel;
import channel.state.ChannelList;
import net.miginfocom.swing.MigLayout;
import settings.SettingsManager;
import source.SourceManager;
import source.tuner.TunerModel;
import source.tuner.TunerViewPanel;
import spectrum.ChannelSpectrumPanel;
import alias.AliasController;
import alias.AliasModel;
import audio.AudioManager;
import audio.AudioPanel;
import com.jidesoft.swing.JideSplitPane;
import com.jidesoft.swing.JideTabbedPane;
import controller.channel.ChannelController;
import controller.channel.ChannelModel;
import controller.channel.ChannelProcessingManager;
import controller.channel.map.ChannelMapModel;
import javax.swing.*;
import java.awt.*;
public class ControllerPanel extends JPanel
{
@@ -61,13 +49,10 @@ public class ControllerPanel extends JPanel
private AliasController mAliasController;
private AudioPanel mAudioPanel;
private BroadcastPanel mBroadcastPanel;
private CallEventPanel mCallEventPanel;
private ChannelController mChannelController;
private ChannelList mChannelStateList;
private ChannelMetadataViewer mChannelMetadataViewer;
private ChannelModel mChannelModel;
private ChannelSpectrumPanel mChannelSpectrumPanel;
private MapPanel mMapPanel;
private MessageActivityPanel mMessageActivityPanel;
private TunerViewPanel mTunerManagerPanel;
private JideTabbedPane mTabbedPane;
@@ -89,24 +74,16 @@ public class ControllerPanel extends JPanel
mAudioPanel = new AudioPanel(iconManager, settingsManager, sourceManager, audioManager);
mMapPanel = new MapPanel(mapService, iconManager, settingsManager);
mChannelMetadataViewer = new ChannelMetadataViewer(channelProcessingManager, iconManager, settingsManager);
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
mMapPanel = new MapPanel(mapService, iconManager, settingsManager);
mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager);
mCallEventPanel = new CallEventPanel(iconManager, channelProcessingManager);
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager,
channelProcessingManager);
mChannelStateList = new ChannelList(channelModel, channelProcessingManager, iconManager,
settingsManager, mAudioPanel);
mChannelController = new ChannelController(channelModel, channelMapModel,
sourceManager, aliasModel);
mChannelController = new ChannelController(channelModel, channelMapModel, sourceManager, aliasModel);
mAliasController = new AliasController(aliasModel, broadcastModel, iconManager);
mTunerManagerPanel = new TunerViewPanel(tunerModel);
init();
@@ -114,66 +91,24 @@ public class ControllerPanel extends JPanel
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0 ",
"[grow,fill]",
"[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[]0[grow,fill]"));
add(mAudioPanel);
//Tabbed View - configuration, calls, messages, map
mTabbedPane = new JideTabbedPane();
mTabbedPane.setFont(this.getFont());
mTabbedPane.setForeground(Color.BLACK);
mTabbedPane.addTab("Tuners", mTunerManagerPanel);
mTabbedPane.addTab("Channels", mChannelController);
mTabbedPane.addTab("Now Playing", mChannelMetadataViewer);
mTabbedPane.addTab("Aliases", mAliasController);
mTabbedPane.addTab("Channel Spectrum", mChannelSpectrumPanel);
mTabbedPane.addTab("Events", mCallEventPanel);
mTabbedPane.addTab("Channels", mChannelController);
mTabbedPane.addTab("Map", mMapPanel);
mTabbedPane.addTab("Messages", mMessageActivityPanel);
mTabbedPane.addTab("Streaming", mBroadcastPanel);
/**
* Change listener to enable/disable the channel spectrum display
* only when the tab is visible, and a channel has been selected
*/
mTabbedPane.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent event)
{
int index = mTabbedPane.getSelectedIndex();
Component component = mTabbedPane.getComponentAt(index);
if (component instanceof ChannelSpectrumPanel)
{
mChannelSpectrumPanel.setEnabled(true);
}
else
{
mChannelSpectrumPanel.setEnabled(false);
}
}
});
/* Register each of the components to receive channel events when the
* channels are selected or change */
mChannelModel.addListener(mCallEventPanel);
mChannelModel.addListener(mChannelStateList);
mChannelModel.addListener(mChannelSpectrumPanel);
mChannelModel.addListener(mMessageActivityPanel);
JScrollPane channelStateListScroll = new JScrollPane();
channelStateListScroll.getViewport().setView(mChannelStateList);
channelStateListScroll.setPreferredSize(new Dimension(400, 500));
mTabbedPane.addTab("Tuners", mTunerManagerPanel);
//Set preferred size to influence the split between these panels
mTabbedPane.setPreferredSize(new Dimension(880, 500));
JideSplitPane channelSplit = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
channelSplit.setDividerSize(5);
channelSplit.add(channelStateListScroll);
channelSplit.add(mTabbedPane);
add(channelSplit);
add(mTabbedPane);
}
}

View File

@@ -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,50 +14,51 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package controller.channel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import alias.AliasModel;
import audio.AudioPacket;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.ChannelMetadataModel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
import controller.channel.map.ChannelMapModel;
import filter.FilterSet;
import message.Message;
import module.Module;
import module.ProcessingChain;
import module.decode.DecoderFactory;
import module.decode.event.MessageActivityModel;
import module.log.EventLogManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import record.RecorderManager;
import record.RecorderType;
import sample.Listener;
import source.Source;
import source.SourceException;
import source.SourceManager;
import alias.AliasModel;
import audio.AudioPacket;
import audio.metadata.Metadata;
import audio.metadata.MetadataType;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent.Event;
import controller.channel.map.ChannelMapModel;
import filter.FilterSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
public class ChannelProcessingManager implements ChannelEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelProcessingManager.class);
private Map<Integer, ProcessingChain> mProcessingChains = new HashMap<>();
private Map<Integer,ProcessingChain> mProcessingChains = new HashMap<>();
private List<Listener<AudioPacket>> mAudioPacketListeners = new CopyOnWriteArrayList<>();
private List<Listener<Message>> mMessageListeners = new CopyOnWriteArrayList<>();
private ChannelModel mChannelModel;
private ChannelMapModel mChannelMapModel;
private ChannelMetadataModel mChannelMetadataModel = new ChannelMetadataModel();
private AliasModel mAliasModel;
private EventLogManager mEventLogManager;
private RecorderManager mRecorderManager;
@@ -78,6 +79,14 @@ public class ChannelProcessingManager implements ChannelEventListener
mSourceManager = sourceManager;
}
/**
* Channel metadata model containing metadata for each channel or channel time-slice that is currently processing.
*/
public ChannelMetadataModel getChannelMetadataModel()
{
return mChannelMetadataModel;
}
/**
* Indicates if a processing chain is constructed for the channel and that
* the processing chain is currently processing.
@@ -85,7 +94,7 @@ public class ChannelProcessingManager implements ChannelEventListener
private boolean isProcessing(Channel channel)
{
return mProcessingChains.containsKey(channel.getChannelID()) &&
mProcessingChains.get(channel.getChannelID()).isProcessing();
mProcessingChains.get(channel.getChannelID()).isProcessing();
}
/**
@@ -102,19 +111,19 @@ public class ChannelProcessingManager implements ChannelEventListener
{
Channel channel = event.getChannel();
switch (event.getEvent())
switch(event.getEvent())
{
case REQUEST_ENABLE:
if (!mProcessingChains.containsKey(channel.getChannelID()) ||
!mProcessingChains.get(channel.getChannelID()).isProcessing())
if(!mProcessingChains.containsKey(channel.getChannelID()) ||
!mProcessingChains.get(channel.getChannelID()).isProcessing())
{
startProcessing(event);
}
break;
case REQUEST_DISABLE:
if (channel.getEnabled())
if(channel.getEnabled())
{
switch (channel.getChannelType())
switch(channel.getChannelType())
{
case STANDARD:
stopProcessing(channel, true);
@@ -129,13 +138,13 @@ public class ChannelProcessingManager implements ChannelEventListener
}
}
case NOTIFICATION_DELETE:
if (channel.getEnabled())
if(channel.getEnabled())
{
stopProcessing(channel, true);
}
break;
case NOTIFICATION_CONFIGURATION_CHANGE:
if (isProcessing(channel))
if(isProcessing(channel))
{
stopProcessing(channel, false);
startProcessing(event);
@@ -153,7 +162,7 @@ public class ChannelProcessingManager implements ChannelEventListener
ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID());
//If we're already processing, ignore the request
if (processingChain != null && processingChain.isProcessing())
if(processingChain != null && processingChain.isProcessing())
{
return;
}
@@ -164,63 +173,88 @@ public class ChannelProcessingManager implements ChannelEventListener
try
{
source = mSourceManager.getSource(channel.getSourceConfiguration(),
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth());
channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth());
}
catch (SourceException se)
catch(SourceException se)
{
mLog.debug("Error obtaining source for channel [" + channel.getName() + "]", se);
}
if (source == null)
if(source == null)
{
channel.setEnabled(false);
mChannelModel.broadcast(
new ChannelEvent(channel, Event.NOTIFICATION_ENABLE_REJECTED));
new ChannelEvent(channel, Event.NOTIFICATION_ENABLE_REJECTED));
return;
}
if (processingChain == null)
if(processingChain == null)
{
processingChain = new ProcessingChain(channel.getName(),
channel.getChannelType());
channel.getChannelType());
/* Register global listeners */
for (Listener<AudioPacket> listener : mAudioPacketListeners)
for(Listener<AudioPacket> listener : mAudioPacketListeners)
{
processingChain.addAudioPacketListener(listener);
}
for (Listener<Message> listener : mMessageListeners)
for(Listener<Message> listener : mMessageListeners)
{
processingChain.addMessageListener(listener);
}
/* Processing Modules */
List<Module> modules = DecoderFactory.getModules(mChannelModel,
mChannelMapModel, this, mAliasModel, channel);
List<Module> modules = DecoderFactory.getModules(mChannelModel, mChannelMapModel, this,
mAliasModel, channel, processingChain.getChannelState().getMutableMetadata());
processingChain.addModules(modules);
/* Setup message activity model with filtering */
FilterSet<Message> messageFilter = DecoderFactory.getMessageFilters(modules);
MessageActivityModel messageModel = new MessageActivityModel(messageFilter);
processingChain.setMessageActivityModel(messageModel);
/* Inject channel metadata that will be inserted into audio packets
* for the recorder manager and streaming */
processingChain.broadcast(
new Metadata(MetadataType.SYSTEM, channel.getSystem()));
processingChain.broadcast(
new Metadata(MetadataType.SITE_ID, channel.getSite()));
processingChain.broadcast(
new Metadata(MetadataType.CHANNEL_NAME, channel.getName()));
//Override recordable flag in metadata if user has specified audio recording
if(channel.getRecordConfiguration() != null &&
channel.getRecordConfiguration().getRecorders().contains(RecorderType.AUDIO))
{
processingChain.getChannelState().getMutableMetadata().setRecordable(true);
}
//Inject channel metadata that will be inserted into audio packets for recorder manager and streaming
StringBuilder sb = new StringBuilder();
if(channel.getSystem() != null)
{
sb.append(channel.getSystem());
}
if(channel.getSite() != null)
{
if(sb.length() > 0)
{
sb.append(" - ");
}
sb.append(channel.getSite());
}
if(sb.length() > 0)
{
processingChain.getChannelState().getMutableMetadata()
.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_LABEL_1, sb.toString()));
}
processingChain.getChannelState().getMutableMetadata()
.receive(new AttributeChangeRequest<String>(Attribute.CHANNEL_CONFIGURATION_LABEL_2, channel.getName()));
}
/* Setup event logging */
List<Module> loggers = mEventLogManager.getLoggers(channel.getEventLogConfiguration(), channel.getName());
if (!loggers.isEmpty())
if(!loggers.isEmpty())
{
processingChain.addModules(loggers);
}
@@ -228,16 +262,16 @@ public class ChannelProcessingManager implements ChannelEventListener
/* Setup recorders */
List<RecorderType> recorders = channel.getRecordConfiguration().getRecorders();
if (!recorders.isEmpty())
if(!recorders.isEmpty())
{
/* Add baseband recorder */
if ((recorders.contains(RecorderType.BASEBAND) && channel.getChannelType() == ChannelType.STANDARD))
/* Add baseband recorder */
if((recorders.contains(RecorderType.BASEBAND) && channel.getChannelType() == ChannelType.STANDARD))
{
processingChain.addModule(mRecorderManager.getBasebandRecorder(channel.toString()));
}
/* Add traffic channel baseband recorder */
if (recorders.contains(RecorderType.TRAFFIC_BASEBAND) && channel.getChannelType() == ChannelType.TRAFFIC)
if(recorders.contains(RecorderType.TRAFFIC_BASEBAND) && channel.getChannelType() == ChannelType.TRAFFIC)
{
processingChain.addModule(mRecorderManager.getBasebandRecorder(channel.toString()));
}
@@ -245,13 +279,13 @@ public class ChannelProcessingManager implements ChannelEventListener
processingChain.setSource(source);
if (event instanceof TrafficChannelEvent)
if(event instanceof TrafficChannelEvent)
{
TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent) event;
processingChain.getChannelState().configureAsTrafficChannel(
trafficChannelEvent.getTrafficChannelManager(),
trafficChannelEvent.getCallEvent());
trafficChannelEvent.getTrafficChannelManager(),
trafficChannelEvent.getCallEvent());
}
processingChain.start();
@@ -261,14 +295,14 @@ public class ChannelProcessingManager implements ChannelEventListener
mProcessingChains.put(channel.getChannelID(), processingChain);
mChannelModel.broadcast(
new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_START));
new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_START));
}
private void stopProcessing(Channel channel, boolean remove)
{
channel.setEnabled(false);
if (mProcessingChains.containsKey(channel.getChannelID()))
if(mProcessingChains.containsKey(channel.getChannelID()))
{
ProcessingChain processingChain = mProcessingChains.get(channel.getChannelID());
@@ -281,7 +315,7 @@ public class ChannelProcessingManager implements ChannelEventListener
mChannelModel.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_PROCESSING_STOP));
if (remove)
if(remove)
{
mProcessingChains.remove(channel.getChannelID());