Initial code check in continued

This commit is contained in:
dennis.sheirer
2014-04-26 19:48:24 +00:00
parent 960a6f9952
commit 3dfedcf8d1
474 changed files with 76613 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import controller.Editor;
public abstract class AbstractChannelEditor extends Editor
{
private static final long serialVersionUID = 1L;
protected ChannelNode mChannelNode;
public AbstractChannelEditor( ChannelNode channel )
{
super();
mChannelNode = channel;
}
public ChannelNode getChannelNode()
{
return mChannelNode;
}
public abstract void save();
public abstract void reset();
}

View File

@@ -0,0 +1,902 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
import log.Log;
import message.Message;
import record.config.RecordConfiguration;
import sample.Listener;
import source.SourceType;
import source.config.SourceConfigFactory;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import controller.ResourceManager;
import controller.activity.ActivitySummaryFrame;
import controller.config.Configuration;
import controller.site.Site;
import controller.state.ChannelState;
import controller.state.ChannelState.State;
import controller.system.System;
import decode.config.AuxDecodeConfiguration;
import decode.config.DecodeConfigFactory;
import decode.config.DecodeConfiguration;
import eventlog.config.EventLogConfiguration;
@XmlSeeAlso( { Configuration.class } )
@XmlRootElement( name = "channel" )
public class Channel extends Configuration
{
public enum ChannelType
{
STANDARD,
TRAFFIC
};
private static final boolean ENABLED = true;
private static final boolean DISABLED = false;
private static final boolean BROADCAST_CHANGE = true;
private CopyOnWriteArrayList<ChannelListener> mChannelListeners =
new CopyOnWriteArrayList<ChannelListener>();
private CopyOnWriteArrayList<Listener<Message>> mMessageListeners =
new CopyOnWriteArrayList<Listener<Message>>();
private HashMap<Integer,Channel> mTrafficChannels =
new HashMap<Integer,Channel>();
private DecodeConfiguration mDecodeConfiguration =
DecodeConfigFactory.getDefaultDecodeConfiguration();
private AuxDecodeConfiguration mAuxDecodeConfiguration =
new AuxDecodeConfiguration();
private SourceConfiguration mSourceConfiguration =
SourceConfigFactory.getDefaultSourceConfiguration();
private EventLogConfiguration mEventLogConfiguration =
new EventLogConfiguration();
private RecordConfiguration mRecordConfiguration = new RecordConfiguration();
private ResourceManager mResourceManager;
private String mAliasListName;
private System mSystem;
private Site mSite;
private String mName;
private boolean mEnabled;
private boolean mSelected;
private ChannelType mChannelType;
private ProcessingChain mProcessingChain;
/**
* Constructs a new standard channel with a default name of "New Channel"
*/
public Channel()
{
this( "New Channel", ChannelType.STANDARD );
}
/**
* Constructs a new standard channel with the specified channel name
*/
public Channel( String channelName )
{
this( channelName, ChannelType.STANDARD );
}
/**
* Constructs a new channel with the specified channel name and type.
*
* @param channelName
* @param channelType
*/
public Channel( String channelName, ChannelType channelType )
{
mName = channelName;
mChannelType = channelType;
}
public ChannelType getChannelType()
{
return mChannelType;
}
/**
* Sets the resource manager responsible for this channel and registers
* the channel manager as a listener to this channel. Fires channel add
* channel event to the newly added channel manager channel listener.
*/
public void setResourceManager( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
/* Add system-wide channel listeners onto this channel */
addListeners( mResourceManager.getChannelManager().getChannelListeners() );
/* If we're enabled, fire enable changed event to get processing started */
if( mEnabled )
{
enableChanged();
}
}
@XmlTransient
public ResourceManager getResourceManager()
{
return mResourceManager;
}
/**
* Returns the processing chain for this channel. If the processing chain
* is not currently constructed and running, this method will return null.
*/
@XmlTransient
public ProcessingChain getProcessingChain()
{
return mProcessingChain;
}
/**
* Indicates if this channel has a processing chain constructed and it
* is processing.
*/
public boolean isProcessing()
{
return mProcessingChain != null && mProcessingChain.isProcessing();
}
/**
* Indicates if this channel has been selected for audio output
*/
@XmlTransient
public boolean getSelected()
{
return mSelected;
}
public void setSelected( boolean selected )
{
mSelected = selected;
if( mProcessingChain != null && mProcessingChain.isProcessing() )
{
mProcessingChain.getAudioOutput().setAudioPlaybackEnabled( selected );
}
fireChannelEvent( ChannelEvent.CHANGE_SELECTED );
}
public JMenu getContextMenu()
{
JMenu menu = new JMenu( mSite + "-" + mName );
if( mEnabled )
{
JMenuItem disable = new JMenuItem( "Disable" );
disable.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
setEnabled( DISABLED, BROADCAST_CHANGE );
getResourceManager().getPlaylistManager().save();
}
} );
menu.add( disable );
menu.add( new JSeparator() );
JMenuItem actySummaryItem =
new JMenuItem( "Activity Summary" );
actySummaryItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
if( mResourceManager != null )
{
ProcessingChain chain = getProcessingChain();
if( chain != null )
{
ChannelState state = chain.getChannelState();
if( state != null )
{
String summary = state.getActivitySummary();
new ActivitySummaryFrame( summary,
mResourceManager.getController().getTree() );
}
}
}
}
} );
menu.add( actySummaryItem );
}
else
{
JMenuItem enable = new JMenuItem( "Enable" );
enable.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
setEnabled( ENABLED, BROADCAST_CHANGE );
getResourceManager().getPlaylistManager().save(); }
} );
menu.add( enable );
}
menu.add( new JSeparator() );
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
dispose();
getResourceManager().getPlaylistManager().save(); }
} );
menu.add( deleteItem );
return menu;
}
/**
* Orderly shutdown method when this channel is going to be deleted.
*/
public void dispose()
{
setEnabled( DISABLED, BROADCAST_CHANGE );
/* Broadcast channel deleted event */
fireChannelEvent( ChannelEvent.CHANNEL_DELETED );
}
/**
* Returns a full channel name containing the system, site and channel name.
*/
public String getChannelDisplayName()
{
StringBuilder sb = new StringBuilder();
if( mSystem != null )
{
sb.append( mSystem.getName() );
}
else
{
sb.append( "-" );
}
sb.append( "/" );
if( mSite != null )
{
sb.append( mSite.getName() );
}
else
{
sb.append( "-" );
}
sb.append( "/" );
sb.append( mName );
return sb.toString();
}
/**
* Returns the tuner channel requirement for this channel when the source
* configuration specifies a tuner and frequency as the source for this
* channel.
*
* If the source configuration is anything else, this method returns null.
*/
public TunerChannel getTunerChannel()
{
TunerChannel retVal = null;
if( mSourceConfiguration.getSourceType() == SourceType.TUNER )
{
SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
retVal = new TunerChannel( Type.LOCKED, config.getFrequency(),
mDecodeConfiguration.getDecoderType().getChannelBandwidth() );
}
return retVal;
}
/**
* Returns the owning system for this channel.
*/
public System getSystem()
{
return mSystem;
}
/**
* Sets the owning system for this channel and optionally broadcasts a
* system change channel event.
*/
public void setSystem( System system, boolean fireChangeEvent )
{
mSystem = system;
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_SYSTEM );
}
}
/**
* Returns the owning site for this channel.
*/
public Site getSite()
{
return mSite;
}
/**
* Sets the owning site for this channel and optionally broadcasts a site
* change channel event.
*/
public void setSite( Site site, boolean fireChangeEvent )
{
mSite = site;
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_SITE );
}
}
/**
* Default display string for this channel -- the channel name.
*/
public String toString()
{
return mName;
}
/**
* Indicates if this channel is enabled for processing.
*/
@XmlAttribute
public boolean getEnabled()
{
return mEnabled;
}
/**
* Enables/disables this channel for processing. Automatically starts or
* stops channel processing in response to this change.
*/
public void setEnabled( boolean enabled )
{
mEnabled = enabled;
enableChanged();
}
/**
* Enables/disables this channel for processing. Automatically starts or
* stops channel processing in response to this change. Optionally
* broadcasts a enabled change channel event.
*/
public void setEnabled( boolean enabled, boolean fireChannelEvent )
{
setEnabled( enabled );
if( fireChannelEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_ENABLED );
}
}
/**
* Returns the alias list that is used by this channel for looking up alias
* values for the various identifiers produced by the decoder.
*/
@XmlElement( name = "alias_list_name" )
public String getAliasListName()
{
return mAliasListName;
}
/**
* Sets the alias list to be used for looking up the alias values for the
* various identifiers produced by the decoder
*/
public void setAliasListName( String name )
{
mAliasListName = name;
}
/**
* Sets the alias list to be used for looking up the alias values for the
* various identifiers produced by the decoder. This method is the same
* as setAliasName(name) and optionally broadcasts an alias list change
* channel event.
*/
public void setAliasListName( String name, boolean fireChangeEvent )
{
mAliasListName = name;
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_ALIAS_LIST );
}
}
/**
* Gets the decoder configuration used by this channel
*/
@XmlElement( name = "decode_configuration" )
public DecodeConfiguration getDecodeConfiguration()
{
return mDecodeConfiguration;
}
/**
* Sets the decoder configuration used by this channel
*/
public void setDecodeConfiguration( DecodeConfiguration config )
{
mDecodeConfiguration = config;
if( mProcessingChain != null && mProcessingChain.isRunning() )
{
mProcessingChain.updateDecoder();
}
}
/**
* Gets the aux decoder configuration used by this channel
*/
@XmlElement( name = "aux_decode_configuration" )
public AuxDecodeConfiguration getAuxDecodeConfiguration()
{
return mAuxDecodeConfiguration;
}
/**
* Sets the decoder configuration used by this channel
*/
public void setAuxDecodeConfiguration( AuxDecodeConfiguration config )
{
mAuxDecodeConfiguration = config;
if( mProcessingChain != null && mProcessingChain.isRunning() )
{
mProcessingChain.updateDecoder();
}
}
/**
* Sets the decoder configuration used by this channel and optionally
* broadcasts a decoder change channel event.
*/
public void setDecodeConfiguration( DecodeConfiguration config,
boolean fireChangeEvent )
{
setDecodeConfiguration( config );
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_DECODER );
}
}
/**
* Returns the source configuration for this channel.
*/
@XmlElement( name = "source_configuration" )
public SourceConfiguration getSourceConfiguration()
{
return mSourceConfiguration;
}
/**
* Sets the source configuration for this channel.
*/
public void setSourceConfiguration( SourceConfiguration config )
{
mSourceConfiguration = config;
if( mProcessingChain != null && mProcessingChain.isRunning() )
{
mProcessingChain.updateSource();
}
}
/**
* Sets the source configuration for this channel and optionally broadcasts
* a source change channel event.
*/
public void setSourceConfiguration( SourceConfiguration config,
boolean fireChangeEvent )
{
setSourceConfiguration( config );
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_SOURCE );
}
}
/**
* Returns the event logger configuration for this channel.
*/
@XmlElement( name = "event_log_configuration" )
public EventLogConfiguration getEventLogConfiguration()
{
return mEventLogConfiguration;
}
/**
* Sets the event logger configuration for this channel.
*/
public void setEventLogConfiguration( EventLogConfiguration config )
{
mEventLogConfiguration = config;
if( mProcessingChain != null && mProcessingChain.isRunning() )
{
mProcessingChain.updateEventLogging();
}
}
/**
* Sets the event logger configuration for this channel and optionally
* broadcasts an event logger change channel event.
*/
public void setEventLogConfiguration( EventLogConfiguration config,
boolean fireChangeEvent )
{
setEventLogConfiguration( config );
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_EVENT_LOGGER );
}
}
/**
* Returns the recorder configuration for this channel.
*/
@XmlElement( name = "record_configuration" )
public RecordConfiguration getRecordConfiguration()
{
return mRecordConfiguration;
}
/**
* Sets the recorder configuration for this channel.
*/
public void setRecordConfiguration( RecordConfiguration config )
{
mRecordConfiguration = config;
if( mProcessingChain != null && mProcessingChain.isRunning() )
{
mProcessingChain.updateRecording();
}
}
/**
* Sets the recorder configuration for this channel and optionally broadcasts
* a recorder configuration change channel event.
*/
public void setRecordConfiguration( RecordConfiguration config,
boolean fireChangeEvent )
{
setRecordConfiguration( config );
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_RECORDER );
}
}
/**
* Returns the name of this channel.
*/
@XmlAttribute
public String getName()
{
return mName;
}
/**
* Sets the name of this channel.
*/
public void setName( String name )
{
mName = name;
}
/**
* Sets the name of this channel and optionally broadcasts a name change
* channel event.
*/
public void setName( String name, boolean fireChangeEvent )
{
mName = name;
if( fireChangeEvent )
{
fireChannelEvent( ChannelEvent.CHANGE_NAME );
}
}
/**
* Responds to changes in the enabled setting by starting the processing
* change if the channel is now enabled, or stopping the procesing chain
* if it was already started.
*/
private void enableChanged()
{
if( mEnabled )
{
start();
}
else
{
stop();
}
}
/**
* Starts the channel processing chain.
*/
private void start()
{
if( mEnabled && mProcessingChain == null && mResourceManager != null )
{
mProcessingChain = new ProcessingChain( this, mResourceManager );
/* Add system-wide message listeners */
mProcessingChain.addListeners(
mResourceManager.getChannelManager().getMessageListeners() );
/* Add individual message listeners */
mProcessingChain.addListeners( mMessageListeners );
mProcessingChain.start();
fireChannelEvent( ChannelEvent.PROCESSING_STARTED );
}
/* If this is a traffic channel, override the call fade timeout before
* we set the state to call, so that it will auto-expire after the
* fade timeout and be auto deleted */
if( mChannelType == ChannelType.TRAFFIC )
{
if( mProcessingChain.isProcessing() )
{
mProcessingChain.getChannelState().setCallFadeTimeout( 40000 );
mProcessingChain.getChannelState().setState( State.CALL );
}
else
{
mProcessingChain.getChannelState().setCallFadeTimeout( 5000 );
/* Channel state should already be no_tuner */
}
}
}
/**
* Stops the channel processing chain
*/
private void stop()
{
/* Stop and remove any traffic channels */
for( Channel traffic: mTrafficChannels.values() )
{
traffic.stop();
traffic.dispose();
}
mTrafficChannels.clear();
if( !mEnabled && mProcessingChain != null )
{
mProcessingChain.stop();
mProcessingChain.dispose();
mProcessingChain = null;
mSelected = false;
}
fireChannelEvent( ChannelEvent.PROCESSING_STOPPED );
}
/**
* Broadcasts a channel change event to all registered listeners
*/
public void fireChannelEvent( ChannelEvent event )
{
for( ChannelListener listener: mChannelListeners )
{
listener.occurred( this, event );
}
}
/**
* Adds a channel listener to receive all channel events from this channel
* and automatically sends the listener a channel add event.
*/
public void addListener( ChannelListener listener )
{
if( !mChannelListeners.contains( listener ) )
{
mChannelListeners.add( listener );
listener.occurred( this, ChannelEvent.CHANNEL_ADDED );
}
else
{
Log.error( "Channel - attempt to add already existing channel listener [" + listener.getClass() + "]" );
}
}
/**
* Adds a list of channel listeners to receive all channel events from this
* channel and automatically sends each one a channel add event.
*/
public void addListeners( List<ChannelListener> listeners )
{
for( ChannelListener listener: listeners )
{
addListener( listener );
}
}
/**
* Removes a channel listener from receiving channel events from this channel
*/
public void removeListener( ChannelListener listener )
{
mChannelListeners.remove( listener );
}
public void addListener( Listener<Message> listener )
{
mMessageListeners.add( listener );
if( isProcessing() )
{
mProcessingChain.addListener( listener );
}
}
public void removeListener( Listener<Message> listener )
{
mMessageListeners.remove( listener );
if( isProcessing() )
{
mProcessingChain.removeListener( listener );
}
}
/**
* Indicates if any part of this channel is contained within the
* minimum and maximum frequency values.
*/
public boolean isWithin( int minimum, int maximum )
{
return getTunerChannel() != null &&
getTunerChannel().isWithin( minimum, maximum );
}
public void addTrafficChannel( int channelNumber, Channel channel )
{
mTrafficChannels.put( channelNumber, channel );
}
public boolean hasTrafficChannel( Channel channel )
{
return mTrafficChannels.containsValue( channel );
}
public void removeTrafficChannel( Channel channel )
{
int keyToRemove = -1;
if( mTrafficChannels.values().contains( channel ) )
{
for( Integer channelNumber: mTrafficChannels.keySet() )
{
if( mTrafficChannels.get( channelNumber ).equals( channel ) )
{
keyToRemove = channelNumber;
}
}
}
if( keyToRemove != -1 )
{
mTrafficChannels.remove( keyToRemove );
}
}
public Collection<Channel> getTrafficChannels()
{
return Collections.unmodifiableCollection( mTrafficChannels.values() );
}
public boolean hasTrafficChannel( int channelNumber )
{
return mTrafficChannels.containsKey( channelNumber );
}
/**
* Channel Events - used to specify channel events and changes to channel
* configurations and settings.
*/
public enum ChannelEvent
{
CHANGE_ALIAS_LIST,
CHANGE_DECODER,
CHANGE_ENABLED,
CHANGE_EVENT_LOGGER,
CHANGE_NAME,
CHANGE_SITE,
CHANGE_RECORDER,
CHANGE_SELECTED,
CHANGE_SOURCE,
CHANGE_SYSTEM,
CHANNEL_ADDED,
CHANNEL_DELETED,
PROCESSING_STARTED,
PROCESSING_STOPPED,
}
}

