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,177 @@
/*******************************************************************************
* 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;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.tree.DefaultMutableTreeNode;
public class BaseNode extends DefaultMutableTreeNode
implements Comparable<BaseNode>
{
private static final long serialVersionUID = 1L;
/**
* Model backing the JTree and nodes. Since we're primarily interacting
* with the model via JTree tree nodes via context menus, injecting a
* reference to the model facilitates nodal access to the model's methods
*/
protected ConfigurationControllerModel mModel = null;
/**
* Implements a base node that has a reference to the underlying
* tree data model to facilitate tree node operations from nodal context
* menus.
*/
public BaseNode( Object object )
{
super( object );
}
/**
* Override this method to return a custom icon for the node
* @return
*/
public String getIconPath()
{
return null;
}
public String toString()
{
if( getUserObject() != null )
{
return (String)getUserObject();
}
else
{
return "SDRTrunk";
}
}
public JPanel getEditor()
{
return new EmptyEditor();
}
/**
* Comparator to support nodal sorting/ordering
*/
@Override
public int compareTo( BaseNode node )
{
return toString().compareTo( node.toString() );
}
/**
* This method only needs to be called once on the root node and all nodes
* in the tree will then have a reference to the model via getModel()
*/
public void setModel( ConfigurationControllerModel model )
{
mModel = model;
}
/**
* Returns reference to the SystemTreeModel. Only ConfigurableNode types
* or children are allowed in this tree, so this method assumes that the
* node or parent is of ConfigurableNode class or subclass.
*
* Reference to the model is required to enable node context operations
* like adding/deleting/stopping/starting.
*/
public ConfigurationControllerModel getModel()
{
if( mModel == null )
{
return ((BaseNode)getParent()).getModel();
}
else
{
return mModel;
}
}
/**
* Supports a context menu for any children of the BaseNode. Override the
* method in the child node to provide a custom context menu for that node.
*/
public JPopupMenu getContextMenu()
{
return null;
}
/**
* Invokes a recursive delete() on all children to perform any cleanup
* operations prior to deleting. Override this method to perform cleanup
* operations specific to the node. Remember to also either invoke
* super.delete(), or specifically call .delete() on any children of the
* node with the custom delete method.
*/
public void delete()
{
for( int x = 0; x < getChildCount(); x++ )
{
BaseNode node = (BaseNode)getChildAt( x );
//Tell the node to delete its children
node.delete();
//Delete the child node
getModel().deleteNode( node );
}
}
public void show()
{
getModel().showNode( this );
}
public void refresh()
{
getModel().nodeChanged( this );
}
public void sort()
{
List<BaseNode> nodesToSort = new ArrayList<BaseNode>();
while( children().hasMoreElements() )
{
BaseNode child = (BaseNode)children().nextElement();
nodesToSort.add( child );
getModel().removeNodeFromParent( child );
}
if( !nodesToSort.isEmpty() )
{
Collections.sort( nodesToSort );
for( BaseNode node: nodesToSort )
{
getModel().insertNodeInto( node, this, getChildCount() );
}
}
}
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* 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;
public abstract class ConfigurableNode extends BaseNode
{
private static final long serialVersionUID = 1L;
public ConfigurableNode( Object object )
{
super( object );
}
/**
* Saves changes to the node
*/
public void save()
{
getModel().getResourceManager().getPlaylistManager().save();
BaseNode parent = (BaseNode)getParent();
if( parent instanceof ConfigurableNode )
{
((ConfigurableNode)parent).sort();
}
}
}

View File

@@ -0,0 +1,280 @@
/*******************************************************************************
* 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;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
import map.IconManager;
import playlist.PlaylistNode;
import source.config.SourceConfigTuner;
import source.tuner.Tuner;
import source.tuner.TunerGroupNode;
import source.tuner.TunerNode;
import source.tuner.TunerSelectionListener;
import controller.channel.Channel;
import controller.channel.ChannelListener;
import controller.channel.ChannelNode;
import controller.site.Site;
import controller.system.System;
import controller.system.SystemListNode;
import controller.system.SystemNode;
import decode.DecoderType;
import decode.config.DecodeConfigFactory;
public class ConfigurationControllerModel extends DefaultTreeModel
{
private static final long serialVersionUID = 1L;
private JTree mTree;
private ResourceManager mResourceManager;
private ArrayList<ChannelListener> mChannelConfigChangeListeners =
new ArrayList<ChannelListener>();
private ArrayList<TunerSelectionListener> mTunerSelectionListeners =
new ArrayList<TunerSelectionListener>();
private PlaylistNode mPlaylistNode;
private TunerGroupNode mTunerGroupNode;
private IconManager mIconManagerFrame;
/**
* Implements a merged system controller and (tree) model. The user will
* primarily interact with the system nodes via the tree view, so from a
* design perspective, the model and the control are necessarily merged to
* work well with the JTree as the system view.
*/
public ConfigurationControllerModel( ResourceManager resourceManager )
{
super( new BaseNode( null ) );
mResourceManager = resourceManager;
/**
* Give the root node a reference to this model, so that all nodes can
* invoke add/delete operations on the tree via context menu
*/
((BaseNode)getRoot()).setModel( this );
}
public JTree getTree()
{
return mTree;
}
public void showIconManager()
{
if( mIconManagerFrame == null )
{
mIconManagerFrame = new IconManager( mResourceManager, mTree );
mResourceManager.getSettingsManager().addListener( mIconManagerFrame );
}
if( !mIconManagerFrame.isVisible() )
{
mIconManagerFrame.setVisible( true );
}
mIconManagerFrame.toFront();
}
/**
* init() gets invoked manually/externally, after all of the references
* are established in the constructor
*/
public void init()
{
/**
* Add the root tuner group node
*/
mTunerGroupNode = new TunerGroupNode();
insertNodeInto( mTunerGroupNode, (MutableTreeNode)root, 0 );
addTuners( mTunerGroupNode );
/**
* Add the playlist node
*/
mPlaylistNode = new PlaylistNode();
insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 );
mPlaylistNode.loadPlaylist();
}
public void showNode( DefaultMutableTreeNode node )
{
if( mTree != null && node != null )
{
mTree.setSelectionPath( new TreePath( node.getPath() ) );
}
}
public ResourceManager getResourceManager()
{
return mResourceManager;
}
public void setTree( JTree tree )
{
mTree = tree;
}
private void addTuners( TunerGroupNode parent )
{
List<Tuner> tuners = mResourceManager.getTunerManager().getTuners();
for( Tuner tuner: tuners )
{
TunerNode child = new TunerNode( tuner );
insertNodeInto( child, parent, parent.getChildCount() );
parent.sort();
show( child );
}
}
/**
* Convenience method to add a new default system, site and channel to
* the tree
*/
public void createChannel( int frequency, DecoderType decoder )
{
System system = new System();
Site site = new Site();
system.addSite( site );
Channel channel = new Channel();
channel.setDecodeConfiguration(
DecodeConfigFactory.getDecodeConfiguration( decoder ) );
SourceConfigTuner sourceConfig = new SourceConfigTuner();
sourceConfig.setFrequency( frequency );
channel.setSourceConfiguration( sourceConfig );
channel.setEnabled( true, true );
site.addChannel( channel );
mPlaylistNode.getPlaylist().getSystemList().addSystem( system );
SystemNode systemNode = new SystemNode( system );
SystemListNode systemListNode = mPlaylistNode.getSystemListNode();
addNode( systemNode, systemListNode, systemListNode.getChildCount() );
systemNode.init();
systemNode.save();
}
public void addNode( BaseNode child, BaseNode parent, int index )
{
insertNodeInto( child, parent, index );
show( child );
}
public void deleteNode( BaseNode node )
{
node.delete();
}
private void show( BaseNode node )
{
if( mTree != null )
{
mTree.scrollPathToVisible( new TreePath( node.getPath() ) );
}
}
public void addListener( TunerSelectionListener listener )
{
mTunerSelectionListeners.add( listener );
}
public void removeListener( TunerSelectionListener listener )
{
mTunerSelectionListeners.remove( listener );
}
/**
* Notifies registered listeners that the tuner has been selected
*/
public void fireTunerSelectedEvent( Tuner tuner )
{
Iterator<TunerSelectionListener> it =
mTunerSelectionListeners.iterator();
while( it.hasNext() )
{
it.next().tunerSelected( tuner );
}
}
/**
* Fires a tuner selection event for the first tuner listed in the tree.
* This is mainly used on application startup to show the spectral display
* of the first tuner in the spectrum panel.
*/
public void fireFirstTunerSelected()
{
if( mTunerGroupNode.getChildCount() > 0 )
{
TunerNode node = (TunerNode)mTunerGroupNode.getChildAt( 0 );
fireTunerSelectedEvent( node.getTuner() );
}
}
public ArrayList<Channel> getCurrentChannelConfigs()
{
return getChannelConfigs( mPlaylistNode );
}
private ArrayList<Channel> getChannelConfigs( DefaultMutableTreeNode parent )
{
ArrayList<Channel> configs = new ArrayList<Channel>();
Enumeration<DefaultMutableTreeNode> nodes = parent.children();
while( nodes.hasMoreElements() )
{
DefaultMutableTreeNode node = nodes.nextElement();
if( node instanceof ChannelNode )
{
configs.add( ((ChannelNode)node).getChannel() );
}
//Recursive call
configs.addAll( getChannelConfigs( node ) );
}
return configs;
}
}

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* 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;
import java.awt.BorderLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
public class ConfigurationEditor extends JPanel
implements TreeSelectionListener
{
private static final long serialVersionUID = 1L;
/**
* Host for configuration editor display panels
*/
public ConfigurationEditor()
{
init();
}
public void init()
{
setPanel( new EmptyEditor() );
}
public void setPanel( JPanel display )
{
removeAll();
setLayout( new BorderLayout() );
JScrollPane scroll = new JScrollPane( display );
add( scroll, BorderLayout.CENTER );
revalidate();
repaint();
}
@Override
public void valueChanged( TreeSelectionEvent e )
{
JTree tree = (JTree)e.getSource();
BaseNode node =
(BaseNode)tree.getLastSelectedPathComponent();
if( node != null )
{
setPanel( node.getEditor() );
}
}
}

