Merge pull request #178 from DSheirer/165-overflowable-buffer-issue-causes-channels-decoding-stop

165 overflowable buffer issue causes channels decoding stop
This commit is contained in:
Denny Sheirer
2017-02-10 14:50:30 +00:00
committed by GitHub
18 changed files with 630 additions and 363 deletions

View File

@@ -7,6 +7,7 @@
<w>fleetsync</w> <w>fleetsync</w>
<w>icecast</w> <w>icecast</w>
<w>ident</w> <w>ident</w>
<w>overflowable</w>
<w>polyphase</w> <w>polyphase</w>
<w>remez</w> <w>remez</w>
<w>samplerate</w> <w>samplerate</w>

View File

@@ -1,10 +1,10 @@
0.3.0-beta1 20170129 0.3.0-beta1 20170129
- REQUIRES: Java 8 (or newer) for compatibility and eventual changeover to JavaFX
- FEATURE: audio streaming - adds capability to stream to multiple remote broadcast - FEATURE: audio streaming - adds capability to stream to multiple remote broadcast
servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each servers (Broadcastify, Icecast v2.3 and v2.4+, and Shoutcast V1 and V2). Each
stream can be independently configured with a delay. Audio calls are directed stream can be independently configured with a delay. Audio calls are directed
to one or more streams by attaching one or more stream identifiers to each alias. to one or more streams by attaching one or more stream identifiers to each alias.
- Requires Java 8 (or newer) for compatibility and eventual changeover to JavaFX
- Extensive GUI updates for tuner, channel and alias management. Includes new - Extensive GUI updates for tuner, channel and alias management. Includes new
'Now Playing' channel view and 'Detail' view. 'Now Playing' channel view and 'Detail' view.
- Updated alias list management of alias id values and wildcards. Weighting is now - Updated alias list management of alias id values and wildcards. Weighting is now
@@ -17,7 +17,7 @@
- Implemented bounded queues for both tuner and individual channel IQ sample - Implemented bounded queues for both tuner and individual channel IQ sample
buffers. Bounded queues have ability to enter/exit overflow state when queue buffers. Bounded queues have ability to enter/exit overflow state when queue
size reaches overflow threshold and reset when queue size falls below the reset size reaches overflow threshold and reset when queue size falls below the reset
threshold. threshold. Channel state will show OVERFLOW in red/yellow.
- Updated airspy controller for airspy-mini specific sample rate handling. - Updated airspy controller for airspy-mini specific sample rate handling.
- New developer remez filter designer tool and internal usage - New developer remez filter designer tool and internal usage

View File

@@ -168,11 +168,14 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
for(AliasAction action: alias.getAction()) if(!(alias instanceof PatchGroupAlias))
{ {
if(!aliasActions.contains(action)) for(AliasAction action: alias.getAction())
{ {
aliasActions.add(action); if(!aliasActions.contains(action))
{
aliasActions.add(action);
}
} }
} }
} }
@@ -185,7 +188,7 @@ public class PatchGroupAlias extends Alias
{ {
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.hasActions()) if(!(alias instanceof PatchGroupAlias) && alias.hasActions())
{ {
return true; return true;
} }
@@ -201,7 +204,9 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.hasCallPriority() && alias.getCallPriority() < highestPriority) if(!(alias instanceof PatchGroupAlias) &&
alias.hasCallPriority() &&
alias.getCallPriority() < highestPriority)
{ {
highestPriority = alias.getCallPriority(); highestPriority = alias.getCallPriority();
} }
@@ -220,7 +225,7 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.hasCallPriority()) if(!(alias instanceof PatchGroupAlias) && alias.hasCallPriority())
{ {
return true; return true;
} }
@@ -239,7 +244,7 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.isRecordable()) if(!(alias instanceof PatchGroupAlias) && alias.isRecordable())
{ {
return true; return true;
} }
@@ -258,7 +263,7 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.isStreamable()) if(!(alias instanceof PatchGroupAlias) && alias.isStreamable())
{ {
return true; return true;
} }
@@ -279,11 +284,14 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels()) if(!(alias instanceof PatchGroupAlias))
{ {
if(!broadcastChannels.contains(broadcastChannel)) for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
{ {
broadcastChannels.add(broadcastChannel); if(!broadcastChannels.contains(broadcastChannel))
{
broadcastChannels.add(broadcastChannel);
}
} }
} }
} }
@@ -301,7 +309,7 @@ public class PatchGroupAlias extends Alias
for(Alias alias: mPatchedAliases) for(Alias alias: mPatchedAliases)
{ {
if(alias.hasBroadcastChannel(channel)) if(!(alias instanceof PatchGroupAlias) && alias.hasBroadcastChannel(channel))
{ {
return true; return true;
} }

View File

@@ -210,6 +210,10 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE); background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "idle", Color.WHITE);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE); foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "idle", Color.BLUE);
break; break;
case OVERFLOW:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "overflow", Color.RED);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "overflow", Color.YELLOW);
break;
case RESET: case RESET:
background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK); background = SystemProperties.getInstance().get(PROPERTY_PREFIX_BACKGROUND + "reset", Color.PINK);
foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW); foreground = SystemProperties.getInstance().get(PROPERTY_PREFIX_FOREGROUND + "reset", Color.YELLOW);