View File

@@ -0,0 +1,235 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.awt.Color;
import java.awt.ComponentOrientation;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
import record.RecordComponentEditor;
import source.SourceComponentEditor;
import alias.AliasList;
import com.jidesoft.swing.JideTabbedPane;
import controller.Editor;
import decode.AuxDecodeComponentEditor;
import decode.DecodeComponentEditor;
import eventlog.EventLogComponentEditor;
public class ChannelEditor extends Editor implements ActionListener
{
private static final long serialVersionUID = 1L;
private DefaultTreeModel mTreeModel;
private ChannelNode mChannelNode;
private DecodeComponentEditor mDecodeEditor;
private AuxDecodeComponentEditor mAuxDecodeEditor;
private EventLogComponentEditor mEventLogEditor;
private RecordComponentEditor mRecordEditor;
private SourceComponentEditor mSourceEditor;
private JTextField mChannelName;
private JCheckBox mChannelEnabled = new JCheckBox();
private JComboBox<AliasList> mComboAliasLists;
public ChannelEditor( ChannelNode channel )
{
super();
mChannelNode = channel;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) );
add( new JLabel( "Channel Configuration" ), "span, align center" );
add( new JLabel( "Enabled:" ) );
mChannelEnabled.setSelected(
mChannelNode.getChannel().getEnabled() );
mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT );
add( mChannelEnabled );
add( new JLabel( "Name:" ) );
mChannelName = new JTextField( mChannelNode.getChannel().getName() );
add( mChannelName, "growx,push" );
/**
* ComboBox: Alias Lists
*/
add( new JLabel( "Aliases:" ) );
mComboAliasLists = new JComboBox<AliasList>();
List<AliasList> lists = mChannelNode.getModel().getResourceManager()
.getPlaylistManager().getPlayist().getAliasDirectory().getAliasList();
Collections.sort( lists );
mComboAliasLists.setModel( new DefaultComboBoxModel<AliasList>(
lists.toArray( new AliasList[ lists.size() ] ) ) );
String aliasListName =
mChannelNode.getChannel().getAliasListName();
if( aliasListName != null )
{
AliasList selected = mChannelNode.getModel().getResourceManager()
.getPlaylistManager().getPlayist().getAliasDirectory()
.getAliasList( aliasListName );
mComboAliasLists.setSelectedItem( selected );
}
add( mComboAliasLists, "growx,push" );
JideTabbedPane tabs = new JideTabbedPane();
tabs.setFont( this.getFont() );
tabs.setForeground( Color.BLACK );
mSourceEditor = new SourceComponentEditor( mChannelNode );
tabs.addTab( "Source", mSourceEditor );
mDecodeEditor = new DecodeComponentEditor( mChannelNode );
tabs.addTab( "Decoder", mDecodeEditor );
mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannelNode );
tabs.addTab( "Aux", mAuxDecodeEditor );
mEventLogEditor = new EventLogComponentEditor( mChannelNode );
tabs.addTab( "Event Log", mEventLogEditor );
mRecordEditor = new RecordComponentEditor( mChannelNode );
tabs.addTab( "Record", mRecordEditor );
add( tabs, "span,grow,push" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ChannelEditor.this );
add( btnSave, "growx,push" );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( ChannelEditor.this );
add( btnReset, "growx,push" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
save();
}
else if( command.contentEquals( "Reset" ) )
{
reset();
}
if( mTreeModel != null )
{
mTreeModel.nodeChanged( mChannelNode );
}
}
@Override
public void save()
{
boolean expanded = mChannelNode.getModel().getTree()
.isExpanded( new TreePath( mChannelNode ) );
mSourceEditor.save();
mDecodeEditor.save();
mAuxDecodeEditor.save();
mEventLogEditor.save();
mRecordEditor.save();
mChannelNode.getChannel().setName( mChannelName.getText(), false );
mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected(), false );
AliasList selected = mComboAliasLists.getItemAt(
mComboAliasLists.getSelectedIndex() );
if( selected != null )
{
mChannelNode.getChannel().setAliasListName( selected.getName(), false );
}
mChannelNode.save();
mChannelNode.show();
if( expanded )
{
mChannelNode.getModel().getTree().expandPath( new TreePath( mChannelNode ) );
}
}
@Override
public void reset()
{
mChannelName.setText(
mChannelNode.getChannel().getName() );
mChannelName.requestFocus();
mChannelName.requestFocusInWindow();
mChannelEnabled.setSelected(
mChannelNode.getChannel().getEnabled() );
mChannelEnabled.requestFocus();
mChannelEnabled.requestFocusInWindow();
String aliasListName =
mChannelNode.getChannel().getAliasListName();
if( aliasListName != null )
{
AliasList selected = mChannelNode.getModel().getResourceManager()
.getPlaylistManager().getPlayist().getAliasDirectory()
.getAliasList( aliasListName );
mComboAliasLists.setSelectedItem( selected );
}
mSourceEditor.reset();
mDecodeEditor.reset();
mAuxDecodeEditor.reset();
mEventLogEditor.reset();
mRecordEditor.reset();
}
}

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import controller.channel.Channel.ChannelEvent;
/**
* Interface for receiving channel events. Each listener will receive
* notification of all channel events.
*/
public interface ChannelListener
{
/**
* Notifies the listener that a channel has changed and what has changed
*/
public void occurred( Channel channel, ChannelEvent event );
}

