unit VisMachine;

interface

uses
  DesignIntf, DesignEditors, 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, stdctrls, ScadaBase;



type
  PVisMachineDat =^TVisMachineDat;
  TVisMachineDat = record
    Name: string[32];
    TagPostfix: string[16];
    Shape: TShape;
    ColorScheme: byte;
    TagIdxOutput: integer;
    TagIdxAlarm: integer;
    TagIdxBlok: integer;
    valSost: integer;
  end;

  TVisMachine = class(TCustomScadaObject)
  private
    FBckgFlicker: boolean;

    FData: TStringList;

    FDevTypeMCTL: string;

    procedure SetupData(Value: TStringList);
    procedure LoadSetupData;
    procedure SaveSetupData;
  protected
    FImagePic1: TPicName;
    FImagePic2: TPicName;
    FImagePic3: TPicName;
    FImagePic4: TPicName;

    FTagMctlControl: integer;
    FTagMachSost: integer;
    FTagMachBlok: integer;

    FCurSost: integer;
    FCurMode: integer;
    FCurBlok: integer;

    FHideControl: boolean;

    procedure SetImageSost(Value: integer);
    procedure SetFrameMode(Value: integer);
    procedure SetFrameBlok(Value: integer);
    procedure SetDatColor(idx, value: integer);

    procedure Loaded; override;
  public
    TagName: TTagName;
    DeviceName: TDeviceName;
    ImagePic: TPicName;

    ImageObj: TImage;
    LabelObj: TLabel;
    FrameModeObj: TShape;
    FrameBlokObj: TShape;

    DatCount: integer;
    Datchik: array of PVisMachineDat;
    OnlyDat: boolean;

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

    procedure Init; override;
    procedure Process; override;

    procedure FrameUpdate;
    procedure FreeDatchiks;

    function expandTagname: string;

  published
    property Data: TStringList read FData write SetupData;
    property HideControl: boolean read FHideControl write FHideControl;
    property DevTypeMCTL: string read FDevTypeMCTL write FDevTypeMCTL;
    property BckgFlicker: boolean read FBckgFlicker write FBckgFlicker;
  end;

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



procedure Register;

implementation
uses
  TagStorage,
  RpVisualGlobal,
  RpVisualUtils,
  numbers,
  ExtDlgs,
  VisMachineSetup;

procedure Register;
begin
  RegisterComponents('Scada', [TVisMachine]);
  RegisterComponentEditor(TVisMachine, TVisMachineEditor);
end;

{ TVisMachine }

constructor TVisMachine.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FEraseBackground := true;
  FBckgFlicker := false;

  Width := 100;
  Height := 100;
//  ShowHint := true;

  FData := TStringList.Create;

  Datchik := nil;
  DatCount := 0;

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

  // Создание рамки Mode
  FrameModeObj := TShape.Create(self);
  FrameModeObj.Parent := self;
  FrameModeObj.Visible := true;
  FrameModeObj.Pen.Color := clAqua;
  FrameModeObj.Brush.Color := clRed;
  FrameModeObj.Brush.Style := bsClear;
  FrameModeObj.Cursor := crHandPoint;
  FrameModeObj.ShowHint := true;

  // Создание рамки Blok
  FrameBlokObj := TShape.Create(self);
  FrameBlokObj.Parent := self;
  FrameBlokObj.Visible := true;
  FrameBlokObj.Brush.Style := bsClear;
  FrameBlokObj.Pen.Color := clYellow;
  FrameBlokObj.Cursor := crHandPoint;
  FrameBlokObj.ShowHint := true;

  // Создание метки
  LabelObj := TLabel.Create(Self);
  LabelObj.Parent := self;
  LabelObj.Visible := true;
  LabelObj.Transparent := true;
  LabelObj.Font.Color := clWhite;
  LabelObj.Font.Size := 10;
  LabelObj.Font.Style := [fsBold];
  LabelObj.Font.Name := 'Tahoma';
  LabelObj.Cursor := crHandPoint;
  LabelObj.ShowHint := true;

  ImageObj.OnClick := OnDeviceClick;
  LabelObj.OnClick := OnDeviceClick;
  FrameModeObj.OnMouseUp := OnDeviceMouseUp;
  FrameBlokObj.OnMouseUp := OnDeviceMouseUp;

  FTagMctlControl := -1;
  FTagMachSost := -1;
  FTagMachBlok  := -1;

  FCurSost := -1;
  FCurMode := -1;
  FCurBlok := -1;

  FDevTypeMCTL := 'MCTL';
