unit VisDynamicPict;

interface

uses
{$ifdef VER150}
  DesignIntf, DesignEditors, DesignWindows, DsnConst, variants,
{$else}
  DsgnIntf,
{$endif}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, stdctrls, ScadaBase, RpVisualGlobal;


type
  PVisDynamicPictRec =^TVisDynamicPictRec;
  TVisDynamicPictRec = record
    FileName: string;
    Cndt: string;
    _cndt: string;
    Marks: array of integer;
    MarkChars: array of char;
    UseCndt: boolean;
    Value: integer;
  end;

  PVisDynPictTagRec =^TVisDynPictTagRec;
  TVisDynPictTagRec = record
    id: integer;
    Index: integer;
    Value: integer;
    Name: string;
    _name: string;
  end;



  TVisDynamicPict = class(TCustomScadaObject)
  private
    FData: TStringList;
    FStretch: boolean;
    FLastPictIdx: integer;
    FLastPictStr: string[10];

    procedure SetupData(Value: TStringList);
    procedure SetStretch(Value: boolean);
  protected
    procedure Loaded; override;
  public
    Picts: array of PVisDynamicPictRec;
    PictCount: integer;

    Tags: array of PVisDynPictTagRec;
    TagCount: integer;

    TagName: TTagName;
//    TagPath: TTagPath;
    DeviceName: TDeviceName;
    ImageObj: TImage;

    MainExpr: string;
    _MainExpr: string;

    MarkPos: array of integer;
    MarkTag: array of integer;

    UpdateOnTagsChanging: boolean;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure Init; override;
    procedure Process; override;

    procedure ClearArrays;
    procedure LoadSetupData;
    procedure SaveSetupData;

  published
    property Data: TStringList read FData write SetupData;
    property Stretch: boolean read FStretch write SetStretch;
  end;

  TVisDynamicPictEditor = class(TComponentEditor)
  private
  public
    procedure Edit; override;
  end;



procedure Register;

implementation
uses
  TagStorage,
  rp_pars,
  numbers,
  ExtDlgs,
  VisDynamicPictSetup;

procedure Register;
begin
  RegisterComponents('Scada', [TVisDynamicPict]);
  RegisterComponentEditor(TVisDynamicPict, TVisDynamicPictEditor);
end;

{ TVisDynamicPict }

constructor TVisDynamicPict.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  Width := 100;
  Height := 100;
  ShowHint := true;

  FData := TStringList.Create;

  Picts := nil;
  PictCount := 0;

  FLastPictIdx := 0;
  FLastPictStr := '0';

  Tags := nil;
  TagCount := 0;

  DeviceName := '';
  UpdateOnTagsChanging := true;


  // Создание изображения
  ImageObj := TImage.Create(Self);
  ImageObj.Parent := self;
  ImageObj.Stretch := true;
  ImageObj.Visible := true;
  ImageObj.Align := alClient;

  ImageObj.OnClick := OnDeviceClick;
  ImageObj.OnDblClick := OnDeviceDblClick;
end;

destructor TVisDynamicPict.Destroy;
begin
  ClearArrays;
  inherited;
end;

procedure TVisDynamicPict.SetupData(Value: TStringList);
begin
  FData.Assign(Value);
  LoadSetupData;
end;


procedure TVisDynamicPict.SetStretch(Value: boolean);
begin
  FStretch := value;
  ImageObj.Stretch := value;
end;



// INIT
procedure TVisDynamicPict.Init;
var
  i,k1,k2,n,k: integer;
  s,src,sval: string;
  v: variant;
  prs: TfrParser;
  c: char;

    function find_tag(ctag: char): boolean;
    begin
      c := ctag;
      k1 := pos(c, Picts[i]^._cndt);
      result := k1>0;
    end;
    