View File

@@ -0,0 +1,117 @@
/*******************************************************************************
* 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;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeSelectionModel;
import net.miginfocom.swing.MigLayout;
public class ConfigurationTreePanel extends JPanel
{
private static final long serialVersionUID = 1L;
protected JTree mSystemTree;
protected ConfigurationControllerModel mTreeModel;
public ConfigurationTreePanel( ConfigurationControllerModel model )
{
mTreeModel = model;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") );
mSystemTree = new JTree( mTreeModel );
mSystemTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION );
mSystemTree.setShowsRootHandles( true );
mSystemTree.addMouseListener( new Handler() );
mSystemTree.setCellRenderer( new ControllerTreeCellRenderer() );
//Give the model a reference to the tree, so that it can force newly
//added nodes to be displayed
mTreeModel.setTree( mSystemTree );
JScrollPane treeScroll = new JScrollPane( mSystemTree );
add( treeScroll, "cell 0 0,span,grow" );
}
public void addTreeSelectionListener( TreeSelectionListener listener )
{
mSystemTree.addTreeSelectionListener( listener );
}
public class Handler implements MouseListener
{
public Handler()
{
}
@Override
public void mouseClicked( MouseEvent event )
{
if( SwingUtilities.isRightMouseButton( event ) )
{
int row = mSystemTree.getRowForLocation( event.getX(),
event.getY() );
if( row != -1 )
{
mSystemTree.setSelectionRow( row );
Object selectedNode =
mSystemTree.getLastSelectedPathComponent();
if( selectedNode instanceof BaseNode )
{
JPopupMenu contextMenu =
((BaseNode)selectedNode).getContextMenu();
if( contextMenu != null )
{
contextMenu.show( mSystemTree,
event.getX(),
event.getY() );
}
}
}
}
}
@Override
public void mousePressed( MouseEvent e ){}
@Override
public void mouseReleased( MouseEvent e ){}
@Override
public void mouseEntered( MouseEvent e ){}
@Override
public void mouseExited( MouseEvent e ){}
}
}

View File

@@ -0,0 +1,134 @@
/*******************************************************************************
* 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;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import map.MapPanel;
import message.Message;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
import com.jidesoft.swing.JideSplitPane;
import com.jidesoft.swing.JideTabbedPane;
import controller.activity.CallEventPanel;
import controller.activity.MessageActivityPanel;
import controller.state.ChannelStateList;
public class ControllerPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private ChannelStateList mChannelStateList;
private CallEventPanel mCallEventPanel = new CallEventPanel();
private MessageActivityPanel mMessageActivityPanel =
new MessageActivityPanel();
private JideTabbedPane mTabbedPane;
protected ConfigurationTreePanel mSystemControlViewPanel;
protected ConfigurationEditor mConfigurationEditor;
protected JideSplitPane mSystemControlSplitPane;
protected JTable mChannelActivityTable = new JTable();
protected ResourceManager mResourceManager;
public ControllerPanel( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
init();
}
private void init()
{
setLayout( new MigLayout( "insets 0 0 0 0 ",
"[grow,fill]",
"[grow,fill]") );
//System Configuration View and Editor
mConfigurationEditor = new ConfigurationEditor();
mSystemControlViewPanel =
new ConfigurationTreePanel( mResourceManager.getController() );
mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor );
mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT );
mSystemControlSplitPane.setDividerSize( 5 );
mSystemControlSplitPane.add( mSystemControlViewPanel );
mSystemControlSplitPane.add( mConfigurationEditor );
//Tabbed View - configuration, calls, messages, map
mTabbedPane = new JideTabbedPane();
mTabbedPane.setFont( this.getFont() );
mTabbedPane.setForeground( Color.BLACK );
mTabbedPane.addTab( "Configuration", mSystemControlSplitPane );
mTabbedPane.addTab( "Calls", mCallEventPanel );
mTabbedPane.addTab( "Messages", mMessageActivityPanel );
/**
* Add mapping services and map panel to a new tab
*/
/* Add Map Service as message listener to receive all messages */
mResourceManager.getChannelManager().addListener(
(Listener<Message>)mResourceManager.getMapService() );
MapPanel mapPanel = new MapPanel( mResourceManager );
mTabbedPane.addTab( "Map", mapPanel );
/* Channel state list */
mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() );
/* Add message activity panel to receive channel selection events so
* that we can display the correct message listings */
mResourceManager.getChannelManager().addListener( mMessageActivityPanel );
/* Add call event panel to receive channel selection events so
* that we can display the correct call event listings */
mResourceManager.getChannelManager().addListener( mCallEventPanel );
/* Register ChannelStateList as a listener on the channel manager to
* receive channel starts and stops and deletes */
mResourceManager.getChannelManager().addListener( mChannelStateList );
JScrollPane channelStateListScroll = new JScrollPane();
channelStateListScroll.getViewport().setView( mChannelStateList );
channelStateListScroll.setPreferredSize( new Dimension( 150,
(int)channelStateListScroll.getPreferredSize().getHeight() ) );
JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT );
channelSplit.setDividerSize( 5 );
channelSplit.add( channelStateListScroll );
channelSplit.add( mTabbedPane );
add( channelSplit );
}
public ConfigurationControllerModel getController()
{
return mResourceManager.getController();
}
}

View File

@@ -0,0 +1,106 @@
/*******************************************************************************
* 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;
import java.awt.Component;
import java.awt.Image;
import java.util.HashMap;
import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeCellRenderer;
public class ControllerTreeCellRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
public static final int MAX_ICON_HEIGHT = 16;
private HashMap<String,ImageIcon> mIcons = new HashMap<String,ImageIcon>();
public ControllerTreeCellRenderer()
{
}
@Override
public Component getTreeCellRendererComponent( JTree tree,
Object value, boolean selected, boolean expanded, boolean isLeaf,
int row, boolean focused )
{
Component c = super.getTreeCellRendererComponent( tree, value, selected,
expanded, isLeaf, row, focused );
if( value instanceof BaseNode )
{
ImageIcon icon = getIcon( (BaseNode)value );
if( icon != null )
{
if( icon.getIconHeight() > 16 )
{
}
else
{
setIcon( icon );
}
}
}
return c;
}
private ImageIcon getIcon( BaseNode node )
{
ImageIcon icon = null;
String filepath = node.getIconPath();
if( filepath != null )
{
if( mIcons.containsKey( filepath ) )
{
icon = mIcons.get( filepath );
}
else
{
icon = new ImageIcon( filepath );
if( icon.getIconHeight() > MAX_ICON_HEIGHT )
{
Image image = icon.getImage();
double scale = (double)icon.getIconHeight() / (double)MAX_ICON_HEIGHT;
int scaledWidth = (int)( (double)icon.getIconWidth() / scale );
Image scaledImage = image.getScaledInstance( scaledWidth,
MAX_ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH );
mIcons.put( filepath, new ImageIcon( scaledImage ) );
}
else
{
mIcons.put( filepath, icon );
}
}
}
return icon;
}
}

View File

@@ -0,0 +1,36 @@
/*******************************************************************************
* 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;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
public abstract class Editor extends JPanel
{
private static final long serialVersionUID = 1L;
public Editor()
{
setLayout( new MigLayout() );
}
public abstract void save();
public abstract void reset();
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* 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;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class EmptyEditor extends JPanel
{
private static final long serialVersionUID = 1L;
public EmptyEditor()
{
setLayout( new BorderLayout() );
setPreferredSize( new Dimension( 200, (int)getPreferredSize().getHeight() ) );
String text = "<html>"
+ "<p>SDR Trunk</p>"
+ "<br>"
+ "<p>Use the tree structure to the left to configure SDRTrunk"
+ " to use multiple SDR tuners and sound card sources to"
+ " decode multiple trunked and digital radio channels.</p>"
+ "<br>"
+ "<p>Each tree branch can be accessed by clicking on the branch"
+ " and a configuration editor will appear here.</p>"
+ "<br>"
+ "<p>Many of the branches contain context sensitive menus that "
+ " you can access by right-clicking on the node.</p>"
+ "<br>"
+ "<p>To begin, right-click on the 'Systems' branch under 'Playlist'"
+ "and create a new 'System'. Right-click the 'System' node to create "
+ "a new 'Site'. Right-click on the 'Site' node to create a new Channel.</p>"
+ "<br>"
+ "<p>In the new channel, select a channel source and decoder "
+ "and select event logging and recording options. Check 'Enabled'"
+ " and hit the 'Save' button to begin decoding.</p>"
+ "</html>";
add( new JLabel( text ), BorderLayout.NORTH );
}
}

View File

@@ -0,0 +1,159 @@
/*******************************************************************************
* 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;
import map.MapService;
import playlist.PlaylistManager;
import record.RecorderManager;
import settings.SettingsManager;
import source.SourceManager;
import source.tuner.TunerManager;
import controller.channel.ChannelManager;
import eventlog.EventLogManager;
/**
* Manager for system wide resources
*/
public class ResourceManager
{
private ChannelManager mChannelManager;
private EventLogManager mEventLogManager;
private PlaylistManager mPlaylistManager;
private RecorderManager mRecorderManager;
private SettingsManager mSettingsManager;
private SourceManager mSourceManager;
private TunerManager mTunerManager;
private ConfigurationControllerModel mController;
private MapService mMapService;
private ThreadPoolManager mThreadPoolManager;
public ResourceManager()
{
this( new SettingsManager(),
new PlaylistManager(),
new EventLogManager(),
new RecorderManager() );
}
public ResourceManager( SettingsManager settingsManager,
PlaylistManager playlistManager,
EventLogManager eventLogManager,
RecorderManager recorderManager )
{
mSettingsManager = settingsManager;
mPlaylistManager = playlistManager;
mEventLogManager = eventLogManager;
mRecorderManager = recorderManager;
/**
* ChannelManager requires a reference to the ResourceManager so that
* channel processing chains can access system wide resources
*/
mChannelManager = new ChannelManager( this );
mSourceManager = new SourceManager( this );
mTunerManager = new TunerManager( this );
mController = new ConfigurationControllerModel( this );
mMapService = new MapService( this );
mThreadPoolManager = new ThreadPoolManager();
}
public ConfigurationControllerModel getController()
{
return mController;
}
public ThreadPoolManager getThreadPoolManager()
{
return mThreadPoolManager;
}
public void setController( ConfigurationControllerModel controller )
{
mController = controller;
}
public TunerManager getTunerManager()
{
return mTunerManager;
}
public MapService getMapService()
{
return mMapService;
}
public SettingsManager getSettingsManager()
{
return mSettingsManager;
}
public void setSettingsManager( SettingsManager manager )
{
mSettingsManager = manager;
}
public PlaylistManager getPlaylistManager()
{
return mPlaylistManager;
}
public void setPlaylistManager( PlaylistManager aliasManager )
{
mPlaylistManager = aliasManager;
}
public EventLogManager getEventLogManager()
{
return mEventLogManager;
}
public void setEventLogManager( EventLogManager eventLogManager )
{
mEventLogManager = eventLogManager;
}
public RecorderManager getRecorderManager()
{
return mRecorderManager;
}
public void setRecorderManager( RecorderManager recorderManager )
{
mRecorderManager = recorderManager;
}
public SourceManager getSourceManager()
{
return mSourceManager;
}
public void setSourceManager( SourceManager sourceManager )
{
mSourceManager = sourceManager;
}
public ChannelManager getChannelManager()
{
return mChannelManager;
}
public void setChannelManager( ChannelManager channelManager )
{
mChannelManager = channelManager;
}
}

