| 0 comments ]

Re-Share This Post: Technorati Digg This Stumble Stumble Facebook Twitter
First of all, a 'datastore' is a generic term to refer to a location where data is stored with no bearing on the type of storage used. It;s just a place where data is or needs to be stored. With properly thought out design and planning, this concept can easily be applied to mIRC windows.

Why is this useful?
Access via prestored data in a window is faster than repeatedly reading a disk file sequentially. It also presents opportunities in which the data can be filtered using mIRC services directly allowing subsets of data with known characteristics to be extracted while preserving performance (and sanity).
Loading the data
When approaching data, try to devise a method in which the data is dealt with in a batch all at once. This is always preferable over 'item at a time' processing which will almost always be the most time consuming method in which to massage data. mIRC's FILTER and LOADBUF services are excellent methods with which to populate a window. The FILTER service can also be used to populate a dialog control as of 5.6.

For example, if we wanted to know how many files have been downloaded, a typical approach would be to open the STATUS.LOG file, read each line, process it if necessary, and go on to the next item. This is a fairly time consuming process when dealing with files which are quite large, however mIRC provides other services which are much better at solving the problem than this approach.

The first step is to perform some sort of analysis on the data to be worked with. Most of the time, the data being looked for has a characteristic which can be used to easily identify it. This can be used to pattern match against raw data to retrieve specific results, which in this example are DCC GETs which have completed. This is a perfect application of using windows to temporarily store results and process them later. The following code shows this technique:
gen_get_stats {

 %ggs_datastore = @ggs_GetsDatastore
 %ggs_infile    = $mircdirlogs\status.log
 %ggs_Looper    = 0

 window -c %ggs_datastore
 window -lh %ggs_datastore

 filter -fw %ggs_infile %ggs_datastore *DCC GET*COMPLETED*
 %ggs_total_matched = $filtered

 :ggs_TopLoop

 if %ggs_Looper < %ggs_total_matched {

  inc %ggs_Looper
  %ggs_this_item = $line(%ggs_datastore,%ggs_Looper)
  %ggs_cps       = $gettok(%ggs_this_item,9,32)
  %ggs_cps       = $mid(%ggs_cps,1,$calc($pos(%ggs_cps,cps,1)-1))
  %ggs_total_cps = $calc(%ggs_total_cps + %ggs_cps )

  GOTO ggs_TopLoop

}

 echo -s ----------------------------------------
 echo -s -
 echo -s - Downloads: %ggs_total_matched Avg CPS: $calc(%ggs_total_cps/%ggs_total_matched)
 echo -s -
 echo -s ----------------------------------------

 :Cleanup
 window -c %ggs_datastore
 unset %ggs_*
 RETURN

}
The section of code:
 %ggs_datastore = @ggs_GetsDatastore
 %ggs_infile    = $mircdirlogs\status.log
 %ggs_Looper    = 0
simply performs initialization. The next section:
 window -c %ggs_datastore
 window -lh %ggs_datastore
Closes the window if it already exists (just in case it was left open previously), and then recreates it with the List and Hidden attributes. The next section:
 filter -fw %ggs_infile %ggs_datastore *DCC GET*COMPLETED*
 %ggs_total_matched = $filtered
is the first real work of the script. The filter service scans the input file (status.log) for any lines which have DCC GET and COMPLETED in them and writes them to the datastore window (@ggs_GetsDatastore). The '%ggs_total_matched = $filtered' sets a variable to number of matching entries the filter method found.

NOTE: $filtered is always populated with the matches found after a FILTER command completes.

At this point, with the code explained so far, we have created a datastore of raw data which can be processed, which is each DCC GET which completed, and the CPS reported. The rest of the code processes the raw data for the results we are trying to generate:
 :ggs_TopLoop

 if %ggs_Looper < %ggs_total_matched {

  inc %ggs_Looper
  %ggs_this_item = $line(%ggs_datastore,%ggs_Looper)
  %ggs_cps       = $gettok(%ggs_this_item,9,32)
  %ggs_cps       = $mid(%ggs_cps,1,$calc($pos(%ggs_cps,cps,1)-1))
  %ggs_total_cps = $calc(%ggs_total_cps + %ggs_cps )

  GOTO ggs_TopLoop

 }
