unit MesLoggingV1;

interface
uses Dialogs, SysUtils, Graphics, Classes, pFIBDatabase, MMSystem;

Type
  TMesFilterRuleType = (mfDisabled, mfInclude, mfExclude);
  TMesFilterRuleTarget = (mfMsgName, mfTagDescr, mfTagName);
  TMesFilterRule = record
    RuleType: TMesFilterRuleType;
    RuleTarget: TMesFilterRuleTarget;
    Expr: string;
  end;


  PMesTag =^TMesTag;
  TMesTag = record
    Name: string;
    Descr: string[64];
    TagId: integer;
    OpcIdx: integer;
    Value: integer;
    DT: TDateTime;
    State: byte;      // 0-не активно; 1-пришло; 2-ушло; 3-подтверждено
    Spoken: boolean;
    Filtered: Boolean;
  end;

// Формат значения поля Alarm:
//   b0=Is Alm;
//   b1=Can't Display;
//   b2=Can't Ackn;
//   b3=Can't WentOut
//   b4=No Arc In-Messages
//   b5=No Arc Out-Messages

  TMesRec = record
    MesId: integer;
    Name: string[64];
    Alarm: integer;
    Clb_inp: Integer;
    Clf_inp: Integer;
    Clb_out: Integer;
    Clf_out: Integer;
    Clb_ack: Integer;
    Clf_ack: Integer;
    EventType: integer;
    EventVal: integer;
    Tags: array of TMesTag;
  end;

  function ConnectToMesLogging(silent: Boolean = false): boolean;
  procedure DisconnectFromMesLogging;
  function checkMesloggingAlive: boolean;


  procedure SaveMessage(MesId: integer; TagName: string; Data: string);
  procedure SaveMessageText(MsgText: string; ObjName: string; Text: string;
          BckgColor: integer = 16777215; FontColor: integer = 0);
  procedure UpdateMesLogging;
  procedure AcknoledgeMes;
  procedure AlarmReset(tagname: string; value: integer);
  procedure AcknoledgeMesAll;
  function IsEventOccur(CurVal, EventType, EventVal: integer): boolean;
  procedure ClearMesBar;

  procedure loadMesFilterFromIni;
  procedure loadMesFilterFromList(sl: TStringList);
  procedure saveMesFilterToIni;
  procedure saveMesFilterToList(var sl: TStringList);
  function checkMesFilter(mesidx, tagidx: integer): boolean;
  procedure rescanMesFilter;

  function hasMesActive: boolean;
  function isMesActiveAcknowledged: Boolean;

  function isMesConnected: boolean;

  procedure showMesMonitor;
  procedure showMesView;


var
  MesFilterRules: array of TMesFilterRule;
  Mess: array of TMesRec;
  MesActiveMesIdx: integer;
  MesActiveTagPtr: PMesTag;
  MesConnected: boolean;

implementation
uses
  infodlg,
  connecting,
  MesLoggingCommon,
  _PrjProcs,
  DbUtils,
  IniFiles,
  Users,
  MesLoggingDBCheck,
  UserControlEx,
  DataMod,
  TagStorage,
  main,
  MesMonitor,
  MesView,
  MesSound,
  Status,
  numbers,
  RpVisualGlobal;


var
  user_id, place_id: Integer;
  user_name, place_name: string;



function getPlaceId(place: string): integer;
var
  place_id: integer;