begin
  inherited;

  // Формирование имен тегов. Расчет и замена выражений на значения
  prs := TfrParser.Create;
  try

    for i:=0 to TagCount-1 do begin

      src := Tags[i].Name + ' ';
      rpReplaceS(src, '^', TagName);
      repeat
        k1 := pos('{',src);
        k2 := pos('}',src);
        if (k1=0) or (k2=0) then break;
        s := copy(src, k1+1, k2-k1-1);
        delete(src, k1, k2-k1+1);

        try
          v := prs.Calc(s);
          sval := VarToStr(v);
        except
          sval := '(error: ' + s + ')';
        end;

        insert(sval, src, k1);
      until false;

      Tags[i]._name := trim(src);
    end;
  finally
    prs.Free;
  end;

  // получение индексов тегов
  IsConnectionBad := false;
  for i:=0 to TagCount-1 do with Tags[i]^ do begin
    index := GetTagIndex(TagPath+'.'+_name);
    IsConnectionBad := (IsConnectionBad) or (index<0);
    Tags[i].Value := -1;
  end;


  // составление списка меток для расчета выражения MainExpr
  _MainExpr := MainExpr;
  MarkPos := nil;
  MarkTag := nil;
  k := 0;
  repeat
    k1 := pos('[',_MainExpr);
    k2 := pos(']',_MainExpr);
    if (k1=0) or (k2=0) then break;
    s := copy(_MainExpr, k1+1, k2-k1-1);
    delete(_MainExpr, k1, k2-k1+1);
    val(s, n, i);

    for i:=0 to TagCount-1 do
      if Tags[i].id = n then begin
        SetLength(MarkPos, k+1);
        SetLength(MarkTag, k+1);
        MarkPos[k] := k1;
        MarkTag[k] := i;
        inc(k);
        break;
      end;
  until false;

  // составление списка меток для расчета выражений картинок
  c := '@';
  for i:=0 to PictCount-1 do with Picts[i]^ do begin
    _Cndt := cndt;
    Marks := nil;
    MarkChars := nil;
    k := 0;
    repeat
      if not find_tag('@') then
      if not find_tag('№') then
      break;

      delete(_cndt, k1, 1);
      SetLength(Marks, k+1);
      SetLength(MarkChars, k+1);
      Marks[k] := k1;
      MarkChars[k] := c;
      inc(k);
    until false;
    UseCndt := k>0;
    if not UseCndt then begin
      val(Cndt, Value, k);
    end;
  end;

end;

// PROCESS HANDLER
procedure TVisDynamicPict.Process;
var
  i,k,x: integer;
  s,sx: string;
  v: variant;
  flag: boolean;
  prs: TfrParser;
begin

  // получение значений тегов
  flag := false;
  IsValueBad := false;
  for i:=0 to TagCount-1 do begin
    x := GetTagValue(Tags[i].Index);
    flag := flag or (Tags[i].Value <> x);
    Tags[i].Value := x;
    IsValueBad := IsValueBad or (Tags[i].Value < 0);
  end;

  flag := flag or (not UpdateOnTagsChanging);

  prs := TfrParser.Create;
  if flag then
  try

    // расчет значения _MainExpr
    x := 0;
    if length(MarkPos)>0 then begin
      s := _MainExpr;
      for i:=length(MarkPos)-1 downto 0 do
        insert(inttostr(Tags[MarkTag[i]].Value), s, MarkPos[i]);
      try
        v := prs.Calc(s);
        x := v;
      except
      end;
    end else begin
      if TagCount>0 then x := Tags[0].Value;
    end;
    sx := IntToStr(x);

    // расчет _cndt картинок
    for k:=0 to PictCount-1 do with Picts[k]^ do begin
      flag := false;
      if UseCndt then begin
        s := _cndt;
        for i:=length(Marks)-1 downto 0 do
          case MarkChars[i] of
            '@':
              insert(sx, s, Marks[i]);
            '№':
              insert(FLastPictStr, s, Marks[i]);
          end;
        try
          v := prs.Calc(s);
          flag := (v>0);
        except
        end;
      end else begin
        flag := (Value = x);
      end;

      if (flag) and (FLastPictIdx<>k) then
        if FileExists(VisImagesPath+Picts[k].FileName) then begin
          ImageObj.Picture.LoadFromFile(VisImagesPath+Picts[k].FileName);
          FLastPictIdx := k;
          FLastPictStr := inttostr(k);
        end else begin
          ImageObj.Picture.Assign(nil);
          Color := clBlue;
        end;
      if flag then break;
    end;


  finally
    prs.Free;
  end;

  inherited;
end;



procedure TVisDynamicPict.LoadSetupData;
var
  s: string;
  k, i, j, v, ps: integer;
