StartStopRulesDefaultPluginpublic class StartStopRulesDefaultPlugin extends Object implements org.gudy.azureus2.core3.config.COConfigurationListener, AEDiagnosticsEvidenceGenerator, PluginHandles Starting and Stopping of torrents.
TODO: RANK_TIMED is quite a hack and is spread all over. It needs to be
redone, probably with a timer on each seeding torrent which triggers
when time is up and it needs to stop.
BUG: When "AutoStart 0 Peers" is on, and minSpeedForActivelySeeding is
enabled, the 0 peer torrents will continuously switch from seeding to
queued, probably due to the connection attempt registering speed.
This might be fixed by the "wait XX ms before switching active state"
code.
Other Notes:
"CD" is often used to refer to "Seed" or "Seeding", because "C" sounds like
"See" |
Fields Summary |
---|
private static final String | sStates | public static final int | RANK_NONEDo not rank completed torrents | public static final int | RANK_SPRATIORank completed torrents using Seeds:Peer Ratio | public static final int | RANK_SEEDCOUNTRank completed torrents using Seed Count method | public static final int | RANK_TIMEDRank completed torrents using a timed rotation of minTimeAlive | private static final int | FORCE_CHECK_PERIODForce at least one check every period of time (in ms).
Used in ChangeFlagCheckerTask | private static final int | CHECK_FOR_GROSS_CHANGE_PERIODCheck for non triggerable changes ever period of time (in ms) | private static final int | PROCESS_CHECK_PERIODInterval in ms between checks to see if the {@link #somethingChanged}
flag changed | private static final int | MIN_SEEDING_STARTUP_WAITWait xx ms before starting completed torrents (so scrapes can come in) | private static final int | MIN_FIRST_SCRAPE_WAITWait at least xx ms for first scrape, before starting completed torrents | private AEMonitor | this_mon | private PluginInterface | pi | protected PluginConfig | plugin_config | private DownloadManager | download_manager | protected org.gudy.azureus2.plugins.logging.LoggerChannel | log | private RecalcSeedingRanksTask | recalcSeedingRanksTaskUsed only for RANK_TIMED. Recalculate ranks on a timer | private static Map | downloadDataMapMap to relate downloadData to a Download | private volatile DefaultRankCalculator[] | sortedArrayCachethis is used to reduce the number of comperator invocations
by keeping a mostly sorted copy around, must be nulled whenever the map is changed | private volatile boolean | closingDown | private volatile boolean | somethingChanged | private Set | ranksToRecalc | private long | startedOnWhen rules class started. Used for initial waiting logic | protected boolean | bDebugLogWhether Debug Info is written to the log and tooltip | private int | iRankTypeRanking System to use. One of RANK_* constants | private int | minSpeedForActiveSeeding | private int | numPeersAsFullCopy | private int | iFakeFullCopySeedStart | private int | _maxActive | private boolean | _maxActiveWhenSeedingEnabled | private int | _maxActiveWhenSeeding | private int | maxDownloads | private int | minDownloads | private boolean | bAutoReposition | private long | minTimeAlive | private boolean | bAutoStart0Peers | private int | iMaxUploadSpeed | private static boolean | bAlreadyInitialized | private TableColumn | seedingRankColumn | private TableContextMenuItem | debugMenuItem | private boolean | bSWTUI | private com.aelitis.azureus.core.util.CopyOnWriteList | listenersFP | private long | changeCheckCount | private long | changeCheckTotalMS | private long | changeCheckMaxMS | private long | processCount | private long | processTotalMS | private long | processMaxMS | private long | processLastComplete | private long | processTotalGap | private long | processTotalRecalcs | private long | processTotalZeroRecalcs | private long | processMergeCountRequest that the startstop rules process. Used when it's known that
something has changed that will effect torrent's state/position/rank. |
Methods Summary |
---|
public void | addListener(StartStopRulesFPListener listener)
listenersFP.add(listener);
| private java.lang.String | boolDebug(boolean b)
return b ? "Y" : "N";
| private int | calcMaxSeeders(int iDLs)
// XXX put in subtraction logic here
int maxActive = getMaxActive();
if (maxActive == 0) {
return 999999;
}
return maxActive - iDLs;
| public int | calcPeersNoUs(Download download)Get # of peers not including us
int numPeers = 0;
DownloadScrapeResult sr = download.getLastScrapeResult();
if (sr.getScrapeStartTime() > 0) {
numPeers = sr.getNonSeedCount();
// If we've scraped after we started downloading
// Remove ourselves from count
if ((numPeers > 0) && (download.getState() == Download.ST_DOWNLOADING)
&& (sr.getScrapeStartTime() > download.getStats().getTimeStarted()))
numPeers--;
}
if (numPeers == 0) {
// Fallback to the # of peers we know of
DownloadAnnounceResult ar = download.getLastAnnounceResult();
if (ar != null
&& ar.getResponseType() == DownloadAnnounceResult.RT_SUCCESS)
numPeers = ar.getNonSeedCount();
if (numPeers == 0) {
DownloadActivationEvent activationState = download.getActivationState();
if (activationState != null) {
numPeers = activationState.getActivationCount();
}
}
}
return numPeers;
| public int | calcSeedsNoUs(Download download)Get # of seeds, not including us, AND including fake full copies
return calcSeedsNoUs(download, calcPeersNoUs(download));
| public int | calcSeedsNoUs(Download download, int numPeers)Get # of seeds, not including us, AND including fake full copies
int numSeeds = 0;
DownloadScrapeResult sr = download.getLastScrapeResult();
if (sr.getScrapeStartTime() > 0) {
long seedingStartedOn = download.getStats().getTimeStartedSeeding();
numSeeds = sr.getSeedCount();
// If we've scraped after we started seeding
// Remove ourselves from count
if ((numSeeds > 0) && (seedingStartedOn > 0)
&& (download.getState() == Download.ST_SEEDING)
&& (sr.getScrapeStartTime() > seedingStartedOn))
numSeeds--;
}
if (numSeeds == 0) {
// Fallback to the # of seeds we know of
DownloadAnnounceResult ar = download.getLastAnnounceResult();
if (ar != null
&& ar.getResponseType() == DownloadAnnounceResult.RT_SUCCESS)
numSeeds = ar.getSeedCount();
}
if (numPeersAsFullCopy != 0 && numSeeds >= iFakeFullCopySeedStart)
numSeeds += numPeers / numPeersAsFullCopy;
return numSeeds;
| public void | configurationSaved()
reloadConfigParams();
| public void | generate(IndentWriter writer)
writer.println("StartStopRules Manager");
try {
writer.indent();
writer.println("Started " + (SystemTime.getCurrentTime() - startedOn)
+ "ms ago");
writer.println("downloadDataMap size = " + downloadDataMap.size());
if (changeCheckCount > 0) {
writer.println("changeCheck CPU ms: avg="
+ (changeCheckTotalMS / changeCheckCount) + "; max = "
+ changeCheckMaxMS);
}
if (processCount > 0) {
writer.println("# process cycles: " + processCount);
writer.println("process CPU ms: avg=" + (processTotalMS / processCount)
+ "; max = " + processMaxMS);
if (processCount > 1) {
writer.println("process avg gap: "
+ (processTotalGap / ((processCount - 1))) + "ms");
}
writer.println("Avg # recalcs per process cycle: "
+ (processTotalRecalcs / processCount));
if (processTotalZeroRecalcs > 0) {
writer.println("# process cycle with 0 recalcs: "
+ processTotalZeroRecalcs);
}
}
} catch (Exception e) {
// ignore
} finally {
writer.exdent();
}
| public java.util.List | getFPListeners()
return listenersFP.getList();
| protected int | getMaxActive()
if (!_maxActiveWhenSeedingEnabled)
return (_maxActive);
if (download_manager.isSeedingOnly()) {
if (_maxActiveWhenSeeding <= _maxActive)
return (_maxActiveWhenSeeding);
// danger here if we are in a position where allowing more to start when seeding
// allows a non-seeding download to start (looping occurs)
Download[] downloads = download_manager.getDownloads();
boolean danger = false;
for (int i = 0; i < downloads.length && !danger; i++) {
Download download = downloads[i];
int state = download.getState();
if (state == Download.ST_DOWNLOADING || state == Download.ST_SEEDING
|| state == Download.ST_STOPPED || state == Download.ST_STOPPING
|| state == Download.ST_ERROR) {
// not interesting, they can't potentially cause trouble
} else {
// look for incomplete files
DiskManagerFileInfo[] files = download.getDiskManagerFileInfo();
for (int j = 0; j < files.length; j++) {
DiskManagerFileInfo file = files[j];
if ((!file.isSkipped()) && file.getDownloaded() != file.getLength()) {
danger = true;
break;
}
}
}
}
if (!danger)
return (_maxActiveWhenSeeding);
}
return (_maxActive);
| public static DefaultRankCalculator | getRankCalculator(Download dl)
return (DefaultRankCalculator) downloadDataMap.get(dl);
| private void | handleCompletedDownload(DefaultRankCalculator[] dlDataArray, DefaultRankCalculator dlData, com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin$ProcessVars vars, com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin$TotalsStats totals)Process Completed (Seeding) downloads, starting and stopping as needed
if (!totals.bOkToStartSeeding)
return;
Download download = dlData.dl;
int state = download.getState();
String[] debugEntries = null;
String sDebugLine = "";
// Queuing process:
// 1) Torrent is Queued (Stopped)
// 2) Slot becomes available
// 3) Queued Torrent changes to Waiting
// 4) Waiting Torrent changes to Ready
// 5) Ready torrent changes to Seeding (with startDownload)
// 6) Trigger stops Seeding torrent
// a) Queue Ranking drops
// b) User pressed stop
// c) other
// 7) Seeding Torrent changes to Queued. Go to step 1.
int numPeers = dlData.lastModifiedScrapeResultPeers;
boolean isFP = false;
if (bDebugLog) {
isFP = dlData.isFirstPriority();
debugEntries = new String[] {
"CD state=" + sStates.charAt(state),
"shareR=" + download.getStats().getShareRatio(),
"nWorCDing=" + vars.numWaitingOrSeeding,
"nWorDLing=" + vars.numWaitingOrDLing,
"sr=" + download.getSeedingRank(),
"hgherQd=" + boolDebug(vars.higherCDtoStart),
"maxCDrs=" + totals.maxSeeders,
"FP=" + boolDebug(isFP),
"nActCDing=" + totals.activelyCDing,
"ActCDing=" + boolDebug(dlData.getActivelySeeding())
};
}
try {
boolean bScrapeOk = dlData.lastScrapeResultOk;
// Ignore rules and other auto-starting rules do not apply when
// bAutoStart0Peers and peers == 0. So, handle starting 0 peers
// right at the beginning, and loop early
if (bAutoStart0Peers && numPeers == 0 && bScrapeOk) {
if (state == Download.ST_QUEUED) {
try {
if (bDebugLog)
sDebugLine += "\nrestart() 0Peers";
download.restart(); // set to Waiting
totals.waitingToSeed++;
vars.numWaitingOrSeeding++;
state = download.getState();
if (state == Download.ST_READY) {
if (bDebugLog)
sDebugLine += "\nstart(); 0Peers";
download.start();
totals.activelyCDing++;
}
} catch (Exception ignore) {/*ignore*/
}
}
if (state == Download.ST_READY) {
try {
if (bDebugLog)
sDebugLine += "\nstart(); 0Peers";
download.start();
totals.activelyCDing++;
vars.numWaitingOrSeeding++;
} catch (Exception ignore) {/*ignore*/
}
}
return;
}
if (bDebugLog && bAutoStart0Peers && numPeers == 0 && !bScrapeOk
&& (state == Download.ST_QUEUED || state == Download.ST_READY)) {
sDebugLine += "\n NOT starting 0 Peer torrent because scrape isn't ok";
}
if (!bDebugLog) {
// In debug mode, we already calculated FP
isFP = dlData.isFirstPriority();
}
boolean bActivelySeeding = dlData.getActivelySeeding();
boolean okToQueue = (state == Download.ST_READY || state == Download.ST_SEEDING)
&& (!isFP || (isFP && ((totals.maxActive != 0 && vars.numWaitingOrSeeding >= totals.maxActive
- minDownloads))))
//&& (!isFP || (isFP && ((vars.numWaitingOrSeeding >= totals.maxSeeders) || (!bActivelySeeding && (vars.numWaitingOrSeeding + totals.totalStalledSeeders) >= totals.maxSeeders))) )
&& (!download.isForceStart());
int rank = download.getSeedingRank();
// in RANK_TIMED mode, we use minTimeAlive for rotation time, so
// skip check
// XXX do we want changes to take effect immediately ?
if (okToQueue && (state == Download.ST_SEEDING)
&& iRankType != RANK_TIMED) {
long timeAlive = (SystemTime.getCurrentTime() - download.getStats().getTimeStarted());
okToQueue = (timeAlive >= minTimeAlive);
if (!okToQueue && bDebugLog)
sDebugLine += "\n Torrent can't be stopped yet, timeAlive("
+ timeAlive + ") < minTimeAlive(" + minTimeAlive + ")";
}
if (state != Download.ST_QUEUED && // Short circuit.
(state == Download.ST_READY || state == Download.ST_WAITING
|| state == Download.ST_PREPARING ||
// Forced Start torrents are pre-included in count
(state == Download.ST_SEEDING && bActivelySeeding && !download.isForceStart()))) {
vars.numWaitingOrSeeding++;
if (bDebugLog)
sDebugLine += "\n Torrent is waiting or seeding";
}
// Note: First Priority are sorted to the top,
// so they will always start first
// XXX Change to waiting if queued and we have an open slot
if (!okToQueue
&& (state == Download.ST_QUEUED)
&& (totals.maxActive == 0 || vars.numWaitingOrSeeding < totals.maxSeeders)
//&& (totals.maxActive == 0 || (activeSeedingCount + activeDLCount) < totals.maxActive) &&
&& (rank >= DefaultRankCalculator.SR_IGNORED_LESS_THAN)
&& !vars.higherCDtoStart) {
try {
if (bDebugLog)
sDebugLine += "\n restart: ok2Q=" + okToQueue
+ "; QUEUED && numWaitingOrSeeding( "
+ vars.numWaitingOrSeeding + ") < maxSeeders ("
+ totals.maxSeeders + ")";
download.restart(); // set to Waiting
okToQueue = false;
totals.waitingToSeed++;
vars.numWaitingOrSeeding++;
if (iRankType == RANK_TIMED)
dlData.recalcSeedingRank();
} catch (Exception ignore) {/*ignore*/
}
state = download.getState();
} else if (bDebugLog && state == Download.ST_QUEUED) {
sDebugLine += "\n NOT restarting:";
if (rank < DefaultRankCalculator.SR_IGNORED_LESS_THAN)
sDebugLine += " torrent is being ignored";
else if (vars.higherCDtoStart)
sDebugLine += " a torrent with a higher rank is queued or starting";
else {
if (okToQueue)
sDebugLine += " no starting of okToQueue'd;";
if (vars.numWaitingOrSeeding >= totals.maxSeeders)
sDebugLine += " at limit, numWaitingOrSeeding("
+ vars.numWaitingOrSeeding + ") >= maxSeeders("
+ totals.maxSeeders + ")";
}
}
boolean bForceStop = false;
// Start download if ready and slot is available
if (state == Download.ST_READY
&& totals.activelyCDing < totals.maxSeeders) {
if (rank >= DefaultRankCalculator.SR_IGNORED_LESS_THAN
|| download.isForceStart()) {
try {
if (bDebugLog)
sDebugLine += "\n start: READY && total activelyCDing("
+ totals.activelyCDing + ") < maxSeeders("
+ totals.maxSeeders + ")";
download.start();
okToQueue = false;
} catch (Exception ignore) {
/*ignore*/
}
state = download.getState();
totals.activelyCDing++;
bActivelySeeding = true;
vars.numWaitingOrSeeding++;
} else if (okToQueue) {
// In between switching from STATE_WAITING and STATE_READY,
// and ignore rule was met, so move it back to Queued
bForceStop = true;
}
}
// if there's more torrents waiting/seeding than our max, or if
// there's a higher ranked torrent queued, stop this one
if (okToQueue || bForceStop) {
boolean okToStop = bForceStop;
if (!okToStop) {
// break up the logic into variables to make more readable
boolean bOverLimit = vars.numWaitingOrSeeding > totals.maxSeeders
|| (vars.numWaitingOrSeeding >= totals.maxSeeders && vars.higherCDtoStart);
boolean bSeeding = state == Download.ST_SEEDING;
// not checking AND (at limit of seeders OR rank is set to ignore) AND
// (Actively Seeding OR StartingUp OR Seeding a non-active download)
okToStop = !download.isChecking()
&& (bOverLimit || rank < DefaultRankCalculator.SR_IGNORED_LESS_THAN)
&& (bActivelySeeding || !bSeeding || (!bActivelySeeding && bSeeding));
if (bDebugLog) {
if (okToStop) {
sDebugLine += "\n stopAndQueue: ";
if (bOverLimit) {
if (vars.higherCDtoStart)
sDebugLine += "higherQueued (it should be seeding instead of this one)";
else
sDebugLine += "over limit";
} else if (rank < DefaultRankCalculator.SR_IGNORED_LESS_THAN)
sDebugLine += "ignoreRule met";
sDebugLine += " && ";
if (bActivelySeeding)
sDebugLine += "activelySeeding";
else if (!bSeeding)
sDebugLine += "not SEEDING";
else if (!bActivelySeeding && bSeeding)
sDebugLine += "SEEDING, but not actively";
}
} else {
sDebugLine += "\n NOT queuing: ";
if (download.isChecking())
sDebugLine += "can't auto-queue a checking torrent";
else if (!bOverLimit)
sDebugLine += "not over limit. numWaitingOrSeeding("
+ vars.numWaitingOrSeeding + ") <= maxSeeders("
+ totals.maxSeeders + ")";
else
sDebugLine += "bActivelySeeding=" + bActivelySeeding
+ ";bSeeding" + bSeeding;
}
} else {
if (bDebugLog)
sDebugLine += "\n Forcing a stop..";
}
if (okToStop) {
try {
if (state == Download.ST_READY)
totals.waitingToSeed--;
download.stopAndQueue();
vars.bStopAndQueued = true;
// okToQueue only allows READY and SEEDING state.. and in both cases
// we have to reduce counts
if (bActivelySeeding) {
totals.activelyCDing--;
bActivelySeeding = false;
vars.numWaitingOrSeeding--;
}
// force stop allows READY states in here, so adjust counts
if (state == Download.ST_READY)
totals.waitingToSeed--;
} catch (Exception ignore) {
/*ignore*/
}
state = download.getState();
}
}
// move completed timed rank types to bottom of the list
if (vars.bStopAndQueued && iRankType == RANK_TIMED) {
for (int j = 0; j < dlDataArray.length; j++) {
Download dl = dlDataArray[j].getDownloadObject();
int sr = dl.getSeedingRank();
if (sr > 0 && sr < DefaultRankCalculator.SR_TIMED_QUEUED_ENDS_AT) {
// Move everyone up
// We always start by setting SR to SR_TIMED_QUEUED_ENDS_AT - position
// then, the torrent with the biggest starts seeding which is
// (SR_TIMED_QUEUED_ENDS_AT - 1), leaving a gap.
// when it's time to stop the torrent, move everyone up, and put
// us at the end
dl.setSeedingRank(sr + 1);
}
}
rank = DefaultRankCalculator.SR_TIMED_QUEUED_ENDS_AT - totals.complete;
download.setSeedingRank(rank);
}
state = download.getState();
if (rank >= 0
&& (state == Download.ST_QUEUED || state == Download.ST_READY
|| state == Download.ST_WAITING || state == Download.ST_PREPARING)) {
vars.higherCDtoStart = true;
}
} finally {
if (bDebugLog) {
String[] debugEntries2 = new String[] {
"CD state=" + sStates.charAt(download.getState()),
"shareR=" + download.getStats().getShareRatio(),
"nWorCDing=" + vars.numWaitingOrSeeding,
"nWorDLing=" + vars.numWaitingOrDLing,
"sr=" + download.getSeedingRank(),
"hgherQd=" + boolDebug(vars.higherCDtoStart),
"maxCDrs=" + totals.maxSeeders,
"FP=" + boolDebug(isFP),
"nActCDing=" + totals.activelyCDing,
"ActCDing=" + boolDebug(dlData.getActivelySeeding())
};
printDebugChanges("", debugEntries, debugEntries2, sDebugLine, " ",
true, dlData);
}
}
| private void | handleInCompleteDownload(DefaultRankCalculator dlData, com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin$ProcessVars vars, com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin$TotalsStats totals)
Download download = dlData.dl;
int state = download.getState();
if (download.isForceStart()) {
if (bDebugLog) {
String s = "isForceStart.. rules skipped";
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
return;
}
// Don't mess with preparing torrents. they could be in the
// middle of resume-data building, or file allocating.
if (state == Download.ST_PREPARING) {
vars.numWaitingOrDLing++;
if (bDebugLog) {
String s = "ST_PREPARING.. rules skipped. numW8tngorDLing="
+ vars.numWaitingOrDLing;
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
return;
}
int maxDLs = 0;
if (totals.maxActive == 0) {
maxDLs = maxDownloads;
} else {
int DLmax = 0;
DLmax = totals.stalledFPSeeders + totals.forcedActive + totals.maxActive
- totals.firstPriority - totals.forcedSeedingNonFP;
maxDLs = (DLmax <= 0) ? 0 : maxDownloads - DLmax <= 0 ? maxDownloads
: DLmax;
}
if (maxDLs < minDownloads) {
maxDLs = minDownloads;
}
if (bDebugLog) {
String s = ">> DL state=" + sStates.charAt(download.getState())
+ ";shareRatio=" + download.getStats().getShareRatio()
+ ";numW8tngorDLing=" + vars.numWaitingOrDLing + ";maxCDrs="
+ totals.maxSeeders + ";forced=" + boolDebug(download.isForceStart())
+ ";actvDLs=" + totals.activelyDLing + ";maxDLs=" + maxDLs
+ ";ActDLing=" + boolDebug(dlData.getActivelyDownloading())
+ ";hgherQd=" + boolDebug(vars.higherDLtoStart) + ";isCmplt="
+ boolDebug(download.isComplete());
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
// must use fresh getActivelyDownloading() in case state changed to
// downloading
if ((state == Download.ST_DOWNLOADING && dlData.getActivelyDownloading())
|| state == Download.ST_READY || state == Download.ST_WAITING
|| state == Download.ST_PREPARING) {
vars.numWaitingOrDLing++;
}
if (state == Download.ST_READY || state == Download.ST_DOWNLOADING
|| state == Download.ST_WAITING) {
boolean bActivelyDownloading = dlData.getActivelyDownloading();
// Stop torrent if over limit
boolean bOverLimit = vars.numWaitingOrDLing > maxDLs
|| (vars.numWaitingOrDLing >= maxDLs && vars.higherDLtoStart);
boolean bDownloading = state == Download.ST_DOWNLOADING;
if ((maxDownloads != 0)
&& bOverLimit
&& (bActivelyDownloading || !bDownloading || (bDownloading
&& totals.maxActive != 0 && !bActivelyDownloading && totals.activelyCDing
+ totals.activelyDLing >= totals.maxActive))) {
try {
if (bDebugLog) {
String s = " stopAndQueue: " + vars.numWaitingOrDLing
+ " waiting or downloading, when limit is " + maxDLs + "("
+ maxDownloads + ")";
if (vars.higherDLtoStart) {
s += " and higher DL is starting";
}
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
download.stopAndQueue();
// reduce counts
vars.numWaitingOrDLing--;
if (state == Download.ST_DOWNLOADING) {
totals.downloading--;
if (bActivelyDownloading)
totals.activelyDLing--;
} else {
totals.waitingToDL--;
}
totals.maxSeeders = calcMaxSeeders(totals.activelyDLing
+ totals.waitingToDL);
} catch (Exception ignore) {
/*ignore*/
}
state = download.getState();
} else if (bDebugLog) {
String s = "NOT queuing: ";
if (maxDownloads == 0) {
s += "maxDownloads = " + maxDownloads;
} else if (!bOverLimit) {
s += "not over limit. numWaitingOrDLing(" + vars.numWaitingOrDLing
+ ") <= maxDLs(" + maxDLs + ")";
} else if (!bActivelyDownloading || bDownloading) {
s += "not actively downloading";
} else if (totals.maxActive == 0) {
s += "unlimited active allowed (set)";
} else {
s += "# active(" + (totals.activelyCDing + totals.activelyDLing)
+ ") < maxActive(" + totals.maxActive + ")";
}
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
}
if (state == Download.ST_READY) {
if ((maxDownloads == 0) || (totals.activelyDLing < maxDLs)) {
try {
if (bDebugLog) {
String s = " start: READY && activelyDLing ("
+ totals.activelyDLing + ") < maxDLs (" + maxDownloads + ")";
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
download.start();
// adjust counts
totals.waitingToDL--;
totals.activelyDLing++;
totals.maxSeeders = calcMaxSeeders(totals.activelyDLing
+ totals.waitingToDL);
} catch (Exception ignore) {
/*ignore*/
}
state = download.getState();
}
}
if (state == Download.ST_QUEUED) {
if ((maxDownloads == 0) || (vars.numWaitingOrDLing < maxDLs)) {
try {
if (bDebugLog) {
String s = " restart: QUEUED && numWaitingOrDLing ("
+ vars.numWaitingOrDLing + ") < maxDLS (" + maxDLs + ")";
log.log(LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
download.restart();
// increase counts
vars.numWaitingOrDLing++;
totals.waitingToDL++;
totals.maxSeeders = calcMaxSeeders(totals.activelyDLing
+ totals.waitingToDL);
} catch (Exception ignore) {/*ignore*/
}
state = download.getState();
}
}
state = download.getState();
if (download.getSeedingRank() >= 0
&& (state == Download.ST_QUEUED || state == Download.ST_READY
|| state == Download.ST_WAITING || state == Download.ST_PREPARING)) {
vars.higherDLtoStart = true;
}
if (bDebugLog) {
String s = "<< DL state=" + sStates.charAt(download.getState())
+ ";shareRatio=" + download.getStats().getShareRatio()
+ ";numW8tngorDLing=" + vars.numWaitingOrDLing + ";maxCDrs="
+ totals.maxSeeders + ";forced=" + boolDebug(download.isForceStart())
+ ";actvDLs=" + totals.activelyDLing + ";hgherQd="
+ boolDebug(vars.higherDLtoStart) + ";ActDLing="
+ boolDebug(dlData.getActivelyDownloading());
log.log(download.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
| public void | initialize(PluginInterface _plugin_interface)
if (bAlreadyInitialized) {
System.err.println("StartStopRulesDefaultPlugin Already initialized!!");
} else {
bAlreadyInitialized = true;
}
AEDiagnostics.addEvidenceGenerator(this);
startedOn = SystemTime.getCurrentTime();
pi = _plugin_interface;
download_manager = pi.getDownloadManager();
pi.getPluginProperties().setProperty("plugin.version", "1.0");
pi.getPluginProperties().setProperty("plugin.name", "Start/Stop Rules");
pi.getPluginconfig().setPluginConfigKeyPrefix("");
// Create a configModel for StartStopRules
// We always need to do this in order to set up configuration defaults
UIManager manager = pi.getUIManager();
// TODO: don't name it Q
final BasicPluginConfigModel configModel = manager.createBasicPluginConfigModel(
ConfigSection.SECTION_ROOT, "Q");
setupConfigModel(configModel);
pi.addListener(new PluginListener() {
public void initializationComplete() {
// CPU Intensive, delay until a little after all plugin initializations
// XXX Would be better if we could delay it until UI is done,
// but there may be no UI..
new DelayedEvent("StartStop:initComp", 12000, new AERunnable() {
public void runSupport() {
download_manager.addListener(new StartStopDMListener());
SimpleTimer.addPeriodicEvent("StartStop:gross",
CHECK_FOR_GROSS_CHANGE_PERIOD, new ChangeCheckerTimerTask());
SimpleTimer.addPeriodicEvent("StartStop:check",
PROCESS_CHECK_PERIOD, new ChangeFlagCheckerTask());
}
});
}
public void closedownInitiated() {
closingDown = true;
// we don't want to go off recalculating stuff when config is saved
// on closedown
COConfigurationManager.removeListener(StartStopRulesDefaultPlugin.this);
}
public void closedownComplete() { /* not implemented */
}
});
log = pi.getLogger().getChannel("StartStopRules");
log.log(LoggerChannel.LT_INFORMATION,
"Default StartStopRules Plugin Initialisation");
COConfigurationManager.addListener(this);
plugin_config = pi.getPluginconfig();
try {
pi.getUIManager().addUIListener(new UIManagerListener() {
public void UIAttached(UIInstance instance) {
TableManager tm = pi.getUIManager().getTableManager();
seedingRankColumn = tm.createColumn(
TableManager.TABLE_MYTORRENTS_COMPLETE, "SeedingRank");
seedingRankColumn.initialize(TableColumn.ALIGN_TRAIL,
TableColumn.POSITION_LAST, 80, TableColumn.INTERVAL_LIVE);
SeedingRankColumnListener columnListener = new SeedingRankColumnListener(
downloadDataMap, plugin_config);
seedingRankColumn.addCellRefreshListener(columnListener);
tm.addColumn(seedingRankColumn);
if (instance instanceof UISWTInstance) {
bSWTUI = true;
// We have our own config model :)
configModel.destroy();
new StartStopRulesDefaultPluginSWTUI(pi);
}
}
public void UIDetached(UIInstance instance) {
}
});
} catch (Throwable e) {
Debug.printStackTrace(e);
}
reloadConfigParams();
| private void | printDebugChanges(java.lang.String sPrefixFirstLine, java.lang.String[] oldEntries, java.lang.String[] newEntries, java.lang.String sDebugLine, java.lang.String sPrefix, boolean bAlwaysPrintNoChangeLine, DefaultRankCalculator dlData)
boolean bAnyChanged = false;
String sDebugLineNoChange = sPrefixFirstLine;
String sDebugLineOld = "";
String sDebugLineNew = "";
for (int j = 0; j < oldEntries.length; j++) {
if (oldEntries[j].equals(newEntries[j]))
sDebugLineNoChange += oldEntries[j] + ";";
else {
sDebugLineOld += oldEntries[j] + ";";
sDebugLineNew += newEntries[j] + ";";
bAnyChanged = true;
}
}
String sDebugLineOut = ((bAlwaysPrintNoChangeLine || bAnyChanged)
? sDebugLineNoChange : "")
+ (bAnyChanged ? "\nOld:" + sDebugLineOld + "\nNew:" + sDebugLineNew
: "") + sDebugLine;
if (!sDebugLineOut.equals("")) {
String[] lines = sDebugLineOut.split("\n");
for (int i = 0; i < lines.length; i++) {
String s = sPrefix + ((i > 0) ? " " : "") + lines[i];
if (dlData == null) {
log.log(LoggerChannel.LT_INFORMATION, s);
} else {
log.log(dlData.dl.getTorrent(), LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
}
}
}
| protected void | process()
long now = 0;
try {
this_mon.enter();
now = SystemTime.getCurrentTime();
somethingChanged = false;
Object[] recalcArray = ranksToRecalc.toArray();
ranksToRecalc.clear();
for (int i = 0; i < recalcArray.length; i++) {
DefaultRankCalculator rankObj = (DefaultRankCalculator) recalcArray[i];
if (bDebugLog) {
long oldSR = rankObj.dl.getSeedingRank();
rankObj.recalcSeedingRank();
String s = "recalc seeding rank. old/new=" + oldSR + "/"
+ rankObj.dl.getSeedingRank();
log.log(rankObj.dl.getTorrent(), LoggerChannel.LT_INFORMATION, s);
} else {
rankObj.recalcSeedingRank();
}
}
processTotalRecalcs += recalcArray.length;
if (recalcArray.length == 0) {
processTotalZeroRecalcs++;
}
// pull the data into a local array, so we don't have to lock/synchronize
DefaultRankCalculator[] dlDataArray;
if(sortedArrayCache != null && sortedArrayCache.length == downloadDataMap.size())
dlDataArray = sortedArrayCache;
else
dlDataArray = sortedArrayCache = (DefaultRankCalculator[]) downloadDataMap.values().toArray(
new DefaultRankCalculator[downloadDataMap.size()]);
TotalsStats totals = new TotalsStats(dlDataArray);
String[] mainDebugEntries = null;
if (bDebugLog) {
log.log(LoggerChannel.LT_INFORMATION, ">>process()");
mainDebugEntries = new String[] {
"ok2Start=" + boolDebug(totals.bOkToStartSeeding),
"tFrcdCding=" + totals.forcedSeeding,
"actvCDs=" + totals.activelyCDing,
"tW8tingToCd=" + totals.waitingToSeed,
"tDLing=" + totals.downloading,
"actvDLs=" + totals.activelyDLing,
"tW8tingToDL=" + totals.waitingToDL,
"tCom=" + totals.complete,
"tIncQd=" + totals.incompleteQueued,
"mxCdrs=" + totals.maxSeeders,
"tFP=" + totals.firstPriority,
"maxT=" + totals.maxTorrents
};
}
// Sort
Arrays.sort(dlDataArray);
ProcessVars vars = new ProcessVars();
// pre-included Forced Start torrents so a torrent "above" it doesn't
// start (since normally it would start and assume the torrent below it
// would stop)
vars.numWaitingOrSeeding = totals.forcedSeeding; // Running Count
vars.numWaitingOrDLing = 0; // Running Count
vars.higherCDtoStart = false;
vars.higherDLtoStart = false;
vars.posComplete = 0;
// Loop 2 of 2:
// - Start/Stop torrents based on criteria
for (int i = 0; i < dlDataArray.length; i++) {
DefaultRankCalculator dlData = dlDataArray[i];
Download download = dlData.getDownloadObject();
vars.bStopAndQueued = false;
dlData.sTrace = "";
// Initialize STATE_WAITING torrents
if ((download.getState() == Download.ST_WAITING)) {
try {
download.initialize();
} catch (Exception ignore) {
/*ignore*/
}
if (bDebugLog && download.getState() == Download.ST_WAITING) {
dlData.sTrace += "still in waiting state after initialize!\n";
}
}
if (bAutoReposition && (iRankType != RANK_NONE)
&& download.isComplete()
&& (totals.bOkToStartSeeding || totals.firstPriority > 0))
download.setPosition(++vars.posComplete);
int state = download.getState();
// Never do anything to stopped entries
if (state == Download.ST_STOPPING || state == Download.ST_STOPPED
|| state == Download.ST_ERROR) {
continue;
}
if (download.isForceStart()) {
if (state == Download.ST_STOPPED || state == Download.ST_QUEUED) {
try {
download.restart();
String s = "restart: isForceStart";
log.log(LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
} catch (DownloadException e) {
}
state = download.getState();
}
if (state == Download.ST_READY) {
try {
download.start();
String s = "Start: isForceStart";
log.log(LoggerChannel.LT_INFORMATION, s);
dlData.sTrace += s + "\n";
} catch (DownloadException e) {
/* ignore */
}
}
}
// Handle incomplete DLs
if (!download.isComplete()) {
handleInCompleteDownload(dlData, vars, totals);
} else {
handleCompletedDownload(dlDataArray, dlData, vars, totals);
}
} // Loop 2/2 (Start/Stopping)
if (bDebugLog) {
String[] mainDebugEntries2 = new String[] {
"ok2Start=" + boolDebug(totals.bOkToStartSeeding),
"tFrcdCding=" + totals.forcedSeeding,
"actvCDs=" + totals.activelyCDing,
"tW8tingToCd=" + totals.waitingToSeed,
"tDLing=" + totals.downloading,
"actvDLs=" + totals.activelyDLing,
"tW8tingToDL=" + totals.waitingToDL,
"tCom=" + totals.complete,
"tIncQd=" + totals.incompleteQueued,
"mxCdrs=" + totals.maxSeeders,
"tFP=" + totals.firstPriority,
"maxT=" + totals.maxTorrents
};
printDebugChanges("<<process() ", mainDebugEntries, mainDebugEntries2,
"", "", true, null);
}
} finally {
if (now > 0) {
processCount++;
long timeTaken = (SystemTime.getCurrentTime() - now);
processTotalMS += timeTaken;
if (timeTaken > processMaxMS) {
processMaxMS = timeTaken;
}
if (processLastComplete > 0) {
processTotalGap += (now - processLastComplete);
}
processLastComplete = now;
}
this_mon.exit();
}
| private void | recalcAllSeedingRanks(boolean force)
if (closingDown) {
return;
}
try {
this_mon.enter();
DefaultRankCalculator[] dlDataArray = (DefaultRankCalculator[]) downloadDataMap.values().toArray(
new DefaultRankCalculator[0]);
// Check Group #1: Ones that always should run since they set things
for (int i = 0; i < dlDataArray.length; i++) {
if (force)
dlDataArray[i].getDownloadObject().setSeedingRank(0);
dlDataArray[i].recalcSeedingRank();
}
} finally {
this_mon.exit();
}
| private void | reloadConfigParams()
try {
this_mon.enter();
int iNewRankType = plugin_config.getIntParameter("StartStopManager_iRankType");
minSpeedForActiveSeeding = plugin_config.getIntParameter("StartStopManager_iMinSpeedForActiveSeeding");
_maxActive = plugin_config.getIntParameter("max active torrents");
_maxActiveWhenSeedingEnabled = plugin_config.getBooleanParameter("StartStopManager_bMaxActiveTorrentsWhenSeedingEnabled");
_maxActiveWhenSeeding = plugin_config.getIntParameter("StartStopManager_iMaxActiveTorrentsWhenSeeding");
minDownloads = plugin_config.getIntParameter("min downloads");
maxDownloads = plugin_config.getIntParameter("max downloads");
numPeersAsFullCopy = plugin_config.getIntParameter("StartStopManager_iNumPeersAsFullCopy");
iFakeFullCopySeedStart = plugin_config.getIntParameter("StartStopManager_iFakeFullCopySeedStart");
bAutoReposition = plugin_config.getBooleanParameter("StartStopManager_bAutoReposition");
minTimeAlive = plugin_config.getIntParameter("StartStopManager_iMinSeedingTime") * 1000;
bDebugLog = plugin_config.getBooleanParameter("StartStopManager_bDebugLog");
bAutoStart0Peers = plugin_config.getBooleanParameter("StartStopManager_bAutoStart0Peers");
iMaxUploadSpeed = plugin_config.getIntParameter("Max Upload Speed KBs", 0);
boolean move_top = plugin_config.getBooleanParameter("StartStopManager_bNewSeedsMoveTop");
plugin_config.setBooleanParameter(
PluginConfig.CORE_PARAM_BOOLEAN_NEW_SEEDS_START_AT_TOP, move_top);
if (iNewRankType != iRankType) {
iRankType = iNewRankType;
// shorten recalc for timed rank type, since the calculation is fast and we want to stop on the second
if (iRankType == RANK_TIMED) {
if (recalcSeedingRanksTask == null) {
recalcSeedingRanksTask = new RecalcSeedingRanksTask();
SimpleTimer.addPeriodicEvent("StartStop:recalcSR", 1000,
recalcSeedingRanksTask);
}
} else if (recalcSeedingRanksTask != null) {
recalcSeedingRanksTask.cancel();
recalcSeedingRanksTask = null;
}
}
/*
// limit _maxActive and maxDownloads based on TheColonel's specs
// maxActive = max_upload_speed / (slots_per_torrent * min_speed_per_peer)
if (_maxActive > 0) {
int iSlotsPerTorrent = plugin_config.getIntParameter("Max Uploads");
// TODO: Track upload speed, storing the max upload speed over a minute
// and use that for "unlimited" setting, or huge settings (like 200)
if (iSlotsPerTorrent > 0) {
int iMinSpeedPerPeer = 3; // for now. TODO: config value
int _maxActiveLimit = iMaxUploadSpeed / (iSlotsPerTorrent * iMinSpeedPerPeer);
if (_maxActive > _maxActiveLimit) {
_maxActive = _maxActiveLimit;
plugin_config.setIntParameter(PluginConfig.CORE_PARAM_INT_MAX_ACTIVE, _maxActive);
}
}
if (maxDownloads > _maxActive) {
maxDownloads = _maxActive;
plugin_config.setIntParameter(PluginConfig.CORE_PARAM_INT_MAX_DOWNLOADS, maxDownloads);
}
}
*/
// force a recalc on all downloads by setting SR to 0, scheduling
// a recalc on next process, and requsting a process cycle
Collection allDownloads = downloadDataMap.values();
DefaultRankCalculator[] dlDataArray = (DefaultRankCalculator[]) allDownloads.toArray(new DefaultRankCalculator[0]);
for (int i = 0; i < dlDataArray.length; i++) {
dlDataArray[i].getDownloadObject().setSeedingRank(0);
}
ranksToRecalc.addAll(allDownloads);
requestProcessCycle(null);
if (bDebugLog) {
log.log(LoggerChannel.LT_INFORMATION, "somethingChanged: config reload");
try {
if (debugMenuItem == null) {
final String DEBUG_MENU_ID = "StartStopRules.menu.viewDebug";
MenuItemListener menuListener = new MenuItemListener() {
public void selected(MenuItem menu, Object target) {
if (!(target instanceof TableRow))
return;
TableRow tr = (TableRow) target;
Object ds = tr.getDataSource();
if (!(ds instanceof Download))
return;
DefaultRankCalculator dlData = (DefaultRankCalculator) downloadDataMap.get(ds);
if (dlData != null) {
if (bSWTUI)
StartStopRulesDefaultPluginSWTUI.openDebugWindow(dlData);
else
pi.getUIManager().showTextMessage(
null,
null,
"FP:\n" + dlData.sExplainFP + "\n" + "SR:"
+ dlData.sExplainSR + "\n" + "TRACE:\n"
+ dlData.sTrace);
}
}
};
TableManager tm = pi.getUIManager().getTableManager();
TableContextMenuItem menu;
menu = tm.addContextMenuItem(
TableManager.TABLE_MYTORRENTS_COMPLETE, DEBUG_MENU_ID);
menu.addListener(menuListener);
menu = tm.addContextMenuItem(
TableManager.TABLE_MYTORRENTS_INCOMPLETE, DEBUG_MENU_ID);
menu.addListener(menuListener);
}
} catch (Throwable t) {
Debug.printStackTrace(t);
}
}
} finally {
this_mon.exit();
}
| public void | removeListener(StartStopRulesFPListener listener)
listenersFP.remove(listener);
| public void | requestProcessCycle(DefaultRankCalculator rankToRecalc)
if (rankToRecalc != null) {
try {
this_mon.enter();
ranksToRecalc.add(rankToRecalc);
} finally {
this_mon.exit();
}
}
if (somethingChanged) {
processMergeCount++;
} else {
somethingChanged = true;
}
| private boolean | scrapeResultOk(Download download)
DownloadScrapeResult sr = download.getLastScrapeResult();
return (sr.getResponseType() == DownloadScrapeResult.RT_SUCCESS);
| private void | setupConfigModel(org.gudy.azureus2.plugins.ui.model.BasicPluginConfigModel configModel)
String PREFIX_RES = "ConfigView.label.seeding.";
configModel.addIntParameter2(
"StartStopManager_iRankType",
"ConfigView.label.seeding.rankType",
com.aelitis.azureus.plugins.startstoprules.defaultplugin.StartStopRulesDefaultPlugin.RANK_SPRATIO);
configModel.addIntParameter2("StartStopManager_iRankTypeSeedFallback",
"ConfigView.label.seeding.rankType.seed.fallback", 0);
configModel.addBooleanParameter2("StartStopManager_bAutoReposition",
"ConfigView.label.seeding.autoReposition", false);
configModel.addIntParameter2("StartStopManager_iMinSeedingTime",
"ConfigView.label.minSeedingTime", 60 * 3);
// ignore rules subsection
// ---------
configModel.addBooleanParameter2("StartStopManager_bIgnore0Peers",
"ConfigView.label.seeding.ignore0Peers", true);
configModel.addIntParameter2("StartStopManager_iIgnoreSeedCount",
"ConfigView.label.ignoreSeeds", 0);
// for "Stop Peers Ratio" ignore rule
configModel.addIntParameter2("StartStopManager_iIgnoreRatioPeersSeedStart",
"ConfigView.label.seeding.fakeFullCopySeedStart", 0);
// for "Stop Ratio" ignore rule
configModel.addIntParameter2("StartStopManager_iIgnoreShareRatioSeedStart",
"ConfigView.label.seeding.fakeFullCopySeedStart", 0);
// Auto Starting
// ---------
configModel.addBooleanParameter2("StartStopManager_bPreferLargerSwarms",
"ConfigView.label.seeding.preferLargerSwarms", true);
configModel.addBooleanParameter2("StartStopManager_bAutoStart0Peers",
"ConfigView.label.seeding.autoStart0Peers", false);
configModel.addIntParameter2("StartStopManager_iMinPeersToBoostNoSeeds",
"ConfigView.label.minPeersToBoostNoSeeds", 1);
// queue section
// ---------
configModel.addIntParameter2("StartStopManager_iMinSpeedForActiveDL",
"ConfigView.label.minSpeedForActiveDL", 512);
configModel.addIntParameter2("StartStopManager_iMinSpeedForActiveSeeding",
"ConfigView.label.minSpeedForActiveSeeding", 512);
configModel.addBooleanParameter2("StartStopManager_bDebugLog",
"ConfigView.label.queue.debuglog", false);
configModel.addBooleanParameter2("StartStopManager_bNewSeedsMoveTop",
"ConfigView.label.queue.newseedsmovetop", true);
configModel.addIntParameter2(
"StartStopManager_iMaxActiveTorrentsWhenSeeding",
"ConfigView.label.queue.maxactivetorrentswhenseeding", 0);
configModel.addBooleanParameter2(
"StartStopManager_bMaxActiveTorrentsWhenSeedingEnabled",
"ConfigView.label.queue.maxactivetorrentswhenseeding", false);
// first Priority subsection
// ---------
configModel.addIntParameter2("StartStopManager_iFirstPriority_Type",
"ConfigView.label.seeding.firstPriority",
DefaultRankCalculator.FIRSTPRIORITY_ANY);
configModel.addIntParameter2("StartStopManager_iFirstPriority_ShareRatio",
"ConfigView.label.seeding.firstPriority.shareRatio", 500);
configModel.addIntParameter2(
"StartStopManager_iFirstPriority_SeedingMinutes",
"ConfigView.label.seeding.firstPriority.seedingMinutes", 0);
configModel.addIntParameter2("StartStopManager_iFirstPriority_DLMinutes",
"ConfigView.label.seeding.firstPriority.DLMinutes", 0);
// for ignore FP rules
configModel.addIntParameter2(
"StartStopManager_iFirstPriority_ignoreSPRatio",
"ConfigView.label.seeding.firstPriority.ignoreSPRatio", 0);
configModel.addBooleanParameter2(
"StartStopManager_bFirstPriority_ignore0Peer",
"ConfigView.label.seeding.firstPriority.ignore0Peer", false);
// seeding subsection
configModel.addIntParameter2("StartStopManager_iAddForSeedingDLCopyCount",
"ConfigView.label.seeding.addForSeedingDLCopyCount", 1);
configModel.addIntParameter2("StartStopManager_iNumPeersAsFullCopy",
PREFIX_RES + "numPeersAsFullCopy", 0);
configModel.addIntParameter2("StartStopManager_iFakeFullCopySeedStart",
PREFIX_RES + "fakeFullCopySeedStart", 1);
configModel.destroy();
|
|