begin
  Result := 0;
  if not isMesloggingConnected then exit;
  if place = '' then exit;

  dm.QueryMes.Transaction.StartTransaction;
  dm.QueryMes.SQL.Text := 'SELECT ID FROM PLACES WHERE NAME=''' + place + '''';
  dm.QueryMes.ExecQuery;
  place_id := dm.QueryMes.Fields[0].AsInteger;
  dm.QueryMes.Close;
  dm.QueryMes.Transaction.Commit;

  if place_id <= 0 then begin
    place_id := GenerateId('GEN_PLACES_ID', dm.pFIBDbMes);
    dm.WrQueryMes.Transaction.StartTransaction;
    dm.WrQueryMes.SQL.Text := 'INSERT INTO PLACES(ID, NAME) VALUES (' +
          inttostr(place_id)+', ''' + place + ''')';
    dm.WrQueryMes.ExecQuery;
    dm.WrQueryMes.Transaction.Commit;
  end;
  result := place_id;
end;



procedure getUserAndPlaceId;
begin
  if not isMesloggingConnected then exit;

  if isModeUserEx then begin
    try

      // user_id
      if (user_name <> userEx.name) then begin

        dm.QueryMes.Transaction.StartTransaction;
        dm.QueryMes.SQL.Text := 'SELECT IDUSER FROM USERS WHERE NAME=''' + userEx.name + '''';
        dm.QueryMes.ExecQuery;
        user_id := dm.QueryMes.Fields[0].AsInteger;
        dm.QueryMes.Close;
        dm.QueryMes.Transaction.Commit;

        if user_id <= 0 then begin
          user_id := GenerateId('GEN_USERS_ID', dm.pFIBDbMes);
          dm.WrQueryMes.Transaction.StartTransaction;
          dm.WrQueryMes.SQL.Text := 'INSERT INTO USERS(IDUSER, NAME) VALUES (' +
                inttostr(user_id)+', '''+userEx.name+''')';
          dm.WrQueryMes.ExecQuery;
          dm.WrQueryMes.Transaction.Commit;
        end;

        user_name := userEx.name;
      end;

      // place_id
      if (place_name <> userEx.place) then begin
        place_id := getPlaceId( userEx.place );
        place_name := userEx.place;
      end;
    except
      dm.QueryMes.Transaction.Active := false;
      dm.WrQueryMes.Transaction.Active := false;
    end;

  end else begin
    user_id := CurUserId;
    place_id := getPlaceId( CurPlaceName );
  end;

end;






//============================================================================
// Поиск id тега по TagName
//============================================================================
function SearchMesTag(TagName: string): integer;
begin
  Result := 0;
  if not isMesloggingConnected then exit;

  try
    dm.QueryMes.Transaction.StartTransaction;
    dm.QueryMes.SQL.Text := 'SELECT IDTAG FROM MESTAGS WHERE TAGNAME='#39 +
          TagName + #39;
    dm.QueryMes.ExecQuery;
    result := dm.QueryMes.Fields[0].AsInteger;
    dm.QueryMes.Close;

    dm.QueryMes.Transaction.Commit;
  except
    dm.QueryMes.Transaction.Active := false;
  end;
end;

//============================================================================
// ЗАПИСЬ СООБЩЕНИЯ В БАЗУ ДАННЫХ по TagName
//============================================================================
procedure SaveMessageText(MsgText: string; ObjName: string; Text: string; BckgColor: integer = 16777215; FontColor: integer = 0);
var
  IdMsg: integer;
begin
  if not isMesloggingConnected then exit;

//  IdMsg := -1;
  try
    dm.QueryMes.Transaction.StartTransaction;
    dm.QueryMes.SQL.Text := 'SELECT IDMSG FROM MESLIST WHERE IDMSG>=100000 AND NAME=''' + MsgText + '''';
    dm.QueryMes.ExecQuery;
    IdMsg := dm.QueryMes.Fields[0].AsInteger;
    dm.QueryMes.Close;
    dm.QueryMes.Transaction.Commit;
  except
    dm.QueryMes.Transaction.Active := false;
    IdMsg := -1;
  end;

  if IdMsg<=0 then begin
    try
      dm.QueryMes.Transaction.StartTransaction;
      dm.QueryMes.SQL.Text := 'SELECT MAX(IDMSG) FROM MESLIST';
      dm.QueryMes.ExecQuery;
      IdMsg := dm.QueryMes.Fields[0].AsInteger;
      if IdMsg < 100000 then
        IdMsg := 100000
      else
        inc(IdMsg);
      dm.QueryMes.Transaction.Commit;

      dm.WrQueryMes.Transaction.StartTransaction;
      dm.WrQueryMes.SQL.Text := 'INSERT INTO MESLIST '+
            '(IDMSG, NAME, ALARM, CLB_INP, CLF_INP, CLB_OUT, CLF_OUT, CLB_ACK, '+
            'CLF_ACK, EVENTTYPE, EVENTVAL) VALUES '+
            '('+inttostr(IdMsg)+', '''+MsgText+''', 0, '+inttostr(BckgColor)+', '+
            inttostr(FontColor)+', 16777215, 0, 16777215, 0, 0, 0)';
      dm.WrQueryMes.ExecQuery;
      dm.WrQueryMes.Transaction.Commit;
    except
      dm.QueryMes.Transaction.Active := false;
      dm.WrQueryMes.Transaction.Active := false;
      IdMsg := -1;
    end;
  end;

  if IdMsg=-1 then
    showInfoDlg('Ошибка записи сообщения!', mtError, [mbOk], 0)
  else
    SaveMessage(IdMsg, ObjName, Text);
end;


procedure SaveMessage(MesId: integer; TagName: string; Data: string);
var
  i,IdMsg, IdTag, Clb, Clf: integer;
  s: string;
begin
  if not isMesloggingConnected then exit;

  getUserAndPlaceId;

  if length(TagName)>128 then TagName := copy(TagName, 1, 128);
  if length(Data)>128 then Data := copy(Data, 1, 128);

  i := 0;
  try
    // проверка - существует ли такое сообщение
    dm.QueryMes.Transaction.StartTransaction;
    dm.QueryMes.SQL.Text := 'SELECT COUNT(*) FROM MESLIST WHERE IDMSG='+inttostr(MesId);
    dm.QueryMes.ExecQuery;
    i := dm.QueryMes.Fields[0].AsInteger;
    dm.QueryMes.Transaction.Commit;
  except
    dm.QueryMes.Transaction.Active := false;
  end;

  if i=0 then exit;




  try
    IdTag := SearchMesTag(TagName);
    if IdTag = 0 then begin
      IdTag := SearchMesTag('#'+TagName);
      if IdTag = 0 then begin
        try
          // добавляем запись в MesTags
          dm.WrQueryMes.Transaction.StartTransaction;
          dm.WrQueryMes.SQL.Text := 'INSERT INTO MESTAGS (IDMSG, TAGNAME, DESCR) VALUES ' +
                '(' + inttostr(MesId) + ', '#39 + '#'+TagName + #39','#39 + TagName + #39')';
          dm.WrQueryMes.ExecQuery;
          dm.WrQueryMes.Transaction.Commit;
        except
          dm.WrQueryMes.Transaction.Active := false;
        end;

        IdTag := SearchMesTag('#'+TagName);
        if IdTag = 0 then
          Exit;
      end;
    end;


    IdMsg := 0;
    Clb := clWhite;
    Clf := clBlack;
    if MesId > 0 then begin
      try
        dm.QueryMes.Transaction.StartTransaction;
        dm.QueryMes.SQL.Text := 'SELECT IDMSG, CLB_INP, CLF_INP ' +
            'FROM MESLIST WHERE IDMSG=' + IntToStr(MesId);
        dm.QueryMes.ExecQuery;
        IdMsg := MesId;
        Clb := dm.QueryMes.Fields[1].AsInteger;
        Clf := dm.QueryMes.Fields[2].AsInteger;

        dm.QueryMes.Transaction.Commit;
      except
        dm.QueryMes.Transaction.Active := false;
      end;
    end;

    s := 'INSERT INTO MESSAGES (DT,IDMSG,IDTAG,DATA,ACT,CLB,CLF,IDUSER,IDPLACE) VALUES (' +
            #39 + DateTimeToStr(now) + #39 + ',' +
            IntToStr(IdMsg) + ',' +
            IntToStr(IdTag) + ',' +
            #39 + Data + #39',' +
            #39'M'#39 + ',' +
            IntToStr(Clb) + ',' +
            IntToStr(Clf) + ',' +
            IntToStr(user_id) + ',' +
            IntToStr(place_id) +
            ')';

    if IdMsg>0 then try
      dm.WrQueryMes.Transaction.StartTransaction;
      dm.WrQueryMes.SQL.Text := s;
      dm.WrQueryMes.ExecQuery;
      dm.WrQueryMes.Transaction.Commit;
    except
      dm.WrQueryMes.Transaction.Active := false;
    end;

  except
    dm.WrQueryMes.Transaction.Active := false;
    dm.QueryMes.Transaction.Active := false;
    showInfoDlg('Ошибка запроса записи сообщения!', mtError, [mbOk], 0);
  end;
end;


//============================================================================
// CONNECT
//============================================================================
function ConnectToMesLogging(silent: Boolean = false): boolean;
var
  OkFlag: boolean;
  i,j: integer;
  s: string;
begin
  Result := true;
  if databaseDisabled then Exit;

  ShowStatusMessage('Подключение к БД сообщений');

  loadMesFilterFromIni;

  user_id := -1;
  place_id := -1;
  user_name := '';
  place_name := '';

  OkFlag := false;
  dm.pFIBDbMes.DatabaseName := DBNameMes;
  dm.pFIBDbMes.ConnectParams.UserName := DBUser;
  dm.pFIBDbMes.ConnectParams.Password := DBPass;
  try
    dm.pFIBDbMes.Connected := true;
    setupCurPlaceId;
    initUsers;

    if dm.pFIBDbMes.Connected then begin
      dm.QueryMes.Transaction.StartTransaction;

      // загрузка сообщений
      dm.QueryMes.SQL.Text := 'select count(*) from MESLIST where ALARM>0';
      dm.QueryMes.ExecQuery;
      i := dm.QueryMes.Fields[0].AsInteger;
      dm.QueryMes.Close;
      SetLength(Mess, i);

      dm.QueryMes.SQL.Text := 'select * from MESLIST where ALARM>0';
      dm.QueryMes.ExecQuery;
      i := 0;
      while (not dm.QueryMes.Eof) and (i<length(Mess)) do begin
        Mess[i].MesId := dm.QueryMes.FieldByName('IDMSG').AsInteger;
        Mess[i].Name := trim(dm.QueryMes.FieldByName('NAME').AsString);
        Mess[i].Clb_inp := dm.QueryMes.FieldByName('CLB_INP').AsInteger;
        Mess[i].Clf_inp := dm.QueryMes.FieldByName('CLF_INP').AsInteger;
        Mess[i].Clb_out := dm.QueryMes.FieldByName('CLB_OUT').AsInteger;
        Mess[i].Clf_out := dm.QueryMes.FieldByName('CLF_OUT').AsInteger;
        Mess[i].Clb_ack := dm.QueryMes.FieldByName('CLB_ACK').AsInteger;
        Mess[i].Clf_ack := dm.QueryMes.FieldByName('CLF_ACK').AsInteger;
        Mess[i].Alarm := dm.QueryMes.FieldByName('ALARM').AsInteger;
        try
          Mess[i].EventType := dm.QueryMes.FieldByName('EVENTTYPE').AsInteger;
          Mess[i].EventVal := dm.QueryMes.FieldByName('EVENTVAL').AsInteger;
        except
          Mess[i].EventType := 1;
          Mess[i].EventVal := 1;
        end;

        inc(i);
        dm.QueryMes.Next;
      end;
      dm.QueryMes.Close;

      // загрузка тегов, создание таймеров
      for i:=0 to length(Mess)-1 do begin
        dm.QueryMes.SQL.Text := 'select count(*) from MESTAGS where IDMSG=' +
            IntToStr(Mess[i].MesId);
        dm.QueryMes.ExecQuery;
        j := dm.QueryMes.Fields[0].AsInteger;
        dm.QueryMes.Close;
        SetLength(Mess[i].Tags, j);

        dm.QueryMes.SQL.Text := 'select * from MESTAGS where IDMSG=' +
            IntToStr(Mess[i].MesId);
        dm.QueryMes.ExecQuery;
        j := 0;
        while (not dm.QueryMes.Eof) and (j<length(Mess[i].Tags)) do begin
          Mess[i].Tags[j].TagId := dm.QueryMes.FieldByName('IDTAG').AsInteger;
//          Mess[i].Tags[j].Name := trim(dm.Query2.FieldByName('TAGNAME').AsString);
          Mess[i].Tags[j].Descr := trim(dm.QueryMes.FieldByName('DESCR').AsString);
          s := trim(dm.QueryMes.FieldByName('TAGNAME').AsString);
          Mess[i].Tags[j].Name := s;
          Mess[i].Tags[j].OpcIdx := GetTagIndex(s);
          Mess[i].Tags[j].OpcIdx := GetTagIndex(s);
          Mess[i].Tags[j].Value := -1;
          Mess[i].Tags[j].State := 0;
          Mess[i].Tags[j].Filtered := not checkMesFilter(i,j);

          inc(j);
          dm.QueryMes.Next;
        end;
        dm.QueryMes.Close;
      end;

      OkFlag := true;
      MesConnected := true;
    end;

    dm.QueryMes.Transaction.Commit;

    CheckDatabase(dm.WrQueryMes);
  except
    dm.QueryMes.Transaction.Active := false;
  end;

  MesActiveMesIdx := -1;
  MesActiveTagPtr := nil;

  Result := OkFlag;
  CloseStatusMessage;
end;


//============================================================================
// DISCONNECT
//============================================================================
procedure DisconnectFromMesLogging;
var
  i: integer;
begin
  if databaseDisabled then Exit;

  MesConnected := false;
  try
    dm.pFIBDbMes.Connected := false;
  except
  end;  

  for i:=0 to length(Mess)-1 do begin
    Mess[i].Tags := nil;
  end;
  Mess := nil;

  MesActiveMesIdx := 0;
  MesActiveTagPtr := nil;
end;


function checkMesloggingAlive: boolean;
begin
  result := dm.pFIBDbMes.ExTestConnected(laCloseConnect);
end;


//============================================================================
// UPDATE MESSAGE BAR
//============================================================================
procedure UpdateMesBar;
var
  i,j, clb, clf: integer;
  MesIdx: integer;
  p: PMesTag;
  CanBeDisp, CanWentOut: boolean;
begin
  if not isMesloggingConnected then exit;

  p := nil;
  MesIdx := 0;
  for i:=0 to length(Mess)-1 do begin
    CanBeDisp := (Mess[i].Alarm and 2) = 0;
    CanWentOut := (Mess[i].Alarm and 8) = 0;

    for j:=0 to length(Mess[i].Tags)-1 do
      if (Mess[i].Tags[j].State > 0) and (CanBeDisp) and ((CanWentOut) or (Mess[i].Tags[j].State in [1,3])) then begin
        if p = nil then begin
          MesIdx := i;
          p := @Mess[i].Tags[j];
        end else begin
          if Mess[i].Tags[j].State < p.State then begin
            MesIdx := i;
            p := @Mess[i].Tags[j];
          end else begin
            if (Mess[i].Tags[j].State = p.State) and (Mess[i].Tags[j].DT > p.DT) then begin
              MesIdx := i;
              p := @Mess[i].Tags[j];
            end;
          end;
        end;
      end;
  end;

  if p <> nil then begin
    clb := clWhite;
    clf := clBlack;
    case p.State of
      1: begin
           clb := TColor(Mess[MesIdx].Clb_inp);
           clf := TColor(Mess[MesIdx].Clf_inp);
         end;
      2: begin
           clb := TColor(Mess[MesIdx].Clb_out);
           clf := TColor(Mess[MesIdx].Clf_out);
         end;
      3: begin
           clb := TColor(Mess[MesIdx].Clb_ack);
           clf := TColor(Mess[MesIdx].Clf_ack);
         end;
    end;

    Form1.PanelMesDT.Color := clb;
    Form1.PanelMesDT.Font.Color := clf;
    Form1.PanelMesDT.Caption := DateTimeToStr(p.DT);

    Form1.PanelMesText.Color := clb;
    Form1.PanelMesText.Font.Color := clf;
    Form1.PanelMesText.Caption := Mess[MesIdx].Name + ': ' + p.Descr;

    MesActiveMesIdx := MesIdx;
    MesActiveTagPtr := p;
  end else begin
    Form1.PanelMesDT.Color := clBtnFace;
    Form1.PanelMesDT.Caption := '';

    Form1.PanelMesText.Color := clBtnFace;
    Form1.PanelMesText.Caption := '';

    MesActiveMesIdx := -1;
    MesActiveTagPtr := nil;
  end;
end;




procedure UpdateMesBarFlashing;
begin
  if not isMesloggingConnected then exit;

  if MesActiveTagPtr <> nil then
    if MesActiveTagPtr.State = 1 then
      if Form1.PanelMesDT.Color = TColor(Mess[MesActiveMesIdx].Clb_inp) then begin
        Form1.PanelMesDT.Color := TColor(Mess[MesActiveMesIdx].Clf_inp);
        Form1.PanelMesDT.Font.Color := TColor(Mess[MesActiveMesIdx].Clb_inp);
      end else begin
        Form1.PanelMesDT.Color := TColor(Mess[MesActiveMesIdx].Clb_inp);
        Form1.PanelMesDT.Font.Color := TColor(Mess[MesActiveMesIdx].Clf_inp);
      end;
end;



//============================================================================
// UPDATE MESSAGE GRID
//============================================================================
procedure UpdateMesGrid;
begin
  if not isMesloggingConnected then exit;

  if MesMonitorForm.Visible then begin
    MesMonitorForm.FormActivate(nil);
  end;
end;


//============================================================================
// UPDATE MESSAGES SCAN
//============================================================================
procedure UpdateMesLogging;
var
  i,j,v: integer;
  EventCome: boolean;
begin
  if not isMesloggingConnected then exit;

  if not( (DataClient.portConnectBad=0) and (DataClient.Connected) ) then exit;

  EventCome := false;
  for i:=0 to length(Mess)-1 do
    for j:=0 to length(Mess[i].Tags)-1 do begin
      if( Mess[i].Tags[j].Filtered ) then
        Continue;

      if (Mess[i].Alarm and 2) = 0 then begin
        v := GetTagValue(Mess[i].Tags[j].OpcIdx);
        if v <> Mess[i].Tags[j].Value then begin

          // пришло
          if (IsEventOccur(v, Mess[i].EventType, Mess[i].EventVal)) and
              (Mess[i].Tags[j].State<>1) then begin
            Mess[i].Tags[j].State := 1;
            Mess[i].Tags[j].Spoken := false;

            EventCome := true;
            Mess[i].Tags[j].Value := v;
            Mess[i].Tags[j].DT := now;

          // ушло из пришло
          end else if (not IsEventOccur(v, Mess[i].EventType, Mess[i].EventVal))
              and (Mess[i].Tags[j].State=1) then begin
            Mess[i].Tags[j].State := iif((Mess[i].Alarm and 12)=0, 2, 0);
            Mess[i].Tags[j].Value := v;
            EventCome := true;

          // ушло из подтверждено
          end else if (not IsEventOccur(v, Mess[i].EventType, Mess[i].EventVal))
              and (Mess[i].Tags[j].State=3) then begin
            Mess[i].Tags[j].State := 0;
            Mess[i].Tags[j].Value := v;
            EventCome := true;
          end;
        end;
      end;
    end;

  if EventCome then begin
    UpdateMesBar;
    UpdateMesGrid;
  end;

  if MessageBarFlashingEnabled then
    UpdateMesBarFlashing;

  updatePlaySound((MesActiveTagPtr <> nil) and (MesActiveTagPtr.State = 1));
end;

//============================================================================
// ACK ONE
//============================================================================
procedure AcknoledgeMes;
begin
  if not isMesloggingConnected then exit;

  if MesActiveTagPtr <> nil then
    if (MesActiveTagPtr.State in [1,2]) and ((Mess[MesActiveMesIdx].Alarm and 4)=0) then begin

      if not IsEventOccur(MesActiveTagPtr.Value,
            Mess[MesActiveMesIdx].EventType,
            Mess[MesActiveMesIdx].EventVal) then begin

        MesActiveTagPtr.State := 0;
      end else begin
        MesActiveTagPtr.State := 3;
      end;

      // Запись
      try
        dm.WrQueryMes.Transaction.StartTransaction;
        dm.WrQueryMes.SQL.Text := 'INSERT INTO MESSAGES (DT,IDMSG,IDTAG,ACT,CLB,CLF,IDUSER,IDPLACE) VALUES (' +
            #39 + DateTimeToStr(now) + #39 + ',' +
            IntToStr(Mess[MesActiveMesIdx].MesId) + ',' +
            IntToStr(MesActiveTagPtr.TagId) + ','#39'A'#39',' +
            IntToStr(Mess[MesActiveMesIdx].Clb_ack) + ',' +
            IntToStr(Mess[MesActiveMesIdx].Clf_ack) + ',' +
            IntToStr(user_id) + ',' +
            IntToStr(place_id) +
            ')';

        dm.WrQueryMes.ExecQuery;
        dm.WrQueryMes.Transaction.Commit;
      except
        dm.WrQueryMes.Transaction.Active := false;
      end;


      UpdateMesBar;
      UpdateMesGrid;
    end;
    
  AlarmReset(_PrjAlarmResetTagName, 1);
end;

////////////////////////////////////////////////////////////////////////////////
// Запись значения в тег при подтверждении всех аварий
procedure AlarmReset(tagname: string; value: integer);
var
  s,ss: string;
  k: integer;
begin
  if (hasMesActive()) and (rvgLogIn) and (isMesActiveAcknowledged()) then begin
    s := tagname;
    while s<>'' do begin
      k := pos(';',s);
      if k=0 then begin
        ss := s;
        s := '';
      end else begin
        ss := copy(s,1,k-1);
        delete(s,1,k);
      end;

      SetTagValue(GetTagIndex(ss), value);
    end;
  end;
end;



procedure ClearMesBar;
begin
//  if not isMesloggingConnected then exit;

  Form1.PanelMesDT.Color := clBtnFace;
  Form1.PanelMesDT.Caption := '';

  Form1.PanelMesText.Color := clBtnFace;
  Form1.PanelMesText.Caption := '';

  MesActiveMesIdx := -1;
  MesActiveTagPtr := nil;

  Mess := nil;
end;




//============================================================================
// ACK ALL
//============================================================================
procedure AcknoledgeMesAll;
var
  i,j: integer;
begin
  if not isMesloggingConnected then exit;

  for i:=0 to length(Mess)-1 do
    for j:=0 to length(Mess[i].Tags)-1 do
      if (Mess[i].Tags[j].State in [1,2]) and ((Mess[i].Alarm and 4)=0) then begin
        MesActiveMesIdx := i;
        MesActiveTagPtr := @Mess[i].Tags[j];
        AcknoledgeMes;
      end;
end;


//============================================================================
// ФУНКЦИЯ ОПРЕДЕЛЕНИЯ УСЛОВИЯ СОБЫТИЯ
//============================================================================
// EventType:
//  0. x = val
//  1. x >= val
//  2. x <= val
//  3. (x & val) = val
//  4. (x & val) = 0
//  5. (x & val) > 0
//  6. ((x & val) > 0) && (x & val) != val
//  7. по изменению
function IsEventOccur(CurVal, EventType, EventVal: integer): boolean;
begin
  result := false;
  case EventType of
    0: result := CurVal = EventVal;
    1: result := CurVal >= EventVal;
    2: result := CurVal <= EventVal;
    3: result := (CurVal and EventVal) = EventVal;
    4: result := (CurVal and EventVal) = 0;
    5: result := (CurVal and EventVal) > 0;
    6: result := ((CurVal and EventVal) > 0) and ((CurVal and EventVal) <> EventVal);
    7: begin
         result := (CurVal <> EventVal) and (EventVal >= 0);
       end;
  end;
end;


procedure loadMesFilterFromIni;
var
  sl, rules: TStringList;
  i,n: integer;
begin
  sl := TStringList.Create;
  rules := TStringList.Create;
  with TIniFile.Create(CurDir + RpVisualIniFile) do
  try
    ReadSection('MesFilterRules', sl);
    n := sl.Count;
    for i:=0 to n-1 do begin
      rules.Add( ReadString('MesFilterRules', sl[i], '') );
    end;
  finally
    Free;
  end;
  sl.Free;

  loadMesFilterFromList(rules);

  rules.Free;
end;


procedure loadMesFilterFromList(sl: TStringList);
var
  i,k,n: integer;
  s,strg: string;
  stype: Char;
begin
  SetLength(MesFilterRules, 0);

  n := sl.Count;
  SetLength(MesFilterRules, n);

  for i:=0 to n-1 do
    with MesFilterRules[i] do begin
      RuleType := mfDisabled;

      s := sl[i];
      k := Pos(':', s);
      if k < 3 then
        Continue;

      stype := s[1];
      strg := Copy(s,2,k-2);
      Delete(s,1,k);
      Expr := s;

      if strg='m' then
        RuleTarget := mfMsgName
      else if strg='t' then
        RuleTarget := mfTagName
      else if strg='d' then
        RuleTarget := mfTagDescr
      else
        Continue;

      if stype='+' then
        RuleType := mfInclude
      else if stype='-' then
        RuleType := mfExclude
      else
        Continue;

    end;
end;

procedure saveMesFilterToIni;
var
  i: Integer;
  s: string;
begin
  with TIniFile.Create(CurDir + RpVisualIniFile) do
  try
    EraseSection('MesFilterRules');
    for i:=0 to length(MesFilterRules)-1 do
      with MesFilterRules[i] do begin
        s := '';
        if RuleType = mfInclude then
          s := s + '+'
        else if RuleType = mfExclude then
          s := s + '-'
        else
          Continue;

        if RuleTarget = mfMsgName then
          s := s + 'm'
        else if RuleTarget = mfTagDescr then
          s := s + 'd'
        else if RuleTarget = mfTagName then
          s := s + 't'
        else
          Continue;

        s := s + ':' + Expr;

        WriteString('MesFilterRules', 'rule' + IntToStr(i), s);
      end;
  finally
    Free;
  end;
end;



procedure saveMesFilterToList(var sl: TStringList);
var
  i: Integer;
  s: string;
begin
  sl.Clear;
  for i:=0 to length(MesFilterRules)-1 do
    with MesFilterRules[i] do begin
      s := '';
      if RuleType = mfInclude then
        s := s + '+'
      else if RuleType = mfExclude then
        s := s + '-'
      else
        Continue;

      if RuleTarget = mfMsgName then
        s := s + 'm'
      else if RuleTarget = mfTagDescr then
        s := s + 'd'
      else if RuleTarget = mfTagName then
        s := s + 't'
      else
        Continue;

      s := s + ':' + Expr;

      sl.Add(s);
    end;
end;


function checkMesFilter(mesidx, tagidx: integer): boolean;
var
  s: string;
  i,n: integer;
begin
  if( Length(MesFilterRules) = 0 ) then begin
    result := true;
    Exit;
  end;

  Result := false;
  if (mesidx<0) or (mesidx>=Length(Mess)) then
    exit;
  if (tagidx<0) or (tagidx>=Length(Mess[mesidx].Tags)) then
    exit;

  result := (MesFilterRules[0].RuleType = mfExclude);

  n := Length(MesFilterRules);
  for i:=0 to n-1 do
    with MesFilterRules[i] do begin
      if RuleType = mfDisabled then
        Continue;

      if RuleTarget = mfMsgName then
        s := Mess[mesidx].Name
      else if RuleTarget = mfTagName then
        s := Mess[mesidx].Tags[tagidx].Name
      else
        s := Mess[mesidx].Tags[tagidx].Descr;

      if CheckFilter(s, Expr) then
        Result := (RuleType = mfInclude);
    end;
    
end;


procedure rescanMesFilter;
var
  i,j: integer;
begin
  for i:=0 to length(Mess)-1 do
    for j:=0 to length(Mess[i].Tags)-1 do
          Mess[i].Tags[j].Filtered := not checkMesFilter(i,j);
end;


function hasMesActive: boolean;
begin
  result := MesActiveTagPtr <> nil;
end;

function isMesActiveAcknowledged: boolean;
begin
  result := (MesActiveTagPtr <> nil) and (not (MesActiveTagPtr.State in [1,2]));
end;

function isMesConnected: boolean;
begin
  result := dm.pFIBDbMes.Connected;
end;


procedure showMesMonitor;
begin
  if not isMesloggingConnected then exit;
  MesMonitorForm.ShowModal;
end;


procedure showMesView;
begin
  if not isMesloggingConnected then exit;
  MesViewForm.ShowModal;
end;


initialization
  MesConnected := false;

end.
