unit VisSeqc;



interface

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

const
  TAGNAME_BTN_START = 'BTN_^_Start_Input';
  TAGNAME_BTN_STOP  = 'BTN_^_Stop_Input';
  TAGNAME_BTN_EMRG  = 'BTN_^_Emrg_Input';

type
  PSeqcDeviceRec =^TSeqcDeviceRec;
  TSeqcDeviceRec = record
    Addr: integer;
    Name: string;
  end;

  TSeqcRootDevice = class
    Addr: integer;
    Name: string;
    devtype: string;
    tagname: string;
//    soleOwner: Boolean;
  end;

  TVisSeqc = class(TCustomScadaObject)
  private
    procedure KillDevices;
    procedure addToRepo;
    procedure removeFromRepo;
    procedure ClickButton(Sender: TObject);
  protected
    FTagname_Seqc: string;
    FTagname_StartDelays: string;
    FTagname_StopDelays: string;
    FUseTagnameSeqc: boolean;
    FDevType_StartDelays: string;
    FDevType_StopDelays: string;

    FFilterAdd: string;
    FFilterDel: string;

    FLabel: TLabel;
    FPanel: TPanel;
    FBevel: TBevel;
    FButton: TButton;

    FDBNameParam: string;

    FTagSost: integer;
    FTagCurrent: integer;
    FTagCode: integer;

    FFirstPass: boolean;
    FSecondPass: boolean;

  public
    Devices: array of PSeqcDeviceRec;
    RootDevices: array of TSeqcRootDevice;
    DBName: string;
    Code: integer;
    Sost: integer;
    Current: integer;
    DatabaseConnectOk: boolean;

    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Init; override;
    procedure Process; override;
    procedure ButtonClick(sender: TObject);

    procedure LoadDevicesFromDatabase;
    procedure GetSeqNameFromDatabase;
    procedure KillRootDevices;
    function AddRootDevices: TSeqcRootDevice;

    function CheckRootDevicesBeforeStart: Boolean; 

  published
    property objLabel: TLabel read FLabel;
    property objPanel: TPanel read FPanel;
    property objBevel: TBevel read FBevel;
    property objButton: TButton read FButton;

    property Tagname_Seqc: string read FTagname_Seqc write FTagname_Seqc;
    property Tagname_StartDelays: string read FTagname_StartDelays write FTagname_StartDelays;
    property Tagname_StopDelays: string read FTagname_StopDelays write FTagname_StopDelays;
    property UseTagnameSeqc: boolean read FUseTagnameSeqc write FUseTagnameSeqc;

    property DevType_StartDelays: string read FDevType_StartDelays write FDevType_StartDelays;
    property DevType_StopDelays: string read FDevType_StopDelays write FDevType_StopDelays;

    property DBNameParam: string read FDBNameParam write FDBNameParam;

    property FilterAdd: string read FFilterAdd write FFilterAdd;
    property FilterDel: string read FFilterDel write FFilterDel;

    property Align;
    property Anchors;
    property Enabled;
    property Visible;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
  end;


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


  function findSeqc(TagRoot: string; var seqc: TVisSeqc): boolean; overload;
  function findSeqc(Sender: TObject; var seqc: TVisSeqc): boolean; overload;
  procedure ClickButtonSeqc(Sender: TObject);

  procedure Register;

implementation
uses
  MesConst,
  infodlg,
  MesLogging,
  VisSeqCCheckBS,
  RpVisualUtils,
  RpVisualParams,
  RpVisualGlobal,
  VisSeqcSetup,
  VisSeqcConfig,
  TagStorage,
  numbers,
  hyperstr, DB;

var
  SeqcRepo: TStringList;


procedure Register;
begin
  RegisterComponents('Scada', [TVisSeqc]);
  RegisterComponentEditor(TVisSeqc, TVisSeqcEditor);
end;

{ TVisualDevice }