end;

destructor TVisMachine.Destroy;
begin
  FreeDatchiks;
  inherited;
end;

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


procedure TVisMachine.SetImageSost(Value: integer);
begin
  if OnlyDat then begin
    // do nothing
  end
  else if (Value <> FCurSost) and (Value>=0) then try
    case value of
      0:   ImageObj.Picture.LoadFromFile(VisImagesPath + FImagePic1);
      1:   ImageObj.Picture.LoadFromFile(VisImagesPath + FImagePic2);
      4,8: ImageObj.Picture.LoadFromFile(VisImagesPath + FImagePic3);
    else
      ImageObj.Picture.LoadFromFile(VisImagesPath + FImagePic4);
    end;
    FCurSost := Value;
    RepaintRect(ImageObj);

//    if (FBckgFlicker) and (Visible) then begin
//      RepaintRect(ImageObj);
//    end;
  except
    LabelObj.Caption := 'PIC';
  end;
end;


procedure TVisMachine.SetFrameMode(Value: integer);
begin
  if OnlyDat then exit;
  if (Value <> FCurMode) then begin
    if (value) > 0 then
      FrameModeObj.Pen.Color := clAqua
    else
      FrameModeObj.Pen.Color := Color;
    FCurMode := Value;
  end;
end;

procedure TVisMachine.SetFrameBlok(Value: integer);
begin
  if (Value <> FCurBlok) then begin
    if (value) > 0 then
      FrameBlokObj.Pen.Color := clYellow
    else
      FrameBlokObj.Pen.Color := color;
    FCurBlok := Value;
  end;
end;

procedure TVisMachine.SetDatColor(idx, value: integer);
begin
  if (value <> Datchik[idx].valSost) or (value>0) then begin

    if Datchik[idx].ColorScheme = 0 then begin
      case value of
      0: Datchik[idx].Shape.Brush.Color := clAqua;

      1: Datchik[idx].Shape.Brush.Color := clYellow;

      2: Datchik[idx].Shape.Brush.Color := clRed;

      3: if Datchik[idx].Shape.Brush.Color = clRed then
           Datchik[idx].Shape.Brush.Color := clYellow
         else
           Datchik[idx].Shape.Brush.Color := clRed;
      end;
    end;

    if Datchik[idx].ColorScheme = 1 then begin
      if value = 0 then
        Datchik[idx].Shape.Brush.Color := clAqua
      else
        Datchik[idx].Shape.Brush.Color := clYellow;
    end;



    if Datchik[idx].ColorScheme = 3 then begin
      case value of
      0: Datchik[idx].Shape.Brush.Color := clAqua;

      1: Datchik[idx].Shape.Brush.Color := clYellow;

      2: Datchik[idx].Shape.Brush.Color := clRed;

      3: if Datchik[idx].Shape.Brush.Color = clRed then
           Datchik[idx].Shape.Brush.Color := clYellow
         else
           Datchik[idx].Shape.Brush.Color := clRed;
      end;
    end;



    Datchik[idx].valSost := value;
  end;

end;


// INIT
procedure TVisMachine.Init;
var
  i: integer;
  _tagname: string;
