unit VisDataboardWnd;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls, VisDataboard, ExtCtrls, Menus;


type
  PVisDataboardMesRec =^TVisDataboardMesRec;
  TVisDataboardMesRec = record
    Text: string;
    Tagname: string;
    Value: integer;
    Oper: integer;
    tag: integer;
  end;

  PVisDataboardParTransRec =^TVisDataboardParTransRec;
  TVisDataboardParTransRec = record
    Oper: char;
    Value: double;
  end;

  PVisDataboardParEnumRec =^TVisDataboardParEnumRec;
  TVisDataboardParEnumRec = record
    Value: integer;
    Text: string;
  end;

  PVisDataboardParRec =^TVisDataboardParRec;
  TVisDataboardParRec = record
    Text: string;
    Tagname: string;
    Trans: array of PVisDataboardParTransRec;
    Enum: array of PVisDataboardParEnumRec;
    Decnum: integer;
    AdminAccess: integer;
    tag: integer;
  end;

type
  TVisDataboardWndForm = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    TabSheet2: TTabSheet;
    memo: TMemo;
    list: TListView;
    Timer1: TTimer;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    Button1: TButton;
    Button2: TButton;
    btTrend: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure listCustomDrawItem(Sender: TCustomListView; Item: TListItem;
      State: TCustomDrawState; var DefaultDraw: Boolean);
    procedure Timer1Timer(Sender: TObject);
    procedure listDblClick(Sender: TObject);
    procedure btTrendClick(Sender: TObject);
  private
    function hasChannelParameters: boolean;
    function isChannelParameter(i: integer): boolean;
    { Private declarations }
  public
    M: TVisDataboard;
    mes: array of PVisDataboardMesRec;
    par: array of PVisDataboardParRec;

    procedure Init;
  end;

var
  VisDataboardWndForm: TVisDataboardWndForm;

implementation
uses
  Trends,
  TrendWindow,
  InputDialog,
  ChannelMap,
  ChannelInput,
  DataClientBase,
  UserControlEx,
  Users,
  MesConst,
  meslogging,
  SetChannel,
  RpVisualUtils,
  TagStorage,
  numbers,
  hyperstr,
  VisDataboardWndParEnum;

{$R *.dfm}

var
  lastPageIndex: integer = 0;

{ TVisDataboardWndForm }

procedure TVisDataboardWndForm.Init;

  function _get_tagidx(s: string): integer;
  var
    k: integer;
  begin
    if length(M.TagRoot)>0 then begin
      k:=pos(M.TagIdRoot, s);
      if k>0 then begin
        delete(s, k, length(M.TagIdRoot));
        insert(M.TagRoot, s, k);
      end;
    end;

    if pos('$',s)=1 then
      delete(s,1,1)
    else if length(M.TagPath)>0 then
      s := M.TagPath + '.' + s;

    k := GetTagIndex(s);

    result := k;
  end;

var
  sl, sla: TStringList;
  i,j,k,kk,v,q: integer;
  s,ss, a,b: string;
  flag: boolean;
