unit VisMchbControlPanel;

interface

uses
  MotohrPanel,
  NotesCtrl,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, VisMchb, ExtCtrls, ScadaBase, VisMachine, VisMcha, VisCheckBox,
  StdCtrls, VisButton, VisLabel, Buttons, VisDataboard, VisMchbMdtaControlPanel,
  VisControl;


type
  TVisMchbControlPanelForm = class(TForm)         
    ScadaUpdateTimer: TTimer;
    pnlControl: TPanel;
    btAuto: TVisButton;
    btManOn: TVisButton;
    btManOff: TVisButton;
    Label4: TLabel;
    Label5: TLabel;
    Bevel1: TBevel;
    pnlDeviceName: TPanel;
    pnlSost: TPanel;
    pnlBlok: TPanel;
    Bevel2: TBevel;
    vlbSost: TVisLabel;
    Label2: TLabel;
    Bevel5: TBevel;
    vlbPlata: TVisLabel;
    btBlok: TVisButton;
    btReset: TVisButton;
    btCrash: TVisButton;
    Label3: TLabel;
    pnlParams: TPanel;
    Bevel4: TBevel;
    dbrdParams: TVisDataboard;
    btParams: TBitBtn;
    Label1: TLabel;
    pnlNotes: TPanel;
    btResetSensors: TBitBtn;
    pnlMotohr: TPanel;
    Bevel3: TBevel;
    SpeedButton1: TSpeedButton;
    procedure ScadaUpdateTimerTimer(Sender: TObject);

    procedure FormActivate(Sender: TObject);
    procedure VisButtonDownChange(Sender: TObject);
    procedure btParamsClick(Sender: TObject);
    procedure VisButtonClick(Sender: TObject);
    procedure pnlDeviceNameClick(Sender: TObject);
    procedure btResetCanClick(Sender: TObject; var Can: Boolean);
    procedure btAutoCanClick(Sender: TObject; var Can: Boolean);
    procedure btManOnCanClick(Sender: TObject; var Can: Boolean);
    procedure btBlokCanClick(Sender: TObject; var Can: Boolean);
    procedure btManOffCanClick(Sender: TObject; var Can: Boolean);
    procedure btCrashCanClick(Sender: TObject; var Can: Boolean);
    procedure OnTimerSetTagValue(Sender: TObject);
    procedure btResetSensorsClick(Sender: TObject);
    procedure vlbSostClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
  private
    MdtaWin: array of TVisMchbMdtaControlPanelForm;
    NotesCtrlFrame: TNotesCtrlFrame;
    MotohrPanel: TMotohrPanel;

    procedure setTagValueWithTimer(idx, value, interval: integer);
    procedure doButtonAction(sender: TObject; value: integer;
      needBeep: boolean);
  public
    M: TVisMchb;
    tagname: string;
  end;

  PIdxValueRec = ^TIdxValueRec;
  TIdxValueRec = record
    idx: integer;
    value: integer;
  end;

  function hasModifier(modifier: string; text: string): boolean;
  function removeModifiers(text: string): string;
var
  VisMchbControlPanelForm: TVisMchbControlPanelForm;

implementation
uses
  infodlg,
  _prjprocs,
  numbers,
  main,
  UserControlEx,
  users,
  VisMchbControlConfirm,
  MesConst,
  TrendWindow,
  meslogging,
  SetChannel,
  TagStorage,
  RpVisualUtils,
  RpVisualGlobal, VisNumericPanel;

{$R *.dfm}

const
  BEEP_DURATION_S = 3;

// Таймер
procedure TVisMchbControlPanelForm.ScadaUpdateTimerTimer(Sender: TObject);
begin
  CustomScanScadaObjects(self, UpdateDeviceProc);
  MotohrPanel.updateValues;
end;

// Инициализация
procedure TVisMchbControlPanelForm.FormActivate(Sender: TObject);
var
  i: integer;
begin
  if M=nil then
    exit;

  Visible := false;