begin

  // [0] TagPath
  if FData.Count>0 then s := FData.Strings[0] else s := '';
  k := pos('.', s);
  TagPath := copy(s, 1, k-1);

  // TagName
  delete(s, 1, k); k := pos(';', s);
  TagName := copy(s, 1, k-1);

  // DeviceName
  delete(s, 1, k);
  DeviceName := trim(s);
  Hint := DeviceName;

  // [1] Main Expression
  if FData.Count>1 then s := FData.Strings[1] else s := '';
  MainExpr := s;


  // Очистка массивов перед заполением
  ClearArrays;

  // [2 >] Загрузка тегов
  ps := 2;
  if FData.Count>ps then s := FData.Strings[ps] else s := '';
  val(s, v, j);
  TagCount := v;
  SetLength(Tags, TagCount);
  inc(ps);

  for i:=0 to TagCount-1 do begin
    new(Tags[i]);

    if FData.Count>ps then s := FData.Strings[ps] else s := '';
    k := pos(';', s);
    val(copy(s, 1, k-1), v, j);
    Tags[i].Id := v;

    delete(s, 1, k);
    Tags[i].Name := trim(s);

    Tags[i].Index := -1;
    Tags[i].Value := 0;

    inc(ps);
  end;

  // [ps >] Загрузка картинок
  if FData.Count>ps then s := FData.Strings[ps] else s := '';
  val(s, v, j);
  PictCount := v;
  SetLength(Picts, PictCount);
  inc(ps);

  for i:=0 to PictCount-1 do begin
    new(Picts[i]);

    if FData.Count>ps then s := FData.Strings[ps] else s := '';
    k := pos(';', s);
    Picts[i].FileName := copy(s, 1, k-1);

    delete(s, 1, k);
    Picts[i].Cndt := trim(s);

    Picts[i]._Cndt := '';
    Picts[i].Marks := nil;
    Picts[i].MarkChars := nil;
    Picts[i].UseCndt := false;
    Picts[i].Value := i;

    inc(ps);
  end;

  if FData.Count>ps then
    s := FData.Strings[ps]
  else
    s := '';
//  inc(ps);
  val(s, v, j);
  UpdateOnTagsChanging := v>0;


  if PictCount>0 then begin
    VisImagesPath := VisImagesPath;
//    ShowMessage(GetCurrentDir + ' $$$ ' +  VisImagesPath+Picts[0].FileName);                                       

    if FileExists(VisImagesPath+Picts[0].FileName) then begin
      ImageObj.Picture.LoadFromFile(VisImagesPath+Picts[0].FileName);
      exit;
    end;
  end;

  Color := clBlue;

end;

procedure TVisDynamicPict.SaveSetupData;
var
  i: integer;
begin

  FData.Clear;
  FData.Add( TagPath + '.' + TagName + ';' + DeviceName );
  FData.Add( MainExpr );
  FData.Add( inttostr(TagCount) );
  for i:=0 to TagCount-1 do
    FData.Add( inttostr(Tags[i].id) + ';' + Tags[i].Name );

  FData.Add( inttostr(PictCount) );
  for i:=0 to PictCount-1 do
    FData.Add( Picts[i].FileName + ';' + Picts[i].Cndt );

  FData.Add( iif(UpdateOnTagsChanging, '1', '0') );

end;


procedure TVisDynamicPict.Loaded;
begin
  inherited;
  LoadSetupData;
end;


procedure TVisDynamicPict.ClearArrays;
var
  i: integer;
begin
  for i:=0 to TagCount-1 do begin
    Dispose(Tags[i]);
  end;
  Tags := nil;
  TagCount := 0;

  for i:=0 to PictCount-1 do begin
    Dispose(Picts[i]);
  end;
  Picts := nil;
  PictCount := 0;
end;




{ TVisDynamicPictEditor }

procedure TVisDynamicPictEditor.Edit;
begin
  inherited;
  VisDynamicPictSetupForm := TVisDynamicPictSetupForm.Create(nil);

  VisDynamicPictSetupForm.M := Component as TVisDynamicPict;
  VisDynamicPictSetupForm.D := Self;
  if VisDynamicPictSetupForm.ShowModal = mrOk then
    VisDynamicPictSetupForm.M.SaveSetupData
  else
    VisDynamicPictSetupForm.M.LoadSetupData;

  VisDynamicPictSetupForm.Free;

end;




end.