View File

@@ -0,0 +1,127 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.util.ArrayList;
import message.Message;
import sample.Listener;
import controller.ResourceManager;
import controller.channel.Channel.ChannelEvent;
public class ChannelManager implements ChannelListener
{
private ArrayList<Channel> mChannels = new ArrayList<Channel>();
private ArrayList<ChannelListener> mChannelListeners =
new ArrayList<ChannelListener>();
private ArrayList<Listener<Message>> mMessageListeners =
new ArrayList<Listener<Message>>();
public ChannelManager( ResourceManager resourceManager )
{
mChannelListeners.add( this );
}
/**
* Notification that a channel has been added or deleted, so that we can
* keep track of all channels and manage system-wide listeners for each
* of those channels
*/
@SuppressWarnings( "incomplete-switch" )
@Override
public void occurred( Channel channel, ChannelEvent component )
{
switch( component )
{
case CHANNEL_ADDED:
if( !mChannels.contains( channel ) )
{
mChannels.add( channel );
}
break;
case CHANNEL_DELETED:
mChannels.remove( channel );
break;
}
}
/**
* Adds a channel listener that will be added to all channels to receive
* any channel change events. This listener will automatically receive
* a channel add event as it is added to each of the existing channels.
*/
public void addListener( ChannelListener listener )
{
mChannelListeners.add( listener );
for( Channel channel: mChannels )
{
channel.addListener( listener );
}
}
/**
* Removes a channel listener from being automatically added to all channels
* to receive channel change events.
*/
public void removeListener( ChannelListener listener )
{
mChannelListeners.remove( listener );
for( Channel channel: mChannels )
{
channel.removeListener( listener );
}
}
/**
* Returns the list of channel change listeners that will be automatically
* added to all channels to receive channel change events
*/
public ArrayList<ChannelListener> getChannelListeners()
{
return mChannelListeners;
}
/**
* Returns the list of channel change listeners that will be automatically
* added to all channels to receive channel change events
*/
public ArrayList<Listener<Message>> getMessageListeners()
{
return mMessageListeners;
}
/**
* Adds a message listener that will be added to all channels to receive
* any messages.
*/
public void addListener( Listener<Message> listener )
{
mMessageListeners.add( listener );
}
/**
* Removes a message listener.
*/
public void removeListener( Listener<Message> listener )
{
mMessageListeners.remove( listener );
}
}