View File

@@ -0,0 +1,97 @@
/*******************************************************************************
* 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;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class ThreadPoolManager
{
private int THREAD_POOL_SIZE = 10;
private int MAX_TASK_COUNT = 24;
private HashMap<ScheduledFuture<?>,ThreadType> mTasks =
new HashMap<ScheduledFuture<?>,ThreadType>();
private ScheduledExecutorService mExecutor;
public ThreadPoolManager()
{
}
public ScheduledFuture<?> schedule( ThreadType type,
Runnable command,
long period,
TimeUnit unit )
throws RejectedExecutionException
{
if( mExecutor == null )
{
mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE );
}
if( mTasks.size() <= MAX_TASK_COUNT )
{
ScheduledFuture<?> task =
mExecutor.scheduleAtFixedRate( command, 0, period, unit );
mTasks.put( task, type );
return task;
}
else
{
throw new RejectedExecutionException( "Cannot schedule task - at "
+ "max task count [" + mTasks.size() + "]" );
}
}
public void cancel( ScheduledFuture<?> task )
{
task.cancel( true );
ThreadType type = mTasks.remove( task );
}
public int getTaskCount( ThreadType type )
{
int count = 0;
for( ThreadType current: mTasks.values() )
{
if( current == type )
{
count++;
}
}
return count;
}
public enum ThreadType
{
SOURCE_SAMPLE_PROCESSING,
DECIMATION,
DECODER;
}
}

View File

@@ -0,0 +1,69 @@
/*******************************************************************************
* 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.activity;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import net.miginfocom.swing.MigLayout;
public class ActivitySummaryFrame extends JFrame
{
private static final long serialVersionUID = 1L;
public ActivitySummaryFrame( String summary )
{
this( summary, null );
}
public ActivitySummaryFrame( String summary, Component displayOver )
{
setTitle( "Activity Summary" );
setLocationRelativeTo( displayOver );
setSize( 400, 400 );
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill][]" ) );
JTextArea summaryText = new JTextArea( summary );
JScrollPane scroller = new JScrollPane( summaryText );
scroller.setViewportView( summaryText );
add( scroller, "wrap" );
JButton close = new JButton( "Close" );
close.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
dispose();
}
} );
add( close );
setVisible( true );
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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.activity;
import message.Message;
import sample.Listener;
public interface ActivitySummaryProvider extends Listener<Message>
{
public String getSummary();
}

View File

@@ -0,0 +1,226 @@
/*******************************************************************************
* 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.activity;
import java.text.SimpleDateFormat;
import java.util.Date;
import alias.Alias;
import alias.AliasList;
import decode.DecoderType;
public abstract class CallEvent
{
protected long mEventTime = System.currentTimeMillis();
private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd','HHmmss" );
protected DecoderType mDecoderType;
protected CallEventType mCallEventType;
protected AliasList mAliasList;
protected String mFromID;
protected String mToID;
protected String mDetails;
public CallEvent( DecoderType decoder,
CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
String details )
{
mDecoderType = decoder;
mCallEventType = callEventType;
mAliasList = aliasList;
mFromID = fromID;
mToID = toID;
mDetails = details;
}
public DecoderType getDecoderType()
{
return mDecoderType;
}
public CallEventType getCallEventType()
{
return mCallEventType;
}
public AliasList getAliasList()
{
return mAliasList;
}
public void setAliasList( AliasList aliasList )
{
mAliasList = aliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
public long getEventTime()
{
return mEventTime;
}
public String getFromID()
{
return mFromID;
}
public void setFromID( String id )
{
mFromID = id;
}
public String getToID()
{
return mToID;
}
public void setToID( String id )
{
mToID = id;
}
public String getDetails()
{
return mDetails;
}
public void setDetails( String details )
{
mDetails = details;
}
public abstract Alias getFromIDAlias();
public abstract Alias getToIDAlias();
public abstract int getChannel();
public abstract int getFrequency();
public static String getCSVHeader()
{
return "DATE,TIME,DECODER,EVENT,FROM,FROM_ALIAS,TO,TO_ALIAS,"
+ "CHANNEL,FREQUENCY,DETAILS";
}
public String toCSV()
{
StringBuilder sb = new StringBuilder();
sb.append( "'" );
sb.append( mSDF.format( new Date( getEventTime() ) ) );
sb.append( "','" );
sb.append( getDecoderType().toString() );
sb.append( "','" );
sb.append( getCallEventType().toString() );
sb.append( "'," );
if( getFromID() != null )
{
sb.append( "'" );
sb.append( getFromID() );
sb.append( "'" );
}
sb.append( "," );
Alias fromAlias = getFromIDAlias();
if( fromAlias != null )
{
sb.append( "'" );
sb.append( fromAlias.getName() );
sb.append( "'" );
}
sb.append( "," );
if( getToID() != null )
{
sb.append( "'" );
sb.append( getToID() );
sb.append( "'" );
}
sb.append( "," );
Alias toAlias = getToIDAlias();
if( toAlias != null )
{
sb.append( "'" );
sb.append( toAlias.getName() );
sb.append( "'" );
}
sb.append( ",'" );
sb.append( getChannel() );
sb.append( "','" );
sb.append( getFrequency() );
sb.append( "'," );
if( getDetails() != null )
{
sb.append( "'" );
sb.append( getDetails() );
sb.append( "'" );
}
return sb.toString();
}
public enum CallEventType
{
ACKNOWLEDGE( "Acknowledge" ),
CALL_DETECT( "Call Detect" ),
CALL_START( "Call Start" ),
CALL_START_UNIQUE_ID( "Call Start" ),
CALL_START_NO_TUNER( "Call - No Tuner" ),
CALL_END( "Call End" ),
CALL_TIMEOUT( "Call Timeout" ),
EMERGENCY( "EMERGENCY" ),
GPS( "GPS" ),
ID_ANI( "ANI" ),
ID_UNIQUE( "Unique ID" ),
PAGE( "Page" ),
REGISTER( "Register" ),
REGISTER_ESN( "ESN" ),
SDM( "SDM" ),
STATUS( "Status" ),
UNKNOWN( "Unknown" );
private String mLabel;
private CallEventType( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return mLabel;
}
}
}

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* 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.activity;
import javax.swing.ImageIcon;
import javax.swing.table.DefaultTableCellRenderer;
import settings.SettingsManager;
import alias.Alias;
/**
* Custom renderer for JTable cells that contain Alias objects. Renders the
* alias name, and if there is an icon name, attempts to get that icon from
* the settings manager and render it.
*/
public class CallEventAliasCellRenderer extends DefaultTableCellRenderer
{
private SettingsManager mSettingsManager;
public CallEventAliasCellRenderer( SettingsManager settingsManager )
{
super();
mSettingsManager = settingsManager;
}
public void setValue( Object obj )
{
if( obj != null && obj instanceof Alias )
{
Alias alias = (Alias)obj;
setText( alias.getName() );
ImageIcon icon = getIcon( alias, 12 );
if( icon != null )
{
setIcon( icon );
}
}
else
{
setText( " " );
setIcon( null );
}
}
private ImageIcon getIcon( Alias alias, int height )
{
if( mSettingsManager != null && alias != null )
{
return mSettingsManager.getImageIcon( alias.getIconName(), height );
}
return null;
}
}

View File

@@ -0,0 +1,176 @@
/*******************************************************************************
* 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.activity;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import javax.swing.table.AbstractTableModel;
import log.Log;
import sample.Broadcaster;
import sample.Listener;
public class CallEventModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
private static final int DEFAULT_ICON_HEIGHT = 14;
private static final int sTIME = 0;
private static final int sEVENT = 1;
private static final int sFROM_ID = 2;
private static final int sFROM_ALIAS = 3;
private static final int sTO_ID = 4;
private static final int sTO_ALIAS =5;
private static final int sCHANNEL = 6;
private static final int sFREQUENCY = 7;
private static final int sDETAILS = 8;
protected int mMaxMessages = 500;
protected LinkedList<CallEvent> mEvents = new LinkedList<CallEvent>();
protected String[] mHeaders = new String[] { "Time",
"Event",
"From",
"From Alias",
"To",
"To Alias",
"Channel",
"Frequency",
"Details" };
private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" );
private Broadcaster<CallEvent> mBroadcaster = new Broadcaster<CallEvent>();
private boolean mNewMessagesFirst = true;
public CallEventModel()
{
}
public void dispose()
{
mEvents.clear();
mBroadcaster.dispose();
}
public void flush()
{
/* Flush all events to listeners ( normally the call event logger ) */
for( CallEvent event: mEvents )
{
mBroadcaster.receive( event );
}
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount( int count )
{
mMaxMessages = count;
}
public void add( final CallEvent event )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
mEvents.addFirst( event );
CallEventModel.this.fireTableRowsInserted( 0, 0 );
prune();
}
} );
}
private void prune()
{
while( mEvents.size() > mMaxMessages )
{
CallEvent event = mEvents.removeLast();
mBroadcaster.receive( event );
super.fireTableRowsDeleted( mEvents.size(), mEvents.size() );
}
}
@Override
public int getRowCount()
{
return mEvents.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName( int column )
{
return mHeaders[ column ];
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
switch( columnIndex )
{
case sTIME:
return mSDFTime.format( mEvents.get( rowIndex ).getEventTime() );
case sEVENT:
return mEvents.get( rowIndex ).getCallEventType();
case sFROM_ID:
return mEvents.get( rowIndex ).getFromID();
case sFROM_ALIAS:
return mEvents.get( rowIndex ).getFromIDAlias();
case sTO_ID:
return mEvents.get( rowIndex ).getToID();
case sTO_ALIAS:
return mEvents.get( rowIndex ).getToIDAlias();
case sCHANNEL:
return mEvents.get( rowIndex ).getChannel();
case sFREQUENCY:
return mEvents.get( rowIndex ).getFrequency();
case sDETAILS:
return mEvents.get( rowIndex ).getDetails();
}
return null;
}
public void addListener( Listener<CallEvent> listener )
{
mBroadcaster.addListener( listener );
}
public void removeListener( Listener<CallEvent> listener )
{
mBroadcaster.removeListener( listener );
}
}