View File

@@ -51,7 +51,7 @@ public class ChannelMetadataViewer extends JPanel
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager); mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
mCallEventPanel = new CallEventPanel(iconManager); mCallEventPanel = new CallEventPanel(iconManager);
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager); mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager); // mChannelSpectrumPanel = new ChannelSpectrumPanel(settingsManager, channelProcessingManager);
mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager); mChannelMetadataPanel = new ChannelMetadataPanel(channelProcessingManager, iconManager);
init(); init();

View File

@@ -35,6 +35,7 @@ import module.decode.event.ICallEventProvider;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sample.Listener; import sample.Listener;
import sample.real.IOverflowListener;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -42,7 +43,7 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener, public class ChannelState extends Module implements ICallEventProvider, IDecoderStateEventListener,
IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener IDecoderStateEventProvider, ISquelchStateProvider, IAttributeChangeRequestListener, IOverflowListener
{ {
private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class); private final static Logger mLog = LoggerFactory.getLogger(ChannelState.class);
@@ -63,6 +64,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
private boolean mSquelchLocked = false; private boolean mSquelchLocked = false;
private boolean mSelected = false; private boolean mSelected = false;
private boolean mSourceOverflow = false;
private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
@@ -123,6 +125,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
mState = State.IDLE; mState = State.IDLE;
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState)); mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));
mSourceOverflow = false;
} }
@Override @Override
@@ -350,15 +353,26 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
break; break;
} }
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState)); mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
} mSourceOverflow ? State.OVERFLOW : mState));
else
{
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
} }
} }
} }
/**
* This method is invoked if the source buffer provider goes into overflow state. Since this is an external state,
* we use the mSourceOverflow variable to override the internal state reported to external listeners.
* @param overflow true to indicate an overflow state
*/
@Override
public void sourceOverflow(boolean overflow)
{
mSourceOverflow = overflow;
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE,
mSourceOverflow ? State.OVERFLOW : mState));
}
/** /**
* Sets the state and processes related actions * Sets the state and processes related actions
*/ */

View File

@@ -51,6 +51,14 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
return mAliasList != null; return mAliasList != null;
} }
/**
* Provides subclass reference to the call event broadcaster
*/
protected Broadcaster<CallEvent> getCallEventBroadcaster()
{
return mCallEventBroadcaster;
}
/** /**
* Reset the decoder state to prepare for processing a different sample * Reset the decoder state to prepare for processing a different sample
* source * source

View File

@@ -24,97 +24,93 @@ import java.util.EnumSet;
*/ */
public enum State public enum State
{ {
IDLE( "IDLE" ) IDLE("IDLE")
{
@Override
public boolean canChangeTo( State state )
{ {
return state != TEARDOWN && @Override
state != RESET; public boolean canChangeTo(State state)
} {
}, return state != TEARDOWN && state != RESET && state != OVERFLOW;
CALL( "CALL" ) }
{ },
@Override CALL("CALL")
public boolean canChangeTo( State state )
{ {
return state == CONTROL || @Override
state == DATA || public boolean canChangeTo(State state)
state == ENCRYPTED || {
state == FADE || return state == CONTROL || state == DATA || state == ENCRYPTED || state == FADE || state == TEARDOWN;
state == TEARDOWN; }
} },
}, DATA("DATA")
DATA( "DATA" )
{
@Override
public boolean canChangeTo( State state )
{ {
return state == CALL || @Override
state == CONTROL || public boolean canChangeTo(State state)
state == ENCRYPTED || {
state == FADE || return state == CALL || state == CONTROL || state == ENCRYPTED || state == FADE || state == TEARDOWN;
state == TEARDOWN; }
} },
}, ENCRYPTED("ENCRYPTED")
ENCRYPTED( "ENCRYPTED" ) {
@Override
public boolean canChangeTo(State state)
{
return state == FADE || state == TEARDOWN;
}
},
CONTROL("CONTROL")
{
@Override
public boolean canChangeTo(State state)
{
return state == IDLE || state == FADE;
}
},
FADE("FADE")
{
@Override
public boolean canChangeTo(State state)
{
return state != FADE && state != RESET && state != OVERFLOW;
}
},
OVERFLOW("OVERFLOW")
{
@Override
public boolean canChangeTo(State state)
{
return false;
}
},
RESET("RESET")
{
@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)
{ {
@Override mDisplayValue = displayValue;
public boolean canChangeTo( State state ) }
{
return state == FADE || state == TEARDOWN; public abstract boolean canChangeTo(State state);
}
}, public String getDisplayValue()
CONTROL( "CONTROL" )
{ {
@Override return mDisplayValue;
public boolean canChangeTo( State state ) }
{
return state == IDLE ||
state == FADE;
}
},
FADE( "FADE" )
{
@Override
public boolean canChangeTo( State state )
{
return state != FADE &&
state != RESET;
}
},
RESET( "RESET" )
{
@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;
}
public abstract boolean canChangeTo( State state );
public String getDisplayValue()
{
return mDisplayValue;
}
} }