View File

@@ -0,0 +1,120 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
@XmlSeeAlso( { ChannelRange.class } )
public class ChannelMap
{
private String mName;
private ArrayList<ChannelRange> mRanges = new ArrayList<ChannelRange>();
public ChannelMap()
{
this( "New Channel Map" );
}
public ChannelMap( String name )
{
mName = name;
}
public String toString()
{
return mName;
}
/**
* Creates a copy of this channel map
*/
public ChannelMap copyOf()
{
ChannelMap map = new ChannelMap( mName );
for( ChannelRange range: mRanges )
{
map.addRange( range.copyOf() );
}
return map;
}
public int getInvalidRange()
{
for( int x = 0; x < mRanges.size(); x++ )
{
if( !mRanges.get( x ).isValid() )
{
return x;
}
}
return -1;
}
public ArrayList<ChannelRange> getRanges()
{
return mRanges;
}
@XmlElement( name = "range" )
public void setRanges( ArrayList<ChannelRange> ranges )
{
mRanges = ranges;
}
@XmlAttribute
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public int getFrequency( int channelNumber )
{
for( ChannelRange range: mRanges )
{
if( range.hasChannel( channelNumber ) )
{
return range.getFrequency( channelNumber );
}
}
return 0;
}
public void addRange( ChannelRange range )
{
mRanges.add( range );
}
public void deleteRange( ChannelRange range )
{
mRanges.remove( range );
}
}

View File