View File

@@ -0,0 +1,84 @@
/*******************************************************************************
* 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.activity;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import net.miginfocom.swing.MigLayout;
import controller.channel.Channel;
import controller.channel.Channel.ChannelEvent;
import controller.channel.ChannelListener;
public class CallEventPanel extends JPanel implements ChannelListener
{
private static final long serialVersionUID = 1L;
private JScrollPane mEmptyScroller;
private Channel mDisplayedChannel;
public CallEventPanel()
{
setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") );
JTable table = new JTable( new CallEventModel() );
table.setAutoCreateRowSorter( true );
table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
mEmptyScroller = new JScrollPane( table );
add( mEmptyScroller );
}
@Override
public void occurred( Channel channel, ChannelEvent event )
{
if( event == ChannelEvent.CHANGE_SELECTED && channel.getSelected() )
{
if( mDisplayedChannel == null ||
( mDisplayedChannel != null && mDisplayedChannel != channel ) )
{
removeAll();
JScrollPane scroller = new JScrollPane(
channel.getProcessingChain().getChannelState()
.getCallEventTable() );
add( scroller );
mDisplayedChannel = channel;
revalidate();
repaint();
}
}
else if( event == ChannelEvent.PROCESSING_STOPPED ||
event == ChannelEvent.CHANNEL_DELETED )
{
if( mDisplayedChannel != null && mDisplayedChannel == channel )
{
mDisplayedChannel = null;
removeAll();
add( mEmptyScroller );
revalidate();
repaint();
}
}
}
}

View File

@@ -0,0 +1,155 @@
/*******************************************************************************
* 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.activity;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import javax.swing.table.AbstractTableModel;
import message.Message;
import sample.Listener;
public class MessageActivityModel extends AbstractTableModel
implements Listener<Message>
{
private static final long serialVersionUID = 1L;
private static final int sTIME = 0;
private static final int sPROTOCOL = 1;
private static final int sERROR_STATUS = 2;
private static final int sMESSAGE = 3;
private static final int sMESSAGE_BITS = 4;
protected int mMaxMessages = 500;
protected LinkedList<Message> mMessages = new LinkedList<Message>();
protected int[] mColumnWidths = { 110, 110, 110, -1, -1 };
protected String[] mHeaders = new String[] { "Time",
"Protocol",
"Error Check",
"Message",
"Binary" };
private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" );
private boolean mNewMessagesFirst = true;
public MessageActivityModel()
{
}
public void dispose()
{
mMessages.clear();
}
public int[] getColumnWidths()
{
return mColumnWidths;
}
public void setColumnWidths( int[] widths )
{
if( widths.length != 5 )
{
throw new IllegalArgumentException( "MessageActivityModel - "
+ "column widths array should have 5 elements" );
}
else
{
mColumnWidths = widths;
}
}
public int getMaxMessageCount()
{
return mMaxMessages;
}
public void setMaxMessageCount( int count )
{
mMaxMessages = count;
}
public void receive( final Message message )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
mMessages.addFirst( message );
MessageActivityModel.this.fireTableRowsInserted( 0, 0 );
prune();
}
} );
}
private void prune()
{
while( mMessages.size() > mMaxMessages )
{
mMessages.removeLast();
super.fireTableRowsDeleted( mMessages.size() - 1, mMessages.size() - 1 );
}
}
@Override
public int getRowCount()
{
return mMessages.size();
}
@Override
public int getColumnCount()
{
return mHeaders.length;
}
public String getColumnName( int column )
{
return mHeaders[ column ];
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
Message message = mMessages.get( rowIndex );
switch( columnIndex )
{
case sTIME:
return mSDFTime.format( message.getDateReceived() );
case sPROTOCOL:
return message.getProtocol();
case sERROR_STATUS:
return message.getErrorStatus();
case sMESSAGE:
return message.getMessage();
case sMESSAGE_BITS:
return message.getBinaryMessage();
}
return null;
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* 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.activity;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import net.miginfocom.swing.MigLayout;
import controller.channel.Channel;
import controller.channel.Channel.ChannelEvent;
import controller.channel.ChannelListener;
public class MessageActivityPanel extends JPanel implements ChannelListener
{
private static final long serialVersionUID = 1L;
private JScrollPane mEmptyScrollPane;
private Channel mDisplayedChannel;
public MessageActivityPanel()
{
setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") );
mEmptyScrollPane =
new JScrollPane( new JTable( new MessageActivityModel() ) );
add( mEmptyScrollPane );
}
@Override
public void occurred( Channel channel, ChannelEvent event )
{
if( event == ChannelEvent.CHANGE_SELECTED && channel.getSelected() )
{
if( mDisplayedChannel == null ||
( mDisplayedChannel != null && mDisplayedChannel != channel ) )
{
removeAll();
JScrollPane scroll =
new JScrollPane( channel.getProcessingChain()
.getChannelState().getMessageActivityTable() );
add( scroll );
mDisplayedChannel = channel;
revalidate();
repaint();
}
}
else if( event == ChannelEvent.PROCESSING_STOPPED ||
event == ChannelEvent.CHANNEL_DELETED )
{
if( mDisplayedChannel != null && mDisplayedChannel == channel )
{
mDisplayedChannel = null;
removeAll();
add( mEmptyScrollPane );
revalidate();
repaint();
}
}
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* 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.activity;
import alias.Alias;
/**
* Getter methods to support the Message Activity Table/Model
* @author denny
*
*/
public interface MessageDetailsProvider
{
/**
* Indicates if the message is valid and has passed crc/integrity checks
*/
public abstract boolean isValid();
/**
* Status of the CRC check of the message
*/
public abstract String getErrorStatus();
/**
* Parsed Message
* @return
*/
public abstract String getMessage();
/**
* Raw ( 0 & 1 ) message bits
*/
public abstract String getBinaryMessage();
/**
* Decoded protocol
*/
public abstract String getProtocol();
/**
* Event - call, data, idle, etc.
*/
public abstract String getEventType();
/**
* Formatted from identifier
*/
public abstract String getFromID();
/**
* From identifier alias (from AliasManager)
*/
public abstract Alias getFromIDAlias();
/**
* Formatted to identifier
*/
public abstract String getToID();
/**
* To identifier alias (from AliasManager)
* @return
*/
public abstract Alias getToIDAlias();
}

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 ) );
}
}
}

View File

