//--------------------------------------------------------------------------- #include #pragma hdrstop #include "UdTransferHistory.h" #include "UdTransfer.h" #include "DataModule.h" #include //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma link "Grids_ts" #pragma link "TSDBGrid" #pragma link "TSGrid" //#pragma link "PERFGRAP" #pragma link "AdvPageControl" #pragma link "AdvGrid" #pragma link "BaseGrid" #pragma link "DBAdvGrd" #pragma link "AdvGrid" #pragma link "BaseGrid" #pragma resource "*.dfm" TUdTransferHistoryForm *UdTransferHistoryForm; namespace { AnsiString GetDurationString( const TDateTime & now, const TDateTime & then ) { AnsiString duration; AnsiString text; // Longest duration should be in days int days = DaysBetween( now, then ); __int64 hours = HoursBetween( now, then ); __int64 mins = MinutesBetween( now, then ); __int64 secs = SecondsBetween( now, then ); if( days > 0 ) { text.sprintf( "%d day(s), ", days ); duration = text; hours = hours % 24; mins = mins % 60; secs = secs % 60; } else if( hours > 0 ) { mins = mins % 60; secs = secs % 60; } else if( mins > 0 ) { secs = secs % 60; } text.sprintf( "%02Ld:%02Ld:%02Ld", hours, mins, secs ); duration += text; return duration; } AnsiString GetDurationString( const int & duration ) { TDateTime then = 0; TDateTime now = (double)duration/(24*60*60); return GetDurationString( now, then ); } AnsiString FormatNumeric( long double number, int precision = 0 ) { return AnsiString::FloatToStrF((long double)number, AnsiString::sffNumber, 18, precision); } typedef enum { COL_ATTRIB = 1, COL_VALUE = 2 } TransferDetailsColumns; typedef enum { ROW_SOURCE_PATH = 1, ROW_DESTINATION = 2, ROW_BATCH_SIZE = 3, ROW_END_TIME = 4, ROW_COMMENTS = 5, ROW_RAMP_TYPE = 6, ROW_RAMP_TIME = 7, ROW_DURATION = 8, ROW_FREQUENCY = 9, ROW_GRADIENT = 10, ROW_RECURRING = 11 } TransferDetailsRows; } //--------------------------------------------------------------------------- __fastcall TUdTransferHistoryForm::TUdTransferHistoryForm(TComponent* Owner) : TForm(Owner), m_manager( NULL ) { } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::btAddClick(TObject *Sender) { if( UdTransferForm->ShowForm( m_manager ) == mrOk ) { RefreshHistory(); } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::btnAbortClick(TObject *Sender) { if( MessageDlg("Are you sure you want to abort this transfer task?", mtConfirmation, TMsgDlgButtons() << mbNo << mbYes, 0 ) == mrYes ) { UdTransferTask task; task.id = m_curTransferNum; if( m_manager->GetTaskDetails( task ) ) { AnsiString comment; int status = gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger; task.state = UdTransferAborted; if( status == UdTransferPending ) { comment = "Transfer aborted before processing started."; } else if( status == UdTransferInProgress ) { if( !task.recurring ) { comment = "Transfer aborted during processing."; } else { // Stopping a recurring task is not really an error comment = "User stopped recurring transfers during processing."; task.state = UdTransferCompleted; } } m_manager->UpdateTask( task, comment ); } else { // Only way this could happen is if we are aborting a task set as // in progress after which the program crashed. Simply update the status flag. TADOQuery * txnquery = new TADOQuery(NULL); if( txnquery == NULL ) return; txnquery->Connection = Data_Module->IntegrationDBConnection; AnsiString sql_statement; sql_statement.sprintf("update TRANSFER_HISTORY set " " STATUS = %d, " " END_TIME = sysdate " "where TRANSFER_NO = %d", (int)UdTransferAborted, m_curTransferNum); txnquery->SQL->Text = sql_statement; txnquery->ExecSQL(); txnquery->Close(); delete txnquery; RefreshHistory(); } } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::gridHistoryCellLoaded( TObject *Sender, int DataCol, Variant &DataRow, Variant &Value) { // Format some of the loaded grid data if( DataCol == 5 ) // Status { switch( gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger ) { case UdTransferCompleted: Value = "Completed"; break; case UdTransferFailed: Value = "Failed"; break; case UdTransferAborted: Value = "Aborted"; break; case UdTransferInProgress: Value = "In Progress"; break; case UdTransferPending: Value = "Pending"; break; }; } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::gridHistoryRowChanged( TObject *Sender, Variant &OldRow, Variant &NewRow) { m_curTransferNum = gridHistory->DataSource->DataSet->FieldByName("transfer_no")->AsInteger; LoadTransferDetails(); LoadTransferStatistics(); try { bool isOwner = (gridHistory->DataSource->DataSet ->FieldByName("username")->AsString == m_manager->GetUsername()); switch( gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger ) { case UdTransferCompleted: case UdTransferFailed: case UdTransferAborted: btnAbort->Enabled = false; break; case UdTransferInProgress: case UdTransferPending: btnAbort->Enabled = isOwner; break; }; } catch( EDatabaseError & e ) { btnAbort->Enabled = false; } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::ShowForm(TObject *Sender) { btnAbort->Enabled = false; m_curTransferNum = 0; gridDetails->Cells[COL_ATTRIB][ROW_SOURCE_PATH] = "Source Path"; gridDetails->Cells[COL_ATTRIB][ROW_DESTINATION] = "Destination"; gridDetails->Cells[COL_ATTRIB][ROW_BATCH_SIZE] = "Batch Size"; gridDetails->Cells[COL_ATTRIB][ROW_END_TIME] = "End Time"; gridDetails->Cells[COL_ATTRIB][ROW_COMMENTS] = "Comments"; gridDetails->Cells[COL_ATTRIB][ROW_RAMP_TYPE] = "Ramp Type"; gridDetails->Cells[COL_ATTRIB][ROW_RAMP_TIME] = "Ramp Time"; gridDetails->Cells[COL_ATTRIB][ROW_DURATION] = "Duration"; gridDetails->Cells[COL_ATTRIB][ROW_FREQUENCY] = "Frequency"; gridDetails->Cells[COL_ATTRIB][ROW_GRADIENT] = "Ramp Gradient"; gridDetails->Cells[COL_ATTRIB][ROW_RECURRING] = "Recurring Test"; qryTransferHistory->Active = true; gridHistory->DataSource = dsTransferHistory; } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::LoadTransferDetails() { // Get the data and load the new entries TADOQuery * txnquery = new TADOQuery(NULL); if( txnquery == NULL ) return; txnquery->Connection = Data_Module->IntegrationDBConnection; AnsiString sql_statement; sql_statement.sprintf("select SOURCE_PATH, DESTINATION, BATCH_SIZE, END_TIME, COMMENTS, " " RAMP_TYPE, RAMP_TIME, DURATION, FREQUENCY, RAMP_GRADIENT, RECURRING " "from TRANSFER_HISTORY " "where TRANSFER_NO = %d", m_curTransferNum); txnquery->SQL->Text = sql_statement; txnquery->Open(); if( !txnquery->Eof ) { UdTransferTask task; AnsiString comments; TDateTime endTime; task.sourcePath = txnquery->FieldByName("SOURCE_PATH")->AsString; task.destination = txnquery->FieldByName("DESTINATION")->AsString; task.batchSize = txnquery->FieldByName("BATCH_SIZE")->AsInteger; endTime = txnquery->FieldByName("END_TIME")->AsDateTime; comments = txnquery->FieldByName("COMMENTS")->AsString; task.rampType = (RampTestType)txnquery->FieldByName("RAMP_TYPE")->AsInteger; task.rampTime = txnquery->FieldByName("RAMP_TIME")->AsDateTime; task.duration = txnquery->FieldByName("DURATION")->AsInteger; task.frequency = txnquery->FieldByName("FREQUENCY")->AsInteger; task.gradient = txnquery->FieldByName("RAMP_GRADIENT")->AsFloat; task.recurring = (txnquery->FieldByName("RECURRING")->AsInteger > 0); SetTransferDetails( task, endTime, comments ); } delete txnquery; } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::ResetTransferDetails() { gridDetails->ClearCols( 3, 1 ); } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::ResetTransferStatistics() { bool isTransferRunning = false; if( (m_curTransferNum != 0) && gridHistory->DataSource->DataSet->Active ) { int status = gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger; isTransferRunning = ((status == UdTransferInProgress) || (status == UdTransferPending)); } lblNumFiles->Caption = "N/A"; lblBytes->Caption = "N/A"; lblTransferTime->Caption= "N/A"; lblFilesPerSec->Caption = "N/A"; lblBytesPerSec->Caption = "N/A"; lblPercent->Caption = "0%"; pbCompleted->Position = 0; lblPercent->Visible = isTransferRunning; pbCompleted->Visible = isTransferRunning; lblProgress->Visible = isTransferRunning; seriesBatches->Clear(); chartBatchSize->LeftAxis->Automatic = false; chartBatchSize->BottomAxis->Automatic = false; chartBatchSize->LeftAxis->SetMinMax( 0, 10 ); chartBatchSize->BottomAxis->SetMinMax( 1, 10 ); } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::SetTransferDetails( const UdTransferTask & task, const TDateTime & endTime, const AnsiString & comments ) { ResetTransferDetails(); gridDetails->Cells[COL_VALUE][ROW_SOURCE_PATH] = task.sourcePath; gridDetails->Cells[COL_VALUE][ROW_DESTINATION] = task.destination; gridDetails->Cells[COL_VALUE][ROW_BATCH_SIZE] = task.batchSize; gridDetails->Cells[COL_VALUE][ROW_COMMENTS] = comments; gridDetails->Cells[COL_VALUE][ROW_DURATION] = GetDurationString( task.duration ); gridDetails->Cells[COL_VALUE][ROW_FREQUENCY] = GetDurationString( task.frequency ); gridDetails->Cells[COL_VALUE][ROW_END_TIME] = ((int)endTime == 0) ? "Not Finished" : endTime.DateTimeString().c_str(); gridDetails->Cells[COL_VALUE][ROW_RAMP_TIME] = (task.rampType == RampTestDisabled) ? "Not Applicable" : task.rampTime.DateTimeString().c_str(); gridDetails->Cells[COL_VALUE][ROW_GRADIENT] = (task.rampType == RampTestByDemand) ? FloatToStr(task.gradient).c_str() : "Not Applicable"; gridDetails->Cells[COL_VALUE][ROW_RECURRING] = (task.recurring ? "True" : "False"); AnsiString rampType = "Unknown"; switch( task.rampType ) { case RampTestDisabled: rampType = "Batch Transfer"; break; case RampTestByDemand: rampType = "Ramp by Demand"; break; case RampTestByRatio: rampType = "Ramp by Ratio"; break; } gridDetails->Cells[COL_VALUE][ROW_RAMP_TYPE] = rampType; } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::LoadTransferStatistics() { ResetTransferStatistics(); UpdateTransferStatistics(); } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::UpdateTransferStatistics() { UdTransferStatistics stats; if( m_manager->GetTaskStatistics( m_curTransferNum, stats ) ) { int percentDone = 0; bool displayDurations = true; // If the task is pending, duration statistics are not valid yet try { int status = gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger; if( status == UdTransferPending ) { lblTransferTime->Caption = "Pending ..."; displayDurations = false; } } catch( EDatabaseError & e ) { displayDurations = false; } // Set processed/total files stats if( stats.completed ) { lblNumFiles->Caption = FormatNumeric( stats.processedFiles ); lblBytes->Caption = FormatNumeric( stats.processedBytes ); displayDurations = (stats.processedFiles > 0); } else if( stats.totalFiles > 0 ) { // Stats provided in realtime (non-complete) have more info to display lblBytes->Caption = FormatNumeric( stats.processedBytes ); lblNumFiles->Caption = FormatNumeric( stats.processedFiles % stats.totalFiles ) + " / " + FormatNumeric( stats.totalFiles ); if( stats.processedFiles > stats.totalFiles ) { AnsiString recurses = lblNumFiles->Caption; recurses += " (x" + IntToStr( stats.processedFiles / stats.totalFiles ) + ")"; lblNumFiles->Caption = recurses; } if( gridDetails->Cells[COL_VALUE][ROW_RECURRING] != "True" ) { percentDone = (((stats.processedFiles % stats.totalFiles)*100) / stats.totalFiles); } } // Fill in the chart details if( !stats.batches.empty() ) { seriesBatches->Clear(); chartBatchSize->LeftAxis->Automatic = true; chartBatchSize->BottomAxis->Automatic = true; BatchSizeItem batch = stats.batches.begin(); for( int count = 1; batch != stats.batches.end(); batch++, count++ ) { seriesBatches->AddXY( count, (double)(*batch)/1024, "", clBlue ); } } // Dont display durations for transfers with no files sent if( displayDurations ) { try { TDateTime startTime = stats.startTime; TDateTime endTime = TDateTime::CurrentDateTime(); if( stats.completed ) endTime = gridDetails->Cells[COL_VALUE][ROW_END_TIME]; lblTransferTime->Caption = GetDurationString( endTime, startTime ); __int64 duration = SecondsBetween( endTime, startTime ); if( duration > 0 ) { lblFilesPerSec->Caption = FormatNumeric( (double)stats.processedFiles / duration, 3 ); lblBytesPerSec->Caption = FormatNumeric( (double)stats.processedBytes / duration, 3 ); } else { lblFilesPerSec->Caption = FormatNumeric( (double)stats.processedFiles, 3 ); lblBytesPerSec->Caption = FormatNumeric( (double)stats.processedBytes, 3 ); } if( !stats.completed ) { __int64 completion = SecondsBetween( stats.endTime, startTime ); if( completion > 0 ) { if( duration > completion ) { percentDone = 100; } else { int timePercent = (((duration % completion)*100) / completion); if( timePercent > percentDone ) { percentDone = timePercent; } } } } } catch( EConvertError & e ) { // If we're here, it means we have a case where we are accessing // an in-progress task after a program crash (end time will fail to convert // as it is just text). We wont know the end time in anycase, so just // ignore it and display "N/A"s. } catch( EDatabaseError & e ) {} // Ignore } // Set the progress bar if required. Take the max between files remaining // (if not recurring task), and the time remaining till completion. if( !stats.completed ) { if( percentDone > 100 ) percentDone = 100; lblPercent->Caption = IntToStr( percentDone ) + "%"; pbCompleted->Position = percentDone; } } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::gridRowResized( TObject *Sender, int RowColnr) { // Ensure row height is not less than the font size int minHistoryRowSize = (gridHistory->Font->Size + 5); if( gridHistory->DefaultRowHeight < minHistoryRowSize ) { gridHistory->DefaultRowHeight = minHistoryRowSize; } int minDetailsRowSize = (gridDetails->Font->Size +5); if( gridDetails->DefaultRowHeight < minDetailsRowSize ) { gridDetails->DefaultRowHeight = minDetailsRowSize; } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::btnCloseClick(TObject *Sender) { Visible = false; } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::btnRefreshClick(TObject *Sender) { RefreshHistory(); } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::TimerTick(TObject *Sender) { if( (tabDetails->ActivePageIndex == 1) && qryTransferHistory->Active ) { int status = gridHistory->DataSource->DataSet->FieldByName("status")->AsInteger; if( (status == UdTransferPending || status == UdTransferInProgress) ) { UpdateTransferStatistics(); } } } //--------------------------------------------------------------------------- void __fastcall TUdTransferHistoryForm::RefreshHistory() { qryTransferHistory->Active = false; qryTransferHistory->Active = true; /* if( gridHistory-> ) { gridHistory->CurrentCell->MoveTo( 1, 1 ); } LoadTransferDetails(); LoadTransferStatistics(); */ }