unit Processoru;{v0.49}{ Nonvisible object for controlling sample sequences / channels }
{
  (C) 2000 - 2001 Jindrich Jindrich, Pavel Pisa, PiKRON Ltd.

  Originators of the CHROMuLAN project:

  Jindrich Jindrich - http://www.jindrich.com
                      http://orgchem.natur.cuni.cz/Chromulan
                      software developer, project coordinator
  Pavel Pisa        - http://cmp.felk.cvut.cz/~pisa
                      embeded software developer
  PiKRON Ltd.       - http://www.pikron.com
                      project initiator, sponsor, instrument developer

  The CHROMuLAN project is distributed under the GNU General Public Licence.
  See file COPYING for details.

  Originators reserve the right to use and publish sources
  under different conditions too. If third party contributors
  do not accept this condition, they can delete this statement
  and only GNU license will apply.
}

interface
uses
  SysUtils, Classes, Controls, ActnList, Menus, Dialogs, Forms,
  FileCtrl,
  UtlType, WinUtl, Msgu, Language, ExeLogu, Timer, Timersu,
  ULRecTyp, ULRecUtl, ULObju,
  {$IFNDEF CONSOLE}
  ULStringGrid,
  {$ENDIF}
  ULObjUsru,
  UlanType,
  ULMType, ULIType,
  AAPGType,
  AAPLType, AAPLObju, AAPrgu,
  USPType,
  ULSRType,
  Sequenceu, Prgu, Modulu, ModuUtl,
  UlanGlob,
  AcqInfou, Spectrum, SModulu,
  {ulanrecs.lst}
  UPOType, UPOObju,
  {$IFNDEF CONSOLE}
  SpecForm,
  {$ENDIF}
  Methodu
  {v0.50}
  ,Channelsu, SeqPrgu
  {/v0.50}
  {v0.52}
  , stru, ULScriptType
  {/v0.52}
  ;