//  SetPathRootScadaObjects(self, M.TagPath, M.TagRoot );
  // todo: move these three panels into a new common one for all
  vlbSost.ExecuteOnTagChange := true;
  vlbPlata.ExecuteOnTagChange := true;
  SetPathRootScadaObjects(pnlSost, M.TagPath, M.TagRoot );
  SetPathRootScadaObjects(pnlControl, M.TagPath, M.TagRoot );
  SetPathRootScadaObjects(pnlBlok, M.TagPath, M.TagRoot );
  vlbSost.ExecuteOnTagChange := false;
  vlbPlata.ExecuteOnTagChange := false;

  pnlDeviceName.Caption := M.DeviceName;
  dbrdParams.Caption := M.DeviceName;

  if M.OnlyDat then begin
    pnlBlok.Visible := false;
    pnlControl.Visible := false;
    pnlSost.Visible := false;
  end else begin
    pnlSost.Visible := true;
    pnlControl.Visible := not M.HideControl;
    pnlBlok.Visible := true;
  end;
  btResetSensors.Visible := M.OnlyDat;


  // создание панелей для датчиков
  for i:=0 to M.DatCount-1 do begin
    if length(MdtaWin)<=i then begin
      SetLength(MdtaWin, i+1);
      MdtaWin[i] := TVisMchbMdtaControlPanelForm.Create(self);
      MdtaWin[i].Parent := self;
      MdtaWin[i].Top := 3000;
      MdtaWin[i].show;
      MdtaWin[i].Align := alTop;
    end;

    MdtaWin[i].M := M;
    MdtaWin[i].Init( M.Datchik[i] );
  end;

  // удаление лишних панелей
  if M.DatCount < length(MdtaWin) then begin
    for i:=M.DatCount to length(MdtaWin)-1 do
      MdtaWin[i].Free;
    SetLength(MdtaWin, M.DatCount);
  end;

  // заготовка для получения имен тегов
  tagname := M.TagPath + '.' + M.TypeName1 + '_' + repl(M.TagName, M.TagIdRoot, M.TagRoot) + '_';

  if NotesCtrlFrame=nil then begin
    NotesCtrlFrame := TNotesCtrlFrame.Create(self);
    NotesCtrlFrame.Parent := pnlNotes;
    NotesCtrlFrame.Align := alClient;
    NotesCtrlFrame.Show;
  end;

  NotesCtrlFrame.TagName := tagname;
  NotesCtrlFrame.Init;

  Visible := true;

  ScadaUpdateTimerTimer(self);

  if SuperMasterMode then begin
    btBlok.QueryTextDown := '';
    btBlok.QueryTextUp := '';
    btCrash.QueryTextDown := '';
    for i:=0 to M.DatCount-1 do begin
      MdtaWin[i].btBlok.QueryTextDown := '';
      MdtaWin[i].btBlok.QueryTextUp := '';
    end;

  end else begin

  end;


  MotohrPanel.init(pnlMotohr, M.expandTagname, m.DeviceName);
end;


// Жирный шрифт для нажатых кнопок
procedure TVisMchbControlPanelForm.VisButtonDownChange(Sender: TObject);
begin
  with Sender as TVisButton do
    if objButton.Down then
      objButton.Font.Style := [fsBold]
    else
      objButton.Font.Style := [];
end;


// Параметры
procedure TVisMchbControlPanelForm.btParamsClick(Sender: TObject);
var
  i, dv, ml, prec: integer;
  s: string;
  trans: string;
  prefix: string;
