unit AcqInfou;{v0.14}

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};

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;
    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}
  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}
  public
    constructor Create(AData: TAcqData);reintroduce;
    destructor Destroy;override;
    {v0.24}
    procedure PointsAddStart;
    procedure PointsAddAdd(ADataIndex: integer; dr: TExpPoint);
    procedure PointsAddStop;
    {/v0.24}
    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}
    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}
  end;

implementation
uses main, SpecForm;

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;
  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
    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.41}
        if Assigned(FOnMark) then begin
          FOnMark(Self);
        end else
        {/v0.41}
        begin
          if RunningState =  rsReadyToRun then begin
            {if FForm <> nil then
              TSpectrumForm(FForm).Run;}
            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.DetAddrList{/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;

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.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}
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
    Stop
  else
    Run;
end;

{v0.21}
procedure TAcqInfo.DeviceStart;
begin
  ResetPacketInfo;
  ZeroTime := mstime;
  {v0.31}
  Data.ULAD.Start;
  {/v0.31
  CurUlanTime := 0;}
  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
  case DeviceMode of
    dmPasive: DoApexTimer;
    dmUlan: DoUlanTimer;
    dmExtDev: DoExtDevTimer;
  else
    raise EAcqInfo.Create('Invalid DeviceMode');
  end;
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);
    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
    case ai.Pkt.Data[ApexEventIDPos] of
      eiMark: begin
        {v0.41}
        if Assigned(FOnMark) then begin
          FOnMark(Self);
        end else
        {/v0.41}
        begin
          if RunningState =  rsReadyToRun then
            Run;
        end;
      end;
    else
      DoApexPacket(ai);
    end;
  end;
end;

procedure TAcqInfo.DoSuspendResume;
      { called when menuitem Suspen/Resume clicked, calls one of the following }
begin
  if RunningState = rsSuspended then
    Resume
  else
    Suspend;
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
    if FRunningState = rsRunning then
    begin
      PointsAddAdd(0, dr);
    end;
  end;
  PointsAddStop;
end;
{/v0.19}


end.
