unit Import;
{ ==============================================================
  WinWCP - General purpose Binary/ASCII data file import module
  (c) J. Dempster, University of Strathclyde, 1997
  15/12/97
  ==============================================================}

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, TabNotBk, maths, shared, global, fileio;

type
  TImportFrm = class(TForm)
    bOK: TButton;
    bCancel: TButton;
    tnImportType: TTabbedNotebook;
    mePreview: TMemo;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label4: TLabel;
    Label6: TLabel;
    edIgnore: TEdit;
    EdDt: TEdit;
    edNumChannels: TEdit;
    EdTimeColumn: TEdit;
    cbTUnits: TComboBox;
    Label5: TLabel;
    Label3: TLabel;
    edNumSamples: TEdit;
    GroupBox2: TGroupBox;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    edNumFileHeaderBytes: TEdit;
    edDTBinary: TEdit;
    EdNumChannelsBinary: TEdit;
    EdScaleBy: TEdit;
    cbTUnitsBinary: TComboBox;
    EdNumSamplesBinary: TEdit;
    Label13: TLabel;
    EdOffsetBy: TEdit;
    Label14: TLabel;
    edNumRecordHeaderBytes: TEdit;
    procedure FormShow(Sender: TObject);
    procedure bOKClick(Sender: TObject);
    procedure cbTUnitsChange(Sender: TObject);
    procedure edNumChannelsKeyPress(Sender: TObject; var Key: Char);
    procedure edNumSamplesKeyPress(Sender: TObject; var Key: Char);
    procedure EdNumSamplesBinaryKeyPress(Sender: TObject; var Key: Char);
  private
    { Private declarations }
    procedure InspectASCIIFile ;
    procedure ImportASCIIData ;
    procedure ImportBinaryData ;
  public
    { Public declarations }
    FileName : string ;
  end;

var
  ImportFrm: TImportFrm;

implementation

{$R *.DFM}

var
   MaxValues : Array[0..20] of single ;

procedure TImportFrm.FormShow(Sender: TObject);
{ -----------------------------------
  Display first 10 lines of data file
  -----------------------------------}
begin

     cbTUnits.Clear ;
     cbTUnits.Items.Add('s') ;
     cbTUnits.Items.Add('ms') ;
     cbTUnits.Items.Add('us') ;
     cbTUnits.ItemIndex := 1 ;

     cbTUnitsBinary.Clear ;
     cbTUnitsBinary.Items.Add('s') ;
     cbTUnitsBinary.Items.Add('ms') ;
     cbTUnitsBinary.Items.Add('us') ;
     cbTUnitsBinary.ItemIndex := 1 ;

     InspectASCIIFile ;
         end ;


procedure TImportFrm.InspectASCIIFile ;
{ ----------------------------------------------
  Inspect the data file assuming an ASCII format
  ----------------------------------------------}
var
   F : TextFile ;
   i,Col,nCols,nColsOld,nSamples,MaxSamples : Integer ;
   nLines : LongInt ;
   T,TOld : single ;
   Values : Array[0..20] of Single ;
   s : string ;
begin

     { Open file for reading as text }
     AssignFile( F, ImportFrm.FileName ) ;
     Reset(F) ;

     { Initialise counters }
     for Col := 0 to High(MaxValues) do MaxValues[Col] := -1E30 ;
     nSamples := 0 ;
     MaxSamples := 0 ;
     nLines := 0 ;
     nColsOld := 0 ;

     mePreview.Clear ;
     while not EOF(F) do begin
         { Read a line of text from file }
         ReadLn( F,s ) ;
         { Display first 10 lines into memo box }
         if nLines < 10 then mePreview.Lines.Add( s ) ;
         Inc(nLines) ;

         { Number of columns }
         nCols := ExtractListOfFloats( s, Values, False ) ;
         if nCols = nColsOld then begin
            edNumChannels.text := format( ' %d ', [nCols-1] ) ;
            end ;
         nColsOld := nCols ;

         { Determine maximum absolute values within data columns }
         for Col := 0 to nCols-1 do begin
              MaxValues[Col] := MaxFlt( [Abs(Values[Col]),MaxValues[Col]] ) ;
              end ;

         { Sampling interval }
         if nCols > 1 then begin
            Inc(nSamples) ;
            MaxSamples := MaxInt( [nSamples,MaxSamples] ) ;
            T := Values[0] ;
            edDT.text := format( ' %.4g %s', [T-TOld, cbTUnits.text] ) ;
            TOld := T ;
            edTimeColumn.text := ' 1 ' ;
            if T < Told then nSamples := 0 ;
            end ;

         end ;

     edNumChannels.text := format( ' %d ', [nCols-1] ) ;
     MaxSamples := MakeMultiple( MaxSamples, 256, 1 ) ;
     edNumSamples.text := format( ' %d ',[MaxSamples]) ;

     CloseFile(F) ;
     end;