View File

@@ -1,5 +1,9 @@
package controller; package controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.ThreadPool;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -8,24 +12,23 @@ import java.util.concurrent.atomic.AtomicInteger;
*/ */
public class NamingThreadFactory implements ThreadFactory public class NamingThreadFactory implements ThreadFactory
{ {
static final AtomicInteger poolNumber = new AtomicInteger(1); private final static Logger mLog = LoggerFactory.getLogger(ThreadPool.class);
final ThreadGroup mThreadGroup; private static final AtomicInteger mPoolNumber = new AtomicInteger(1);
final AtomicInteger mThreadNumber = new AtomicInteger(1); private final ThreadGroup mThreadGroup;
final String mNamePrefix; private final AtomicInteger mThreadNumber = new AtomicInteger(1);
private final String mNamePrefix;
public NamingThreadFactory( String prefix ) public NamingThreadFactory( String prefix )
{ {
SecurityManager s = System.getSecurityManager(); SecurityManager s = System.getSecurityManager();
mThreadGroup = (s != null)? s.getThreadGroup() : mThreadGroup = (s != null)? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
Thread.currentThread().getThreadGroup();
mNamePrefix = prefix + " pool-" + mNamePrefix = prefix + " pool-" + mPoolNumber.getAndIncrement() + "-thread-";
poolNumber.getAndIncrement() +
"-thread-";
} }
public Thread newThread( Runnable runnable ) public Thread newThread( Runnable runnable )
@@ -43,6 +46,15 @@ public class NamingThreadFactory implements ThreadFactory
thread.setPriority( Thread.NORM_PRIORITY ); thread.setPriority( Thread.NORM_PRIORITY );
} }
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler()
{
@Override
public void uncaughtException(Thread t, Throwable e)
{
mLog.error("Error while executing runnable in scheduled thread pool [" + t.getName() + "]", e);
}
});
return thread; return thread;
} }
} }

View File