constructor TVisSeqc.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ShowHint := true;

  Width := 437;
  Height := 68;

  // Создание shape
  FBevel := TBevel.Create(Self);
  FBevel.Name := 'objBevel';
  FBevel.Parent := self;
  FBevel.Visible := true;
  FBevel.SetSubComponent(true);
  FBevel.Align := alClient;
  FBevel.Shape := bsFrame;

  // Создание метки
  FLabel := TLabel.Create(Self);
  FLabel.Name := 'objLabel';
  FLabel.Parent := self;
  FLabel.Visible := true;
  FLabel.SetSubComponent(true);
//  FLabel.Caption := 'Маршрут не выбран';
  FLabel.Font.Name := 'Tahoma';
  FLabel.Font.Size := 10;
  FLabel.Font.Style := [fsBold];
  FLabel.Left := 12;
  FLabel.Top := 8;
  FLabel.ShowHint := true;
//  FLabel.AutoSize := false;
//  FLabel.Width := 348;
//  FLabel.WordWrap := false;
//  FLabel.Anchors := [akTop, akLeft, akRight];
//  FLabel.Transparent := false;
//  FLabel.Color := clAqua;

  // Создание панели
  FPanel := TPanel.Create(Self);
  FPanel.Name := 'objPanel';
  FPanel.Caption := '';
  FPanel.Parent := self;
  FPanel.Visible := true;
  FPanel.SetSubComponent(true);
  FPanel.Anchors := [akLeft, akTop, akRight];
  FPanel.BevelOuter := bvLowered;
  FPanel.Font.Style := [fsBold];
  FPanel.Height := 25;
  FPanel.Left := 11;
  FPanel.Top := 31;
  FPanel.Width := 414;

  FButton := TButton.Create(Self);
  FButton.Name := 'objButton';
  FButton.Parent := self;
  FButton.Visible := true;
  FButton.SetSubComponent(true);
  FButton.Caption := 'Выбрать';
  FButton.Font.Name := 'Tahoma';
  FButton.Font.Size := 7;
  FButton.Left := 364;
  FButton.Top := 8;
  FButton.Width := 61;
  FButton.Height := 19;
  FButton.Anchors := [akTop, akRight];
  FButton.OnClick := ButtonClick;
  FButton.Enabled := false;
  
  FUseTagnameSeqc := true;
  FDBNameParam := '';

  DevType_StartDelays := 'BYTE';
  DevType_StopDelays := 'BYTE';

  ShowHint := false;

  Devices := nil;
end;


destructor TVisSeqc.Destroy;
begin
  removeFromRepo;
  KillDevices;
  KillRootDevices;

  inherited;
end;

procedure TVisSeqc.KillDevices;
var
  i: integer;
begin
  for i:=0 to length(Devices)-1 do
    dispose(Devices[i]);
  Devices := nil;
end;


procedure TVisSeqc.KillRootDevices;
var
  i: integer;
begin
  for i:=0 to length(RootDevices)-1 do
    RootDevices[i].Free;
  RootDevices := nil;
end;


function TVisSeqc.AddRootDevices: TSeqcRootDevice;
var
  i: Integer;
begin
  i := length(RootDevices);
  SetLength(RootDevices, i+1);
  RootDevices[i] := TSeqcRootDevice.Create;
  result := RootDevices[i];
end;


// INIT
procedure TVisSeqc.Init;
var
  _tagname: string;
begin
  inherited;

  _tagname := repl(FTagname_Seqc, TagIdRoot, TagRoot);

  FTagSost := GetTagIndex(TagPath + '.SEQC_' + _TagName + '_Sost');
  FTagCurrent := GetTagIndex(TagPath + '.SEQC_' + _TagName + '_Current');
  FTagCode := GetTagIndex(TagPath + '.SEQC_' + _TagName + '_Code');

  DBNameParam := trim(DBNameParam);
  if DBNameParam='' then
    DBName := ''
  else
    DBName := rpVisualParams_Read( DBNameParam, 'База данных RoboPlant проекта', '');

  FFirstPass := true;
  FSecondPass := true;

  Code := 0;
  Sost := -1;
  Current := -1;

  addToRepo;
end;

// PROCESS HANDLER
procedure TVisSeqc.Process;
var
  i,j: integer;