begin
  btTrend.Visible := M.TrendButtonVisible;

  DoubleBuffered := True;
  list.DoubleBuffered := true;

  if M=nil then exit;
  Caption := Trim(M.Caption);
  if Caption = '' then
    Caption := Trim(M.Hint);

  sl := TStringList.Create;
  sla := TStringList.Create;

  // Mes
  sl.Text := M.DataMes.Text;
  k := 0;
  for i:=0 to sl.Count-1 do begin
    s := trim(sl[i]);
    if s<>'' then begin
      SetLength(mes, k+1);
      new(mes[i]);
      mes[i].Text := get_substr_beg(';', s);
      mes[i].Tagname := trim(get_substr_beg(';', s));
      val(get_substr_beg(';', s), mes[i].Value, q);
      val(s, mes[i].Oper, q);
      mes[i].tag := _get_tagidx(mes[i].Tagname);
      inc(k);
    end;
  end;

  // Par
  sl.Text := M.DataPar.Text;
  k := 0;
  for i:=0 to sl.Count-1 do begin
    s := trim(sl[i]);

    a := get_substr_beg(';', s);
    b := trim(get_substr_beg(';', s));
    kk := _get_tagidx(b);

    if (a<>'') and ((kk<>-1) or (b='')) then begin
      SetLength(par, k+1);
      new(par[k]);

      par[k].Text := a;
      par[k].Tagname := b;
      par[k].tag := kk;

      // Par Trans
      ss := get_substr_beg(';', s);
      ReplaceS(ss, ',', #13#10);
      sla.text := ss;
      kk := 0;
      for j:=0 to sla.Count-1 do begin
        ss := trim(sla[j]);
        if ss<>'' then begin
          SetLength(par[k].Trans, kk+1);
          new(par[k].Trans[kk]);
          par[k].Trans[kk].Oper := ss[1];
          delete(ss,1,1);
          val(ss,v,q);
          par[k].Trans[kk].Value := v;
          inc(kk);
        end;
      end;

      // Par Enum
      ss := get_substr_beg(';', s);
      ReplaceS(ss, ',', #13#10);
      sla.text := ss;
      kk := 0;
      for j:=0 to sla.Count-1 do begin
        ss := trim(sla[j]);
        if ss<>'' then begin
          SetLength(par[k].Enum, kk+1);
          new(par[k].Enum[kk]);
          val(get_substr_beg('=', ss), v, q);
          par[k].Enum[kk].Value := v;
          par[k].Enum[kk].Text := ss;
          inc(kk);
        end;
      end;

      if (getTagType(par[k].tag) = TAGTYPE_BOOL) and (Length(par[k].Enum) = 0) then begin
          SetLength(par[k].Enum, 2);
          new(par[k].Enum[0]);
          new(par[k].Enum[1]);
          par[k].Enum[0].Value := 0;
          par[k].Enum[0].Text := 'выкл';
          par[k].Enum[1].Value := 1;
          par[k].Enum[1].Text := 'ВКЛ';
      end;

      ss := get_substr_beg(';', s);
      val(ss, v, q);
      par[k].Decnum := v;

      ss := get_substr_beg(';', s);
      val(ss, v, q);
      par[k].AdminAccess := v;

      inc(k);
    end;
  end;

  sl.Free;
  sla.Free;


  // Заполнение списка параметров
  flag := false;
  for i:=0 to length(par)-1 do
    with list.Items.Add do begin
      Caption := par[i].Text;
      SubItems.Add('');
      if par[i].Tagname<>'' then
        flag:=true;
    end;

  if flag then
    for i:=0 to length(par)-1 do
      if par[i].Tagname<>'' then
        with list.Items[i] do
          Caption := '    ' + Caption;


  if hasChannelParameters() then
    chmapUpdate;

  Timer1Timer(nil);

  if ((lastPageIndex = 0) and (not m.HideMessages)) or ((lastPageIndex = 1) and (not m.HideParameters)) then
    PageControl1.ActivePageIndex := lastPageIndex;

end;

procedure TVisDataboardWndForm.FormDestroy(Sender: TObject);
var
  i,j: integer;
begin
  lastPageIndex := PageControl1.ActivePageIndex;

  for i:=0 to length(mes)-1 do
    Dispose(mes[i]);

  for i:=0 to length(par)-1 do begin
    for j:=0 to length(par[i].Trans)-1 do
      Dispose(par[i].Trans[j]);

    for j:=0 to length(par[i].Enum)-1 do
      Dispose(par[i].Enum[j]);

    Dispose(par[i]);
  end;
end;

// Подкраска в списке
procedure TVisDataboardWndForm.listCustomDrawItem(
  Sender: TCustomListView; Item: TListItem; State: TCustomDrawState;
  var DefaultDraw: Boolean);
var
  r: TRect;
begin
  if par[Item.Index].Tagname='' then begin
    r := Item.DisplayRect(drBounds);
    list.Canvas.Brush.Color := clBtnFace;
    List.Canvas.Font.Style := [fsBold];
    list.Canvas.FillRect(r);
    List.Canvas.TextRect(r, r.Left+3, r.Top, Item.Caption);
    DefaultDraw := false;
  end;
end;

// Таймер
procedure TVisDataboardWndForm.Timer1Timer(Sender: TObject);
var
  i,j: integer;
  s: string;
  v: Int64;
  r: double;
  flag, hasTrans, hasEnum, typeString: boolean;
  addr_flag: boolean;
begin

  // mes process
  s := '';
  for i:=0 to length(mes)-1 do begin
    v := GetTagValue(mes[i].tag);
    case mes[i].Oper of
      0: flag := v = mes[i].Value;
      1: flag := v >= mes[i].Value;
      2: flag := v <= mes[i].Value;
      3: flag := v <> mes[i].Value;
      4: flag := (v and mes[i].Value) = 0;
      5: flag := (v and mes[i].Value) > 0;
    else
      flag := false;
    end;

    if flag then
      s := s + mes[i].Text + #13#10#13#10
  end;

  if not sametext(s, memo.Text) then
    memo.Text := s;

  // par process
  for i:=0 to length(par)-1 do begin
    if par[i].tag = -1 then continue;

    hasTrans := par[i].Trans <> nil;
    hasEnum := par[i].Enum <> nil;
    typeString := getTagType(par[i].tag) = TAGTYPE_STRING;

    addr_flag := isChannelParameter(i);// (hasTrans) and (length(par[i].Trans)>0) and (par[i].Trans[0].Oper='A');
    if addr_flag then begin
      if typeString then
        s := chmapGetPrettyChannelStr( GetTagValueString(par[i].tag) )
      else
        s := GetStringChannelName( GetTagValue(par[i].tag) );
    end else

    if ((hasTrans) or (hasEnum)) and (not typeString) then begin
      v := GetTagValueLong(par[i].tag);
      r := GetTagValueDouble(par[i].tag);

      // par trans
      if hasTrans then
        for j:=0 to length(par[i].Trans)-1 do
          case par[i].Trans[j].Oper of
            '*': r :=r * par[i].Trans[j].Value;
            '/': r :=r / par[i].Trans[j].Value;
            '+': r :=r + par[i].Trans[j].Value;
            '-': r :=r - par[i].Trans[j].Value;
            '&': r :=trunc(r) and trunc(par[i].Trans[j].Value);
            '|': r :=trunc(r) or trunc(par[i].Trans[j].Value);
            'b': r := iif(v and (1 shl trunc(par[i].Trans[j].Value))>0, 1, 0);
          end;


      // par enum
      if not hasEnum then begin
        s := floatstr(r, 15, par[i].decnum);
      end else begin
        v := trunc(r);
        s := '?';
        for j:=0 to length(par[i].Enum)-1 do
          if v = par[i].Enum[j].Value then begin
            s := par[i].Enum[j].Text;
          end;
      end;

    end else begin
      s := GetTagValueString(par[i].tag);
    end;


    if not SameText(list.Items[i].SubItems[0], s) then
      list.Items[i].SubItems[0] := s;
  end;

end;


function TVisDataboardWndForm.isChannelParameter(i: integer): boolean;
begin
 result := (par[i].Trans <> nil) and (length(par[i].Trans)>0) and (par[i].Trans[0].Oper='A');
end;


function TVisDataboardWndForm.hasChannelParameters: boolean;
var
  i: Integer;
begin
  result := false;
  for i:=0 to length(par)-1 do
    if isChannelParameter(i) then begin
      Result := true;
      Break;
    end;
end;


// Ввод значения параметра
procedure TVisDataboardWndForm.listDblClick(Sender: TObject);
var
  i,j,q: integer;
  s: string;
  v,orig_val: Int64;
  r: double;
  w: TVisDataboardWndParEnumForm;
  mes1,mes2,mes3: string;
  hasTrans, hasEnum, typeString, typeDouble: boolean;
begin
  if (M = nil) or (list.Selected = nil) or (M.AccessReadonly) then
    exit;

  i := list.Selected.Index;
  if par[i].Tagname='' then
    exit;





  mes1 := trim(M.Caption);

  mes2 := '';
  for j:=i-1 downto 0 do
    if par[j].Tagname='' then begin
      mes2 := trim(par[j].Text);
      break;
    end;
  mes3 := trim(par[i].Text);


  if isModeUserEx then
    if not isPermitted(M.ClassName + '~' + mes1 + '~' + mes2 + '~' + mes3) then
      Exit;

  if par[i].AdminAccess>0 then begin
    if isModeUserEx then begin
//      if not isPermitted(M.ClassName + '~Supervisor') then
//        exit;
    end else
      if not CheckAccess(PREVILEG_PRM_SPEC1, true) then
        exit;
  end;


  if (mes3<>'') and (mes2<>'') then
      mes3 := ' - ' + mes3;





  hasTrans := par[i].Trans <> nil;
  hasEnum := par[i].Enum <> nil;
  typeString := getTagType(par[i].tag) = TAGTYPE_STRING;
//  typeInt := getTagType(par[i].tag) = TAGTYPE_INT;
  typeDouble := getTagType(par[i].tag) = TAGTYPE_DOUBLE;

  // Адрес
  if par[i].Trans <> nil then
    for j:=0 to length(par[i].Trans)-1 do
      if par[i].Trans[j].Oper = 'A' then begin
        showChannelInput( GetTagName(par[i].tag), mes1+mes3);
        exit;
      end;



  if ((hasTrans) or (hasEnum)) and (not typeString) then begin
    // Значение
    if par[i].Enum = nil then begin
      s := list.Selected.SubItems[0];
      if not ShowInputDialog(Caption, par[i].Text, s) then exit;
      val(s,r,q);

    // Перечисление
    end else begin
      w := TVisDataboardWndParEnumForm.Create(self);
      w.Caption := Caption;
      w.lb.Caption := par[i].Text;

      v := -1;
      for j:=0 to length(par[i].Enum)-1 do begin
//        w.cmb.Items.Add(par[i].Enum[j].Text);
        w.listbox.AddItem(par[i].Enum[j].Text, nil);
        if par[i].Enum[j].Text = list.Selected.SubItems[0] then
          v := j;
      end;
//      w.cmb.ItemIndex := v;
      w.listbox.ItemIndex := v;

      if w.ShowModal<>mrOk then
        exit;

//      v := w.cmb.ItemIndex;
//      s := w.cmb.Text;

      v := w.listbox.ItemIndex;
      s := w.listbox.Items[v];

      if (v<0) or (v>=length(par[i].Enum)) then
        exit;
      r := par[i].Enum[v].Value;

      w.Free;
    end;

    // Преобразование
    orig_val := GetTagValueLong(par[i].tag);
    if par[i].Trans <> nil then begin
      for j:=length(par[i].Trans)-1 downto 0 do begin
        case par[i].Trans[j].Oper of
          '*': r :=r / par[i].Trans[j].Value;
          '/': r :=r * par[i].Trans[j].Value;
          '+': r :=r - par[i].Trans[j].Value;
          '-': r :=r + par[i].Trans[j].Value;
          '&': r :=(orig_val and (not trunc(par[i].Trans[j].Value))) + r;
          '|': ;
          'b': begin
                 v := GetTagValue(par[i].tag);
                 if r=0 then
                   r := v and not(1 shl trunc(par[i].Trans[j].Value))
                 else
                   r := v or (1 shl trunc(par[i].Trans[j].Value));
               end;
        end;
      end;
    end;
    v := round(r);

    // Запись
    if typeDouble then
      SetTagValueDouble(par[i].tag, r)
    else
      SetTagValueLong(par[i].tag, v);
  end else begin
    s := list.Selected.SubItems[0];
    if not ShowInputDialog(Caption, par[i].Text, s) then
      exit;
    SetTagValueString(par[i].tag, s);
  end;
  SaveMessageText(mcParam_text, mes1, mes2+mes3 + ' = ' + s, mcParam_bc, mcParam_fc);

end;

procedure TVisDataboardWndForm.btTrendClick(Sender: TObject);
var
  i, j: Integer;
  tagnames: string;
  descrs: string;
  s: string;
  pr: PVisDataboardParRec;
  tr: PVisDataboardParTransRec;
begin
  tagnames := '';
  descrs := '';

  for i := 0 to Length(par) - 1 do begin
    pr := par[i];
    if pr.tag = -1 then
      continue;

    tagnames := tagnames + ';' + GetTagName(pr.tag);
    if Length(pr.Trans) > 0 then begin
      tagnames := tagnames + '~CAST(^ AS FLOAT)';
      for j := 0 to Length(pr.Trans) - 1 do begin
        tr := pr.Trans[j];
        tagnames := tagnames + tr.Oper + FloatToStr(tr.Value);
      end;
    end;

    s := par[i].Text;
    ReplaceS(s, ';', ' ');
    descrs := descrs + ';' + s;
  end;

  if tagnames <> '' then begin
    Delete(tagnames, 1, 1);
    Delete(descrs, 1, 1);
    ShowTrendWindow(Caption, tagnames, descrs, 'visdataboard_' + M.HelpKeyword, '', '');
  end;
  close;
//  PostMessage(Handle, WM_CLOSE, 0, 0);
end;

end.