@@ -538,6 +538,9 @@ public class ProcessingChain implements IChannelEventListener
module.reset(); module.reset();
} }
//Setup the channel state to monitor source overflow conditions
mSource.setOverflowListener(mChannelState);
/* Register with the source to receive sample data. Setup a /* Register with the source to receive sample data. Setup a
* timer task to process the buffer queues 50 times a second * timer task to process the buffer queues 50 times a second
* (every 20 ms) */ * (every 20 ms) */
@@ -606,6 +609,7 @@ public class ProcessingChain implements IChannelEventListener
if(mSource != null) if(mSource != null)
{ {
mSource.stop(); mSource.stop();
mSource.setOverflowListener(null);
switch(mSource.getSampleType()) switch(mSource.getSampleType())
{ {

View File

@@ -19,9 +19,7 @@ package module.decode.p25;
import alias.Alias; import alias.Alias;
import alias.AliasList; import alias.AliasList;
import alias.PatchGroupAlias;
import alias.id.AliasIDType; import alias.id.AliasIDType;
import alias.id.talkgroup.TalkgroupID;
import channel.metadata.AliasedStringAttributeMonitor; import channel.metadata.AliasedStringAttributeMonitor;
import channel.metadata.Attribute; import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest; import channel.metadata.AttributeChangeRequest;
@@ -163,7 +161,6 @@ public class P25DecoderState extends DecoderState
private Map<Integer,IdentifierUpdate> mBands = new HashMap<>(); private Map<Integer,IdentifierUpdate> mBands = new HashMap<>();
private Map<String,Long> mRegistrations = new HashMap<>(); private Map<String,Long> mRegistrations = new HashMap<>();
private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>(); private Map<String,IAdjacentSite> mNeighborMap = new HashMap<>();
private Map<String,List<String>> mPatchGroupMap = new HashMap<>();
private String mLastCommandEventID; private String mLastCommandEventID;
private String mLastPageEventID; private String mLastPageEventID;
@@ -188,6 +185,7 @@ public class P25DecoderState extends DecoderState
private P25CallEvent mCurrentCallEvent; private P25CallEvent mCurrentCallEvent;
private List<String> mCallDetectTalkgroups = new ArrayList<>(); private List<String> mCallDetectTalkgroups = new ArrayList<>();
private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>(); private Map<String,P25CallEvent> mChannelCallMap = new HashMap<>();
private PatchGroupManager mPatchGroupManager;
public P25DecoderState(AliasList aliasList, public P25DecoderState(AliasList aliasList,
ChannelType channelType, ChannelType channelType,
@@ -199,6 +197,7 @@ public class P25DecoderState extends DecoderState
mModulation = modulation; mModulation = modulation;
mIgnoreDataCalls = ignoreDataCalls; mIgnoreDataCalls = ignoreDataCalls;
mPatchGroupManager = new PatchGroupManager(aliasList, getCallEventBroadcaster());
mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2, mSiteAttributeMonitor = new AliasedStringAttributeMonitor(Attribute.NETWORK_ID_2,
getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE); getAttributeChangeRequestListener(), getAliasList(), AliasIDType.SITE);
mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM, mFromTalkgroupMonitor = new AliasedStringAttributeMonitor(Attribute.PRIMARY_ADDRESS_FROM,
@@ -2719,6 +2718,9 @@ public class P25DecoderState extends DecoderState
switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode()) switch(((MotorolaTSBKMessage)tsbk).getMotorolaOpcode())
{ {
case PATCH_GROUP_CHANNEL_GRANT: case PATCH_GROUP_CHANNEL_GRANT:
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
mPatchGroupManager.cleanupPatchGroups();
PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk; PatchGroupVoiceChannelGrant pgvcg = (PatchGroupVoiceChannelGrant)tsbk;
channel = pgvcg.getChannel(); channel = pgvcg.getChannel();
@@ -2754,6 +2756,9 @@ public class P25DecoderState extends DecoderState
mChannelCallMap.get(channel))); mChannelCallMap.get(channel)));
break; break;
case PATCH_GROUP_CHANNEL_GRANT_UPDATE: case PATCH_GROUP_CHANNEL_GRANT_UPDATE:
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
mPatchGroupManager.cleanupPatchGroups();
PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk; PatchGroupVoiceChannelGrantUpdate gvcgu = (PatchGroupVoiceChannelGrantUpdate)tsbk;
channel = gvcgu.getChannel1(); channel = gvcgu.getChannel1();
@@ -2784,35 +2789,11 @@ public class P25DecoderState extends DecoderState
break; break;
case PATCH_GROUP_ADD: case PATCH_GROUP_ADD:
PatchGroupAdd pga = (PatchGroupAdd)tsbk; PatchGroupAdd pga = (PatchGroupAdd)tsbk;
mPatchGroupManager.updatePatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups());
if(addPatchGroup(pga.getPatchGroupAddress(), pga.getPatchedTalkgroups()))
{
StringBuilder sb = new StringBuilder();
sb.append("PATCH GROUP:").append(pga.getPatchGroupAddress());
sb.append(" ").append(pga.getPatchedTalkgroups());
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_ADD)
.aliasList(getAliasList())
.details(sb.toString())
.build());
}
break; break;
case PATCH_GROUP_DELETE: case PATCH_GROUP_DELETE:
PatchGroupDelete pgd = (PatchGroupDelete)tsbk; PatchGroupDelete pgd = (PatchGroupDelete)tsbk;
mPatchGroupManager.removePatchGroup(pgd.getPatchGroupAddress());
if(removePatchGroup(pgd.getPatchGroupAddress(), pgd.getPatchedTalkgroups()))
{
StringBuilder sbpgd = new StringBuilder();
sbpgd.append("PATCH GROUP:").append(pgd.getPatchGroupAddress());
sbpgd.append(" ").append(pgd.getPatchedTalkgroups());
broadcast(new P25CallEvent.Builder(CallEventType.PATCH_GROUP_DELETE)
.aliasList(getAliasList())
.details(sbpgd.toString())
.build());
}
break; break;
case CCH_PLANNED_SHUTDOWN: case CCH_PLANNED_SHUTDOWN:
if(!mControlChannelShutdownLogged) if(!mControlChannelShutdownLogged)
@@ -2827,174 +2808,14 @@ public class P25DecoderState extends DecoderState
} }
} }
/**
* Creates a new patch group or updates an existing patch group with the patched talkgroup entries.
*
* @param patchGroupID for the patch group
* @param talkgroupsToAdd to include in the patch group
* @return true if the patch group or the patch group map was updated
*/
private boolean addPatchGroup(String patchGroupID, List<String> talkgroupsToAdd)
{
boolean updated = false;
if(patchGroupID != null && !talkgroupsToAdd.isEmpty())
{
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
if(patchedTalkgroups == null)
{
patchedTalkgroups = new ArrayList<>();
}
for(String talkgroupToAdd : talkgroupsToAdd)
{
//Exclude the patch group ID if it is included in the patched talkgroup list so that we don't have
//an infinite loop situation
if(talkgroupToAdd.equals(patchGroupID))
{
continue;
}
if(!patchedTalkgroups.contains(talkgroupToAdd))
{
patchedTalkgroups.add(talkgroupToAdd);
updated = true;
}
}
if(updated)
{
mPatchGroupMap.put(patchGroupID, patchedTalkgroups);
updatePatchAlias(patchGroupID, patchedTalkgroups);
}
}
return updated;
}
/**
* Removes the patched talkgroups from the patch group and removes the patch group from the patch group map if
* there are no more patched talkgroups in the patch group.
*
* @param patchGroupID for the patch group
* @param talkgroupsToRemove to remove from the patch group
* @return true if the patch group or the patch group map was updated
*/
private boolean removePatchGroup(String patchGroupID, List<String> talkgroupsToRemove)
{
boolean updated = false;
if(patchGroupID != null && !talkgroupsToRemove.isEmpty())
{
List<String> patchedTalkgroups = mPatchGroupMap.get(patchGroupID);
if(patchedTalkgroups != null)
{
for(String talkgroupToRemove : talkgroupsToRemove)
{
if(patchedTalkgroups.contains(talkgroupToRemove))
{
patchedTalkgroups.remove(talkgroupToRemove);
updated = true;
}
}
if(patchedTalkgroups.isEmpty())
{
mPatchGroupMap.remove(patchGroupID);
removePatchGroupAlias(patchGroupID);
updated = true;
}
else
{
updatePatchAlias(patchGroupID, patchedTalkgroups);
}
}
}
return updated;
}
/**
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
* replaced with the aliases corresponding to the patched talkgroup aliases.
*
* @param patchGroupID for the patch group
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
*/
private void updatePatchAlias(String patchGroupID, List<String> patchedTalkgroups)
{
PatchGroupAlias patchGroupAlias = null;
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
Alias existingAlias = getAliasList().getTalkgroupAlias(patchGroupID, false);
if(existingAlias instanceof PatchGroupAlias)
{
patchGroupAlias = (PatchGroupAlias)existingAlias;
}
else
{
patchGroupAlias = new PatchGroupAlias();
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
if(existingAlias != null)
{
getAliasList().removeAlias(existingAlias);
patchGroupAlias.setPatchGroupAlias(existingAlias);
}
getAliasList().addAlias(patchGroupAlias);
}
if(patchGroupAlias != null)
{
patchGroupAlias.setPatchedTalkgroupIDs(patchedTalkgroups);
patchGroupAlias.clearPatchedAliases();
for(String patchedTalkgroup : patchedTalkgroups)
{
Alias patchedAlias = getAliasList().getTalkgroupAlias(patchedTalkgroup);
if(patchedAlias != null)
{
patchGroupAlias.addPatchedAlias(patchedAlias);
}
}
}
}
/**
* Removes the temporary patch group alias from the alias list
*/
private void removePatchGroupAlias(String patchGroupID)
{
Alias alias = getAliasList().getTalkgroupAlias(patchGroupID);
if(alias instanceof PatchGroupAlias)
{
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
getAliasList().removeAlias(patchGroupAlias);
//Replace our temporary patch group alias with the original alias for the patch group ID
if(patchGroupAlias.hasPatchGroupAlias())
{
getAliasList().addAlias(patchGroupAlias.getPatchGroupAlias());
}
}
}
/** /**
* Process a traffic channel allocation message * Process a traffic channel allocation message
*/ */
private void processTSBKChannelGrant(TSBKMessage message) private void processTSBKChannelGrant(TSBKMessage message)
{ {
//Cleanup patch groups - auto-expire any patch groups before we allocate a channel
mPatchGroupManager.cleanupPatchGroups();
String channel = null; String channel = null;
String from = null; String from = null;
String to = null; String to = null;

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.p25;
import java.util.ArrayList;
import java.util.List;
public class PatchGroup
{
private long mExpireThreshold = 5000; //5 seconds
private long mLastUpdated;
private String mPatchGroupID;
private List<String> mPatchedGroups = new ArrayList<>();
/**
* Constructs a patch group with the specified patch group ID
* @param patchGroupID that identifies this patch group
*/
public PatchGroup(String patchGroupID)
{
mPatchGroupID = patchGroupID;
mLastUpdated = System.currentTimeMillis();
}
/**
* Patch group ID for this patch group
*/
public String getPatchGroupID()
{
return mPatchGroupID;
}
/**
* Adds the patched group to this patch group. Duplicate patched groups are ignored.
*
* Note: the patch group ID will not be added as a patched group, to avoid circular dependencies.
*
* @return true if the patched group was added to this patch group
*/
public boolean addPatchedGroup(String patchedGroup)
{
if(patchedGroup != null && !patchedGroup.equals(mPatchGroupID) && !mPatchedGroups.contains(patchedGroup))
{
mPatchedGroups.add(patchedGroup);
return true;
}
return false;
}
/**
* Patched talkgroups that are part of this patch group.
*/
public List<String> getPatchedGroups()
{
return mPatchedGroups;
}
/**
* Updates the timestamp for this patch group.
*/
public void updateTimestamp()
{
mLastUpdated = System.currentTimeMillis();
}
/**
* Indicates if this patch group is expired, meaning that it hasn't been updated in the past (expire
* threshold) seconds.
*/
public boolean isExpired()
{
return System.currentTimeMillis() - mLastUpdated > mExpireThreshold;
}
/**
* Sets the patch group expiration threshold (default = 5000 ms). A patch group is considered expired if
* no new updates have been received within the past (expire threshold) milliseconds
*
* @param expireThreshold in milliseconds
*/
public void setExpireThreshold(long expireThreshold)
{
mExpireThreshold = expireThreshold;
}
}

View File

@@ -0,0 +1,245 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package module.decode.p25;
import alias.Alias;
import alias.AliasList;
import alias.PatchGroupAlias;
import alias.id.talkgroup.TalkgroupID;
import module.decode.event.CallEvent;
import sample.Broadcaster;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class PatchGroupManager
{
private AliasList mAliasList;
private List<PatchGroup> mPatchGroups = new ArrayList<>();
private Broadcaster<CallEvent> mCallEventBroadcaster;
/**
* Manages active P25 patch groups and auto-expires patch groups if not updated every 5 seconds.
*
* @param aliasList to keep updated with patch group changes
* @param broadcaster for rebroadcasting patch group related call events
*/
public PatchGroupManager(AliasList aliasList, Broadcaster<CallEvent> broadcaster)
{
mAliasList = aliasList;
mCallEventBroadcaster = broadcaster;
}
/**
* Removes the patch group.
*
* @param patchGroupID of the patch group to remove
*/
public void removePatchGroup(String patchGroupID)
{
removePatchGroup(patchGroupID, false);
}
/**
* Removes the patch group.
*
* @param patchGroupID of the patch group to remove
* @param expired true if the patch group is being removed because it expired, false if we receive a
* message commanding commanding a patch group delete.
*/
private void removePatchGroup(String patchGroupID, boolean expired)
{
PatchGroup patchGroup = getPatchGroup(patchGroupID);
if(patchGroup != null)
{
//Cleanup the alias list
if(mAliasList != null)
{
Alias alias = mAliasList.getTalkgroupAlias(patchGroupID);
if(alias instanceof PatchGroupAlias)
{
PatchGroupAlias patchGroupAlias = (PatchGroupAlias)alias;
mAliasList.removeAlias(patchGroupAlias);
//Replace our temporary patch group alias with the original alias for the patch group ID
if(patchGroupAlias.hasPatchGroupAlias())
{
mAliasList.addAlias(patchGroupAlias.getPatchGroupAlias());
}
}
}
mPatchGroups.remove(patchGroup);
if(mCallEventBroadcaster != null)
{
StringBuilder sb = new StringBuilder();
sb.append((expired ? "EXPIRED " : "DELETED "));
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
sb.append(" ").append(patchGroup.getPatchedGroups());
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_DELETE)
.to(patchGroup.getPatchGroupID())
.details(sb.toString())
.build());
}
}
}
/**
* Adds/updates a patch group alias to the alias list containing aliases for each of the patched talkgroups. If the
* patch group alias already exists, any patched talkgroups will be removed from the existing patch group alias and
* replaced with the aliases corresponding to the patched talkgroup aliases.
*
* @param patchGroupID for the patch group
* @param patchedTalkgroups containing the talkgroup IDs for each of the patched talkgroups
*/
public void updatePatchGroup(String patchGroupID, List<String> patchedTalkgroups)
{
boolean updated = false;
PatchGroup patchGroup = getPatchGroup(patchGroupID);
if(patchGroup == null)
{
//Remove any expired patch groups before we add a new one
cleanupPatchGroups();
patchGroup = new PatchGroup(patchGroupID);
mPatchGroups.add(patchGroup);
updated = true;
}
else
{
patchGroup.updateTimestamp();
}
for(String patchedGroup : patchedTalkgroups)
{
if(patchGroup.addPatchedGroup(patchedGroup))
{
updated = true;
}
}
//If we make any changes to the patch group, update the patch group alias
if(updated)
{
PatchGroupAlias patchGroupAlias = null;
//Check for an existing alias for the patch talkgroup - do not include wildcard aliases
if(mAliasList != null)
{
Alias existingAlias = mAliasList.getTalkgroupAlias(patchGroupID, false);
if(existingAlias instanceof PatchGroupAlias)
{
patchGroupAlias = (PatchGroupAlias)existingAlias;
}
else
{
patchGroupAlias = new PatchGroupAlias();
patchGroupAlias.addAliasID(new TalkgroupID(patchGroupID));
if(existingAlias != null)
{
mAliasList.removeAlias(existingAlias);
patchGroupAlias.setPatchGroupAlias(existingAlias);
}
mAliasList.addAlias(patchGroupAlias);
}
if(patchGroupAlias != null)
{
patchGroupAlias.setPatchedTalkgroupIDs(patchGroup.getPatchedGroups());
patchGroupAlias.clearPatchedAliases();
for(String patchedTalkgroup : patchGroup.getPatchedGroups())
{
Alias patchedAlias = mAliasList.getTalkgroupAlias(patchedTalkgroup);
if(patchedAlias != null)
{
patchGroupAlias.addPatchedAlias(patchedAlias);
}
}
}
}
if(mCallEventBroadcaster != null)
{
StringBuilder sb = new StringBuilder();
sb.append("PATCH GROUP:").append(patchGroup.getPatchGroupID());
sb.append(" ").append(patchGroup.getPatchedGroups());
mCallEventBroadcaster.broadcast(new P25CallEvent.Builder(CallEvent.CallEventType.PATCH_GROUP_ADD)
.aliasList(mAliasList)
.details(sb.toString())
.build());
}
}
}
/**
* Returns the patch group matching the patch group ID or null
*/
private PatchGroup getPatchGroup(String patchGroupID)
{
for(PatchGroup patchGroup : mPatchGroups)
{
if(patchGroup.getPatchGroupID().equals(patchGroupID))
{
return patchGroup;
}
}
return null;
}
/**
* Checks the last updated timestamp on all patch groups and removes any patch groups that have not been
* updated in the past 5 seconds
*/
public void cleanupPatchGroups()
{
Iterator<PatchGroup> it = mPatchGroups.iterator();
while(it.hasNext())
{
PatchGroup patchGroup = it.next();
if(patchGroup.isExpired())
{
it.remove();
removePatchGroup(patchGroup.getPatchGroupID(), true);
}
}
}
}