begin
  if isModeUserEx then begin
    dbrdParams.AccessReadonly := not isPermitted(M.ClassName + '~' + M.DeviceName + '~Параметры');
  end else begin
    dbrdParams.AccessReadonly := not CheckAccess(PREVILEG_SET_PARAMETERS, false);
  end;

  dbrdParams.DataPar.Clear;
  dbrdParams.DataPar.Add('Адреса ввода/вывода;;;;0');

  if not M.OnlyDat then begin
    dbrdParams.DataPar.Add('Управление;MCHB_^_Channel1;A;;0');
    dbrdParams.DataPar.Add('Контроль;MCHB_^_Channel2;A;;0');
  end;

  for i:=0 to M.DatCount-1 do
    dbrdParams.DataPar.Add( removeModifiers(M.Datchik[i].Name) +
          ';MDTA_^' + M.Datchik[i].TagPostfix + '_Channel;A;;0');

  if not M.OnlyDat then begin
    dbrdParams.DataPar.Add('Параметры управления и контроля;;;;0');
    dbrdParams.DataPar.Add('Время запуска, с;MCHB_^_TimeStart;;;0');
    dbrdParams.DataPar.Add('Время останова, с;MCHB_^_TimeStop;;;0');
    dbrdParams.DataPar.Add('Положительное значение контроля;MCHB_^_Flags;& 128;0=0,128=1;0;1');
    dbrdParams.DataPar.Add('Режим;MCHB_^_Flags;& 3;0=тип упр1,1=тип упр2,2=индикация,3=эмуляция;0;1');
    dbrdParams.DataPar.Add('Выход управления Control;MCHB_^_Control;;;0');
  end;

//Код ошибки;ErrorCode;/ 10,* 3;;7;0

  for i:=0 to M.DatCount-1 do begin
    s := '';
    dv := MdtaWin[i].pnlValue.ValueDiv;
    ml := MdtaWin[i].pnlValue.ValueMul;
    prec := MdtaWin[i].pnlValue.Precision;
    if ml > 1 then
      s := '* ' + IntToStr(ml);

    if dv > 1 then begin
      if s <> '' then
        s := s + ',';

      s := '/ ' + IntToStr(dv);
    end;

    trans := s + ';;' + IntToStr(prec) + ';0';
    prefix := ';MDTA_^' + M.Datchik[i].TagPostfix + '_';

    dbrdParams.DataPar.Add(removeModifiers(M.Datchik[i].Name) + ';;;;0');
    dbrdParams.DataPar.Add('Текущее значение'                       + prefix + 'Value;' + trans);
    dbrdParams.DataPar.Add('Реальное значение (не преобразованное)' + prefix + 'Primeval;;;0');
    dbrdParams.DataPar.Add('Нижний предел'                          + prefix + 'Low;' + trans);
    dbrdParams.DataPar.Add('Верхний предел'                         + prefix + 'High;' + trans);
    dbrdParams.DataPar.Add('Задержка предварительная, с'            + prefix + 'DlyDrebezg;;;0');
    dbrdParams.DataPar.Add('Задержка выключения, с'                 + prefix + 'DlyStart;;;0');
    dbrdParams.DataPar.Add('Задержка включения, с'                  + prefix + 'DlyOutput;;;0');
    dbrdParams.DataPar.Add('Калибровка нуля: значение рассчетное'   + prefix + 'PointV;' + trans);
    dbrdParams.DataPar.Add('Калибровка нуля: значение реальное'     + prefix + 'PointP;;;0;1');
    dbrdParams.DataPar.Add('Калибровка уровня: значение рассчетное' + prefix + 'PointV1;' + trans);
    dbrdParams.DataPar.Add('Калибровка уровня: значение реальное'   + prefix + 'PointP1;;;0;1');
    dbrdParams.DataPar.Add('Запрет действия при сработке'           + prefix + 'Flags;& 64;0=нет,64=да;1');
    dbrdParams.DataPar.Add('Запрет сигнализации'                    + prefix + 'Flags;& 128;0=нет,128=да;1');
  end;

  SetPathRootScadaObjects(pnlParams, M.TagPath, M.TagRoot );
  dbrdParams.OnControlClick(sender);
end;


procedure TVisMchbControlPanelForm.VisButtonClick(Sender: TObject);
var
  text: string;
begin
  with sender as TVisButton do begin
    text := Hint;
    if Sender = btBlok then begin
      if( objButton.Down ) then
        text := text + ' Выкл'
      else
        text := text + ' Вкл';
    end;

    SaveMessageText(mcMode_text, M.DeviceName, text, mcMode_bc, mcMode_fc);
  end;