type
  TSeqFormState = (
    sfsIdle,  // nothing to do - does not change the last sample name when set
    sfsLoadSample, // user should load sample
    sfsLoaded, // user is informed that the sample was loaded
    sfsInjectSample, // user should inject sample
    sfsInjected,     // user 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
  );

  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
    {v0.52}
    ,sesWaiting     // running, but just now waiting for event (usually mark)
    {/v0.52}
    ); // ... same as AAAType.TAAAState


  TProcessor = class(TULObjUsr){was in TSSequence}
  private
    FSequence: TSequence;
      { non nil if running sequence }
    FChannelName: string;
      { on what channel this processor works }
    FLogHead: string;
      { changed runtime, used in Log procedure }
    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;
    FSequenceStopRunningAction: TAction;
    {FNulAction: TAction;
    FPrgEditAction: TAction;
    FMethodEditAction: TAction;}
    FDoAcqShowAction: TAction;
    FShowActPrgAction: TAction;
    {/actions}

    FSeqForm: TForm;
      { comunicates with usr (old one) }
    FForm: TForm;
      { processor form }
    FCurPrg: TSeqPrg;
    FBrowsingCurPrg: TPrg;{ non nil, if for FCurPrg was called ShowRunning }

    FAcqInfo: TAcqInfo;
    FAcqData: TAcqData;

    {FDataTemplate: TAcqData;}
    {
    FPump: TPumpModule;
    FPumpTimerLogged: boolean;

    FAutoSampler: TAutoSamplerModule;
    FAutoSamplerChecked: boolean;}
      { already tried to find autosampler }

    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: TSeqPrg);
    procedure StateStart(ASequenceState: TSequenceState; AUsrTimeOffset: integer);
    {v0.55}
    procedure StateRunSuspend;
    procedure StateRunResume;
    {/v0.55}
    {v0.56}
    procedure CurPrgStart;
    procedure HookOnMarkToAcqInfo;
      { Assigns Self.OnMark to FAcqInfo.OnMark, calls HookOnMarkToModules(false) }
    procedure HookOnMarkToModules(OnOff:boolean);
      { Called with true if WaitForMark script called (CurPrg suspended
        for 'mark'). Hooks Self.OnMark event directly to each current channel's
        module OnMark event. Called with false (unhook) from resume, destroy or
        before hooking to AcqInfo.OnMark from HookOnMarkToAcqInfo }
    {/v0.56}

    {function GetDataTemplateFileName: TFileName;
    procedure SetDataTemplateFileName(const AFileName: TFileName);}

    {procedure DefMethodSetDefaults;}
    {procedure DefPrgCreateDefault;}

    {function GetNewSequenceName: string;}
    {procedure CheckDir;{sequenceu}
    procedure OnGetWindowCaption(var ACaption: string);
    procedure ShowRunningPrg;
    {function GetDataTemplate: TAcqData;}
    function GetSampleToLoad: TSeqSample;
    {function GetPump: TPumpModule;
    function GetAutoSampler: TAutoSamplerModule;
    function AutoSamplerLoad(AVialNr: integer): boolean;
    function AutoSamplerInject: boolean;}
    {function GetAutomatic: boolean;}

    procedure OnMark(Sender: TObject);
    {
    procedure PumpPrgRun(const msg: string);
    procedure PumpPrgEnd(const msg: string);
    procedure PumpPrgActNrSelect(i:integer);
    procedure PumpStartOrRun(const msg: string);
    }
    function GetSequence: TSequence;
    procedure SetSequence(ASequence: TSequence);

    function GetCurSample: TSeqSample;
      { = Sequence.CurSample }
    {v0.50}
    function GetChannel: TChannel;
    function GetNextSample: TSeqSample;
    function GetRunning: boolean;
    {/v0.50}
    {v0.51}
    function CheckCurSamplePrg: boolean;
    {/v0.51}
    {v0.52} {properties for ProcessFrm}
    function GetSequenceName: string;
    {function GetSequenceStateStr: string;}
    function GetActiveSampleName: string;
    function GetActivePrgTimeStr: string;
    procedure SetChannelName(const AName: string);
    function GetChannelName: string;
    {/v0.52}
  protected
    class function GetClassRecID: TULRecID; override;
    function GetUPO: TUPOObj;
    {v0.52}             {ulobjusru}
    function ChildCreate(AChildObj: TULObj): TULObjUsr; override;
    procedure ChildDestroyed(AChild: TULObjUsr); override;
    {/v0.52}

    procedure DoTimer(Sender: TObject);{register in timersu}
      procedure DoTimerRunningStopRunning;

    procedure FirstMenuActionNeeded; override;
    procedure LogState(const msg: string);
    procedure ObjUpdated; override;
    function CheckCurSample: boolean;
    {v0.50}
    function CheckSequence: boolean;
    {/v0.50}
    function FirstSampleRun: TSequenceResult;

    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: TPrg;    {override;}

    function GetSequenceStateStr(ASequenceState:TSequenceState): string;
    function GetCurSequenceStateStr: string;

    {v0.50}
    procedure UsrReqState(ASequenceState: TSequenceState);
    {/v0.50}
  public
    constructor Create(AOwner: TULObjUsr; AObj: TULObj; ARecID: TULRecID); reintroduce;
    constructor Load(const AFileName: string); override;
    destructor Destroy; override;
    {procedure FileNew; override;}

    procedure FormClear;
      { called from the processor's form when it is beeing destroyed }
    procedure FormShow;
      { called from standalone sequence browser local menu to show the processor
        window, enable start/stop of sequence }

    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 }

    {action/button handlers}{sequenceu}
    procedure SequenceBrowse(Sender: TObject);{ called from processor form }
    procedure SequenceRun(Sender: TObject);
      { start the first (non processed yet) sample of the sequence }
    procedure SequenceStopRunning(Sender: TObject);
      { stop running of the current sequence }
    procedure PrgEdit(Sender: TObject);
    procedure MethodEdit(Sender: TObject);
    procedure DoAcqShow(Sender: TObject);
    procedure ShowActPrgAction(Sender: TObject);
    {/action handlers}
    {v0.55}
    procedure DoMark(Sender: TObject);
      { can be called directly to simulate mark from device; calls OnMark }
    {/v0.55}


    property SequenceState: TSequenceState read FSequenceState write SetSequenceState;
    property CurPrg: {v0.49}TSeqPrg{/v0.49 TAAPrg} read FCurPrg write SetCurPrg;
    {property DataTemplateFileName: TFileName read GetDataTemplateFileName write SetDataTemplateFileName;}
    property LogHead: string read FLogHead write FLogHead;
    {property Automatic: boolean read GetAutomatic;}
    {property DataTemplate: TAcqData read GetDataTemplate;{set just through DataTemplateFileName}
    {property Pump: TPumpModule read GetPump;
    property AutoSampler: TAutoSamplerModule read GetAutoSampler;}
    property UPO: TUPOObj read GetUPO;

    property Sequence: TSequence read GetSequence write SetSequence;
    property CurSample: TSeqSample read GetCurSample;
    {v0.50}
    procedure Log(const msg: string); override;
    property CurData: TAcqData read FAcqData;
    property NextSample: TSeqSample read GetNextSample;
    property Channel: TChannel read GetChannel;
    property Running: boolean read GetRunning;
    {/v0.50}
    property ChannelName: string read {v0.52}GetChannelName
      {/v0.52 FChannelName} write {v0.52}SetChannelName{/v0.52 FChannelName};
  {v0.52}
  published
    property SequenceName: string read GetSequenceName;
    property SequenceStateStr: string read GetCurSequenceStateStr;
    property ActiveSampleName: string read GetActiveSampleName;
    property ActivePrgTimeStr: string read GetActivePrgTimeStr;
  {/v0.52}
  end;

  TProcessors = class(TULObjUsr)
  protected
    class function GetClassRecID: TULRecID; override;
    function ChildCreate(AChildObj: TULObj): TULObjUsr; override;
    function GetActiveProcessor: TProcessor;
      { the one that corresponds to activechannel (activechild in ULL) }
  public
    function ProcessorGet(const AChannelName: string): TProcessor;
    {v0.55}
    procedure DoMark(Sender: TObject);
      { send mark to the active processor, if any; used for simulation
        of mark from device }
    {/v0.55}
    property ActiveProcessor: TProcessor read GetActiveProcessor;
  end;

function Processors: TProcessors;
{v0.59}
procedure ProcessorsFree;
{/v0.59}
implementation
uses
  SeqFrm{v0.50}, ProcessFrm{/v0.50};


{TProcessor}
{procedure TProcessor.BeforeDelete;
begin
end;}

function TProcessor.GetUPO: TUPOObj;
begin
  Result := TUPOObj(Obj);
end;

constructor TProcessor.Create(AOwner: TULObjUsr; AObj: TULObj; ARecID: TULRecID);
begin
  inherited;
end;

class function TProcessor.GetClassRecID: TULRecID;
begin
  Result := UPOID;
end;

{
procedure TProcessor.FieldChangedInBrowser(AField: TULObjField);
begin
  if (AField.FldDesc.Name = 'UsrTmpName'
  begin

  end;
end;}


procedure TProcessor.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;
}
begin
  inherited;
{  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 Sequence <> nil then
    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'));
  if SequenceState = sesRunning then
    ActionUpdate(FSequenceStopRunningAction, SequenceStopRunning, GetTxt({#}'Stop'));
  {AddClearError;}
end;

procedure TProcessor.ShowActPrgAction(Sender: TObject);
begin
  ShowRunningPrg;
end;
{
procedure TProcessor.DefMethodSetDefaults;
begin
  if Sequence <> nil then begin
    Sequence.DefMethod.ULM.NoUnknownPeaks := true;
    Sequence.DefMethod.Save;
  end;
end;
}

constructor TProcessor.Load(const AFileName: string);
begin
  inherited;{ulsqtype}
  {v0.52}
  CurPrg := nil;
  {/v0.52}
  SequenceState := sesStarting;
  Obj.OnGetWindowCaption := OnGetWindowCaption;
end;

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

function TProcessor.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, Sequence.DataTemplate.Channel);
        except
        end;
      end;
    end;
  end;
  Result := FAutoSampler;
end;

function TProcessor.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 TProcessor.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 TProcessor.GetAutomatic: boolean;
begin
  Result := AutoSampler <> nil;
end;}


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

function TProcessor.GetCurSequenceStateStr: string;
begin
  Result := GetSequenceStateStr(SequenceState);
end;

function TProcessor.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 }
    {v0.52}
    sesWaiting: begin
       s := 'Waiting';
       if (CurPrg <> nil) and (CurPrg.USP.WaitingFor <> '') then begin
         s := s + ' for ' + CurPrg.USP.WaitingFor;
       end;
    end;
    {/v0.52}
  else
    s := 'Unknown';
  end;
  Result := s;
end;

{procedure TProcessor.ClassFieldsCreate;
begin
  inherited;
{  if Sequence <> nil then with Sequence do begin
    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('');
  end;
end;}

{
procedure TProcessor.CheckDefPrg;
begin
  if Sequence <> nil then with Sequence do begin
    DefPrg.FullFileName := ULSQ.PrgFileName;
    if (DefPrg.FileName = '') or (pos('NONAME', Uppercase(DefPrg.FileName)) <> 0) then begin
      DefPrg.FullFileName := ULRecDefFileName(PrgRecID);
      ULSQ.PrgFileName := DefPrg.RelFileName;
    end;
    if (DefPrg.RelFileName = ULRecDefFileName(AAPGID))
      or (DefPrg.RelFileName = ULRecDefFileName(USPID))
    then begin
      if (not FileExists(DefPrg.FullFileName))then begin
        //DefPrgCreateDefault;
      end;
    end;
    DefPrg.LoadFromFile('');
    if DefPrg.Obj.ObjDesc.DefDir = '' then
      DefPrg.Obj.ObjDesc.DefDir := ulanglob.PrgDir;
  end;
end;

procedure TProcessor.DoAfterCreate;
begin
  // UpdateDefDirs;
end;
}
{v0.49}
{procedure TProcessor.UpdateDefDirs;
var fd: TULObjFldDesc;
begin
  Obj.ObjDesc.DefDir := ulanglob.SequenceDir;

  fd := Obj.FindField('PrgFileName').FldDesc;
  fd.DefDir := ulanglob.PrgDir;
  fd.Filter := USPFilter;

  Obj.FindField('MethodFileName').FldDesc.DefDir := ulanglob.MethodDir;
  DefMethod.Obj.ObjDesc.DefDir := ulanglob.MethodDir;
  DefMethod.UpdateDefDirs;

  DefPrg.Obj.ObjDesc.DefDir := ulanglob.PrgDir;
  DefPrg.Obj.ObjDesc.OpenFilter := USPFilter;
end;}

{procedure TProcessor.DoAfterLoad;
var bn: string;
begin
  inherited;
  bn := ChangeFileExt(ExtractFileName(ULSQ.PrgFileName), '');
  if (bn = 'NONAME') or (bn = 'AAPG') then begin
    Obj.FindField('PrgFileName').FldDesc.Filter := USPFilter;
    Obj.FindField('PrgFileName').FldDesc.DefDir := ulanglob.PrgDir;
    ULSQ.PrgFileName := '';
    CheckDefPrg;
  end;
end;}
{/v0.49}


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

procedure TProcessor.FileNew;
begin
  if Sequence <> nil then with Sequence do begin
    ClassFieldsUpdate;

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

procedure TProcessor.CheckDir;
begin
  if Sequence <> nil then with Sequence do begin
    DefMethod.Obj.FindField('CalibrationFileName').FldDesc.DefDir := DirName;
    CreateDir(DirName);
    if not FileExists(FileName) then
      Save;
  end;
end;
}
procedure TProcessor.LogState(const msg: string);
begin
  ExeLog.LogErr('Processor.' + LogHead + ' ' + msg);
end;

procedure TProcessor.Log(const msg: string);
begin
  inherited Log(LogHead + ' ' + msg);
end;

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

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

procedure TProcessor.DoCurSampleSelected;
var
  pn, mn: string;
begin
  if Sequence <> nil then with Sequence do begin

    pn := AbsoluteFileName(DefPrg.Obj.ObjDesc.DefDir, CurSample.ULSR.PrgFileName, {v0.49}PrgExt{v0.49 AAPGExt});
    if pn <> '' then begin
      CurSample.Prg.LoadFromFile(pn);{v0.55}CurSample.Log('Sequence program (unique for sample) loaded from file: ' + pn);{/v0.55}
    end else begin
      CurSample.Prg.Obj.Assign(Sequence.DefPrg.Obj);
      CurSample.Prg.ClassFieldsUpdate;{ulobju ulobjusru}{v0.55}CurSample.Log('Sequence program (default for sequence) assigned.');{/v0.55}
    end;

    mn := AbsoluteFileName(DefMethod.Obj.ObjDesc.DefDir, CurSample.ULSR.MethodFileName, ULMExt);
    if mn <> '' then begin
      CurSample.Method.LoadFromFile(mn);{v0.55}CurSample.Log('Method (unique for sample) loaded from file: ' + mn);{/v0.55}
    end else begin
      CurSample.Method.Obj.Assign(Sequence.DefMethod.Obj);
      CurSample.Method.ClassFieldsUpdate;{v0.55}CurSample.Log('Method (default for sequence) assigned.');{/v0.55}
    end;
  end;
end;


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

procedure TProcessor.CurSampleStateChanged;
begin
  UPO.DoChange;
end;

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

procedure TProcessor.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 TProcessor.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 TProcessor.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 TProcessor.SetDataTemplateFileName(const AFileName: TFileName);
begin

  if (AFileName = ULSQ.DataTemplateFileName) then
    exit;
  FDataTemplate.Free;
  FDataTemplate := nil;
  ULSQ.DataTemplateFileName := AFileName;
end;

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

procedure TProcessor.SeqFormCheck(sfs: TSeqFormState; const ASampleName: string);
begin
  {v0.52}
  {/v0.52
  if FSeqForm = nil then begin
    FSeqForm := TSeqForm.Create(Self);
    TSeqForm(FSeqForm).Processor := 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 TProcessor.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 TProcessor.GetSampleToLoad: TSeqSample;
begin
  Result := nil;
  if Sequence <> nil then with Sequence do begin
    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;
end;

(*
procedure TProcessor.DoLoadSample;
var
  cs: TSeqSample;
  vnr: integer;
begin
{  if not Automatic then
    CurPrg.Resume;}
  if Sequence = nil then
    exit;
  with Sequence do begin
    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;

  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 TProcessor.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 TProcessor.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 TProcessor.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 TProcessor.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 TProcessor.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;
*)

function TProcessor.CheckCurSamplePrg: boolean;
begin
  Result := true;
  if not (CurSample.Prg is TSeqPrg) then begin
    Result := false;
    Log('CurSample Prg in old format');
  end;
end;

procedure TProcessor.DoTimerRunningStopRunning;
var
  pl: TPrgLine;
  timedif: integer;

  (*
  function DoInject: boolean;
  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
    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
  DebLog('Processor DoTimerRun begin');
  try
    if FCurPrg = nil then begin
      DebLog('Processor DoTimerRun ... FCurPrg = nil');
      exit;
    end;
    {v0.55}
    if FCurPrg.RunningBeforeStartLines then
      exit;
    {/v0.55}
    {/v0.43}
    if FCurPrg.LineReady(pl, FStateStartTime) then begin

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

      try
        Log('START');

        (*v0.50*)
        if pl is TSeqPrgLine then begin
          TSeqPrgLine(pl).RunScript;
          if pl is TSeqPrgTimeLine then with pl as TSeqPrgTimeLine do begin

            case USPT.Command of

              scAcqStart: begin
                AcqStart;
                CurSample.ULSR.SampleState := sasRunning;
                CurSampleStateChanged;
                Log('CMD ACQ START');
              end;

              scAcqStop: begin
                AcqStop;
                CurSample.ULSR.SampleState := sasCompleting;{ulsrtype}
                CurSampleStateChanged;
                Log('CMD ACQ STOP');
              end;

            end;
          end;
        end;
        (*/v0.50
        if pl is TAAPrgLine then
        with pl as TAAPrgLine do
        {/v0.49}
        begin

        case {v0.49}{/v0.49 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;
        {v0.49}
        end;
        {/v0.49}
        *)

        {/command}

      finally
        Log('END');
        LogHead := '';
        Log('');
        {v0.38}
        {v0.49}
        pl.State := plsDone;
        {/v0.49
        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 }
          {v0.52}
          {v0.56}
          CurPrgStart;
          {/v0.56
          StateStart(sesRunning, 0);
          FCurPrg.Start;}
          {/v0.52
          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 Sequence.Next then begin
            DoCurSampleSelected;
            Log('SAMPLE NEXT: Sample='+ IntToStr(CurSample.ULSR.VialNr) + ' PRG LOADED=' + CurSample.ULSR.PrgFileName);
            {v0.51}
            if not CheckCurSamplePrg then begin
              CurPrg := nil;
              SequenceState := sesAborting;
              exit;
            end;
            {/v0.51}
            CurPrg := TSeqPrg(CurSample.Prg);

            if (Sequence.LastSample.ULSR.PrgFileName <> CurSample.ULSR.PrgFilename)
               {v0.52}
               and FCurPrg.HasEquilCommand
               {/v0.52}
            then
            begin
              FCurPrg.StartEquil(timedif);
              AcqEquilStart;
              StateStart(sesRunning, timedif);
              Log('PRG ' + FCurPrg.FileName + ' EQUIL STARTED');
            end else begin
              {v0.52}
              {v0.56}
              CurPrgStart;
              {/v0.56
              StateStart(sesRunning, 0);
              FCurPrg.Start;}
              {/v0.52
              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('Processor DoTimerRun.. end');
  end;
  {/v0.43}
end;


procedure TProcessor.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
    {v0.52}
    case SequenceState of
      sesFinishing: begin
        SequenceState := sesFinished;
      end;
    else
      SequenceState := sesStandby;
    end;
    {/v0.52
    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('Processor timer begin');
    {/v0.43}
    case FSequenceState of
      sesStarting: DoStarting;
      sesRunning, sesStopRunning: DoRunnig;
      sesAborting: DoAborting;
      sesFinishing, sesStopping: DoStopping;
      {v0.52}
      sesWaiting: {do nothing?};
      {/v0.52}
    end;
    SeqFormDoTimer;
    {v0.52}
    UPO.DoChange;
    {/v0.52}
  finally
    {v0.43}
    DebLog('Processor timer end');
    {/v0.43}
    FIsInTimer := false;
  end;
end;

{v0.56}
procedure TProcessor.CurPrgStart;
begin
  StateStart(sesRunning, 0);
  FCurPrg.Start;

end;
{/v0.56}

function TProcessor.FirstSampleRun: TSequenceResult;
var
  timedif:integer;
  {prgid: integer;}
begin
  if (Sequence <> nil) and Sequence.First then
  begin
    DoCurSampleSelected;
    if not CheckCurSamplePrg then begin
      CurPrg := nil;
      Result := urInvalidProgramFormat;{sequenceu}
      exit;
    end;
    CurPrg := TSeqPrg(CurSample.Prg);
    Log('PRG - FIRST ASSIGNED ' + CurPrg.FileName);
    {v0.51}
    if not FCurPrg.HasEquilCommand then begin
      {v0.52}
      {v0.56}
      CurPrgStart;
      {/v0.56
      StateStart(sesRunning, 0);
      FCurPrg.Start;}
      {/v0.52
      FCurPrg.Start;
      StateStart(sesRunning, 0);}
      Log('PRG ' + FCurPrg.FileName + ' STARTED Sample ' +
        IntToStr(CurSample.ULSR.VialNr) + ' ' + CurSample.ULSR.FileName);
    end else
    {/v0.51}
    begin
      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;
    end;
    //CurSampleStateChanged;
  end else begin
    CurPrg := nil;
    Result := urNoSamples;
    Log('RUN: NO SAMPLES IN SEQUENCE');
  end;
end;

procedure TProcessor.SetCurPrg(APrg: TSeqPrg);
begin
  if FCurPrg <> nil then begin
    FCurPrg.Active := false;
    {v0.51}
    FCurPrg.Script.Usr := nil;{ulscriptu}
    {/v0.51}
  end;

  if (FBrowsingCurPrg <> nil) then begin
    if (FBrowsingCurPrg = FCurPrg) then begin
      FCurPrg.Obj.UsersNotify(cmULObjCloseViews);
    end else begin
      FBrowsingCurPrg := nil;
    end;
  end;
  {v0.52}
  if FCurPrg <> nil then
    FCurPrg.Obj.Free;{FCurPrg will be set nil in ObjDestroyed}
  if APrg <> nil then begin
    ChildAdd(nil, USPID, '');{FCurPrg will be set in ChildCreate}
    FCurPrg.Obj.Assign(APrg.Obj);
  end;
  {/v0.52
  FCurPrg := APrg;}

  if FCurPrg <> nil then begin
    FCurPrg.Active := true;
    {v0.51}
    FCurPrg.Script.Usr := Self;
    {/v0.51}
  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 TProcessor.StateStart(ASequenceState: TSequenceState; AUsrTimeOffset: integer);
begin
  FUsrStateTimeOffset := AUsrTimeOffset;
  FStateStartTime := mstime;
  FStateTime := 0;
  FSequenceState := ASequenceState;
end;

{v0.55}
procedure TProcessor.StateRunSuspend;
begin
  FSequenceState := sesWaiting;
  {v0.56}
  if (CurPrg <> nil) and (CurPrg.USP.WaitingFor = 'mark') then
    HookOnMarkToModules(true);
  {/v0.56}
  if FForm <> nil then
    FForm.BringToFront;
end;

procedure TProcessor.StateRunResume;
begin
  FSequenceState := sesRunning;
  {v0.56}
  HookOnMarkToModules(false);
  {/v0.56}
end;
{/v0.55}

{v0.56}
procedure TProcessor.HookOnMarkToAcqInfo;
begin
  if FAcqInfo <> nil then begin
    HookOnMarkToModules(false);
    FAcqInfo.OnMark := OnMark;
  end;
end;

procedure TProcessor.HookOnMarkToModules(OnOff:boolean);
var
  i: integer;
  d: TModule;
  c: TChannel;
begin
  {channelsu}
  c := Channel;
  for i := 0 to c.DeviceCount - 1 do begin
    d := c.Devices[i];
    if OnOff then begin
      d.OnMark := OnMark
    end else begin
      d.OnMark := nil;
    end;
  end;
end;
{/v0.56}

procedure TProcessor.UsrReqState(ASequenceState: TSequenceState);
begin
  SequenceState := ASequenceState;
end;


procedure TProcessor.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
        {v0.55}
        if SequenceState = sesRunning then
          CurPrg := nil;
        {/v0.55}
        StateStart(sesReady, 0);
      end;

      {v0.52}
      sesWaiting: begin              {called from curprg.suspend}
        if SequenceState = sesRunning then begin
          {v0.55}
          StateRunSuspend;
          {/v0.55
          StateStart(sesWaiting, 0);}
        end;
      end;
      {/v0.52}
      sesRunning: begin
        {v0.52}
        if SequenceState = sesWaiting then begin
          CurPrg.Resume;
          {v0.55}
          StateRunResume;
          {/v0.55
          StateStart(sesRunning, 0);}
        end else
        {/v0.52}
        begin
          {v0.52}
          if SequenceState < sesRunning then
            FirstSampleRun;
          {/v0.52
          if FirstSampleRun = 0 then
            StateStart(sesRunning, 0);}
        end;
      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;

{v0.50}
function TProcessor.CheckSequence: boolean;
begin
  if Sequence = nil then begin
    Log('AcqEquilStart - Sequence = nil');
    Result := false;
  end else
    Result := true;
end;
{/v0.50}

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

function TProcessor.AcqEquilStart: boolean;
var
  pn: string;
begin
  Result := false;
  if FAcqInfo <> nil then begin
    Log('AcqEquilStart - acquisition is already running');
    exit;
  end;
  if CheckSequence then begin
    if CheckCurSample then begin
      {v0.62}
      FAcqData := TAcqData.Create(CurSample.DataTemplateFileName, omCreate);
      {/v0.62
      FAcqData := TAcqData.Create(Sequence.DataTemplateFileName, omCreate);}
      pn := ChangeFileExt(ExtractFileName(CurSample.ULSR.PrgFileName),'');
      pn := Sequence.DirName + pn + '_Equilibration.ULF';
      FAcqData.ChangeFileName(pn);
      AcqDataUpdateViewLimit;
      FAcqInfo := TAcqInfo.Create(FAcqData);
      try
        {if not Automatic then}
        {v0.56}
        HookOnMarkToAcqInfo;
        {/v0.56
        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;
end;

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

function TProcessor.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}
    Sequence.CheckDir;{!!!}
    {v0.62}
    FAcqData := TAcqData.Create(CurSample.DataTemplateFileName, omCreate);
    {/v0.62
    FAcqData := TAcqData.Create(Sequence.DataTemplateFileName, omCreate);}
    FAcqData.ChangeFileName(Sequence.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) {ulobju}
    FAcqData.ULM.Assign(CurSample.Method.ULM);
    FAcqData.CheckULMRecords;
    FAcqData.ULM.MethodTemplate := CurSample.ULSR.MethodFileName;
    AcqDataUpdateViewLimit;
    FAcqInfo := TAcqInfo.Create(FAcqData);
    {if not Automatic then}
    begin
      {v0.56}
      HookOnMarkToAcqInfo;
      {/v0.56
      FAcqInfo.OnMark := OnMark;
      }
    end;
    Result := true;
  end;
end;

procedure TProcessor.OnMark(Sender: TObject);
begin
  Log('Mark pressed');
  {v0.52}
  if CurPrg <> nil then begin   {ulscripttype}
    if CurPrg.USP.WaitingFor = neMark then
      CurPrg.Resume;
  end;
  {/v0.52}
  {v0.55}
  SequenceState := sesRunning;
  {/v0.55}
  DoConfirm;
end;

function TProcessor.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 TProcessor.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 TProcessor.AcqDoTimer;
begin
  FAcqInfo.DoTimer;
  if FAcqInfo.SuspendedForNoData then{acqinfou ulstringgrid}
    SequenceState := sesAborting;
end;


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

procedure TProcessor.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 TProcessor.AcqDataUpdateViewLimit;
begin
  if (CurSample <> nil) and (FAcqData <> nil) then
    FAcqData.ULVL.MaxX := CurSample.Prg.TotalSecTime / 60;
end;

procedure TProcessor.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 TProcessor.SequenceRun(Sender: TObject);
begin
  if Sequence = nil then
    exit;
  SequenceState := sesRunning;
end;

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

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

procedure TProcessor.MethodEdit(Sender: TObject);
var m: TUlanMethod;
begin
  if Sequence = nil then
    exit;
  with Sequence do 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;
end;

procedure TProcessor.SequenceStopRunning(Sender: TObject);
begin
  UsrReqState(sesReady);{SequenceState := sesStopping;}
end;

procedure TProcessor.SeqFormFree;
var
  f: TSeqForm;
begin
  f := TSeqForm(FSeqForm);
  FSeqForm := nil;
  {v0.43}
  if f <> nil then begin
    f.Processor := nil;
    f.Release;
  end;
  {/v0.43
  f.Free;}
end;

destructor TProcessor.Destroy;
begin
  {0.56}
  HookOnMarkToModules(false);
  {/v0.56}
  {v0.47}
  if Obj <> nil then begin
    {if CompareRec(Obj.OnGetWindowCaption, TMethod(OnGetWindowCaption)) = 0 then}
    Obj.OnGetWindowCaption := nil;
  end;
  {/v0.47}
  TimerDel;
  SeqFormFree;
  AcqStop;
  inherited;
end;

function TProcessor.GetDefPrg: TPrg;
begin
  if Sequence <> nil then
    Result := Sequence.DefPrg
  else
    Result := nil;
end;

function TProcessor.GetDirName: string;
begin
  if Sequence <> nil then
    Result := Sequence.DirName
  else
    Result := '';
end;
{
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 TProcessor.ShowRunningPrg;
begin
  if FCurPrg <> nil then begin
    FBrowsingCurPrg := FCurPrg;
    FCurPrg.Browse;
  end else begin
    FBrowsingCurPrg := nil;
  end;
end;

function TProcessor.GetSequence: TSequence;
begin
  Result := FSequence;
end;

procedure TProcessor.SetSequence(ASequence: TSequence);
begin
  if FSequence = ASequence then
    exit;
  if FSequence <> nil then begin
    if Running {v0.55}and (ASequence <> nil) {/v0.55} then begin
      ShowMessage('Other sequence is running on this channel', smError, 0);
      exit;
    end;
    FSequence.Processor := nil;
    {v0.56}
    FSequence := ASequence;
      { setting curprg nil caused calling TProcessor.GetSequenceName,
        while the old FSequence does not exist anymore }
    {/v0.56}
    {v0.55}
    CurPrg := nil;
    {/v0.55}
  end {v0.56} else begin
    FSequence := ASequence;
  end;{/v0.56;
  FSequence := ASequence;}

  if FSequence <> nil then begin
    FSequence.Processor := Self;
    {v0.52}
    SequenceState := sesStarting;
    {/v0.52}
  end;
end;

function TProcessor.GetCurSample: TSeqSample;
begin
  if Sequence = nil then
    Result := nil
  else
    Result := Sequence.CurSample
end;

procedure TProcessor.FormClear;
begin
  FForm := nil;
end;

procedure TProcessor.FormShow;
begin
{  SeqFormCheck(sfsIdle, '');}
  if FForm = nil then begin
    FForm := TProcessForm.Create(Application);
    TProcessForm(FForm).Processor := Self;
  end;
  {v0.55}
  Channels.ActiveChannel := Channel;
  {/v0.55}
  FForm.Show;
  FForm.BringToFront;
end;

{procedure TProcessor.StartProgram(APrg: TSeqPrg);
begin
  SequenceState := sesRunning;
end;}

function TProcessor.GetChannel: TChannel;
begin
  Result := Channels.FindChannel(ChannelName);
end;

function TProcessor.GetNextSample: TSeqSample;
begin
  Result := nil;
  if Sequence <> nil then
    Result := Sequence.NextSample;
end;

{v0.50}
procedure TProcessor.SequenceBrowse(Sender: TObject);{ called from processor form }
begin
  if Sequence <> nil then
    Sequence.Browse;{ulobjusru}
end;

function TProcessor.GetRunning: boolean;
begin
  Result := (CurPrg <> nil);
end;

{/v0.50}

{v0.52} {properties for ProcessFrm}
function TProcessor.GetSequenceName: string;
begin
  if Sequence <> nil then
    Result := Sequence.ULSQ.SequenceName
  else
    Result := '';
end;


function TProcessor.GetActiveSampleName: string;
begin
  if CurSample <> nil then
    Result := CurSample.ULSR.SampleName
  else
    Result := '';
end;

function TProcessor.GetActivePrgTimeStr: string;
begin
  if CurPrg <> nil then
    Result := RealToString(CurPrg.Time / 1000 /60, 6, 2)
  else
    Result := '0';
end;

function TProcessor.GetChannelName: string;
begin
  Result := FChannelName;{UPO.ProcessorName;}
end;

procedure TProcessor.SetChannelName(const AName: string);
begin
  FChannelName := AName;
  UPO.ProcessorName := AName + '_Processor';
end;

function TProcessor.ChildCreate(AChildObj: TULObj): TULObjUsr;
begin
  if AChildObj.RecID = USPID then begin {ulobjusru}
    Result := TSeqPrg.Create(Self, AChildObj, USPID);
    FCurPrg := TSeqPrg(Result);
  end else begin
    Result := nil;
  end;
end;

procedure TProcessor.ChildDestroyed(AChild: TULObjUsr);
begin
  if AChild = FCurPrg then
    FCurPrg := nil;
  inherited;
end;
{/v0.52}

{v0.55}
procedure TProcessor.DoMark(Sender: TObject);
begin
  Log('DoMark called');
  OnMark(Sender);
end;
{/v0.55}
{/TProcessor.}

{TProcessors.}
const
  FProcessors: TProcessors = nil;
  ProcessorBaseName = 'Processor.UPR';

function Processors: TProcessors;
begin
  if FProcessors = nil then
    FProcessors := TProcessors.Create(nil, nil, 0);
  Result := FProcessors;
end;

class function TProcessors.GetClassRecID: TULRecID;
begin
  Result := 0;
end;

function TProcessors.ChildCreate(AChildObj: TULObj): TULObjUsr;
begin
  Result := nil;
end;

function TProcessors.ProcessorGet(const AChannelName: string): TProcessor;
var
  i: integer;
  fn: string;
begin
  for i := 0 to ChildCount - 1 do begin
    if TProcessor(Childs[i]).ChannelName = AChannelName then begin
      Result := TProcessor(Childs[i]);
      exit;
    end;
  end;
  fn := DataDir + AChannelName + ProcessorBaseName;
  if FileExists(fn) then begin
    Result := TProcessor.Load(fn);
  end else begin
    Result := TProcessor.Load('');
    Result.FileName := fn;
  end;
  Result.ChannelName := AChannelName;
  Result.ULObjOwner := Self;
  {ulobjusru}
  //FUsrTmps.Obj.ObjDesc.GridOptions := [ogAutosizeBrowseCols, ogColumnSortEnabled];
  //    FUsrTmps.Obj.ObjDesc.ExcludedMenuItems :=
  //      [{omHeader, omFocused, omGridOptions, omCopy,} omSaveAs,
  //       omPaste, omDeleteSelected, omDeleteAll, {omSort,} {omChildAdd, }omChildInsert,
  //       {omChildDelete, }omChildFileSelect, omPrintOptions, omPrint];{ulobjact}
  //    FUsrTmps.Obj.ChildsSetFlag(rfOwnMessageOnSelect, true);
end;

function TProcessors.GetActiveProcessor: TProcessor;
var c: TChannel;
begin
  Result := nil;
  c := Channels.ActiveChannel;
  if c <> nil then
    Result := ProcessorGet(c.ChannelName);
end;

{v0.55}
procedure TProcessors.DoMark(Sender: TObject);
var p: TProcessor;
begin
  p := ActiveProcessor;
  if p <> nil then
    p.DoMark(Sender);
end;
{/v0.55}

{/TProcessors.}

{v0.59}
procedure ProcessorsFree;
begin
  FProcessors.Free;
  FProcessors := nil;
end;
{/v0.59}

initialization

finalization
{v0.59 moved to ulmaglob}{/v0.59  FProcessors.Free;}
end.