@@ -0,0 +1,40 @@
/*******************************************************************************
* 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.config;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import record.config.RecordConfiguration;
import source.config.SourceConfiguration;
import decode.config.AuxDecodeConfiguration;
import decode.config.DecodeConfiguration;
import eventlog.config.EventLogConfiguration;
@XmlSeeAlso( { AuxDecodeConfiguration.class,
DecodeConfiguration.class,
EventLogConfiguration.class,
RecordConfiguration.class,
SourceConfiguration.class } )
@XmlRootElement( name = "configuration" )
public abstract class Configuration
{
public Configuration()
{
}
}

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* 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.site;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import controller.channel.Channel;
@XmlSeeAlso( { Channel.class } )
@XmlRootElement( name = "site" )
public class Site
{
private String mName;
private ArrayList<Channel> mChannel = new ArrayList<Channel>();
public Site()
{
this( "New Site" );
}
public Site( String name )
{
mName = name;
}
public String toString()
{
return mName;
}
@XmlAttribute
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public ArrayList<Channel> getChannel()
{
return mChannel;
}
public void setChannel( ArrayList<Channel> configs )
{
mChannel = configs;
}
public void addChannel( Channel channel )
{
mChannel.add( channel );
}
public void removeChannel( Channel channel )
{
mChannel.remove( channel );
}
public void removeAllChannels()
{
mChannel.clear();
}
}

View File

@@ -0,0 +1,101 @@
/*******************************************************************************
* 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.site;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
public class SiteEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private SiteNode mSiteNode;
private JLabel mLabelName;
private JTextField mTextSite;
public SiteEditor( SiteNode site )
{
mSiteNode = site;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout() );
setBorder( BorderFactory.createTitledBorder( "Site" ) );
mLabelName = new JLabel( "Name:" );
add( mLabelName, "align right" );
mTextSite = new JTextField( mSiteNode.getSite().getName() );
add( mTextSite, "grow, wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( SiteEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( SiteEditor.this );
add( btnReset, "wrap" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
boolean expanded = mSiteNode.getModel().getTree()
.isExpanded( new TreePath( mSiteNode ) );
String name = mTextSite.getText();
if( name != null )
{
mSiteNode.getSite().setName( name );
mSiteNode.save();
mSiteNode.show();
if( expanded )
{
mSiteNode.getModel().getTree().expandPath( new TreePath( mSiteNode ) );
}
}
else
{
JOptionPane.showMessageDialog( SiteEditor.this, "Please enter a system name" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextSite.setText( mSiteNode.getSite().getName() );
}
}
}

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.site;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.tree.DefaultMutableTreeNode;
import controller.ConfigurableNode;
import controller.channel.Channel;
import controller.channel.ChannelNode;
import controller.system.System;
import controller.system.SystemNode;
public class SiteNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public SiteNode( Site site )
{
super( site );
}
public void init()
{
for( Channel channel: getSite().getChannel() )
{
ChannelNode node = new ChannelNode( channel );
getModel().insertNodeInto( node, SiteNode.this, getChildCount() );
node.init();
}
sort();
}
public String getIconPath()
{
String retVal = "images/site.png";
@SuppressWarnings( "unchecked" )
Enumeration<DefaultMutableTreeNode> nodes = children();
while( nodes.hasMoreElements() )
{
ChannelNode child = (ChannelNode)nodes.nextElement();
if( child.getIconPath().contains( "green.png" ) )
{
return "images/site_green.png";
}
else if( child.getIconPath().contains( "red.png" ) )
{
retVal = "images/site_red.png";
}
}
return retVal;
}
@Override
public JPanel getEditor()
{
return new SiteEditor( this );
}
public Site getSite()
{
return (Site)getUserObject();
}
public String toString()
{
return getSite().getName();
}
public void delete()
{
for( int x = 0; x < getChildCount(); x++ )
{
((ChannelNode)getChildAt( x )).delete();
}
((SystemNode)getParent()).getSystem().removeSite( getSite() );
save();
getModel().removeNodeFromParent( SiteNode.this );
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addChannelItem = new JMenuItem( "Add Channel" );
addChannelItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
System system = ((SystemNode)getParent()).getSystem();
Channel channel = new Channel();
channel.setSystem( system, false );
channel.setSite( getSite(), false );
getSite().addChannel( channel );
ChannelNode node = new ChannelNode( channel );
getModel().addNode( node,
SiteNode.this,
SiteNode.this.getChildCount() );
sort();
save();
node.show();
/* Give channel reference to the resource manager and the
* channel will register the listeners and send a channel
* add event */
channel.setResourceManager( getModel().getResourceManager() );
}
} );
retVal.add( addChannelItem );
retVal.add( new JSeparator() );
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,80 @@
/*******************************************************************************
* 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.state;
import message.Message;
import sample.Broadcaster;
import sample.Listener;
import controller.state.ChannelState.ChangedAttribute;
/**
* ChannelState for auxiliary decoders.
*/
public abstract class AuxChannelState implements Listener<Message>
{
protected ChannelState mParentChannelState;
protected Broadcaster<ChangedAttribute> mChangeBroadcaster =
new Broadcaster<ChangedAttribute>();
public AuxChannelState( ChannelState parentState )
{
mParentChannelState = parentState;
}
/**
* Prepare for garbage collect
*/
public void dispose()
{
mChangeBroadcaster.dispose();
mParentChannelState = null;
}
/**
* Call fade - change display and await a call to reset()
*/
public abstract void fade();
/**
* Activity summary for this decoder
*/
public abstract String getActivitySummary();
/**
* Call end - cleanup the display
*/
public abstract void reset();
public void addListener( Listener<ChangedAttribute> listener )
{
mChangeBroadcaster.addListener( listener );
}
public void removeListener( Listener<ChangedAttribute> listener )
{
mChangeBroadcaster.removeListener( listener );
}
public void broadcastChange( ChangedAttribute attribute )
{
if( mChangeBroadcaster != null )
{
mChangeBroadcaster.broadcast( attribute );
}
}
}

View File

@@ -0,0 +1,143 @@
/*******************************************************************************
* 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.state;
import java.awt.Color;
import java.awt.Font;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import alias.Alias;
import controller.state.ChannelState.ChangedAttribute;
public abstract class AuxStatePanel extends JPanel
implements Listener<ChangedAttribute>,
SettingChangeListener
{
private static final long serialVersionUID = 1L;
protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
protected Color mColorLabelDetails;
protected Color mColorLabelAuxDecoder;
protected SettingsManager mSettingsManager;
protected AuxChannelState mAuxChannelState;
public AuxStatePanel( SettingsManager settingsManager,
AuxChannelState auxChannelState )
{
mSettingsManager = settingsManager;
mAuxChannelState = auxChannelState;
/* Register to receive event updates */
mAuxChannelState.addListener( this );
/* Register to receive settings updates */
mSettingsManager.addListener( this );
getColors();
setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") );
}
public void dispose()
{
/* Deregister from changed settings events */
mSettingsManager.removeListener( this );
mSettingsManager = null;
mAuxChannelState = null;
}
protected void addLabel( JLabel label, boolean wrap )
{
label.setFont( mFontAuxDecoder );
label.setForeground( mColorLabelAuxDecoder );
if( wrap )
{
add( label, "wrap" );
}
else
{
add( label );
}
}
protected void updateAliasLabel( JLabel label, Alias alias )
{
if( alias != null )
{
label.setText( alias.getName() );
String iconName = alias.getIconName();
ImageIcon icon = mSettingsManager.getImageIcon( iconName, 12 );
label.setIcon( icon );
}
else
{
label.setText( null );
label.setIcon( null );
}
}
private void getColors()
{
mColorLabelDetails = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor();
mColorLabelAuxDecoder = mSettingsManager.getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor();
}
@Override
public void settingChanged( Setting setting )
{
if( setting instanceof ColorSetting )
{
ColorSetting colorSetting = (ColorSetting)setting;
switch( colorSetting.getColorSettingName() )
{
case CHANNEL_STATE_LABEL_AUX_DECODER:
mColorLabelAuxDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DETAILS:
mColorLabelDetails = colorSetting.getColor();
repaint();
break;
}
}
}
@Override
public void settingDeleted( Setting setting ) {}
}

View File

