unit AcqInfou;{v0.14}
{
  (C) 2000 - 2001 Jindrich Jindrich, Pavel Pisa, PiKRON Ltd.

  Originators of the CHROMuLAN project:

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

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

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

interface
uses
  {v0.15}Windows,{/v0.15 ulantype}
  SysUtils, Classes, Forms,
  MyType, {v0.39}UtlType{/v0.39 Conftype}, ListType, Listu,
  UlanType, ApexType,
  ul_lcabs, timer,
  CommInt, ComPort,
  Spectrum, Channelsu, DevMode, ExtDevIntu, UlanGlob
  {v0.19},TVType, FileMenuHdl, Msgu, ShowMsg, PortLog, LogType, Logu{/v0.19}
  {v0.24},ExeLogu {/v0.24}{v0.28}, Language{/v0.28}
  {v0.53}, Simulationu{v0.53};

type
  EAcqInfo = class(Exception);

  TAcqInfo = class(TObject)
    {v0.19}{/v0.19 RunningState: TRunningState;}
    CommPort: TComm;
    RcvBuf: array[0..RcvBufSize - 1] of char;
    SII: PScanInputInfo;
    PacketFifo: TLst;
    ApexPktInfo: TApexPacketInfo;
    ExtDev: TExtDev;
    {ExtDevDrv: PExtDevDrv;}
    ZeroValue: single;  //currently set zero value (in absolute units)
                       //of the y value, used to  calculate apexdatarec.y value from
                      // apexpacket values
    ZeroTime: Longint;
    {v0.31}{/v0.31
    CurUlanTime: single; }//gets incremented for every received experimental
                         // point by ulanglob . UlanPointTimeInterval (= 40 ms)
    LogPackets: boolean;
    IsInReceive: boolean;
    {v0.19 moved to private}{/v0.19
    FNextAcqTime: single;
    FSampleInt: single;}
  private
    {v0.19}
    FIgnoredCharCount: longint;
    FRecentPoints: TStream;
    FLastSuspendTime: single;
      { at what time (s) was the acquisition suspended, used upon resume to
        recalculate FAllSuspendInterval }
    FAllSuspendInterval: single;
      { sum of all intervals for which the acquisition was suspended
        (in seconds), just informative }
    FAllAcqTime: single;
    FLastResumeTime: single;
      { sum of all acqusition times (useful if suspend was called one or
        more times), in (s) }
    FSuspendCount: longint;
      { how many times was the acquisition suspended; (if > 0, then
        FAllSuspendInterval is used adjust the point's device time) }
    {/v0.19}
    FNextAcqTime: single;
    FSampleInt: single; { in ms }
    FRunningState: TRunningState;
    FData: TAcqData;
    FForm: TForm;
      { that should be informed to Run, when eiMark received
        (only TSpectrumForm accepted now) }
    {v0.24}
    FOwnerForm: TForm;
      { if non nil then this form will destroy the AcqInfo upon its destroy }
    {v0.24}
    FStopTime: longint;
      { if <> 0 then at this ms time the acquisiton will auto stop,
        calculated from AcqData.ULI.Duration at Start }
    {v0.21}
    FDataWaitTimeout: longint;
      { for how long can not come any date from port in running state
        (if not comming for longer time - error) (assigned ULI.DataWaitTimeout * 1000)}
    FNoDataInLimit: longint;
      { if mstime becomes > FNoDataInLimit, error will be reported, acq.suspended }
    FLastDataTime: longint;
      { mstime value at the moment when last data point was added }
    FSuspendedForNoData: boolean;
      { Suspend method was called before data stopped to come from device
        (i.e. some data already came but suddenly stopped comming) }
    {v0.53}
    FNoDataDetected: boolean;
      { set true if in FDataWaitTimeout interval did not come any data;
        cleared in DeviceStart method, called from Start, Resume }
    FGotDataInTimer: boolean;
      { set true in modetimers if some data came from device; used
        to decide if simulation data should be created (if SimulatingData
        is on and FNoDataDetected is true) }
    FTimerInterval: integer;
      { how ofthe is DoTimer called; should be set by the object
        that calls DoTimer }
    {/v0.53}
    FIsInTimer: boolean;
    FLastDataX: TXValue;
      { last added data point X value }
    {/v0.21}
    {v0.41}
    {FCurTime: single;{last data acqtime}
    FOnMark: TNotifyEvent;
      { if assigned, then Run won't be called (if RunningState = rsReadyToRun),
        it is responsibility of the FOnMark user }
    {/v0.41}
    {v0.31}
    procedure DoStartResume;
    {/v0.31}
    {v0.53}
    procedure DoMark(const AI: TApexPacketInfo);{called when mark packet received, decides accorging
      to source address if should react on it, if yes, calls Run or
      FOnMark }
    {/v0.53}
  protected
    function GetDeviceMode: TDeviceMode;
    function GetPortName: shortstring;
    function GetUVDetAddr: integer;
    function GetChannel: TChannel;
    function GetExtDevDrvName: TExtDevDrvName;
    procedure PortSetDefaults;

    procedure ComPortReceive(Sender: TObject; Count: Integer);
      procedure DoInChar(CommChar: char);
        procedure DoApexChar(CommChar: char);
        procedure DoExtDevInChar(CommChar: char);
    {v0.19}
    function GetFMH: TFileMenuHandler;
    procedure DoApexTimer;
    procedure DoUlanTimer;
    procedure DoExtDevTimer;
    procedure DoApexPacket(var ai:TApexPacketInfo);
    {v0.24}{/v0.24
    procedure PointsAddStart;
    procedure PointsAddAdd(ADataIndex: integer; dr: TExpPoint);
    procedure PointsAddStop;}
    {/v0.19}
    {v0.21}
    procedure DeviceStart;
      { (re)starts the device (and set its ZeroTime to mstime) }
    procedure DeviceStop;
      { stop device's time }
    {/v0.21}
    {v0.50 moved from public}
    function PortOpen: boolean;
    procedure PortClose;
    {/v0.50}

  public
    constructor Create(AData: TAcqData);reintroduce;
    destructor Destroy;override;
    {v0.24}
    procedure PointsAddStart;
    procedure PointsAddAdd(ADataIndex: integer; dr: TExpPoint);
    procedure PointsAddStop;
    {/v0.24}
    {v0.50}
    {procedure PortSuspend;
    procedure PortResume;}
    {/0.50
    function PortOpen: boolean;
    procedure PortClose;
    }
    procedure ResetPacketInfo;
    {v0.41}
    function IsPortOpen: boolean;
    {/v0.41}
    {v0.15}
    function Start: boolean;
      {v0.41 start changed to function, returns false if could not open port }{/v0.41}
    {v0.19}{/v0.19 procedure RequestStop;}
    {/v0.15}
    {v0.19}
    procedure DoRunStop;
      procedure Run;
      procedure Stop;
    {/v0.19}

    {v0.17}
    procedure CheckStop;
      { called from Timer, finds out if it is time to stop, if yes
        calls requestStop }
    {/v0.17}
    {v0.21}
    procedure CheckNoData;
      { called from Timer if running, finds out if no data came from the port
        for longer time then DataWaitTimeout (ms), if so, suspends
        the acquisition and shows warning to the user }
    {/v0.21}
    {v0.19}
    procedure DoTimer;
      { called from TheSpecForm window timer }
      {0.v21}
      procedure DoModeTimers;
        { called from DoTimer and from Suspend (reads buffers) }
      {/v0.21}
      {v0.53}
      procedure DoSimulationTimer;
      {/v0.53}
    procedure DoSuspendResume;
      { called when menuitem Suspen/Resume clicked, calls one of the following }
      procedure Suspend;
      procedure Resume;
    function CanClose: boolean;
      { can destroy the object, stop/abort acquisition if running?  }
    {v0.21}
    procedure NotifyForms(cm:integer);
      { send WM_APPMessage to FForm }
    {/v0.21}

    property Data: TAcqData read FData;
    property FMH: TFileMenuHandler read GetFMH;
    property RunningState: TRunningState read FRunningState;
    property RecentPoints: TStream read FRecentPoints;
    {/v0.19}
    property DeviceMode: TDeviceMode read GetDeviceMode;
    property PortName: shortstring read GetPortName;
    property UVDetAddr: integer read GetUVDetAddr;
    property Channel: TChannel read GetChannel;
    property TheSpecForm: TForm read FForm write FForm;
    property ExtDevDrvName: TExtDevDrvName read GetExtDevDrvName;
    {v0.24}
    property OwnerForm: TForm read FOwnerForm write FOwnerForm;
    {/v0.24}
    {v0.41}
    property SuspendedForNoData: boolean read FSuspendedForNoData;
    property CurTime: single read FLastDataX;
    property OnMark: TNotifyEvent read FOnMark write FOnMark;
    {/v0.41}
    {v0.53}
    property TimerInterval: integer read FTimerInterval write FTimerInterval;
    {/v0.53}
  end;

implementation
uses Main, SpecForm{v0.50}, Processoru{/v0.50}{v0.53}, Modulu{/v0.53};

constructor TAcqInfo.Create(AData: TAcqData);
var
  dm: TDeviceMode;
  li: TListInfo;
begin
  inherited Create;
  FData := AData;
  ExtDev := nil;
  ZeroTime := mstime;
  ZeroValue := 0;
  FRunningState := rsStopped;
  CommPort := TComm.Create(nil);
  {v0.19}
  {v0.21}
  FDataWaitTimeout := round(FData.ULI.DataWaitTimeout * 1000);
  if FDataWaitTimeout = 0 then
    FDataWaitTimeout := DefDataWaitTimeout * 1000;
  CommPort.DeviceName := PortName;
  {/v0.21}
  PortSetDefaults;
  {/v0.19}
  {v0.43}{/v0.43
  MainForm.ComPort := CommPort;}
  dm := DeviceMode;
  {v0.15}
  FNextAcqTime := 0;
  FSampleInt := FData.ULI.SamplingInterval * 1000;
  {/v0.15}
  {v0.19}
  FRecentPoints := TMemoryStream.Create;
  {/v0.19}
  DeviceModeCheck(dm);
  if dm <> DeviceMode then begin
    raise EInvalidDeviceMode.Create('Specified device mode not supported.');
  end;
  {v0.15}
  if dm = dmUlan then begin
    if FSampleInt > 0 then begin
      FSampleInt := ( round((FSampleInt - 1) / UlanPointTimeInterval) + 1) * UlanPointTimeInterval;
    end;
  end;
  {/v0.15}
  ResetPacketInfo;
  {v0.31 now in ULADRc}{/v0.31
  CurUlanTime := 0;}
  FillChar(li, sizeof(li), 0);
  case dm of
    dmUlan, dmPasive: begin
      li.Capacity := 100;
      li.RecordSize := sizeof(TApexPacketInfo);
      if not ListInit(ltRecords or ltAutoDestroy, @li, PacketFifo) then
        raise EAcqInfo.Create('AcqInfo ListInit PacketFifo failed.');
    end;
    dmExtDev: begin
      ExtDev := ExtDevs.FindDev(ExtDevDrvName);
      if (ExtDev = nil) or  (ExtDev.Init('') <> 0) then begin
        raise EAcqInfo.Create('AcqInfo ExtDevInit failed.');
      end;
    end;
    {v0.51pi}
    dmUnspecified: begin
       {!!!!!!!!!!}
    end;
    {/v0.51pi}
  else
    raise EInvalidDeviceMode.Create('Unknown DeviceMode');
  end;
end;

destructor TAcqInfo.Destroy;
begin
  if TheSpecForm <> nil then begin
    if OwnerForm <> TheSpecForm then begin
      NotifyForms(cmAcqInfoDestroyed);{TheSpecForm}
    end;
  end;
  {v0.43}{/v0.43
  if MainForm.ComPort = CommPort then
    MainForm.ComPort := nil;}
  CommPort.Free;
  {v0.19}
  FRecentPoints.Free;
  {/v0.19}
  ListDone(PacketFifo);
  inherited;
end;

procedure TAcqInfo.ResetPacketInfo;
begin
  FillChar(ApexPktInfo, sizeof(ApexPktInfo), 0);
end;

function TAcqInfo.GetChannel: TChannel;
begin
  Result := Channels.FindChannel(FData.ULI.ChannelName);
end;

function TAcqInfo.GetDeviceMode: TDeviceMode;
begin
  Result := Channel.DeviceMode;
end;

function TAcqInfo.GetPortName: shortstring;
begin
  Result := Channel.PortName;
end;

function TAcqInfo.GetUVDetAddr: integer;
begin
  Result := Channel.DetectorAddress;
end;

function TAcqInfo.GetExtDevDrvName: TExtDevDrvName;
begin
  Result := Channel.ExtDevDrvName;
end;

procedure TAcqInfo.PortSetDefaults;
var
  op:TCommOptions;
begin
  {$IFDEF WIN32}
  with CommPort do begin
    AfterOpenState := aoSpecified;
    BaudRate := br9600;
    DataBits := da8;
    DeviceName := 'COM2';
    FlowControl := {v0.21}fcNone;{/v0.21 fcDefault;}
    MonitorEvents := [evRxChar];
    Options := [];
    Parity := paNone; {paSpace;}
    ReadBufSize := 4096;
    ReadTimeout := 1000;
    StopBits := sb10;
    WriteBufSize := 2048;
    WriteTimeout := 1000;
    AfterOpenState := aoSpecified;
    op := [];
    Options := op;
    DTROnOpen := TCommEscapeState(1);
    RTSOnOpen := TCommEscapeState(0);
    XOnOnOpen := TCommEscapeState(2);
    BreakOnOpen := TCommEscapeState(2);
   end;
  {$ELSE}
  {comm}
  with CommPort do begin
    BaudRate := tbr9600;
    DataBits := tdbEight;
    Parity := tpSpace;
    ReadBufferSize := 2048;
    Stopbits := tsbOne;
    TxLowCount := 1024;
    WriteBufferSize := 1024;
    Events := [tceRxChar];
  end;
  {$ENDIF}
end;

{v0.17}
procedure TAcqInfo.CheckStop;
begin
  if FStopTime <> 0 then begin
    if mstime >= FStopTime then begin
      {v0.19}
      Stop;
      {/v0.18
      RequestStop;}
    end;
  end;
end;
{/v0.17}

{v0.21}
procedure TAcqInfo.CheckNoData;
begin
  {v0.42}
{  if not ShouldCheckNoData then
    exit;}
  {/v0.42}
  if mstime > FNoDataInLimit then begin
    {v0.53}
    FNoDataDetected := true;
    if SimulatingData then
      exit;
    {/v0.53}
    if Data.{v0.24}DataSize{/v0.24 ULAD.Data.Size} = 0 then begin
      Stop;
    end else begin
      FSuspendedForNoData := true;
      Suspend;
    end;
    ShowMessage(GetTxt({#}'No data from acquisition'), smError, 0);
  end;
end;
{/v0.21}

procedure TAcqInfo.ComPortReceive(Sender: TObject; Count: Integer);
var
  CommChar:Char;
  i:Word;
  b:word;
begin
  if {v0.19} RunningState in [rsStopped{v0.21}{/v0.21, rsSuspended}] {/v0.19 RunningState = rsStopped} then
    exit;
  {v0.15}
  {v0.17 moved to CheckStop method, called from TheSpecForm.TimerTimer }{/v0.17
  if FStopTime <> 0 then begin
    if mstime >= FStopTime then
      RequestStop;
  end;}
  {/v0.15}
  if IsInReceive then
    exit;
  if Count = 0 then
    exit;
  try
    IsInReceive := true;
    repeat
      if Count <= sizeof(RcvBuf) then begin
        b := Count;
      end else begin
        b := sizeof(RcvBuf);
      end;
      dec(Count, b);
      CommPort.Read(RcvBuf, b);
      {v0.19}
      PortLogAddRecord(StrToInt(copy(CommPort.DeviceName,4,1)) ,'g', b);
      {/v0.19}

      {v0.21}
      if RunningState <> rsSuspended then
      {/v0.21}
      for i := 0 to b - 1 do begin
        CommChar := RcvBuf[i];
        DoInChar(CommChar);
      end;
    until Count = 0;
  finally
    IsInReceive := false;
  end;
end;

procedure TAcqInfo.DoApexChar(CommChar:char);

  procedure HandlePacket(var ApexPktInfo:TApexPacketInfo);
  begin
    case ApexPktInfo.Pkt.Data[ApexEventIDPos] of
      eiMark: begin
        {v0.53}
        DoMark(ApexPktInfo);
        {/v0.53
        if Assigned(FOnMark) then begin
          FOnMark(Self);
        end else
        begin
          if RunningState =  rsReadyToRun then begin
            Run;
          end;
        end;
        }
      end;
      eiData: begin
        if RunningState = rsRunning then
          ListRecAdd(PacketFifo, ApexPktInfo);
      end;
    else
      begin
      end;
    end;
  end;

begin
  with ApexPktInfo do begin
    if (CurPos < 4) and (CommChar <> #0) then begin
      {v0.19}
      inc(FIgnoredCharCount);
      if FIgnoredCharCount = ApexPacketSize then begin
        {v0.24}
        ExeLog.LogEvent([leError], 'DoApexChar: Too many non packet bytes received -'
          + IntToStr(FIgnoredCharCount));
        {/v0.24 SysLogLog(leError, 'DoApexChar: Too many non packet bytes received -'
          + IntToStr(FIgnoredCharCount));}
        FIgnoredCharCount := 0;
      end;
      {/v0.19}
      CurPos := 0;
      EndTime := mstime;
    end else begin
      {v0.19}
      FIgnoredCharCount := 0;
      {/v0.19}
      Pkt.Data[CurPos] := ord(CommChar);
      if CurPos = 0 then
        Time := EndTime;
      inc(CurPos);
      if CurPos = ApexPacketSize then begin
        CurPos := 0;
        EndTime := mstime;
        if Pkt.Data[ApexEventIDPos] <> eiData then begin
          {v0.19}
          {v0.24}
          ExeLog.LogEvent([leError], 'DoApexChar: Invalid ID byte - ' +
            IntToStr(Pkt.Data[ApexEventIDPos]) + ' x ' + IntToStr(eiData));
          {/v0.24
          SysLogLog(leError, 'DoApexChar: Invalid ID byte - ' +
            IntToStr(Pkt.Data[ApexEventIDPos]) + ' x ' + IntToStr(eiData));}
          FIgnoredCharCount := 0;
          {/v0.19}
          CurPos := 0;
        end;
        HandlePacket(ApexPktInfo);
      end;
    end;
  end;
end;

procedure TAcqInfo.DoExtDevInChar(CommChar:char);
begin
  ExtDev.DoCharIn(CommChar);
end;

procedure TAcqInfo.DoInChar(CommChar:char);
begin
  case DeviceMode of
    dmPasive: DoApexChar(CommChar);
    dmExtDev: DoExtDevInChar(CommChar);
  end;
end;

function TAcqInfo.PortOpen: boolean;
var pn: shortstring;
begin
{  Result := false;}
  case DeviceMode of
    dmUlan: begin
      Result := RcvInit(SII, {v0.31} FData.ULDetAddrList{/v0.31 UVDetAddr});
    end;
  else
    CommPort.OnRxChar := ComPortReceive;
    CommPort.DeviceName := PortName;

    {v0.21}
    pn := CommPort.DeviceName;
    if ExtDev <> nil then
      ExtDev.DoAction(eaPortBeforeOpen, longint(@pn));{extdevintu}
    {/v0.21}
    ReadWriteComConfig(rwRead, CommPort);
    {v0.21}
    {/v0.21
    case DeviceMode of
      dmExtDev: begin
        CommPort.DTROnOpen := esOn;
        CommPort.RTSOnOpen := esOff;
      end;
    end;}
    {v0.43}{/v0.43
    if CommPort = MainForm.ComPort then
      MainForm.ComPort := nil;}
    CommPort.Open;
    Result := true;
  end;
end;

{v0.50}
{procedure TAcqInfo.PortSuspend;
begin
  if DeviceMode <> dmUlan then
    PortClose;
end;
procedure TAcqInfo.PortResume;
begin
  if DeviceMode <> dmUlan then
    PortOpen;
end;}
{/v0.50}

procedure TAcqInfo.PortClose;
begin
  case DeviceMode of
    dmUlan: begin
      RcvDone(SII);
    end;
  else
    //$IFDEF WIN32
    CommPort.Close;
    CommPort.OnRxChar := nil;
    ReadWriteComConfig(rwWrite, CommPort);
    {v0.43}{/v0.43
    if MainForm.ComPort = nil then
      MainForm.ComPort := CommPort;}
    //$ELSE
    //CommPort.Port := tptNone;
    //CommPort.OnReceive := nil;
    //CommPort := nil;
    //$ENDIF
  end;
end;

{v0.41}
function TAcqInfo.IsPortOpen: boolean;
begin
  case DeviceMode of
    dmUlan: Result := SII <> nil;
  else
    Result := CommPort.Enabled;
  end;
end;
{/v0.41}

{v0.31}
procedure TAcqInfo.DoStartResume;
begin
  if FData.ULI.Duration <> 0 then begin
    FStopTime := mstime + round(FData.ULI.Duration * 1000)
      - round(FAllAcqTime * 1000);
  end else begin
    FStopTime := 0;
  end;
  if FSampleInt <> 0 then begin
    FNextAcqTime := mstime + FSampleInt;
  end;
  FNoDataInLimit := mstime + FDataWaitTimeout;
  FRunningState := rsRunning;
end;
{/v0.31}

function TAcqInfo.Start: boolean;
begin
  {v0.46}
  Result := false;
  {v0.41}
  if not IsPortOpen then begin
    if not PortOpen then
      exit;
  end;
  {/v0.41}
  {v0.31}
  Data.Clear;
  DeviceStart;
  DoStartResume;
  {/v0.31
  if FData.ULI.Duration <> 0 then begin
    FStopTime := mstime + round(FData.ULI.Duration * 1000)
      - round(FAllAcqTime * 1000);
  end else begin
    FStopTime := 0;
  end;
  if FSampleInt <> 0 then begin
    FNextAcqTime := mstime + FSampleInt;
  end;
  FNoDataInLimit := mstime + FDataWaitTimeout;}

  {v0.24}
  {v0.31}{/v0.31 FRunningState := rsRunning;}
  NotifyForms(cmAcquisitionRun);
  {/v0.24}
  {v0.46}
  Result := true;
  {/v0.46}
  {v0.50}{channelsu processoru}
{  Processors.ProcessorGet(Channel.ChannelName).StartProgram(FData.Prg, Data);}
  {/v0.50}
end;

{v0.19}{/v0.19
procedure TAcqInfo.RequestStop;
begin
  if FForm <> nil then begin
    PostMessage(FForm.Handle, WM_APPMESSAGE, cmAcquisitionStop, 0);
  end else begin
  end;
end;
}

{procedure TAcqInfo.Stop;
begin
  RunningState := rsStopped;
  PortClose;
end;}


{v0.19}
procedure TAcqInfo.DoRunStop;
begin
  if RunningState <> rsStopped then begin
    {v0.50}
    if ShowMessage(GetTxt({#}'Really stop the aquisition?'), smYesNo, 0) = cmYes then
    {/v0.50}
    Stop;
  end else begin
    {v0.51pi}
    if DeviceMode<>dmUnspecified then
    {/v0.51p}
      Run;
  end;
end;

{v0.21}
procedure TAcqInfo.DeviceStart;
begin
  ResetPacketInfo;
  ZeroTime := mstime;
  {v0.31}
  Data.ULAD.Start;
  {/v0.31
  CurUlanTime := 0;}
  {v0.53}
  FNoDataDetected := false;
  {/v0.53}
  ApexPktInfo.EndTime := ZeroTime;
  case DeviceMode of
    dmUlan: RcvSetProp(SII, rpZeroTime, @ZeroTime);
    dmExtDev: ExtDev.Start;
  end;
end;

procedure TAcqInfo.DeviceStop;
begin
  case DeviceMode of
    dmExtDev: ExtDev.Stop;
  end;
end;
{/v0.21}

procedure TAcqInfo.Run;
var pn: shortstring;
begin
  if RunningState = rsStopped then begin
    pn := PortName;
    if SendMessage(MainForm.Handle, WM_APPMESSAGE, cmQueryRunning,
      longint(@pn)) = 1 then
    begin
      ShowMsg.ShowMessage(GetTxt({#}'Acquisition is already running'), smError,0);
      exit;
    end;
    if (Data <> nil) and (Data.{v0.24}DataSize{/v0.24 ULAD.Data.Size} <> 0) then begin
      if ShowMessage(GetTxt({#}'Discard already acquired data?'), smNoYes, 0) <> cmYes then
        exit;
      {v0.31}
      Data.Clear;
      {/v0.31}
      {v0.21}
      NotifyForms(cmAcquisitionAborted);
      {/v0.21}
    end;
    ResetPacketInfo;
    if not PortOpen then
      exit;
    FRunningState := rsReadyToRun;
    {v0.31}
    Data.Clear;
    {/v0.31}
    {v0.24}
    NotifyForms(cmAcquisitionRun);
    {/v0.24}
  end else begin
    { i.e. RunningState = rsReadytorun, Run don't get called in other states }
    {ClearData;}

    {v0.21}
    FLastSuspendTime := 0;
    FAllSuspendInterval := 0;
    FAllAcqTime := 0;
    FLastResumeTime := 0;
    FSuspendCount := 0;
    FNextAcqTime := 0;
    {v0.31 moved to start}{/v0.31
    DeviceStart;}
    {/v0.21
    ResetPacketInfo;
    ZeroTime := mstime;
    CurUlanTime := 0;
    ApexPktInfo.EndTime := ZeroTime;
    case DeviceMode of
      dmUlan: RcvSetProp(SII, rpZeroTime, @ZeroTime);
      dmExtDev: ExtDev.Start;
    end;}
    Start;
    {v0.24 moved to Start}{/v0.24 FRunningState := rsRunning;}
  end;
  {v0.21}
  {v0.24}{/v0.24
  NotifyForms(cmAcquisitionRun);}
  {/v0.21
  if FForm <> nil then begin
    SendMessage(FForm.Handle, WM_APPMESSAGE, cmAcquisitionRun, longint(Self));
  end;}
end;

{v0.21}
procedure TAcqInfo.NotifyForms(cm:integer);
begin
  if FForm <> nil then begin
    SendMessage(FForm.Handle, WM_APPMESSAGE, cm, Longint(Self));
  end;
end;
{/v0.21}

procedure TAcqInfo.Stop;
begin
  if RunningState <> rsStopped then begin
    FRunningState := rsStopped;
    try
      PortClose;
    except;
    end;
    {v0.21}
    DeviceStop;
    {/v0.21
    case DeviceMode of
      dmExtDev: ExtDev.Stop;
    end;}

    try
      {v0.28}{ulantype}
      Data.ULA.DateTime := DateTimeToFileDate(Now);
      {/v0.28}
      if Data.Spectrum <> nil then begin
        {v0.28}
        if not Data.ULA.AutodetectLater then
        {/v0.28}
          Data.Spectrum.Autodetect;
      end;
    except
      {v0.21}
      {FLastSuspendTime := 0;
      FAllSuspendInterval := 0;
      FAllAcqTime := 0;
      FLastResumeTime := 0;
      FSuspendCount := 0;
      FNextAcqTime := 0;}
      {/v0.21}
    end;
  end;
  {v0.21}
  NotifyForms(cmAcquisitionStop);
  {/v0.21
  if FForm <> nil then begin
    SendMessage(FForm.Handle, WM_APPMESSAGE, cmAcquisitionStop, longint(Self));
  end;}
end;

{v0.21}
procedure TAcqInfo.DoModeTimers;
begin
  {v0.53}
  FGotDataInTimer := false;
  {/v0.53}
  case DeviceMode of
    dmPasive: DoApexTimer;
    dmUlan: DoUlanTimer;
    dmExtDev: DoExtDevTimer;
  else
    raise EAcqInfo.Create('Invalid DeviceMode');
  end;
  {v0.53}
  if (not FGotDataInTimer) and  SimulatingData and
     FNoDataDetected and (FRunningState = rsRunning)
  then
    DoSimulationTimer;
  {/v0.53}
end;
{/v0.21}

procedure TAcqInfo.DoTimer;
  { called from window timer }
begin
  {v0.21}
  if FIsInTimer then
    exit;
  FIsInTimer := true;
  try
  {/v0.21}
  if RunningState = rsStopped then
  begin
    if DeviceMode = dmUlan then begin
      RcvRun(SII);
    end;
    exit;
  end else if RunningState = rsRunning then begin
    CheckStop;
    {v0.21}
    CheckNoData;
    {/v0.21}
  end;
  {v0.21}
  DoModeTimers;
  {/v0.21
  case DeviceMode of
    dmApex: DoApexTimer;
    dmUlan: DoUlanTimer;
    dmExtDev: DoExtDevTimer;
  else
    raise EAcqInfo.Create('Invalid DeviceMode');
  end;
  }

  {v0.21}
  finally
    FIsInTimer := false;
  end;
  {/v0.21}
end;

procedure TAcqInfo.DoApexTimer;
var
  ai:TApexPacketInfo;
begin
  if PacketFifo <> nil then begin
    while ListRecGet(PacketFifo, ai) do begin
      DoApexPacket(ai);
      {v0.53}
      FGotDataInTimer := true;
      {/v0.53}
    end;
  end;
end;

procedure TAcqInfo.DoUlanTimer;
var
  ai:TApexPacketInfo;
begin
  if PacketFifo = nil then
    exit;
  if SII = nil then
    exit;
  RcvRun(SII);
  while RcvGetPktInfo(SII, ai) do begin
    {v0.53}
    FGotDataInTimer := true;
    {/v0.53}
    case ai.Pkt.Data[ApexEventIDPos] of
      eiMark: begin
        {v0.51pi}
        {Check for dmUlan that pktinfo.Pkt.X2 is equal
         to address of some of devices grouped into channel}
        {/v0.51}
        {v0.53}
        DoMark(ai);
        {/v0.53
        if Assigned(FOnMark) then begin
          FOnMark(Self);
        end else
        begin
          if RunningState =  rsReadyToRun then
            Run;
        end;}
      end;
    else
      {v0.50}
      if RunningState <> rsSuspended then
      {/v0.50}
        DoApexPacket(ai);
    end;

  end;
end;

{v0.53}
procedure TAcqInfo.DoSimulationTimer;
var
  dr: TExpPoint;
  int: single;
  di: integer;
  uladrc: TULADRc;

  i, cnt: integer;
begin
  di := 0;
  uladrc := Data.ULAD[di];
  int := uladrc.PointTimeInterval;
  cnt := TimerInterval div round(int);
  if cnt = 0 then
    cnt := 1;
  PointsAddStart;
  for i := 0 to cnt - 1 do begin
    uladrc.CurTime := uladrc.CurTime + int;
    dr.X := uladrc.CurTime / 1000;
    dr.Y := GetDebugAbsorbance(0, dr.X) * uladrc.YCoef - ZeroValue;
    PointsAddAdd(0, dr);
  end;
  PointsAddStop;
end;

procedure TAcqInfo.DoMark(const AI: TApexPacketInfo);
var m: TModule;
begin
  {Check for dmUlan that pktinfo.Pkt.X2 is equal
         to address of some of devices grouped into channel}
  if DeviceMode = dmUlan then begin          {fieldbyname ulantype apextype}
    if not Channel.DeviceFindByAddr(AI.Pkt.X2, m) then  {channelsu}
      exit;
  end;
  if Assigned(FOnMark) then begin
    FOnMark(Self);
  end else begin
    if RunningState =  rsReadyToRun then
      Run;
  end;
end;
{/v0.53}

procedure TAcqInfo.DoSuspendResume;
      { called when menuitem Suspen/Resume clicked, calls one of the following }
begin
  if RunningState = rsSuspended then begin
    Resume
  end else begin
    {v0.50}
    if ShowMessage(GetTxt({#}'Really suspend acquisition'), smYesNo, 0) <> cmYes then
      exit;
    {/v0.50}
    Suspend;
  end;
end;

procedure TAcqInfo.Suspend;
var t: longint;
begin
  {v0.21}
  DeviceStop;
  FRunningState := rsSuspended;
  DoModeTimers;{read+ignores=clear device data buffers }
  t := mstime;
  if (FLastDataTime > 0) and (FSuspendedForNoData) then begin
    t := FLastDataTime;
  end;
  FAllAcqTime := {v0.21}FLastDataX{/v0.21 FAllAcqTime + (t - ZeroTime)/1000};
  FLastSuspendTime := t / 1000;
  {/v0.21
  DoTimer;
  FRunningState := rsSuspended;
  if FLastSuspendTime = 0 then begin
    FAllAcqTime := FAllAcqTime + (mstime - ZeroTime)/1000;
  end else begin
    FAllAcqTime := FAllAcqTime + (mstime/1000 - FLastResumeTime);
  end;
  FLastSuspendTime := mstime / 1000;
  }
  inc(FSuspendCount);
  {v0.21}
  NotifyForms(cmAcquisitionSuspended);
  {/v0.21
  if FForm <> nil then begin
    SendMessage(FForm.Handle, WM_APPMESSAGE, cmAcquisitionSuspended, longint(Self));
  end;}
end;

procedure TAcqInfo.Resume;
{var l: single;}
begin
  FLastResumeTime := mstime / 1000;
  FAllSuspendInterval := FAllSuspendInterval + (FLastResumeTime - FLastSuspendTime);
  {v0.21}
  DeviceStart;
  {/v0.21}
  {v0.31}
  DoStartResume;
  {/v0.31
  Start;}
  {v0.24 moved to Start}{/v0.24 FRunningState := rsRunning;}
  {v0.21}
  NotifyForms(cmAcquisitionResumed);
  FSuspendedForNoData := false;
  {/v0.21
  if FForm <> nil then begin
    SendMessage(FForm.Handle, WM_APPMESSAGE, cmAcquisitionResumed, longint(Self));
  end;}
end;

function TAcqInfo.GetFMH: TFileMenuHandler;
begin
 with Application.MainForm as TMainForm do
   GetFMH := FMH;
end;

function TAcqInfo.CanClose: boolean;
begin
  Result := true;
  if not VerifyAbort then
    exit;
  if RunningState <> rsStopped then begin
    if ShowMessage(GetTxt({#}'Abort acquisition?'), smNoYes, 0) <> cmYes then begin
      Result := false;
      exit;
    end;
    Stop;
  end;
  if (Data <> nil) (*v0.30 moved to TAcqData*) then begin
    CanClose := Data.CanClose;
  end;
  (*/v0.30 and (Data.ULF <> nil) then begin
    if (pos('NONAME', Data.ULF.FileName) <> 0) then
    begin
      if Data.{v0.24}DataSize{/v0.24 ULAD.Data.Size} > 0 then begin
        if not FMH.SaveAs then begin
          if ShowMessage(GetTxt({#}'Discard data?'), smNoYes, 0) <> cmYes then begin
            Result := false;
          end else begin
            Data.AcquiredDataDiscard;
          end;
        end;
      end;
    end else begin
      if Data.ULF.Modified then begin
        if Data.ULF.ReadOnly then begin
          case ShowMessage(Data.ULF.FileName + ' ' + GetTxt({#}'was opened Read Only but modified.') +
            ' ' + GetTxt({#}'Save to other file?'), smYesNoCancel, 0) of
            cmCancel: CanClose := false;
            cmYes: begin
              if not FMH.SaveAs then begin
                CanClose := false;
              end else begin
                Data.ULF.Modified := false;
              end;
            end;
            cmNo: Data.ULF.Modified := false;
          end;
        end else begin
          case ShowMessage(Data.ULF.FileName, smFileModifiedSave,0) of
            cmCancel: CanClose := false;
            cmNo: Data.ULF.Modified := false;
          end;
        end;
      end;
    end
  end;
  *)
end;

procedure TAcqInfo.PointsAddStart;
begin
  FRecentPoints.Size := 0;
end;

procedure TAcqInfo.PointsAddAdd(ADataIndex: integer; dr: TExpPoint);
var
  t: single;
  x: single;

  {v0.24}
  rep: TRecentExpPoint;
  {/v0.24}
begin
  FLastDataTime := mstime;
  FNoDataInLimit := FLastDataTime + FDataWaitTimeout;
  {/v0.21}
  if (FSampleInt <> 0) then begin
    t := dr.X * 1000;
    if t >= FNextAcqTime then begin
      FNextAcqTime := t + FSampleInt;
    end else begin
      exit;
    end;
  end;
  if FSuspendCount > 0 then begin
    {v0.21}
    if dr.X = 0 then
      exit;
    x := dr.X + FAllAcqTime;
    {/v0.21
      x := dr.X  - FAllSuspendInterval;
      if x < FAllAcqTime then
        exit;
    end;}
    dr.X := x;
  end;
  {v0.21}
  FLastDataX := dr.X;
  {/v0.21}
  {v0.24}
  Data.DataIndex := ADataIndex;
  Data.AddPoint(dr);
  rep.Point := dr;
  rep.Index := ADataIndex;
  FRecentPoints.WriteBuffer(rep, sizeof(rep));
  {/v0.24
  Data.AddPoint(dr);
  FRecentPoints.WriteBuffer(dr, sizeof(dr));}
end;

procedure TAcqInfo.PointsAddStop;
begin
  if FRecentPoints.Size > 0 then begin
    {v0.21}
    NotifyForms(cmExpPointsAdded);
    {/v0.21
    if FForm <> nil then begin
      SendMessage(FForm.Handle, WM_APPMESSAGE, cmExpPointsAdded, longint(Self));
    end;}
  end;
end;

procedure TAcqInfo.DoApexPacket(var ai:TApexPacketInfo);
var
  dr: TExpPoint;
{  sr: TScreenPoint;}
  i: integer;
  values: PApexValues;
  {v0.31}
  di:integer;
  int: single;
  uladrc: TULADRc;
  {/v0.31}

{  acq: boolean;}
  {
  function MakeLogLine(var ai:TApexPacketInfo):string;
  var
    s:string;
    l:single;
    b:array[0..3]of byte absolute l;

  begin
    s := '';
    s := RecToHex(ai.Pkt.Data[ApexSourceIDPos], 3) + ' ' +
      RecToHex(ai.Pkt.Data[ApexFootPos], 3) + ' ' +
      RecToHex(ai.pkt.Data[ApexValuesPos], 4) + ' ';
    s := s + bin(b[0]) + ' ' + bin(b[1]) + ' ' +bin(b[2])+ ' ' +bin(b[3]);
    MakeLogLine := s;
  end;
   }
begin
  {
  if LogPackets then begin
    SpectrumMemo.Lines.Add(MakeLogLine(ai));
  end;}
  if Data <> nil then begin
    if not ai.ScanningValues then
      values := @ai.Pkt.Data[ApexValuesPos]
    else
      values := @ai.Pkt.Values;
    {v0.31}
    di := Data.GetAddrULADIndex(ai.Pkt.X2);{??X1??!!!};
    uladrc := Data.ULAD[di];
    int := uladrc.PointTimeInterval;
    {/v0.31}

    PointsAddStart;
    for i := 0 to ApexPacketValueCount - 1 do begin
      {v0.31}
      uladrc.CurTime := uladrc.CurTime + int;
      dr.X := uladrc.CurTime / 1000;
      {/v0.31
      CurUlanTime := CurUlanTime + UlanPointTimeInterval;
      dr.X := CurUlanTime / 1000;}
      dr.Y := (values^[i]) {v0.31}* uladrc.YCoef{/v0.31} - ZeroValue;
      PointsAddAdd({v0.31}di{/v0.31 0}, dr);
      {if (FSampleInt <> 0) then begin
        if CurUlanTime >= FNextAcqTime then begin
          FNextAcqTime := CurUlanTime + FSampleInt;
        end else begin
          acq := false
        end;
      end;
      if acq then
      begin
        Data.AddPoint(dr);
        FRecentPoints.WriteBuffer(dr, sizeof(dr));
      end;}
    end;
    {v0.41}
    {FCurTime := dr.X;}
    {/v0.41}
    PointsAddStop;
  end;
{  DrawPoints(PaintBox.Canvas, ScreenDisp);}
end;

procedure TAcqInfo.DoExtDevTimer;
var
  dr: TExpPoint;
{  sr: TScreenPoint;}
begin
  if Data = nil then
    exit;
  PointsAddStart;
  while ExtDev.ReadPoint(dr) = 0 do begin
    {v0.53}
    FGotDataInTimer := true;
    {/v0.53}
    if FRunningState = rsRunning then
    begin
      PointsAddAdd(0, dr);
    end;
  end;
  PointsAddStop;
end;
{/v0.19}


end.
