unit MotohrMonitor;

interface

uses
  HyperStr,
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Buttons, StdCtrls, ComCtrls, ExtCtrls, ImgList, Menus, AdvGlowButton;

type
  PCounterRec = ^TCounterRec;
  TCounterRec = record
    machName: string;
    machDescr: string;
    machDescrSort: string;
    tagMachTime: integer;
    tagCounterSec: integer;
    sec: integer;
    running: Boolean;
  end;

  TMotohrMonitorForm = class(TForm)
    Timer1: TTimer;
    PopupMenu1: TPopupMenu;
    miSelect: TMenuItem;
    Splitter1: TSplitter;
    Panel2: TPanel;
    btCancel: TButton;
    btSelect: TButton;
    btSet: TButton;
    btOff: TButton;
    Panel1: TPanel;
    Panel3: TPanel;
    edFilter: TEdit;
    btFilterClear: TAdvGlowButton;
    listSec: TListView;
    listCt: TListView;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure listCtCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure listCtChange(Sender: TObject; Item: TListItem;
      Change: TItemChange);
    procedure listSecCompare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure listSecColumnClick(Sender: TObject; Column: TListColumn);
    procedure edFilterChange(Sender: TObject);
    procedure btFilterClearClick(Sender: TObject);
    procedure btSelectClick(Sender: TObject);
    procedure btSetClick(Sender: TObject);
    procedure btOffClick(Sender: TObject);
    procedure listSecDblClick(Sender: TObject);
    procedure listSecAdvancedCustomDrawSubItem(Sender: TCustomListView;
      Item: TListItem; SubItem: Integer; State: TCustomDrawState;
      Stage: TCustomDrawStage; var DefaultDraw: Boolean);
    procedure FormShow(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
    selCtDescr: string;
    selCtSid: string;
    selCtTimer: boolean;
    selDescr: string;

    counters: TList;
    tagSystemTime: integer;
    procedure clearCounters;
    procedure loadListCt;
    procedure loadListSec;
    procedure loadCounters;
    procedure updateValues;
    procedure setSelectedCounters(sec: Integer);
  public
  
  end;




implementation
uses
  MotohrMessage,
  MotohrDb,
  MotohrView,
  MesLogging,
  InputDialog,
  InfoDlg,
  Utils,
  TagStorage,
  Motohr,
  Numbers,
  RpVisualGlobal,
  main;

{$R *.DFM}

const
  SUBITEM_RUNNING   = 0;
  SUBITEM_SEC       = 1;

var
  sortMode: Integer = 0;
  sortDesc: Boolean = false;



procedure TMotohrMonitorForm.FormCreate(Sender: TObject);
begin
  DoubleBuffered := True;
  listSec.DoubleBuffered := True;
  counters := TList.Create;
end;


procedure TMotohrMonitorForm.FormDestroy(Sender: TObject);
begin
  clearCounters;
  counters.Free;
end;


procedure TMotohrMonitorForm.FormActivate(Sender: TObject);
begin
  tagSystemTime := GetTagIndex(getMotohrModulePath + '.system.time');
  loadListCt;
end;


procedure TMotohrMonitorForm.FormShow(Sender: TObject);
begin
  listCt.SetFocus;
  listSec.Columns[0].Width := listSec.Width - 161;
end;

procedure TMotohrMonitorForm.FormCloseQuery(Sender: TObject;
  var CanClose: Boolean);
begin
  exit;
  listCt.OnChange := nil;
end;



procedure TMotohrMonitorForm.clearCounters;
begin
  while counters.Count > 0 do begin
    dispose(PCounterRec(counters[0]));
    counters.Delete(0);
  end;
end;


procedure TMotohrMonitorForm.loadListCt;
const
  STR_DESCR = '.descr';
  STR_TIMER = '.timer';
var
  sl: TStringList;
  prefix: string;
  s: string;
  sid: string;
  timer: string;
  i: Integer;
  li: TListItem;
begin
  listCt.Items.Clear;

  listCt.OnChange := nil;
  listCt.Items.BeginUpdate;
  listCt.Items.Clear;

  sl := TStringList.Create;
  prefix := getMotohrModulePath + '.cnttype.';
  s := prefix + '*' + STR_DESCR;
  FindTags(s, sl);

  for i:=0 to sl.Count-1 do begin
    li := listCt.Items.Add;
    li.Caption := GetTagValueString( Integer(sl.Objects[i]) );

    s := sl[i];
    sid := copy( s, Length(prefix) + 1, Length(s) - Length(prefix) - Length(STR_DESCR) );
    timer := getTimerMark(GetTagValueByName( prefix + sid + STR_TIMER) > 0);

    li.SubItems.Add(timer);
//    li.SubItems.Add(sid);
    li.Data := Pointer(StrToIntDef(sid, 0));
  end;
  sl.Free;
  listCt.AlphaSort;
  listCt.Items.EndUpdate;
  listCt.OnChange := listCtChange;

  if listCt.Items.Count > 0 then
    listCt.Items[0].Selected := true;
end;


procedure TMotohrMonitorForm.listCtCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
var
  id1,id2: integer;
begin
  id1 := Integer(Item1.Data);
  id2 := Integer(Item2.Data);
  Compare := iif(id1 = COUNTER_TOTAL_ID, 0, 1) - iif(id2 = COUNTER_TOTAL_ID, 0, 1);

  if Compare = 0 then begin
    Compare := CompareText(item1.SubItems[0], item2.SubItems[0]);
    if Compare = 0 then
      Compare := AnsiStrIComp( PChar(Item1.Caption), PChar(Item2.Caption) );
  end;
end;


procedure TMotohrMonitorForm.listCtChange(Sender: TObject; Item: TListItem;
  Change: TItemChange);
var
  id: Integer;
begin
  if (Item <> listCt.Selected) or (Item.Caption = selCtDescr) then
    exit;

  id := Integer(Item.data);
  selCtSid := IntToStr(id);
  selCtDescr := item.Caption;
  selCtTimer := item.SubItems[0] <> '';

  loadCounters;
  loadListSec;

  btSet.Enabled := selCtSid <> COUNTER_TOTAL_SID;
  btOff.Enabled := btSet.Enabled;
end;


procedure TMotohrMonitorForm.loadCounters;
var
  sl: TStringList;
  s: string;
  i: Integer;
  p: PCounterRec;
  tagSec, tagTime: Integer;
  descr: string;
  tagname: string;
  path, cnt: string;
  machName: string;
begin
  clearCounters;

  sl := TStringList.Create;
  path := getMotohrModulePath;
  cnt := 'cnt' + selCtSid;
  FindTags(path + '.*.' + cnt, sl);

  for i:=0 to sl.Count-1 do begin
    tagSec := Integer(sl.Objects[i]);
    tagname := sl[i];
    s := Copy(tagname, 1, Length(tagname) - Length(cnt) - 1);
    machName := IStr(s, Length(path) + 2);
    tagTime := GetTagIndex(s + '.time');

    if (tagSec < 0) or (tagTime < 0) then
      Continue;

    descr := GetTagValueString( GetTagIndex(s + '.descr') );
    if descr = '' then
      descr := Copy(tagname, Length(path)+2, Length(tagname) - Length(path) - Length(cnt) - 2);

    p := new(PCounterRec);
    p.tagCounterSec := tagSec;
    p.tagMachTime := tagTime;
    p.machName := machName;
    p.machDescr := descr;
    p.machDescrSort := toPaddingNumbersLeft(descr);
    counters.Add(p);
  end;
  sl.Free;
end;


procedure TMotohrMonitorForm.loadListSec;
var
  filter: string;
  i: Integer;
  li: TListItem;
  p: PCounterRec;
begin
  if listSec.Selected <> nil then
    selDescr := listSec.Selected.Caption;

  listSec.Items.BeginUpdate;
  listSec.Items.Clear;

  filter := prepareFilter(edFilter.Text);

  for i:=0 to counters.Count-1 do begin
    p := PCounterRec(counters[i]);

    if (filter <> '') and not(CheckFilter(p.machDescr, filter)) then
      Continue;

    li := listSec.Items.Add;
    li.Caption := p.machDescr;
    li.SubItems.Add('');
    li.SubItems.Add('');
    li.Data := p;

    if p.machDescr = selDescr then
      li.Selected := true;
  end;
  updateValues;
  listSec.Items.EndUpdate;

  listSec.AlphaSort;

  if listSec.Selected <> nil then
    listSec.Selected.MakeVisible(false);

//  listSec.Columns[0].Width := listSec.Width - 161;
end;


procedure TMotohrMonitorForm.updateValues;
var
  i: Integer;
  systime: Int64;
  time: Int64;
  runsec: Int64;
  running: boolean;
  p: PCounterRec;
  s: string;
  v: integer;
  d: Double;
begin
  systime := GetTagValueLong(tagSystemTime);
  if systime <= 0 then
    exit;

  for i:=0 to listSec.Items.Count-1 do begin
    p := PCounterRec(listSec.Items[i].Data);
    v := GetTagValueInt(p.tagCounterSec);

    time := GetTagValueLong(p.tagMachTime);
    running := time > 0;
    if v = COUNTER_DISABLED then begin
      s := '---';
    end else begin
      if running then begin
        runsec := systime - time;
        if selCtTimer then
          v := v - runsec
        else
          v := v + runsec;
      end;
      d := v / SEC_IN_HOUR;
      s := Format('%.1f', [d]);
    end;
    p.sec := v;
    p.running := running;


    if (listSec.Items[i].SubItems[SUBITEM_RUNNING] <> '') <> p.running then begin
      listSec.Items[i].SubItems[SUBITEM_RUNNING] := iif(p.running, ' ', '');
      listSec.Items[i].Update;
    end;

    if listSec.Items[i].SubItems[SUBITEM_SEC] <> s then begin
      listSec.Items[i].SubItems[SUBITEM_SEC] := s;
      listSec.Items[i].Update;
    end;
  end;
end;


procedure TMotohrMonitorForm.Timer1Timer(Sender: TObject);
begin
  updateValues;
end;




procedure TMotohrMonitorForm.listSecCompare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);

  function boolToInt(value: boolean): Integer;
  begin
    if value then
      result := 1
    else
      Result := 0;
  end;