@@ -0,0 +1,664 @@
/*******************************************************************************
* 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.state;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.swing.JTable;
import log.Log;
import message.Message;
import properties.SystemProperties;
import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
import audio.AudioType;
import audio.AudioTypeListener;
import audio.SquelchListener;
import audio.SquelchListener.SquelchState;
import controller.activity.CallEvent;
import controller.activity.CallEvent.CallEventType;
import controller.activity.CallEventAliasCellRenderer;
import controller.activity.CallEventModel;
import controller.activity.MessageActivityModel;
import controller.channel.Channel.ChannelType;
import controller.channel.ProcessingChain;
/**
* ChannelState provides a state machine for tracking a voice or data call
* through it's life cycle of CALL/DATA, FADE, and then END, and then back to
* an IDLE state. This class also provides squelch control for unsquelching
* sound to the speakers, and/or sound to an audio recorder
*
* State Descriptions:
* IDLE No voice or data call activity
* CALL/DATA A voice or data call has started, or is continuing
* CONTROL Control channel
* FADE The phase after a voice or data call when either an explicit
* call end has been received, or when no new signalling updates
* have been received, and the fade timer has expired. This phase
* allows for gui updates to signal to the user that the call is
* ended, while continuing to display the call details for the user
* END The phase after the voice or data call FADE, when the call is
* now over, and the gui should be reset to IDLE.
*
* When a call is started, invoke mTimerService.startFadeTimer() to start the
* call fade monitoring timer process that will automatically signal a call end
* once no more call signalling has been detected after the fade timeout has
* occurred, by invoking the fade() method.
*
* When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke
* the mTimerService.startResetTimer(). Once the reset timeout occurs,
* the reset() method will be called, allowing the channel state to perform
* any reset actions, like clearing status and id labels on any view panels.
*
* AuxChannelState class allows for auxiliary decoders like Fleetsync to both
* have a state and leverage the state machine of the parent ChannelState.
* This allows a non-squelching channel state like for conventional fm to have
* an aux decoder like fleetsync, and allow the fleetsync auxchannelstate to
* leverage the call/end states of the parent conventional channelstate.
*
*/
public abstract class ChannelState implements Listener<Message>
{
private State mState = State.IDLE;
private Object mCallFadeLock = new Object();
private Object mCallResetLock = new Object();
private long mFadeTimeout = -1;
private long mResetTimeout = -1;
protected long mCallFadeTimeout;
protected long mCallResetTimeout;
protected TimerService mTimerService = new TimerService();
protected ArrayList<SquelchListener> mSquelchListeners =
new ArrayList<SquelchListener>();
protected ArrayList<AuxChannelState> mAuxChannelStates =
new ArrayList<AuxChannelState>();
protected Broadcaster<ChangedAttribute> mChangeBroadcaster =
new Broadcaster<ChangedAttribute>();
protected AudioTypeListener mAudioTypeListener;
protected AudioType mAudioType = AudioType.NORMAL;
protected CallEventModel mCallEventModel = new CallEventModel();
protected JTable mCallEventTable;
protected MessageActivityModel mMessageActivityModel =
new MessageActivityModel();
protected JTable mMessageActivityTable;
protected ProcessingChain mProcessingChain;
protected AliasList mAliasList;
public ChannelState( ProcessingChain processingChain, AliasList aliasList )
{
mProcessingChain = processingChain;
mAliasList = aliasList;
/* Register to receive decoded messages from the processing chain */
mProcessingChain.addListener( this );
SystemProperties props = SystemProperties.getInstance();
mCallFadeTimeout = props.get( "call.fade.timeout", 2000 );
mCallResetTimeout = props.get( "call.reset.timeout", 4000 );
}
/**
* Allows setting the call event model to use for traffic channels, so that
* all traffic channels can share the parent control channel call event
* model.
* @param model
*/
public void setCallEventModel( CallEventModel model )
{
mCallEventModel = model;
}
public CallEventModel getCallEventModel()
{
return mCallEventModel;
}
public void receiveCallEvent( CallEvent event )
{
mCallEventModel.add( event );
}
public void dispose()
{
mTimerService.dispose();
mAuxChannelStates.clear();
mSquelchListeners.clear();
mChangeBroadcaster.dispose();
mChangeBroadcaster = null;
if( mProcessingChain.getChannel().getChannelType() == ChannelType.STANDARD )
{
mCallEventModel.dispose();
}
/* If we're a traffic channel, set the call event model to null so that
* we don't dispose the parent control channel's call event model */
else
{
mCallEventModel = null;
}
mMessageActivityModel.dispose();
mMessageActivityModel = null;
mAliasList = null;
mAudioTypeListener = null;
mCallEventModel = null;
mProcessingChain = null;
}
/**
* Channel Activity Summary - implemented by the sub-class.
*/
public abstract String getActivitySummary();
public AliasList getAliasList()
{
return mAliasList;
}
public boolean hasAliasList()
{
return mAliasList != null;
}
public void broadcastChange( ChangedAttribute attribute )
{
if( mChangeBroadcaster != null )
{
mChangeBroadcaster.broadcast( attribute );
}
}
public void setCallFadeTimeout( long milliseconds )
{
mCallFadeTimeout = milliseconds;
}
public JTable getCallEventTable()
{
if( mCallEventTable == null )
{
mCallEventTable = new JTable( mCallEventModel );
mCallEventTable.setAutoCreateRowSorter( true );
mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
CallEventAliasCellRenderer renderer =
new CallEventAliasCellRenderer( mProcessingChain
.getResourceManager().getSettingsManager() );
mCallEventTable.getColumnModel().getColumn( 3 )
.setCellRenderer( renderer );
mCallEventTable.getColumnModel().getColumn( 5 )
.setCellRenderer( renderer );
}
return mCallEventTable;
}
/**
* Returns the message activity model wrapped in a JTable. Although this
* may seem to violate separation between model and view, it simplifies
* retaining the JTable state for each channel by keeping a local reference
* to reuse
*/
public JTable getMessageActivityTable()
{
if( mMessageActivityTable == null )
{
mMessageActivityTable = new JTable( mMessageActivityModel );
mMessageActivityTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN );
}
return mMessageActivityTable;
}
public void addAuxChannelState( AuxChannelState state )
{
mAuxChannelStates.add( state );
}
public ArrayList<AuxChannelState> getAuxChannelStates()
{
return mAuxChannelStates;
}
public void receive( Message message )
{
if( mMessageActivityModel != null )
{
mMessageActivityModel.receive( message );
}
for( AuxChannelState state: mAuxChannelStates )
{
if( state != null )
{
state.receive( message );
}
}
}
public ProcessingChain getProcessingChain()
{
return mProcessingChain;
}
public State getState()
{
return mState;
}
public void setState( State state )
{
/* Let all CALL state changes go through in order to update the fade
* timer, otherwise, don't update the state if it hasn't changed */
if( mState != state || state == State.CALL )
{
if( mState.canChangeTo( state ) )
{
mState = state;
switch( state )
{
case CALL:
setSquelchState( SquelchState.UNSQUELCH );
case DATA:
mTimerService.stopResetTimer();
mTimerService.startFadeTimer();
break;
case NO_TUNER:
mTimerService.stopResetTimer();
if( getProcessingChain().getChannel()
.getChannelType() == ChannelType.TRAFFIC )
{
mTimerService.startFadeTimer();
}
break;
case FADE:
mTimerService.stopFadeTimer();
mTimerService.startResetTimer();
setSquelchState( SquelchState.SQUELCH );
if( mAudioTypeListener != null )
{
mAudioTypeListener.setAudioType( mAudioType );
}
break;
case END:
mTimerService.stopResetTimer();
mTimerService.stopFadeTimer();
setSquelchState( SquelchState.SQUELCH );
if( mAudioTypeListener != null )
{
mAudioTypeListener.setAudioType( mAudioType );
}
/* If this is a traffic channel, stop it */
if( mProcessingChain.getChannel()
.getChannelType() == ChannelType.TRAFFIC )
{
mProcessingChain.getChannel().setEnabled( false );
}
break;
case IDLE:
break;
default:
break;
}
broadcastChange( ChangedAttribute.CHANNEL_STATE );
}
else
{
Log.error( "Channel State - can't change from [" +
mState.toString() + "] to [" + state.toString() + "]" );
}
}
}
/**
* End method to reset the channel state once a call has completely
* ended and now you want to return the channel state to idle
*/
protected void reset()
{
setState( State.END );
for( AuxChannelState state: mAuxChannelStates )
{
state.reset();
}
//Reset the state to idle
setState( State.IDLE );
}
/**
* Method to perform any call fade actions. On fade, you should change the
* channel state to fading, and construct a call-reset timer to execute a few
* seconds later. This allows the view to signal to the user that the call
* has faded/ended, and display faded information to the user, until the
* cleanup() task is run.
*/
protected void fade( CallEventType type )
{
for( AuxChannelState state: mAuxChannelStates )
{
state.fade();
}
setState( State.FADE );
}
public void setListener( AudioTypeListener listener )
{
mAudioTypeListener = listener;
}
public void addListener( SquelchListener listener )
{
mSquelchListeners.add( listener );
}
public void removeListener( SquelchListener listener )
{
mSquelchListeners.remove( listener );
}
protected void setSquelchState( SquelchState state )
{
Iterator<SquelchListener> it = mSquelchListeners.iterator();
while( it.hasNext() )
{
it.next().setSquelch( state );
}
}
/**
* Timer service
*/
public class TimerService extends ScheduledThreadPoolExecutor
{
private long mTimerMonitorRevisitRate = 100; //100 milliseconds
private ScheduledFuture<?> mTimerMonitor;
public TimerService()
{
super( 1 );
}
public void dispose()
{
if( mTimerMonitor != null )
{
mTimerMonitor.cancel( true );
}
this.shutdownNow();
}
private void checkMonitor()
{
boolean createTimer = false;
synchronized( mCallFadeLock )
{
if( mFadeTimeout != -1 )
{
createTimer = true;
}
}
synchronized( mCallResetLock )
{
if( mResetTimeout != -1 )
{
createTimer = true;
}
}
if( createTimer )
{
//Check to see if the timer is already running, if not, start it
if( mTimerMonitor == null || mTimerMonitor.isDone() )
{
scheduleAtFixedRate( new StateTimeoutMonitor(),
mTimerMonitorRevisitRate,
mTimerMonitorRevisitRate,
TimeUnit.MILLISECONDS );
}
}
else
{
if( mTimerMonitor != null )
{
mTimerMonitor.cancel( true );
}
}
}
public void startFadeTimer()
{
synchronized( mCallFadeLock )
{
mFadeTimeout = System.currentTimeMillis() + mCallFadeTimeout;
}
checkMonitor();
}
public void resetFadeTimer()
{
startFadeTimer();
}
public void stopFadeTimer()
{
synchronized( mCallFadeLock )
{
mFadeTimeout = -1;
}
checkMonitor();
}
public void startResetTimer()
{
synchronized( mCallResetLock )
{
mResetTimeout = System.currentTimeMillis() + mCallResetTimeout;
}
checkMonitor();
}
public void stopResetTimer()
{
synchronized( mCallResetLock )
{
mResetTimeout = -1;
}
checkMonitor();
}
}
public class StateTimeoutMonitor implements Runnable
{
@Override
public void run()
{
long now = System.currentTimeMillis();
synchronized( mCallFadeLock )
{
if( mFadeTimeout != -1 && now >= mFadeTimeout )
{
mFadeTimeout = -1;
fade( CallEventType.CALL_TIMEOUT );
}
}
synchronized( mCallResetLock )
{
if( mResetTimeout != -1 && now >= mResetTimeout )
{
mResetTimeout = -1;
reset();
}
}
}
}
public enum State
{
IDLE( "IDLE" ) {
@Override
public boolean canChangeTo( State state )
{
return state == IDLE ||
state == CALL ||
state == CONTROL ||
state == DATA ||
state == FADE ||
state == NO_TUNER;
}
},
CALL( "CALL" ) {
@Override
public boolean canChangeTo( State state )
{
return state == CALL ||
state == CONTROL ||
state == DATA ||
state == FADE;
}
},
DATA( "DATA" ) {
@Override
public boolean canChangeTo( State state )
{
return state == CALL ||
state == CONTROL ||
state == DATA ||
state == FADE;
}
},
CONTROL( "CONTROL" )
{
@Override
public boolean canChangeTo( State state )
{
return true;
}
},
FADE( "FADE" ) {
@Override
public boolean canChangeTo( State state )
{
return state != FADE; //All states except fade allowed
}
},
END( "END" ) {
@Override
public boolean canChangeTo( State state )
{
return state != FADE; //Fade is only disallowed state
}
},
NO_TUNER( "NO TUNER" ) {
@Override
public boolean canChangeTo( State state )
{
return state == FADE || state == END;
}
};
private String mDisplayValue;
private State( String displayValue )
{
mDisplayValue = displayValue;
}
public abstract boolean canChangeTo( State state );
public String getDisplayValue()
{
return mDisplayValue;
}
}
/**
* Registers a listener to receive channel state attribute changes
*/
public void addListener( Listener<ChangedAttribute> listener )
{
mChangeBroadcaster.addListener( listener );
}
/**
* Removes a listener from receiving channel state attribute changes
*/
public void removeListener( Listener<ChangedAttribute> listener )
{
mChangeBroadcaster.removeListener( listener );
}
public enum ChangedAttribute
{
AUDIO_TYPE,
CHANNEL_NUMBER,
CHANNEL_NAME,
CHANNEL_SITE_NUMBER,
CHANNEL_SITE_NUMBER_ALIAS,
CHANNEL_STATE,
FROM_TALKGROUP,
FROM_TALKGROUP_ALIAS,
FROM_TALKGROUP_TYPE,
MESSAGE,
MESSAGE_TYPE,
SITE_NAME,
SOURCE,
SYSTEM_NAME,
SQUELCH,
TO_TALKGROUP,
TO_TALKGROUP_ALIAS,
TO_TALKGROUP_TYPE;
}
}

View File

