Subversion Repositories DevTools

Rev

Blame | Last modification | View Log | RSS feed

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "UdTransferHistory.h"
#include "UdTransfer.h"
#include "DataModule.h"
#include <dateutils.hpp>
//---------------------------------------------------------------------------
#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();
*/
}