unit Sequenceu;

interface
uses
  SysUtils, Messages, Classes, {v0.30} Controls, Forms, XStringGrid,{/v0.30}
  ULRecTyp, ULRecUtl, ULObju, ULFObju, ULObjUsru,
  Fileu,
  ULIType, ULIObju, Instrumentu,
  ULMType, ULMObju, Methodu,
  AAPGType, AAPGObju, AAPLType, AAPLObju, AAPrgu,
  ULSQType, ULSQObju,
  ULSRType, ULSRObju,
  {v0.30}
  ULPRType, ULPRObju,
  {/v0.30}

  UlanType, Spectrum, AcqInfou
  {v0.30}, ActnList, Menus, Msgu, ShowMsg, Language, XGridFrm,
    SpecForm{/v0.30}
  {v0.36}
  , ULStringGrid, WinUtl
  {/v0.36}
  {v0.38}
  , Stru
  {/v0.38}
  {v0.41}, Timer, Timersu, UlanGlob, ExeLogu, FileCtrl, Modulu,
    ULADType, ULADObju, SModulu
  {/v0.41}
  {v0.47}
  , ModuUtl, Compareu {winutl}
  {/v0.47}
  ;

{urSeqXXXX}
const
  urSeq = 9000;
  urSeqMethodExists = urSeq + 1;
  urSeqInstrumentExists = urSeq + 2;
  urSeqPrgExists = urSeq + 3;
  {v0.41}
  urSetSequenceStateInvalid = urSeq + 4;

  urInvalidProgramNoEquil = urSeq + 5;
  urNoSamples = urSeq + 6;
  {/v0.41}
{/urSeqXXXX}
type
  TSequenceResult = integer;

type
  {v0.30}
  TScanReason = (srResultTable, srCalibrationFile, srOpen{v0.38}, srOpenOverlay{/v0.38});
  {/v0.30}

  TSeqSample = class;

  TSequence = class(TULObjUsr) {ulobju ulobjusru}
  private
    FLastSample: TSeqSample;
    FCurSample: TSeqSample;

    FDefMethod: TUlanMethod;
      { default method for new sample }
    FDefInstrument: TInstrument;
      { default instrument for new sample }
    FDefPrg: TAAPrg;
      { default program for new sample }
      { ulsqtype }
    {v0.30}
    FMakeTable: TAction;
    FCalibrationFileAssign: TAction;
    FDataFilesOpen: TAction;
    {v0.38}
    FDataFilesOpenOverlay: TAction;
    {/v0.38}

    FCurGrid: TXStringGrid;
    FGridValueCaption: string;
    FCurScanULSR: TULSRObj;
    FCurScanAcqData: TAcqData;
    FCurCalibFN: string;
    {/v0.30}
    {v0.38}{ulobju}
    {FDeleteFiles: boolean;}
      { delete files corresponding to deleting ulsr? }
    FCurSpecForm: TSpectrumForm;
    {/v0.38}
    {v0.44}
    FClearSampleErrorAction: TAction;
    {/v0.44}
  protected
    function GetRecID: TULRecID; override;
    function ChildCreate(AChildObj: TULObj): TULObjUsr; override;
    procedure ChildDestroyed(AChild: TULObjUsr); override;
    procedure ClassFieldsCreate; override;

    function GetULSQ: TULSQObj;
    function GetDefMethod: TUlanMethod;
    function GetDefInstrument: TInstrument;
    function GetDefPrg: TAAPrg; {v0.41}virtual;{/v0.41}
    function GetLastSample: TSeqSample;
    function GetCurSample: TSeqSample;
    function GetNextSample: TSeqSample;
    procedure SeqSampleCreated(ss: TSeqSample);
      { update new SeqSample default values }
    function GetDirName: string;{v0.41}virtual;{/v0.41}
    {v0.30}
    procedure MenuActionNeeded;override;
    procedure MakeResultTable(Sender: TObject);
    procedure CalibrationFileAssign(Sender: TObject);
    procedure DataFilesOpen(Sender: TObject);
    {v0.38}
    procedure DataFilesOpenOverlay(Sender: TObject);
    {/v0.38}

    procedure ChildsScan(sr: TScanReason);
      procedure ResultTableAddRow;
      procedure CalibFNAssign;
      procedure DataFileOpen{v0.38}(Aditive:boolean){/v0.38};
    {/v0.30}
    {v0.32}
    procedure FileNameChanged;override;
    {/v0.32}
    {v0.36}{ulobjusru}
    procedure ULGridCreated(AGrid: TULStringGrid); override;
    procedure DoOnDblClick(AGrid: TObject);
    procedure SetCurSample(ASample: TSeqSample);
    {/v0.36}
    {v0.44}
    procedure FirstMenuActionNeeded; override;
    procedure ClearSampleError(Sender: TObject);
    {/v0.44}
  public
    destructor Destroy;override;
    procedure UpdateDefDirs; override;
    function First: boolean;
      { select first sample as current }
    function Next: boolean;
      { select next sample as current }
    {v0.36}
    procedure NoSample;
    {/v0.36}

    property ULSQ: TULSQObj read GetULSQ;
    property CurSample: TSeqSample read GetCurSample{v0.36} write SetCurSample{/v0.36};
    property LastSample: TSeqSample read GetLastSample;
    property NextSample: TSeqSample read GetNextSample;
    property DefMethod: TUlanMethod read GetDefMethod;
    property DefInstrument: TInstrument read GetDefInstrument;
    property DefPrg: TAAPrg read GetDefPrg;
    property DirName: string read GetDirName;
      { Default (full) dir for files of samples = dir of .ULS file + SequenceName }
  end;
  {v0.41}
  TSequenceClass = class of TSequence;
  {/v0.41}

  TSeqSample = class(TULObjUsr)
  private
    FInstrument: TInstrument;
    FMethod: TUlanMethod;
    FPrg: TAAPrg;
  protected
    function GetULSR: TULSRObj;
    procedure ClassFieldsCreate; override;
    function ChildCreate(AChildObj: TULObj): TULObjUsr; override;
    procedure ChildDestroyed(AChild: TULObjUsr); override;
    function GetRecID: TULRecID; override;
  public
    constructor Create(AOwner: TULObjUsr; AObj: TULObj; ARecID: TULRecID); reintroduce;
    destructor Destroy;override;
    {ulobjusru}
    property ULSR: TULSRObj read GetULSR;
    property Method: TUlanMethod read FMethod;
    property Instrument: TInstrument read FInstrument;
    property Prg: TAAPrg read FPrg;
  end;

{v0.41}
  TSeqFormState = (
    sfsIdle, { nothing to do - does not change the last sample name when set }
    sfsLoadSample, { user should load sample }
    sfsLoaded, { uses is informed that the sample was loaded }
    sfsInjectSample, { user should inject sample }
    sfsInjected,     { uses is informed that the sample was injected }
    sfsLoadAndInjectSample, { user should load and inject sample }
    sfsFinished, { user is informed about sequence finished=completed OK }
    sfsAborted   { user is informed about sequence abortion=failure }
  );