end;


procedure TVisMchbControlPanelForm.pnlDeviceNameClick(Sender: TObject);
begin
  if m.OnlyDat then exit;

  ShowTrendWindow(
    M.DeviceName,
    tagname + 'Sost;' + tagname + 'Plata',
    'Состояние;Контроль',
    'mchb_',
    '',
    'Состояние: '#13+
    '  0 - выключено'#13+
    '  1 - работает'#13+
    '  2 - запуск'#13+
    '  3 - останов'#13+
    '  4 - авария'#13+
    '  5 - приостановлено'#13+
    '  7 - выключено, но фаза=1'#13+
    '  8 - неправильный адрес'
  );
end;


procedure TVisMchbControlPanelForm.btResetCanClick(Sender: TObject;
  var Can: Boolean);
var
  needBeep: boolean;
begin
  can := false;
  if (isModeUserEx) and (not isPermitted(M.ClassName + '~' + M.DeviceName + '~Рестарт')) then exit;

  // Сброс
//  value := GetTagValueByName(tagname+'Sost');
//  if not (value in [4,5]) then exit;

  needBeep := false;
  if ((GetTagValueByName(tagname+'Control') and 3) <> 1) and
     (not DoVisMchbControlConfirm('СБРОС аварии и РЕСТАРТ', needBeep)) then exit;

  doButtonAction(sender, GetTagValueByName(tagname+'Control') + 4, needBeep);
end;


procedure TVisMchbControlPanelForm.btAutoCanClick(Sender: TObject;
  var Can: Boolean);
var
  needBeep: boolean;
begin
  can := false;
  
  if not isManualAccessIgnore(M.DeviceName) then begin
    if (isModeUserEx) and (not isPermitted(M.ClassName + '~' + M.DeviceName + '~Режим работы~АВТ')) then
      exit;

    if (not isModeUserEx) and (not CheckAccess(PREVILEG_MCHMODE, true)) then
      exit;
  end;

  // АВТ
  needBeep := false;
  if (GetTagValueByName(tagname+'Sost') <> 1) and
     ((GetTagValueByName(tagname+'Control') and 3) = 1) and
     (not DoVisMchbControlConfirm('перевод в режим АВТ', needBeep)) then exit;

  doButtonAction(sender, GetTagValueByName(tagname+'Control') and $FFFC, needBeep);
end;


procedure TVisMchbControlPanelForm.btManOnCanClick(Sender: TObject;
  var Can: Boolean);
var
  needBeep: boolean;
begin
  can := false;

  if not isManualAccessIgnore(M.DeviceName) then begin
    if (isModeUserEx) and (not isPermitted(M.ClassName + '~' + M.DeviceName + '~Режим работы~Ручное управление~Вкл')) then
      exit;

    if (not isModeUserEx) and (not CheckAccess(PREVILEG_MCHMODE, true)) then
      exit;
  end;

  // Вкл
  needBeep := false;
  if (GetTagValueByName(tagname+'Sost') <> 1) and
     ((GetTagValueByName(tagname+'Control') and 3) <> 3) and
     (not DoVisMchbControlConfirm('перевод в режим ВКЛ', needBeep)) then exit;

  doButtonAction(sender, (GetTagValueByName(tagname+'Control') and $FFFC) + 3, needBeep);
end;


procedure TVisMchbControlPanelForm.doButtonAction(sender: TObject; value: integer; needBeep: boolean);
var
  idx: integer;
begin
  idx := GetTagIndex(tagname+'Control');
  if (modeMchbCtrlBeep) then begin
    if needBeep then
      value := value + $100 * BEEP_DURATION_S;
    setTagValue(idx, value);
  end else begin
    if needBeep then begin
      if getTagIndex(_PrjStartAlarmBtnInputTagName) < 0 then begin
        SetTagValueByName( _PrjBeepControlTagName, 3);
        setTagValueWithTimer( idx, value, BEEP_DURATION_S * 1000);
        setTagValueWithTimer( GetTagIndex(_PrjBeepControlTagName), 0, BEEP_DURATION_S * 1000);
      end else begin
        SetTagValueByName(_PrjStartAlarmBtnInputTagName, 1);
        setTagValueWithTimer(idx, value, BEEP_DURATION_S * 1000);
      end;
    end else begin
      setTagValue(idx, value);
    end;
  end;
  VisButtonClick(sender);
