unit SeqPrgToDevu; { Sending plkDevice program lines to device }
{$I define.pas}
interface
uses
  SysUtils,
  SeqPrgu,
  USPDType, USPDObju,
  Channelsu,
  ULNType, ULNObju;

{ Send device program lines contained in ASeqPrg to the device
  ADeviceAlias that belongs to the channel AChannel.

  AParam specifies the type of the device, right now only value
  'LCP5020' is supported. For this device the following
  device program lines are allowed (LineType Param1 Param2):

  (Program line time is internally in hundreths of minute = PrgTime*100/60);

  FLOW  <Flow[mL/min]> // Flow of the solvent in mL/min. Internally converted
                       // to round((StrToFloat(Param1)*1000)).
  GRADBC <%B> <%C>    // Content of eluent B and C in the gradient mixture in percentsl
                      // Internally converted to round((StrToFloat(ParamX)*256)) (X = 1, 2)
  AUXOUT <AuxOut>     // List of bit numbers (1..9) on AUX output, that should be set on,
                      // e.g. '14' will set bit 1 and bit 4 to value 1.
                      // Using function StrToAuxOut(Param1) for conversion of Param1 to word.
  END                 // End of the program.
  WAITSW              // Wait for mark.
  SENDMARK <DestAddr> // Send mark to the device of specified address.
                      // Using StrToInt(Param1) to get the address.
  AUXVALV  <AuxValv>  // StrToInt(Param1);
  GRADVALV <GradValv> // StrToInt(Param1);
  PRESSCOR <PressCor> // round((StrToFloat(Param1)*10))
  SLAUXVALV <AuxValv>  // StrToInt(Param1);
  SLGRADVALV <GradValv> // StrToInt(Param1);
}
procedure SeqPrgToDev(ASeqPrg: TSeqPrg; AChannel: TChannel;
  const ADeviceAlias: string; const AParam: string);

implementation

uses
  ModuType,
  {$IFDEF USEDLL}
  ModuProc,
  {$ELSE}
  Modulu,
  {$ENDIF}
  SeqPrgLCPbs1{v0.70pi}, StrUtils{/v0.70pi};

function StrToAuxOut(const S:string):Word;
var
  w: Word;
  i: integer;
  c: char;
begin
  w := 0;
  for i := 1 to length(S) do begin
    c := S[i];
    if (Ord(c) >= Ord('1')) and (Ord(c) <= Ord('9')) then
      w := w or (1 shl (Ord(c)-Ord('1')));
  end;
  Result := w;
end;

procedure LCPLineSend(AModule:TModule; var AOffset:Word; var ABuf; ASize:Word);
var
  prop:TModuleProp;
  {modulu}
  {$IFDEF USEDLL}
  w: word;
  {$ENDIF}
begin
  {$IFDEF USEDLL}
  if not ModuleHasProp(AModule, mpPropDesc, 'PRGBYTES', prop) then
    exit;
  if ModuleDoAction(AModule, mdaStreamMessageCreate, nil, 0) = 0 then begin
    w := ModulePropGetPropInt(prop, mpPropIDStr);
    ModuleDoAction(AModule, mdaStreamMessageWrite, @w, 2);
    w := ASize or $8000;
    ModuleDoAction(AModule, mdaStreamMessageWrite, @w, 2);
    ModuleDoAction(AModule, mdaStreamMessageWrite, @AOffset, 2);
    ModuleDoAction(AModule, mdaStreamMessageWrite, @ABuf, ASize);
    w := 0;
    ModuleDoAction(AModule, mdaStreamMessageWrite, @w, 2);
    ModuleDoAction(AModule, mdaStreamMessageClose, nil, 0);
    inc(AOffset, ASize);
  end;
  {$ELSE}
  with AModule do begin
    if not FindPropByDesc('PRGBYTES',prop) then
      exit;
    if OIMessageCreate = 0 then begin
      OIMessageWriteWord(StrToInt(prop.ULDP.PropIDStr));
      OIMessageWriteWord(ASize or $8000);
      OIMessageWriteWord(AOffset);
      OIMessageWriteBuf(ABuf,ASize);
      OIMessageWriteWord(0);
      OIMessageClose;
      inc(AOffset,ASize);
    end;
  end;
  {$ENDIF}
end;

procedure SeqPrgToDev(ASeqPrg: TSeqPrg; AChannel: TChannel;
  const ADeviceAlias: string; const AParam: string);
var
  i: integer;
  dl: TSeqPrgDeviceLine;
  m: TModule;
  offset: Word;
  LCPLine:TlcpbsLine;
  {v0.64}
  er: boolean;
  {/v0.64}
  {v0.70pi}
  prop: TModuleProp;
  s, s1 : string;
  FlowFractions:single;
  {/v0.70pi}