This section of code reads each item in the datastore and aggregates the total CPS. After the loop completes the final results are displayed:
 echo -s ----------------------------------------
 echo -s -
 echo -s - Downloads: %ggs_total_matched Avg CPS: $round($calc(%ggs_total_cps/%ggs_total_matched),2)
 echo -s -
 echo -s ----------------------------------------
As you can see, this process is much faster than the item at a time method mentioned previously. Whenever possible, batch data and filter the data and work with the subsets extracted.

As of mIRC 5.5, the log files are now time date stamped. The above example still applies with a few modifications to the code:
gen_file_stats {

 %gfs_datastore     = @gfs_GetsDatastore
 %gfs_Looper        = 0
 %gfs_log_dir       = $mircdirlogs\
 %gfs_search_target = status*.log
 %gfs_logs          = $findfile(%gfs_log_dir,%gfs_search_target,0)

 window -c %gfs_datastore
 window -lh %gfs_datastore

 :gfs_FileLoop

 if %gfs_Looper < %gfs_logs {

  inc %gfs_Looper
  %gfs_this_status_log = $findfile(%gfs_log_dir,%gfs_search_target,%gfs_Looper)
  filter -fw %gfs_this_status_log %gfs_datastore *DCC GET*COMPLETED*
  GOTO gfs_FileLoop

 }

 %gfs_total_matched = $line(%gfs_datastore,0)
 %gfs_Looper = 0

 :gfs_TopLoop

 if %gfs_Looper < %gfs_total_matched {

  inc %gfs_Looper
  %gfs_this_item = $line(%gfs_datastore,%gfs_Looper)
  %gfs_cps       = $gettok(%gfs_this_item,2,40)
  %gfs_cps       = $mid(%gfs_cps,1,$calc($pos(%gfs_cps,cps,1)-1))
  %gfs_cps       = $gettok(%gfs_cps,2,32)
  %gfs_total_cps = $calc(%gfs_total_cps + %gfs_cps)

  GOTO gfs_TopLoop

 }

 echo -s ----------------------------------------
 echo -s -
 echo -s - Downloads: %gfs_total_matched CPS: $calc(%gfs_total_cps / %gfs_total_matched)
 echo -s -
 echo -s ----------------------------------------

 :Cleanup
 window -c %gfs_datastore
 unset %gfs_*
 RETURN

 }
As shown, the overall approach has not changed, only modifications to extract the additional data. The additional code is:
 %gfs_log_dir       = $mircdirlogs\
 %gfs_search_target = status*.log
 %gfs_logs          = $findfile(%gfs_log_dir,%gfs_search_target,0)
This code is initializes variables for later use. Since we are processing multiple files, a call to the mIRC $FINDFILE service is necessary. To use the $FINDFILE service, additional parameters are needed, the target directory, and the wildcarded file name (status*.*). These have been assigned to %gfs_log_dir and %gfs_search_target respectively. %gfs_datastore and %gfs_Looper are used the same as in the previous example, however the new variable %gfs_logs uses the $FINDFILE service to determine the number of files to be processed.
 :gfs_FileLoop

 if %gfs_Looper < %gfs_logs {

  inc %gfs_Looper
  %gfs_this_status_log = $findfile(%gfs_log_dir,%gfs_search_target,%gfs_Looper)
  filter -fw %gfs_this_status_log %gfs_datastore *DCC GET*COMPLETED*
  GOTO gfs_FileLoop

 }
This section of code extracts data from the file directing the output to the datastore. The FILTER service does not clear the target window before populating with results, so as shown above, each file is filtered to the datastore window appending the results to any that currently exist. The differences between the $FINDFILE service calls should be noted as well. The first call to $FINDFILE passes a zero to the service indicating only a count of the files matching the request should be returned. The second call to $FINDFILE uses the %gfs_Looper variable to retrieve the actual file name which is to be processed on each iteration. See the mIRC help for a complete explanation of the $FINDFILE service.
 %gfs_total_matched = $line(%gfs_datastore,0)