procedure TImportFrm.bOKClick(Sender: TObject);
{ ---------------------
  Import data from file
  ---------------------}
begin

     if tnImportType.Pages[tnImportType.PageIndex] = 'ASCII' then ImportASCIIData
                                                             else ImportBinaryData
     end;

procedure TImportFrm.ImportASCIIData ;
{ ----------------------
  Import ASCII data file
  ----------------------}
var
   F : TextFile ;
   Values,Scale : Array[0..20] of Single ;
   s : string ;
   Col,nCols,TCol,n,nSamples,NumSamplesPerRecord,ch,i : Word ;
   T,TOld,TScale : single ;
   RH : ^TRecHeader ;
   Buf : ^TIntArray ;
   SaveRecord : Boolean ;
begin
     try
        { Create buffer to hold samples }
        New(Buf) ;
        New(RH) ;

        { Close existing WCP data file }
        if RawFH.FileHandle > 0 then FileClose( RawFH.FileHandle ) ;
        { Create name of WCP file to hold ASCII data file }
        RawFH.FileName := ReplaceFileEnding( ImportFrm.FileName, '.wcp' ) ;
        { Create a new WCP file to hold converted data }
        RawFH.FileHandle := FileCreate( RawFH.FileName ) ;

        { Define size of WCP record }

        RawFH.NumRecords := 0 ;
        RawFH.NumSamples := ExtractInt( edNumSamples.text ) ;
        { Number of signal channels }
        RawFH.NumChannels := ExtractInt( edNumChannels.text ) ;
        if RawFH.NumChannels > (ChannelLimit+1) then begin
           MessageDlg( format(' Only %d channels allowed',[ChannelLimit+1]),
                           mtWarning, [mbOK], 0 ) ;
           RawFH.NumChannels := ChannelLimit+1 ;
           end ;

        { Number of samples / channel }
        NumSamplesPerRecord := RawFH.NumSamples*RawFH.NumChannels ;
        if NumSamplesPerRecord > (MaxTBuf+1) then begin
           RawFH.NumSamples := (MaxTBuf+1) div RawFH.NumChannels ;
           RawFH.NumSamples := MakeMultiple( RawFH.NumSamples, 256, 0 ) ;
           NumSamplesPerRecord := RawFH.NumSamples*RawFH.NumChannels ;
           MessageDlg(
           format(' Record size exceeded. Samples/channel=%d',[RawFH.NumSamples]),
                           mtWarning, [mbOK], 0 ) ;
           end ;

        { Open file for reading as text }
        AssignFile( F, ImportFrm.FileName ) ;
        Reset(F) ;

        nCols := RawFH.NumChannels + 1 ;
        TCol := ExtractInt(edTimeColumn.text) - 1 ;
        if TCol >= 0 then Inc(nCols) ;

        RawFH.ADCVoltageRange := 1.0 ;
        Ch := 0 ;
        for Col := 0 to nCols-1 do if Col <> TCol then begin
            Scale[Col] := MaxADCValue / (1.1*MaxValues[Col]) ;
            Channel[Ch].ADCScale := 1.0 / Scale[Col] ;
            Channel[ch].ADCCalibrationFactor := RawFH.ADCVoltageRange /
                          ( Channel[ch].ADCScale * (MaxADCValue+1) ) ;
            Inc(Ch) ;
            end ;

        { Channel calibration and scale factor settings }
        for ch := 0 to RawFH.NumChannels-1 do begin
            Channel[ch].ChannelOffset := ch ;
            Channel[ch].ADCAmplifierGain := 1. ;
            Channel[ch].ADCZero := 0 ;
            Channel[ch].ADCUnits := '' ;
            Channel[ch].ADCName := Format( 'Ch.%d', [ch] ) ;
            end ;

        { Set time conversion factor }
        if cbTUnits.Items[cbTUnits.ItemIndex] = 's' then TScale := 1.0
        else if cbTUnits.Items[cbTUnits.ItemIndex] = 'ms' then TScale := 1E-3
        else TScale := 1E-6 ;
        RawFH.dt := ExtractFloat(edDT.text,1.0)*TScale ;

        { Import ASCII data into .WCP file format }
        Reset(F) ;
        nSamples := 0 ;
        TOld := 0. ;
        for i := 0 to NumSamplesPerRecord-1 do Buf^[i] := 0 ;
        SaveRecord := False ;

        while (not EOF(F)) do begin

          { Read in a row of text }
          ReadLn( F,s ) ;
          { Extract samples from  row }
          nCols := ExtractListOfFloats( s, Values, False ) ;

          { Determine if a new record is needed based upon time column }
          if TCol >= 0 then begin
              T := Values[TCol] ;
              {If time value has decreased, create a new record }
              if T < Told then SaveRecord := True ;
              TOld := T ;
              end ;

          { If at end of file, put last samples into buffer and request save }
          if EOF(F) then begin
             for Col := 0 to nCols-1 do if (Col <> TCol) then begin
                 Buf^[nSamples] := Trunc( Values[Col]*Scale[Col] ) ;
                 Inc(nSamples) ;
                 end ;
             SaveRecord := True ;
             end ;

          { Save record to file when requested }
          if SaveRecord then begin
             Inc(RawFH.NumRecords) ;
             rH^.Status := 'ACCEPTED' ;
             rH^.RecType := 'TEST' ;
             rH^.Number := RawFH.NumRecords ;
             rH^.Time := rH^.Number ;
             rH^.dt := RawfH.dt ;
             rH^.Ident := ' ' ;
             for ch := 0 to RawFH.NumChannels-1 do rH^.ADCVoltageRange[ch] :=
                                                   RawFH.ADCVoltageRange ;
             rH^.Equation.Available := False ;
             rH^.Analysis.Available := False ;

             PutRecord( RawfH, rH^, RawfH.NumRecords, Buf^ ) ;
             { Clear buffer }
             for i := 0 to NumSamplesPerRecord-1 do Buf^[i] := 0 ;
             nSamples := 0 ;
             SaveRecord := False ;
             end ;

          { Normal update of binary data buffer }
          for Col := 0 to nCols-1 do if (Col <> TCol) then begin
              Buf^[nSamples] := Trunc( Values[Col]*Scale[Col] ) ;
              Inc(nSamples) ;
              end ;

          { Request a new record when record is full }
          if (nSamples >= NumSamplesPerRecord) then SaveRecord := True ;

          end ;

        SaveHeader(RawFH) ;
        { Close and re-open WCP file }
        FileClose( RawFH.FileHandle ) ;
        RawFH.FileHandle := FileOpen( RawFH.FileName, fmOpenReadWrite ) ;

        WriteToLogFile( 'ASCII Data File : ' + ImportFrm.FileName ) ;
        WriteToLogFile( 'converted to WCP file : ' + RawFH.FileName ) ;

     finally
          Dispose(Buf) ;
          Dispose(RH) ;
          end ;

     end ;