View File

@@ -18,20 +18,28 @@
******************************************************************************/ ******************************************************************************/
package sample; package sample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.real.IOverflowListener;
import source.tuner.TunerChannelSource;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class OverflowableTransferQueue<E> public class OverflowableTransferQueue<E>
{ {
private final static Logger mLog = LoggerFactory.getLogger(OverflowableTransferQueue.class);
public enum State {NORMAL, OVERFLOW}; public enum State {NORMAL, OVERFLOW};
private Listener<State> mStateListener; private IOverflowListener mOverflowListener;
private LinkedTransferQueue<E> mQueue = new LinkedTransferQueue<E>(); private LinkedTransferQueue<E> mQueue = new LinkedTransferQueue<E>();
private AtomicInteger mCounter = new AtomicInteger(); private AtomicInteger mCounter = new AtomicInteger();
private AtomicBoolean mOverflow = new AtomicBoolean();
private int mMaximumSize; private int mMaximumSize;
private int mResetThreshold; private int mResetThreshold;
private boolean mOverflow = false;
/** /**
* Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for * Concurrent transfer queue that couples a higher-throughput linked transfer queue with an atomic integer for
@@ -53,7 +61,7 @@ public class OverflowableTransferQueue<E>
*/ */
public void offer(E e) public void offer(E e)
{ {
if(!mOverflow) if(!mOverflow.get())
{ {
mQueue.offer(e); mQueue.offer(e);
@@ -73,46 +81,36 @@ public class OverflowableTransferQueue<E>
{ {
int drainCount = mQueue.drainTo(collection, maxElements); int drainCount = mQueue.drainTo(collection, maxElements);
if(drainCount > 0) int size = mCounter.addAndGet(-drainCount);
{
int size = mCounter.addAndGet(-drainCount);
if(mOverflow && size <= mResetThreshold) if(mOverflow.get() && size <= mResetThreshold)
{ {
setOverflow(false); setOverflow(false);
}
} }
return drainCount; return drainCount;
} }
/** /**
* Sets a listener to receive state change events * Sets a listener to receive overflow state change events
*/ */
public void setStateListener(Listener<State> listener) public void setOverflowListener(IOverflowListener listener)
{ {
mStateListener = listener; mOverflowListener = listener;
} }
/** /**
* Current state of the queue, normal or overflow * Toggles the overflow state and broadcast state change to listener
*/
public State getState()
{
return mOverflow ? State.OVERFLOW : State.NORMAL;
}
/**
* Toggles the overflow state
*/ */
private void setOverflow(boolean overflow) private void setOverflow(boolean overflow)
{ {
mOverflow = overflow; if(mOverflow.compareAndSet(!overflow, overflow))
if(mStateListener != null)
{ {
mStateListener.receive(getState()); if(mOverflowListener != null)
{
mOverflowListener.sourceOverflow(overflow);
}
} }
} }
@@ -125,7 +123,11 @@ public class OverflowableTransferQueue<E>
{ {
mQueue.clear(); mQueue.clear();
mCounter.set(0); mCounter.set(0);
mOverflow = false; mOverflow.set(false);
if(mOverflowListener != null)
{
mOverflowListener.sourceOverflow(false);
}
} }
} }
} }

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package sample.real;
public interface IOverflowListener
{
/**
* Indicates that the source is in overflow state (true) or out of overflow state (false)
* @param overflow true to indicate an overflow state
*/
void sourceOverflow(boolean overflow);
}

View File

@@ -19,6 +19,7 @@ package source;
import module.Module; import module.Module;
import sample.SampleType; import sample.SampleType;
import sample.real.IOverflowListener;
/** /**
* Abstract class to define the minimum functionality of a sample data provider. * Abstract class to define the minimum functionality of a sample data provider.
@@ -26,6 +27,7 @@ import sample.SampleType;
public abstract class Source extends Module public abstract class Source extends Module
{ {
protected SampleType mSampleType; protected SampleType mSampleType;
protected IOverflowListener mOverflowListener;
public Source( SampleType sampleType ) public Source( SampleType sampleType )
{ {
@@ -47,4 +49,25 @@ public abstract class Source extends Module
{ {
mSampleType = sampleType; mSampleType = sampleType;
} }
/**
* Registers the listener to receive overflow state changes. Use null argument to clear the listener
*/
public void setOverflowListener(IOverflowListener listener)
{
mOverflowListener = listener;
}
/**
* Broadcasts an overflow state
*
* @param overflow true if overlow, false if normal
*/
protected void broadcastOverflowState(boolean overflow)
{
if(mOverflowListener != null)
{
mOverflowListener.sourceOverflow(overflow);
}
}
} }