This code returns the total number of lines found in the datastore. In the first example discussed, this was not necessary as we were processing a single file. The total matches found could have been aggregated using the $FILTERED identifier after each FILTER command as each file was processed, however this takes less code and is probably faster than trying to aggregate the value manually.
 :gfs_TopLoop

 if %gfs_Looper < %gfs_total_matched {

  inc %gfs_Looper
  %gfs_this_item = $line(%gfs_datastore,%gfs_Looper)
  %gfs_cps       = $gettok(%gfs_this_item,2,40)
  %gfs_cps       = $mid(%gfs_cps,1,$calc($pos(%gfs_cps,cps,1)-1))
  %gfs_cps       = $gettok(%gfs_cps,2,32)
  %gfs_total_cps = $calc(%gfs_total_cps + %gfs_cps)

  GOTO gfs_TopLoop

 }
This section of code is almost identical to that of the first example, however the method in which CPS is being extracted was changed. In the first example, $gettok(%gfs_this_item,2,32) was used instead of $gettok(%gfs_this_item,2,40). The original coding used spaces to determine where in the string CPS was to be extracted, and the new code scans for a left parenthesis which will be more accurate since it is possible to receive files where the names have spaces in them (i.e. birds on the beach.jpg). The original coding will almost certainly have problems in the future, so using the parenthesis as a locator instead of spaces will prove to be more reliable than using spaces as the delimiter.

The following is a practical example of extracting statistics from the status logs and aggregating a total number of file requests along with success/fail counts plus percentages about failed/completed transfers. It demonstrates subset data extraction through filtering for known data characteristics and deriving results based on those subsets. It also demonstrates how to process files in batch sizes to accommodate mIRC's limitation of approximately 32765 lines per window.

; Alias which does the main work.