begin
  if (not rvgLogIn) or (databaseDisabled) then
    exit;

  if FFirstPass then begin
    // Подключение к БД и считывание девайсов

    DatabaseConnectOk := VisSeqcConfigFormCreate(self, DBName);

    if DatabaseConnectOk then begin
      objPanel.Caption := '';

      // чтение девайсов
      LoadDevicesFromDatabase;

      // чтение девайсов
      GetSeqNameFromDatabase;

    end else
      objPanel.Caption := 'Ошибка подключения к "' + DBName + '"';

    FFirstPass := false;
  end else

  if FSecondPass then begin
    // Отключение от БД
    VisSeqcConfigFormFree;
    FSecondPass := false;
    FButton.Enabled := true;
  end;


  if (not FFirstPass) and (not FSecondPass) then begin

    // root name
    i := GetTagValue(FTagCode);
    if Code <> i then begin
      Code := i;
      VisSeqcLoadRootName(self, Code);
    end;

    // sost + current
    i := GetTagValue(FTagSost);
    j := GetTagValue(FTagCurrent);
    if (Sost<>i) or (Current<>j) then begin
      Sost := i;
      Current := j;
      case Sost of
        0: objPanel.Caption := 'Выключен';
        1: objPanel.Caption := 'Запуск - ';
        2: objPanel.Caption := 'Работает';
        3: objPanel.Caption := 'Останов - ';
        4: objPanel.Caption := 'Авария - ';
        5: objPanel.Caption := 'Экстренный стоп';
        6: objPanel.Caption := 'Занято - ';
      else
        objPanel.Caption := '';
      end;

      if Sost in [1,3,4,6] then begin
        j:=-1;
        for i:=0 to length(Devices)-1 do
          if Devices[i].Addr = Current then begin
            j:=i;
            break;
          end;
        if j=-1 then
          objPanel.Caption := objPanel.Caption + 'неизвестное устройство ADDR=' + inttostr(Current)
        else
          objPanel.Caption := objPanel.Caption + devices[j].Name;
      end;

//      objButton.Enabled := Sost=0;

      case Sost of
        4,6: objPanel.Color := clRed;
        1..3: objPanel.Color := clLime;
      else
        objPanel.Color := clBtnFace
      end;

      if (Code=0) or (Code=65535) then begin
        objPanel.Caption := '';
//        objLabel.Font.Color := clBtnShadow;
      end else begin
        objLabel.Font.Color := clBtnText;
      end;


    end;
  end;


end;


procedure TVisSeqc.LoadDevicesFromDatabase;
var
  n: integer;
begin
  KillDevices;
  n:=0;
  with VisSeqcConfigForm.ds2 do begin
    try
      VisSeqcOpenDevices(FilterAdd, FilterDel);

      while not Eof do begin
        SetLength(Devices, n+1);
        new(Devices[n]);
        Devices[n].Addr := fieldbyname('ADDR').AsInteger;
        Devices[n].Name := fieldbyname('NAME').AsString;
        inc(n);
        Next;
      end;

      Close;
    except
      Transaction.Active := false;
    end;
  end;
end;


procedure TVisSeqc.GetSeqNameFromDatabase;
begin

end;


procedure TVisSeqc.ButtonClick(sender: TObject);
begin
  if not rvgLogIn then exit;

  if VisSeqcConfigFormCreate(self, DBName) then begin
    VisSeqcOpenDevices(FilterAdd, FilterDel);
    VisSeqcOpenDatasets;
    VisSeqcConfigForm.dsCurM.Locate('CODE', Code, []);
    VisSeqcConfigForm.FormStyle := fsStayOnTop;
    if Sost>0 then begin
      VisSeqcConfigForm.Visible := false;
      VisSeqcConfigForm.acCurSet.Enabled := false;
      VisSeqcConfigForm.acReset.Enabled := false;
    end;
    VisSeqcConfigForm.Show;

    if (Code>0) and (Code<65535) then
      VisSeqcConfigForm.acCheckExecute(nil);

  end;
end;