@@ -0,0 +1,223 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableCellEditor;
import javax.swing.text.InternationalFormatter;
import log.Log;
import net.miginfocom.swing.MigLayout;
public class ChannelMapEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private ChannelMapNode mChannelMapNode;
private JLabel mNameLabel;
private JTextField mNameText;
private ChannelMapModel mRangeModel;
private JTable mRangeTable;
private JButton mDelete;
private ChannelMap mChannelMapCopy;
public ChannelMapEditor( ChannelMapNode ChannelMap )
{
mChannelMapNode = ChannelMap;
mChannelMapCopy = mChannelMapNode.getChannelMap().copyOf();
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) );
add( new JLabel( "Channel Map" ), "span, align center" );
mNameLabel = new JLabel( "Name:" );
add( mNameLabel );
mNameText = new JTextField( mChannelMapCopy.getName() );
mNameText.addFocusListener( new FocusListener()
{
@Override public void focusGained( FocusEvent arg0 ) {}
@Override
public void focusLost( FocusEvent arg0 )
{
Log.info( "text field action event fired, new value:" + mNameText.getText() );
mChannelMapCopy.setName( mNameText.getText() );
}
} );
add( mNameText, "growx, push" );
mRangeModel = new ChannelMapModel( mChannelMapCopy );
mRangeTable = new JTable( mRangeModel );
mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) );
mRangeTable.setFillsViewportHeight( true );
mRangeTable.setAutoCreateRowSorter( true );
for( int x = 0; x < mRangeTable.getColumnCount(); x++ )
{
mRangeTable.getColumnModel().getColumn( x )
.setCellEditor( new RangeTableEditor() );
}
JScrollPane scrollPane = new JScrollPane( mRangeTable );
add( scrollPane, "span,grow,push");
JButton btnNew = new JButton( "New Range" );
btnNew.addActionListener( ChannelMapEditor.this );
add( btnNew );
mDelete = new JButton( "Delete" );
mDelete.addActionListener( ChannelMapEditor.this );
add( mDelete, "wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( ChannelMapEditor.this );
add( btnSave, "growx" );
JButton btnReset = new JButton( "Reset " );
btnReset.addActionListener( ChannelMapEditor.this );
add( btnReset, "wrap" );
}
private boolean checkForValidRanges()
{
int invalidRow = mChannelMapCopy.getInvalidRange();
if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() )
{
mRangeTable.setRowSelectionInterval( invalidRow, invalidRow );
JOptionPane.showMessageDialog( this,
"Starting channel number must be less than ending channel number",
"Channel number range error",
JOptionPane.WARNING_MESSAGE );
return false;
}
return true;
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
switch( command )
{
case "New Range":
mRangeModel.addRange( new ChannelRange() );
break;
case "Save":
if( checkForValidRanges() )
{
mChannelMapNode.getChannelMap()
.setName( mChannelMapCopy.getName() );
mChannelMapNode.getChannelMap()
.setRanges( mChannelMapCopy.getRanges() );
mChannelMapNode.save();
mChannelMapNode.refresh();
mChannelMapNode.show();
}
case "Reset":
mChannelMapCopy = mChannelMapNode.getChannelMap();
mRangeModel.setChannelMap( mChannelMapCopy );
mNameText.setText( mChannelMapCopy.getName() );
break;
case "Delete":
int row = mRangeTable.getSelectedRow();
if( row < 0 )
{
JOptionPane.showMessageDialog( this,
"Please select a channel range to delete",
"Select a range",
JOptionPane.WARNING_MESSAGE );
}
else
{
mRangeModel.removeRange(
mRangeTable.convertRowIndexToModel( row ) );
}
break;
}
}
public class RangeTableEditor extends AbstractCellEditor
implements TableCellEditor, ActionListener
{
JFormattedTextField mEditor;
public RangeTableEditor()
{
InternationalFormatter nf = new InternationalFormatter();
nf.setMinimum( 1 );
nf.setAllowsInvalid( false );
mEditor = new JFormattedTextField( nf );
}
@Override
public Object getCellEditorValue()
{
Log.info( "getCellEditorValue - I've been invoked!" );
return mEditor.getText();
}
@Override
public void actionPerformed( ActionEvent e )
{
Log.info( "Action Performed:" + e.getActionCommand() );
}
@Override
public Component getTableCellEditorComponent( JTable table,
Object value,
boolean isSelected,
int row,
int column )
{
mEditor.setValue( value );
return mEditor;
}
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
@XmlSeeAlso( ChannelMap.class )
public class ChannelMapList
{
private ArrayList<ChannelMap> mChannelMap = new ArrayList<ChannelMap>();
public ChannelMapList()
{
/* Empty constructor for JAXB */
}
@XmlElement( name = "channel_map" )
public ArrayList<ChannelMap> getChannelMap()
{
return mChannelMap;
}
public void setChannelMap( ArrayList<ChannelMap> ChannelMaps )
{
mChannelMap = ChannelMaps;
}
public void addChannelMap( ChannelMap ChannelMap )
{
mChannelMap.add( ChannelMap );
}
public void removeChannelMap( ChannelMap ChannelMap )
{
mChannelMap.remove( ChannelMap );
}
public void clearChannelMaps()
{
mChannelMap.clear();
}
/**
* Gets the named channel map, or returns an empty map
*/
public ChannelMap getChannelMap( String name )
{
for( ChannelMap map: mChannelMap )
{
if( map.getName().equalsIgnoreCase( name ) )
{
return map;
}
}
return new ChannelMap();
}
}

View File

@@ -0,0 +1,87 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import controller.ConfigurableNode;
public class ChannelMapListNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public ChannelMapListNode( ChannelMapList list )
{
super( list );
}
public ChannelMapList getChannelMapList()
{
return (ChannelMapList)getUserObject();
}
public void init()
{
for( ChannelMap channelMap: getChannelMapList().getChannelMap() )
{
ChannelMapNode node = new ChannelMapNode( channelMap );
getModel().addNode( node, ChannelMapListNode.this, getChildCount() );
node.init();
}
sort();
}
public String toString()
{
return "Channel Maps [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addSystemItem = new JMenuItem( "Add Channel Map" );
addSystemItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
ChannelMap channelMap = new ChannelMap();
getChannelMapList().addChannelMap( channelMap );
getModel().addNode( new ChannelMapNode( channelMap ),
ChannelMapListNode.this,
ChannelMapListNode.this.getChildCount() );
save();
}
} );
retVal.add( addSystemItem );
return retVal;
}
}

View File

@@ -0,0 +1,167 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import javax.swing.table.AbstractTableModel;
import log.Log;
public class ChannelMapModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
private static final int sSTART = 0;
private static final int sSTOP = 1;
private static final int sBASE = 2;
private static final int sSIZE = 3;
protected int[] mColumnWidths = { 110, 110, 110, -1 };
protected String[] mHeaders = new String[] { "Begin",
"End",
"Base",
"Size" };
private ChannelMap mChannelMap;
public ChannelMapModel( ChannelMap map )
{
mChannelMap = map;
}
public ChannelMap getChannelMap()
{
return mChannelMap;
}
public void setChannelMap( ChannelMap map )
{
mChannelMap = map;
fireTableDataChanged();
}
public void addRange( ChannelRange range )
{
mChannelMap.addRange( range );
int index = mChannelMap.getRanges().size() - 1;
fireTableRowsInserted( index, index );
}
public void removeRange( int rangeIndex )
{
if( rangeIndex < mChannelMap.getRanges().size() )
{
mChannelMap.getRanges().remove( rangeIndex );
}
fireTableRowsDeleted( rangeIndex, rangeIndex );
}
public int[] getColumnWidths()
{
return mColumnWidths;
}
public void setColumnWidths( int[] widths )
{
if( widths.length != 4 )
{
throw new IllegalArgumentException( "ChannelMapRangeModel - "
+ "column widths array should have 5 elements" );
}
else
{
mColumnWidths = widths;
}
}
@Override
public int getRowCount()
{
return mChannelMap.getRanges().size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName( int column )
{
return mHeaders[ column ];
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
ChannelRange range = mChannelMap.getRanges().get( rowIndex );
switch( columnIndex )
{
case sSTART:
return range.getFirstChannelNumber();
case sSTOP:
return range.getLastChannelNumber();
case sBASE:
return range.getBase();
case sSIZE:
return range.getSize();
}
return null;
}
public boolean isCellEditable( int row, int column )
{
return true;
}
public void setValueAt( Object value, int row, int column )
{
try
{
int parsedValue = Integer.parseInt( (String)value );
ChannelRange range = mChannelMap.getRanges().get( row );
switch( column )
{
case sSTART:
range.setFirstChannelNumber( parsedValue );
break;
case sSTOP:
range.setLastChannelNumber( parsedValue );
break;
case sBASE:
range.setBase( parsedValue );
break;
case sSIZE:
range.setSize( parsedValue );
break;
}
}
catch( Exception e )
{
Log.info( "ChannelMapModel - couldn't parse integer value "
+ "for [" + value + "] - " + e.getLocalizedMessage() );
}
}
}

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import controller.ConfigurableNode;
public class ChannelMapNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public ChannelMapNode( ChannelMap ChannelMap )
{
super( ChannelMap );
}
public void init()
{
//For consistency, but not used
}
@Override
public String getIconPath()
{
return "images/ChannelMap.png";
}
@Override
public JPanel getEditor()
{
return new ChannelMapEditor( this );
}
public ChannelMap getChannelMap()
{
return (ChannelMap)getUserObject();
}
public String toString()
{
return getChannelMap().getName();
}
public void delete()
{
ChannelMapListNode parent = (ChannelMapListNode)getParent();
parent.getChannelMapList().removeChannelMap( getChannelMap() );
save();
getModel().removeNodeFromParent( ChannelMapNode.this );
/* Show the first child, if one exists so that we don't have a
* collapsed list */
if( parent.getChildCount() > 0 )
{
((ConfigurableNode)parent.getFirstChild()).show();
}
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
delete();
}
} );
retVal.add( deleteItem );
return retVal;
}
}