gen_log_stats {

unset %gls_*

;Preprocess the status logs

%gls_log_dir = $mircdirlogs
%gls_search_target = status*.log
%gls_log_count = $findfile(%gls_log_dir,%gls_search_target,0)
%gls_LogLooper = 0
%gls_raw_dstore = @dstr_Raw
%gls_wrk_dstore = @dstr_Wrk
%gls_LogLooper = 0


window -c %gls_raw_dstore
window -c %gls_wrk_dstore
window -lh %gls_raw_dstore
window -lh %gls_wrk_dstore


:gls_LogLoop

if %gls_LogLooper < %gls_log_count {

inc %gls_LogLooper
%gls_this_file = $findfile(%gls_log_dir,%gls_search_target,%gls_LogLooper)
%gls_recs_raw = $calc( %gls_recs_raw + $lines( %gls_this_file ) )
%gls_chunks = $split_file_to_windows(@Work_Win_,%gls_this_file)
%gls_ChunkLooper = 0


echo -s Processing %gls_this_file

:gls_ChunkLoop

; Process each chunk of the log file

if %gls_ChunkLooper < %gls_chunks {

inc %gls_ChunkLooper
%gls_this_chunk = @Work_Win_ $+ %gls_ChunkLooper
%gls_raw_filter = *DCC GET*OF*FROM*
%gls_recs_raw = $calc(%gls_recs_raw + $line(%gls_this_chunk,0))


filter -cww %gls_this_chunk %gls_raw_dstore %gls_raw_filter
%gls_get_recs_raw = $calc(%gls_get_recs_raw + $filtered)


; Get the number of GETS which completed

filter -cww %gls_raw_dstore %gls_wrk_dstore *DCC GET*COMPLETED*

%gls_tot_get_completed = $filtered
%gls_grand_tot_get_complete = $calc(%gls_grand_tot_get_complete + %gls_tot_get_completed)


; Aggregate the CPS data from each DCC GET COMPLETED record.

%gls_WindowLooper = 0

:gls_GetWindowLoop

if %gls_WindowLooper < %gls_tot_get_completed {

inc %gls_WindowLooper

%gls_this_rec = $line(%gls_wrk_dstore,%gls_WindowLooper)
%gls_this_token = $mid(%gls_this_rec,$calc($pos(%gls_this_rec,%gls_token_id)+1),$len(%gls_this_rec))
%gls_this_token = $gettok(%gls_this_token,2,32)
%gls_this_token = $mid(%gls_this_token,1,$calc($pos(%gls_this_token,cps)-1))
%gls_tot_get_cps = $calc( %gls_tot_get_cps + %gls_this_token)


GOTO gls_GetWindowLoop

}

; Get the number of GETs which failed.

filter -cxww %gls_raw_dstore %gls_wrk_dstore *DCC GET*COMPLETED*
%gls_grand_tot_get_failed = $calc( %gls_grand_tot_get_failed + $filtered )


; Set up to process the SEND records

%gls_raw_filter = *DCC SEND*OF*TO*

; Determined number of SENDs completed

filter -cww %gls_this_chunk %gls_raw_dstore %gls_raw_filter
%gls_send_recs_processed = $line(%gls_this_chunk,0)
%gls_send_recs_raw = $calc(%gls_send_recs_raw + $filtered)


filter -cww %gls_raw_dstore %gls_wrk_dstore *DCC SEND*COMPLETED*
%gls_tot_send_completed = $filtered
%gls_grand_tot_send_complete = $calc(%gls_grand_tot_send_complete + %gls_tot_send_completed)


; Aggregate the CPS data from each DCC SEND COMPLETED record.

%gls_WindowLooper = 0

:gls_SendWindowLoop

if %gls_WindowLooper < %gls_tot_send_completed {

inc %gls_WindowLooper

%gls_this_rec = $line(%gls_wrk_dstore,%gls_WindowLooper)
%gls_this_token = $mid(%gls_this_rec,$pos(%gls_this_rec,$chr(40)),$len(%gls_this_rec))
%gls_this_token = $gettok(%gls_this_token,2,32)
%gls_this_token = $mid(%gls_this_token,1,$calc($pos(%gls_this_token,cps)-1))
%gls_tot_send_cps = $calc( %gls_tot_send_cps + %gls_this_token)


GOTO gls_SendWindowLoop

}

; Determine number of SENDs which failed.

filter -cxww %gls_raw_dstore %gls_wrk_dstore *DCC SEND*COMPLETED*
%gls_grand_tot_send_failed = $calc( %gls_grand_tot_send_failed + $filtered )


window -c %gls_this_chunk
GOTO gls_ChunkLoop


}

GOTO gls_LogLoop

}

; Generate output

%gls_filler = $str(-,45)
%gls_text_01 = -- Total Records Processed: $get_padding(4) %gls_recs_raw
%gls_text_02 = -- Total GET Records Extracted: $+ $get_padding(1) %gls_get_recs_raw
%gls_text_03 = -- Total GETs Completed Xfer: $get_padding(2) %gls_grand_tot_get_complete
%gls_text_04 = -- Total GETS Failed Xfer: $get_padding(5) %gls_grand_tot_get_failed
%gls_text_05 = -- Average GET CPS: $get_padding(12) $round( $calc( %gls_tot_get_cps / %gls_get_recs_raw ), 2)
%gls_text_06 = -- GET Xfer Percent Failed: $get_padding(4)
$round($calc( ( %gls_grand_tot_get_failed / %gls_get_recs_raw ) * 100 ),2)
%gls_text_07 = -- GET Xfer Percent Success: $get_padding(3)
$round($calc( ( %gls_grand_tot_get_complete / %gls_get_recs_raw ) * 100 ),2)
%gls_text_08 = -- Total SEND Records Extracted: %gls_send_recs_raw
%gls_text_09 = -- Total SENDs Completed Xfer: $get_padding(1) %gls_grand_tot_send_complete
%gls_text_10 = -- Total SENDS Failed Xfer: $get_padding(4) %gls_grand_tot_send_failed
%gls_text_11 = -- Average SEND CPS: $get_padding(11)
$round( $calc( %gls_tot_send_cps / %gls_send_recs_raw ), 2)
%gls_text_12 = -- SEND Xfer Percent Failed: $get_padding(3)
$round($calc( ( %gls_grand_tot_send_failed / %gls_send_recs_raw ) * 100 ),2)
%gls_text_13 = -- SEND Xfer Percent Success: $get_padding(2)
$round($calc( ( %gls_grand_tot_send_complete / %gls_send_recs_raw ) * 100 ),2)


echo -s %gls_filler
echo -s %gls_text_01
echo -s --
echo -s %gls_text_02
echo -s %gls_text_03
echo -s %gls_text_04
echo -s %gls_text_05
echo -s %gls_text_06
echo -s %gls_text_07
echo -s --
echo -s %gls_text_08
echo -s %gls_text_09
echo -s %gls_text_10
echo -s %gls_text_11
echo -s %gls_text_12
echo -s %gls_text_13
echo -s %gls_filler


window -c %gls_raw_dstore
window -c %gls_wrk_dstore
unset %gls_*


}

