597 lines
16 KiB
JavaScript
597 lines
16 KiB
JavaScript
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 2000
|
||
|
//
|
||
|
// File: buildtimes.js
|
||
|
//
|
||
|
// Contents:
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
//
|
||
|
// Globals
|
||
|
//
|
||
|
|
||
|
var g_reDate = new RegExp("[a-zA-Z0-9_-]+\\t(\\d{4})/(\\d{2})/(\\d{2}):(\\d{2}):(\\d{2}):(\\d{2})");
|
||
|
var g_reStartBuildExe = new RegExp("\\(([a-zA-Z]+)\\) Build for [a-zA-Z]+ started. PID=(\\d+)");
|
||
|
var g_rePassComplete = new RegExp("\\(([a-zA-Z]+)\\) pass (\\d) complete on pid (\\d+)");
|
||
|
var g_reTaskStep = new RegExp("\\(([a-zA-Z]+)\\) TASK STEPPING ");
|
||
|
var g_reProcessExit = new RegExp("\\(([a-zA-Z]+)\\) Process id (\\d+) exited");
|
||
|
var g_reStartPost = new RegExp("\\(([a-zA-Z]+)\\) postbuild task thread for Root started.");
|
||
|
var g_reExitPost = new RegExp("\\(([a-zA-Z]+)\\) postbuild for Root completed.");
|
||
|
var g_reStartBuild = new RegExp("STARTING PASS 0 of build.");
|
||
|
var g_reExitBuild = new RegExp("WAIT FOR NEXTPASS 2 of waitbuild");
|
||
|
|
||
|
var g_reStartCFPBTS = new RegExp("Received copyfilesfrompostbuildtoslave command from remote machine");
|
||
|
var g_reStartCFTPB = new RegExp("CopyFilesToPostBuild\\(([a-zA-Z]+)\\).*There are (\\d+) files");
|
||
|
var g_reLastCopyCFTPB = new RegExp("RoboCopyCopyFile\\(([a-zA-Z]+)\\)");
|
||
|
var g_reEndCFTPB = new RegExp("(Received) nextpass command from remote machine.");
|
||
|
|
||
|
|
||
|
var g_reBuildType = new RegExp("razzle.cmd");
|
||
|
|
||
|
// NTAXP01 2000/04/13:17:50:01 taskBuildFiles(strSDRoot) (Root) Build for Root started. PID=548
|
||
|
// NTAXP01 2000/04/13:17:56:20 ParseBuildMessages(pid) (Root) pass 0 complete on pid 548
|
||
|
// NTAXP01 2000/04/13:18:45:20 ParseBuildMessages(pid) (Root) pass 1 complete on pid 548
|
||
|
// NTAXP01 2000/04/13:21:59:11 ParseBuildMessages(pid) (Root) pass 2 complete on pid 548
|
||
|
// NTAXP01 2000/04/13:21:59:12 ParseBuildMessages(pid) (Root) Process id 548 exited!
|
||
|
// NTAXP01 2000/04/13:18:44:53 taskBuildFiles(strSDRoot) (Root) TASK STEPPING Root
|
||
|
// AXP64FRE 2000/04/13:17:49:27 MyRunLocalCommand(strCmd) (Root) RunLocalCommand('cmd /c d:\nt\Tools\razzle.cmd win64 free officialbuild no_certcheck no_sdrefresh no_title & sdinit && sd -s sync 2>&1', 'd:\nt\.', 'Sync Root', 'true', 'true', 'false')
|
||
|
|
||
|
// NTBLD04 2000/04/25:14:20:20 mtscript_js::OnRemoteExec(cmd) Received copyfilesfrompostbuildtoslave command from remote machine.
|
||
|
// NTBLD04 2000/04/25:14:19:55 CopyFilesToPostBuild(aPublishedEnlistments) (#0) There are 3 files
|
||
|
// NTBLD04 2000/04/25:14:19:55 RoboCopyCopyFile(srcdir) RoboCopy file d:\nt\public\sdk\lib\i386\AutoDiscovery.tlb to \\NTBLD03\d$\nt\Public\sdk\lib\i386\AutoDiscovery.tlb
|
||
|
// NTBLD04 2000/04/25:14:20:05 mtscript_js::OnRemoteExec(cmd) Received nextpass command from remote machine.
|
||
|
|
||
|
var g_aMatches =
|
||
|
[
|
||
|
{ re: g_reStartBuildExe, fn: NewDepot},
|
||
|
{ re: g_rePassComplete , fn: PassComplete},
|
||
|
{ re: g_reTaskStep , fn: TaskStep},
|
||
|
{ re: g_reProcessExit , fn: ProcessExit},
|
||
|
{ re: g_reStartPost , fn: PostStart},
|
||
|
{ re: g_reExitPost , fn: PostExit},
|
||
|
{ re: g_reStartBuild , fn: ElapsedStart},
|
||
|
{ re: g_reExitBuild , fn: ElapsedExit},
|
||
|
{ re: g_reStartCFPBTS , fn: StartCFPBTS},
|
||
|
{ re: g_reStartCFTPB , fn: StartCFTPB},
|
||
|
{ re: g_reLastCopyCFTPB, fn: LastFileCFTPB},
|
||
|
{ re: g_reEndCFTPB , fn: EndCFTPB}
|
||
|
];
|
||
|
|
||
|
var g_strBuildType;
|
||
|
var g_strMachine;
|
||
|
var g_aStrFileNames = new Array();
|
||
|
var g_FSObj = new ActiveXObject("Scripting.FileSystemObject");
|
||
|
var g_hDepots = new Object;
|
||
|
var g_hTimers = new Object;
|
||
|
var g_fMerged = true; // Merge "root" and "mergedcomponents" report
|
||
|
var g_fPrintElapsed = false;
|
||
|
var g_fPrintPost = false;
|
||
|
var g_CFObj;
|
||
|
// Add new methods...
|
||
|
Error.prototype.toString = Error_ToString;
|
||
|
//
|
||
|
// First, parse command line arguments
|
||
|
//
|
||
|
|
||
|
|
||
|
ParseArguments(WScript.Arguments);
|
||
|
main();
|
||
|
WScript.Quit(0);
|
||
|
|
||
|
function ParseArguments(Arguments)
|
||
|
{
|
||
|
var strArg;
|
||
|
var chArg0;
|
||
|
var chArg1;
|
||
|
var argn;
|
||
|
|
||
|
for(argn = 0; argn < Arguments.length; argn++)
|
||
|
{
|
||
|
strArg = Arguments(argn);
|
||
|
chArg0 = strArg.charAt(0);
|
||
|
chArg1 = strArg.toLowerCase().slice(1);
|
||
|
|
||
|
if (chArg0 != '-' && chArg0 != '/')
|
||
|
{
|
||
|
g_aStrFileNames[g_aStrFileNames.length] = Arguments(argn);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch(chArg1)
|
||
|
{
|
||
|
case 'm':
|
||
|
g_fMerged = true;
|
||
|
break;
|
||
|
case 's':
|
||
|
g_fMerged = false;
|
||
|
break;
|
||
|
case 'e':
|
||
|
g_fPrintElapsed = true;
|
||
|
break;
|
||
|
case 'p':
|
||
|
g_fPrintPost = true;
|
||
|
break;
|
||
|
case '?':
|
||
|
LogMsg("HELP");
|
||
|
Usage();
|
||
|
break;
|
||
|
default:
|
||
|
LogMsg("HELP " + strArg);
|
||
|
Usage();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (g_aStrFileNames.length < 1)
|
||
|
Usage();
|
||
|
}
|
||
|
|
||
|
// PadDigits(n, cDigits)
|
||
|
// Return a string representation of the given
|
||
|
// number. Leading zeros are added to make the
|
||
|
// string cDigits long.
|
||
|
function PadDigits(n, cDigits)
|
||
|
{
|
||
|
var strDigits = '';
|
||
|
var i;
|
||
|
var strNumber = n.toString();
|
||
|
|
||
|
for(i = 0; i < cDigits - strNumber.length; ++i)
|
||
|
strDigits += '0';
|
||
|
|
||
|
return strDigits + n;
|
||
|
}
|
||
|
|
||
|
// PadString(str, cDigits)
|
||
|
function PadString(str, cDigits)
|
||
|
{
|
||
|
var strDigits = '';
|
||
|
var i;
|
||
|
|
||
|
for(i = 0; i < cDigits - str.length; ++i)
|
||
|
strDigits += ' ';
|
||
|
|
||
|
return str + strDigits;
|
||
|
}
|
||
|
|
||
|
function Error_ToString()
|
||
|
{
|
||
|
var i;
|
||
|
var str = 'Exception(';
|
||
|
|
||
|
/*
|
||
|
Only some error messages get filled in for "ex".
|
||
|
Specifically the text for disk full never seems
|
||
|
to get set by functions such as CreateTextFile().
|
||
|
|
||
|
|
||
|
*/
|
||
|
if (this.number != null && this.description == "")
|
||
|
{
|
||
|
switch(this.number)
|
||
|
{
|
||
|
case -2147024784:
|
||
|
this.description = "There is not enough space on the disk.";
|
||
|
break;
|
||
|
case -2147024894:
|
||
|
this.description = "The system cannot find the file specified.";
|
||
|
break;
|
||
|
case -2147023585:
|
||
|
this.description = "There are currently no logon servers available to service the logon request.";
|
||
|
break;
|
||
|
case -2147023170:
|
||
|
this.description = "The remote procedure call failed.";
|
||
|
break;
|
||
|
case -2147024837:
|
||
|
this.description = "An unexpected network error occurred";
|
||
|
break;
|
||
|
default:
|
||
|
this.description = "Error text not set for (" + this.number + ")";
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for(i in this)
|
||
|
{
|
||
|
str += i + ": " + this[i] + " ";
|
||
|
}
|
||
|
return str + ")";
|
||
|
}
|
||
|
|
||
|
function LogMsg(msg)
|
||
|
{
|
||
|
WScript.Echo(msg);
|
||
|
}
|
||
|
|
||
|
function Usage()
|
||
|
{
|
||
|
LogMsg('Usage: buildtimes [-s] [-e] [-p] path\\filename*.log');
|
||
|
LogMsg(' -s Do not merge build time of "root" and "mergedcomponents"');
|
||
|
LogMsg(' -e Print total elasped build time');
|
||
|
LogMsg(' -p Print postbuild time');
|
||
|
WScript.Quit(0);
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////
|
||
|
//////////////////////////////////////
|
||
|
// The DEPOT object
|
||
|
//////////////////////////////////////
|
||
|
//////////////////////////////////////
|
||
|
function Depot(strDepotName, nDepotPID)
|
||
|
{
|
||
|
this.strName = strDepotName;
|
||
|
this.nPID = nDepotPID;
|
||
|
this.nElapsed = 0;
|
||
|
this.nLast = 0; // used for postbuild
|
||
|
|
||
|
Depot.prototype.Begin = Depot_Begin;
|
||
|
Depot.prototype.SetLast = Depot_SetLast;
|
||
|
Depot.prototype.End = Depot_End;
|
||
|
Depot.prototype.End2 = Depot_End2;
|
||
|
Depot.prototype.Terminate = Depot_Terminate;
|
||
|
Depot.prototype.GetStrStatus = Depot_GetStrStatus;
|
||
|
}
|
||
|
|
||
|
function Depot_Begin(dateVal)
|
||
|
{
|
||
|
if (this.bBegin)
|
||
|
throw new Error(-1, "Multiple begins without end on depot " + this.strName);
|
||
|
this.nBegin = dateVal;
|
||
|
}
|
||
|
|
||
|
function Depot_SetLast(dateVal)
|
||
|
{
|
||
|
if (!this.nBegin)
|
||
|
throw new Error(-1, "SetLast without begin on depot " + this.strName);
|
||
|
|
||
|
this.nLast = dateVal;
|
||
|
}
|
||
|
|
||
|
function Depot_End(dateVal)
|
||
|
{
|
||
|
if (!this.nBegin)
|
||
|
throw new Error(-1, "End without begin on depot " + this.strName);
|
||
|
|
||
|
var delta = dateVal - this.nBegin;
|
||
|
delete this.nBegin;
|
||
|
this.nElapsed += delta;
|
||
|
}
|
||
|
|
||
|
function Depot_End2(dateVal)
|
||
|
{
|
||
|
if (!this.nBegin)
|
||
|
throw new Error(-1, "End without begin on depot " + this.strName);
|
||
|
|
||
|
var delta = dateVal - this.nBegin;
|
||
|
delete this.nBegin;
|
||
|
LogMsg("DELTA " + this.strName + " = " + delta);
|
||
|
this.nElapsed += delta;
|
||
|
}
|
||
|
|
||
|
function Depot_Terminate(dateVal)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
function Depot_GetStrStatus()
|
||
|
{
|
||
|
var seconds = Math.floor(this.nElapsed / 1000);
|
||
|
var s = seconds % 60;
|
||
|
var m = Math.floor(seconds / 60) % 60;
|
||
|
var h = Math.floor(seconds / 60 / 60);
|
||
|
|
||
|
var strElapsed = PadDigits(h, 2) + ":" + PadDigits(m, 2) + ":" + PadDigits(s, 2);
|
||
|
return PadString(this.strName, 16) + " " + strElapsed;// + ", " + (this.nElapsed / 1000);
|
||
|
|
||
|
}
|
||
|
//////////////////////////////////////
|
||
|
//////////////////////////////////////
|
||
|
//////////////////////////////////////
|
||
|
|
||
|
function NewDepot(dateVal, strDepotName, a)
|
||
|
{
|
||
|
var nPID = a[0];
|
||
|
if (g_hDepots[strDepotName])
|
||
|
{
|
||
|
if (strDepotName == "root")
|
||
|
{
|
||
|
var depot = g_hDepots[strDepotName];
|
||
|
depot.Begin(dateVal);
|
||
|
}
|
||
|
else
|
||
|
throw new Error(-1, "Duplicate depot information for depot " + strDepotName);
|
||
|
}
|
||
|
var depot = new Depot(strDepotName, nPID);
|
||
|
depot.Begin(dateVal);
|
||
|
|
||
|
g_hDepots[strDepotName] = depot;
|
||
|
}
|
||
|
|
||
|
function PassComplete(dateVal, strDepotName, a)
|
||
|
{
|
||
|
var nPass = a[0];
|
||
|
var nPID = a[1];
|
||
|
var depot = g_hDepots[strDepotName];
|
||
|
if (!depot)
|
||
|
throw new Error(-1, "PassComplete on missing depot " + strDepotName);
|
||
|
|
||
|
depot.End(dateVal);
|
||
|
}
|
||
|
|
||
|
function TaskStep(dateVal, strDepotName)
|
||
|
{
|
||
|
var depot = g_hDepots[strDepotName];
|
||
|
if (!depot)
|
||
|
throw new Error(-1, "TaskStep on missing depot " + strDepotName);
|
||
|
|
||
|
depot.Begin(dateVal);
|
||
|
}
|
||
|
|
||
|
function ProcessExit(dateVal, strDepotName, a)
|
||
|
{
|
||
|
var nPID = a[0];
|
||
|
var depot = g_hDepots[strDepotName];
|
||
|
if (!depot)
|
||
|
throw new Error(-1, "ProcessExit on missing depot " + strDepotName);
|
||
|
|
||
|
depot.Terminate(dateVal);
|
||
|
}
|
||
|
|
||
|
function PostStart(dateVal, strDepotName)
|
||
|
{
|
||
|
if (g_fPrintPost)
|
||
|
{
|
||
|
var depot = new Depot('postbuild', 0);
|
||
|
depot.Begin(dateVal);
|
||
|
|
||
|
g_hTimers['postbuild'] = depot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function PostExit(dateVal, strDepotName)
|
||
|
{
|
||
|
if (g_fPrintPost)
|
||
|
{
|
||
|
var depot = g_hTimers['postbuild'];
|
||
|
if (!depot)
|
||
|
throw new Error(-1, "TaskStep on missing depot " + 'postbuild');
|
||
|
|
||
|
depot.End(dateVal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ElapsedStart(dateVal, strDepotName)
|
||
|
{
|
||
|
if (g_fPrintElapsed)
|
||
|
{
|
||
|
var depot = new Depot('entirebuild', 0);
|
||
|
depot.Begin(dateVal);
|
||
|
|
||
|
g_hTimers['entirebuild'] = depot;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ElapsedExit(dateVal, strDepotName)
|
||
|
{
|
||
|
if (g_fPrintElapsed)
|
||
|
{
|
||
|
var depot = g_hTimers['entirebuild'];
|
||
|
if (!depot)
|
||
|
throw new Error(-1, "TaskStep on missing depot " + 'entirebuild');
|
||
|
|
||
|
depot.End(dateVal);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function StartCFPBTS(dateVal, strDepotName)
|
||
|
{
|
||
|
var depot;
|
||
|
if (true)
|
||
|
{
|
||
|
if (g_CFObj)
|
||
|
EndCFTPB(dateVal, strDepotName);
|
||
|
|
||
|
depot = g_hTimers['toslave'];
|
||
|
if (!depot)
|
||
|
depot = new Depot('toslave', 0);
|
||
|
|
||
|
depot.Begin(dateVal);
|
||
|
depot.SetLast(dateVal);
|
||
|
|
||
|
g_hTimers['toslave'] = depot;
|
||
|
|
||
|
g_CFObj = depot;
|
||
|
// LogMsg("StartCFPBTS at " + ( new Date(dateVal)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function StartCFTPB(dateVal, strDepotName)
|
||
|
{
|
||
|
var depot;
|
||
|
if (true)
|
||
|
{
|
||
|
if (g_CFObj)
|
||
|
EndCFTPB(dateVal, strDepotName);
|
||
|
|
||
|
depot = g_hTimers['topostbuild'];
|
||
|
if (!depot)
|
||
|
depot = new Depot('topostbuild', 0);
|
||
|
|
||
|
depot.Begin(dateVal);
|
||
|
depot.SetLast(dateVal);
|
||
|
|
||
|
g_hTimers['topostbuild'] = depot;
|
||
|
|
||
|
g_CFObj = depot;
|
||
|
//LogMsg("StartCFTPB at " + ( new Date(dateVal)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function LastFileCFTPB(dateVal, strDepotName)
|
||
|
{
|
||
|
if (true)
|
||
|
{
|
||
|
if (g_CFObj)
|
||
|
{
|
||
|
g_CFObj.SetLast(dateVal);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function EndCFTPB(dateVal, strDepotName)
|
||
|
{
|
||
|
if (true)
|
||
|
{
|
||
|
var depot = null;
|
||
|
if (g_CFObj)
|
||
|
{
|
||
|
depot = g_CFObj;
|
||
|
//LogMsg("EndCFTPB at " + ( new Date(depot.nLast)));
|
||
|
depot.End(depot.nLast);
|
||
|
g_CFObj = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function ParseDate(strLine)
|
||
|
{
|
||
|
var a = g_reDate.exec(strLine);
|
||
|
if (!a)
|
||
|
{
|
||
|
throw new Error(-1, "Incorrect date format: " + strLine);
|
||
|
}
|
||
|
var d = new Date(a[1], a[2] - 1, a[3], a[4], a[5], a[6]);
|
||
|
|
||
|
return d.getTime();
|
||
|
}
|
||
|
|
||
|
|
||
|
function GetBuildType(strLine)
|
||
|
{
|
||
|
//Build Type: free
|
||
|
//Build Platform: 64bit
|
||
|
//Incremental Build: false
|
||
|
//Build Manager: BUILDCON1
|
||
|
//PostBuild Machine: AXP64FRE
|
||
|
|
||
|
var str;
|
||
|
var n;
|
||
|
var m;
|
||
|
var fWin64 = false;
|
||
|
var fFree = false;
|
||
|
|
||
|
n = strLine.indexOf("\t");
|
||
|
if (n >= 0)
|
||
|
{
|
||
|
g_strMachine = strLine.slice(0, n);
|
||
|
}
|
||
|
n = strLine.indexOf("razzle.cmd", 0);
|
||
|
if (n > 0)
|
||
|
{ // Extract the stuff between "razzle.cmd" and the first "&"
|
||
|
m = strLine.indexOf("&", n);
|
||
|
str = strLine.slice(n, m);
|
||
|
|
||
|
n = str.indexOf("win64");
|
||
|
if (n >= 0)
|
||
|
fWin64 = true;
|
||
|
n = str.indexOf("free");
|
||
|
if (n >= 0)
|
||
|
fFree = true;
|
||
|
|
||
|
g_strBuildType = (fWin64 ? "64bit " : "32bit ") + (fFree ? "free" : "checked");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function MergeRoot(str)
|
||
|
{
|
||
|
str = str.toLowerCase();
|
||
|
if (g_fMerged && str == "mergedcomponents")
|
||
|
return "root";
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
function ParseLogFile(strFileName)
|
||
|
{
|
||
|
var strDepotName;
|
||
|
var hFile = g_FSObj.OpenTextFile(strFileName, 1, false);
|
||
|
|
||
|
var a;
|
||
|
var nLine = 0;
|
||
|
var i;
|
||
|
try
|
||
|
{
|
||
|
while(1)
|
||
|
{
|
||
|
r = hFile.ReadLine();
|
||
|
++nLine;
|
||
|
|
||
|
if (!g_strBuildType)
|
||
|
{
|
||
|
if (g_reBuildType.test(r))
|
||
|
GetBuildType(r);
|
||
|
}
|
||
|
// debugger;
|
||
|
for(i = 0; i < g_aMatches.length; ++i)
|
||
|
{
|
||
|
a = g_aMatches[i].re.exec(r);
|
||
|
if (a != null)
|
||
|
{
|
||
|
if (a.length > 1)
|
||
|
strDepotName = MergeRoot(a[1]);
|
||
|
else
|
||
|
strDepotName = '';
|
||
|
a = a.slice(2);
|
||
|
g_aMatches[i].fn(ParseDate(r), strDepotName, a);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
catch(ex)
|
||
|
{
|
||
|
if (ex.number != -2146828226) // Input past end
|
||
|
{
|
||
|
LogMsg("Exception in ParseLogFile " + strFileName + ", line=" + nLine + ", ex=" + ex);
|
||
|
LogMsg("I is " + i);
|
||
|
}
|
||
|
}
|
||
|
hFile.Close();
|
||
|
}
|
||
|
|
||
|
function main()
|
||
|
{
|
||
|
var i;
|
||
|
var strDepot;
|
||
|
var depot;
|
||
|
|
||
|
for(i = 0; i < g_aStrFileNames.length; ++i)
|
||
|
{
|
||
|
g_strMachine = null;
|
||
|
g_strBuildType = null;
|
||
|
g_hDepots = new Object;
|
||
|
g_hTimers = new Object;
|
||
|
|
||
|
ParseLogFile(g_aStrFileNames[i]);
|
||
|
|
||
|
if (!g_strMachine)
|
||
|
g_strMachine = g_aStrFileNames[i];
|
||
|
|
||
|
g_strMachine = PadString(PadString(g_strMachine, 12) + g_strBuildType, 24);
|
||
|
//LogMsg(g_strMachine + ": " + g_strBuildType);
|
||
|
for(strDepot in g_hDepots)
|
||
|
{
|
||
|
LogMsg(g_strMachine + ": Depot: " + g_hDepots[strDepot].GetStrStatus());
|
||
|
}
|
||
|
for(strDepot in g_hTimers)
|
||
|
{
|
||
|
LogMsg(g_strMachine + ": " + g_hTimers[strDepot].GetStrStatus());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|