begin
  inherited;

  _tagname := expandTagname;

    if OnlyDat then
      IsConnectionBad := false
    else begin
      FTagMctlControl := GetTagIndex(TagPath + '.' + FDevTypeMCTL + '_' + _TagName + '_Control');
      FTagMachSost := GetTagIndex(TagPath + '.MACH_' + _TagName + '_Sost');
      FTagMachBlok := GetTagIndex(TagPath + '.MACH_' + _TagName + '_Blok');
      IsConnectionBad := (FTagMctlControl<0) or (FTagMachSost<0) or (FTagMachBlok<0);
    end;

    for i:=0 to DatCount-1 do with Datchik[i]^ do begin
      TagIdxOutput := GetTagIndex(format('%s.RKSV_%s%s_Output',
          [TagPath, _TagName, TagPostfix]));

      TagIdxAlarm := GetTagIndex(format('%s.RKSV_%s%s_Alarm',
          [TagPath, _TagName, TagPostfix]));

      TagIdxBlok := GetTagIndex(format('%s.RKSV_%s%s_Blok',
          [TagPath, _TagName, TagPostfix]));

      IsConnectionBad := (IsConnectionBad) or (TagIdxOutput<0) or
          (TagIdxAlarm<0) or (TagIdxBlok<0);
    end;

  if OnlyDat then begin
    try
      ImageObj.Picture.LoadFromFile(VisImagesPath + ImagePic);
    except
      LabelObj.Caption := 'PIC';
    end;
  end;  

end;

// PROCESS HANDLER
procedure TVisMachine.Process;
var
  value, valOutput, valAalarm, blokval, i: integer;
begin

    IsValueBad := false;

    if OnlyDat then begin
      SetImageSost(0);
      blokval := 0;
    end else begin
      value := GetTagValue(FTagMachSost);
      SetImageSost(value);
      IsValueBad := IsValueBad or (value < 0);

      value := GetTagValue(FTagMctlControl);
      SetFrameMode(value);
      IsValueBad := IsValueBad or (value < 0);

      value := GetTagValue(FTagMachBlok);
      IsValueBad := IsValueBad or (value < 0);
      blokval := value;
    end;

    for i:=0 to DatCount-1 do begin
      valOutput := GetTagValue(Datchik[i].TagIdxOutput);
      valAalarm := GetTagValue(Datchik[i].TagIdxAlarm);
      value := GetTagValue(Datchik[i].TagIdxBlok);
      IsValueBad := IsValueBad or (valOutput < 0) or (valAalarm < 0) or (value < 0);
      blokval := blokval or value;

      SetDatColor(i, valOutput * 2 + valAalarm);
    end;

    SetFrameBlok(blokval);

  inherited;

end;



procedure TVisMachine.LoadSetupData;
var
  s, s1, s2: string;
  k, i, j, v: integer;
begin

  // 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;
  ImageObj.Hint := Hint;
  FrameModeObj.Hint := Hint;
  FrameBlokObj.Hint := Hint;
  LabelObj.Hint := Hint;


  // Image
  if FData.Count>1 then s := FData.Strings[1] else s := '';
  k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  ImageObj.Left := v;

  delete(s, 1, k); k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  ImageObj.Top := v;

  delete(s, 1, k); k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  ImageObj.Width := v;

  delete(s, 1, k); k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  ImageObj.Height := v;

  delete(s, 1, k);
  ImagePic := trim(s);

  s := VisImagesPath + ImagePic;
  try
    if FileExists(s) then ImageObj.Picture.LoadFromFile(s);
  except
  end;


  // Label
  if FData.Count>2 then s := FData.Strings[2] else s := '';
  k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  LabelObj.Left := v;

  delete(s, 1, k); k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  LabelObj.Top := v;

  delete(s, 1, k);
  LabelObj.Caption := trim(s);

  // Обновление рамок
  FrameUpdate;

  // Загрузка датчиков
  FreeDatchiks;
  if FData.Count>3 then s := FData.Strings[3] else s := '';
  k := pos(',', s);
  val(copy(s, 1, k-1), v, j);
  DatCount := v;
  SetLength(Datchik, DatCount);

  delete(s, 1, k);
  val(s, v, j);
  OnlyDat := (v=1);

  for i:=0 to DatCount-1 do begin
    new( Datchik[i] );
    Datchik[i].Shape := TShape.Create(Self);
    Datchik[i].Shape.Parent := self;
    Datchik[i].Shape.Visible := true;
    Datchik[i].Shape.Brush.Color := clAqua;
    Datchik[i].Shape.Cursor := crHandPoint;
    Datchik[i].Shape.ShowHint := true;
    Datchik[i].Shape.OnMouseUp := OnDeviceMouseUp;

    if FData.Count>4+i then s := FData.Strings[4+i] else s := '';
    k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].Shape.Left := v;

    delete(s, 1, k); k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].Shape.Top := v;

    delete(s, 1, k); k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].Shape.Width := v;

    delete(s, 1, k); k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].Shape.Height := v;

    delete(s, 1, k); k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].Shape.Shape := TShapeType(v);

    delete(s, 1, k); k := pos(',', s);
    val(copy(s, 1, k-1), v, j);
    Datchik[i].ColorScheme := v;

    delete(s, 1, k); k := pos(',', s);
    Datchik[i].TagPostfix := copy(s, 1, k-1);

    delete(s, 1, k);
    Datchik[i].Name := trim(s);
    Datchik[i].Shape.Hint := Hint + ' ' + Datchik[i].Name;

    Datchik[i].TagIdxOutput := -1;
    Datchik[i].TagIdxAlarm := -1;
    Datchik[i].TagIdxBlok := -1;

    Datchik[i].valSost := -1;

  end;

  // имена картинок
  s2 := ImagePic;
  i := pos('_',s2);
  s1 := copy(s2, 1, i);
  delete(s2, 1, i+1);

  FImagePic1 := s1 + '1' + s2;
  FImagePic2 := s1 + '2' + s2;
  FImagePic3 := s1 + '3' + s2;
  FImagePic4 := s1 + '4' + s2;

  if OnlyDat then begin
    FrameModeObj.Visible := false;
  end;