View File

@@ -0,0 +1,168 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.text.DecimalFormat;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import log.Log;
import source.tuner.TunerChannel;
import controller.ConfigurableNode;
import controller.channel.Channel.ChannelEvent;
import controller.site.SiteNode;
import controller.system.SystemNode;
/**
* A channel has all of the pieces needed to wire together a source, decoder,
* event logger and recorder and be started and stopped.
*/
public class ChannelNode extends ConfigurableNode implements ChannelListener
{
private static final long serialVersionUID = 1L;
private static DecimalFormat sFORMAT = new DecimalFormat( "0.0000" );
public ChannelNode( Channel channel )
{
super( channel );
}
public void init()
{
/* Set the owning system and site names for the channel */
SiteNode siteNode = (SiteNode)getParent();
if( siteNode != null )
{
getChannel().setSite( siteNode.getSite(), false );
SystemNode systemNode = (SystemNode)siteNode.getParent();
if( systemNode != null )
{
getChannel().setSystem( systemNode.getSystem(), false );
}
}
/* Add this node as listener to receive changes from underlying channel */
getChannel().addListener( this );
/* Add the resource manager to the channel so that the channel
* can provide channel change events to all system resources */
getChannel().setResourceManager( getModel().getResourceManager() );
}
/**
* Listener method to receive channel changes from the underlying chnannel
*/
@Override
public void occurred( Channel channel, ChannelEvent event )
{
switch( event )
{
/* Refresh the node for each of these events */
case CHANGE_DECODER:
case CHANGE_ENABLED:
case CHANGE_NAME:
case CHANGE_SELECTED:
case CHANGE_SITE:
case CHANGE_SYSTEM:
case PROCESSING_STARTED:
case PROCESSING_STOPPED:
this.refresh();
break;
/* We're being deleted, so cleanup */
case CHANNEL_DELETED:
delete();
break;
default:
break;
}
}
@Override
public String getIconPath()
{
StringBuilder sb = new StringBuilder();
Channel channel = getChannel();
sb.append( channel.getDecodeConfiguration().getDecoderType().getIconPrefix() );
if( channel.getEnabled() )
{
sb.append( channel.isProcessing() ? "_green" : "_red" );
}
sb.append( ".png" );
return sb.toString();
}
@Override
public JPanel getEditor()
{
return new ChannelEditor( this );
}
public Channel getChannel()
{
return (Channel)this.getUserObject();
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( getChannel().getName() );
TunerChannel channel = getChannel().getTunerChannel();
if( channel != null )
{
sb.append( " (" );
sb.append( sFORMAT.format( (double)channel.getFrequency() / 1000000.0d ) + " MHz" );
sb.append( ")" );
}
return sb.toString();
}
public JPopupMenu getContextMenu()
{
JPopupMenu popupMenu = new JPopupMenu( "Channel Menu" );
popupMenu.add( getChannel().getContextMenu() );
return popupMenu;
}
public void delete()
{
Log.info( "Channel Node - deleting ..." );
((SiteNode)getParent()).getSite().removeChannel( getChannel() );
Log.info( "Channel Node - saving after channel delete" );
save();
Log.info( "Channel Node - removing from site node" );
getModel().removeNodeFromParent( ChannelNode.this );
}
}

View File

@@ -0,0 +1,122 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
@XmlRootElement
@XmlType( name = "range" )
public class ChannelRange
{
private int mFirst = 1;
private int mLast = 1023;
private int mBase = 450000000;
private int mSize = 12500;
/* JAXB requirement - do not remove */
public ChannelRange() {}
public ChannelRange( int first, int last, int base, int size )
{
mFirst = first;
mLast = last;
mBase = base;
mSize = size;
}
public ChannelRange copyOf()
{
return new ChannelRange( mFirst, mLast, mBase, mSize );
}
public boolean isValid()
{
return mFirst < mLast;
}
public boolean hasChannel( int channel )
{
return mFirst <= channel && channel <= mLast;
}
public int getFrequency( int channel )
{
if( hasChannel( channel ) )
{
return mBase + ( ( channel - mFirst ) * mSize );
}
else
{
return 0;
}
}
@XmlAttribute( name = "first" )
public int getFirstChannelNumber()
{
return mFirst;
}
public void setFirstChannelNumber( int first )
{
mFirst = first;
}
@XmlAttribute( name = "last" )
public void setLastChannelNumber( int last )
{
mLast = last;
}
public int getLastChannelNumber()
{
return mLast;
}
/**
* Sets the base frequency
* @param base frequency in hertz
*/
@XmlAttribute( name = "base" )
public void setBase( int base )
{
mBase = base;
}
public int getBase()
{
return mBase;
}
/**
* Sets the channel size
* @param size in hertz
*/
@XmlAttribute( name = "size" )
public void setSize( int size )
{
mSize = size;
}
public int getSize()
{
return mSize;
}
}

View File

@@ -0,0 +1,23 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
public interface ChannelSelectionListener
{
public void channelSelected( ProcessingChain channel );
}

View File