procedure TImportFrm.ImportBinaryData ;
{ ----------------------
  Import binary data file
  ----------------------}
var
   NumSamplesPerRecord,NumBytesPerRecord,ch,i : Word ;
   Filehandle : Integer ;
   FilePointer, StartAt,NumRecordHeaderBytes : LongInt ;
   TScale,ScaleBy,OffsetBy : single ;
   RH : ^TRecHeader ;
   Buf : ^TIntArray ;
   Done : Boolean ;
begin
     try
        { Create buffer to hold samples }
        New(Buf) ;
        New(RH) ;

        { Close existing WCP data file }
        if RawFH.FileHandle > 0 then FileClose( RawFH.FileHandle ) ;
        { Create name of WCP file to hold ASCII data file }
        RawFH.FileName := ReplaceFileEnding( ImportFrm.FileName, '.wcp' ) ;
        { Create a new WCP file to hold converted data }
        RawFH.FileHandle := FileCreate( RawFH.FileName ) ;

        { Define size of WCP record }

        RawFH.NumRecords := 0 ;
        RawFH.NumSamples := ExtractInt( edNumSamplesBinary.text ) ;
        { Number of signal channels }
        RawFH.NumChannels := ExtractInt( edNumChannelsBinary.text ) ;
        if RawFH.NumChannels > (ChannelLimit+1) then begin
           MessageDlg( format(' Only %d channels allowed',[ChannelLimit+1]),
                           mtWarning, [mbOK], 0 ) ;
           RawFH.NumChannels := ChannelLimit+1 ;
           end ;

        NumSamplesPerRecord := RawFH.NumSamples*RawFH.NumChannels ;
        NumBytesPerRecord := NumSamplesPerRecord*2 ;

        RawFH.ADCVoltageRange := 1.0 ;

        { Channel calibration and scale factor settings }
        for ch := 0 to RawFH.NumChannels-1 do begin
            Channel[ch].ChannelOffset := ch ;
            Channel[Ch].ADCScale := 1.0 ;
            Channel[ch].ADCAmplifierGain := 1. ;
            Channel[ch].ADCCalibrationFactor := RawFH.ADCVoltageRange /
                          ( Channel[ch].ADCScale * (MaxADCValue+1) ) ;
            Channel[ch].ADCZero := 0 ;
            Channel[ch].ADCUnits := '' ;
            Channel[ch].ADCName := Format( 'Ch.%d', [ch] ) ;
            end ;

        { Set sampling interval }
        if cbTUnitsBinary.Items[cbTUnits.ItemIndex] = 's' then TScale := 1.0
        else if cbTUnitsBinary.Items[cbTUnits.ItemIndex] = 'ms' then TScale := 1E-3
        else TScale := 1E-6 ;
        RawFH.dt := ExtractFloat(edDTBinary.text,1.0)*TScale ;

        { Open import file }
        FileHandle := FileOpen( ImportFrm.FileName, fmOpenRead ) ;

        StartAt := ExtractInt( edNumFileHeaderBytes.text ) ;
        FilePointer := FileSeek( FileHandle, StartAt, 0 ) ;

        { Import records }

        ScaleBy := ExtractFloat( edScaleBy.text, 1.0 ) ;
        OffsetBy := ExtractFloat( edOffsetBy.text, 0.0 ) ;
        NumRecordHeaderBytes := ExtractInt( edNumRecordHeaderBytes.text ) ;
        Done := False ;
        while not Done do begin

              { Skip over record header block }
              FilePointer := FileSeek( FileHandle, NumRecordHeaderBytes, 1 ) ;
              { Read sample data }
              if FileRead(FileHandle,Buf^,NumBytesPerRecord)
                 = NumBytesPerRecord then begin

                 { Do scaling and offset }
                 for i := 0 to NumSamplesPerRecord-1 do
                     Buf^[i] := Trunc(Buf^[i]*ScaleBy + OffsetBy) ;

                 { Save record to .WCP file }
                 Inc(RawFH.NumRecords) ;
                 rH^.Status := 'ACCEPTED' ;
                 rH^.RecType := 'TEST' ;
                 rH^.Number := RawFH.NumRecords ;
                 rH^.Time := rH^.Number ;
                 rH^.dt := RawfH.dt ;
                 rH^.Ident := ' ' ;
                 for ch := 0 to RawFH.NumChannels-1 do rH^.ADCVoltageRange[ch] :=
                                                       RawFH.ADCVoltageRange ;
                 rH^.Equation.Available := False ;
                 rH^.Analysis.Available := False ;

                 PutRecord( RawfH, rH^, RawfH.NumRecords, Buf^ ) ;
                 end
              else Done := True ;
              end ;

        { Close import file }
        FileClose( FileHandle ) ;

        SaveHeader(RawFH) ;
        { Close and re-open WCP file }
        FileClose( RawFH.FileHandle ) ;
        RawFH.FileHandle := FileOpen( RawFH.FileName, fmOpenReadWrite ) ;

        WriteToLogFile( 'Binary Data File : ' + ImportFrm.FileName ) ;
        WriteToLogFile( 'converted to WCP file : ' + RawFH.FileName ) ;

     finally
          Dispose(Buf) ;
          Dispose(RH) ;
          end ;

     end ;