function TVisSeqc.CheckRootDevicesBeforeStart: Boolean;
begin
//
//  Result := false;
  VisSeqCCheckBSForm := TVisSeqCCheckBSForm.Create(nil);
  try
    VisSeqCCheckBSForm.M := Self;
    if VisSeqCCheckBSForm.buildList > 0 then
      Result := VisSeqCCheckBSForm.ShowModal = mrOk
    else
      Result := true;
  finally
    VisSeqCCheckBSForm.Free;
  end;
end;



{ TVisSeqcEditor }

procedure TVisSeqcEditor.Edit;
begin
  inherited;
  VisSeqcSetupForm := TVisSeqcSetupForm.Create(nil);
  VisSeqcSetupForm.M := Component as TVisSeqc;
  VisSeqcSetupForm.D := Self;
  VisSeqcSetupForm.ShowModal;
  VisSeqcSetupForm.Free;
end;



function findSeqc(TagRoot: string; var seqc: TVisSeqc): boolean;
var
  i: Integer;
begin
  Result := SeqcRepo.Find(TagRoot, i);
  if Result then
    seqc := SeqcRepo.Objects[i] as TVisSeqc;
end;

function findSeqc(Sender: TObject; var seqc: TVisSeqc): boolean;
begin
  if Sender is TCustomScadaObject then
    result := findSeqc((Sender as TCustomScadaObject).TagRoot, seqc)
  else
  if Sender is TControl then
    result := findSeqc((Sender as TControl).HelpKeyword, seqc)
  else
    result := false;
end;

procedure ClickButtonSeqc(Sender: TObject);
var
  seqc: TVisSeqc;
begin
  if findSeqc(Sender, seqc) then
    seqc.ClickButton(Sender);
end;


procedure TVisSeqc.ClickButton(Sender: TObject);
var
  bt: TButton;
  text: string;
  tagname: string;
  fc: Integer;
begin
  if (Code <= 0) or not(sender is TButton) then
    Exit;
  bt := sender as TButton;

  if AnsiSameText(bt.Caption, 'Старт') then begin
    if Sost = 0 then
      text := 'Запуск'
    else
    if Sost = 4 then
      text := 'Повторный запуск'
    else
      exit;
    tagname := TAGNAME_BTN_START;
    fc := mcSeqStart_fc;
  end
  else
  if AnsiSameText(bt.Caption, 'Стоп') then begin
    if Sost = 0 then
      exit;
    text := 'Останов';
    tagname := TAGNAME_BTN_STOP;
    fc := mcSeqStop_fc;
  end
  else
  if AnsiSameText(bt.Caption, 'ЭкстрСтоп') then begin
    if Sost = 0 then
      exit;
    text := 'Экстренный останов';
    tagname := TAGNAME_BTN_EMRG;
    fc := mcSeqEmrg_fc;
  end
  else begin
    ShowMessage(bt.Caption + ' - неправильный Caption кнопки. Признаю только такие: Старт, Стоп и ЭкстрСтоп');
    exit;
  end;

//  text := text + ' маршрута "' + objLabel.Caption + '"';
  tagname := TagPath + '.' + repl(tagname, '^', TagRoot);

  if showInfoDlg('Выполнить ' + text + ' маршрута ' + TagRoot + ' ' + objLabel.Caption + '?', mtConfirmation, [mbYes, mbNo], 0) <> mrYes then
    Exit;

  SetTagValueByName(tagname, 1);

  SaveMessageText(
    'Маршрут ' + TagRoot,
    objLabel.Caption,
    text,
    mcSeq_bc, fc);


end;


procedure TVisSeqc.addToRepo;
begin
  SeqcRepo.AddObject(TagRoot, self);
end;

procedure TVisSeqc.removeFromRepo;
var
  i: integer;
begin
  if SeqcRepo.Find(TagRoot, i) then
    SeqcRepo.Delete(i);
end;


initialization
  SeqcRepo := TStringList.Create;
  SeqcRepo.Sorted := true;
  SeqcRepo.Duplicates := dupIgnore;

finalization
  SeqcRepo.Free;

end.