@@ -0,0 +1,442 @@
/*******************************************************************************
* 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.state;
import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import settings.ColorSetting.ColorSettingName;
import settings.ColorSettingMenuItem;
import settings.ColorSettingResetMenuItem;
import settings.SettingsManager;
import controller.channel.Channel;
import controller.channel.Channel.ChannelEvent;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelListener;
import decode.fleetsync2.FleetsyncChannelState;
import decode.fleetsync2.FleetsyncPanel;
import decode.ltrnet.LTRNetChannelState;
import decode.ltrnet.LTRNetPanel;
import decode.ltrstandard.LTRChannelState;
import decode.ltrstandard.LTRPanel;
import decode.mdc1200.MDCChannelState;
import decode.mdc1200.MDCPanel;
import decode.mpt1327.MPT1327ChannelState;
import decode.mpt1327.MPT1327Panel;
import decode.nbfm.NBFMChannelState;
import decode.nbfm.NBFMPanel;
import decode.passport.PassportChannelState;
import decode.passport.PassportPanel;
public class ChannelStateList extends JPanel implements ChannelListener
{
private static final long serialVersionUID = 1L;
private HashMap<Channel,ChannelStatePanel> mDisplayedPanels =
new HashMap<Channel,ChannelStatePanel>();
private SettingsManager mSettingsManager;
public ChannelStateList( SettingsManager settingsManager )
{
mSettingsManager = settingsManager;
init();
}
private void init()
{
setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") );
setBackground( Color.BLACK );
addMouseListener( new ListSelectionListener() );
}
@Override
public void occurred( Channel channel, ChannelEvent event )
{
ChannelType type = channel.getChannelType();
switch( event )
{
case PROCESSING_STARTED:
if( type == ChannelType.STANDARD )
{
channelStarted( channel );
}
else if( type == ChannelType.TRAFFIC )
{
for( Channel parent: mDisplayedPanels.keySet() )
{
if( parent.hasTrafficChannel( channel ) )
{
mDisplayedPanels.get( parent ).trafficChannelAdded( channel );
validate();
}
}
}
break;
case PROCESSING_STOPPED:
if( type == ChannelType.STANDARD )
{
channelStopped( channel );
}
/* Since we don't have a reference to the parent channel that
* owns the traffic channel, iterate all channels to find the
* parent */
else if( type == ChannelType.TRAFFIC )
{
for( Channel control: mDisplayedPanels.keySet() )
{
if( control.hasTrafficChannel( channel ) )
{
control.removeTrafficChannel( channel );
mDisplayedPanels.get( control ).trafficChannelDeleted( channel );
validate();
}
}
}
break;
default:
break;
}
}
public void channelStarted( Channel channel )
{
if( !mDisplayedPanels.containsKey( channel ) )
{
ChannelState state = channel.getProcessingChain().getChannelState();
ChannelStatePanel panel = null;
if( state instanceof NBFMChannelState )
{
NBFMChannelState convState =
(NBFMChannelState)state;
panel = new NBFMPanel( channel );
for( AuxChannelState aux: convState.getAuxChannelStates() )
{
if( aux instanceof FleetsyncChannelState )
{
FleetsyncPanel fsPanel =
new FleetsyncPanel( mSettingsManager,
(FleetsyncChannelState)aux );
panel.addPanel( fsPanel );
}
else if( aux instanceof MDCChannelState )
{
MDCPanel mdsPanel = new MDCPanel( mSettingsManager,
(MDCChannelState)aux );
panel.addPanel( mdsPanel );
}
}
}
else if( state instanceof LTRChannelState )
{
LTRChannelState ltrState = (LTRChannelState)state;
panel = new LTRPanel( mSettingsManager, channel );
for( AuxChannelState aux: ltrState.getAuxChannelStates() )
{
if( aux instanceof FleetsyncChannelState )
{
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
(FleetsyncChannelState)aux );
panel.addPanel( fsPanel );
}
}
}
else if( state instanceof LTRNetChannelState )
{
LTRNetChannelState ltrState = (LTRNetChannelState)state;
panel = new LTRNetPanel( channel );
for( AuxChannelState aux: ltrState.getAuxChannelStates() )
{
if( aux instanceof FleetsyncChannelState )
{
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
(FleetsyncChannelState)aux );
panel.addPanel( fsPanel );
}
}
}
else if( state instanceof MPT1327ChannelState )
{
MPT1327ChannelState mptState = (MPT1327ChannelState)state;
panel = new MPT1327Panel( channel );
}
else if( state instanceof PassportChannelState )
{
PassportChannelState passportState = (PassportChannelState)state;
panel = new PassportPanel( mSettingsManager, channel );
for( AuxChannelState aux: passportState.getAuxChannelStates() )
{
if( aux instanceof FleetsyncChannelState )
{
FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager,
(FleetsyncChannelState)aux );
panel.addPanel( fsPanel );
}
}
}
if( panel != null )
{
add( panel, "wrap" );
mDisplayedPanels.put( channel, panel );
validate();
}
}
}
public void channelStopped( Channel channel )
{
if( mDisplayedPanels.containsKey( channel ) )
{
ChannelStatePanel panel = mDisplayedPanels.get( channel );
if( panel != null )
{
remove( panel );
validate();
}
mDisplayedPanels.remove( channel );
}
}
public void setSelectedChannel( ChannelStatePanel selectedPanel )
{
boolean current = selectedPanel.getSelected();
for( ChannelStatePanel panel: mDisplayedPanels.values() )
{
//Turn selected off
panel.setSelected( false );
}
selectedPanel.setSelected( !current );
}
public class ListSelectionListener implements MouseListener
{
@Override
public void mouseClicked( MouseEvent e )
{
Component component = getComponentAt( e.getPoint() );
if( component instanceof ChannelStatePanel )
{
ChannelStatePanel panel = (ChannelStatePanel)component;
/* If the panel has child traffic channels, attempt to get
* one of them for the mouse event */
if( panel.getChannel().getTrafficChannels().size() > 0 )
{
for( ChannelStatePanel trafficPanel: panel.getTrafficPanels().values() )
{
Point translatedPoint = SwingUtilities
.convertPoint( e.getComponent(), e.getPoint(),
trafficPanel );
if( trafficPanel.contains( translatedPoint ) )
{
panel = trafficPanel;
break;
}
}
}
if( e.getButton() == MouseEvent.BUTTON1 )
{
setSelectedChannel( panel );
}
else if( e.getButton() == MouseEvent.BUTTON3 )
{
JPopupMenu popupMenu = new JPopupMenu();
JMenu menu = panel.getChannel().getContextMenu();
popupMenu.add( menu );
JMenu colorMenu = new JMenu( "Color" );
popupMenu.add( colorMenu );
JMenu backgroundMenu = new JMenu( "Background" );
colorMenu.add( backgroundMenu );
backgroundMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_BACKGROUND ) );
backgroundMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_BACKGROUND ) );
JMenu channelsMenu = new JMenu( "Channel State" );
colorMenu.add( channelsMenu );
JMenu callMenu = new JMenu( "Call" );
channelsMenu.add( callMenu );
callMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ) );
callMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ) );
callMenu.add( new JSeparator() );
callMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ) );
callMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ) );
JMenu controlMenu = new JMenu( "Control" );
channelsMenu.add( controlMenu );
controlMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ) );
controlMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ) );
controlMenu.add( new JSeparator() );
controlMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ) );
controlMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ) );
JMenu dataMenu = new JMenu( "Data" );
channelsMenu.add( dataMenu );
dataMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ) );
dataMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ) );
dataMenu.add( new JSeparator() );
dataMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ) );
dataMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ) );
JMenu fadeMenu = new JMenu( "Fade" );
channelsMenu.add( fadeMenu );
fadeMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ) );
fadeMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ) );
fadeMenu.add( new JSeparator() );
fadeMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ) );
fadeMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ) );
JMenu idleMenu = new JMenu( "Idle" );
channelsMenu.add( idleMenu );
idleMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ) );
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ) );
idleMenu.add( new JSeparator() );
idleMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
JMenu noTunerMenu = new JMenu( "No Tuner" );
channelsMenu.add( noTunerMenu );
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
noTunerMenu.add( new JSeparator() );
noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
JMenu labelMenu = new JMenu( "Labels" );
colorMenu.add( labelMenu );
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) );
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) );
labelMenu.add( new JSeparator() );
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) );
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) );
labelMenu.add( new JSeparator() );
labelMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ) );
labelMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ) );
JMenu selectedMenu = new JMenu( "Selected" );
colorMenu.add( selectedMenu );
selectedMenu.add( new ColorSettingMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) );
selectedMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) );
popupMenu.show( ChannelStateList.this, e.getX(), e.getY() );
}
}
}
public void mousePressed( MouseEvent e ) {}
public void mouseReleased( MouseEvent e ) {}
public void mouseEntered( MouseEvent e ) {}
public void mouseExited( MouseEvent e ) {}
}
}

View File