; The next two functions are required to make the gen_log_stats function work

get_padding {

var %gp_string
%gp_string = $chr(3) $+ 0,0 $+ $chr(32) $+ $chr(3)
%return_val = $str(%gp_string,$1)
RETURN %return_val


}

split_file_to_windows {

unset %sftw_*

%sftw_source_file = $2
%sftw_records = $lines(%sftw_source_file)
%sftw_chunk_size = 32765
%sftw_chunk_start = 1
%sftw_chunk_end = 32765
%sftw_number_of_chunks = $calc( %sftw_records / %sftw_chunk_size )
%sftw_Looper = 0
%sftw_window_name = $1


if %sftw_number_of_chunks < 1 { %sftw_number_of_chunks = 1 }
if $remainder(%sftw_number_of_chunks) != $null {
%sftw_number_of_chunks = $calc( $round( %sftw_number_of_chunks,0 ) + 1 )
}


:stl_TopLoop

if %sftw_Looper < %sftw_number_of_chunks {

inc %sftw_Looper

%sftw_chunk_start_window = %sftw_window_name $+ %sftw_Looper

%sftw_chunk_range = %sftw_chunk_start $+ - $+ %sftw_chunk_end

if $window(%sftw_chunk_start_window) != $null { window -c %sftw_chunk_start_window }
window -lh %sftw_chunk_start_window


loadbuf %sftw_chunk_range %sftw_chunk_start_window %sftw_source_file
%sftw_chunk_start = $calc(%sftw_Looper * %sftw_chunk_size ) + %sftw_Looper
%sftw_chunk_end = $calc(%sftw_chunk_start ) + %sftw_chunk_size


GOTO stl_TopLoop

}

%return_val = %sftw_number_of_chunks
unset %sftw_*
RETURN %return_val


}

A few final notes on the above example.

Two additional procedures have been provided: split_file_to_windows and get_padding. split_file_to_windows splits a text file into chunks of 32765 or less, and returns how many windows it took to completely load the file. get_padding returns white space (text which is not visible) to format the final display. Examination of the example code should show how they are being used.

There are two additional options used on the filter command that were not covered in the above text: -x and -c. The –c option clears the target window before filling it with results. The –x option is a not operator, i.e. 'anything that doesn't match the filter target'.

The method in which the CPS is extracted from the data item was changed again. Since it is possible for a filename to include a parenthesis, the code was changed to scan for a space followed by a left parenthesis.

The statistics generated may not be 100% accurate in that the code doesn't account for transfers which did not record a final disposition (i.e. complete/failed) which can occur sometimes.

0 comments

Post a Comment

 
http://www.tsfaenterprise.com TSFA ENTERPRISE TSFA GAS OVEN AND HOME APPLIANCE IN PAKISTAN GAS OVEN GAS OVEN in pakistan Pakistan Home Appliance http://gasovenandhomeappliancesinpakistan.com/ TSFA ENTERPRISE TSFA GAS OVEN AND HOME APPLIANCE IN PAKISTAN GAS OVEN GAS OVEN in pakistan Pakistan Home Appliance