mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Resolves threading issue in ChannelState.
Resolves issue in tuner controller where controller was not releasing sourced tuner channels, preventing re-tuning to other frequencies. Resolves issue with channel timeout on MPT1327 monitored traffic channels
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/*.class
|
||||
/classes*
|
||||
/product*
|
||||
/product*
|
||||
/bin/
|
||||
|
||||
@@ -230,12 +230,20 @@ public class AudioChannelPanel extends JPanel
|
||||
mFrom.setText( from.getValue() );
|
||||
updateAlias( mFromAlias, from.getAlias() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFrom.setText( "-----" );
|
||||
}
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
mTo.setText( to.getValue() );
|
||||
updateAlias( mToAlias, to.getAlias() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTo.setText( "-----" );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
@@ -58,6 +58,16 @@ public class MessageFramer implements Listener<Boolean>,
|
||||
mMessageLength = messageLength;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
for( MessageAssembler assembler: mMessageAssemblers )
|
||||
{
|
||||
assembler.dispose();
|
||||
}
|
||||
|
||||
mMessageAssemblers.clear();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mBroadcaster.dispose();
|
||||
|
||||
@@ -64,11 +64,13 @@ public class ThreadPoolManager
|
||||
mExecutor.schedule( command, delay, unit );
|
||||
}
|
||||
|
||||
public void cancel( ScheduledFuture<?> task )
|
||||
public boolean cancel( ScheduledFuture<?> task )
|
||||
{
|
||||
task.cancel( true );
|
||||
boolean success = task.cancel( true );
|
||||
|
||||
mTasks.remove( task );
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public int getTaskCount( ThreadType type )
|
||||
|
||||
@@ -809,8 +809,7 @@ public class Channel extends Configuration
|
||||
{
|
||||
if( mProcessingChain == null )
|
||||
{
|
||||
mProcessingChain = new ProcessingChain(
|
||||
mResourceManager.getThreadPoolManager() );
|
||||
mProcessingChain = new ProcessingChain();
|
||||
|
||||
/* Get the optional alias list for the decode modules to use */
|
||||
AliasList aliasList = mResourceManager.getPlaylistManager()
|
||||
@@ -901,6 +900,8 @@ public class Channel extends Configuration
|
||||
{
|
||||
if( !mEnabled && mProcessingChain != null )
|
||||
{
|
||||
fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
|
||||
|
||||
mProcessingChain.stop();
|
||||
|
||||
if( mChannelType == ChannelType.STANDARD )
|
||||
@@ -910,8 +911,6 @@ public class Channel extends Configuration
|
||||
}
|
||||
|
||||
mSelected = false;
|
||||
|
||||
fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,8 +99,7 @@ public class ProcessingChain implements IChannelEventListener
|
||||
private Broadcaster<RealBuffer> mRealBufferBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ScheduledFuture<?> mBufferProcessingTask;
|
||||
// private ThreadPoolManager mThreadPoolManager;
|
||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||
|
||||
protected Source mSource;
|
||||
@@ -109,14 +108,8 @@ public class ProcessingChain implements IChannelEventListener
|
||||
|
||||
public ProcessingChain()
|
||||
{
|
||||
this( new ThreadPoolManager() );
|
||||
}
|
||||
|
||||
public ProcessingChain( ThreadPoolManager threadManager )
|
||||
{
|
||||
mThreadPoolManager = threadManager;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
stop();
|
||||
@@ -128,14 +121,6 @@ public class ProcessingChain implements IChannelEventListener
|
||||
|
||||
mModules.clear();
|
||||
|
||||
// mComplexReceiver.dispose();
|
||||
// mComplexReceiver = null;
|
||||
//
|
||||
// mRealReceiver.dispose();
|
||||
// mRealReceiver = null;
|
||||
|
||||
mThreadPoolManager = null;
|
||||
mBufferProcessingTask = null;
|
||||
mFrequencyCorrectionController = null;
|
||||
|
||||
mAudioPacketBroadcaster.dispose();
|
||||
@@ -496,12 +481,6 @@ public class ProcessingChain implements IChannelEventListener
|
||||
mSource = null;
|
||||
}
|
||||
|
||||
if( mBufferProcessingTask != null )
|
||||
{
|
||||
mThreadPoolManager.cancel( mBufferProcessingTask );
|
||||
mBufferProcessingTask = null;
|
||||
}
|
||||
|
||||
/* Stop each of the modules */
|
||||
for( Module module: mModules )
|
||||
{
|
||||
|
||||
@@ -228,7 +228,8 @@ public class DecoderFactory
|
||||
Sync sync = mptConfig.getSync();
|
||||
|
||||
modules.add( new MPT1327Decoder( aliasList, sync ) );
|
||||
modules.add( new MPT1327DecoderState( aliasList, channelMap, channelType ) );
|
||||
modules.add( new MPT1327DecoderState( aliasList, channelMap,
|
||||
channelType, mptConfig.getCallTimeout() * 1000 ) );
|
||||
|
||||
if( channelType == ChannelType.STANDARD )
|
||||
{
|
||||
|
||||
@@ -310,6 +310,11 @@ public abstract class CallEvent implements Comparable<CallEvent>
|
||||
@Override
|
||||
public int compareTo( CallEvent other )
|
||||
{
|
||||
if( other != null && other == this )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Long.compare( mEventStart, other.getEventStartTime() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mLTRMessageFramer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -76,6 +76,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mLTRMessageFramer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -301,8 +301,8 @@ public class MPT1327Decoder extends Decoder
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
mControlMessageFramer.reset();
|
||||
mTrafficMessageFramer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,9 +26,11 @@ import java.util.TreeSet;
|
||||
|
||||
import message.Message;
|
||||
import module.decode.DecoderType;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.CallEvent.CallEventType;
|
||||
import module.decode.mpt1327.MPT1327Message.IdentType;
|
||||
import module.decode.state.ChangeChannelTimeoutEvent;
|
||||
import module.decode.state.ChangedAttribute;
|
||||
import module.decode.state.DecoderState;
|
||||
import module.decode.state.DecoderStateEvent;
|
||||
@@ -41,6 +43,8 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import audio.metadata.Metadata;
|
||||
import audio.metadata.MetadataType;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.map.ChannelMap;
|
||||
|
||||
@@ -60,15 +64,18 @@ public class MPT1327DecoderState extends DecoderState
|
||||
private ChannelMap mChannelMap;
|
||||
|
||||
private long mFrequency = 0;
|
||||
private long mCallTimeout;
|
||||
|
||||
public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap,
|
||||
ChannelType channelType )
|
||||
public MPT1327DecoderState( AliasList aliasList,
|
||||
ChannelMap channelMap,
|
||||
ChannelType channelType,
|
||||
long callTimeout )
|
||||
{
|
||||
super( aliasList );
|
||||
|
||||
mChannelMap = channelMap;
|
||||
|
||||
mChannelType = channelType;
|
||||
mCallTimeout = callTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,8 +259,42 @@ public class MPT1327DecoderState extends DecoderState
|
||||
|
||||
/* Traffic Channel Events */
|
||||
case CLEAR:
|
||||
mChannelNumber = mpt.getChannel();
|
||||
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||
break;
|
||||
case MAINT:
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
||||
if( mChannelType == ChannelType.STANDARD )
|
||||
{
|
||||
/**
|
||||
* When we receive a MAINT message and we're configured
|
||||
* as a standard channel, we need to apply the call
|
||||
* timeout specified by the user. Otherwise we'll
|
||||
* be using the shorter default call timeout
|
||||
*/
|
||||
broadcast( new ChangeChannelTimeoutEvent( this,
|
||||
mChannelType, mCallTimeout ) );
|
||||
|
||||
if( mCurrentCallEvent == null )
|
||||
{
|
||||
mCurrentCallEvent = new MPT1327CallEvent.Builder( CallEventType.CALL )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( String.valueOf( mChannelNumber ) )
|
||||
.details( "MONITORED TRAFFIC CHANNEL" )
|
||||
.frequency( mFrequency )
|
||||
.to( mpt.getToID() )
|
||||
.build();
|
||||
|
||||
broadcast( mCurrentCallEvent );
|
||||
}
|
||||
|
||||
broadcast(new Metadata(MetadataType.TO,
|
||||
mpt.getToID(), mpt.getToIDAlias(), true));
|
||||
|
||||
broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) );
|
||||
|
||||
setToTalkgroup( mpt.getToID() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -276,6 +317,25 @@ public class MPT1327DecoderState extends DecoderState
|
||||
|
||||
mToTalkgroup = null;
|
||||
broadcast( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
/**
|
||||
* If this is a standard channel, reset the fade timeout to the default
|
||||
* timeout. Once processing is underway, if we get a MAINT message,
|
||||
* this indicates we're processing a traffic channel as a standard
|
||||
* channel, so we'll issue a different call timeout then.
|
||||
*/
|
||||
if( mChannelType == ChannelType.STANDARD )
|
||||
{
|
||||
broadcast( new ChangeChannelTimeoutEvent( this, mChannelType,
|
||||
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000 ) );
|
||||
|
||||
if( mCurrentCallEvent != null )
|
||||
{
|
||||
mCurrentCallEvent.end();
|
||||
broadcast( mCurrentCallEvent );
|
||||
mCurrentCallEvent = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getSite()
|
||||
|
||||
@@ -436,7 +436,7 @@ public class P25DecoderState extends DecoderState
|
||||
{
|
||||
if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION )
|
||||
{
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||
|
||||
if( mCurrentCallEvent != null )
|
||||
{
|
||||
@@ -1056,7 +1056,7 @@ public class P25DecoderState extends DecoderState
|
||||
}
|
||||
break;
|
||||
case CALL_TERMINATION_OR_CANCELLATION:
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
||||
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||
mCurrentCallEvent = null;
|
||||
|
||||
if( !( ldu instanceof CallTermination ) )
|
||||
@@ -1520,7 +1520,7 @@ public class P25DecoderState extends DecoderState
|
||||
private void processTSBK( TSBKMessage tsbk )
|
||||
{
|
||||
/* Trunking Signalling Block Messages - indicates Control Channel */
|
||||
broadcast( new DecoderStateEvent( this, Event.START, State.CONTROL ) );
|
||||
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) );
|
||||
|
||||
if( tsbk.getVendor() == Vendor.STANDARD )
|
||||
{
|
||||
|
||||
@@ -87,6 +87,8 @@ public class P25Message extends Message
|
||||
sb.append( " " );
|
||||
sb.append( getDUID().getLabel() );
|
||||
|
||||
sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ public class PassportDecoder extends Decoder
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mPassportMessageFramer.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
29
src/module/decode/state/ChangeChannelTimeoutEvent.java
Normal file
29
src/module/decode/state/ChangeChannelTimeoutEvent.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package module.decode.state;
|
||||
|
||||
import controller.channel.Channel.ChannelType;
|
||||
|
||||
/**
|
||||
* Allows decoders to change the call timeout setting in the channel state
|
||||
*/
|
||||
public class ChangeChannelTimeoutEvent extends DecoderStateEvent
|
||||
{
|
||||
private ChannelType mChannelType;
|
||||
private long mCallTimeout;
|
||||
|
||||
public ChangeChannelTimeoutEvent( Object source, ChannelType channelType, long timeout )
|
||||
{
|
||||
super( source, Event.CHANGE_CALL_TIMEOUT, State.IDLE );
|
||||
mChannelType = channelType;
|
||||
mCallTimeout = timeout;
|
||||
}
|
||||
|
||||
public ChannelType getChannelType()
|
||||
{
|
||||
return mChannelType;
|
||||
}
|
||||
|
||||
public long getCallTimeout()
|
||||
{
|
||||
return mCallTimeout;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014,2015 Dennis Sheirer
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -22,16 +22,9 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import module.Module;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.ICallEventProvider;
|
||||
import module.decode.state.DecoderStateEvent.Event;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import audio.metadata.IMetadataProvider;
|
||||
import audio.metadata.Metadata;
|
||||
import audio.metadata.MetadataReset;
|
||||
@@ -41,38 +34,13 @@ import audio.squelch.SquelchState;
|
||||
import controller.ThreadPoolManager;
|
||||
import controller.ThreadPoolManager.ThreadType;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import module.Module;
|
||||
import module.decode.config.DecodeConfiguration;
|
||||
import module.decode.event.CallEvent;
|
||||
import module.decode.event.ICallEventProvider;
|
||||
import module.decode.state.DecoderStateEvent.Event;
|
||||
import sample.Listener;
|
||||
|
||||
/**
|
||||
* Channel state tracks the overall state of all processing modules and decoders
|
||||
* configured for the channel and provides squelch control events.
|
||||
*
|
||||
* Primary channel state provides a state machine for tracking decode events
|
||||
* during a voice or data call through it's life cycle of CALL/DATA, FADE, END,
|
||||
* and then back to an IDLE state. This class also provides squelch control
|
||||
* to support any audio processing.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
public class ChannelState extends Module implements ICallEventProvider,
|
||||
IChangedAttributeProvider, IDecoderStateEventListener,
|
||||
IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider
|
||||
@@ -84,8 +52,6 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
public static final long RESET_TIMEOUT_DELAY = 2000;
|
||||
|
||||
private State mState = State.IDLE;
|
||||
private long mTrafficChannelTimeout =
|
||||
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS;
|
||||
|
||||
private Listener<CallEvent> mCallEventListener;
|
||||
private Listener<ChangedAttribute> mChangedAttributeListener;
|
||||
@@ -98,12 +64,15 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
private boolean mSquelchLocked = false;
|
||||
private boolean mSelected = false;
|
||||
|
||||
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
|
||||
private long mTrafficChannelFadeTimeout =
|
||||
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
|
||||
private long mFadeTimeout;
|
||||
private long mResetTimeout;
|
||||
private long mEndTimeout;
|
||||
|
||||
private StateMonitor mStateMonitor = new StateMonitor();
|
||||
private AtomicBoolean mMonitoring = new AtomicBoolean();
|
||||
private ScheduledFuture<?> mRunningMonitor;
|
||||
private ScheduledFuture<?> mStateMonitorFuture;
|
||||
|
||||
private ThreadPoolManager mThreadPoolManager;
|
||||
private ChannelType mChannelType;
|
||||
@@ -111,15 +80,48 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
private TrafficChannelStatusListener mTrafficChannelStatusListener;
|
||||
private CallEvent mTrafficChannelCallEvent;
|
||||
|
||||
/**
|
||||
* Channel state tracks the overall state of all processing modules and decoders
|
||||
* configured for the channel and provides squelch control events.
|
||||
*
|
||||
* Uses a state enumeration that defines allowable channel state transitions in
|
||||
* order to track a call or data decode event from start to finish. Uses a
|
||||
* timer to monitor for inactivity and to provide a FADE period that indicates
|
||||
* to the user that the activity has stopped while continuing to provide details
|
||||
* about the call, before the state is reset to IDLE.
|
||||
*
|
||||
* Since this class is multi-threaded between decoder events and the internal
|
||||
* timer that monitors for inactivity, this class uses the mState object for
|
||||
* thread synchronization.
|
||||
*
|
||||
* State Descriptions:
|
||||
* IDLE Normal state. No voice or data call activity
|
||||
* CALL/DATA/ENCRYPTED/CONTROL
|
||||
* Decoding states.
|
||||
* FADE The phase after a voice or data call when either an explicit
|
||||
* call end has been received, or when no new signalling updates
|
||||
* have been received, and the fade timer has expired. This phase
|
||||
* allows for gui updates to signal to the user that the call is
|
||||
* ended, while continuing to display the call details for the user
|
||||
* TEARDOWN Indicates a traffic channel that will be torn down for reuse.
|
||||
*/
|
||||
public ChannelState( ThreadPoolManager manager, ChannelType channelType )
|
||||
{
|
||||
mThreadPoolManager = manager;
|
||||
mChannelType = channelType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets this channel state and prepares it for reuse.
|
||||
*/
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||
broadcast( new MetadataReset() );
|
||||
|
||||
mState = State.IDLE;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -131,6 +133,7 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
public void stop()
|
||||
{
|
||||
mSquelchLocked = false;
|
||||
stopMonitor();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
@@ -145,9 +148,34 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
mStateMonitor = null;
|
||||
}
|
||||
|
||||
private boolean isStandardChannel()
|
||||
{
|
||||
return mChannelType == ChannelType.STANDARD;
|
||||
}
|
||||
|
||||
private boolean isTrafficChannel()
|
||||
{
|
||||
return mChannelType == ChannelType.TRAFFIC;
|
||||
}
|
||||
|
||||
public void setStandardChannelTimeout( long milliseconds )
|
||||
{
|
||||
mStandardChannelFadeTimeout = milliseconds;
|
||||
|
||||
if( mChannelType == ChannelType.STANDARD )
|
||||
{
|
||||
mFadeTimeout = mStandardChannelFadeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTrafficChannelTimeout( long milliseconds )
|
||||
{
|
||||
mTrafficChannelTimeout = milliseconds;
|
||||
mTrafficChannelFadeTimeout = milliseconds;
|
||||
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
{
|
||||
mFadeTimeout = mTrafficChannelFadeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelected( boolean selected )
|
||||
@@ -165,14 +193,6 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
return mState;
|
||||
}
|
||||
|
||||
private boolean isIdleOrEndingState()
|
||||
{
|
||||
return mState == State.FADE ||
|
||||
mState == State.END ||
|
||||
mState == State.IDLE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts the state monitor running when we're not in IDLE state. Calls to
|
||||
* start an already started monitor are ignored.
|
||||
@@ -181,12 +201,12 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
{
|
||||
if( mMonitoring.compareAndSet( false, true ) )
|
||||
{
|
||||
if( mRunningMonitor == null && mStateMonitor != null )
|
||||
if( mStateMonitorFuture == null && mStateMonitor != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
mRunningMonitor = mThreadPoolManager.scheduleFixedRate(
|
||||
ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
|
||||
mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate(
|
||||
ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
catch( RejectedExecutionException ree )
|
||||
{
|
||||
@@ -210,12 +230,17 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
{
|
||||
if( mMonitoring.compareAndSet( true, false ) )
|
||||
{
|
||||
if( mRunningMonitor != null )
|
||||
if( mStateMonitorFuture != null )
|
||||
{
|
||||
mThreadPoolManager.cancel( mRunningMonitor );
|
||||
boolean success = mThreadPoolManager.cancel( mStateMonitorFuture );
|
||||
|
||||
if( !success )
|
||||
{
|
||||
mLog.error("Couldn't stop monitoring scheduled future" );
|
||||
}
|
||||
}
|
||||
|
||||
mRunningMonitor = null;
|
||||
mStateMonitorFuture = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,20 +249,13 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
*/
|
||||
private void updateFadeTimeout()
|
||||
{
|
||||
/* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels
|
||||
* use the TRAFFIC_FADE_TIMEOUT_DELAY. Some traffic channels (e.g. MPT)
|
||||
* don't have signaling on the traffic channel that can be used to
|
||||
* update the fade timeout, so we set the timeout value larger to ensure
|
||||
* the channel doesn't fade before the end of the call. */
|
||||
long current = System.currentTimeMillis();
|
||||
|
||||
long timeout = current +
|
||||
( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY :
|
||||
mTrafficChannelTimeout );
|
||||
|
||||
if( timeout > mFadeTimeout )
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
mFadeTimeout = timeout;
|
||||
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,13 +264,13 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
*/
|
||||
private void updateResetTimeout()
|
||||
{
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis();
|
||||
mEndTimeout = System.currentTimeMillis();
|
||||
}
|
||||
else
|
||||
{
|
||||
mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||
mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,134 +312,129 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
*/
|
||||
protected void setState( State state )
|
||||
{
|
||||
if( mState == state )
|
||||
synchronized( mState )
|
||||
{
|
||||
switch( state )
|
||||
if( state == mState )
|
||||
{
|
||||
case CALL:
|
||||
case CONTROL:
|
||||
case DATA:
|
||||
case ENCRYPTED:
|
||||
if( State.CALL_STATES.contains( state ) )
|
||||
{
|
||||
updateFadeTimeout();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( mState.canChangeTo( state ) )
|
||||
{
|
||||
switch( state )
|
||||
else if( state != mState && mState.canChangeTo( state ) )
|
||||
{
|
||||
case CONTROL:
|
||||
//Don't allow traffic channels to be control channels,
|
||||
//otherwise they won't get call tear down
|
||||
if( mChannelType != ChannelType.TRAFFIC )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
case CONTROL:
|
||||
//Don't allow traffic channels to be control channels,
|
||||
//otherwise they can't transition to call tear down
|
||||
if( isStandardChannel() )
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateFadeTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
break;
|
||||
case DATA:
|
||||
case ENCRYPTED:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateFadeTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
break;
|
||||
case DATA:
|
||||
case ENCRYPTED:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateFadeTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
case CALL:
|
||||
broadcast( SquelchState.UNSQUELCH );
|
||||
updateFadeTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
case FADE:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
updateResetTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
case END:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||
broadcast( new MetadataReset() );
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
case IDLE:
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
stopMonitor();
|
||||
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
{
|
||||
if( mTrafficChannelStatusListener != null )
|
||||
{
|
||||
mTrafficChannelStatusListener.callEnd();
|
||||
mTrafficChannelStatusListener = null;
|
||||
}
|
||||
|
||||
if( mTrafficChannelCallEvent != null )
|
||||
{
|
||||
mTrafficChannelCallEvent.end();
|
||||
|
||||
broadcast( mTrafficChannelCallEvent );
|
||||
mTrafficChannelCallEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
break;
|
||||
case CALL:
|
||||
broadcast( SquelchState.UNSQUELCH );
|
||||
updateFadeTimeout();
|
||||
startMonitor();
|
||||
mState = state;
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
break;
|
||||
case FADE:
|
||||
processFadeState();
|
||||
break;
|
||||
case IDLE:
|
||||
processIdleState();
|
||||
break;
|
||||
case TEARDOWN:
|
||||
processTeardownState();
|
||||
break;
|
||||
case RESET:
|
||||
mState = State.IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( mState == State.ENCRYPTED && state == State.CALL )
|
||||
{
|
||||
updateFadeTimeout();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state and processes related actions
|
||||
*/
|
||||
private void processFadeState()
|
||||
{
|
||||
updateResetTimeout();
|
||||
mState = State.FADE;
|
||||
startMonitor();
|
||||
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
private void processIdleState()
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
|
||||
if( mState == State.FADE )
|
||||
{
|
||||
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||
broadcast( new MetadataReset() );
|
||||
}
|
||||
|
||||
stopMonitor();
|
||||
mState = State.IDLE;
|
||||
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
}
|
||||
|
||||
private void processTeardownState()
|
||||
{
|
||||
broadcast( SquelchState.SQUELCH );
|
||||
|
||||
stopMonitor();
|
||||
|
||||
mState = State.TEARDOWN;
|
||||
|
||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||
|
||||
if( mTrafficChannelCallEvent != null )
|
||||
{
|
||||
mTrafficChannelCallEvent.end();
|
||||
|
||||
broadcast( mTrafficChannelCallEvent );
|
||||
mTrafficChannelCallEvent = null;
|
||||
}
|
||||
|
||||
if( mTrafficChannelStatusListener != null )
|
||||
{
|
||||
mTrafficChannelStatusListener.callEnd();
|
||||
mTrafficChannelStatusListener = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
||||
}
|
||||
}
|
||||
|
||||
public class StateMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if( getState() == State.FADE )
|
||||
{
|
||||
if( mResetTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
setState( State.END );
|
||||
}
|
||||
}
|
||||
else if( getState() == State.END )
|
||||
{
|
||||
setState( State.IDLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mFadeTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
setState( State.FADE );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Exception thrown while state monitor is running " +
|
||||
"- state [" + getState() +
|
||||
"] current [" + System.currentTimeMillis() +
|
||||
"] mResetTimeout [" + mResetTimeout +
|
||||
"] mFadeTimeout [" + mFadeTimeout +
|
||||
"]", e );
|
||||
}
|
||||
mLog.error( "Traffic channel status listener was null - couldn't "
|
||||
+ "teardown the channel");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,35 +628,95 @@ public class ChannelState extends Module implements ICallEventProvider,
|
||||
broadcast( SquelchState.UNSQUELCH );
|
||||
mSquelchLocked = true;
|
||||
break;
|
||||
case CHANGE_CALL_TIMEOUT:
|
||||
if( event instanceof ChangeChannelTimeoutEvent )
|
||||
{
|
||||
ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event;
|
||||
|
||||
if( timeout.getChannelType() == ChannelType.STANDARD )
|
||||
{
|
||||
setStandardChannelTimeout( timeout.getCallTimeout() );
|
||||
}
|
||||
else
|
||||
{
|
||||
setTrafficChannelTimeout( timeout.getCallTimeout() );
|
||||
}
|
||||
}
|
||||
case CONTINUATION:
|
||||
if( isIdleOrEndingState() )
|
||||
{
|
||||
setState( event.getState() );
|
||||
}
|
||||
else
|
||||
{
|
||||
updateFadeTimeout();
|
||||
}
|
||||
break;
|
||||
case DECODE:
|
||||
if( getState() == State.IDLE )
|
||||
case START:
|
||||
if( State.CALL_STATES.contains( event.getState() ) )
|
||||
{
|
||||
setState( event.getState() );
|
||||
}
|
||||
break;
|
||||
case END:
|
||||
setState( State.FADE );
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
setState( State.TEARDOWN );
|
||||
}
|
||||
else
|
||||
{
|
||||
setState( State.FADE );
|
||||
}
|
||||
break;
|
||||
case RESET:
|
||||
/* Channel State does not respond to reset events */
|
||||
break;
|
||||
case START:
|
||||
setState( event.getState() );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors decoder state events to automatically transition the channel
|
||||
* state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
|
||||
* decoding stops or the monitored channel returns to a no signal state.
|
||||
*
|
||||
* Provides a FADE transition state to allow for momentary decoding dropouts
|
||||
* and to allow the user access to call details for a fade period upon
|
||||
* call end.
|
||||
*/
|
||||
public class StateMonitor implements Runnable
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized( mState )
|
||||
{
|
||||
if( State.CALL_STATES.contains( mState ) &&
|
||||
mFadeTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
mState = State.FADE;
|
||||
processFadeState();
|
||||
}
|
||||
else if( mState == State.FADE &&
|
||||
mEndTimeout <= System.currentTimeMillis() )
|
||||
{
|
||||
if( isTrafficChannel() )
|
||||
{
|
||||
processTeardownState();
|
||||
}
|
||||
else
|
||||
{
|
||||
processIdleState();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
mLog.error( "Exception thrown while state monitor is running " +
|
||||
"- state [" + getState() +
|
||||
"] current [" + System.currentTimeMillis() +
|
||||
"] mResetTimeout [" + mEndTimeout +
|
||||
"] mFadeTimeout [" + mFadeTimeout +
|
||||
"]", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import module.decode.DecoderFactory;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelUtils;
|
||||
import module.decode.DecoderFactory;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import playlist.PlaylistManager;
|
||||
import record.RecorderType;
|
||||
import record.config.RecordConfiguration;
|
||||
@@ -29,8 +30,6 @@ import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingChangeListener;
|
||||
import settings.SettingsManager;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ChannelUtils;
|
||||
|
||||
public class ChannelStatePanel extends JPanel
|
||||
implements Listener<ChangedAttribute>, SettingChangeListener
|
||||
@@ -236,26 +235,37 @@ public class ChannelStatePanel extends JPanel
|
||||
Graphics2D g2 = (Graphics2D)g.create();
|
||||
|
||||
Paint p = null;
|
||||
|
||||
final Channel channel = mChannel;
|
||||
|
||||
switch( mChannel.getChannelState().getState() )
|
||||
final ChannelState channelState = ( channel == null ? null : channel.getChannelState() );
|
||||
|
||||
if( channelState != null )
|
||||
{
|
||||
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 END:
|
||||
case IDLE:
|
||||
default:
|
||||
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||
break;
|
||||
switch( channelState.getState() )
|
||||
{
|
||||
case CALL:
|
||||
p = getGradient( mColorTopCall, mColorMiddleCall );
|
||||
break;
|
||||
case CONTROL:
|
||||
p = getGradient( mColorTopControl, mColorMiddleControl );
|
||||
break;
|
||||
case DATA:
|
||||
p = getGradient( mColorTopData, mColorMiddleData );
|
||||
break;
|
||||
case FADE:
|
||||
p = getGradient( mColorTopFade, mColorMiddleFade );
|
||||
break;
|
||||
case IDLE:
|
||||
default:
|
||||
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( p == null )
|
||||
{
|
||||
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||
}
|
||||
|
||||
g2.setPaint( p );
|
||||
|
||||
@@ -56,6 +56,7 @@ public class DecoderStateEvent
|
||||
public enum Event
|
||||
{
|
||||
ALWAYS_UNSQUELCH,
|
||||
CHANGE_CALL_TIMEOUT,
|
||||
CONTINUATION,
|
||||
DECODE,
|
||||
END,
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package module.decode.state;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Details the set of states for a channel and the allowable transition states
|
||||
*/
|
||||
public enum State
|
||||
{
|
||||
IDLE( "IDLE" )
|
||||
@@ -7,7 +12,8 @@ public enum State
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return true;
|
||||
return state != TEARDOWN &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
CALL( "CALL" )
|
||||
@@ -15,11 +21,11 @@ public enum State
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
return state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == ENCRYPTED ||
|
||||
state == FADE;
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
DATA( "DATA" )
|
||||
@@ -29,9 +35,9 @@ public enum State
|
||||
{
|
||||
return state == CALL ||
|
||||
state == CONTROL ||
|
||||
state == DATA ||
|
||||
state == State.ENCRYPTED ||
|
||||
state == FADE;
|
||||
state == ENCRYPTED ||
|
||||
state == FADE ||
|
||||
state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
ENCRYPTED( "ENCRYPTED" )
|
||||
@@ -39,7 +45,7 @@ public enum State
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state == FADE;
|
||||
return state == FADE || state == TEARDOWN;
|
||||
}
|
||||
},
|
||||
CONTROL( "CONTROL" )
|
||||
@@ -47,7 +53,8 @@ public enum State
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return true;
|
||||
return state == IDLE ||
|
||||
state == FADE;
|
||||
}
|
||||
},
|
||||
FADE( "FADE" )
|
||||
@@ -55,20 +62,36 @@ public enum State
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != FADE; //All states except fade allowed
|
||||
}
|
||||
return state != FADE &&
|
||||
state != RESET;
|
||||
}
|
||||
},
|
||||
END( "END" )
|
||||
RESET( "RESET" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo( State state )
|
||||
{
|
||||
return state != FADE; //Fade is only disallowed state
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == IDLE;
|
||||
}
|
||||
},
|
||||
|
||||
TEARDOWN( "TEARDOWN" )
|
||||
{
|
||||
@Override
|
||||
public boolean canChangeTo(State state)
|
||||
{
|
||||
return state == RESET;
|
||||
}
|
||||
};
|
||||
|
||||
private String mDisplayValue;
|
||||
|
||||
public static final EnumSet<State> CALL_STATES =
|
||||
EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED );
|
||||
|
||||
public static final EnumSet<State> IDLE_STATES =
|
||||
EnumSet.of( IDLE, FADE );
|
||||
|
||||
private State( String displayValue )
|
||||
{
|
||||
mDisplayValue = displayValue;
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import record.config.RecordConfiguration;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import source.config.SourceConfigTuner;
|
||||
import source.tuner.TunerChannel;
|
||||
@@ -96,10 +97,21 @@ public class TrafficChannelManager extends Module
|
||||
mPreviousDoNotMonitorCallEvent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a channel by either reusing an existing channel or constructing
|
||||
* a new one, limited by the total number of constructed channels allowed.
|
||||
*
|
||||
* Note: you must enforce thread safety on the mTrafficChannelsInUse
|
||||
* external to this method.
|
||||
*
|
||||
* @param channelNumber
|
||||
* @param tunerChannel
|
||||
* @return
|
||||
*/
|
||||
private Channel getChannel( String channelNumber, TunerChannel tunerChannel )
|
||||
{
|
||||
Channel channel = null;
|
||||
|
||||
|
||||
if( !mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||
{
|
||||
for( Channel configuredChannel: mTrafficChannelPool )
|
||||
@@ -151,10 +163,11 @@ public class TrafficChannelManager extends Module
|
||||
channel.setSystem( mSystem, false );
|
||||
channel.setSite( mSite, false );
|
||||
channel.setName( "Traffic" );
|
||||
mLog.debug("Channel not processing after setting enabled to true - why" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
@@ -164,72 +177,79 @@ public class TrafficChannelManager extends Module
|
||||
*/
|
||||
private void process( TrafficChannelAllocationEvent event )
|
||||
{
|
||||
if( mCallEventListener != null )
|
||||
{
|
||||
synchronized( mCallEventListener )
|
||||
{
|
||||
CallEvent callEvent = event.getCallEvent();
|
||||
CallEvent callEvent = event.getCallEvent();
|
||||
|
||||
/* Check for duplicate events and suppress */
|
||||
if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) )
|
||||
/* Check for duplicate events and suppress */
|
||||
synchronized( mTrafficChannelsInUse )
|
||||
{
|
||||
if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long frequency = callEvent.getFrequency();
|
||||
|
||||
/* Check the from/to aliases for do not monitor priority */
|
||||
if( isDoNotMonitor( callEvent ) )
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DO_NOT_MONITOR );
|
||||
|
||||
if( isSameCallEvent( mPreviousDoNotMonitorCallEvent, callEvent ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long frequency = callEvent.getFrequency();
|
||||
|
||||
/* Check the from/to aliases for do not monitor priority */
|
||||
if( isDoNotMonitor( callEvent ) )
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DO_NOT_MONITOR );
|
||||
|
||||
if( isSameCallEvent( mPreviousDoNotMonitorCallEvent, callEvent ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPreviousDoNotMonitorCallEvent = callEvent;
|
||||
}
|
||||
mPreviousDoNotMonitorCallEvent = callEvent;
|
||||
}
|
||||
else if( frequency > 0 )
|
||||
{
|
||||
Channel channel = getChannel( callEvent.getChannel(),
|
||||
new TunerChannel( Type.TRAFFIC, frequency,
|
||||
mDecodeConfiguration.getDecoderType()
|
||||
.getChannelBandwidth() ) );
|
||||
}
|
||||
else if( frequency > 0 )
|
||||
{
|
||||
Channel channel = getChannel( callEvent.getChannel(),
|
||||
new TunerChannel( Type.TRAFFIC, frequency,
|
||||
mDecodeConfiguration.getDecoderType()
|
||||
.getChannelBandwidth() ) );
|
||||
|
||||
if( channel != null )
|
||||
if( channel != null )
|
||||
{
|
||||
if( channel.isProcessing() )
|
||||
{
|
||||
if( channel.isProcessing() )
|
||||
{
|
||||
ChannelState state = channel.getChannelState();
|
||||
|
||||
/* Register as a listener to be notified when the call
|
||||
* is completed */
|
||||
state.configureAsTrafficChannel(
|
||||
new TrafficChannelStatusListener( this,
|
||||
callEvent.getChannel() ), event );
|
||||
|
||||
/* Set state to call so that audio squelch state is correct */
|
||||
state.setState( State.CALL );
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
}
|
||||
callEvent.setDetails( "TRAFFIC CHANNEL ALLOCATED" );
|
||||
|
||||
ChannelState state = channel.getChannelState();
|
||||
|
||||
/* Register as a listener to be notified when the call
|
||||
* is completed */
|
||||
state.configureAsTrafficChannel(
|
||||
new TrafficChannelStatusListener( this,
|
||||
callEvent.getChannel() ), event );
|
||||
|
||||
/* Set state to call so that audio squelch state is correct */
|
||||
state.setState( State.CALL );
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
callEvent.setDetails( "CHANNEL PROCESSING ERROR" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
callEvent.setDetails( "NO TUNER AVAILABLE" );
|
||||
}
|
||||
|
||||
mCallEventListener.receive( callEvent );
|
||||
}
|
||||
else
|
||||
{
|
||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||
callEvent.setDetails( "UNKNOWN FREQUENCY" );
|
||||
}
|
||||
|
||||
final Listener<CallEvent> listener = mCallEventListener;
|
||||
|
||||
if( listener != null )
|
||||
{
|
||||
listener.receive( callEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -370,14 +390,18 @@ public class TrafficChannelManager extends Module
|
||||
*/
|
||||
public void callEnd( String channelNumber )
|
||||
{
|
||||
if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||
synchronized( mTrafficChannelsInUse )
|
||||
{
|
||||
Channel channel = mTrafficChannelsInUse.get( channelNumber );
|
||||
|
||||
mTrafficChannelsInUse.remove( channelNumber );
|
||||
if( channelNumber != null &&
|
||||
mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||
{
|
||||
Channel channel = mTrafficChannelsInUse.get( channelNumber );
|
||||
|
||||
mTrafficChannelsInUse.remove( channelNumber );
|
||||
|
||||
/* Disable the channel and broadcast a notification */
|
||||
channel.setEnabled( false, true );
|
||||
/* Disable the channel and broadcast a notification */
|
||||
channel.setEnabled( false, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,10 +246,6 @@ public abstract class TunerController implements Tunable
|
||||
{
|
||||
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "Request to release channel that is not in current tuned channels set" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user