begin
  if AChannel = nil then
    exit;
  if ASeqPrg = nil then
    exit;
  offset:=0;
  FlowFractions:=1000;
  with ASeqPrg do begin
    {v0.70pi}
    if (AParam = 'LCP5020')
        or (AParam = 'LCP5080') then begin
      if not  AChannel.DeviceAliasToDevice(ADeviceAlias,  m) then begin
        LogErr('No device with alias ' + ADeviceAlias + 'in channel ' + AChannel.ChannelName);
        exit;
      end;
      {$IFDEF USEDLL}
      if not ModuleHasProp(m, mpPropDesc, 'PRGBYTES', prop) then
      {$ELSE}
      if not m.FindPropByDesc('PRGBYTES',prop) then
      {$ENDIF}
      begin
        LogErr('LCP50xx program download requires PRGBYTES property');
        exit;
      end;
      {$IFDEF USEDLL}
      s := ModulePropGetPropStr(prop, mpTypeDesc);
      {$ELSE}
      s := prop.TypeDesc;
      {$ENDIF}
      if LeftStr(s,12) <> 'prgbyteslcp1' then begin
        LogErr('Unknown type ' + s + ' of LCP50xx property '+
          {$IFDEF USEDLL}
          ModulePropGetPropStr(prop, mpPropDesc)
          {$ELSE}
          prop.PropDesc
          {$ENDIF}

        );
        exit;
      end;
      i := 3;
      if length(s) >= 14 then i := Ord(s[14]) - Ord('0');
      Str(i,s1);
      Log('LCP50xx program download found property '+
        {$IFDEF USEDLL}
        ModulePropGetPropStr(prop, mpPropDesc)
        {$ELSE}
        prop.PropDesc
        {$ENDIF} + ' of type '+ s + ' => frac ' + s1);
      case i of
        0 : FlowFractions:=1;
        1 : FlowFractions:=10;
        2 : FlowFractions:=100;
        3 : FlowFractions:=1000;
      end;
    end else begin
      LogErr('Unknown DeviceType: ' + AParam);
      exit;
    end;
    {/v0.70pi}

    {v0.64}
    Log('START upload program lines to channel.device: ' + AChannel.ChannelName + '.' + ADeviceAlias + ' Type: ' + AParam);
    try
    {/v0.64}
    for i := 0 to DeviceLineCount - 1 do begin
      dl := DeviceLines[i];
      {v0.64}
      er := false;
      {/v0.64}
      with dl.USPD do begin {uspdtype}
        if DeviceAlias = ADeviceAlias then begin
          if AChannel.DeviceAliasToDevice(DeviceAlias,  m) then begin
            if (AParam = 'LCP5020')  {v0.70pi}
                or (AParam = 'LCP5080') {/v0.70pi} then begin
              if LineType = 'FLOW' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineFlow);
                LCPLine.Head.LineType:=lcpltFlow;
                LCPLine.Flow.Time:=round(PrgTime*100/60);
                LCPLine.Flow.Flow:=round((StrToFloat(Param1)*
                        {v0.70pi}FlowFractions{/v0.70pi 1000}));
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end else if LineType = 'GRADBC' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineGradient);
                LCPLine.Head.LineType:=lcpltGradient;
                LCPLine.Gradient.Time:=round(PrgTime*100/60);
                LCPLine.Gradient.GradB:=round((StrToFloat(Param1)*256));
                LCPLine.Gradient.GradC:=round((StrToFloat(Param2)*256));
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end else if LineType = 'AUXOUT' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineAuxOut);
                LCPLine.Head.LineType:=lcpltAuxOut;
                LCPLine.AuxOut.Time:=round(PrgTime*100/60);
                LCPLine.AuxOut.AuxOut:=StrToAuxOut(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize); 
              end else if LineType = 'END' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLinePrgEnd);
                LCPLine.Head.LineType:=lcpltPrgEnd;
                LCPLine.PrgEnd.Time:=round(PrgTime*100/60);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize); 
              end else if LineType = 'WAITSW' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineWaitSW_Mark);
                LCPLine.Head.LineType:=lcpltWaitSW_Mark;
                LCPLine.WaitSW_Mark.Time:=round(PrgTime*100/60);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize); 
              end else if LineType = 'SENDMARK' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineSendMark);
                LCPLine.Head.LineType:=lcpltSendMark;
                LCPLine.SendMark.Time:=round(PrgTime*100/60);
                LCPLine.SendMark.DestAddr:=StrToInt(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end else if LineType = 'AUXVALV' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineAuxValv);
                LCPLine.Head.LineType:=lcpltAuxValv;
                LCPLine.AuxValv.Time:=round(PrgTime*100/60);
                LCPLine.AuxValv.AuxValv:=StrToInt(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize); 
              end else if LineType = 'GRADVALV' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineGradValv);
                LCPLine.Head.LineType:=lcpltGradValv;
                LCPLine.GradValv.Time:=round(PrgTime*100/60);
                LCPLine.GradValv.GradValv:=StrToInt(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize); 
              end else if LineType = 'PRESSCORR' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLinePressCorr);
                LCPLine.Head.LineType:=lcpltPressCorr;
                LCPLine.PressCorr.Time:=round(PrgTime*100/60);
                LCPLine.PressCorr.PressCorr:=round((StrToFloat(Param1)*10));;
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end {v0.70pi}else if LineType = 'SLAUXVALV' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineAuxValv);
                LCPLine.Head.LineType:=lcpltSlAuxValv;
                LCPLine.AuxValv.Time:=round(PrgTime*100/60);
                LCPLine.AuxValv.AuxValv:=StrToInt(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end else if LineType = 'SLGRADVALV' then begin
                FillChar(LCPLine,sizeof(LCPLine),0);
                LCPLine.Head.LineSize:=sizeof(TlcpbsLineHead)+sizeof(TlcpbsLineGradValv);
                LCPLine.Head.LineType:=lcpltSlGradValv;
                LCPLine.GradValv.Time:=round(PrgTime*100/60);
                LCPLine.GradValv.GradValv:=StrToInt(Param1);
                LCPLineSend(m,offset,LCPLine,LCPLine.Head.LineSize);
              end {/v0.70pi} {v0.64} else begin
                LogErr('Unknown LineType: ' + LineType);
                er := true;
              end {/v0.64};
              {v0.64}
              if not er then begin
                Log('Line sent: ' + LineType + ' ' + Param1 + ' ' + Param2);
              end;
              {/v0.64}
            end {v0.64} else begin
              LogErr('Unknown DeviceType: ' + AParam);
            end {/v0.64};
          end;
        end;
      end;

    end;//for
    {v0.64}
    finally
      Log('STOP upload program lines.');
    end;
    {/v0.64}
  end;//with
end;

end.