@@ -0,0 +1,721 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package controller.channel;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import log.Log;
import message.Message;
import record.Recorder;
import record.wave.ComplexWaveRecorder;
import record.wave.FloatWaveRecorder;
import sample.Broadcaster;
import sample.Listener;
import sample.Provider;
import sample.complex.ComplexSample;
import source.ComplexSource;
import source.FloatArraySource;
import source.FloatSource;
import source.Source;
import source.Source.SampleType;
import source.config.SourceConfigMixer;
import source.tuner.Tuner;
import source.tuner.TunerChannelSource;
import alias.AliasList;
import audio.AudioOutput;
import audio.AudioTypeListener;
import audio.SquelchListener;
import controller.ResourceManager;
import controller.ThreadPoolManager.ThreadType;
import controller.activity.CallEvent;
import controller.channel.Channel.ChannelType;
import controller.state.ChannelState;
import controller.state.ChannelState.State;
import decode.Decoder;
import decode.DecoderFactory;
import eventlog.EventLogType;
import eventlog.EventLogger;
/**
* A channel has all of the pieces needed to wire together a source, decoder,
* event logger and recorder and be started and stopped.
*/
public class ProcessingChain implements Listener<Message>
{
protected AtomicBoolean mRunning = new AtomicBoolean();
protected Channel mChannel;
protected Source mSource;
protected Decoder mDecoder;
protected List<Recorder> mRecorders = new ArrayList<Recorder>();
protected ArrayList<EventLogger> mEventLoggers = new ArrayList<EventLogger>();
protected AudioOutput mAudioOutput;
protected ChannelState mChannelState;
protected ResourceManager mResourceManager;
private ScheduledFuture<?> mProcessorTask;
private AliasList mAliasList;
private ComplexListener mComplexListener = new ComplexListener();
private LinkedTransferQueue<List<ComplexSample>> mComplexBuffer =
new LinkedTransferQueue<List<ComplexSample>>();
private FloatListener mFloatListener = new FloatListener();
private FloatArrayListener mFloatArrayListener = new FloatArrayListener();
private LinkedTransferQueue<Float> mFloatBuffer =
new LinkedTransferQueue<Float>();
protected Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
public ProcessingChain( Channel channel, ResourceManager resourceManager )
{
mChannel = channel;
mResourceManager = resourceManager;
if( mResourceManager != null )
{
mAliasList = mResourceManager.getPlaylistManager()
.getPlayist().getAliasDirectory().getAliasList(
mChannel.getAliasListName() );
}
}
public Listener<List<ComplexSample>> getComplexListener()
{
return mComplexListener;
}
public Listener<Float> getFloatListener()
{
return mFloatListener;
}
public Listener<Float[]> getFloatArrayListener()
{
return mFloatArrayListener;
}
public String toString()
{
return mChannel.getName();
}
public AliasList getAliasList()
{
return mAliasList;
}
public void dispose()
{
if( mProcessorTask != null )
{
mResourceManager.getThreadPoolManager().cancel( mProcessorTask );
}
if( mChannelState != null )
{
mChannelState.dispose();
}
mChannelState = null;
if( mDecoder != null )
{
mDecoder.dispose();
}
mBroadcaster.dispose();
mEventLoggers.clear();
mRecorders.clear();
if( mAudioOutput != null )
{
mAudioOutput.dispose();
}
mResourceManager = null;
}
public ResourceManager getResourceManager()
{
return mResourceManager;
}
public void start()
{
if( !mRunning.get() && mChannel.getEnabled() )
{
updateSource();
updateDecoder();
updateEventLogging();
updateRecording();
mRunning.set( true );
}
else
{
Log.info( getLogPrefix() + "start() invoked, but processing chain is "
+ "already started" );
}
}
/**
* Performs closeout operations to prepare for channel deletion
*/
public void stop()
{
if( mRunning.get() )
{
updateSource();
updateDecoder();
updateRecording();
/* Flush call events to the logger */
if( getChannel().getChannelType() == ChannelType.STANDARD )
{
getChannelState().getCallEventModel().flush();
}
updateEventLogging();
if( mAudioOutput != null )
{
mAudioOutput.setAudioPlaybackEnabled( false );
}
mRunning.set( false );
}
else
{
Log.info( getLogPrefix() + "stop() invoked, but processing chain was "
+ "already stopped" );
}
}
private String getLogPrefix()
{
return "Channel [" + mChannel.getName() + "] - ";
}
public void updateSource()
{
/* Cleanup existing source */
if( mSource != null )
{
if( mSource instanceof ComplexSource )
{
((ComplexSource)mSource).removeListener( this.getComplexListener() );
}
else if( mSource instanceof FloatSource )
{
((FloatSource)mSource).removeListener( this.getFloatListener() );
}
else if( mSource instanceof FloatArraySource )
{
((FloatArraySource)mSource)
.removeListener( this.getFloatArrayListener() );
}
if( mProcessorTask != null )
{
mResourceManager.getThreadPoolManager().cancel( mProcessorTask );
}
mSource.dispose();
mSource = null;
}
/* Get a new source if needed and start the sample processor thread */
if( mChannel.getEnabled() && mResourceManager != null )
{
mSource = mResourceManager.getSourceManager()
.getSource( ProcessingChain.this );
if( mSource != null )
{
if( mSource instanceof ComplexSource )
{
try
{
mProcessorTask =
mResourceManager.getThreadPoolManager()
.schedule( ThreadType.DECODER,
new ComplexProcessor(),
50, TimeUnit.MILLISECONDS );
((ComplexSource)mSource)
.setListener( this.getComplexListener() );
}
catch( RejectedExecutionException ree )
{
Log.error( getLogPrefix() + "ProcessingChain - "
+ "error scheduling complex sample processing "
+ "thread - " + ree.getLocalizedMessage() );
mSource.dispose();
}
}
else if( mSource instanceof FloatSource )
{
try
{
mProcessorTask =
mResourceManager.getThreadPoolManager()
.schedule( ThreadType.DECODER,
new FloatProcessor(),
50, TimeUnit.MILLISECONDS );
((FloatSource)mSource)
.setListener( this.getFloatListener() );
}
catch( RejectedExecutionException ree )
{
Log.error( getLogPrefix() + "ProcessingChain - "
+ "error scheduling float sample processing "
+ "thread - " + ree.getLocalizedMessage() );
mSource.dispose();
}
}
else if( mSource instanceof FloatArraySource )
{
try
{
mProcessorTask =
mResourceManager.getThreadPoolManager()
.schedule( ThreadType.DECODER,
new FloatProcessor(),
50, TimeUnit.MILLISECONDS );
((FloatArraySource)mSource)
.setListener( this.getFloatArrayListener() );
}
catch( RejectedExecutionException ree )
{
Log.error( getLogPrefix() + "ProcessingChain - "
+ "error scheduling float sample processing "
+ "thread - " + ree.getLocalizedMessage() );
mSource.dispose();
}
}
}
}
}
public void updateDecoder()
{
if( mChannel.getEnabled() )
{
AliasList aliasList = null;
if( mResourceManager != null )
{
aliasList = mResourceManager.getPlaylistManager()
.getPlayist().getAliasDirectory().getAliasList(
mChannel.getAliasListName() );
}
/* Setup the channel state */
mChannelState = DecoderFactory.getChannelStateNew( this, aliasList );
if( mSource == null )
{
mChannelState.setState( State.NO_TUNER );
}
else
{
mDecoder = DecoderFactory.
getDecoder( this, mSource.getSampleType(), aliasList );
if( mDecoder != null )
{
/* Register to receive decoded messages and auxiliary messages */
mDecoder.addMessageListener( ProcessingChain.this );
/* Setup the audio output processing thread */
String threadName = "SDRTrunk Audio - " +
mChannel.getSite().getName() +
"-" + mChannel.getName();
mAudioOutput = new AudioOutput( threadName );
addFloatListener( mAudioOutput );
}
}
if( mAudioOutput != null )
{
/* Register audio output to be controlled by the channel state */
mChannelState.addListener( (SquelchListener)mAudioOutput );
mChannelState.setListener( (AudioTypeListener)mAudioOutput );
}
}
}
/**
* Starts, stops or changes event logging
*/
public void updateEventLogging()
{
/* Shutdown and remove existing event loggers */
if( mEventLoggers != null )
{
for( EventLogger logger: mEventLoggers )
{
logger.stop();
}
mEventLoggers.clear();
}
/* Construct event loggers and add as message listeners */
if( mChannel.getEnabled() &&
mResourceManager != null )
{
//Get the event loggers
ArrayList<EventLogType> types = getChannel()
.getEventLogConfiguration().getLoggers();
for( EventLogType type: types )
{
EventLogger logger;
if( type == EventLogType.CALL_EVENT )
{
logger = mResourceManager.getEventLogManager()
.getLogger( this, type );
getChannelState().getCallEventModel()
.addListener( (Listener<CallEvent>)logger );
}
else
{
logger = mResourceManager.getEventLogManager()
.getLogger( this, type );
addListener( (Listener<Message>)logger );
}
logger.start();
mEventLoggers.add( logger );
}
}
}
/**
* Starts, stops or changes data recording
*/
public void updateRecording()
{
/* Remove any existing recorders */
if( mRecorders != null )
{
for( Recorder recorder: mRecorders )
{
if( recorder != null )
{
try
{
recorder.stop();
}
catch ( IOException ioe )
{
Log.error( getLogPrefix() + "Couldn't stop recorder [" +
recorder.getFileName() + "] " +
ioe.getLocalizedMessage() );
}
}
}
}
/* Start any new recorders */
if( mChannel.getEnabled() &&
mResourceManager != null &&
mDecoder != null )
{
mRecorders = mResourceManager.getRecorderManager()
.getRecorder( this );
/* Iterate any returned recorders and start/add them */
for( Recorder recorder: mRecorders )
{
try
{
recorder.start();
if( recorder instanceof FloatWaveRecorder )
{
Log.info( getLogPrefix() + "- started audio recording [" +
recorder.getFileName() + "]" );
mDecoder.addFloatListener( (FloatWaveRecorder)recorder );
}
else if( recorder instanceof ComplexWaveRecorder )
{
Log.info( getLogPrefix() + "- started baseband recording [" +
recorder.getFileName() + "]" );
mDecoder.addComplexListener( (ComplexWaveRecorder )recorder );
}
}
catch ( IOException e )
{
Log.error( getLogPrefix() + "Unable to start recorder [" +
recorder.getFileName() + "]" );
}
}
}
}
public Channel getChannel()
{
return mChannel;
}
public AudioOutput getAudioOutput()
{
return mAudioOutput;
}
public Source getSource()
{
return mSource;
}
/**
* Returns the tuner that is sourcing this channel, or null if it's being
* sourced from some other source
*/
public Tuner getTuner()
{
Tuner retVal = null;
if( mSource instanceof TunerChannelSource )
{
retVal = ((TunerChannelSource)mSource).getTuner();
}
return retVal;
}
public Decoder getDecoder()
{
return mDecoder;
}
public ChannelState getChannelState()
{
return mChannelState;
}
public boolean isRunning()
{
return mRunning.get();
}
public boolean isProcessing()
{
return isRunning() && mSource != null;
}
private source.mixer.MixerChannel getMixerChannel()
{
source.mixer.MixerChannel retVal = null;
if( mChannel.getSourceConfiguration() instanceof SourceConfigMixer )
{
SourceConfigMixer mixer = (SourceConfigMixer)mChannel.getSourceConfiguration();
retVal = mixer.getChannel();
}
return retVal;
}
@Override
public void receive( Message message )
{
if( message.isValid() )
{
mBroadcaster.receive( message );
}
}
public void addFloatListener( Listener<Float> listener )
{
if( mDecoder != null )
{
mDecoder.addFloatListener( listener );
}
}
public void removeFloatListener( Listener<Float> listener )
{
if( mDecoder != null )
{
mDecoder.removeFloatListener( listener );
}
}
public void addComplexListener( Listener<ComplexSample> listener )
{
if( mDecoder != null )
{
mDecoder.addComplexListener( listener );
}
else
{
Log.error( getLogPrefix() + "attempt to add a complex sample "
+ "listener, but the decoder is null" );
}
}
public void removeComplexListener( Listener<ComplexSample> listener )
{
if( mDecoder != null )
{
mDecoder.removeComplexListener( listener );
}
else
{
Log.error( getLogPrefix() + "attempt to remove a complex sample "
+ "listener, but the decoder is null" );
}
}
public void addListener( Listener<Message> listener )
{
mBroadcaster.addListener( listener );
}
public void addListeners( List<Listener<Message>> listeners )
{
for( Listener<Message> listener: listeners )
{
addListener( listener );
}
}
public void removeListener( Listener<Message> listener )
{
mBroadcaster.removeListener( listener );
}
/**
* Runnable float sample processing task. Schedule this task to run 20
* times a second when we're receiving complex samples from our source.
* Schedule execution via the thread pool manager, obtainable from the
* resource manager,
*/
private class FloatProcessor implements Runnable
{
@Override
public void run()
{
List<Float> samples = new ArrayList<Float>();
mFloatBuffer.drainTo( samples, 48000 );
for( Float sample: samples )
{
if( mDecoder != null )
{
mDecoder.getFloatReceiver().receive( sample );
}
}
samples.clear();
}
}
/**
* Runnable complex sample processing task. Schedule this task to run 20
* times a second when we're receiving complex samples from our source.
* Schedule execution via the thread pool manager, obtainable from the
* resource manager,
*/
private class ComplexProcessor implements Runnable
{
@Override
public void run()
{
List<List<ComplexSample>> sampleSets =
new ArrayList<List<ComplexSample>>();
mComplexBuffer.drainTo( sampleSets, 16 );
for( List<ComplexSample> samples: sampleSets )
{
for( ComplexSample sample: samples )
{
if( sample != null && mDecoder != null )
{
mDecoder.getComplexReceiver().receive( sample );
}
}
samples.clear();
}
sampleSets.clear();
}
}
public class ComplexListener implements Listener<List<ComplexSample>>
{
@Override
public void receive( List<ComplexSample> samples )
{
mComplexBuffer.add( samples );
}
}
public class FloatListener implements Listener<Float>
{
@Override
public void receive( Float sample )
{
mFloatBuffer.add( sample );
}
}
public class FloatArrayListener implements Listener<Float[]>
{
@Override
public void receive( Float[] samples )
{
mFloatBuffer.addAll( Arrays.asList( samples ) );
}
}
}