end;

procedure TVisMachine.SaveSetupData;
var
  i: integer;
//  s: string;
begin
  FData.Clear;
  FData.Add( TagPath + '.' + TagName + ';' + DeviceName );
  FData.Add( format('%d,%d,%d,%d,%s', [
      ImageObj.Left, ImageObj.Top, ImageObj.Width, ImageObj.Height, ImagePic]) );
  FData.Add( format('%d,%d,%s', [LabelObj.Left, LabelObj.Top, LabelObj.Caption]) );

  FData.Add(IntToStr(DatCount) + ',' + iif(OnlyDat,'1','0'));
  for i:=0 to DatCount-1 do
    FData.Add( format('%d,%d,%d,%d,%d,%d,%s,%s', [
        Datchik[i].Shape.Left, Datchik[i].Shape.Top,
        Datchik[i].Shape.Width, Datchik[i].Shape.Height,
        ord(Datchik[i].Shape.Shape), Datchik[i].ColorScheme,
        Datchik[i].TagPostfix, Datchik[i].Name
        ]));
        
end;


procedure TVisMachine.Loaded;
begin
  inherited;
  LoadSetupData;

  // forbidden properties
  Cursor := crDefault;
  ShowHint := false;
end;


procedure TVisMachine.FrameUpdate;
begin
  FrameModeObj.Left   := LabelObj.Left   - ModeMarginX;
  FrameModeObj.Top    := LabelObj.Top    - ModeMarginY;
  FrameModeObj.Width  := LabelObj.Width  + 2*ModeMarginX;
  FrameModeObj.Height := LabelObj.Height + 2*ModeMarginY;

  FrameBlokObj.Left   := LabelObj.Left   - BlokMarginX;
  FrameBlokObj.Top    := LabelObj.Top    - BlokMarginY;
  FrameBlokObj.Width  := LabelObj.Width  + 2*BlokMarginX;
  FrameBlokObj.Height := LabelObj.Height + 2*BlokMarginY;
end;

procedure TVisMachine.FreeDatchiks;
var
  i: integer;
begin
  for i:=0 to DatCount-1 do begin
    Datchik[i].Shape.Free;
    Dispose(Datchik[i]);
  end;
  Datchik := nil;
end;



function TVisMachine.expandTagname: string;
begin
  result := repl(TagName, TagIdRoot, TagRoot);
end;





{ TVisMachineEditor }

procedure TVisMachineEditor.Edit;
begin
  inherited;
  VisMachineSetupForm := TVisMachineSetupForm.Create(nil);

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

  VisMachineSetupForm.Free;
end;




end.