var
  a,b: PCounterRec;
begin
  a := PCounterRec(Item1.Data);
  b := PCounterRec(Item2.Data);
  Compare := 0;

  // sort by running
  if sortmode = 1 then begin
    if sortDesc then
      Compare := boolToInt(b.running) - boolToInt(a.running)
    else
      Compare := boolToInt(a.running) - boolToInt(b.running);
  end;


  // sort by value
  if (sortmode = 2) or ((sortmode = 1) and (Compare = 0)) then begin
    if sortDesc then
      Compare := b.sec - a.sec
    else
      Compare := a.sec - b.sec;
  end;


  // sort by mach descr
  if Compare = 0 then begin
    if sortDesc then
      Compare := AnsiStrIComp( PChar(b.machDescrSort), PChar(a.machDescrSort) )
    else
      Compare := AnsiStrIComp( PChar(a.machDescrSort), PChar(b.machDescrSort) );
  end
end;


procedure TMotohrMonitorForm.listSecColumnClick(Sender: TObject;
  Column: TListColumn);
begin
  if sortMode = Column.Index then begin
    sortDesc := not sortDesc;
  end else begin
    sortMode := Column.Index;
    sortDesc := False;
  end;
  listSec.AlphaSort;

  if listSec.Selected <> nil then
    listSec.Selected.MakeVisible(false);