View File

@@ -29,6 +29,7 @@ import sample.Listener;
import sample.OverflowableTransferQueue; import sample.OverflowableTransferQueue;
import sample.complex.Complex; import sample.complex.Complex;
import sample.complex.ComplexBuffer; import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.ComplexSource; import source.ComplexSource;
import source.SourceException; import source.SourceException;
import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent;
@@ -51,7 +52,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
private static final int BUFFER_MAX_CAPACITY = 300; private static final int BUFFER_MAX_CAPACITY = 300;
//Threshold for resetting buffer overflow condition //Threshold for resetting buffer overflow condition
private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 200; private static final int BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
private static int CHANNEL_RATE = 48000; private static int CHANNEL_RATE = 48000;
private static int CHANNEL_PASS_FREQUENCY = 12000; private static int CHANNEL_PASS_FREQUENCY = 12000;
@@ -101,21 +102,6 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
mTunerFrequency = mTuner.getTunerController().getFrequency(); mTunerFrequency = mTuner.getTunerController().getFrequency();
mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD); mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
mBuffer.setStateListener(new Listener<OverflowableTransferQueue.State>()
{
@Override
public void receive(OverflowableTransferQueue.State state)
{
if(state == OverflowableTransferQueue.State.NORMAL)
{
mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow - temporary pause until processing catches up");
}
else
{
mLog.debug("Channel [" + mTunerFrequency + "] buffer overflow cleared - resuming normal processing");
}
}
});
/* Setup the frequency translator to the current source frequency */ /* Setup the frequency translator to the current source frequency */
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
@@ -126,6 +112,14 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate())); frequencyChanged(new FrequencyChangeEvent(Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getTunerController().getSampleRate()));
} }
/**
* Overrides the default source overflow listener management to delegate responsibility to the overflow buffer
*/
@Override
public void setOverflowListener(IOverflowListener listener)
{
mBuffer.setOverflowListener(listener);
}
public void start(ScheduledExecutorService executor) public void start(ScheduledExecutorService executor)
{ {
@@ -175,7 +169,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
} }
else else
{ {
mLog.warn("Attempt to stop() and already stopped tuner channel source was ignored"); mLog.warn("Attempt to stop() an already stopped tuner channel source was ignored");
} }
} }
@@ -379,6 +373,10 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang
mLog.error("Error encountered during decimation process", e); mLog.error("Error encountered during decimation process", e);
} }
} }
catch(Throwable throwable)
{
mLog.error("Code error encountered during decimation process - channel thread will probably die", throwable);
}
/* Check to see if we've been shutdown */ /* Check to see if we've been shutdown */
if(!mProcessing) if(!mProcessing)