{  TSampleState = (
  );}
  TSequenceState = (
    sesDisconnected, { devices disconnected }
    sesAborted,      { sequence run aborted, devices off }
    sesFinished,     { sequence run finished by processing last sample, devices off }
    sesStandby,      { sequence run not started yet or stopped upon user request, devices off
                       (not finished) }
    sesOff,          { .. not real state, just for comparing, all below = OFF }
    sesStarting,     { sequence run requested, devices preparing for run }
    sesStopping,     { sequence run stopping upon user request, devices stopping }
    sesFinishing,    { sequence run finishing after last sample processed, devices stopping }
    sesAborting,     { sequence run aborting because of error, devices aborting }
    sesReady,        { sequence is ready to run (devices ready), devices ready to run }
    sesStopRunning,  { running, but after processing current sample Stopping will start
                       (user requested stop after current sample) }
    sesRunning       { sequence is running }
    ); { ... same as AAAType.TAAAState }

  TSSequence = class(TSequence) {standalone sequence}
  private
    FLogHead: string;
    {v0.47}{/v0.47
    FAutomatic: boolean;}
      { working with autosampler if true }
    {FDataTemplateFileName: TFileName;}
    FIsInTimer: boolean;

    FSequenceState: TSequenceState;
    FStateStartTime: integer;
    FUsrStateTimeOffset: integer;
    FStateTime: integer;

    FSubState: integer;
    {FSubStateStartTime: integer;
    FUsrSubStateTimeOffset: integer;
    FSubStateTime: integer;}

    FTimerAdded: boolean;

    {FIsFirstSample: boolean;}

    {actions}
    FSequenceRunAction: TAction;
    {FSequenceStopAction: TAction;
    FNulAction: TAction;}
    FPrgEditAction: TAction;
    FMethodEditAction: TAction;
    FDoAcqShowAction: TAction;
    FShowActPrgAction: TAction;
    {/actions}

    FSeqForm: TForm;
      { comunicates with usr }
    FCurPrg: TAAPrg;
    FBrowsingCurPrg: TAAPrg;{ non nil, if for FCurPrg was called ShowRunning }

    FAcqInfo: TAcqInfo;
    FAcqData: TAcqData;

    FDataTemplate: TAcqData;
    FPump: TPumpModule;
    FPumpTimerLogged: boolean;
    {v0.47}
    FAutoSampler: TAutoSamplerModule;
    FAutoSamplerChecked: boolean;
      { already tried to find autosampler }
    {/v0.47}

    {action handlers}
    procedure SequenceRun(Sender: TObject);
    {procedure SequenceStop(Sender: TObject);}
    procedure PrgEdit(Sender: TObject);
    procedure MethodEdit(Sender: TObject);
    procedure DoAcqShow(Sender: TObject);
    procedure ShowActPrgAction(Sender: TObject);
    {/action handlers}

    procedure SetSequenceState(ASequenceState: TSequenceState);
    procedure TimerAdd;
    procedure TimerDel;

    procedure DoCurSampleSelected;
    procedure CurSampleStateChanged;
    procedure SeqFormFree;
    procedure SeqFormDoTimer;
    procedure SeqFormCheck(sfs: TSeqFormState; const ASampleName: string);
    procedure SetCurPrg(APrg: TAAPrg);
    procedure StateStart(ASequenceState: TSequenceState; AUsrTimeOffset: integer);
    function GetDataTemplateFileName: TFileName;
    procedure SetDataTemplateFileName(const AFileName: TFileName);

    procedure DefMethodSetDefaults;
    procedure DefPrgCreateDefault;

    function GetNewSequenceName: string;
    procedure CheckDir;
    procedure OnGetWindowCaption(var ACaption: string);
    procedure ShowRunningPrg;
    function GetDataTemplate: TAcqData;
    function GetSampleToLoad: TSeqSample;
    function GetPump: TPumpModule;
    {v0.47}
    function GetAutoSampler: TAutoSamplerModule;
    function AutoSamplerLoad(AVialNr: integer): boolean;
    function AutoSamplerInject: boolean;
    function GetAutomatic: boolean;
    {/v0.47}
    procedure OnMark(Sender: TObject);
    procedure PumpPrgRun(const msg: string);
    procedure PumpPrgEnd(const msg: string);
    procedure PumpPrgActNrSelect(i:integer);
    procedure PumpStartOrRun(const msg: string);

  protected
    procedure DoAfterCreate; override;
    procedure ClassFieldsCreate; override;
    procedure DoTimer(Sender: TObject);{register in timersu}
      procedure DoTimerRunningStopRunning;


    procedure FirstMenuActionNeeded; override;{ulobjusru}
    procedure Log(const msg: string);
    procedure LogState(const msg: string);
    procedure ObjUpdated; override;
    function CheckCurSample: boolean;
    function FirstSampleRun: TSequenceResult;{ulantype}


    function AcqEquilStart: boolean;
    procedure AcqEquilStop;

    function AcqGetReady: boolean;
    function AcqInfoCreate: boolean;
    function AcqStart: boolean;
    procedure AcqDoTimer;
    procedure AutoSaveAll;
    procedure AcqShow;
    procedure AcqDataUpdateViewLimit;
    procedure AcqStop;

    procedure DoLoadSample;
    procedure DoInjectSample;

    function GetDirName: string;override;
    function GetDefPrg: TAAPrg; override;

    function GetSequenceStateStr(ASequenceState:TSequenceState): string;
    function SequenceStateStr: string;
  public
    constructor Load(const AFileName: string); override;
    destructor Destroy; override;
    procedure FileNew; override;

    procedure DoOK;
      { called from SeqForm when it's OK button pressed }
      procedure DoConfirm;
        { called either from DoOK or from OnMark }
    procedure DoCancel;
      { called from SeqForm when it's Cancel button pressed }


    property SequenceState: TSequenceState read FSequenceState write SetSequenceState;
    property CurPrg: TAAPrg read FCurPrg write SetCurPrg;
    property DataTemplateFileName: TFileName read GetDataTemplateFileName write SetDataTemplateFileName;
    property LogHead: string read FLogHead write FLogHead;
    property Automatic: boolean read {v0.47}GetAutomatic{/v0.47 FAutomatic write FAutomatic};
    property DataTemplate: TAcqData read GetDataTemplate;{set just through DataTemplateFileName}
    property Pump: TPumpModule read GetPump;
    {v0.47}
    property AutoSampler: TAutoSamplerModule read GetAutoSampler;
    {/v0.47}
  end;
  TSSequenceClass = class of TSSequence;

{const
  IsAAASequence: boolean = false;}
{/v0.41}

{v0.32}
function SequenceBrowserOpen(const AFileName: shortstring; AMode: TOpenMode): boolean;
{/v0.32}

implementation
{v0.41}
uses SeqFrm;
{/v0.41}

{TSequence}
function TSequence.GetRecID: TULRecID;
begin
  Result := ULSQID;
end;

procedure TSequence.UpdateDefDirs;
var
  c: TULObjUsr;
  i: integer;
begin
  Obj.FindField('PrgFileName').FldDesc.DefDir := DefPrg.Obj.ObjDesc.DefDir;{AO.PrgsDir;}
  Obj.FindField('MethodFileName').FldDesc.DefDir := DefMethod.Obj.ObjDesc.DefDir;{AO.MethodsDir;}
  {v0.41}
  Obj.FindField('DataTemplateFileName').FldDesc.DefDir := ulanglob.TemplateDir;
  {/v0.41}{ulsqtype}
  for i := 0 to ChildCount - 1 do begin
    c := Childs[i];
    if c is TSeqSample then with c as TSeqSample do begin
      ULSR.FindField('PrgFileName').FldDesc.DefDir := DefPrg.Obj.ObjDesc.DefDir;
      ULSR.FindField('MethodFileName').FldDesc.DefDir := DefMethod.Obj.ObjDesc.DefDir;
      break;
    end;
  end;
end;

procedure TSequence.ClassFieldsCreate;
begin
  inherited;
  {v0.25}
  {v0.36}
  CurSample := nil;
  {/v0.36 FCurSample := nil;}
  FLastSample := nil;
  {/v0.25
  FLastSampleIndex := -1;
  FCurSampleIndex := -1;}
  {FDefMethod := TUlanMethod}(ChildFindOrAdd(ULMID, ''));
  {FDefInstrument := TInstrument}(ChildFindOrAdd(ULIID, ''));
  {FDefPrg := TAAPrg}(ChildFindOrAdd(AAPGID, ''));
  UpdateDefDirs;
end;

function TSequence.GetDefMethod: TUlanMethod;
begin
  if FDefMethod = nil then
    FDefMethod := TUlanMethod(ChildFindOrAdd(ULMID, ''));
  Result := FDefMethod;
end;

function TSequence.GetDefInstrument: TInstrument;
begin
  if FDefInstrument = nil then
    FDefInstrument := TInstrument(ChildFindOrAdd(ULIID, ''));
  Result := FDefInstrument;
end;

function TSequence.GetDefPrg: TAAPrg;
begin
  if FDefPrg = nil then begin
    FDefPrg := TAAPrg(ChildFindOrAdd(AAPGID, ''));
    {v0.41}
    FDefPrg.Obj.SetFlag(rfAskForSave, true);
    {/v0.41}
  end;
  Result := FDefPrg;
end;

procedure TSequence.ChildDestroyed(AChild: TULObjUsr);
begin
  if AChild = FDefMethod then
    FDefMethod := nil
  else if AChild = FDefInstrument then
    FDefInstrument := nil
  else if AChild = FDefPrg then
    FDefPrg := nil;
  {v0.25}
  if AChild = FCurSample then begin
    {v0.36}
    CurSample := nil;
    {/v0.36 FCurSample := nil;}
  end else if AChild = FLastSample then
    FLastSample := nil;
  {/v0.25}
end;

procedure TSequence.SeqSampleCreated(ss: TSeqSample);
var
  sn, vn: integer;
  i: integer;
{  fn: string;}
{  seqn: string;}
  ou: TULObjUsr;
begin
  if ss.ULSR.PrgFileName = '' then begin
    ss.ULSR.PrgFileName := ULSQ.PrgFileName;
    ss.Prg.Obj.Assign(DefPrg.Obj);
  end;

  if ss.ULSR.MethodFileName = '' then begin
    ss.ULSR.MethodFileName := ULSQ.MethodFileName;
    ss.Method.Obj.Assign(DefMethod.Obj);
  end;

  if ss.ULSR.SampleState = sasWaiting then
    ss.Instrument.Obj.Assign(DefInstrument.Obj);{methodu instrumentu}

  if ss.ULSR.VialNr = 0 then begin
    sn := 0;
    vn := 0;
    for i := 0 to ChildCount - 1  do begin
      ou := Childs[i];
      if ou is TSeqSample then with ou as TSeqSample do begin
        if ou <> ss then begin
          sn := ULSR.SampleNr;
          vn := ULSR.VialNr; {ulsrtype}
        end;
      end;
    end;
    inc(sn);
    inc(vn);
                                                                            
    ss.ULSR.FileName := 'Sample' + {v0.38} LZero(IntToStr(sn), 3){/v0.38 IntToStr(sn)};
    ss.ULSR.VialNr := vn;
    ss.ULSR.SampleNr := sn;
  end;

  ss.ULSR.FindField('MethodFileName').FldDesc.DefDir := DefMethod.Obj.ObjDesc.DefDir;
  ss.ULSR.FindField('PrgFileName').FldDesc.DefDir := DefPrg.Obj.ObjDesc.DefDir;
end;{ulsrobju}


function TSequence.GetDirName: string;
var
  n: string;
  i: integer;
begin
  n := ULSQ.SequenceName;
  for i := 1 to length(n) do begin
    if n[i] in [':',',','/','\'] then
      n[i] := '_';
  end;
  Result := AddBackSlash(ULSQ.RootFileDir) + n + '\';
end;

function TSequence.ChildCreate(AChildObj: TULObj): TULObjUsr;
var
  id:TULRecID;
  ss: TSeqSample;
begin
  Result := nil;
  if AChildObj = nil then begin
    id := ULSRID
  end else begin
    id := AChildObj.RecID;
  end;

  case id of
    ULSRID: begin
      ss := TSeqSample.Create(Self, AChildObj, id);
      Result := ss;
      SeqSampleCreated(ss);
    end;
    ULMID: begin
      Result := TUlanMethod.Create(Self, AChildObj, id);
      if (FDefMethod = nil) then begin
        {SetResult(urSeqMethodExists, '');}
        FDefMethod := TUlanMethod(Result);
      end;
    end;
    ULIID: begin
      Result := TInstrument.Create(Self, AChildObj, id);
      if FDefInstrument = nil then begin
        {SetResult(urSeqInstrumentExists, '');}
        FDefInstrument := TInstrument(Result);
      end;
    end;
    AAPGID: begin
      Result := TAAPrg.Create(Self, AChildObj, id);
      if FDefPrg = nil then begin
        {SetResult(urSeqPrgExists, '');}
        FDefPrg := TAAPrg(Result);
      end;
    end;
  else
    SetResult(urUnknownChildRecID, ULRecIDToStrStrip(id));{ulrecutl}
  end;
end;

function TSequence.GetULSQ;
begin
  Result := TULSQObj(Obj);
end;

destructor TSequence.Destroy;
begin
  inherited Destroy;
end;

function TSequence.GetCurSample: TSeqSample;
{var c: TULObjUsr;}
begin
  {v0.25}
  Result := FCurSample;
  {/v0.25
  Result := nil;
  if (FCurSampleIndex >= 0) and (FCurSampleIndex < ChildCount) then begin
    c := Childs[FCurSampleIndex];
    if c is TSeqSample then
      Result := TSeqSample(c);
  end;}
end;

function TSequence.GetLastSample: TSeqSample;
{v0.25}{/v0.25 var c: TULObjUsr;}
begin
  {v0.25}
  Result := FLastSample;
  {/v0.25
  Result := nil;
  if (FLastSampleIndex >= 0) and (FLastSampleIndex < ChildCount) then begin
    c := Childs[FLastSampleIndex];
    if c is TSeqSample then
      Result := TSeqSample(c);
  end;}
end;

function TSequence.GetNextSample: TSeqSample;
var
  i: integer;
  s: TSeqSample;
begin
  Result := nil;
  {v0.25}
  i := ChildList.IndexOf(FCurSample) + 1;
  {/v0.25
  i := FCurSampleIndex + 1;}
  if i < 0 then
    exit;
  while i < ChildCount do begin
    if Childs[i] is TSeqSample then begin
      s := TSeqSample(Childs[i]);{ulsrtype}
      if (s.ULSR.SampleState = sasWaiting) or (s.ULSR.SampleState = sasInLoop) then begin
        Result := s;
        exit;
      end;
    end;
    inc(i);
  end;
end;

function TSequence.First: boolean;
  { select first sample as current }
begin
  {v0.25}
  {v0.36}
  CurSample := nil;
  {/v0.36 FCurSample := nil;}
  FLastSample := nil;
  {/v0.25
  FCurSampleIndex := -1;
  FLastSampleIndex := -1;}
  Result := Next;
end;

function TSequence.Next: boolean;
      { select next sample as current }
var s: TSeqSample;
begin
  s := NextSample;
  {v0.25}
  {v0.36}{/v0.36
  if FCurSample <> nil then begin
    FCurSample.Obj.SetFlag(rfCantDelete, false);
  end;}
  FLastSample := FCurSample;
  {/v0.25
  FLastSampleIndex := FCurSampleIndex;}
  if s <> nil then begin
    {v0.25}
    FCurSample := s;
    {v0.36}{/v0.36
    FCurSample.Obj.SetFlag(rfCantDelete, true);}
    {/v0.25
    FCurSampleIndex := ChildList.IndexOf(s);}
    Result := true;
  end else begin
    Result := false;
    {v0.36}
    CurSample := nil;
    {/v0.36}
  end;
end;

{v0.30}
procedure TSequence.MenuActionNeeded;
var
  s: string;
  o: TULObj;
begin
  s := GetTxt({#}'all');
  if Obj.ChildWithFlagCount(rfSelected) > 0 then begin
    s := GetTxt({#}'selected')
  end else begin
    if not Obj.FindObj(ULSRID, foChilds, '', o) then
      exit;
  end;

  if FMakeTable = nil then begin
    FMakeTable := TAction.Create(Self);
    FMakeTable.OnExecute := MakeResultTable;
  end;
  FMakeTable.Caption := GetTxt({#}'Show results for') + ' ' + s;
  Obj.MenuActionAdd(FMakeTable);

  if FCalibrationFileAssign = nil then begin
    FCalibrationFileAssign := TAction.Create(Self);
    FCalibrationFileAssign.OnExecute := CalibrationFileAssign;
  end;
  FCalibrationFileAssign.Caption := GetTxt({#}'Assign calib.file to') + ' ' + s;
  Obj.MenuActionAdd(FCalibrationFileAssign);

  if FDataFilesOpen = nil then begin
    FDataFilesOpen := TAction.Create(Self);
    FDataFilesOpen.OnExecute := DataFilesOpen;
  end;
  FDataFilesOpen.Caption := GetTxt({#}'Open window for') + ' ' + s;
  Obj.MenuActionAdd(FDataFilesOpen);

  {v0.38}
  if FDataFilesOpenOverlay = nil then begin
    FDataFilesOpenOverlay := TAction.Create(Self);
    FDataFilesOpenOverlay.OnExecute := DataFilesOpenOverlay;
  end;
  FDataFilesOpenOverlay.Caption := GetTxt({#}'Open') + ' ' + s + ' ' +
    GetTxt('in one window');
  Obj.MenuActionAdd(FDataFilesOpenOverlay);
  {/v0.38}
end;

procedure TSequence.DataFileOpen{v0.38}(Aditive:boolean);{/v0.38}
begin
  if FileExists(FCurScanAcqData.ULF.FileName) then begin
    {v0.38}
    if Aditive then begin
      if FCurSpecForm = nil then
        FCurSpecForm := CreateSpectrumForm(FCurScanAcqData.ULF.FileName, omRead)
      else
        FCurSpecForm.AddDataFile(FCurScanAcqData.ULF.FileName);
    end else
    {/v0.38}
    begin
      {v0.38} FCurSpecForm := {/v0.38} CreateSpectrumForm(FCurScanAcqData.ULF.FileName, omRead);
    end;
  end;
  {specform}
end;

procedure TSequence.CalibFNAssign;
var ad: TAcqData;
begin
  ad := FCurScanAcqData;
  ad.CalibrationFileSelect(FCurCalibFN);{spectrum}
end;

procedure TSequence.ResultTableAddRow;
var
  r, c, pc, pi: integer;
{  pn: string;}

  ppn: string;
  pv: string;

  p: TULPRObj;

  ad: TAcqData;
  ulsr: TULSRObj;
  xg: TXStringGrid;
  sp: TSpectrum;

  procedure ColFind(const n: string; var col: integer);
  var
    i: integer;
    s: string;
  begin
    for i := 1 to xg.ColCount - 1 do begin
      s := xg.Cells[i, 0];
      if s = '' then begin
        xg.Cells[i, 0] := n;
        col := i;
        exit;
      end else if s = n then begin
        col := i;
        exit;
      end;
    end;
    xg.ColCount := xg.ColCount + 1;
    col := xg.ColCount - 1;
    xg.Cells[col, 0] := n;
  end;

  procedure RowFind(var row: integer);
  var i: integer;
  begin
    row := -1;
    for i := 0 to xg.RowCount - 1 do begin
      if i > 0 then begin
        if xg.Cells[0, i] = '' then begin
          row := i;
          break;
        end;
      end;
    end;
    if row = -1 then begin
      xg.RowCount := xg.RowCount + 1;
      row := xg.RowCount - 1;
    end;
    xg.Cells[0, row] := ChangeFileExt(ulsr.FileName,'');
  end;

begin
  ad := FCurScanAcqData;
  ulsr := FCurScanULSR;
  xg := FCurGrid;

 {ResultTableValueName[ord(ULSQ.ResultTableValue)]}

  if ad.ULA.AutodetectLater then begin
    try
      sp := TSpectrum.Create(ad);
      try
        try
          sp.AutoDetect;
        except
        end;
      finally
        sp.Free;
      end;
    except;
    end;
  end;

  RowFind(r);
  pc := ad.ULP.ChildCount;
  if pc > (xg.ColCount - 1) then
    xg.ColCount := pc + 1;

  ppn := ResultTableValueName[ULSQ.ResultTableValue];
  for pi := 0 to pc - 1 do begin
    p := TULPRObj(ad.ULP.Childs[pi]);
    if p.RecID = ULPRID then begin
      ColFind(p.PeakName, c);            {ulsqobju uledfrm aapltype}
      if FGridValueCaption = '' then begin
        FGridValueCaption := p.FindField(ppn).FldDesc.Caption;
      end;
      pv := p.FindField(ppn).AsUsrString;
      xg.Cells[c, r] := pv;{FloatToStrF(pv, ffGeneral, 7, 4);}
    end;
  end;
end;

procedure TSequence.ChildsScan(sr: TScanReason);
var
  i: integer;
  fl: TULRecFlags;
  o: TULObj;
  ad: TAcqData;
  releaseData: boolean;
  fn: string;
  frm: TForm;
begin
  i := -1;
  {v0.38}
  FCurSpecForm := nil;
  {/v0.38}
  if Obj.ChildWithFlagCount(rfSelected) > 0 then
    fl := rfSelected
  else
    fl := 0;
  while Obj.ChildWithFlagNext(fl, i) do begin
    o := Obj.Childs[i];
    if o.RecID = ULSRID then begin
      FCurScanULSR := TULSRObj(o);
      try
        releaseData:= true;
        fn := ChangeFileExt(DirName + FCurScanULSR.FileName, ULFExt);
        {v0.31}
        case sr of
          srResultTable, srOpen {v0.38},srOpenOverlay{/v0.38}: begin
            if not FileExists(fn) then
              continue;
          end;
        end;
        {/v0.31}

        {v0.33}
        case sr of
          srOpen{v0.38}, srOpenOverlay{/v0.38}: begin
            ad := TAcqData.Create(fn, omRead);
          end;
        else
          if not FindOpenedData(fn, ad {v0.36}, frm{/v0.36}) then begin
            ad := TAcqData.Create(fn, omRead);
          end else begin
            releaseData := false;
          end;
        end;
        {/v0.33
        ad := TAcqData.Create(fn, omRead);}
        try
          FCurScanAcqData := ad;
          case sr of
            srResultTable: ResultTableAddRow;
            srCalibrationFile: CalibFNAssign;
            srOpen: DataFileOpen{v0.38}(false){/v0.38};
            {v0.38}
            srOpenOverlay: DataFileOpen(true);
            {/v0.38}
          end;
        except
          { ignore adddata failure }
        end;
        FCurScanAcqData := nil;
        if releaseData then begin
          ad.Free;
        end;
      except
        { ignore one datafile read failure }
        {if releaseData then begin
          ad.Free;
          ad := nil;
        end;}
      end;
    end;
  end;
end;

procedure TSequence.DataFilesOpen(Sender: TObject);
begin
  ChildsScan(srOpen);
end;

{v0.38}
procedure TSequence.DataFilesOpenOverlay(Sender: TObject);
begin
  ChildsScan(srOpenOverlay);
end;
{/v0.38}

procedure TSequence.CalibrationFileAssign(Sender: TObject);
var fn: string;
begin
  {ulobju}
  fn := '';
  if FileNameOpenSelect(fn, GetTxt({#}'Select calibration file'),
    CalibrationFileFilter, DirName) then
  begin
    FCurCalibFN := fn;
    try
      ChildsScan(srCalibrationFile);
    finally
      FCurCalibFN := '';
    end;
    {specform}
  end;
end;

procedure TSequence.MakeResultTable(Sender: TObject);
var
  f: TXGridForm;
{  i: integer;}
  ok: boolean;
begin
  ok := false;
  FGridValueCaption := '';
  f := TXGridForm.Create(Application);
  try
    f.Caption := ChangeFileExt(FileName, '') + ' ' + GetTxt({#}'Results Table');
    FCurGrid := f.Grid;
    try
      ChildsScan(srResultTable);
    finally
      FCurGrid := nil;
    end;
    ok := true;
    if f.Grid.RowCount > 1 then begin
      f.Grid.FixedRows := 1;
    end else begin
      ok := false;
    end;
    if f.Grid.ColCount > 1 then begin
      f.Grid.FixedCols := 1;
    end else begin
      ok := false;
    end;
    f.Grid.RemoveEmptyColsRows;
  finally
    if ok then begin
      f.Grid.Cells[0,0] := FGridValueCaption;
      f.Show
    end else begin
      f.Free;
      (*ShowMessage(GetTxt({#}'No data found'), smError, 0);*)
    end;
  end
end;
{/v0.30}

{v0.32}
procedure TSequence.FileNameChanged;
begin
  if ULSQ <> nil then
    ULSQ.SequenceName := ChangeFileExt(ExtractFileName(FileName), '');
end;
{/v0.32}

{v0.36}{ulobjusru}
procedure TSequence.ULGridCreated(AGrid: TULStringGrid);
begin
  inherited;
  AGrid.OnDblClick := DoOnDblClick;
end;

procedure TSequence.DoOnDblClick(AGrid: TObject);
var
  o: TULObj;
  fn: string;
  d: TAcqData;
  f: TForm;
begin
  if not (AGrid is TULStringGrid) then
    exit;
  {ulstringgrid}
  o := TULStringGrid(AGrid).CurChild;
  if o is TULSRObj then with o as TULSRObj do begin
    fn := AbsoluteFileName(DirName, FileName, ULFExt);
    if FindOpenedData(fn, d, f) then begin
      f.BringToFront;
    end else begin
      if FileExists(fn) then
        CreateSpectrumForm(fn, omRead);
    end;
  end;
end;

procedure TSequence.NoSample;
begin
  CurSample := nil;
  FLastSample := nil;
end;

procedure TSequence.SetCurSample(ASample: TSeqSample);
begin
  if FCurSample = ASample then
    exit;
  if FCurSample <> nil then begin
    FCurSample.Obj.SetFlag(rfWriteLocked, false);
    FCurSample.Obj.SetFlag(rfCantDelete, false);
  end;
  FCurSample := ASample;
  if FCurSample <> nil then begin
    FCurSample.Obj.SetFlag(rfWriteLocked, true);{disable for usr}
    FCurSample.Obj.SetFlag(rfCantDelete, true);
  end;
end;
{/v0.36}

{v0.44}
procedure TSequence.FirstMenuActionNeeded;

  {procedure ActionUpdate(var AAction: TAction; ANotifyEvent: TNotifyEvent; const ACaption: string);
  begin
    if AAction = nil then begin
      AAction := TAction.Create(Self);
      AAction.OnExecute := ANotifyEvent;
    end;
    AAction.Caption := ACaption;
    Obj.MenuActionAdd(AAction);
  end;}

  procedure AddClearError;
  {var sr: TULSRObj;}
  begin
    {sr := TULSRObj(Obj.ActiveChild);}
    ActionUpdate(FClearSampleErrorAction, ClearSampleError, GetTxt({#}'Clear Sample Error'));
  end;

begin
{ulstringgrid  s := GetTxt('all');
  if Obj.ChildWithFlagCount(rfSelected) > 0 then begin
    s := GetTxt('selected')
  end else begin
    if not Obj.FindObj(ULSRID, foChilds, '', o) then
      exit;
  end; }
  AddClearError;
end;

procedure TSequence.ClearSampleError(Sender: TObject);
begin
  if Obj.ActiveChild is TULSRObj then with Obj.ActiveChild as TULSRObj do begin
    if SampleState = sasError then
      SampleState := sasWaiting;
  end;
    {ulsrtype}
end;
{/v0.44}

{/TSequence}


{/TSeqSample}
constructor TSeqSample.Create(AOwner: TULObjUsr; AObj: TULObj; ARecID: TULRecID);
begin
  inherited Create(AOwner, AObj, ARecID);
end;

procedure TSeqSample.ClassFieldsCreate;
begin
  inherited; {ulfobju}
  ChildFindOrAdd(ULMID, '');
  ChildFindOrAdd(ULIID, '');
  ChildFindOrAdd(AAPGID, '');
end;

function TSeqSample.GetRecID: TULRecID;
begin
  Result := ULSRID;
end;

function TSeqSample.ChildCreate(AChildObj: TULObj): TULObjUsr;
var
  id:TULRecID;
begin
  Result := nil;
  id := 0;
  if AChildObj = nil then begin
    SetResult(urUnknownChildRecID, '');
  end else begin
    id := AChildObj.RecID;
  end;

  case id of
    ULMID: begin
      Result := TUlanMethod.Create(Self, AChildObj, id);
      if (FMethod = nil) then begin
        FMethod := TUlanMethod(Result);
      end;
      Result := FMethod;
    end;
    ULIID: begin
      Result := TInstrument.Create(Self, AChildObj, id);
      if FInstrument = nil then begin
        FInstrument := TInstrument(Result);
      end;
    end;
    AAPGID: begin
      Result := TAAPrg.Create(Self, AChildObj, id);{aaprgu}
      if FPrg = nil then begin
        FPrg := TAAPrg(Result);
      end;
    end;
  else
    SetResult(urUnknownChildRecID, ULRecIDToStrStrip(id));{ulrecutl}
  end;
end;

procedure TSeqSample.ChildDestroyed(AChild: TULObjUsr);
begin
  if AChild = FMethod then
    FMethod := nil
  else if AChild = FInstrument then
    FInstrument := nil
  else if AChild = FPrg then
    FPrg := nil;
end;

function TSeqSample.GetULSR: TULSRObj;
begin
  Result := TULSRObj(Obj);
end;

destructor TSeqSample.Destroy;
begin
  inherited Destroy;
end;
{/TSeqSample}

{v0.41}
{TSSequence}
procedure TSSequence.FirstMenuActionNeeded;

  procedure ActionUpdate(var AAction: TAction; ANotifyEvent: TNotifyEvent; const ACaption: string);
  begin
    if AAction = nil then begin
      AAction := TAction.Create(Self);
      AAction.OnExecute := ANotifyEvent;
    end;
    AAction.Caption := ACaption;
    Obj.MenuActionAdd(AAction);
  end;

  procedure AddClearError;
  {var sr: TULSRObj;}
  begin
    {sr := TULSRObj(Obj.ActiveChild);}
    ActionUpdate(FClearSampleErrorAction, ClearSampleError, GetTxt({#}'Clear Sample Error'));
  end;

begin
  {v0.44}
  inherited;
  {/v0.44}
{  s := GetTxt('all');
  if Obj.ChildWithFlagCount(rfSelected) > 0 then begin
    s := GetTxt('selected')
  end else begin
    if not Obj.FindObj(ULSRID, foChilds, '', o) then
      exit;
  end; }
  ActionUpdate(FSequenceRunAction, SequenceRun, GetTxt({#}'Run'));
  ActionUpdate(FPrgEditAction, PrgEdit, GetTxt({#}'Program Edit'));
  ActionUpdate(FMethodEditAction, MethodEdit, GetTxt({#}'Method Edit'));
  if FAcqInfo <> nil then
    ActionUpdate(FDoAcqShowAction, DoAcqShow, GetTxt({#}'Show Acquisition'));
  if CurPrg <> nil then
    ActionUpdate(FShowActPrgAction, ShowActPrgAction, GetTxt({#}'Show Running Program'));
//  ActionUpdate(FSequenceStopAction, SequenceStop, GetTxt({#}'Stop'));
  {v0.44}
  AddClearError;
  {/v0.44
  ActionUpdate(FNulAction, nil, '-');}
end;

procedure TSSequence.ShowActPrgAction(Sender: TObject);
begin
  ShowRunningPrg;
end;

procedure TSSequence.DefMethodSetDefaults;
begin
  FDefMethod.ULM.NoUnknownPeaks := true;
  FDefMethod.Save;
end;

procedure TSSequence.DefPrgCreateDefault;

  procedure AddLine0(l: TAAPLObj);
  begin
    l.PrgTime := 0;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acInject;
    l.Note := 'Start by injecting the sample on the column (acqusition starts)';
  end;

  procedure AddLine1(l: TAAPLObj);
  begin
    l.PrgTime := 30;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acNHD;
    l.Note := 'Ninhydrin should be switched on for poscolumn detection';
  end;

  procedure AddLine2(l: TAAPLObj);
  begin
    l.PrgTime := 60;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acZero;
    l.Note := 'Eventually zero the detector';
  end;

  procedure AddLine3(l: TAAPLObj);
  begin
    l.PrgTime := 90;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acAcqStop;
    l.Note := 'Acquisition can be stopped before program ends';
  end;

  procedure AddLine4(l: TAAPLObj);
  begin
    l.PrgTime := 120;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acStartEquil;
    l.Note := 'Start equilibrating the columnn for next sample';
  end;

  procedure AddLine5(l: TAAPLObj);
  begin
    l.PrgTime := 150;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acLoad;
    l.Note := 'Load next sample to injector loop';
  end;

  procedure AddLine6(l: TAAPLObj);
  begin
    l.PrgTime := 180;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acNone;
    l.Note := 'Just waiting for the end (of equilibration)';
  end;

var
  pl: TAAPrgLine;
  i: integer;

begin
  DefPrg.Obj.Clear;
  for i := 0 to 6 do begin
    pl := TAAPrgLine(DefPrg.ChildAdd(nil, AAPLID, ''));
    case i of
      0: AddLine0(pl.AAPL);
      1: AddLine1(pl.AAPL);
      2: AddLine2(pl.AAPL);
      3: AddLine3(pl.AAPL);
      4: AddLine4(pl.AAPL);
      5: AddLine5(pl.AAPL);
      6: AddLine6(pl.AAPL);
    end;
  end;
  DefPrg.Save;
end;

constructor TSSequence.Load(const AFileName: string);
begin
  inherited;{ulsqtype}
  SequenceState := sesStarting;
  Obj.OnGetWindowCaption := OnGetWindowCaption;
end;

function TSSequence.GetPump: TPumpModule;
begin
  if FPump = nil then begin
    if CurDeviceMode = dmUlan then
      FPump := TPumpModule.Create(Self, DataTemplate.Channel);
  end;
  Result := FPump;
end;

{v0.47}
function TSSequence.GetAutoSampler: TAutoSamplerModule;
var
  m: TModule;
begin
  if FAutoSampler = nil then begin
    if not FAutoSamplerChecked then begin
      if CurDeviceMode = dmUlan then begin
        FAutoSamplerChecked := true;
        try
          FAutoSampler := TAutoSamplerModule.Create(Self, DataTemplate.Channel);
        except
          { fails if autosampler not found }
        end;
      end;
    end;
  end;
  Result := FAutoSampler;
end;

function TSSequence.AutoSamplerLoad(AVialNr: integer): boolean;
begin
  if AutoSampler <> nil then begin
    AutoSampler.PREPSAMP.AsInteger := AVialNr;
    Log('AutoSampler PREPSAMP=' + IntToStr(AVialNr));
    Result := true;
    {
    if SimulationOn then begin
      AS_SAMPNUM.AsInteger := vnr;
    end;
    }
  end else begin
    Result := false;
    LogState('AutoSamplerLoad - no autosampler');
    SequenceState := sesAborting;
  end;
end;

function TSSequence.AutoSamplerInject: boolean;
begin
  if AutoSampler = nil then begin
    LogState('AutoSamplerInject - no autosampler');
    Result := false;
  end else begin
    AutoSampler.INJECT.AsInteger := 1;
    Result := true;
  end;
end;

function TSSequence.GetAutomatic: boolean;
begin
  Result := AutoSampler <> nil;
end;
{/v0.47}

procedure TSSequence.OnGetWindowCaption(var ACaption: string);
begin
  ACaption := ULSQ.ObjDesc.Caption + ' ' + ULSQ.SequenceName + ' ' + SequenceStateStr;
  if (FAcqInfo <> nil) then begin
    ACaption := ACaption + ' ' + ExtractFileName(FAcqInfo.Data.ULF.FileName) + ' ' +
    FloatToStrF(FAcqInfo.CurTime / 60, ffFixed, 7, 2);
  end;
end;

function TSSequence.SequenceStateStr: string;
begin
  Result := GetSequenceStateStr(SequenceState);
end;

function TSSequence.GetSequenceStateStr(ASequenceState:TSequenceState): string;
var s: string;
begin
  case ASequenceState of
    sesDisconnected: s := 'Disconnected'; { devices disconnected }
    sesAborted: s := 'Aborted';      { sequence run aborted, devices off }
    sesFinished: s := 'Finished';    { sequence run finished by processing last sample, devices off }
    sesStandby: s := 'Standby';      { sequence run not started yet or stopped upon user request, devices off
                       (not finished) }
    sesOff: s := 'Off';              { .. not real state, just for comparing, all below = OFF }
    sesStarting: s := 'Starting';    { sequence run requested, devices preparing for run }
    sesStopping: s := 'Stopping';    { sequence run stopping upon user request, devices stopping }
    sesFinishing: s := 'Finishing';  { sequence run finishing after last sample processed, devices stopping }
    sesAborting: s := 'Aborting';     { sequence run aborting because of error, devices aborting }
    sesReady: s:= 'Ready';        { sequence is ready to run (devices ready), devices ready to run }
    sesStopRunning: s := 'StopRunnig';  { running, but after processing current sample Stopping will start
                       (user requested stop after current sample) }
    sesRunning: s := 'Running';       { sequence is running }
  else
    s := 'Unknown';
  end;
  Result := s;
end;

procedure TSSequence.ClassFieldsCreate;
begin
  inherited;
  DefMethod.FullFileName := ULSQ.MethodFileName;

  if (DefMethod.FileName = '') or (pos('NONAME', Uppercase(DefMethod.FileName)) <> 0) then begin
    DefMethod.FullFileName := ULRecDefFileName(ULMID);
    ULSQ.MethodFileName := DefMethod.RelFileName;
  end;
  if (not FileExists(DefMethod.FullFileName)) and
    (DefMethod.RelFileName = ULRecDefFileName(ULMID))
  then begin
    DefMethodSetDefaults;
  end;
  DefMethod.LoadFromFile('');

  DefPrg.FullFileName := ULSQ.PrgFileName;
  if (DefPrg.FileName = '') or (pos('NONAME', Uppercase(DefPrg.FileName)) <> 0) then begin
    DefPrg.FullFileName := ULRecDefFileName(AAPGID);
    ULSQ.PrgFileName := DefPrg.RelFileName;
  end;
  if (DefPrg.RelFileName = ULRecDefFileName(AAPGID)) then begin
    if (not FileExists(DefPrg.FullFileName))then begin
      DefPrgCreateDefault;
    end;
  end;
  DefPrg.LoadFromFile('');
end;

procedure TSSequence.DoAfterCreate;
begin
  {update def dirs}
  Obj.ObjDesc.DefDir := ulanglob.SequenceDir;

  Obj.FindField('PrgFileName').FldDesc.DefDir := ulanglob.PrgDir;
  Obj.FindField('MethodFileName').FldDesc.DefDir := ulanglob.MethodDir;

  DefMethod.Obj.ObjDesc.DefDir := ulanglob.MethodDir;
  DefMethod.UpdateDefDirs;

  DefPrg.Obj.ObjDesc.DefDir := ulanglob.PrgDir;

  UpdateDefDirs;
  {/update def dirs}
end;

function TSSequence.GetNewSequenceName: string;
var
  d: string;
  n: string;
  i: integer;
begin
  n := FormatDateTime('yymmdd', Now);
  i := 1;
  repeat {ulsqtype}
    d := n + '-' + IntToStr(i);
    if not DirectoryExists(AddBackSlash(Obj.ObjDesc.DefDir) + d) then begin
      break;
    end;
    inc(i);
  until false;
  Result := d;
end;

procedure TSSequence.FileNew;
begin
{ if ULSQ.PrgFileName = '' then begin
    ULSQ.PrgFileName := AO.PrgFileName;
  end;

  if ULSQ.MethodFileName = '' then begin
    ULSQ.MethodFileName := AO.MethodFileName;
  end; }
  ClassFieldsUpdate;

  if ULSQ.SequenceName = '' then begin
    ULSQ.SequenceName := GetNewSequenceName;
    {ULSQ.MethodFileName := AO.MethodFileName;
    ULSQ.PrgFileName := AO.PrgFileName;}
  end;
  FullFileName := ULSQ.SequenceName;
  CheckDir;
end;

procedure TSSequence.CheckDir;
begin
  DefMethod.Obj.FindField('CalibrationFileName').FldDesc.DefDir := DirName;
  CreateDir(DirName);
  if not FileExists(FileName) then
    Save;
end;

procedure TSSequence.LogState(const msg: string);
begin
  ExeLog.LogErr('Sequence.' + LogHead + ' ' + msg);
end;

procedure TSSequence.Log(const msg: string);
begin
  ExeLog.Log('Sequence.' + LogHead + ' ' + msg);
end;

procedure TSSequence.TimerAdd;
begin
  if not FTimerAdded then begin
    Log('TimerAdd begin');
    Timers.AddTimerProc(DoTimer, 1000);
    FTimerAdded := true;
    Log('TimerAdd end');
  end;
end;

procedure TSSequence.TimerDel;
begin
  if FTimerAdded then begin
    Log('TimerDel begin');
    Timers.DeleteTimerProc(DoTimer);
    FTimerAdded := false;
    Log('TimerDel end');
  end;
end;

procedure TSSequence.DoCurSampleSelected;
begin
  CurSample.Prg.LoadFromFile(
     AbsoluteFileName(DefPrg.Obj.ObjDesc.DefDir, CurSample.ULSR.PrgFileName, AAPGExt)
  );
  CurSample.Method.LoadFromFile(
    AbsoluteFileName(DefMethod.Obj.ObjDesc.DefDir, CurSample.ULSR.MethodFileName, ULMExt)
  );
end;

procedure TSSequence.ObjUpdated;
begin
  inherited;
  CurSampleStateChanged;
end;

procedure TSSequence.CurSampleStateChanged;
begin
//  AO.DoChange;
end;

procedure TSSequence.DoOK;
begin
  if FSeqForm <> nil then begin
    Log('OK pressed');
    DoConfirm;
  end;
end;

procedure TSSequence.DoConfirm;
begin
  if SequenceState <= sesOff then begin
    Log('DoConfirm ... OFF - SeqFormFree');
    SeqFormFree;
    exit;
  end;
  if FSeqForm = nil then
    exit;
  {TSeqForm(FSeqForm).MsgMemo.Lines.Add('OK pressed');}
  case TSeqForm(FSeqForm).State of
    sfsLoadSample: begin
      DoLoadSample;
      {CurSample.ULSR.SampleState := sasInLoop;}
    end;
    sfsInjectSample: begin
      DoInjectSample;
      {CurSample.ULSR.SampleState := sasRunning;}
    end;
    sfsLoadAndInjectSample: begin
      DoLoadSample;
      DoInjectSample;
    end;
  end;
end;

procedure TSSequence.DoCancel;
begin
  if FSeqForm <> nil then begin
    if SequenceState <= sesOff then begin
      SeqFormFree;
      exit;
    end;
    {TSeqForm(FSeqForm).MsgMemo.Lines.Add('Cancel pressed'); ulstringgrid}
  end;
  SequenceState := sesAborting;
end;

function TSSequence.GetDataTemplate: TAcqData;
var
  d: TAcqData;
{  ad: TULADObj;
  cnt, i: integer;
  o: TULObj;}
  ul: TUserViewLimit;
begin
  if FDataTemplate = nil then begin
    d := TAcqData.Create(DataTemplateFileName, omRead);
    try
      {
      cnt := 0;
      i := 0;
      while i < d.ULA.ChildCount do begin
        o := d.ULA.Childs[i];
        inc(i);
        if o is TULADObj then begin
          inc(cnt);
          if cnt = 1 then begin
            TULADObj(o).Color := clGreen;
          end else if cnt = 2 then begin
            TULADObj(o).Color := clBlue;
            TULADObj(o).DataName := 'B';
          end else begin
            o.Free;
            dec(i);
          end
        end;
      end;
      if cnt = 0 then begin
        ad := TULADObj(d.ULA.Add(ULADID));
        inc(cnt);
      end;
      if cnt = 1 then begin
        ad := TULADObj(d.ULA.Add(ULADID));
        ad.DataName := 'B';
        ad.Color := clBlue;
      end;
      d.ULAD.Update; }

      if d.ULI.Duration <> 0 then begin
        ul := NewUserViewLimit;
        d.ULVL.MinX := ul.Min.X;
        d.ULVL.MinY := ul.Min.Y;
        d.ULVL.MaxX := d.ULI.Duration / 60; //min
        d.ULVL.MaxY := ul.Max.Y;
      end;
      d.ULF.SaveToFile(d.ULF.FileName);

    except
      d.Free;
    end;
    FDataTemplate := d;
  end;
  Result := FDataTemplate;
end;

procedure TSSequence.SetDataTemplateFileName(const AFileName: TFileName);
begin
  if (AFileName = ULSQ.DataTemplateFileName) then
    exit;
  FDataTemplate.Free;
  FDataTemplate := nil;
  ULSQ.DataTemplateFileName := AFileName;
end;

function TSSequence.GetDataTemplateFileName: TFileName;
begin
  if ULSQ.DataTemplateFileName = '' then begin
    ULSQ.DataTemplateFileName := AddBackSlash(TemplateDir) + pvUlanDefaultSeq;
  end;
  Result := ULSQ.DataTemplateFileName;
end;

procedure TSSequence.SeqFormCheck(sfs: TSeqFormState; const ASampleName: string);
begin
  if FSeqForm = nil then begin
    FSeqForm := TSeqForm.Create(Self);
    TSeqForm(FSeqForm).Sequence := Self;
  end;
  with FSeqForm as TSeqForm do begin
    SampleName := ASampleName;
    if State <> sfs then begin
      State := sfs;
    end;
  end;
  Obj.UsersNotify(cmULObjFileNameChanged);{forces updating caption of browser}
end;

procedure TSSequence.SeqFormDoTimer;
begin
  if FSeqForm is TSeqForm then with FSeqForm as TSeqForm do begin
    if State in [sfsLoadSample, sfsInjectSample, sfsLoadAndInjectSample] then begin
      DoSound;
      Show;
      BringToFront;
    end;
  end;
end;

function TSSequence.GetSampleToLoad: TSeqSample;
begin
  Result := nil;
  if CurSample.ULSR.SampleState = sasWaiting then begin
    Result := CurSample;
  end else if NextSample <> nil then begin
    if CurSample.ULSR.PrgFileName <> NextSample.ULSR.PrgFileName then begin
      {Log('LOAD - ignored (next sample has dif.program)');}
    end else begin
      Result := NextSample;
    end;
  end else begin
    {Log('LOAD - no more samples');}
  end;
end;

procedure TSSequence.DoLoadSample;
var
  cs: TSeqSample;
  vnr: integer;
begin
{  if not Automatic then
    CurPrg.Resume;}
  cs := nil;
  {v0.46}
  vnr := 0;
  {/v0.46}

  if CurSample.ULSR.SampleState = sasWaiting then begin

    cs := CurSample;
    vnr := CurSample.ULSR.VialNr;
    CurSample.ULSR.SampleState := sasInLoop;

    Log('LOAD - loading current sample');

  end else if NextSample <> nil then begin

    if CurSample.ULSR.PrgFileName <> NextSample.ULSR.PrgFileName then begin
      Log('LOAD - ignored (next sample has dif.program)');
    end else begin
      vnr := NextSample.ULSR.VialNr;
      cs := NextSample;
      NextSample.ULSR.SampleState := sasInLoop;
      NextSample.Obj.SetFlag(rfCantDelete, true);
      CurSampleStateChanged;
      Log('PRG LINE: LOAD - loading next sample');
    end;

  end else begin

    Log('LOAD - no more samples');

  end;

  if vnr <> 0 then begin
    {v0.47}
    if Automatic then begin

      AutoSamplerLoad(vnr);

    end else
    {/v0.47}
      SeqFormCheck(sfsLoaded, cs.ULSR.FileName);

  end else begin
    {v0.47}
    if not Automatic then
    {/v0.47}
      SeqFormCheck(sfsIdle,'');

  end;

  {
  if vnr <> 0 then begin

    if (FDetState <> dstOK) and (not SimulationOn) then begin
      LogState('Before LOAD - DETECTOR FAILURE(' + GetDetStateMsg +
        ') detected, STOPPING before ' + CurSample.ULSR.FileName);
      SetAAAState(aasAborting);
      Result := false;
    end else  begin

      AS_PREPSAMP.AsInteger := vnr;
      Log('AS_PREPSAMP=' + IntToStr(vnr));

      if SimulationOn then begin
        AS_SAMPNUM.AsInteger := vnr;
      end;

    end;

  end;
  }

end;

procedure TSSequence.DoInjectSample;
var
  vnr: integer;
  aborted:boolean;
begin
  aborted := false;                                        {aaau}
  if {v0.47}Automatic and (AutoSampler.State <> astPrepared){/v0.47 false}
    {(FASState <> astPrepared)} then
  begin
    vnr := 0;
    { AS Not prepared: }
    if FCurPrg.Equil then begin
      Log('INJECT - Sample not prepared - Ignored (equilibrating)');
    end else begin
      //Result := false;
      LogState('INJECT - AS NOT READY');
      aborted := true;
      SequenceState := sesAborting;
    end;
  end else begin
    { AS Prepared: }
    {vnr := AS_SAMPNUM.AsInteger;}
    vnr := CurSample.ULSR.VialNr;{!!!}
    if (CurSample.ULSR.VialNr = vnr) then begin
      { Prepared vial position is ok: }
      if (CurSample.ULSR.SampleState <> sasInLoop) then
      begin
        if FCurPrg.Equil then begin
          Log('INJECT - Ignored (equilibrating), VialNr:' + IntToStr(vnr));
        end else begin
          LogState('INJECT - Sample NOT LOADED!, VialNr:' + IntToStr(vnr));
          SequenceState := sesAborting;
        end;
      end else begin
        {AS_Inject.AsInteger := 1;}
        {v0.47}
        if Automatic then begin
          if not AutoSamplerInject then begin
            aborted := true;
            vnr := 0;
          end;
        end;
        if not aborted then
        {/v0.47}
        begin
          Log('INJECTED VialNr: ' + IntToStr(vnr));
          AcqStart;
          CurSample.ULSR.SampleState := sasRunning;
          CurSampleStateChanged;
        end;
      end;
    end else begin
      LogState('INJECTED - Dif.Cur.Sample VialNr(' +
        IntToStr(CurSample.ULSR.VialNr) + ') and Prepared VialNr(' + IntToStr(vnr));
      //Result := false;
      vnr := 0;
      aborted := true;
      SequenceState := sesAborting;
    end;

  end;

  if not Automatic then
    CurPrg.Resume;

  if vnr <> 0 then begin
    SeqFormCheck(sfsInjected, CurSample.ULSR.FileName);

    {PumpPrgRun;}
  end else begin
    if not aborted then
      SeqFormCheck(sfsIdle, '');
  end;

{  if not Result then begin
    LogState('INJECT FAILED, ABORTING. Sample=' + CurSample.ULSR.FileName);
    SequenceState := aasAborting;
  end;}
end;

procedure TSSequence.PumpPrgEnd(const msg:string);
begin
  if Pump = nil then begin
    Log('[PUMP PRGEND]');
    exit;
  end;
  if Pump.PRGEND <> nil then begin
    Pump.PRGEND.AsInteger := 1;{end any eventually running program}
    Log('Pump.PRGEND ' + msg);
    if Pump.START <> nil then
      Log('Pump.START ' + msg);
  end;
end;

procedure TSSequence.PumpPrgRun(const msg:string);
begin
  if Pump = nil then begin
    Log('[PUMP PRGRUN]');
    exit;
  end;
  if Pump.PRGRUN <> nil then begin
    if Pump.STATUS <> nil then begin
      {if Pump.STATUS.AsInteger < 256 then}
      begin
        Pump.PRGRUN.AsInteger := 1;
        Log('Pump.PRGRUN ' + msg);
      end;
    end else begin
      Pump.PRGRUN.AsInteger := 1;
      Log('Pump.PRGRUN ' + msg);
    end;
  end;
end;

procedure TSSequence.PumpStartOrRun(const msg: string);
begin
  if Pump = nil then begin
    if not FPumpTimerLogged then begin
      Log('[PUMP START OR RUN]');
      FPumpTimerLogged := true;
    end else begin
      DebLog('[PUMP START OR RUN]');
    end;
    exit;
  end;

  if Pump.STATUS <> nil then begin

    if Pump.STATUS.AsInteger = 0 then begin

      if Pump.START <> nil then begin
        Pump.START.AsInteger := 1;
        Log('Pump.START ' + msg);
      end;

    end else if Pump.STATUS.AsInteger = 1 then begin

      if Pump.PRGRUN <> nil then begin
        Pump.PRGRUN.AsInteger := 1;
        Log('Pump.PRGRUN ' + msg);
      end;

    end;
  end;
end;

procedure TSSequence.PumpPrgActNrSelect(i:integer);
var prgid: integer;
begin
  if Pump = nil then begin
    Log('[PUMP PRG ACT NR SELECT ' + IntToStr(i) + ']');
    exit;
  end;
  prgid := i;
  if prgid <= 0 then
    prgid := 1;
  if Pump.PRGACTN <> nil then begin
    Pump.PRGACTN.AsInteger := prgid;
    Log('Pump.PRGACTN=' + IntToStr(prgid));
  end;
  if Pump.START <> nil then begin
    Pump.START.AsInteger := 1;
    Log('Pump.START');
  end;
end;

procedure TSSequence.DoTimerRunningStopRunning;
var
  pl: TAAPrgLine;
  timedif: integer;

  {procedure DoBuffer;
  var
     bnr: integer;
  begin
    bnr := ord(pl.AAPL.BufferNr);
    if bnr <> 0 then begin
      P1_AUXVALV.AsInteger := bnr;
      Log('BUFFER=' + IntToStr(bnr));
    end;
  end;

  procedure DoColumnTemp;
  var
    ot: extended;
    nt: extended;
  begin
    ot := StrToFloat(AS_TEMP1RQ.AsUsrString);
    nt := pl.AAPL.Temperature;
    if abs(ot - nt) > 1 then begin
      FColumnTempHistory.ChangedCounter := 5*60;
    end;
    AS_TEMP1.AsUsrString := FloatToStr(nt);
    Log('TEMP=' + IntToStr(round(nt)));
  end;
  }

  function DoInject: boolean;
  {var vnr: integer;}
  begin
    Result := true;
    {if SimulationOn then begin
      if not FCurPrg.Equil then begin
        FASState := astPrepared;
      end;
    end;}

    if not Automatic then begin
      CurPrg.Suspend;{ulsrtype}
      AcqGetReady;
      if CurSample.ULSR.SampleState <> sasInLoop then begin
        SeqFormCheck(sfsLoadAndInjectSample, CurSample.ULSR.FileName);
      end else begin
        SeqFormCheck(sfsInjectSample, CurSample.ULSR.FileName);
      end;
      PumpPrgRun('DoInject');
      exit;
    end;

    DoInjectSample;
  end;

  function DoLoad: boolean;
  var
    {vnr: integer;}
    ss: TSeqSample;
  begin
    Result := true;
    {vnr := 0;}
    if not Automatic then begin
      {CurPrg.Suspend;}
      ss := GetSampleToLoad;
      if ss <> nil then
        SeqFormCheck(sfsLoadSample, ss.ULSR.FileName);
      exit;
    end;

    DoLoadSample;
  end;

begin
  {v0.43}
  DebLog('SSequence DoTimerRun begin');
  try
    if FCurPrg = nil then begin
      DebLog('SSequence DoTimerRunnn... FCurPrg = nil');
      exit;
    end;
    {/v0.43}
    if FCurPrg.LineReady(pl, FStateStartTime) then begin

      if FCurPrg.Equil then
        LogHead := 'EQUIL:PRG_LINE: '
      else
        LogHead := 'PRG_LINE: ';

      try
        Log('START');

        {DoBuffer;
        DoColumnTemp;}

        {command}
        case pl.AAPL.Command of

          acInject: begin
            Log('CMD INJECT');
            if not DoInject then
              exit;
          end;

          acZero: begin
            Log('DET_ZERO');
            {DET_ZERO.AsInteger := 1;}
          end;

          acH2O: begin
            {P2_GRADDIR.AsInteger := 0;}
            Log('P2_GRADDIR=H2O(0)');
          end;

          acNHD: begin
            {P2_GRADDIR.AsInteger := 1;}
            Log('P2_GRADDIR=NHD(1)');
          end;

          acAcqStop: begin
            if not FCurPrg.Equil then begin
              AcqStop;
              CurSample.ULSR.SampleState := sasCompleting;{ulsrtype}
              Log('CMD ACQ STOP');
            end else begin
              {AcqEquilStop;}
              Log('CMD ACQ STOP - ignored (equilibrating)');
            end;
          end;

          acStartEquil: begin
            Log('CMD START EQUIL');
          end;

          acLoad: begin
            Log('CMD LOAD');
            if not DoLoad then
              exit;
          end;

        end;
        {/command}

      finally
        Log('END');
        LogHead := '';
        Log('');
        {v0.38}
        pl.AAPL.State := plsDone;
        {/v0.38}
      end;
    end;

    if not FCurPrg.Running then begin
      { CurPrg finished (no more lines): }
      {v0.43}
      Log('CurPrg Not running');
      {/v0.43}
      if FCurPrg.Equil then begin
        AcqEquilStop
      end else begin
        AcqStop;{exelogu}
          { just to be sure the acq is stopped (if AcqStop command was not in program) }
      end;
      if SequenceState = sesStopRunning then begin
        Log('SEQUENCE STOPPED BY USER REQUEST DURING LAST SAMPLE RUN');
        SequenceState := sesStandby;
      end else
      begin
        if FCurPrg.Equil then begin
          { was equilibrating: }
          CurPrg := FCurPrg; { reselect/reactivate the same prg }
          FCurPrg.Start;
          StateStart(sesRunning, 0);
          Log('PRG ' + FCurPrg.FileName + ' STARTED (AFTER EQUIL) Sample ' +
            IntToStr(CurSample.ULSR.VialNr) + ' ' + CurSample.ULSR.FileName);
        end else begin
          { CurSample finihed }
          CurSample.ULSR.SampleState := sasDone;
          Log('SAMPLE DONE: Sample='+ IntToStr(CurSample.ULSR.VialNr));

          if Next then begin
            DoCurSampleSelected;
            Log('SAMPLE NEXT: Sample='+ IntToStr(CurSample.ULSR.VialNr) + ' PRG LOADED=' + CurSample.ULSR.PrgFileName);
            CurPrg := CurSample.Prg;

            if LastSample.ULSR.PrgFileName <> CurSample.ULSR.PrgFilename then
            begin
              FCurPrg.StartEquil(timedif);
              AcqEquilStart;
              StateStart(sesRunning, timedif);
              Log('PRG ' + FCurPrg.FileName + ' EQUIL STARTED');
            end else begin
              FCurPrg.Start;
              StateStart(sesRunning, 0);
              Log('PRG ' + FCurPrg.FileName + ' STARTED');
            end;

          end else begin

            CurPrg := nil;
            SequenceState := sesFinishing;

          end;

        end;
      end;

    end else begin
      { CurPrg is runnning }
      if FAcqInfo <> nil then begin

        PumpStartOrRun('from acq timer');

        AcqDoTimer;
        Obj.UsersNotify(cmULObjFileNameChanged);{forces updating caption of browser}
      end;
    end;
  {v0.43}
  finally
    DebLog('SSequence DoTimerRun.. end');
  end;
  {/v0.43}
end;


procedure TSSequence.DoTimer(Sender: TObject);

  procedure DoStarting;
  begin
    {inc(FSubState);
    case FSubState of
      1: exit;
    end;}
    SequenceState := sesReady;{!!!for now; should wait for some condition met}
  end;

  procedure DoAborting;
  begin
    SequenceState := sesAborted;{!!!for now, should wait for some conditions met}
    SeqFormCheck(sfsAborted, '');
  end;

  procedure DoStopping;
  begin
    SequenceState := sesStandby;
  end;

  procedure DoRunnig;
  begin
    if FSubState < 10 then begin
      inc(FSubState);
      case FSubState of
        1: begin
          PumpPrgEnd('DoRunning');
        end;
      end;
    end;
    DoTimerRunningStopRunning;
    (*
    case CurSample.ULSR.SampleState of
      sasWaiting: begin
        SeqFormCheck(sfsLoadSample);
      end;    { sample waiting for processing }
      sasInLoop: begin;     { sample loaded to injector valve }
        SeqFormCheck(sfsInjectSample);
      end;
      sasRunning: ;    { sample injected }
      sasCompleting: ; { sample acquisition stopped, equilibrating }
      sasDone:;       { sample finished }
      sasError:;       { sample finished, during processing of the sample some
                       error encountered }
    end;
    *)
  end;

begin
  if FIsInTimer then
    exit;
  FIsInTimer := true;
  try
    {v0.43}
    DebLog('SSequence timer begin');
    {/v0.43}
    {Log('Timer begin');}
    case FSequenceState of
      sesStarting: DoStarting;
      sesRunning, sesStopRunning: DoRunnig;
      sesAborting: DoAborting;
      sesFinishing, sesStopping: DoStopping;
    end;
    SeqFormDoTimer;
  finally
    {Log('Timer end');}
    {v0.43}
    DebLog('SSequence timer end');
    {/v0.43}
    FIsInTimer := false;
  end;
end;

function TSSequence.FirstSampleRun: TSequenceResult;
var
  timedif:integer;
  {prgid: integer;}
begin
  if First then begin
    DoCurSampleSelected;
    Log('PRG FIRST LOADED ' + CurSample.ULSR.PrgFileName);
    CurPrg := CurSample.Prg;
    if FCurPrg.StartEquil(timedif) then begin
      PumpPrgActNrSelect(ord(TAAPLObj(FCurPrg.Obj.Childs[0]).BufferNr));
      AcqEquilStart;
      StateStart(sesRunning, timedif);
      Log('PRG ' + FCurPrg.FileName + ' EQUIL START');
      Result := 0;
    end else begin
      LogState('PRQ EQUIL START FAILED');
      Result := urInvalidProgramNoEquil;
    end;
    //CurSampleStateChanged;
  end else begin
    CurPrg := nil;
    Result := urNoSamples;
    Log('RUN: NO SAMPLES IN SEQUENCE');
  end;
end;

procedure TSSequence.SetCurPrg(APrg: TAAPrg);
begin
  if FCurPrg <> nil then begin
    FCurPrg.Active := false;
  end;

  if (FBrowsingCurPrg <> nil) then begin
    if (FBrowsingCurPrg = FCurPrg) then begin
      FCurPrg.Obj.UsersNotify(cmULObjCloseViews);
    end else begin
      FBrowsingCurPrg := nil;
    end;
  end;

  FCurPrg := APrg;

  if FCurPrg <> nil then begin
    FCurPrg.Active := true;
  end;

  if FBrowsingCurPrg <> nil then begin
    // was watching the current prog, open the view again if running:
    if FCurPrg <> nil then
      ShowRunningPrg
    else
      FBrowsingCurPrg := nil;
  end;
end;

procedure TSSequence.StateStart(ASequenceState: TSequenceState; AUsrTimeOffset: integer);
begin
  FUsrStateTimeOffset := AUsrTimeOffset;
  FStateStartTime := mstime;
  FStateTime := 0;
  FSequenceState := ASequenceState;
end;

procedure TSSequence.SetSequenceState(ASequenceState: TSequenceState);
begin
  Log('State Req=' + GetSequenceStateStr(ASequenceState) + ' (Now=' + SequenceStateStr + ')');
  try
    {v0.42}
    {if FSequenceState = ASequenceState then
      exit;}
    {/v0.42}
    case ASequenceState of
      sesStarting: begin
        StateStart(sesStarting, 0);
        TimerAdd;
      end;

      sesReady: begin
        StateStart(sesReady, 0);
      end;

      sesRunning: begin
        if FirstSampleRun = 0 then
          StateStart(sesRunning, 0);
      end;

      sesStopRunning: begin
        StateStart(sesStopRunning, 0);
      end;

      sesStopping: begin
        StateStart(sesStopping, 0);
        SeqFormCheck(sfsFinished, '');
        AcqStop;
      end;

      sesFinishing: begin
        StateStart(sesFinishing, 0);
        SeqFormCheck(sfsFinished, '');
        AcqStop;
      end;

      sesAborting: begin
        StateStart(sesAborting, 0);
        SeqFormCheck(sfsAborted, '');
        AcqStop;
      end;

      sesAborted: begin
        StateStart(sesAborted, 0);
        SeqFormCheck(sfsAborted, '');
        {TimerDel;}
      end;

      sesFinished: begin
        StateStart(sesFinished, 0);
        {TimerDel;}
        SeqFormCheck(sfsFinished, '');
      end;

      sesStandby: begin
        StateStart(sesStandby, 0);
        SeqFormCheck(sfsFinished, '');
        {TimerDel;}
      end;

    else
      SetResult(urSetSequenceStateInvalid, IntToStr(ord(ASequenceState)));
    end;
    Obj.UsersNotify(cmULObjFileNameChanged);{forces updating caption of browser}
  finally
    Log('State=' + SequenceStateStr);
  end;
end;

function TSSequence.CheckCurSample: boolean;
begin
  if CurSample = nil then begin
    Log('CurSample=nil');
    Result := false;
  end else begin
    Result := true;
  end;
end;

function TSSequence.AcqEquilStart: boolean;
var
  pn: string;
begin
  Result := false;
  if FAcqInfo <> nil then begin
    Log('AcqEquilStart - acquisition is already running');
    exit;
  end;

  if CheckCurSample then begin {sequenceu}
    FAcqData := TAcqData.Create(DataTemplateFileName, omCreate);
    pn := ChangeFileExt(ExtractFileName(CurSample.ULSR.PrgFileName),'');
    pn := DirName + pn + '_Equilibration.ULF';
    FAcqData.ChangeFileName(pn);
    AcqDataUpdateViewLimit;
    FAcqInfo := TAcqInfo.Create(FAcqData);
    try
      if not Automatic then
        FAcqInfo.OnMark := OnMark;
      SpectrumFormOpenForAcqData(FAcqData, FAcqInfo);
      FAcqInfo.Start;
      Log('AcqEquil Started ' + pn);
    except
      FAcqInfo.Free;             {acqinfou}
      FAcqInfo := nil;
      FAcqData.Free;
      FAcqData := nil;
    end;
    Result := true;
  end;
end;

procedure TSSequence.AcqEquilStop;
begin
  if FAcqInfo <> nil then begin
    Log('AcqEquil Stopping..');
    AcqStop;
  end;
end;

function TSSequence.AcqInfoCreate: boolean;
begin
  Result := false;
  if FAcqInfo <> nil then begin
    Log('AcqInfoCreate - acquisition already exists');
    exit;
  end;

  if CheckCurSample then begin {acqinfou sequenceu ulantype ulanglob}
    CheckDir;{!!!}
    FAcqData := TAcqData.Create(DataTemplateFileName, omCreate);
    FAcqData.ChangeFileName(DirName + CurSample.ULSR.FileName);
    if CurSample.ULSR.SampleName = '' then
      CurSample.ULSR.SampleName := CurSample.ULSR.FileName;{ulsrtype}
    FAcqData.ULA.AssignClass(CurSample.ULSR);
      { copy just header (class published fields, not childs) }
    FAcqData.ULM.Assign(CurSample.Method.ULM);
    FAcqData.CheckULMRecords;
    FAcqData.ULM.MethodTemplate := CurSample.ULSR.MethodFileName;

    AcqDataUpdateViewLimit;

    FAcqInfo := TAcqInfo.Create(FAcqData);
    if not Automatic then begin
      FAcqInfo.OnMark := OnMark;
    end;
    Result := true;
  end;
end;

procedure TSSequence.OnMark(Sender: TObject);
begin
  Log('Mark pressed');
  DoConfirm;
end;

function TSSequence.AcqGetReady: boolean;
begin
  Result := false;
  if FAcqInfo <> nil then begin
    Log('AcqGetReady - acquisition is already running');
    exit;
  end;
  Result := AcqInfoCreate;
  if Result then begin
    Log('AcqGetReady ' + FAcqData.ULF.FileName);
    FAcqInfo.Run;
  end;
end;

function TSSequence.AcqStart: boolean;
begin
  Result := false;
  if (FAcqInfo <> nil) and Automatic then begin
    Log('AcqStart - acquisition is already running');
    exit;
  end;
  if (FAcqInfo <> nil) or AcqInfoCreate then begin
    Log('AcqStart ' + FAcqData.ULF.FileName);
    SpectrumFormOpenForAcqData(FAcqData, FAcqInfo);
    FAcqInfo.Start;
    Result := true;
  end;
end;

procedure TSSequence.AcqDoTimer;
begin
  FAcqInfo.DoTimer;
  if FAcqInfo.SuspendedForNoData then{acqinfou ulstringgrid}
    SequenceState := sesAborting;
end;
(*
var
  dr: TExpPoint;
  y: single;
begin
  if FAcqInfo = nil then
    exit;
  if FAcqDebugStartTime <> 0 then begin
    dr.X := (mstime - FAcqDebugStartTime) / 1000;
  end else begin
    dr.X := (FCurPrg.Time {v0.25}+ FCurPrg.EquilOffset{/v0.25})/ 1000;{ aaprgu }
  end;
  if dr.X >= 0 then begin
    FAcqInfo.PointsAddStart;

    if SimulationOn then begin
      dr.Y := GetDebugAbsorbance(0, dr.X);
      FAcqInfo.PointsAddAdd(0, dr);
      dr.Y := GetDebugAbsorbance(1, dr.X);
      FAcqInfo.PointsAddAdd(1, dr);
    end else begin
      dr.Y := FDET_CHB.AsFloat;
      FAcqInfo.PointsAddAdd(0, dr);
      {v0.25}
      y := dr.Y;
      {/v0.25}{ulsqtype}
      dr.Y := FDET_CHA.AsFloat;
      FAcqInfo.PointsAddAdd(1, dr);
      {v0.25}
      if DebLogAcq then begin
        DebLog(IntToString(mstime, 10) + ' AAA.AcqDoTimer: Time=' + FloatToStrF(dr.x, ffFixed, 6, 2) +
          ' G=' + FloatToStrF(y, ffFixed, 6, 4) + ' B=' + FloatToStrF(y, ffFixed, 6, 4));
      end;
      FCHAProp.RequestRead;{SetState(psReadRequested, true);}
      FCHBProp.RequestRead;{SetState(psReadRequested, true);}
      {FCHAProp.SetFlag(pfNeedUpdateFromDevice, true);
      FCHBProp.SetFlag(pfNeedUpdateFromDevice, true);}
      {/v0.25}
    end;

    FAcqInfo.PointsAddStop;
  end;
end;
*)

procedure TSSequence.AutoSaveAll;
begin
  Save;
{  DefPrg.Save;
  DefMethod.Save;}
end;

procedure TSSequence.AcqShow;
begin
  if (FAcqInfo <> nil) and (FAcqData <> nil) then begin
    if FAcqInfo.TheSpecForm <> nil then
      FAcqInfo.TheSpecForm.BringToFront
    else
      SpectrumFormOpenForAcqData(FAcqData, FAcqInfo);
  end;
end;

procedure TSSequence.AcqDataUpdateViewLimit;
begin
  if (CurSample <> nil) and (FAcqData <> nil) then
    FAcqData.ULVL.MaxX := CurSample.Prg.TotalSecTime / 60;
end;

procedure TSSequence.AcqStop;
var
  ai: TAcqInfo;
  ad: TAcqData;
begin
  if (FAcqInfo = nil) and (FAcqData = nil) then
    exit;
  Log('AcqStop begin');

  ai := FAcqInfo;
  FAcqInfo := nil;{disables timer calls}
  ad := FAcqData;
  FAcqData := nil;

  if ai <> nil then begin
    if ad <> nil then
      ad.ULA.AutoDetectLater := true;
    ai.Stop;
    ai.Free;
    Log('AcqStopped');
  end;

  if ad <> nil then begin
    ad.Free;
  end;

  PumpPrgEnd('AcqStop');

  AutoSaveAll;
  Log('AcqStop end');
end;


procedure TSSequence.SequenceRun(Sender: TObject);
begin {aaau}
  SequenceState := sesRunning;
end;

procedure TSSequence.PrgEdit(Sender: TObject);
begin
  if DefPrg.RelFileName <> ULSQ.PrgFileName then begin
    DefPrg.FullFileName := ULSQ.PrgFileName;
    DefPrg.LoadFromFile('');
  end;
  AAPrgBrowserOpen(DefPrg.FileName, omRead);
{  DefPrg.Browse;}
end;

procedure TSSequence.DoAcqShow(Sender: TObject);
begin
  AcqShow;
end;

procedure TSSequence.MethodEdit(Sender: TObject);
var m: TUlanMethod;
begin
  if DefMethod.RelFileName <> ULSQ.MethodFileName then begin
    DefMethod.FullFileName := ULSQ.MethodFileName;
    DefMethod.LoadFromFile('');
  end;
  m := TUlanMethod.Load(DefMethod.FileName);
  try
    if m.EditModal = mrOK then begin
      m.Obj.DoFileSave;
      ULSQ.MethodFileName := m.RelFileName;
    end;
  finally
    m.Free;
  end;
  {
  if DefMethod.EditModal = mrOK then begin
    DefMethod.Obj.DoFileSave;
    ULSQ.MethodFileName := DefMethod.RelFileName;
  end;}
end;
{
procedure TSSequence.SequenceStop(Sender: TObject);
begin
  SequenceState := sesStopping;
end;
}
procedure TSSequence.SeqFormFree;
var
  f: TSeqForm;
begin
  f := TSeqForm(FSeqForm);
  FSeqForm := nil;
  {v0.43}
  if f <> nil then begin
    f.Sequence := nil;
    f.Release;
  end;
  {/v0.43
  f.Free;}
end;

destructor TSSequence.Destroy;
begin
  {v0.47}
  if Obj <> nil then begin
    {if CompareRec(Obj.OnGetWindowCaption, TMethod(OnGetWindowCaption)) = 0 then}
    Obj.OnGetWindowCaption := nil;
  end;
  {/v0.47}

  TimerDel;
  SeqFormFree;
  AcqStop;
  FDataTemplate.Free;
  inherited;
end;

function TSSequence.GetDefPrg: TAAPrg;
begin
  Result := inherited GetDefPrg;
{  if Result.FileName = '' then
    Result.FullFileName := ULRecDefFileName(AAPGID);}
end;

function TSSequence.GetDirName: string;
var
  n: string;
  i: integer;
  fn: string;
begin
  n := ULSQ.SequenceName;
  for i := 1 to length(n) do begin
    if n[i] in [':',',','/','\'] then
      n[i] := '_';
  end;
  fn := ULSQ.RootFileDir;
  if fn = '' then
    fn := ULSQ.ObjDesc.DefDir;
  if n <> '' then
  Result := AddBackSlash(fn) + n + '\'
end;

procedure TSSequence.ShowRunningPrg;
begin
  if FCurPrg <> nil then begin
    FBrowsingCurPrg := FCurPrg;
    FCurPrg.Browse;
  end else begin
    FBrowsingCurPrg := nil;
  end;
end;

{/TSSequence}
{/v0.41}

{v0.32}
function SequenceBrowserOpen(const AFileName: shortstring; AMode: TOpenMode): boolean;
var
  s: TSequence;
  t: TSequence;
  fn: string;
  ext: string;
  {v0.41}
  sf: TSequenceClass;
  {/v0.41}
begin
  {v0.41}
  if AAAActive then
    sf := TSequence
  else
    sf := TSSequence;
  {/v0.41}
  if ChangeFileExt(ExtractFileName(AFileName), '') = 'Default' then begin
    fn := ''
  end else
    fn := AFileName;

  ext := ExtractFileExt(fn);
  if ext = '' then
    ext := ULSExt;
  if (AMode = omCreate) then begin
    if (fn <> '') then begin
      fn := ChangeFileExt(fn, ext);{ ulsqtype ulanrecs.lst}
      if FileExists(fn) then begin
        t := {v0.41}sf{/v0.41 TSequence}.Load(fn);
        try
          s := {v0.41}sf{/v0.41 TSequence}.Load('');
          try
            s.Obj.Assign(t.Obj);
            {aaau}
          except
            s.Free;
            raise;
          end;
        finally
          t.Free;
        end;
      end else begin
        raise Exception.Create(GetTxt({#}'Sequence template not found') + ' ' + fn);
      end;
      {v0.41}
      if ExtractFileExt(AFileName) = ULTExt then begin
        if s is TSSequence then
          TSSequence(s).DataTemplateFileName := AFileName;
      end;
      {/v0.41}
    end else begin
      s := {v0.41}sf{/v0.41 TSequence}.Load('');
    end;
  end else begin
    s := {v0.41}sf{/v0.41 TSequence}.Load(fn);
    {v0.41}
    {if ExtractFileExt(AFileName) = ULTExt then begin
      if s is TSSequence then
        TSSequence(s).DataTemplateFileName := AFileName;
    end;}
    {/v0.41}
  end;
  s.Obj.BrowserAutoClose := true;
  s.Obj.SetFlag(rfBrowseModal, false);
  s.Browse;
  Result := true;
end;
{/v0.32}

{v0.24}
initialization
  RegisterClasses([TSequence, TSeqSample]);
{/v0.24}
end.