end;


procedure TVisMchbControlPanelForm.setTagValueWithTimer(idx, value, interval: integer);
var
  tm: TTimer;
  p: PIdxValueRec;
begin
    tm := TTimer.Create(Form1);
    tm.Interval := interval;

    new(p);
    p.idx := idx;
    p.value := value;
    tm.Tag := integer(p);

    tm.OnTimer := OnTimerSetTagValue;
end;


procedure TVisMchbControlPanelForm.OnTimerSetTagValue(Sender: TObject);
var
  p: PIdxValueRec;
  idx, value: integer;
begin
  with sender as TTimer do begin
    p := PIdxValueRec(tag);
    idx := p.idx;
    value := p.value;
    Dispose(p);
    Free;
  end;

  SetTagValue(idx, value);
end;


procedure TVisMchbControlPanelForm.btBlokCanClick(Sender: TObject;
  var Can: Boolean);
begin
  if isModeUserEx then
    Can := isPermitted(M.ClassName + '~' + M.DeviceName + '~Блокирование')
  else
    Can := CheckAccess(PREVILEG_BLOK, true);
end;


procedure TVisMchbControlPanelForm.btManOffCanClick(Sender: TObject;
  var Can: Boolean);
begin
  if not isManualAccessIgnore(M.DeviceName) then begin
    if isModeUserEx then
      Can := isPermitted(M.ClassName + '~' + M.DeviceName + '~Режим работы~Ручное управление~Выкл')
    else
      Can := CheckAccess(PREVILEG_MCHMODE, true);
  end;
end;


procedure TVisMchbControlPanelForm.btCrashCanClick(Sender: TObject;
  var Can: Boolean);
begin
  Can := isPermitted(M.ClassName + '~' + M.DeviceName + '~Авария')
end;


function hasModifier(modifier: string; text: string): boolean;
var
  k: integer;
begin
  result := false;
  k := pos('~', text);
  if k > 0 then begin
    delete(text,1,k);
    text := ',' + uppercase(trim(text)) + ',';
    result := pos(','+modifier+',', text) > 0;
  end;
end;


function removeModifiers(text: string): string;
var
  k: integer;
begin
  k := pos('~', text);
  if k > 0 then
    result := copy(text, 1, k-1)
  else
    result := text;
end;


procedure TVisMchbControlPanelForm.btResetSensorsClick(Sender: TObject);
var
  i: integer;
  s: string;
begin
  if showInfoDlg('Выполнить сброс аварии датчиков?', mtConfirmation, [mbYes, mbNo], 0) <> mrYes then exit;
  SaveMessageText(mcMode_text, M.DeviceName, 'Сброс аварии датчиков', mcMode_bc, mcMode_fc);
  for i:=0 to M.DatCount-1 do begin
    s := M.MakeDatchikTagname(M.Datchik[i].TagPostfix, 'TrigRes');
    SetTagValueByName(s, 1);
  end;
end;

procedure TVisMchbControlPanelForm.vlbSostClick(Sender: TObject);
begin
//  showInfoDlg(
//    IntToStr(Width) + ' ' +
//    IntToStr(Height) + ' ');
  btParamsClick(nil);    
end;

procedure TVisMchbControlPanelForm.FormCreate(Sender: TObject);
begin
  MotohrPanel := TMotohrPanel.Create;
//  MotohrPanel := TMotohrPanel.Create(pnlMotohr);
end;

procedure TVisMchbControlPanelForm.FormDestroy(Sender: TObject);
begin
  MotohrPanel.Free;
end;

procedure TVisMchbControlPanelForm.SpeedButton1Click(Sender: TObject);
begin
  MotohrPanel.toggleFolded;
end;

end.