View File

@@ -30,6 +30,7 @@ import sample.Listener;
import sample.OverflowableTransferQueue; import sample.OverflowableTransferQueue;
import sample.adapter.ISampleAdapter; import sample.adapter.ISampleAdapter;
import sample.complex.ComplexBuffer; import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.tuner.TunerManager; import source.tuner.TunerManager;
import util.ThreadPool; import util.ThreadPool;
@@ -56,7 +57,7 @@ public class USBTransferProcessor implements TransferCallback
private static final int FILLED_BUFFER_MAX_CAPACITY = 300; private static final int FILLED_BUFFER_MAX_CAPACITY = 300;
//Threshold for resetting buffer overflow condition //Threshold for resetting buffer overflow condition
private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 200; private static final int FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD = 100;
private String mDeviceName; private String mDeviceName;
@@ -101,12 +102,12 @@ public class USBTransferProcessor implements TransferCallback
mBufferSize = bufferSize; mBufferSize = bufferSize;
mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD); mFilledBuffers = new OverflowableTransferQueue<>(FILLED_BUFFER_MAX_CAPACITY, FILLED_BUFFER_OVERFLOW_RESET_THRESHOLD);
mFilledBuffers.setStateListener(new Listener<OverflowableTransferQueue.State>() mFilledBuffers.setOverflowListener(new IOverflowListener()
{ {
@Override @Override
public void receive(OverflowableTransferQueue.State state) public void sourceOverflow(boolean overflow)
{ {
if(state == OverflowableTransferQueue.State.NORMAL) if(overflow)
{ {
mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up"); mLog.debug(mDeviceName + " - buffer overflow - temporary pause until processing catches up");
} }