end;


procedure TMotohrMonitorForm.edFilterChange(Sender: TObject);
begin
  loadListSec;
end;


procedure TMotohrMonitorForm.btFilterClearClick(Sender: TObject);
begin
  edFilter.Text := '';
end;


procedure TMotohrMonitorForm.btSelectClick(Sender: TObject);
begin
  listSec.SelectAll;
  listSec.SetFocus;
end;


procedure TMotohrMonitorForm.btSetClick(Sender: TObject);
var
  s: string;
  d: Double;
  v: integer;
begin
  if selCtTimer then begin
    s := '0';
    if not ShowInputDialog('Установка выделенных таймеров', 'Введите количество часов', s) then
      exit;

    d := StrToFloatDef(s, 0.0);
    s := FloatToStr(d);
    v := Trunc(d * SEC_IN_HOUR);
  end else begin
    if showInfoDlg('Сбросить выделенные счетчики?', 'Подтверждение', 'Сбросить', 'Отмена') <> 1 then
      Exit;

    v := 0;
  end;

  setSelectedCounters(v);
  listSec.SetFocus;
end;


procedure TMotohrMonitorForm.btOffClick(Sender: TObject);
var
  s: string;
begin
  s := iif(selCtTimer, 'таймеры', 'счетчики');
  if showInfoDlg('Отключить выделенные ' + s + '?', 'Подтверждение', 'Отключить', 'Отмена') <> 1 then
    Exit;

  Delete(s, Length(s), 1);
  setSelectedCounters(COUNTER_DISABLED);
  listSec.SetFocus;
end;


procedure TMotohrMonitorForm.setSelectedCounters(sec: Integer);
var
  i: integer;
  p: PCounterRec;
  dt: TDateTime;
  text, fio: string;
begin
  if not showMotohrMessage(text, fio) then
    exit;

  try
    startTransaction(true);
    dt := Now;

    for i:=0 to listSec.items.Count-1 do begin
      if not listSec.Items[i].Selected then
        Continue;

      p := PCounterRec(listSec.Items[i].Data);
      insertLogbookRecord(dt, selCtDescr, p.machName, text, fio, p.sec, sec);
      SetTagValue(p.tagCounterSec, sec);
    end;

    commitTransaction;
  except
    on e : Exception do begin
      rollbackTransaction;
      showInfoDlg(e.Message, mtError, [mbOk], 0);
    end;
  end;
end;


procedure TMotohrMonitorForm.listSecDblClick(Sender: TObject);
begin
  if btSet.Enabled then
    btSetClick(nil);
end;


procedure TMotohrMonitorForm.listSecAdvancedCustomDrawSubItem(
  Sender: TCustomListView; Item: TListItem; SubItem: Integer;
  State: TCustomDrawState; Stage: TCustomDrawStage;
  var DefaultDraw: Boolean);
var
  p: PCounterRec;
begin
  p := PCounterRec(Item.Data);


  if SubItem = 1 then begin
    if p.running then
      Sender.Canvas.Brush.Color := COLOR_RUNNING
    else
      Sender.Canvas.Brush.Color := COLOR_LISTVIEW;
  end else

  if SubItem = 2 then begin
    if p.sec < 0 then
      Sender.Canvas.Brush.Color := COLOR_NEGATIVE
    else
      Sender.Canvas.Brush.Color := COLOR_LISTVIEW;
  end

end;




end.