procedure TImportFrm.cbTUnitsChange(Sender: TObject);
var
   dt : single ;
begin
     dt := ExtractFloat( edDT.text, 1.0 ) ;
     edDT.text := format( ' %.4g %s', [dt,cbTUnits.text] ) ;
     end;


procedure TImportFrm.edNumChannelsKeyPress(Sender: TObject; var Key: Char);
var
   n : Integer ;
begin
     if key = chr(13) then begin
        n := ExtractInt( TEdit(Sender).text ) ;
        n := IntLimitTo( n, 1, ChannelLimit+1 ) ;
        TEdit(Sender).text := format( ' %d ', [n] ) ;
        end ;
     end;



procedure TImportFrm.edNumSamplesKeyPress(Sender: TObject; var Key: Char);
var
   nSamples,nChannels,NumSamplesPerRecord : Word ;
begin
     if key = chr(13) then begin
        nSamples := ExtractInt( TEdit(Sender).text ) ;
        nChannels := ExtractInt(edNumChannels.text) ;
        NumSamplesPerRecord := nSamples*nChannels ;
        if NumSamplesPerRecord > (MaxTBuf+1) then
           nSamples := (MaxTBuf+1) div nChannels ;
        nSamples := MakeMultiple( nSamples, 256, 0 ) ;
        TEdit(Sender).text := format( ' %d ', [nSamples] ) ;
        end ;
     end;

procedure TImportFrm.EdNumSamplesBinaryKeyPress(Sender: TObject;
  var Key: Char);
var
   nSamples,nChannels,NumSamplesPerRecord : Word ;
begin
     if key = chr(13) then begin
        nSamples := ExtractInt( TEdit(Sender).text ) ;
        nChannels := ExtractInt(edNumChannelsBinary.text) ;
        NumSamplesPerRecord := nSamples*nChannels ;
        if NumSamplesPerRecord > (MaxTBuf+1) then
           nSamples := (MaxTBuf+1) div nChannels ;
        nSamples := MakeMultiple( nSamples, 256, 0 ) ;
        TEdit(Sender).text := format( ' %d ', [nSamples] ) ;
        end ;
     end;

end.