@@ -0,0 +1,386 @@
/*******************************************************************************
* 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.state;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.util.HashMap;
import javax.swing.JMenu;
import javax.swing.JPanel;
import log.Log;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import controller.channel.Channel;
import controller.state.ChannelState.ChangedAttribute;
import controller.state.ChannelState.State;
public abstract class ChannelStatePanel extends JPanel
implements Listener<ChangedAttribute>,
SettingChangeListener
{
private static final long serialVersionUID = 1L;
protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 );
protected Color mColorChannelBackground;
protected Color mColorChannelSelected;
protected Color mColorTopCall;
protected Color mColorMiddleCall;
protected Color mColorTopControl;
protected Color mColorMiddleControl;
protected Color mColorTopData;
protected Color mColorMiddleData;
protected Color mColorTopFade;
protected Color mColorMiddleFade;
protected Color mColorTopIdle;
protected Color mColorMiddleIdle;
protected Color mColorTopNoTuner;
protected Color mColorMiddleNoTuner;
protected Color mColorLabelDetails;
protected Color mColorLabelDecoder;
protected Color mColorLabelAuxDecoder;
private HashMap<Channel,ChannelStatePanel> mTrafficPanels =
new HashMap<Channel,ChannelStatePanel>();
protected Channel mChannel;
public ChannelStatePanel( Channel channel )
{
mChannel = channel;
/* Register to receive settings updates */
getSettingsManager().addListener( this );
/* Register to receive channel updates */
mChannel.getProcessingChain().getChannelState().addListener( this );
getColors();
setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") );
}
public SettingsManager getSettingsManager()
{
return mChannel.getResourceManager().getSettingsManager();
}
public void dispose()
{
mTrafficPanels.clear();
mChannel = null;
}
private void getColors()
{
mColorChannelBackground = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor();
mColorChannelSelected = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor();
mColorTopCall = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor();
mColorMiddleCall = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor();
mColorTopControl = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor();
mColorMiddleControl = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor();
mColorTopData = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor();
mColorMiddleData = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor();
mColorTopFade = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor();
mColorMiddleFade = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor();
mColorTopIdle = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor();
mColorMiddleIdle = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor();
mColorTopNoTuner = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor();
mColorMiddleNoTuner = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor();
mColorLabelDetails = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor();
mColorLabelDecoder = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor();
mColorLabelAuxDecoder = getSettingsManager().getColorSetting(
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor();
}
public HashMap<Channel,ChannelStatePanel> getTrafficPanels()
{
return mTrafficPanels;
}
public JMenu getContextMenu()
{
return mChannel.getContextMenu();
}
public Channel getChannel()
{
return mChannel;
}
public ChannelState getChannelState()
{
if( mChannel.getProcessingChain() != null )
{
return mChannel.getProcessingChain().getChannelState();
}
return null;
}
public boolean getSelected()
{
return mChannel.getSelected();
}
public void addPanel( final JPanel panel )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
add( panel, "span,wrap" );
validate();
}
} );
}
public void removePanel( final JPanel panel )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
remove( panel );
validate();
getParent().validate();
if( panel instanceof ChannelStatePanel )
{
((ChannelStatePanel)panel).dispose();
}
else if( panel instanceof AuxStatePanel )
{
((AuxStatePanel)panel).dispose();
}
}
} );
}
public void trafficChannelAdded( Channel traffic )
{
//Disregard -- sub class can override and implement
}
public void trafficChannelDeleted( Channel traffic )
{
ChannelStatePanel panelToRemove =
getTrafficPanels().remove( traffic );
removePanel( panelToRemove );
}
public void setSelected( boolean selected )
{
mChannel.setSelected( selected );
for( ChannelStatePanel traffic: getTrafficPanels().values() )
{
traffic.setSelected( false );
}
repaint();
}
private GradientPaint getGradient( Color top, Color middle )
{
return new GradientPaint( 0f, -50.0f, top,
0f, (float)getHeight() / 2.2f, middle, true );
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent( g );
setBackground( mColorChannelBackground );
Graphics2D g2 = (Graphics2D)g.create();
Paint p = null;
ChannelState channelState = getChannelState();
if( channelState != null )
{
State state = channelState.getState();
if( state != null )
{
switch( state )
{
case CALL:
p = getGradient( mColorTopCall, mColorMiddleCall );
break;
case CONTROL:
p = getGradient( mColorTopControl, mColorMiddleControl );
break;
case DATA:
p = getGradient( mColorTopData, mColorMiddleData );
break;
case FADE:
p = getGradient( mColorTopFade, mColorMiddleFade );
break;
case NO_TUNER:
p = getGradient( mColorTopNoTuner, mColorMiddleNoTuner );
break;
case END:
case IDLE:
default:
p = getGradient( mColorTopIdle, mColorMiddleIdle );
break;
}
}
}
g2.setPaint( p );
g2.fillRect( 0, 0, getWidth(), getHeight() );
/* Draw bottom separator line */
g2.setColor( Color.LIGHT_GRAY );
g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 );
/* Draw channel selected box */
if( mChannel.getSelected() )
{
g2.setColor( mColorChannelSelected );
g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 );
}
g2.dispose();
}
@Override
public void settingChanged( Setting setting )
{
if( setting instanceof ColorSetting )
{
ColorSetting colorSetting = (ColorSetting)setting;
switch( colorSetting.getColorSettingName() )
{
case CHANNEL_STATE_BACKGROUND:
mColorChannelBackground = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_CALL:
mColorMiddleCall = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_CALL:
mColorTopCall = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL:
mColorMiddleControl = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_CONTROL:
mColorTopControl = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_DATA:
mColorMiddleData = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_DATA:
mColorTopData = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_FADE:
mColorMiddleFade = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_FADE:
mColorTopFade = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE:
mColorMiddleIdle = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_IDLE:
mColorTopIdle = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER:
mColorMiddleNoTuner = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER:
mColorTopNoTuner = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_AUX_DECODER:
mColorLabelAuxDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DECODER:
mColorLabelDecoder = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_LABEL_DETAILS:
mColorLabelDetails = colorSetting.getColor();
repaint();
break;
case CHANNEL_STATE_SELECTED_CHANNEL:
mColorChannelSelected = colorSetting.getColor();
repaint();
break;
}
}
}
@Override
public void settingDeleted( Setting setting ) {}
}

View File

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

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* 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.system;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import controller.site.Site;
@XmlSeeAlso( { Site.class } )
@XmlRootElement( name = "system" )
public class System
{
private String mName;
private ArrayList<Site> mSite = new ArrayList<Site>();
public System()
{
this( "New System" );
}
public System( String name )
{
mName = name;
}
@XmlAttribute
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
public ArrayList<Site> getSite()
{
return mSite;
}
public void setSite( ArrayList<Site> sites )
{
mSite = sites;
}
public void addSite( Site site )
{
mSite.add( site );
}
public void removeSite( Site site )
{
mSite.remove( site );
}
public void clearSites()
{
mSite.clear();
}
public String toString()
{
return mName;
}
}

View File

@@ -0,0 +1,101 @@
/*******************************************************************************
* 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.system;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.tree.TreePath;
import net.miginfocom.swing.MigLayout;
public class SystemEditor extends JPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
private SystemNode mSystemNode;
private JLabel mLabelSystem;
private JTextField mTextSystem;
public SystemEditor( SystemNode system )
{
mSystemNode = system;
initGUI();
}
private void initGUI()
{
setLayout( new MigLayout() );
setBorder( BorderFactory.createTitledBorder( "System" ) );
mLabelSystem = new JLabel( "Name:" );
add( mLabelSystem, "align right" );
mTextSystem = new JTextField( mSystemNode.getSystem().getName() );
add( mTextSystem, "grow, wrap" );
JButton btnSave = new JButton( "Save" );
btnSave.addActionListener( SystemEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.addActionListener( SystemEditor.this );
add( btnReset, "wrap" );
}
@Override
public void actionPerformed( ActionEvent e )
{
String command = e.getActionCommand();
if( command.contentEquals( "Save" ) )
{
boolean expanded = mSystemNode.getModel().getTree()
.isExpanded( new TreePath( mSystemNode ) );
String name = mTextSystem.getText();
if( name != null )
{
mSystemNode.getSystem().setName( name );
mSystemNode.save();
mSystemNode.show();
if( expanded )
{
mSystemNode.getModel().getTree().expandPath( new TreePath( mSystemNode ) );
}
}
else
{
JOptionPane.showMessageDialog( SystemEditor.this, "Please enter a system name" );
}
}
else if( command.contentEquals( "Reset" ) )
{
mTextSystem.setText( mSystemNode.getSystem().getName() );
}
}
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* 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.system;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
@XmlSeeAlso( { controller.system.System.class } )
@XmlRootElement( name = "system_list" )
public class SystemList
{
private ArrayList<System> mSystem = new ArrayList<System>();
public SystemList()
{
}
public ArrayList<System> getSystem()
{
return mSystem;
}
public void setSystem( ArrayList<System> systems )
{
mSystem = systems;
}
public void addSystem( System system )
{
mSystem.add( system );
}
public void removeSystem( System system )
{
mSystem.remove( system );
}
public void clearSystems()
{
mSystem.clear();
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* 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.system;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import controller.ConfigurableNode;
public class SystemListNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public SystemListNode( SystemList list )
{
super( list );
}
public SystemList getSystemList()
{
return (SystemList)getUserObject();
}
public void init()
{
for( System system: getSystemList().getSystem() )
{
SystemNode node = new SystemNode( system );
getModel().addNode( node, SystemListNode.this, getChildCount() );
node.init();
}
sort();
}
public String toString()
{
return "Systems [" + this.getChildCount() + "]";
}
public JPopupMenu getContextMenu()
{
JPopupMenu retVal = new JPopupMenu();
JMenuItem addSystemItem = new JMenuItem( "Add System" );
addSystemItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
System system = new System();
getSystemList().addSystem( system );
getModel().addNode( new SystemNode( system ),
SystemListNode.this,
SystemListNode.this.getChildCount() );
}
} );
retVal.add( addSystemItem );
return retVal;
}
}

View File

@@ -0,0 +1,165 @@
/*******************************************************************************
* 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.system;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.tree.DefaultMutableTreeNode;
import controller.ConfigurableNode;
import controller.site.Site;
import controller.site.SiteNode;
public class SystemNode extends ConfigurableNode
{
private static final long serialVersionUID = 1L;
public SystemNode( System system )
{
super( system );
}
public void init()
{
for( Site site: getSystem().getSite() )
{
SiteNode node = new SiteNode( site );
getModel().insertNodeInto( node, SystemNode.this, getChildCount() );
node.init();
}
sort();
}
@Override
public String getIconPath()
{
String retVal = "images/system.png";
@SuppressWarnings( "unchecked" )
Enumeration<DefaultMutableTreeNode> nodes = children();
while( nodes.hasMoreElements() )
{
SiteNode child = (SiteNode)nodes.nextElement();
if( child.getIconPath().contains( "green.png" ) )
{
return "images/system_green.png";
}
else if( child.getIconPath().contains( "red.png" ) )
{
retVal = "images/system_red.png";
}
}
return retVal;
}
@Override
public JPanel getEditor()
{
return new SystemEditor( this );
}
public System getSystem()
{
return (System)getUserObject();
}
public String toString()
{
return getSystem().getName();
}
public void delete()
{
for( int x = 0; x < getChildCount(); x++ )
{
((SiteNode)getChildAt( x )).delete();
}
SystemListNode parent = (SystemListNode)getParent();
parent.getSystemList().removeSystem( getSystem() );
save();
getModel().removeNodeFromParent( SystemNode.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 addSiteItem = new JMenuItem( "Add Site" );
addSiteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
Site site = new Site();
getSystem().addSite( site );
SiteNode node = new SiteNode( site );
getModel().addNode( node,
SystemNode.this,
SystemNode.this.getChildCount() );
sort();
node.show();
}
} );
retVal.add( addSiteItem );
retVal.add( new JSeparator() );
JMenuItem deleteItem = new JMenuItem( "Delete" );
deleteItem.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
delete();
}
} );
retVal.add( deleteItem );
return retVal;
}
}