unit AAAu;{ Amino Acid Analyzer non visible control objects }
{
  (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.
}

{
  Stopping:
  - switch NHD to H2O
  - set buffer 1
  - switch off detector

  - switch off reactor temp. (2 min before end??)
  - wait some time (fatal - 2 min, normal 20 min)

  Starting:
  - switch on IK
  - wait 10 s
  - send default values to devices
  - wait 10 s
  - switch on detector
  - send calibration to AS
  - start reactor temp.
  - start column temp.
  - if column temp. > 45 (see below) start P1
  - if P1.Press > P1.Press_L start P2

  - wait max.32 min for OK (was 20 before 0.60.3), then Alarm

  - if OK earlier swith to Ready

  Device Errors:
  - Detector (DevStatToInt(status) < 0) - stop sequence, switch off AAA
}
{ ulandef\ulsrtype }
{$I DEFINE.PAS}
interface
uses
  Messages, SysUtils, {FileCtrl, }Classes, TypInfo, Forms, Controls, Dialogs,
  Graphics,
  UtlType, PropUtl, WinUtl, ExeLogu,
  Fifou, Timer, Msgu,
  UlanType, UlanGlob,
  ULRecTyp, ULRecUtl, ULObju, ULFObju, ULObjUsru,
  ModuType,
  {$IFDEF USEDLL}
  ModuProc,
  {$ELSE}
  Modulu, ModuUtl,
  {$ENDIF}


  Timersu,
  ULADType, ULADObju,

  ULDRType, ULDRObju, { device }
  ULDPType, ULDPObju, { device property }

  ULIType, ULIObju, Instrumentu,
  ULMType, ULMObju, Methodu,
  AAPLType, AAPLObju, { AAA Program Line }
  AAPGType, AAPGObju, AAPrgu, { AAA program }

  ULSRType, ULSRObju, { Sequence record=sample }
  ULSQType, ULSQObju, Sequenceu, { AAA sequence }

  AAAType, AAAObju{, AAAUtl - in ModuUtl}, AAFatalFrm,
  Spectrum, AcqInfou, SpecForm{v0.25}, DebugFrm{/v0.25}
  {v0.28}, Language{/v0.28}
  {v0.49}, Prgu{/v0.49}
  {v0.50}
  ,Channelsu
  ,ULLType, ULLObju
  ,ULNType, ULNObju
  ,ULNDType, ULNDObju
  {/v0.50}
  {v0.53}
  ,Simulationu
  {/v0.53}
  {$IFDEF PROPSTREAMLOG}
  , DebStrmu
  {$ENDIF}
  ,MailerFrm, UPSUnitu
  ;
{uledfrm}
const
  AAACfgBaseName  = 'AAA' + AAAExt;
  MaxHistCount = 4;
  AbortingInterval = -2*60*1000;{ms, = 2 min}
  {v0.75}
  EmergencyAbortingInterval = -5000;
  {/v0.75}
type
  TAAAResult = integer;

{arXXXX}
const
  {c:\mikro\analytik.pas}
  {}
  ar0 = 12000;
  arInvalidStateRequest = ar0 + 1;
  arNoSamples = ar0 + 2;
  arInvalidProgramNoEquil = ar0 + 3;
  arFilesMissing = ar0 + 4;

{/arXXXX}
type
  TTechValue = single;

const
  MinTechValue: TTechValue = MinSingle;
  MaxTechValue: TTechValue = MaxSingle;

type
  {v0.41 moved to ULObju}{/v0.41
  TULObjFldAutoCon = class(TULObjField);}

  TTechHistory = class(TCircularBuffer)
  private
    FMinVal, FMaxVal, FAvgVal: TTechValue;
    FChangedCounter: integer;
    FDeviationOK: boolean;
    FLastDeviationOK: boolean;
    FLimitsOK: boolean;
    FLastLimitsOK: boolean;
    FHardLimitsOK: boolean;
  protected
    procedure SetChangedCounter(AValue: integer);
    {function GetStatusString: string;}

  public
    constructor Create(ASlotCount: integer); reintroduce;
    function PutVal(const ATechValue: TTechValue): boolean;
    {function GetVal(var ATechValue: TTechValue): boolean;}
    function CheckDeviation(AMinValue: TTechValue; AMaxDev: TTechValue; var AState: integer): boolean;
    function CheckLimits(AMinValue, AMaxValue: TTechValue; var AState: integer): boolean;
    function CheckHardLimits(AMinValue, AMaxValue: TTechValue; var AState: integer): boolean;

    procedure SetOK;
    function IsOK(var OKChanged:boolean): boolean;
    {function CheckOKChanged: boolean;}
    procedure Clear;

    property Min:TTechValue read FMinVal;
    property Max:TTechValue read FMaxVal;
    property Avg:TTechValue read FAvgVal;
    property ChangedCounter: integer read FChangedCounter write SetChangedCounter;
    property DeviationOK: boolean read FDeviationOK{ write FDeviationOK};
    property LimitsOK: boolean read FLimitsOK{ write FLimitsOK};
    property HardLimitsOK: boolean read FHardLimitsOK;
  end;

{                                                uladtype
type
  TPropState = (psOK, psSuspend, psDevAlarm, psLimitAlarm, psFatal);
  TPropStatus = class(TObject)
  private
    FErrorCount,
    FErrorAlarmCount,
    FErrorFatalCount: integer;
    FMin, FMax, FDev: TTechValue;
    FPropState: TPropState;
    FHistory: TTechHistory;
    FOnAlarm: TNotifyEvent;
    FOnFatal: TNotifyEvent;
  public
    constructor Create(AHistorySlotCount, AErrorAlarmCount, AErrorFatalCount: integer);reintroduce;
    destructor Destroy;override;
    procedure Update(AValue: TTechValue);
    procedure UpdateLimits(AMinValue, AMaxValue, ADeviation: TTechValue);
    property OnAlarm: TNotifyEvent read FOnAlarm write FOnAlarm;
    property OnFatal: TNotifyEvent read FOnFatal write FOnFatal;
    property History: TTechHistory read FHistory;
    property PropState: TPropState read FPropState;
  end;
}

type
  TAAA = class(TULObjUsr) {aaatype}
  private
    FDefPrg: TAAPrg;{default prg for sequence/seqSamples}
    FDefMethod: TUlanMethod; { loaded from AO.MethodFileName }
    FDefInstrument: TInstrument;{ autocreated }
    FSequence: TSequence; { loaded from AO.SeqFileName }
    FCurPrg: TAAPrg;
    {v0.38}
    FBrowsingCurPrg: TAAPrg;{ non nil, if for FCurPrg was called ShowRunning }
    {/v0.38}
      { pointer to currently running program (of FSequence.CurSample) }
    FAcqInfo: TAcqInfo;
    FAcqData: TAcqData;

    FHistCount: integer;
    FHists:array[0..MaxHistCount-1] of TTechHistory;

    FP1PressHistory: TTechHistory;
    FP2PressHistory: TTechHistory;
    FColumnTempHistory: TTechHistory;
    FReactTempHistory: TTechHistory;

    FIsInTimer: boolean;

    FIK_ON: TULObjFldAutoCon;
    FIK_OFF: TULObjFldAutoCon;
    FIK_STATUS: TULObjFldAutoCon;{uld.asc}
    FIK_WDG: TULObjFldAutoCon;
    //FIK_COOLER_EN: TULObjFldAutoCon;

    FP1_STATUS: TULObjFldAutoCon;
    FP1_FLOW: TULObjFldAutoCon;
    FP1_PRESS: TULObjFldAutoCon;
    FP1_PRESS_L: TULObjFldAutoCon;
    FP1_PRESS_H: TULObjFldAutoCon;
    FP1_AUXVALV: TULObjFldAutoCon;{ 1 - 8 ... buffer valves 1 - 8,
      if = 0 ... unconnected wire }
    FP1_STOP: TULObjFldAutoCon;
    FP1_START: TULObjFldAutoCon;
    FP1_ERRCLR: TULObjFldAutoCon;{ Clear error status field in device }

    FP2_STATUS: TULObjFldAutoCon;
    FP2_FLOW: TULObjFldAutoCon;
    FP2_PRESS: TULObjFldAutoCon;
    FP2_PRESS_L: TULObjFldAutoCon;
    FP2_PRESS_H: TULObjFldAutoCon;
    FP2_GRADDIR: TULObjFldAutoCon; { Switch gradient valves, 0 .. H2O, 1 .. NHD }
    FP2_GRAD_B: TULObjFldAutoCon;  { <> 0 -> NHD, = 0 -> H2O }
    FP2_ERRCLR: TULObjFldAutoCon;
    FP2_STOP: TULObjFldAutoCon;
    FP2_START: TULObjFldAutoCon;

    FAS_OFF: TULObjFldAutoCon;
    FAS_TEMP_OFF: TULObjFldAutoCon;
    FAS_STATUS: TULObjFldAutoCon;
    FAS_TEMP1: TULObjFldAutoCon; { Column temperature (read/write) }
    FAS_TEMP1RQ: TULObjFldAutoCon;
    FAS_CALLPS: TULObjFldAutoCon; { Get autosampler ready (flushing, sensors calibration) }
    FAS_ERRCLR: TULObjFldAutoCon;
    FAS_SAMPNUM: TULObjFldAutoCon; { Number of the next sample to be prepared }
    FAS_PREPSAMP: TULObjFldAutoCon; { Prepare sample sample for inject (select position, load) }
    FAS_INJECT: TULObjFldAutoCon; { Turn inject valve - inject sample }
    FAS_TEMP_ST: TULObjFldAutoCon;

    FDET_TEMP1: TULObjFldAutoCon;
    FDET_TEMP_ST: TULObjFldAutoCon;
    FDET_ERRCLR: TULObjFldAutoCon;
    FDET_CHA: TULObjFldAutoCon; { Absorbance on channel A }
    FDET_CHB: TULObjFldAutoCon; { Absorbance on channel B }
    FDET_ZERO: TULObjFldAutoCon; { Set zero absorbance}
    FDET_ON: TULObjFldAutoCon;   { switch the lamp on }
    FDET_OFF: TULObjFldAutoCon; { switch the lamp off }
    FDET_TEMP_OFF: TULObjFldAutoCon; { switch react.termostat off }
    FDET_STATUS: TULObjFldAutoCon;

    FP1: TULDRObj;
    FP2: TULDRObj;
    FAS: TULDRObj;
    FIK: TULDRObj;
    FDET: TULDRObj;

    FSubState: integer;
    FStateStartTime: integer;
    FStateTime: integer;
    FUsrStateTimeOffset: integer;
    FLogActive:boolean;

    {states of devices, can be changed only by assigning to corresponing property }
    FIKState: TDevState;

    FP1State: TDevState;
    FP2State: TDevState;
    FDetState: TDevState;
    FColState: TDevState;
    FReactState: TDevState;

    FASState: TASState;{moduutl}
    {/states}

    {states of devices, current variables used for getting state from devices
      (passed as var parameters) in DoTimer-DoTechControl method. Assigned
      to corresponding properties at DoTimer end }
    FcIKState: TDevState;

    FcP1State: TDevState;
    FcP2State: TDevState;
    FcDetState: TDevState;
    FcColState: TDevState;
    FcReactState: TDevState;

    FcASState: TASState;{moduutl}
    {/states}
    FDevStatesOK: boolean;
    FcDevStatesOK: boolean;
      { set true if all FxxxState values are OK }
    FDevStatesLastErrorMsg: string;
    FFatalForm: TAAFatalForm;
    FFatalCancelTime: integer;
    FAlarmCancelTime: integer;
    FSimulationOn: boolean;
    FSkipCalibration: boolean;

    {FindULDP: TULDPObj;
      { set true if just testing software without presence of the AAA device }
    FAAATemplateFileName: string;
    FSeqFileOpenDlg: TOpenDialog;
    FAcqDebugStartTime: integer;
    FDebLogAcq: boolean;
      { log acquired values from Channel A,B to DebLog? }
    {v0.25}
    FDebErrorDlg: boolean;
    FCHAProp: TModuleProp;
    FCHBProp: TModuleProp;
{    FNextSampleEnabled: boolean;}
      { set flag false if some error (loading next sample) failed during the
        run, will cause stopping the analysis }
    FLogHead: string;
      { string prepended to Log parameter }
    {/v0.25}
    {v0.75}
    FEmergencyAborting: boolean;
    {/v0.75}
    { Set through AAFrm.RunWhenReadyCheckBox on Options panel }
    FRunWhenReady: boolean;
    function GetDataRootDir: string;
    function GetUPSStateStr: string;
    //procedure SetUPSStateStr(Value: string);

  protected
    class function GetClassRecID: TULRecID; override;
    function GetAAAObj: TAAAObj;
    procedure ClassFieldsCreate;override;
    procedure ClassFieldsDestroy;override;
    function GetULDP(const AAAObjPropName: string; var AULDP:TULDPObj): boolean;
      { find uldp object that corresponds to property name of TAAAObj
        (i.e. to what ULDPObj should be assigned initial values)
        Syntax of AAAObjPropName: DeviceName_PropDesc }
    function FindULDP(const AAAObjPropName: string): TULDPObj;
      { as GeTULDP but raises exception if not found }
    procedure FindULDRs;
    function FindULDR(const AAAObjPropName: string): TULDRObj;

    procedure CopyAAAToULD;
      { copy values from AAAObj to ULDR.ULDP objects (e.g. to devices)
        using AAAObj property names }

      procedure GetULField(const AAAPropName: string; var AField: TULObjFldAutoCon);

    {v0.29}
    procedure LogState(const msg: string);
      { log error status to logfile, eventually to the current sample}
      procedure SampleLog(const msg: string);
        { called from logstate, if cursample <> nil then
          adds message to its Protocol }
    {/v0.29
    procedure LogState;
    }
    procedure AlarmActivate(const AErrorMsg: string);
      { start alarm (PC speaker), user can cancel, then ignore alarms for 3-5 min }
    procedure AlarmDeactivate;
    procedure FatalActivate(const AErrorMsg: string);
      { in 10 seconds will start switch off devices (if user won't correct the error) }
    function SetAAAState(AAAAState: TAAAState): TAAAResult;
      { tries to set specified state, returns error if not possible }
      procedure StateStart(aas: TAAAState; AUsrTimeOffset: integer);
        { no checking - set specified state and starts its time }

    procedure DoTechControl;
      procedure ClearParams;
      procedure DoTechStarting;
      procedure DoTechStoppingFinishing;
      {v0.25}
      {procedure DoTechStopRunning;}
      {/v0.25}
      procedure DoTechAborting;
      procedure DoTechRunningStopRunning;

    procedure DoTimer(Sender: TObject);
    function GetAAAState: TAAAState;

    procedure GetULFields;
    procedure UpdateUpdateIntervals;
    function GetP1StateMsg: string;
    function GetP2StateMsg: string;
    function GetDetStateMsg: string;
    function GetColStateMsg: string;
    function GetReactStateMsg: string;
    function GetASStateMsg: string;

    {set devices states}
    procedure SetIKState(dst: TDevState);
    procedure SetP1State(dst: TDevState);
    procedure SetP2State(dst: TDevState);
    procedure SetDetState(dst: TDevState);
    procedure SetColState(dst: TDevState);
    procedure SetReactState(dst: TDevState);

    procedure SetASState(ast: TASState);
    {/set devices states}
    function GetUsrStateTimeStr: string;
      { time of current AAAState in minutes }
    procedure SetUsrStateTimeStr(const AValue: string);

    function FirstSampleRun: TAAAResult;
    function GetErrorActive: boolean;
      { true if fatal or alarm active }
    function GetFatalActive: boolean;
    function GetAlarmActive: boolean;
    function ShouldIgnoreError: boolean;
    function GetASTempMin: TTemperature;
    function GetASTempMax: TTemperature;
    function GetDetTempMin: TTemperature;
    function GetDetTempMax: TTemperature;
    function ChildCreate(AChildObj: TULObj):TULObjUsr;override;
    procedure WMAppMessage(var Msg: TMessage);message WM_APPMESSAGE;
    procedure SequenceUpdateDefaults(AULSQ: TULSQObj);
    function AcqStart: boolean;
      { start acquisition of data for current sample }
    procedure AcqDoTimer;
      { called every sec. acquire the data, append to FAcqData }
    procedure AcqStop;
    function AcqEquilStart: boolean;
      { as AcqStart but with file name set to PrgName_Equilibration }
    procedure AcqEquilStop;
      { just calls AcqStop }
      { stop the acquisition }
    function GetCurSample: TSeqSample;
    function CheckSequenceFilesPresence: boolean;
    procedure AcqDataUpdateViewLimit;
      { reflect CurPrg.Time of last line to X max limit of SpecForm view limit }

    {v0.25}
    function GetCurPrgName: string;{ for status wnd }
    function GetCurSampleName: string;{ for status wnd }
    function GetCurSampleStateName: string; { ulsrtype }

    procedure CurSampleStateChanged;
      {call when current sample program/name/state changed}
    procedure DoCurSampleSelected;
      { called after Sequence.First or Sequence.Next, i.e. when CurSample
        pointer changed; loads params from prg, method files }
    function IsInFatalCancelTime: boolean;
    function IsInAlarmCancelTime: boolean;
    procedure DefMethodSetDefaults;
    {/v0.25}
    function CheckCurSample: boolean; {ulsrtype}
      { returns true if Sequence has CurSample (selected by First or Next call,
        ready to run) }
    procedure CheckDefTemplate;
    procedure UpdateDefDirs;override;
      { Update DefDir properties of ULObjs and ULObjFields according to
        current AO.SeqsDir, AO.MethodsDir, AO.PrgsDir values }
    procedure DoAfterSettingsEdit;
    function GetNewSequenceName: string;
      { returns name of new (non existing) directory (yymmdd-n), subdirectory
        of AO.SeqsDir }
    function GetMethodFullFileName: string;
      { creates full filename from AO MethodsDir and MethodFileName fields }
    function GetSeqFullFileName: string;
      { creates full filename from AO SeqsDir and SeqFileName fields }
    function GetCurSequenceName: string;
      { returns name of current sequence for displaying }

    function GetPrgFullFileName: string;

    procedure SequenceCheckDir;
      { makes sure that directory for currently selected sequence exists }
    procedure SequenceFileNew;
      { non interactive creation of new sequence (used just during the first
        program start, or if SeqFileName cleared ) }
    {v0.29}
    procedure DefPrgCreateDefault;
    {/v0.29}
    {v0.38}
    procedure SetCurPrg(APrg: TAAPrg);
    procedure SetDefPrg(APrg: TAAPrg);{not public method accessible through property!!}
    procedure DefPrgNameChanged;
    procedure SetLogActive(OnOff: boolean);
    {/v0.38}
    procedure CoolerEnable(OnOff: boolean);


    {v0.75}
    property EmergencyAborting: boolean read FEmergencyAborting;
    {/v0.75}
    property DevStatesLastErrorMsg: string read FDevStatesLastErrorMsg;


  public

    constructor Load(const AFileName: string); reintroduce;
    {constructor Create(const AFileName: string);}
    {v0.74}
    procedure SetDefaultLogging;
    {/v0.74}

    procedure Log(const msg:string);override;{ulobjusru}
    destructor Destroy;override;
    procedure ErrorCanceled;{from FatalForm}
    procedure ErrorConfirmed;{from FatalForm}
    procedure AutoSaveAll;
      { called upon every ended acquisition to save all opened files }


    {usr methods}
    function SettingsEdit: integer;
    procedure SequenceBrowse;
    procedure SequenceOpen;
    procedure SequenceNew;
    procedure SeqFileOpen;
      { File Open Dialog for files of current sequence }
    procedure DefPrgBrowse;
    {v0.38}
    procedure ShowRunningPrg;
    {/v0.38}
    procedure DefPrgOpen;
    procedure DefPrgNew;
    procedure DefMethodEdit;
    procedure DefMethodOpen;
    procedure DefMethodNew;

    function UsrSetAAAState(AAAAState: TAAAState): TAAAResult;
    procedure UsrRequestOnOff;
    procedure AcqShow;
     { show window for current acquisition }

    {/usr methods}
    {debug methods}
    procedure AcqDebugStart;
    procedure AcqDebugStop;

    property LogHead: string read FLogHead write FLogHead;
    procedure DebugAlarmActivate(const AErrorMsg: string);
    procedure DebugFatalActivate(const AErrorMsg: string);
    {/debug}
    {v0.75}
    procedure EmergencyAbort;
    {/v0.75}
    {v0.76}
    procedure DebLog(const msg: string);
    {/v0.76}

    {debug properties}
    property DebLogAcq: boolean read FDebLogAcq write FDebLogAcq;
    {/debug properties}
    property ULDP[const AAAObjPropName: string]: TULDPObj read FindULDP;{ulobju}
    property AO:TAAAObj read GetAAAObj;

    property DefPrg: TAAPrg read FDefPrg;
    property DefMethod: TUlanMethod read FDefMethod;
    property DefInstrument: TInstrument read FDefInstrument;
    property Sequence: TSequence read FSequence;
    property CurSample: TSeqSample read GetCurSample;
    property StateTime: integer read FStateTime;
    property StateStartTime: integer read FStateStartTime;

    property State: TAAAState read GetAAAState;

    {dev states write only props (read xxxStateMsg) }
    property IKState: TDevState write SetIKState;
    property P1State: TDevState write SetP1State;
    property P2State: TDevState write SetP2State;
    property DetState: TDevState write SetDetState;
    property ColState: TDevState write SetColState;
    property ReactState: TDevState write SetReactState;
    property ASState: TASState write SetASState;
    {/dev states}
    property DevStatesOK: boolean read FDevStatesOK write FDevStatesOK;
    property FatalActive: boolean read GetFatalActive;
    property ErrorActive: boolean read GetErrorActive;
    property AlarmActive: boolean read GetAlarmActive;
    property DataRootDir: string read GetDataRootDir;
    property AAATemplateFileName: string read FAAATemplateFileName;
    {v0.38}
    property CurPrg: TAAPrg read FCurPrg write SetCurPrg;{aaprgu}
    property LogActive: boolean read FLogActive write SetLogActive;
    {/v0.38}
  published
    property DP1: TULDRObj read FP1 write FP1;
    property DP2: TULDRObj read FP2 write FP2;
    property DAS: TULDRObj read FAS write FAS;
    property DDET: TULDRObj read FDET write FDET;
    property DIK: TULDRObj read FIK write FIK;

    property IK_ON: TULObjFldAutoCon read FIK_ON write FIK_ON;
    property IK_OFF: TULObjFldAutoCon read FIK_OFF write FIK_OFF;
    property IK_STATUS: TULObjFldAutoCon read FIK_STATUS write FIK_STATUS;
    property IK_WDG: TULObjFldAutoCon read FIK_WDG write FIK_WDG;

    property P1_STATUS: TULObjFldAutoCon read FP1_STATUS write FP1_STATUS;
    property P1_FLOW: TULObjFldAutoCon read FP1_FLOW write FP1_FLOW;
    property P1_PRESS: TULObjFldAutoCon read FP1_PRESS write FP1_PRESS;
    property P1_PRESS_L: TULObjFldAutoCon read FP1_PRESS_L write FP1_PRESS_L;
    property P1_PRESS_H: TULObjFldAutoCon read FP1_PRESS_H write FP1_PRESS_H;
    property P1_AUXVALV: TULObjFldAutoCon read FP1_AUXVALV write FP1_AUXVALV;
    property P1_STOP: TULObjFldAutoCon read FP1_STOP write FP1_STOP;
    property P1_START: TULObjFldAutoCon read FP1_START write FP1_START;
    property P1_ERRCLR: TULObjFldAutoCon read FP1_ERRCLR write FP1_ERRCLR;

    property P2_STATUS: TULObjFldAutoCon read FP2_STATUS write FP2_STATUS;
    property P2_FLOW: TULObjFldAutoCon read FP2_FLOW write FP2_FLOW;
    property P2_PRESS: TULObjFldAutoCon read FP2_PRESS write FP2_PRESS;
    property P2_PRESS_L: TULObjFldAutoCon read FP2_PRESS_L write FP2_PRESS_L;
    property P2_PRESS_H: TULObjFldAutoCon read FP2_PRESS_H write FP2_PRESS_H;
    property P2_GRADDIR: TULObjFldAutoCon read FP2_GRADDIR write FP2_GRADDIR;
    property P2_GRAD_B: TULObjFldAutoCon read FP2_GRAD_B write FP2_GRAD_B;
    property P2_STOP: TULObjFldAutoCon read FP2_STOP write FP2_STOP;
    property P2_START: TULObjFldAutoCon read FP2_START write FP2_START;
    property P2_ERRCLR: TULObjFldAutoCon read FP2_ERRCLR write FP2_ERRCLR;

    property AS_OFF: TULObjFldAutoCon read FAS_OFF write FAS_OFF;
    property AS_TEMP_OFF: TULObjFldAutoCon read FAS_TEMP_OFF write FAS_TEMP_OFF;
    property AS_STATUS: TULObjFldAutoCon read FAS_STATUS write FAS_STATUS;
    property AS_TEMP1: TULObjFldAutoCon read FAS_TEMP1 write FAS_TEMP1;
    property AS_TEMP1RQ: TULObjFldAutoCon read FAS_TEMP1RQ write FAS_TEMP1RQ;
    property AS_CALLPS: TULObjFldAutoCon read FAS_CALLPS write FAS_CALLPS;
    property AS_ERRCLR: TULObjFldAutoCon read FAS_ERRCLR write FAS_ERRCLR;
    property AS_SAMPNUM: TULObjFldAutoCon read FAS_SAMPNUM write FAS_SAMPNUM;
    {property AS_ASPREPARE: TULObjFldAutoCon read FAS_ASPREPARE write FAS_ASPREPARE;}
    property AS_INJECT: TULObjFldAutoCon read FAS_INJECT write FAS_INJECT;
    property AS_TEMP_ST: TULObjFldAutoCon read FAS_TEMP_ST write FAS_TEMP_ST;
    property AS_PREPSAMP: TULObjFldAutoCon read FAS_PREPSAMP write FAS_PREPSAMP;

    property DET_TEMP1: TULObjFldAutoCon read FDET_TEMP1 write FDET_TEMP1;
    property DET_TEMP_ST: TULObjFldAutoCon read FDET_TEMP_ST write FDET_TEMP_ST;
    property DET_ERRCLR: TULObjFldAutoCon read FDET_ERRCLR write FDET_ERRCLR;
    property DET_CHA: TULObjFldAutoCon read FDET_CHA write FDET_CHA;
    property DET_CHB: TULObjFldAutoCon read FDET_CHB write FDET_CHB;
    property DET_ZERO : TULObjFldAutoCon read FDET_ZERO  write FDET_ZERO;
    property DET_ON : TULObjFldAutoCon read FDET_ON  write FDET_ON;
    property DET_OFF : TULObjFldAutoCon read FDET_OFF  write FDET_OFF;
    property DET_TEMP_OFF : TULObjFldAutoCon read FDET_TEMP_OFF  write FDET_TEMP_OFF;
    property DET_STATUS : TULObjFldAutoCon read FDET_STATUS  write FDET_STATUS;

    property P1StateMsg: string read GetP1StateMsg;
    property P2StateMsg: string read GetP2StateMsg;
    property DetStateMsg: string read GetDetStateMsg;
    property ColStateMsg: string read GetColStateMsg;
    property ReactStateMsg: string read GetReactStateMsg;
    property ASStateMsg: string read GetASStateMsg;

    property UsrStateTimeStr: string read GetUsrStateTimeStr write SetUsrStateTimeStr;
    property SimulationOn: boolean read FSimulationOn write FSimulationOn;
    property SkipCalibration: boolean read FSkipCalibration write FSkipCalibration;
    property ASTempMin: TTemperature read GetASTempMin;
    property ASTempMax: TTemperature read GetASTempMax;
    property DetTempMin: TTemperature read GetDetTempMin;
    property DetTempMax: TTemperature read GetDetTempMax;

    {v0.25}
    property CurSampleName: string read GetCurSampleName;
    property CurPrgName: string read GetCurPrgName;
    property CurSampleStateName: string read GetCurSampleStateName;
    property CurSequenceName: string read GetCurSequenceName;
      { for display purposes }
    {/v0.25}
    property UPSStateStr: string read GetUPSStateStr;// write SetUPSStateStr;
  end;

{ Returns the AAA (Amino Acid Analyzer) object. Only one instance gets created. }
function AAA: TAAA;
{ Release the AAA (Amino Acid Analyzer) object. }
procedure AAAFree;
{v0.50}
function AAAIsOn: boolean;
function CheckAAADevices(Setup: boolean; var namesOK:boolean): boolean;
{/v0.50}

implementation

const
  FAAA: TAAA = nil;

{$IFDEF USEDLL}
{moduutl}
function AS_STATUS_ToString(AStatus: word): string;
begin
  Result := ModulesIntToStr(Modules, mcnASStatus2Str, AStatus);
end;

{ converts AutoSampler STATUS property to TASState (astXXXX) value  }
function AS_STATUS_Decode(AStatus: word) : TASState;
begin
  Result := TASState(ModulesIntToInt(Modules, mcnASStatusDecode, AStatus));
end;

function ASStateToString(ast: TASState): string;
begin
  Result := ModulesIntToStr(Modules, mcnASState2Str, Integer(ast));
end;

{Devices (other than AutoSampler) }        a
function DevStatToInt(AStatus: integer): integer;
begin
  Result := ModulesIntToInt(Modules, mcnDevStat2Int, AStatus);
end;

function DevStateToString(dst: TDevState): string;
begin
  Result := ModulesIntToStr(Modules, mcnDevState2Str, integer(dst));
end;
{$ENDIF}
{v0.50}
function AAAIsOn: boolean;
begin
  Result := FAAA <> nil;
end;
{/v0.50}

function AAACfgName: string;
begin
  Result := DataDir + AAACfgBaseName;
end;

function AAA: TAAA;
begin
  if FAAA = nil then begin
    FAAA := TAAA.Load(AAACfgName);
  end;
  Result := FAAA;
end;

{TAAA}
class function TAAA.GetClassRecID: TULRecID;
begin
  Result := AAAID;
end;

constructor TAAA.Load(const AFileName: string);
const
  DefHistorySlotCount = 6;
{var
  d: string;}
begin{ulobjusru}
  inherited; {Load{Create(nil, nil, AAAID); ulanrecs.lst aapgtype}
{  if (AFileName <> '') and (AFileName <> FileName) then begin
    if FileExists(AFileName) then begin
      LoadFromFile(AFileName);
    end else begin
      FileName := AFileName;
    end;
  end;}
  {v0.24}
  {v0.38}
  LogActive := true;
  {/v0.38
  FLogActive := true;
  }
  AO.AAAName := 'AAA';
  AO.AAAState := aasDisconnected;
  {/v0.24}
  {techhistory}
  FP1PressHistory := TTechHistory.Create(DefHistorySlotCount);
  FP2PressHistory := TTechHistory.Create(DefHistorySlotCount);
  FColumnTempHistory := TTechHistory.Create(DefHistorySlotCount);
  FReactTempHistory := TTechHistory.Create(DefHistorySlotCount);
  FHistCount:= MaxHistCount;
  FHists[0] := FP1PressHistory;
  FHists[1] := FP2PressHistory;
  FHists[2] := FColumnTempHistory;
  FHists[3] := FReactTempHistory;
  {/techhistory}
  GetULFields;
  {v0.24}
  UpdateUpdateIntervals;
  FindULDRs;
  ClearParams;
  CheckDefTemplate;
  {v0.74}
  SetDefaultLogging;
  {/v0.74}

  Timers.AddTimerProc(DoTimer, 1000);{timersu}
  {/v0.24}
  {v0.41}
  AAAActive := true;
  {/v0.41}
end;

{v0.74}
procedure TAAA.SetDefaultLogging;
var
  m: TModule;
  p: TModuleProp; {modutype, modulu}

begin
  {$IFDEF USEDLL}
  if ModulesHasModule(Modules, mpDeviceName, 'IK', m) then begin
    if ModuleHasProp(m, mpPropDesc, 'WDG', p) then begin
      ModulePropSetPropInt(p, mpLogState, TModPropIntValue(plsNo));
    end;
  end;
  {$ELSE}
  if Modules.FindModuleWithPropVal(piDeviceName, 'IK', m) then begin
    if m.FindPropByDesc('WDG', p) then begin
      p.SetLogState(plsNo);
    end;
  end;
  {$ENDIF}
end;
{/v0.74}
procedure TAAA.ClassFieldsDestroy;
begin

  {if FDefPrg <> nil then
    AO.PrgFileName := RelativeFileName(FDefPrg.FileName;}
  {v0.38}
  SetDefPrg(nil);
  {/v0.38
  FDefPrg.Free;
  FDefPrg := nil;}

  {if FSequence <> nil then
    AO.SeqFileName := FSequence.FileName;}
  FSequence.Free;
  FSequence := nil;

  {if FDefMethod <> nil then
    AO.MethodFileName := FDefMethod.FileName;}
  FDefMethod.Free;
  FDefMethod := nil;

  inherited;
end;

{v0.25}
procedure TAAA.DefMethodSetDefaults;
begin
  {v0.58}
  try
  {/v0.58}
  FDefMethod.ULM.NoUnknownPeaks := true;
  {v0.29}
  {v0.76}
  DebLog('DefMethod.Save');
  {/v0.76}
  FDefMethod.Save;
  {/v0.29}

  {v0.58 if can not save, it might try to write to readonly media,
    just ignore the exception, user should notice it and change path}
  except
  end;
  {/v0.58}
end;
{/v0.25}

{v0.29}
procedure TAAA.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 := 60;
    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 := 120;
    l.Temperature := 50;
    l.BufferNr := bn1;
    l.Command := acZero;
    l.Note := 'Eventually zero the detector';
  end;

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

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

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

  procedure AddLine6(l: TAAPLObj);
  begin
    l.PrgTime := 360;
    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
  for i := 0 to 6 do begin
    pl := TAAPrgLine(FDefPrg.ChildAdd(nil, AAPLID, ''));{ulobjusru}
    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;
  {v0.76}
  DebLog('DefPrg.Save');
  {/v0.76}
  FDefPrg.Save;
end;
{/v0.29}

procedure TAAA.ClassFieldsCreate;
{var d: string;}
begin
  inherited;

  if AO.SeqsDir = '' then begin
    AO.SeqsDir := DataRootDir + 'Sequences\';
    CreateDir(AO.SeqsDir);
  end;

  if ExtractFilePath(AO.SeqFileName) = '' then begin
    if AO.SeqFileName = '' then begin
      AO.SeqFileName  := '';{GetNewSequenceName;{ULRecDefFileName(ULSQID);}
    end;
  end;

  if AO.MethodsDir = '' then begin
    AO.MethodsDir := DataRootDir + 'Methods\';
    CreateDir(AO.MethodsDir);
  end;
  if ExtractFilePath(AO.MethodFileName) = '' then begin
    if AO.MethodFileName = '' then
      AO.MethodFileName  := ULRecDefFileName(ULMID);
  end;

  if AO.PrgsDir = '' then begin
    AO.PrgsDir := DataRootDir + 'Programs\';
    CreateDir(AO.PrgsDir);
  end;
  if ExtractFilePath(AO.PrgFileName) = '' then begin
    if AO.PrgFileName = '' then
      AO.PrgFileName  := ULRecDefFileName(AAPGID);
  end;

  if FDefInstrument = nil then
    FDefInstrument := TInstrument(ChildFindOrAdd(ULIID, ''));{ulobjusru}

  if FDefMethod = nil then begin
    FDefMethod := TUlanMethod.Load(GetMethodFullFileName); {ulobju extractfilename }
    {v0.25}
    if not FileExists(GetMethodFullFileName) then begin
      DefMethodSetDefaults;
    end;
    {/v0.25}
  end;

  if FDefPrg = nil then begin
    {v0.38}
    SetDefPrg(TAAPrg.Load(GetPrgFullFileName));
    {/v0.38
    FDefPrg := TAAPrg.Load(GetPrgFullFileName);}
    {v0.25}
    FDefPrg.Obj.SetFlag(rfAskForSave, true);{ulobju}
    {/v0.25}
    {v0.29}
    if not FileExists(GetPrgFullFileName) then begin
      DefPrgCreateDefault;
    end;
    {/v0.29}
  end;

  if FSequence = nil then begin
    if AO.SeqFileName = '' then begin
      FSequence := TSequence.Load('');
      {v0.25}
      FSequence.Obj.SetFlag(rfAskForSave, false);
      {/v0.25}
      SequenceFileNew;
    end else begin
      FSequence := TSequence.Load(GetSeqFullFileName);
      {v0.25}
      FSequence.Obj.SetFlag(rfAskForSave, false);
      {/v0.25}
    end;
  end;

  SequenceUpdateDefaults(FSequence.ULSQ);
  UpdateDefDirs;

  {!!! just for testing:}
  {FCurPrg := FDefPrg;}
end;

function TAAA.GetMethodFullFileName: string;
begin
  Result := AbsoluteFileName(AO.MethodsDir, AO.MethodFileName{v0.36}, ULMExt{/v0.36});
end;

function TAAA.GetCurSequenceName: string;
begin
  Result := ExtractFileName(ChangeFileExt(AO.SeqFileName, ''));
end;

function TAAA.GetSeqFullFileName: string;
begin
  Result := AbsoluteFileName(AO.SeqsDir, AO.SeqFileName {v0.36}, ULSExt{/v0.36});
end;

function TAAA.GetPrgFullFileName: string;
begin
  Result := AbsoluteFileName(AO.PrgsDir, AO.PrgFileName{v0.36}, AAPGExt{/v0.36});
end;

function TAAA.GetAAAObj: TAAAObj;
begin
  Result := TAAAObj(Obj);
end;

procedure TAAA.SequenceBrowse;
begin
  FSequence.Browse;
end;

procedure TAAA.SequenceOpen;
begin
  {v0.58}
  if Sequence.Obj.Modified then begin
    if not Sequence.Obj.CanClose(nil) then
      exit;
  end;   {winutl}
  {/v0.58}
  if Sequence.FileOpen then begin
    {v0.25}
    Sequence.Obj.SetFlag(rfAskForSave, false);
    AO.SeqFileName := Sequence.ULSQ.SequenceName + ULRecDefFileExt(ULSQID);
    AO.DoChange;
    {v0.58}
    {Sequence.Obj.Modified := false; fixed in lower level}
    {/v0.58}
    {/v0.25}
    SequenceBrowse;
  end;
end;

procedure TAAA.SeqFileOpen;
 { File Open Dialog for files of current sequence }
begin
  if FSeqFileOpenDlg = nil then begin
    FSeqFileOpenDlg := TOpenDialog.Create(Self);
  end;
  FSeqFileOpenDlg.FileName := '';
  FSeqFileOpenDlg.InitialDir := Sequence.DirName;
  FSeqFileOpenDlg.Filter := GetTxt({#}'Data file (*.ULF)|*.ULF');
  if FSeqFileOpenDlg.Execute then begin
    CreateSpectrumForm(FSeqFileOpenDlg.FileName, omRead);{ulantype}
  end;{main}
end;

procedure TAAA.SequenceCheckDir;
begin
  CreateDir(AO.SeqsDir + Sequence.ULSQ.SequenceName);
  {v0.29}
  if not FileExists(Sequence.FileName) then begin
    {v0.76}
    DebLog('Sequence.Save');
    {/v0.76}
    Sequence.Save;
  end;
  {/v0.29}
end;

procedure TAAA.SequenceFileNew;
begin
  Sequence.FileNew;
  SequenceUpdateDefaults(Sequence.ULSQ);
  AO.SeqFileName := Sequence.ULSQ.SequenceName + ULRecDefFileExt(ULSQID);
  AO.DoChange;
  Sequence.FileName := GetSeqFullFileName;
  Sequence.ClassFieldsUpdate;
  SequenceCheckDir;
end;

procedure TAAA.SequenceNew;
var
  f: TULFObj;
  s: TULSQObj;
begin
  { vybrat z analsetup form }
  f := TULFObj.Create(nil);
  try
    s := TULSQObj(f.FindOrAdd(ULSQID, ''));
    SequenceUpdateDefaults(s);
    if s.EditModal = mrOK then begin
      Sequence.FileNew;
      {$IFDEF PROPSTREAMLOG}
      DebStreamOpen;
      {$ENDIF}
      Sequence.ULSQ.Assign(s);
      {$IFDEF PROPSTREAMLOG}
      DebStreamClose;
      {$ENDIF}

      AO.SeqFileName := Sequence.ULSQ.SequenceName + ULRecDefFileExt(ULSQID);
      AO.DoChange;

      Sequence.FileName := GetSeqFullFileName;
      Sequence.ClassFieldsUpdate;
      SequenceCheckDir;
      SequenceBrowse;
    end;
  finally
    f.Free;
  end;
end;

function TAAA.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(AO.SeqsDir + d) then begin
      break;
    end;
    inc(i);
  until false;
  Result := d;
end;

procedure TAAA.SequenceUpdateDefaults(AULSQ: TULSQObj);
begin
  if AULSQ.PrgFileName = '' then begin
    AULSQ.PrgFileName := AO.PrgFileName;
  end;

  if AULSQ.MethodFileName = '' then begin
    AULSQ.MethodFileName := AO.MethodFileName;
  end;

  if AULSQ.SequenceName = '' then begin
    AULSQ.SequenceName := GetNewSequenceName;
    AULSQ.MethodFileName := AO.MethodFileName;
    AULSQ.PrgFileName := AO.PrgFileName;
  end;
  {v0.76}
  AULSQ.SequenceKind := skAAA;
  {/v0.76}
  {ulsqtype}
end;

procedure TAAA.DefPrgBrowse;
begin
  FDefPrg.Browse;
end;

{v0.38}
procedure TAAA.ShowRunningPrg;
begin
  if FCurPrg <> nil then begin
    FBrowsingCurPrg := FCurPrg;
    FCurPrg.Browse;
  end else begin
    FBrowsingCurPrg := nil;
  end;
end;
{/v0.38}

procedure TAAA.DefPrgOpen;
begin
  if DefPrg.FileOpen then begin {ulobjusru ulmtype}
    {v0.25}
    DefPrg.Obj.SetFlag(rfAskForSave, true);{ulobju}
    {/v0.25}
    {v0.38}
    DefPrgNameChanged;
    {/v0.38
    AO.PrgFileName := RelativeFileName(AO.PrgsDir, DefPrg.FileName
      , AAPGExt);}
    DefPrgBrowse;
  end;
end;

{v0.38}
procedure TAAA.DefPrgNameChanged;
begin
  AO.PrgFileName := RelativeFileName(AO.PrgsDir, DefPrg.FileName, AAPGExt);
end;
{/v0.38}

procedure TAAA.DefPrgNew;
begin
  DefPrg.FileNew;
  AO.PrgFileName := ExtractFileName(DefPrg.FileName);
  DefPrg.FileName := GetPrgFullFileName;
  DefPrg.ClassFieldsUpdate;
  DefPrgBrowse;
end;

procedure TAAA.DefMethodEdit;
begin
  {v0.26}
  if DefMethod.EditModal = mrOK then begin
    {v0.37}
    {v0.76}
    DebLog('DefMethod.Obj.DoFileSave');
    {/v0.76}
    DefMethod.Obj.DoFileSave;
    AO.MethodFileName := RelativeFileName(AO.MethodsDir, DefMethod.FileName,
      ULMExt);
    {/v0.37
    DefMethod.Save;}
  end;
  {/v0.26 ulobjusru
    DefMethod.EditModal; }
end;

procedure TAAA.DefMethodOpen;
begin
  if DefMethod.FileOpen then begin
    AO.MethodFileName := RelativeFileName(AO.MethodsDir, DefMethod.FileName
      {v0.36}, ULMExt{/v0.36});
    DefMethodEdit;
  end;
end;

procedure TAAA.DefMethodNew;
begin
  DefMethod.FileNew;
  AO.MethodFileName := {v0.36}RelativeFileName(AO.MethodsDir, DefMethod.FileName, ULMExt);{/v0.36
    ExtractFileName(DefMethod.FileName);}
  DefMethod.FileName := GetMethodFullFileName;
  DefMethod.ClassFieldsUpdate;
  DefMethodSetDefaults;
  DefMethodEdit;
end;

function TAAA.SettingsEdit: integer;
begin
  Result := EditModal; {ulobjusru}
  if Result = mrOK then begin
    DoAfterSettingsEdit;
    {CopyAAAToULD;}
  end;
end;

function TAAA.GetULDP(const AAAObjPropName: string; var AULDP:TULDPObj): boolean;
{ find uldp object that corresponds to property name of TAAAObj
 (i.e. to what uldpobj should be assigned these initial values }
var
  dn, pn: string;
  o: TULObj;
  i: integer;
begin
  Result := false;
  i := pos('_', AAAObjPropName);
  if i > 0 then begin
    dn := copy(AAAObjPropName, 1, i - 1);
    pn := copy(AAAObjPropName, i + 1, length(AAAObjPropName));
    if ULFKeeper.FindByULObjPath(dn + '.' + pn, 0, o) then begin
      if o is TULDPObj then begin
        AULDP := TULDPObj(o);
        Result := true;
        exit;
      end;
    end;
  end;
end;

function TAAA.FindULDR(const AAAObjPropName: string): TULDRObj;
var o: TULObj;
begin
  if ULFKeeper.FindByULObjPath(AAAObjPropName, 0, o) and
    (o is TULDRObj)
  then begin
    Result := TULDRObj(o);
  end else begin
    raise Exception.Create('AAA ' + AAAObjPropName + ' not found.');
  end;
end;

procedure TAAA.FindULDRs;
{v0.25}
var m: TModule;
{/v0.25}
begin
  FP1 := FindULDR('P1');
  FP2 := FindULDR('P2');
  FAS := FindULDR('AS');
  FIK := FindULDR('IK');
  FDET := FindULDR('DET');

  {$IFDEF USEDLL}
  if not ModulesHasModule(Modules, mpDeviceName, 'DET', m) then
     raise Exception.Create('Module DeviceName=DET not found in Modules');
  if not ModuleHasProp(m, mpPropDesc, 'CHA', FCHAProp) then
    raise  Exception.Create('Property CHA not found in Module DET');
  if not ModuleHasProp(m, mpPropDesc, 'CHB', FCHBProp) then
    raise  Exception.Create('Property CHB not found in Module DET');
  {$ELSE}
  if not Modules.FindModuleWithPropVal(piDeviceName, 'DET', m) then
    raise Exception.Create('Module DeviceName=DET not found in Modules');
  if not m.FindPropByDesc('CHA', FCHAProp) then
    raise  Exception.Create('Property CHA not found in Module DET');
  if not m.FindPropByDesc('CHB', FCHBProp) then
    raise  Exception.Create('Property CHB not found in Module DET');
  {$ENDIF}

end;

function TAAA.FindULDP(const AAAObjPropName: string): TULDPObj;
{ as GeTULDP but raises exception if not found }
var o:TULDPObj;
begin
  if not GetULDP(AAAObjPropName, o) then
    raise Exception.Create('AAA ' + AAAObjPropName + ' not found.');
  Result := o;
end;

procedure TAAA.CopyAAAToULD;
var
  up: TULDPObj;
  i: integer;
  s: string;
begin
  for i := 0 to AO.FieldCount - 1 do begin
    if GetULDP(AO.Fields[i].FldDesc.Name, up) then begin
      if (up.TypeID <> ptCommand) and ((up.PropFlags and pfWrite) <> 0) then begin

        if up.PropDesc = 'PRESS_L' then begin
          s := '0';
        end else if up.PropDesc = 'PRESS_H' then begin
          s := IntToStr(StrToInt(AO.Fields[i].AsString) + 10);
        end else begin
          s := AO.Fields[i].AsString;
        end;

        up.ValueInPC := s;
        Log('AAA->ULD ' + AO.Fields[i].FldDesc.Name + '=' + s);
      end;{uldptype}
    end;
  end;
end;

procedure TAAA.UpdateUpdateIntervals;
var
  up: TULDPObj;
  i: integer;
  n: shortstring;
  v: AnsiString;
  pi: PPropInfo;
begin
  {proputl}
  i := 0;
  while ClassGetPropNameAndValue(Self, i, n, v) do begin
    if ClassGetPropInfo(Self, n, pi) then begin
      if pi^.PropType^^.Name = 'TULObjFldAutoCon' then begin
        if GetULDP(n, up) then begin
          if (up.TypeID <> ptCommand) and ((up.PropFlags and pfRead) <> 0) then begin
            up.UpdateInterval := 1000;
            Log('AAA->ULD UpdateTime ' + n + ' 1000 ms');
          end;
        end;
      end;
    end;
    inc(i);
  end;
end;

procedure TAAA.GetULFields;
var
  i: integer;
  n: shortstring;
  value: ansistring;
  f: TULObjFldAutoCon;
  pi: PPropInfo;
begin
  i := 0;
  while ClassGetPropNameAndValue(Self, i, n, value) do begin
    if ClassGetPropInfo(Self, n, pi) then begin
      if pi^.PropType^^.Name = 'TULObjFldAutoCon' then begin
      {if pos('_', n) > 0 then begin}
        GetULField(n, f);
        SetOrdProp(Self, pi, integer(f));
      end;
    end;
    inc(i);
  end;
end;

procedure TAAA.GetULField(const AAAPropName: string; var AField: TULObjFldAutoCon);
begin
  AField := TULObjFldAutoCon(FindULDP(AAAPropName).FindField(pvValueInPC));
end;

destructor TAAA.Destroy;
begin
  {v0.41}
  AAAActive := false;
  {/v0.41}

  {v0.38}
  LogActive := false;
  {/v0.38}

  AcqStop;
  {FAcqInfo.Free;
  FAcqData.Free;}
  Timers.DeleteTimerProc(DoTimer);
  FP1PressHistory.Free;
  FP2PressHistory.Free;
  FColumnTempHistory.Free;
  FReactTempHistory.Free;
  inherited Destroy;
end;

procedure TAAA.DoTimer(Sender: TObject);
begin
  if FIsInTimer then
    exit;
  FIsInTimer := true;
  try
    DoTechControl;{sleep}
  finally
    FIsInTimer := false;
  end;
end;

procedure TAAA.AlarmDeactivate;
begin
  if not AlarmActive then
    exit;
  if FFatalForm <> nil then begin
    FFatalForm.Free;
    FFatalForm := nil;
    FAlarmCancelTime := 0;
  end;{ulobjusru}
end;

{v0.25}
procedure TAAA.DebugAlarmActivate(const AErrorMsg: string);
begin
  FDebErrorDlg := true;
  try
    AlarmActivate(AErrorMsg);
  finally
    FDebErrorDlg := false;
  end;
end;

procedure TAAA.DebugFatalActivate(const AErrorMsg: string);
begin
  FDebErrorDlg := true;
  try
    FatalActivate(AErrorMsg);
  finally
    FDebErrorDlg := false;
  end;
end;
{/v0.25}

procedure TAAA.AlarmActivate(const AErrorMsg: string);
var msg: string;
begin
  {see beeper unit}
  {v0.25}
  if FDebErrorDlg then begin
    if FFatalForm <> nil then
      exit;
  end else
  {/v0.25}
  begin
    if ShouldIgnoreError then
      exit;
    if ErrorActive then
      exit;
    if SimulationOn then
      exit;
  end;
  msg := AErrorMsg;
  if msg = '' then
    msg := {#}'Error conditions.';
  msg := msg + {#}' Shut down AAA?';
  FFatalForm := TAAFatalForm.Create(Application.MainForm);
  FFatalForm.Text := GetTxt(msg);
  FFatalForm.Timeout := 5*60*1000;
  FFatalForm.IsAlarm := true;
  FFatalForm.Show;

  {v0.29}
  LogState('ALARM WARNING');
  {/v0.29
  Log('ALARM WARNING');
  LogState;}
end;

procedure TAAA.ErrorCanceled;
begin
  if FFatalForm = nil then
    exit;
  if FFatalForm.IsAlarm then begin
    FAlarmCancelTime := mstime;
    FFatalCancelTime := 0;
  end else begin
    FAlarmCancelTime := 0;
    FFatalCancelTime := mstime;
  end;
  FFatalForm := nil;
end;

procedure TAAA.ErrorConfirmed;
begin
  LogState('ErrorConfirmed');
  FFatalForm := nil;
  FFatalCancelTime := 0;
  FAlarmCancelTime := 0;
  SetAAAState(aasAborting);
end;

function TAAA.ShouldIgnoreError: boolean;
begin
  Result := (State = aasAborting) or (State <= aasOff);
end;

function TAAA.GetASTempMin: TTemperature;
begin
  Result := {AO.AS_TEMP1RQ}AS_TEMP1RQ.AsFloat - AO.ASTempMinDif;
end;

function TAAA.GetASTempMax: TTemperature;
begin
  Result := {AO.AS_TEMP1RQ}AS_TEMP1RQ.AsFloat + AO.ASTempMaxDif;
end;

function TAAA.GetDetTempMin: TTemperature;
begin
  Result := AO.DET_TEMP1RQ - AO.DetTempMinDif;
end;

function TAAA.GetDetTempMax: TTemperature;
begin
  Result := AO.DET_TEMP1RQ + AO.DetTempMaxDif;
end;


function TAAA.IsInFatalCancelTime: boolean;
begin
  Result := false;
  if FFatalCancelTime <> 0 then begin
    if (mstime - FFatalCancelTime) < 2*60*1000 then
      Result := true
    else
      FFatalCancelTime := 0;
  end;
end;

function TAAA.IsInAlarmCancelTime: boolean;
begin
  Result := false;
  if FAlarmCancelTime <> 0 then begin
    if (mstime - FAlarmCancelTime) < 2*60*1000 then
      Result := true
    else
      FAlarmCancelTime := 0;
  end;
end;


function TAAA.GetFatalActive:boolean;
begin
{  Result := false;}
  if (FFatalForm <> nil) and (not FFatalForm.IsAlarm) then begin
    Result := true;
  end else begin
    Result := IsInFatalCancelTime;
    {if (FFatalCancelTime <> 0) and ((mstime - FFatalCancelTime) < 2*60*1000) then begin
      Result := true;
    end;}
  end;
end;

function TAAA.GetAlarmActive:boolean;
begin
{  Result := false;}
  if (FFatalForm <> nil) and FFatalForm.IsAlarm then begin
    Result := true;
  end else begin
    Result := IsInAlarmCancelTime;
    {if (FAlarmCancelTime <> 0) and ((mstime - FAlarmCancelTime) < 2*60*1000) then begin
      Result := true;
    end;}
  end;
end;

function TAAA.GetErrorActive: boolean;
begin
  Result := (FFatalForm <> nil) or IsInFatalCancelTime or IsInAlarmCancelTime;
end;

procedure TAAA.FatalActivate(const AErrorMsg: string);
var
  msg: string;
begin
  {v0.25}
  if FDebErrorDlg then begin
    if FFatalForm <> nil then
      exit;
  end else
  {/v0.25}
  begin
    if SimulationOn then
      exit;
    if ShouldIgnoreError then
      exit;
    if FatalActive then
      exit;
  end;

  if AlarmActive then
    AlarmDeactivate;
  msg := 'FATAL ERROR ' + AErrorMsg;
  FFatalForm := TAAFatalForm.Create(Application.MainForm);
  FFatalForm.Text := AErrorMsg;
  FFatalForm.Show;
  {v0.29}
  LogState(msg);
  {/v0.29
  Log('FATAL ERROR ');
  LogState;}
end;

procedure TAAA.LogState{v0.29}(const msg: string){/v0.29};
begin
  {v0.29}
  Log(msg);
  {/v0.29}
{  Log('IK    ' + IKStateMsg);}
  Log('AS    ' + ASStateMsg);
  Log('P1    ' + P1StateMsg);
  Log('P2    ' + P2StateMsg);
  Log('Det   ' + DetStateMsg);
  Log('Col   ' + ColStateMsg);
  Log('React ' + ReactStateMsg);
  {v0.29}
  SampleLog(msg);
  SampleLog('AS    ' + ASStateMsg);
  SampleLog('P1    ' + P1StateMsg);
  SampleLog('P2    ' + P2StateMsg);
  SampleLog('Det   ' + DetStateMsg);
  SampleLog('Col   ' + ColStateMsg);
  SampleLog('React ' + ReactStateMsg);
  {/v0.29}
end;

procedure TAAA.Log(const msg: string);
var s:string;
begin {ulobju}
  if not FLogActive then
    exit;
  s := IntToString(FStateTime, 6) + ' '  +
       Pad(AO.FindField('AAAState').AsString, 20) + ' ' +
       IntToString(FSubState, 3);{logfrm}
  {v0.38}
  if SimulationOn then begin
    s := '[SIMUL]' + s;
  end;
  {/v0.38}
  ExeLog.Log(s + ' ' + LogHead + msg);
end;

{v0.29}
procedure TAAA.SampleLog(const msg: string);
var
  s:string;
  cs: TSeqSample;
begin
  cs := CurSample;
  if cs = nil then
    exit;
  s := FloatToStrF(FStateTime/60000, ffFixed, 6, 2) + ' '  + msg;
  cs.ULSR.Protocol := cs.ULSR.Protocol + s + #13#10;
  {ExeLog.Log(s + ' ' + LogHead + msg);}
  {v0.31}
  {v0.36}{/v0.36
  cs.ULSR.SampleState := sasError;}
  {/v0.31}
end;
{/v0.29}

procedure TAAA.DoTechStarting;
begin
  case FSubState of

    0: begin
      {v0.44}
      AO.PumpStartInhibit := true;
      {/v0.44}
      DIK.DetectState := dsUnknown;
      IK_ON.AsInteger := 1;
      IK_ON.UpdateNow;

      Log('IK_ON');
      inc(FSubState);
    end;

    1: begin
      if FStateTime < 8000 then
        exit;

      DP1.DetectState := dsUnknown;
      DP2.DetectState := dsUnknown;
      DAS.DetectState := dsUnknown;
      DDET.DetectState := dsUnknown;

      Log('P1,P2,AS,DET - DetectState set to Unknown');
      inc(FSubState);
    end;

    2: begin
      if FStateTime < 10000 then
        exit;

      CopyAAAToULD;

      P1_STOP.AsInteger := 1;
      P2_STOP.AsInteger := 1;
      DET_OFF.AsInteger := 1;

      {AS_TEMP_OFF.AsInteger := 1;
      DET_TEMP_OFF.AsInteger := 1;
        .. dont call - will clear TEMP set by CopyAAAToULD }

      Log('AAA -> ULD');
      inc(FSubState);
    end;

    3: begin
      {v0.47}
      if SimulationOn then begin
        Log('P1.AUXVALV=1 P2.GRADDIR=NHD DET.ON AS.CALLPS Skipped');
        FSubState := 10;
        exit;
      end;
      {/v0.47}
      if FStateTime < 15000 then
        exit;

      P1_ERRCLR.AsInteger := 1;
      P1_AUXVALV.AsInteger := 1;

      P2_ERRCLR.AsInteger := 1;
      P2_GRADDIR.AsInteger := 1;

      DET_ERRCLR.AsInteger := 1;
      DET_ON.AsInteger := 1;

      AS_OFF.AsInteger := 1;
      AS_OFF.UpdateNow;
      AS_ERRCLR.AsInteger := 1;
      AS_OFF.UpdateNow;

      CoolerEnable(true);

      if not SkipCalibration then
        AS_CALLPS.AsInteger := 1;
      {SendCommand(IID_POD,I_ERRCLR);}

      Log('P1.AUXVALV=1 P2.GRADDIR=NHD DET.ON AS.CALLPS');
      FSubState := 10;
    end;

    {v0.44}
    10: begin
      {v0.47}
      if SimulationOn then begin
        inc(FSubState);
        exit;
      end;
      {/v0.47}
      if FColumnTempHistory.Avg >= AsTempMin then begin
        AO.PumpStartInhibit := false;
        inc(FSubState);
      end;
    end;
    {/v0.44}

    11: begin
      {v0.47}
      if SimulationOn then begin
        Log('DET_ZERO skipped');
        inc(FSubState);
        exit;
      end;
      {/v0.47}
      if FStateTime < {v0.69}45*1000{/v0.69 2*60*1000} then
        exit;
      DET_ZERO.AsInteger := 1;
      Log('DET_ZERO ');
      inc(FSubState);
    end;

    {v0.44}12{/v0.44 11}: begin
      {v0.47}
      if SimulationOn then begin
        if FStateTime < 18*60*1000 then
          exit;
        Log('DevStatesOK skipped');
        SetAAAState(aasReady);
        exit;
      end;
      {/v0.47}
      if (FStateTime < {v0.60.3}32{/v0.60.3 18}*60*1000) and
        (not DevStatesOK)
      then
        exit;
      SetAAAState(aasReady);
    end;

{  - start reactor temp.
  - start column temp.
  - if column temp. > 45 (see below) start P1
  - if P1.Press > P1.Press_L start P2

  - wait max.20 min for OK, then Alarm
  - if OK earlier swith to Ready
}
  end;

end;

procedure TAAA.DoTechStoppingFinishing;
begin
  case FSubState of
    0: begin

      AS_CALLPS.AsInteger := 1;

      DET_TEMP_OFF.AsInteger := 1;
      DET_OFF.AsInteger := 1;

      P1_AUXVALV.AsInteger := 1;
      P2_GRADDIR.AsInteger := 0;

      Log('AS_CALLPS DET-TEMP-OFF DET-OFF P1-AUXVALV=1 P2-GRADDIR=H2O');
      inc(FSubState);
    end;

    1: begin
      if FStateTime < 19*60*1000 then
        exit;

      AS_TEMP_OFF.AsInteger := 1;

      AS_OFF.AsInteger := 1;
      P1_STOP.AsInteger := 1;
      P2_STOP.AsInteger := 1;

      Log('AS-TEMP-OFF AS-OFF P1,P2-STOP');
      FSubState := 10;
    end;

    10: begin
      if FStateTime < 20*60*1000 then
        exit;

      IK_OFF.AsInteger := 1;

      Log('IK-OFF');
      inc(FSubState);
    end;

    11: begin
      if State = aasFinishing then
        SetAAAState(aasFinished)
      else
        SetAAAState(aasStandby);
    end;

  end;
end;

{v0.25}
{procedure TAAA.DoTechStopRunning;
begin
  case FSubState
end;            }
{/v0.25}

procedure TAAA.DoTechAborting;
begin
  case FSubState of
    0: begin

      {AS_CALLPS.AsInteger := 1;}

      DET_TEMP_OFF.AsInteger := 1;
      DET_OFF.AsInteger := 1;

      P1_AUXVALV.AsInteger := 1;
      P2_GRADDIR.AsInteger := 0;

      Log('DET-TEMP-OFF DET-OFF P1-AUXVALV=1 P2-GRADDIR=H2O');
      inc(FSubState);
    end;

    1: begin
      {v0.75}
      if not EmergencyAborting then
      {/v0.75}
      begin
        if FStateTime < 1*60*1000 then
          exit;
      end;

      AS_TEMP_OFF.AsInteger := 1;

      AS_OFF.AsInteger := 1;
      P1_STOP.AsInteger := 1;
      P2_STOP.AsInteger := 1;

      Log('AS-TEMP-OFF AS-OFF P1,P2-STOP');
      FSubState := 10;
    end;

    10: begin
      {v0.75}
      if not EmergencyAborting then
      {/v0.75}
      begin
        if FStateTime < 2*60*1000 then
          exit;
      end;

      IK_OFF.AsInteger := 1;

      Log('IK-OFF');
      inc(FSubState);
    end;

    11: begin
      SetAAAState(aasAborted);
    end;

  end;
end;

procedure TAAA.DoTechRunningStopRunning;
var
  pl: TAAPrgLine;
{  dr: TExpPoint;}
  timedif: integer;
{v0.25}

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

  procedure DoColumnTemp;
  var
    ot: extended;
    nt: extended;
  begin
    {column temp.}
    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)));
    {/column temp.}
  end;

  function DoInject: boolean;
  var vnr: integer;
  begin
    Result := true;
    {v0.38}
    if SimulationOn then begin
      {v0.38}
      if not FCurPrg.Equil then begin
        FASState := astPrepared;
        {AS_SAMPNUM.AsInteger := CurSample.ULSR.VialNr; already set in Load }
      end;
    end;
    {/v0.38}

    if (FASState <> astPrepared) {v0.24}{v0.38}{/v0.38 and (not SimulationOn) }{/v0.24} then
    begin
      { 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');
      end;
    end else begin
      { AS Prepared: }
      vnr := AS_SAMPNUM.AsInteger;
      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));
          end;
        end else begin
          AS_Inject.AsInteger := 1;
          Log('INJECTED VialNr: ' + IntToStr(vnr));
          AcqStart;
          CurSample.ULSR.SampleState := sasRunning;{ulsrtype}
          CurSampleStateChanged;
        end;
      end else begin
        LogState('INJECTED - Dif.Cur.Sample VialNr(' +
          IntToStr(CurSample.ULSR.VialNr) + ') and Prepared VialNr(' + IntToStr(vnr));
        Result := false;
      end;

    end;

    if not Result then begin
      LogState('INJECT FAILED, ABORTING. Sample=' + CurSample.ULSR.FileName);
      {v0.36}{/v0.36
      CurSample.ULSR.SampleState := sasError;
      CurSampleStateChanged;}
      SetAAAState(aasAborting);
    end;

  end;

  function DoLoad: boolean;
  var
    vnr: integer;
    {v0.47}
    ulsr: TULSRObj;
    {/v0.47}
  begin
    Result := true;
    vnr := 0;
    {v0.47}
    ulsr := nil;
    {/v0.47}
    if UPSUnit.IsPresent then begin
      if UPSUnit.ShouldShutDown then begin
        LogState('UPS/Line Power Condition');
        SetAAAState(aasStopRunning);
        Result := false;
        exit;
      end;
    end;

    if AO.AAAState = aasStopRunning then begin
      Log('LOAD - ignored (stop running requested)');
      exit;
    end;

    if CurSample.ULSR.SampleState = sasWaiting then begin
      vnr := CurSample.ULSR.VialNr;
      {v0.47}
      ulsr := CurSample.ULSR;
      {/v0.47
      CurSample.ULSR.SampleState := sasInLoop;}
      Log('LOAD - loading current sample');
    end else if Sequence.NextSample <> nil then begin
      if CurSample.ULSR.PrgFileName <> Sequence.NextSample.ULSR.PrgFileName then begin
        Log('LOAD - ignored (next sample has dif.program)');
      end else begin
        vnr := Sequence.NextSample.ULSR.VialNr;
        {v0.47}
        ulsr := Sequence.NextSample.ULSR;
        {/v0.47
        Sequence.NextSample.ULSR.SampleState := sasInLoop;
        Sequence.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

      if (FDetState <> dstOK) {v0.25} and (not SimulationOn){/v0.25} then begin
        {v0.29}
        LogState('Before LOAD - DETECTOR FAILURE(' + GetDetStateMsg +
          ') detected, STOPPING before ' + CurSample.ULSR.FileName);
        {/v0.29
        Log('before LOAD - DETECTOR FAILURE(' + GetDetStateMsg +
          ') detected, STOPPING before ' + CurSample.ULSR.FileName);}
        {v0.36}{/v0.36
        CurSample.ULSR.SampleState := sasError;}
        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;
        {v0.47}
        ulsr.SampleState := sasInLoop;
        ulsr.SetFlag(rfCantDelete, true);
        CurSampleStateChanged;
        {/v0.47}
      end;

    end;

  end;
{/v0.25}
  procedure DoSampleDone;
  begin
    if CurSample = nil then
      exit;
    if CurSample.ULSR.SampleState in [sasRunning, sasCompleting] then begin
      CurSample.ULSR.SampleState := sasDone;
      Log('SAMPLE DONE: Sample='+ IntToStr(CurSample.ULSR.VialNr));
    end;
  end;


begin{DoTechRunningStopRunning}
{  if FCurPrg = nil then
    exit;}
  {aaprgu}

  if FCurPrg.LineReady({v0.49}TPrgLine{/v0.49}(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
          if not DoInject then
            exit;
        end;

        acZero: begin
          DET_ZERO.AsInteger := 1;
          Log('DET_ZERO');
        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('ACQ STOP');
          end else begin
            {AcqEquilStop;}
            Log('ACQ STOP - ignored (equilibrating)');
          end;
        end;

        acStartEquil:;

        acLoad: begin
          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.25}
    if FCurPrg.Equil then begin
      AcqEquilStop
    end else begin
      AcqStop;{just to be sure the acq is stopped (if AcqStop command was not in program)}
    end;

    if State = aasStopRunning then begin
      DoSampleDone;
      Log('POSTPONED SEQUENCE STOP EXECUTED');
      SetAAAState(aasStopping{aasReady});
    end else
    {/v0.25}
    begin
      if FCurPrg.Equil then begin
        { was equilibrating: }{v0.38}CurPrg := FCurPrg;{/v0.38}{ reselect/reactivate the same prg }
        FCurPrg.Start;
        StateStart(aasRunning, 0);
        {v0.74}
        Log('PRG ' + FCurPrg.FileName + ' STARTED (AFTER EQUIL) Vial ' +
          IntToStr(CurSample.ULSR.VialNr) + ' ' +
          Sequence.DirName + CurSample.ULSR.FileName);
        {/v0.74
        Log('PRG ' + FCurPrg.FileName + ' STARTED (AFTER EQUIL) Sample ' +
          IntToStr(CurSample.ULSR.VialNr) + ' ' + CurSample.ULSR.FileName);
        }
      end else begin
        {CurSample finihed}
        DoSampleDone;


        if Sequence.Next then begin

          {v0.25}
          DoCurSampleSelected;
          {/v0.25
          CurSample.Prg.LoadFromFile(AbsoluteFileName(AO.PrgsDir, CurSample.ULSR.PrgFileName));}
          Log('SAMPLE NEXT: Sample='+ IntToStr(CurSample.ULSR.VialNr) + ' PRG LOADED=' + CurSample.ULSR.PrgFileName);
          {v0.38}CurPrg{/v0.38 FCurPrg} := {v0.49}TAAPrg{/v0.49}(CurSample.Prg);
          {aaprgu}

          if Sequence.LastSample.ULSR.PrgFileName <> Sequence.CurSample.ULSR.PrgFilename then
          begin
            FCurPrg.StartEquil(timedif);
            {v0.25}
            AcqEquilStart;
            {/v0.25}
            StateStart(aasRunning, timedif);
            Log('PRG ' + FCurPrg.FileName + ' EQUIL STARTED');
          end else begin
            FCurPrg.Start;
            StateStart(aasRunning, 0);

            Log('PRG ' + FCurPrg.FileName + ' STARTED');
            {v0.74}
            Log('Vial ' + IntToStr(CurSample.ULSR.VialNr) + ' ' +
            Sequence.DirName + CurSample.ULSR.FileName);
            {/v0.74};
          end;

        end else begin

          {v0.38}
          CurPrg := nil;
          {/v0.38}

          SetAAAState(aasFinishing);

        end;

      end;
    end;

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

{debug ulanutl debugu}
{v0.53 moved to Simulationu}
{/v0.53
function GetDebugAbsorbance(ADataIndex: integer; ATimeSec: single): single;
const
  MaxTime = 20 * 60;
  PeakTime = 1 * 60;
  PeakHeight = 1;
  PeakCount = 10;
begin
  result := (sin((ATimeSec/PeakTime) * 2 * pi) + 1) * PeakHeight * ((1 + ADataIndex)/2);
end;
}
procedure TAAA.AcqDoTimer;
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;
      {$IFDEF USEDLL}
      ModulePropDoAction(FCHAProp, mpaRequestRead, nil, 0);
      ModulePropDoAction(FCHBProp, mpaRequestRead, nil, 0);
      {$ELSE}
      FCHAProp.RequestRead;{SetState(psReadRequested, true);}
      FCHBProp.RequestRead;{SetState(psReadRequested, true);}
      {$ENDIF}
      {FCHAProp.SetFlag(pfNeedUpdateFromDevice, true);
      FCHBProp.SetFlag(pfNeedUpdateFromDevice, true);}
      {/v0.25}
    end;

    FAcqInfo.PointsAddStop;
  end;
  {specform}
end;

{procedure TAAA.SetDevProp(ULDR: TULDRObj; ULDP: TULDPObj; AValue: single; const msg: string);
  SetDevProp(P1, P1_FLOW, 1, 'protoze chci');}

procedure TAAA.DoTechControl;
var
{  v: TTechValue;}
  ok: boolean;
  chn: boolean;
  ermsg: string;

  procedure CheckHardwareRanges;
  var
    ok:boolean;
    fatmsg: string;
  begin
    ok := true;
    fatmsg := '';
    if not FColumnTempHistory.CheckHardLimits(0, 900, FcColState) then begin
      ok := false;
      fatmsg := 'Column temp. out of hard limits.';
    end;

    if not FReactTempHistory.CheckHardLimits(0, 1500, FcReactState) then begin
      ok := false;
      fatmsg := 'Reactor temp. out of hard limits.';
    end;

    if (AO.AAAState >= aasReady) then begin
      if not FP1PressHistory.CheckHardLimits(AO.P1_PRESS_L, AO.P1_PRESS_H, FcP1State) then begin
        ok := false;
        fatmsg := 'P1 out of hard limits.';
      end;
      if not FP2PressHistory.CheckHardLimits(AO.P2_PRESS_L, AO.P2_PRESS_H, FcP2State) then begin
        ok := false;
        fatmsg := 'P2 out of hard limits.';
      end;
    end else begin
      if not FP1PressHistory.CheckHardLimits(0, AO.P1_PRESS_H, FcP1State) then begin
        ok := false;
        fatmsg := 'P1 out of hard limits.';
      end;
      if not FP2PressHistory.CheckHardLimits(0, AO.P2_PRESS_H, FcP2State) then begin
        ok := false;
        fatmsg := 'P1 out of hard limits.';
      end;
    end;

    if (DevStatToInt(P1_STATUS.AsInteger) < 0) then begin
      FcP1State := dstDeviceError;
      ok := false;
      fatmsg := 'P1 ' + DevStateToString(FcP1State);
    end;

    if (DevStatToInt(P2_STATUS.AsInteger) < 0)  then begin
      FcP2State := dstDeviceError;
      ok := false;
      fatmsg := 'P2 ' + DevStateToString(FcP2State);
    end;


    if not ok then begin
      if not FatalActive then begin
        if not ShouldIgnoreError then begin
          {v0.25}
          if not SimulationOn then
          {/v0.25}
          begin
            {v0.29}
            LogState('FATAL - OOR');
            {/v0.29
            Log('FATAL - OOR');}
          end;
        end;
        FatalActivate(fatmsg);
      end;
    end;

  end;

  procedure CheckDevicesPresence;
  var
    ok: boolean;
    dst: TDevState;
  begin
    ok := true;
    if DIK.DetectState <> dsPresent then begin
      FcIKState := dstCommunicationError;
      ok := false;
    end else begin
      dst := DevStatToInt(IK_STATUS.AsInteger);
      if dst < 0 then
        FcIKState := dstDeviceError
      else if dst = 0 then
        FcIKState := dstOK;{dstOff;{moduutl}
    end;

    if DAS.DetectState <> dsPresent then begin
      FcASState := astCommunicationError;
      FcColState := dstCommunicationError;
      ok := false;
    end else begin
      FcASState := AS_STATUS_Decode(AS_STATUS.AsInteger);
      dst := DevStatToInt(AS_TEMP_ST.AsInteger);
      if dst < 0 then
        FcColState := dstDeviceError
      else if dst = 0 then
        FcColState := dstOff;
    end;

    if DP1.DetectState <> dsPresent then begin
      FcP1State := dstCommunicationError;
      ok := false;
    end else begin
      dst := DevStatToInt(P1_STATUS.AsInteger);
      if dst < 0 then
        FcP1State := dstDeviceError
      else if dst = 0 then
        FcP1State := dstOff;
    end;

    if DP2.DetectState <> dsPresent then begin
      FcP2State := dstCommunicationError;
      ok := false;
    end else begin
      dst := DevStatToInt(P2_STATUS.AsInteger);
      if dst < 0 then
        FcP2State := dstDeviceError
      else if dst = 0 then
        FcP2State := dstOff;
    end;

    if DDET.DetectState <> dsPresent then begin
      FcDetState := dstCommunicationError;
      FcReactState := dstCommunicationError;
      ok := false;
    end else begin
      dst := DevStatToInt(DET_STATUS.AsInteger);
      if dst < 0 then
        FcDetState := dstDeviceError
      else if dst = 0 then
        FcDetState := dstOff;

      dst := DevStatToInt(DET_TEMP_ST.AsInteger);
      if dst < 0 then
        FcReactState := dstDeviceError
      else if dst = 0 then
        FcReactState := dstOff;

    end;

    if not OK then begin
      if not FatalActive then begin
        if not ShouldIgnoreError then begin
          if not SimulationOn then begin
            {v0.29}
            LogState('FATAL - Communication Error');
            {/v0.29
            Log('FATAL - Communication Error');}
          end;
        end;
        FatalActivate('Communication Error');
      end;
    end;
  end;

  procedure CheckReady;
  begin
    if not DevStatesOK then begin
      if not ShouldIgnoreError then begin
        if (State <> aasStarting) and (State <> aasStopping) and (State <> aasFinishing) then
          AlarmActivate(DevStatesLastErrorMsg);
      end;
      {case State of
        aasStarting: begin
          SetAAAState(aasReady);
        end;
      end;}
    end else begin
      if AlarmActive then
        AlarmDeactivate;
    end;
  end;

  procedure AcqDebugDoTimer;
  begin
    if FAcqDebugStartTime = 0 then
      exit;
    AcqDoTimer;
  end;


  procedure DoCheckCooler;
  begin
    if not AO.CoolerBlockEnabled then
      exit;

    if UPSUnit.IsPresent then begin
      if UPSUnit.Changed then begin
        if UPSUnit.IsPowerDown then begin
          if State in [aasRunning,aasStopRunning] then
            CoolerEnable(false);
        end else begin
          CoolerEnable(true);
        end;
        AO.DoChange;
      end;
    end;
  end;

begin
  {v0.24}
  AcqDebugDoTimer;
  {/v0.24}
  AO.DoChangeLock; { prevent sending cmULObjUpdate messages during this method }
  try
    if AO.AAAState <= aasOff then begin
      {switch off alarms...}
      FStateTime := 0;
      FUsrStateTimeOffset := 0;
      exit;
    end;

    DoCheckCooler;

    {v0.25}
    IK_WDG.AsInteger := 1;
    {/v0.25}
    {v0.74}
    {/v0.74}
    {update state time}
    FStateTime := mstime - FStateStartTime;{aaatype}
    AO.DoChange;
    {/update state time}

    case AO.AAAState of
      aasStarting: begin
        DoTechStarting;
        if FSubState < 10 then
          exit;
      end;

      aasRunning, aasStopRunning: begin
        DoTechRunningStopRunning;
      end;

      aasFinishing, aasStopping: begin
        DoTechStoppingFinishing;
        if FSubState >= 10 then
          exit;
      end;

      aasAborting: begin
        DoTechAborting;
        if FSubState >= 10 then
          exit;
      end;

    end;

    {v0.36}
    if AO.AAAState <= aasOff then begin
      FStateTime := 0;
      FUsrStateTimeOffset := 0;
      exit;
    end;
    {/v0.36}

    {clear FcXXState}
    FcP1State := dstOK;
    FcP2State := dstOK;
    FcDetState := dstOK;
    FcColState := dstOK;
    FcReactState := dstOK;
    FcIKState := dstOK;

    FcASState := astOK;
    {/clear FcXXState}

    FP1PressHistory.PutVal(P1_PRESS.AsFloat);
    FP2PressHistory.PutVal(P2_PRESS.AsFloat);
    FColumnTempHistory.PutVal(AS_TEMP1.AsFloat);
    FReactTempHistory.PutVal(DET_TEMP1.AsFloat);

    FP1PressHistory.CheckDeviation(1, AO.P1PressDev, FcP1State);
    FP1PressHistory.CheckLimits(AO.P1_PRESS_L, AO.P1_PRESS_H, FcP1State);

    FP2PressHistory.CheckDeviation(1, AO.P2PressDev, FcP2State);
    FP2PressHistory.CheckLimits(AO.P2_PRESS_L, AO.P2_PRESS_H, FcP2State);

    FColumnTempHistory.CheckDeviation(10, AO.AsTempDev, FcColState);
    FColumnTempHistory.CheckLimits(AsTempMin, AsTempMax, FcColState);

    FReactTempHistory.CheckDeviation(10, AO.DetTempDev, FcReactState);
    FReactTempHistory.CheckLimits(DetTempMin, DetTempMax, FcReactState);

    ok := true;

    if AO.AAAState >= aasReady then begin
      ermsg := '';

      if not FP1PressHistory.IsOK(chn) then begin
        if chn then begin
          ermsg := 'P1 Deviation Too High';
          Log(ermsg);
        end else begin
          ermsg := 'P1 History Error';
        end;
        ok := false;
      end;

      if not ok then begin
        AlarmActivate(ermsg);
      end else begin

      end;

    end;

    CheckHardwareRanges;

    CheckDevicesPresence;

    {
    if (FColumnTempHistory.Avg > 90) or (FReactTempHistory.Avg > 150) or
       (FP1PressHistory.Avg > AO.P1_PRESS_H) or
       (FP2PressHistory.Avg > AO.P2_PRESS_H) or
       (DevStatToInt(P1_STATUS.AsInteger) < 0) or
       (DevStatToInt(P2_STATUS.AsInteger) < 0) or
       ( (AO.AAAState >= aasReady) and ( FP1PressHistory.Avg < AO.P1_PRESS_L ) ) or
       ( (AO.AAAState >= aasReady) and ( FP2PressHistory.Avg < AO.P2_PRESS_L ) ) then
    begin
      Log('FATAL - OOR');
      FatalActivate;
    end;
    }

    if (FP1PressHistory.Avg < AO.P1_PRESS_L) or (DevStatToInt(P1_STATUS.AsInteger) <= 0)then
    begin
      if DevStatToInt(P2_STATUS.AsInteger) > 0 then begin
        {PumpCheckState(2, false);}
        P2_STOP.AsInteger := 1;
        {v0.29}
        LogState('P2_STOP, reason: P1 OOR or ERROR');
        {/v0.29
        Log('P2_STOP, reason: P1 OOR or ERROR');
        LogState;}
      end;
    end else begin
      if P2_STATUS.AsInteger = 0 then begin
        {v0.44}
        if not AO.PumpStartInhibit then
        {/v0.44}
        begin
          P2_START.AsInteger := 1;
          {v0.25}
          if not SimulationOn then
          {/v0.25}
            Log('P2_START');
        end;
      end;
    end;

    if FColumnTempHistory.Avg < 25 * 10 then begin
      if DevStatToInt(P1_STATUS.AsInteger) > 0 then begin
        P1_STOP.AsInteger := 1; {ulobju}
        P2_STOP.AsInteger := 1;
        {v0.29}
        LogState('P1,P2_STOP, reason: Column Temp.');
        {/v0.29
        Log('P1,P2_STOP, reason: Column Temp.');
        LogState;}
      end;
    end else begin
      if P1_STATUS.AsInteger = 0 then begin
        {v0.44}
        if not AO.PumpStartInhibit then
        {/v0.44}
        begin
          P1_START.AsInteger := 1;
          {v0.25}
          if not SimulationOn then
          {/v0.25}
            Log('P1_START');
        end;
      end;
    end;

  finally
    {update dev state properties}
    FcDevStatesOK := true;
      { following state setting commands will set this false, if any state <> OK }
    IKState := FcIKState;

    P1State := FcP1State;
    P2State := FcP2State;
    DetState := FcDetState;
    ColState := FcColState;
    ReactState := FcReactState;
    DetState := FcDetState;

    ASState := FcASState;
    {/update dev state properties}
        { assigning to these properties (will) send update message to AO users
          (especially TULObjCtrl), necessary if they use AAA object properties
          (i.e. AAA is AO usr) }
    if SimulationOn then begin
      FcDevStatesOK := true;
    end;
    DevStatesOK := FcDevStatesOK;

    CheckReady;
      { check the DevStatesOK and behave accordingly }
    AO.DoChangeUnlock;
  end;
end;

{v0.25}
function TAAA.CheckSequenceFilesPresence: boolean;
var
  i : integer;
  s: string;
  ss: TSeqSample;
  fn: string;
{  cnt: integer;}
begin
  Result := true;
  s := '';
  for i := 0 to Sequence.ChildCount - 1 do begin
    ss := TSeqSample(Sequence.Childs[i]);
    if ss.Obj.RecID = ULSRID then begin
      fn := AbsoluteFileName(AO.PrgsDir, ss.ULSR.PrgFileName
        {v0.36}, AAPGExt{/v0.36});
      if not FileExists(fn) then
        s := s + ' ' + fn;
      fn := AbsoluteFileName(AO.MethodsDir, ss.ULSR.MethodFileName
        {v0.36}, ULMExt{/v0.36});
      if not FileExists(fn) then
        s := s + ' ' + fn;
    end;
  end;
  if s <> '' then begin
    ShowMessage(GetTxt({#}'Program or Method Files Missing:') + ' ' + s, smError, 0);
    Result := false;
  end;
end;
{/v0.25}
function TAAA.UsrSetAAAState(AAAAState: TAAAState): TAAAResult;
begin
{  case AAAAState of
    aasDisconnected,
    aasAborted,
    aasFinished,
    aasStandby,
    aasOff,
    aasStarting,
    aasStopping,
    aasAborting,
    aasReady,
    aasRunning
  end;}
  {v0.25}
  if AAAAState = aasRunning then begin
    if not CheckSequenceFilesPresence then begin
      Result := arFilesMissing;
      exit;
    end;
  end;
  Log('UsrSetAAAState ' + IntToStr(ord(AAAAState)));
  Result := SetAAAState(AAAAState);
end;

procedure TAAA.ClearParams;
var i: integer;
begin
  for i := 0 to FHistCount - 1 do
    FHists[i].Clear;
  FcIKState := dstUnknownState;

  FcP1State := dstUnknownState;
  FcP2State := dstUnknownState;
  FcDetState := dstUnknownState;
  FcColState := dstUnknownState;
  FcReactState := dstUnknownState;

  FcASState := astUnknownState;{moduutl}

  FcDevStatesOK := false;
end;

function TAAA.FirstSampleRun: TAAAResult;
var timedif:integer;
begin
  {!!! set to prg from sequence.cursample
  if FCurPrg = nil then begin
    result := -1;
    exit;
  end;}
  if Sequence.First then begin{sequenceu}
    {v0.25}
    DoCurSampleSelected;
    {/v0.25
    CurSample.Prg.LoadFromFile(AbsoluteFileName(AO.PrgsDir, CurSample.ULSR.PrgFileName));}
    Log('PRG FIRST LOADED ' + CurSample.ULSR.PrgFileName);
    {v0.38}CurPrg{/v0.38 FCurPrg}:= {v0.49}TAAPrg{/v0.49}(CurSample.Prg);
    {
    FSubState := 0;
    AO.AAAState := aasRunning;
    FUsrStateTimeOffset := 0;
    FStateStartTime := mstime;
    FStateTime := 0;}

    if FCurPrg.StartEquil(timedif) then begin
      {v0.25}
      AcqEquilStart;
      {/v0.25}
      StateStart(aasRunning, timedif);
      Log('PRG ' + FCurPrg.FileName + ' EQUIL START');
      Result := 0;
    end else begin
      {v0.29}
      LogState('PRQ EQUIL START FAILED');
      {/v0.29
      Log('PRQ EQUIL START FAILED');}
      Result := arInvalidProgramNoEquil;
    end;
    {v0.25}
    CurSampleStateChanged;
    {/v0.25}
  end else begin
    {v0.38}
    CurPrg := nil;
    {/v0.38 FCurPrg}
    Result := arNoSamples;
    Log('RUN: NO SAMPLES IN SEQUENCE');
  end;
end;

procedure TAAA.StateStart(aas: TAAAState; AUsrTimeOffset: integer);
begin
  FSubState := 0;
  AO.AAAState := aas;
  FUsrStateTimeOffset := AUsrTimeOffset;
  FStateStartTime := mstime;
  FStateTime := 0;
end;

function TAAA.SetAAAState(AAAAState: TAAAState): TAAAResult;

  function DoStarting: TAAAResult;
  begin
    Result := 0;
    ClearParams;
    StateStart(aasStarting, {v0.60.3}-30{/v0.60.3 -18}*60*1000);
  end;

  function DoStopping: TAAAResult;
  begin
    Result := 0;
    StateStart(aasStopping, -20*60*1000);
  end;

  function DoFinishing:TAAAResult;
  begin
    Result := 0;
    StateStart(aasFinishing, -20*60*1000);
  end;

  function DoReady: TAAAResult;
  begin
    Result := 0;
    StateStart(aasReady, 0);
  end;

  function DoFinished: TAAAResult;
  begin
    Result := 0;
    StateStart(aasFinished, 0);
  end;

  function DoStandby: TAAAResult;
  begin
    Result := 0;
    StateStart(aasStandby, 0);
  end;

  function DoAborting: TAAAResult;
  {v0.36}
  var
    cs: TSeqSample;
  {/v0.36}
  {v0.47}
    ns: TSeqSample;
  {/v0.47}
  begin
    Result := 0;

    {v0.47}
    ns := Sequence.NextSample;
    if (ns <> nil) and (ns.ULSR.SampleState = sasInLoop) then begin
      ns.ULSR.SampleState := sasError;
    end;
    {/v0.47]

    {v0.36}
    cs := CurSample;
    if cs <> nil then begin
      case cs.ULSR.SampleState of
        sasRunning, sasInLoop: begin
          cs.ULSR.SampleState := sasError;
        end;
        sasWaiting: begin
          if (FCurPrg <> nil) and (FCurPrg.Equil) then begin
            cs.ULSR.SampleState := sasError;{ulsrtype}
          end;
        end;
      end;
      Sequence.NoSample;
      CurSampleStateChanged;
    end;
    {/v0.36}
    {v0.38}
    CurPrg := nil;
    {/v0.38}
    {v0.75}
    if EmergencyAborting then begin
      StateStart(aasAborting, EmergencyAbortingInterval);
    end else begin
      StateStart(aasAborting, AbortingInterval);

      if EMailOnAbort then begin
        if cs <> nil then
          TryEMailCulMsg(cs.ULSR.Protocol)
        else
          TryEMailCulMsg('Aborting sequence for unknown reason.');
      end;

    end;
    {/v0.75
    StateStart(aasAborting, AbortingInterval);}
    {v0.25}
    {AcqEquilStop;}
    AcqStop;
    {/v0.25}
  end;

  function DoAborted: TAAAResult;
  begin
    Result := 0;
    StateStart(aasAborted, 0);
    {v0.75}
    FEmergencyAborting := false;
    {/v0.75}
  end;

  function DoRunning: TAAAResult;
  begin
    Result := FirstSampleRun;
    if Result <> 0 then
      exit;
    {
    FSubState := 0;
    AO.AAAState := aasRunning;



    FUsrStateTimeOffset := 0;
    FStateStartTime := mstime;
    FStateTime := 0;}
  end;

  function DoStopRunning: TAAAResult;
  begin
    Result := 0;
    AO.AAAState := aasStopRunning;
    AO.RunWhenReady := false;
  end;

  {v0.25}
  function RequestStopRunning: TAAAResult;
  begin
    Result := 0;
    case ShowMessage(GetTxt({#}'Stop after current sample completed? (No for ABORT)'), smYesNoCancel, 0) of
      cmYes: begin
        {StateStart(aasStopRunning, 0); .. do not reset time:}
        DoStopRunning;
        {
        AO.AAAState := aasStopRunning;
        AO.RunWhenReady := false;}
      end;
      cmNo: {v0.36}DoAborting{/v0.36 StateStart(aasAborting, AbortingInterval);}
    end;
  end;
  {/v0.25}


begin
  Result := -1;
  Log('SetAAAState begin=' + IntToStr(integer(AAAAState)));
  case AAAAState of

    aasStandby: begin

      case AO.AAAState of

        aasStandby: begin
          Result := 0;
        end;

        aasDisconnected: begin
          AO.AAAState := aasStandby;
          Result := 0;
        end;

        aasStopping: begin
          Result := DoStandby;
        end;

      else
        Result := arInvalidStateRequest;
      end;
    end;

    aasStarting: begin

      if (State <= aasOff) or (State = aasStopping) or (State = aasAborting) then begin
        Result := DoStarting;
      end else begin
        Result := arInvalidStateRequest;
      end;

    end;

    aasStopping: begin
      if State <= aasOff then begin
        Result := arInvalidStateRequest;
      end else begin
        Result := DoStopping;
      end;
    end;

    aasFinishing: begin
      if (State <> aasRunning) {v0.25} and (State <> aasStopRunning) {/v0.25} then begin
        Result := arInvalidStateRequest;
      end else begin
        Result := DoFinishing;
      end;
    end;

    aasDisconnected: begin
      case State of
        aasStopping, aasAborting: begin
          AO.AAAState := aasDisconnected;
          Result := 0;
        end;
      else
        Result := arInvalidStateRequest;
      end;
    end;

    aasReady: begin
      {v0.44}
      AO.PumpStartInhibit := false;
      {/v0.44}
      if (State = aasStarting) {v0.25} or (State = aasStopRunning){/v0.25} then begin
        Result := DoReady;
      end else {v0.25} if State = aasRunning then begin
        Result := RequestStopRunning;
      end else {/v0.25} begin
        Result := arInvalidStateRequest;
      end;
    end;

    {v0.25}
    aasStopRunning: begin
      if State <> aasRunning then begin
        Result := arInvalidStateRequest;
      end else begin
        Result := DoStopRunning;{RequestStopRunning;}
      end;
    end;
    {/v0.25}

    aasRunning: begin
      {v0.44}
      AO.PumpStartInhibit := false;
      {/v0.44}
      if State = aasReady then begin
        Result := DoRunning;
      end else{v0.25} if State = aasStopRunning then begin
        AO.AAAState := aasRunning;
      end else{/v0.25} begin
        Result := arInvalidStateRequest;
      end;
    end;

    aasFinished: begin
      if (State = aasFinishing) then begin
        Result := DoFinished;
      end else begin
        Result := arInvalidStateRequest;
      end;
    end;

    aasAborting: begin
      {if (State = aasAborting) then begin
        Result := DoAborted;
      end else begin
        Result := arInvalidStateRequest;
      end;}
      Result := DoAborting;
    end;

    aasAborted: begin
      if (State = aasAborting) then begin
        Result := DoAborted;
      end else begin
        Result := arInvalidStateRequest;
      end;
    end;

  end;{/case AAAAState}         {proputl}
  Log('SetAAAState end=' + IntToStr(integer(AAAAState)) + ' Result:' + IntToStr(Result));
  if AO.RunWhenReady and (State = aasReady) then begin
    Log('RunWhenReady true');
    AO.RunWhenReady := false;
    SetAAAState(aasRunning);
  end;
end;

function TAAA.GetAAAState: TAAAState;
begin
  Result := AO.AAAState;
end;

function TAAA.GetP1StateMsg: string;
begin
  Result := DevStateToString(FP1State);
end;

function TAAA.GetP2StateMsg: string;
begin
  Result := DevStateToString(FP2State);
end;

function TAAA.GetDetStateMsg: string;
begin
  Result := DevStateToString(FDetState);
end;

function TAAA.GetColStateMsg: string;
begin
  Result := DevStateToString(FColState);
end;

function TAAA.GetReactStateMsg: string;
begin
  Result := DevStateToString(FReactState);
end;

function TAAA.GetASStateMsg: string;
begin
  Result := ASStateToString(FASState)
  {v0.64} + ' SAMPNUM:' + AS_SAMPNUM.AsString
  + ' STATUS:' + AS_STATUS.AsString + ' TEMP_ST:' + AS_TEMP_ST.AsString{/v0.64};
end;

{set devices states}
procedure TAAA.SetIKState(dst: TDevState);
begin
  if dst <> FIKState then begin
    FIKState := dst;
    AO.DoChange;
  end;
  if FIKState <> dstOK then begin
    FcDevStatesOK := false;
    FDevStatesLastErrorMsg := 'IK ' + DevStateToString(FIKState)+ '.';
  end;
end;

procedure TAAA.SetP1State(dst: TDevState);
begin
  if dst <> FP1State then begin
    FP1State := dst;
    AO.DoChange;
  end;
  if FP1State <> dstOK then begin
    FcDevStatesOK := false;
    FDevStatesLastErrorMsg := 'P1 ' + P1StateMsg + '.';
  end;

end;

procedure TAAA.SetP2State(dst: TDevState);
begin
  if dst <> FP2State then begin
    FP2State := dst;
    AO.DoChange;
  end;
  if FP2State <> dstOK then begin
    FcDevStatesOK := false;
    FDevStatesLastErrorMsg := 'P2 ' + P2StateMsg + '.';
  end;
end;

procedure TAAA.SetDetState(dst: TDevState);
begin
  if dst <> FDetState then begin
    FDetState := dst;
    AO.DoChange;
  end;
{v0.25}{/v0.25 if FDetState <> dstOK then
    FcDevStatesOK := false;}
end;

procedure TAAA.SetColState(dst: TDevState);
begin
  if dst <> FColState then begin
    FColState := dst;
    AO.DoChange;
  end;
  if FColState <> dstOK then begin
    FcDevStatesOK := false;
    FDevStatesLastErrorMsg := 'Col ' + ColStateMsg + '.';
  end;
end;

procedure TAAA.SetReactState(dst: TDevState);
begin
  if dst <> FReactState then begin
    FReactState := dst;
    AO.DoChange;
  end;
  if FReactState <> dstOK then begin
    FcDevStatesOK := false;
    FDevStatesLastErrorMsg := 'React ' + ReactStateMsg + '.';
  end;
end;


procedure TAAA.SetASState(ast: TASState);
var oast: TASState;
begin
  oast := FASState;
  if ast <> FASState then begin
    FASState := ast;
    AO.DoChange;
  end;
  if FASState <> astOK then begin
    if oast <> FASState then begin
      Log('SetASState ' + IntToStr(FASState));
    end;
    {DebLog('SetASState ' + IntToStr(ast));}
    if FASState >= astError then begin
      FcDevStatesOK := false;
      FDevStatesLastErrorMsg := 'AS ' + ASStateMsg + '.';
    end;
  end;
end;
{/set devices states}

function TAAA.GetUsrStateTimeStr: string;
  { time of current AAAState in minutes }
begin
  Result := FloatToStrF((FStateTime + FUsrStateTimeOffset)/ 1000 / 60, ffFixed, 6, 2);
end;

procedure TAAA.SetUsrStateTimeStr(const AValue: string);
var i: integer;
begin
  if UserMode <> umSysOp then
    exit;
  i := round(StrToFloat(AValue) * 60 * 1000) - FUsrStateTimeOffset;
  FStateStartTime := mstime - i;
end;

procedure TAAA.UsrRequestOnOff;
begin
  if State <= aasOff then begin
    UsrSetAAAState(aasStarting);
  end else begin
    if (State = aasRunning){v0.25} or (State = aasStopRunning) {/v0.25} then begin
      ShowMessage(GetTxt({#}'Can not switch off. Stop the sequence first.'), smError, 0);
    end else begin
      {v0.25}
      if (State = aasStopping) or (State = aasAborting) then begin
         UsrSetAAAState(aasStarting);
      end else
      {/v0.25}
      begin
        if ShowMessage(GetTxt({#}'Really switch the AAA off?'), smNoYes, 0) <> cmYes then
          exit;
        UsrSetAAAState(aasStopping);
      end;
    end;
  end;
end;

function TAAA.ChildCreate(AChildObj: TULObj):TULObjUsr;
var id: TULRecID;
begin
  if AChildObj = nil then begin
    id := ULIID;
  end else begin
    id := AChildObj.RecID;
  end;

  case id of
    ULIID: begin
      if FDefInstrument = nil then begin
        FDefInstrument := TInstrument.Create(Self, AChildObj, id);{instrumentu}
        {v0.50
        if FDefInstrument.ULI.ChannelName = '' then}
        {/v0.50}
        begin
          FDefInstrument.ULI.ChannelName := {v0.50}pvAAAChannel{/v0.50 pvUlanDefaultChannel};
        end;
        Result := FDefInstrument;
      end else
        Result := nil;
    end;
  else
    Result := nil;
  end;
end;

procedure TAAA.UpdateDefDirs;{sequenceu ulobjdes}
begin
  Obj.FindField('PrgFileName').FldDesc.DefDir := AO.PrgsDir;
  Obj.FindField('MethodFileName').FldDesc.DefDir := AO.MethodsDir;
  Obj.FindField('SeqFileName').FldDesc.DefDir := AO.SeqsDir;

  DefMethod.Obj.ObjDesc.DefDir := AO.MethodsDir;{ulmtype}
  DefMethod.UpdateDefDirs; { methodu }

  DefPrg.Obj.ObjDesc.DefDir := AO.PrgsDir;{aapgtype}

  Sequence.Obj.ObjDesc.DefDir := AO.SeqsDir;{ulsqtype}
  Sequence.UpdateDefDirs;
end;

procedure TAAA.DoAfterSettingsEdit;
begin
  if DefPrg.FileName <> GetPrgFullFileName then begin
    DefPrg.LoadFromFile(GetPrgFullFileName);
  end;
  if DefMethod.FileName <> GetMethodFullFileName then
    DefMethod.LoadFromFile(GetMethodFullFileName);
  if Sequence.FileName <> GetSeqFullFileName then begin
    Sequence.LoadFromFile(GetSeqFullFileName);
    SequenceUpdateDefaults(Sequence.ULSQ);
  end;
  UpdateDefDirs;
  if State <> aasRunning then
    CopyAAAToULD; {aaatype}
  {v0.36}
  {v0.76}
  DebLog('AAA.Save');
  {/v0.76}

  AAA.Save;
  {/v0.36}
end;

procedure TAAA.WMAppMessage(var Msg: TMessage);
var o: TULObj;
begin
  o := TULObj(Msg.lParam);
  if o = AO then begin
    case Msg.wParam of
      cmULObjAfterEdit: begin
        {DoAfterSettingsEdit;}
      end;
    end;
  end{v0.38} else begin
    if (FDefPrg <> nil) and (o = FDefPrg.Obj) then begin
      case Msg.WParam of
        cmULObjFileNameChanged: DefPrgNameChanged;
      end;
    end;
    if (FCurPrg <> nil) and (o = FCurPrg.Obj) then begin
      case Msg.WParam of
        cmULObjUpdated: begin
          if FCurPrg.Active then begin
          end;
        end;
      end;
    end;
  end{/v0.38};
end;

function TAAA.GetDataRootDir: string;
begin
  { directory where .AAA config file located, used as default root
    directory for all other files or subdirectories } {ulsrtype ulatype}
  Result := AO.RootFileDir;
end;

procedure TAAA.AcqDebugStart;
begin
  if not CheckCurSample then begin
    if not Sequence.First then
      exit;
    DoCurSampleSelected;
    {v0.38}CurPrg{/v0.38 FCurPrg} := {v0.49}TAAPrg{/v0.49}(CurSample.Prg);
  end;
  if AcqStart then
    FAcqDebugStartTime := mstime;
end;

procedure TAAA.AcqDebugStop;
begin
  FAcqDebugStartTime := 0;
  AcqStop;
end;

{v0.25}
procedure TAAA.AutoSaveAll;
begin
  {v0.76}
  DebLog('AutoSaveAll begin');
  {/v0.76}


  {v0.58 can be nil if exception appeared during load}
  if Sequence <> nil then
  {/v0.58}
  begin
    {v0.76}
    DebLog('Sequence.Save');
    {/v0.76}
    Sequence.Save;
  end;
  {v0.58}
  if DefPrg <> nil then
  {/v0.59}
  begin
    {v0.76}
    DebLog('DefPrg.Save');
    {/v0.76}
    DefPrg.Save;
  end;
  {v0.58}
  if DefMethod <> nil then
  {/v0.58}
  begin
    {v0.76}
    DebLog('DefMethod.Save');
    {/v0.76}
    DefMethod.Save;{ulobjusru}
  end;
  {v0.76}
  DebLog('AutoSaveAll end');
  {/v0.76}

end;
{/v0.25}

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

{v0.25}
procedure TAAA.AcqDataUpdateViewLimit;
begin
  {v0.26}
  if (CurSample <> nil) and (FAcqData <> nil) then
    FAcqData.ULVL.MaxX := CurSample.Prg.TotalSecTime / 60;
  {/v0.26}
end;
{/v0.25}

function TAAA.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(AAATemplateFileName, omCreate);
    pn := ChangeFileExt(ExtractFileName(CurSample.ULSR.PrgFileName),'');
    pn := Sequence.DirName + pn + '_Equilibration.ULF';
    FAcqData.ChangeFileName(pn);
    {v0.25}
    AcqDataUpdateViewLimit;
    {/v0.25}

    FAcqInfo := TAcqInfo.Create(FAcqData);
    try
      SpectrumFormOpenForAcqData(FAcqData, FAcqInfo);
      FAcqInfo.Start;
      Log('AcqEquil Started ' + pn);
    except
      FAcqInfo.Free;
      FAcqInfo := nil;
      FAcqData.Free;
      FAcqData := nil;
    end;
    Result := true;
  end;
end;

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

function TAAA.AcqStart: boolean;
{v0.46}
var sf: TSpectrumForm;
{/0.46}
begin
  {acqinfou}
  {v0.46}
  sf := nil;
  {/v0.46}
  Result := false;
  if FAcqInfo <> nil then begin
    Log('AcqStart - acquisition is already running');
    exit;
  end;

  if CheckCurSample then begin {sequenceu}
    FAcqData := TAcqData.Create(AAATemplateFileName, omCreate);
    {v0.46}
    try
    {/v0.46}
      FAcqData.ChangeFileName(Sequence.DirName + CurSample.ULSR.FileName);
      {v0.25}
      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;
        {ulsrtype ulitype ulmtype aapgtype}
      {FAcqData.ULI.InstrumentTemplate := CurSample.ULSR.}
      FAcqData.AAPGAssign(FCurPrg.AAPG);

      AcqDataUpdateViewLimit;

      {FAcqData.ULI.Assign(FDefInstrument.ULI);}
      {/v0.25}

      FAcqInfo := TAcqInfo.Create(FAcqData);
      {specform}
      sf := SpectrumFormOpenForAcqData(FAcqData, FAcqInfo);

      FAcqInfo.Start;
      Result := true;
    {v0.46}
    except
      sf.Free;
      FAcqData.Free;
      FAcqData := nil;
      FAcqInfo.Free;
      FAcqInfo := nil;
      raise;
    end;
    {/v0.46}
  end;
end;

procedure TAAA.AcqStop; { ulsrtype }
var
  ai: TAcqInfo;
  ad: TAcqData;
begin
  ai := FAcqInfo;
  FAcqInfo := nil;{disables timer calls}
  ad := FAcqData;
  FAcqData := nil;

  if ai <> nil then begin
    {v0.28}
    if ad <> nil then
      ad.ULA.AutodetectLater := true;
    {/v0.28}
    ai.Stop;
    ai.Free;
    Log('AcqStopped');
  end;

  if ad <> nil then begin {acqinfou}
    ad.Free;
  end;

  {v0.25}
  AutoSaveAll;
  {/v0.25}
end;

function TAAA.GetCurSample: TSeqSample;
begin
  Result := Sequence.CurSample;
end;

{v0.25}
function TAAA.GetCurPrgName: string;{ for status wnd }
var s: TSeqSample;
begin
  Result := '';
  s := CurSample;
  if s <> nil then begin
    {v0.36}
    Result := ChangeFileExt(ExtractFileName(CurSample.ULSR.PrgFileName), '');
    {Result := CurSample.ULSR.FindField('PrgFileName').AsUsrString;}
    {/v0.36
    Result := ChangeFileExt(CurSample.ULSR.PrgFileName, '');}
  end;
end;

function TAAA.GetCurSampleName: string;{ for status wnd }
var
  s: TSeqSample;
begin
  Result := '';
  s := CurSample;
  if s <> nil then begin
    {v0.36}
    Result := ChangeFileExt(ExtractFileName(CurSample.ULSR.FileName), '');
    {Result := CurSample.ULSR.FindField('FileName').AsUsrString;}
    {/v0.36
    Result := CurSample.ULSR.FileName;}
  end;
end;

function TAAA.GetCurSampleStateName: string; { ulsrtype }
var
  s: TSeqSample;
  n: string;
  i: integer;
begin
  Result := '';
  s := CurSample;
  if s <> nil then begin
    n := CurSample.Obj.FindField('SampleState').AsUsrString;
    i := pos('sas',n);
    if i = 1 then
      n := copy(n, 4, length(n));
    Result := n;
  end;
end;

procedure TAAA.CurSampleStateChanged;
begin
  AO.DoChange;
end;

{/v0.25}



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

procedure TAAA.CheckDefTemplate;
var
  d: TAcqData;
  ad: TULADObj;
  cnt, i: integer;
  o: TULObj;
  {v0.26}
  ul: TUserViewLimit;
  {/v0.26}{ulsrtype ulstringgrid}
begin
  if FAAATemplateFileName = '' then begin
    FAAATemplateFileName := AO.SeqsDir + 'AAATemplate' + ULTExt;
    d := TAcqData.Create(FAAATemplateFileName, omCreateTemplate);
    try
      d.ULI.Assign(DefInstrument.ULI);{ChannelName := pvUlanDefaultChannel; {channelsu}
      cnt := 0;
      {v0.34}
      i := 0;
      while i < d.ULA.ChildCount do
      {/v0.34
      for i := 0 to d.ULA.ChildCount - 1 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;
            {v0.34}
            TULADObj(o).DataName := 'B';
            {/v0.34}
          end {v0.34} else begin
            o.Free;
            dec(i);
          end {/v0.34};
        end;
      end;

      {v0.34}
      if cnt = 0 then begin
        {ad := TULADObj(}d.ULA.Add(ULADID){)};{uladtype}
        inc(cnt);
      end;
      {/v0.34}

      if cnt = 1 then begin
        ad := TULADObj(d.ULA.Add(ULADID));{uladtype}
        {v0.25}
        ad.DataName := 'B';
        {/v0.25}
        ad.Color := clBlue;
      end;
      {v0.26}
      {v0.34}
      d.ULAD.Update;
      {/v0.34}
      ul := NewUserViewLimit;
      d.ULVL.MinX := ul.Min.X;
      d.ULVL.MinY := ul.Min.Y;
      d.ULVL.MaxX := 120;{min}
      d.ULVL.MaxY := ul.Max.Y;
      {/v0.26}
    finally
      {v0.76}
      DebLog('d.ULF.SaveToFile');
      {/v0.76}
      d.ULF.SaveToFile('');
      {v0.76}
      DebLog('d.ULF.SaveToFile(' + ChangeFileExt(d.ULF.FileName, ASCExt) + ')');
      {/v0.76}
      d.ULF.SaveToFile(ChangeFileExt(d.ULF.FileName, ASCExt));
      d.Free;
    end;
  end;
end;

{v0.25}
procedure TAAA.DoCurSampleSelected;
begin
  CurSample.Prg.LoadFromFile(AbsoluteFileName(AO.PrgsDir, CurSample.ULSR.PrgFileName
    {v0.36}, AAPGExt{/v0.36}));
  {v0.75}
  try
  {/v0.75}
  CurSample.Method.LoadFromFile(AbsoluteFileName(AO.MethodsDir, CurSample.ULSR.MethodFileName
    {v0.36}, ULMExt{/v0.36}));
  {v0.75}
  except
  end;
  {/v0.75}  
end;
{/v0.25}

{v0.38}
procedure TAAA.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); {ulrectyp}
    end else begin
      FBrowsingCurPrg := nil;{error?}
    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 TAAA.SetDefPrg(APrg: TAAPrg);{not public method accessible through property!!}
begin
  if FDefPrg <> nil then begin
    FDefPrg.Obj.UserUnregister(Self);
    if APrg = nil then
      FDefPrg.Free;
  end;
  FDefPrg := APrg;
  if FDefPrg <> nil then begin
    FDefPrg.Obj.UserRegister(Self);
  end;
end;

{v0.38}
procedure TAAA.SetLogActive(OnOff: boolean);
begin
  if FLogActive {v0.46}={/v0.46 <>} OnOff then
    exit;
  if FLogActive then begin
    Log('AAA Destroyed.');{ulantype}
  end;
  FLogActive := OnOff;
  if FLogActive then begin
    Log('AAA Created. Chromulan version ' + uLanVersion);{ulantype}
  end;
end;
{/v0.38}

procedure TAAA.CoolerEnable(OnOff: boolean);
var
  up:TULDPObj;
  nv: string;
begin
  if GetULDP('IK_COOLING_EN', up) then begin
    nv := IntToStr(byte(OnOff));
    if nv <> up.ValueInPC then begin
      Log('IK.COOLING_EN setting to ' + nv);
      up.ValueInPC := nv;
    end;
  end;
end;

function TAAA.GetUPSStateStr: string;
begin
  if UPSUnit.IsPresent then begin
    Result := 'UPS: (next check:' + UPSUnit.NextCheckTimeStr + ')';
    if UPSUnit.IsPowerDown then begin
      Result := Result + ' Power Down :' + UPSUnit.PowerDownPeriodStr;
    end else begin
      Result := Result + ' Power Up   :'
    end;
  end else begin
    Result := '';
  end;
end;

//procedure TAAA.SetUPSStateStr(Value: string);
//begin
//end;
{/TAAA.}
{v0.50}
const
  AAADeviceCount = 5;

type
  TAAADeviceName = string[4];
  TIDNameSub = string[8];
  TAAADevice = record
    IDSub: TIDNameSub;
    Name: TAAADeviceName;
    Addr: integer;
    DeviceType: TDeviceType;
  end;
  PAAADevice = ^TAAADevice;

const
  AAADeviceTable: array[0..AAADeviceCount - 1] of TAAADevice = (
    (IDSub:'AAA_IKB'; Name:'IK'; Addr: 3; DeviceType: dtDetector),
    (IDSub:'LCP'; Name:'P1'; Addr: 4; DeviceType: dtPump),
    (IDSub:'LCP'; Name:'P2'; Addr: 5; DeviceType: dtPump),
    (IDSub:'DET'; Name:'DET'; Addr: 6; DeviceType: dtDetector),
    (IDSub:'POD'; Name:'AS'; Addr: 7; DeviceType: dtAutoSampler)
  );


procedure CheckAAAChannel;
var
  n: TULNObj;
  nd: TULNDObj;
  j: integer;
begin
  if not Channels.ULL.HasChildWithFieldUsrValue(fnChannelName, pvAAAChannel, TULObj(n)) then
  begin
    n := TULNObj(Channels.ULL.Add(ULNID));
    n.ChannelName := pvAAAChannel;
    for j := 0 to AAADeviceCount - 1 do begin
      nd := TULNDObj(n.Add(ULNDID));
      nd.DeviceName := AAADeviceTable[j].Name;
    end;
  end;
  Channels.UpdateFromULL;
end;

function CheckAAADevices(Setup: boolean; var namesOK: boolean): boolean;

  function IsAAADev(m: TModule; var ai: integer): boolean;
  var
    i: integer;
  begin
    Result := false;
    for i := 0 to AAADeviceCount - 1 do begin
      with AAADeviceTable[i] do begin
        {$IFDEF USEDLL}
        if (pos(IDSub, ModuleGetPropStr(m, mpIDName)) <> 0) and (Addr = ModuleGetPropInt(m, mpAddrStr)) then begin
          ai := i;
          Result := true;
          if Setup then begin
            ModuleSetPropStr(m, mpDeviceName, Name);
            ModuleSetPropInt(m, mpDeviceType, ord(DeviceType));
          end;
          if ModuleGetPropStr(m, mpDeviceName) <> Name then
            namesOK := false;
          exit;
        end;
        {$ELSE}
        if (pos(IDSub, m.IDName) <> 0) and (Addr = m.Addr) then begin
          ai := i;
          Result := true;
          if Setup then begin
            m.DeviceName := Name;
            m.DeviceType := DeviceType;
          end;
          if m.DeviceName <> Name then
            namesOK := false;
          exit;
        end;
        {$ENDIF}
      end;
    end;
  end;

var
  m: TModule;
  fnd: array[0..AAADeviceCount-1] of boolean;
  fndcnt: integer;
  ai, i: integer;
{  aname: string;}

begin
  namesOK := true;
  FillChar(fnd, sizeof(fnd), 0);
  fndcnt := 0;
  {$IFDEF USEDLL}
  for i := 0 to ModulesCount(Modules) - 1 do begin
    m := ModulesModule(Modules, i);
    if IsAAADev(m, ai) then begin
      inc(fndcnt);
      fnd[ai] := true;
    end;
  end;
  {$ELSE}
  for i := 0 to Modules.Count - 1 do begin
    m := Modules[i];
    if IsAAADev(m, ai) then begin
      inc(fndcnt);
      fnd[ai] := true;
    end;
  end;
  {$ENDIF}
  Result := (fndcnt = AAADeviceCount);
  if Result and Setup then
    CheckAAAChannel;
end;
{v0.75}
procedure TAAA.EmergencyAbort;
begin
  FEmergencyAborting := true;
  UsrSetAAAState(aasAborting);
end;
{/v0.75}
{v0.76}
procedure TAAA.DebLog(const msg: string);
begin
  if not DebugLogging then
    exit;
  Log('AAA.DEB: ' + msg);
end;
{/v0.76}

{/TAAA.}

{TTechHistory}
constructor TTechHistory.Create(ASlotCount: integer);
begin
  inherited Create(sizeof(TTechValue), ASlotCount);
end;

function TTechHistory.CheckDeviation(AMinValue: TTechValue; AMaxDev: TTechValue; var AState: integer):boolean;
var
  v: TTechValue;
begin
  v := abs(Avg);
  if v < AMinValue then
    v := AMinValue;

  FDeviationOK := not ( (((abs(Avg - Min)*100/v) > AMaxDev) or
                  (((abs(Avg - Max)*100/v) > AMaxDev) )  ));
  if (not FDeviationOK) and (FChangedCounter = 0) then
    AState := dstUnstable;
  Result := FDeviationOK;
end;

function TTechHistory.CheckHardLimits(AMinValue, AMaxValue: TTechValue; var AState: integer):boolean;
begin
  FHardLimitsOK := true;
  if (Avg < AMinValue) then begin
    FHardLimitsOK := false;
    AState := dstTooLow;
  end;
  if (Avg > AMaxValue) then begin
    FHardLimitsOK := false;
    AState := dstTooHigh;
  end;
  Result := FHardLimitsOK;
end;

function TTechHistory.CheckLimits(AMinValue, AMaxValue: TTechValue; var AState: integer):boolean;
begin
  FLimitsOK := ((Avg >= AMinValue) and (Avg <= AMaxValue));
  if (not FLimitsOK) and (FChangedCounter = 0) then
    AState := dstOutOfLimits;
  Result := FLimitsOK;
end;

function TTechHistory.IsOK(var OKChanged:boolean): boolean;
begin
  Result := true;
  OKChanged := false;

  if not FDeviationOK then begin
    Result := false;
  end;
  if FDeviationOK <> FLastDeviationOK then begin
    OKChanged := true;
    FLastDeviationOK := FDeviationOK;
  end;

  if not FLimitsOK then begin
    Result := false;
  end;

  if FLimitsOK <> FLastLimitsOK then begin
    OKChanged := true;
    FLastLimitsOK := FLimitsOK;
  end;
end;

{function TTechHistory.CheckOKChanged: boolean;
begin
  Result := true;
  if FDeviationOK <> FLastDeviationOK then begin
    if not FDeviationOK then begin
      Result := false;
    end;
    FLastDeviationOK := FDeviationOK;
  end;
  if FLimitsOK <> FLastLimitsOK then begin
    if not FLimitsOK then
      Result := false;
    FLastLimitsOK := FLimitsOK;
  end;
end;
}
procedure TTechHistory.SetOK;
begin
  FDeviationOK := true;
  FLimitsOK := true;
end;

procedure TTechHistory.SetChangedCounter(AValue: integer);
begin
  FChangedCounter := AValue;
  SetOK;
end;

function TTechHistory.PutVal(const ATechValue: TTechValue): boolean;
var
  i: integer;
  v: TTechValue;
begin
  if FChangedCounter > 0 then
    dec(FChangedCounter);
  Result := Put(ATechValue);
  if Result then begin
    FMaxVal := MinSingle;
    FMinVal := MaxSingle;
    FAvgVal := 0;
    for i := 0 to RecCount - 1 do begin
      GetRec(i, v);
      if v > FMaxVal then
        FMaxVal := v;
      if v < FMinVal then
        FMinVal := v;
      FAvgVal := FAvgVal + v;
    end;
    FAvgVal := FAvgVal / RecCount;
  end;
end;

{function TTechHistory.GetVal(var ATechValue: TTechValue): boolean;
begin
  Result := Get(ATechValue);
end;}

procedure TTechHistory.Clear;
begin
  inherited Clear;
  FMinVal := 0;
  FMaxVal := 0;
  FAvgVal := 0;
  FChangedCounter := 0;
  FDeviationOK := false;
  FLastDeviationOK:= false;
  FLimitsOK:= false;
  FLastLimitsOK:= false;
end;

{function TTechHistory.GetStatusString;
begin

end;}

{/TTechHistory}

{TPropStatus}
{  TPropState = (psOK, psSuspend, psAlarm, psFatal);}
{
constructor TPropStatus.Create(AHistorySlotCount, AErrorAlarmCount, AErrorFatalCount: integer);
begin
  inherited Create;
  FErrorCount := 0;
  FErrorAlarmCount := AErrorAlarmCount;
  FErrorFatalCount := AErrorFatalCount;
  FHistory:= TTechHistory.Create(AHistorySlotCount);
end;

destructor TPropStatus.Destroy;
begin
  FHistory.Free;
  inherited Destroy;
end;

procedure TPropStatus.Update(AValue: TTechValue);
begin
  FHistory.Put(AValue);
end;

procedure TPropStatus.UpdateLimits(AMinValue, AMaxValue, ADeviation: TTechValue);
begin
  FMin := AMinValue;
  FMax := AMaxValue;
  FDev := ADeviation;
end;
}
{/TPropStatus}

procedure AAAFree;
begin
  FAAA.Free;
  FAAA := nil;
end;

initialization
  RegisterClass(TAAA);
finalization
end.
