unit MesLoggingV2;

interface
uses
  Dialogs, SysUtils, Graphics, Classes, FIBQuery, pFIBDatabase, Math, HyperStr,
  MMSystem;

const
  MES_COLOR_BACK_FINISHED = $00400080;
  MES_COLOR_FONT_FINISHED = clWhite;

  MES_COLOR_BACK_ACKNOWLEDGED = clGray;
  MES_COLOR_FONT_ACKNOWLEDGED = clBlack;

type
  TMessageRec = record
    idm: Integer;
    dt: TDateTime;
    finished: boolean;
    acknowledged: Boolean;
    text: string;
    idplace: Integer;
    placename: string;
    clb: Integer;
    clf: Integer;
  end;

  PAlarmResetTagRec = ^TAlarmResetTagRec;
  TAlarmResetTagRec = record
    tagname: string;
    placename: string;
    idplace: Integer;
  end;

  function ConnectMeslogging(silent: Boolean = false): boolean;
  procedure DisconnectMeslogging;
  function checkMesloggingAlive: boolean;
  procedure onMessagesEvent;

  procedure SaveMessage(idmsg: integer; tagname: string; data: string);
  procedure SaveMessageText(msgtext: string; tagtext: string; data: string;
          BckgColor: integer = clWhite; FontColor: integer = clBlack);

  procedure UpdateMesLogging;
  procedure AcknoledgeLastMessage;
  procedure AcknoledgeMessage(idm: integer; idplace: integer);
  procedure AcknoledgeAllMessages;
  procedure ClearMesBar;

  function isMesConnected: boolean;

  procedure showMesMonitor;
  procedure showMesView;

  procedure updateLastMessage;

  procedure updateBar;
  procedure updateBarBlinking;

  procedure addAlarmResetTag(tagname: string; placename: string);
  procedure initAlarmResetTags;
  procedure clearAlarmResetTags;
  procedure resetAlarmByPlace(idplace: integer);
  procedure resetAlarmGlobal;

  procedure updatePlaySound;

implementation
uses
  infodlg,
  connecting,
  _PrjProcs,
  MesLoggingCommon,
  RpVisualGlobal,
  DbUtils,
  MesMonitorV2,
  MesViewV2,
  MesSound,
  Users,
  UserControlEx,
  DataMod,
  TagStorage,
  main,
  MesMonitor,
  Status,
  numbers;

var
  lastMessage: TMessageRec;
  hasLastMessage: Boolean = False;
  alarmResetTags: array of PAlarmResetTagRec;
  needUpdateLastMessage: Boolean = false;
  playingSound: boolean = false;

function checkMesMonitorExists: boolean;
begin
  Result := False;
  try
    dm.WrQueryMes.Transaction.StartTransaction;
    dm.WrQueryMes.SQL.Text := 'select * from mesmonitor';
    dm.WrQueryMes.ExecQuery;
    dm.WrQueryMes.Close;
    dm.WrQueryMes.Transaction.Commit;
    Result := True;
  except
    dm.WrQueryMes.Transaction.Active := false;
  end;
end;

function ConnectMeslogging(silent: Boolean = false): boolean;
begin
  Result := true;
  if databaseDisabled then Exit;

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

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

    if checkMesMonitorExists then begin
      setupCurPlaceId;
      setupFilterPlaces;
      initAlarmResetTags;
      initUsers;
      dm.evntMes.RegisterEvents;
      needUpdateLastMessage := True;
      hasLastMessage := False;
    end else begin
      Result := false;
      databaseDisabled := True;
      showInfoDlg('Для работы с журналом сообщений версии 2 необходимо пересоздать '+
            'базу данных сообщений jrobo версии от 1.5.4!'#13+
            'Включен режим работы без баз данных (см. Настройки/Системные)', mtError, [mbOk], 0);
    end;
  except
    Result := False;
  end;

  if not Result then
    DisconnectMeslogging;

  CloseStatusMessage;
end;

procedure DisconnectMeslogging;
begin
  if databaseDisabled then Exit;
  try
    if dm.evntMes.Registered then
      dm.evntMes.UnRegisterEvents;
  except
  end;

  try
    if dm.pFIBDbMes.Connected then
      dm.pFIBDbMes.Connected := false;
  except
  end;
  hasLastMessage := False;
end;

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


procedure onMessagesEvent;
begin
  needUpdateLastMessage := True;
end;



procedure SaveMessage(idmsg: integer; tagname: string; data: string);
var
  clb, clf: integer;
  msgname: string;
begin
  if not isMesloggingConnected then
    exit;

  clb := clWhite;
  clf := clBlack;
  try
    dm.WrQueryMes.Transaction.StartTransaction;

    dm.WrQueryMes.SQL.Text := 'select clb_inp, clf_inp, name from meslist where idmsg=' + IntToStr(idmsg);
    dm.WrQueryMes.ExecQuery;
    if dm.WrQueryMes.Eof then
      Exit;
    clb := dm.WrQueryMes.Fields[0].AsInteger;
    clf := dm.WrQueryMes.Fields[1].AsInteger;
    msgname := dm.WrQueryMes.Fields[2].AsString;
    dm.WrQueryMes.Close;
    dm.WrQueryMes.Transaction.Commit;
  except
    dm.WrQueryMes.Transaction.Active := false;
    showInfoDlg('Ошибка записи сообщения!', mtError, [mbOk], 0);
  end;

  SaveMessageText(msgname, tagname, data, clb, clf);
end;


procedure SaveMessageText(msgtext: string; tagtext: string; data: string; BckgColor: integer; FontColor: integer);
begin
  if not isMesloggingConnected then exit;

  try
    dm.WrQueryMes.Transaction.StartTransaction;
    dm.WrQueryMes.SQL.Text := format(
          'select * from save_message(''%s'', ''%s'', ''%s'', %d, %d, %d, %d)',
          [msgtext, tagtext, data, CurUserId, CurPlaceId, BckgColor, FontColor]);
    dm.WrQueryMes.ExecQuery;
    dm.WrQueryMes.Transaction.Commit;
  except
    dm.WrQueryMes.Transaction.Active := false;
    showInfoDlg('Ошибка записи сообщения!', mtError, [mbOk], 0);
  end;
end;


procedure UpdateMesLogging;
begin
  if needUpdateLastMessage then begin
    needUpdateLastMessage := false;
    updateLastMessage;
    updateBar;
    updatePlaySound; //((hasLastMessage) and (not lastMessage.acknowledged) and (not lastMessage.finished));
    updateMesMonitor;
    updateMesView;
  end;

  updateBarBlinking;
end;


procedure AcknoledgeLastMessage;
begin
  if not isMesloggingConnected then exit;
  if (not hasLastMessage) then exit;

  AcknoledgeMessage(lastMessage.idm, lastMessage.idplace)
end;


procedure AcknoledgeMessage(idm: integer; idplace: integer);
var
  dt: string;
begin
  if not isMesloggingConnected then exit;

  if idm <= 0 then exit;

  dt := DateTimeToStr(now);
  try
    dm.WrQueryMes.Transaction.StartTransaction;

    dm.WrQueryMes.SQL.Text := Format('update messages set dtack=''%s'', iduser=%d where idm=%d and dtack is null',
          [dt, CurUserId, idm]);
    dm.WrQueryMes.ExecQuery;
    dm.WrQueryMes.Close;
    dm.WrQueryMes.Transaction.Commit;

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

  resetAlarmByPlace(idplace);
  resetAlarmGlobal;
end;


procedure AcknoledgeAllMessages;
var
  dt: string;
begin
  if not isMesloggingConnected then exit;

  dt := DateTimeToStr(now);
  try
    dm.WrQueryMes.Transaction.StartTransaction;

    dm.WrQueryMes.SQL.Text := Format('update messages set dtack=''%s'', iduser=%d where dtack is null',
          [dt, CurUserId]);

    if filterPlaceIds <> '' then
      dm.WrQueryMes.SQL.add(' and idplace in (' + filterPlaceIds + ')');

    dm.WrQueryMes.ExecQuery;
    dm.WrQueryMes.Close;
    dm.WrQueryMes.Transaction.Commit;

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

  resetAlarmGlobal;
end;



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


procedure showMesMonitor;
begin
  MesMonitorV2.showMesMonitor;
end;


procedure showMesView;
begin
  MesViewV2.showMesView;
end;


procedure updateLastMessage;
begin
  if not isMesloggingConnected then exit;

  try
    dm.QueryMes.Transaction.StartTransaction;
    dm.QueryMes.SQL.Text := 'select first 1 '+
          {0    1      2      3      4         5        6        7    8 }
          'IDM, DT, DTEND, DTACK, IDPLACE, PLACENAME, FULLTEXT, CLB, CLF '+
          'from mesmonitor';

//          {0    1      2      3      4         5        6          7      8    9   10}
//          'IDM, DT, DTEND, DTACK, IDPLACE, PLACENAME, MESTEXT, TAGTEXT, DATA, CLB, CLF '+
//          'from mesmonitor';

    if filterPlaceIds <> '' then
      dm.QueryMes.SQL.Add( ' where idplace in (' + filterPlaceIds + ')' );

    dm.QueryMes.ExecQuery;
    hasLastMessage := not dm.QueryMes.Eof;
    if hasLastMessage then
      with lastMessage do begin
        idm := dm.QueryMes.Fields[0].AsInteger;
        dt := dm.QueryMes.Fields[1].AsDateTime;
        finished := not dm.QueryMes.Fields[2].IsNull;
        acknowledged := not dm.QueryMes.Fields[3].IsNull;
        text := dm.QueryMes.Fields[6].AsString;
        idplace := dm.QueryMes.Fields[4].AsInteger;
        placename := dm.QueryMes.Fields[5].AsString;
        clb := dm.QueryMes.Fields[7].AsInteger;
        clf := dm.QueryMes.Fields[8].AsInteger;
      end;
    dm.QueryMes.Transaction.Commit;
  except
    dm.QueryMes.Transaction.Active := false;
  end;
end;



procedure updateBar;
var
  clb, clf: integer;
begin
  if hasLastMessage then begin
    Form1.PanelMesDT.Caption := DateTimeToStr(lastMessage.dt);
    Form1.PanelMesText.Caption := lastMessage.text;

    if lastMessage.acknowledged then begin
      clb := MES_COLOR_BACK_ACKNOWLEDGED;
      clf := MES_COLOR_FONT_ACKNOWLEDGED;
    end else if lastMessage.finished then begin
      clb := MES_COLOR_BACK_FINISHED;
      clf := MES_COLOR_FONT_FINISHED;
    end else begin
      clb := lastMessage.clb;
      clf := lastMessage.clf;
    end;

    if (Form1.PanelMesText.Color <> clb) or (Form1.PanelMesText.Font.Color <> clf) then begin
      Form1.PanelMesDT.Color := clb;
      Form1.PanelMesDT.Font.Color := clf;

      Form1.PanelMesText.Color := clb;
      Form1.PanelMesText.Font.Color := clf;
    end;

  end else begin
    ClearMesBar;
  end;

end;

procedure ClearMesBar;
begin
  Form1.PanelMesDT.Color := clBtnFace;
  Form1.PanelMesDT.Caption := '';

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

  updatePlaySound;
end;


procedure updateBarBlinking;
begin
  if (not hasLastMessage) or (lastMessage.finished) or (lastMessage.acknowledged) then exit;

  if Form1.PanelMesDT.Color <> lastMessage.clb then begin
    Form1.PanelMesDT.Color := lastMessage.clb;
    Form1.PanelMesDT.Font.Color := lastMessage.clf;
  end else begin
    Form1.PanelMesDT.Color := lastMessage.clf;
    Form1.PanelMesDT.Font.Color := lastMessage.clb;
  end;
end;


procedure addAlarmResetTag(tagname: string; placename: string);
var
  i: Integer;
begin
  i := Length(alarmResetTags);
  SetLength(alarmResetTags, i+1);
  New(alarmResetTags[i]);
  alarmResetTags[i].tagname := tagname;
  alarmResetTags[i].placename := placename;
  alarmResetTags[i].idplace := 0;
end;


procedure clearAlarmResetTags;
var
  i: integer;
begin
  for i:=0 to Length(alarmResetTags)-1 do
    dispose( alarmResetTags[i] );
  SetLength(alarmResetTags, 0);
end;

procedure initAlarmResetTags;
var
  i: integer;
  placename: string;
begin
  try
    dm.QueryMes.Transaction.StartTransaction;
    dm.QueryMes.SQL.Text := 'select id, name from places';
    dm.QueryMes.ExecQuery;
    while not dm.QueryMes.Eof do begin
      placename := dm.QueryMes.Fields[1].AsString;
      for i:=0 to Length(alarmResetTags)-1 do
        if alarmResetTags[i].placename = placename then begin
          alarmResetTags[i].idplace := dm.QueryMes.Fields[0].AsInteger;
          Break;
        end;
      dm.QueryMes.Next;
    end;
    dm.QueryMes.Close;
    dm.QueryMes.Transaction.Commit;
  except
    dm.QueryMes.Transaction.Active := false;
  end;
end;


procedure resetAlarmByPlace(idplace: integer);
var
  i: Integer;
begin
  for i:=0 to Length(alarmResetTags)-1 do
    if alarmResetTags[i].idplace = idplace then
      SetTagValueByName(alarmResetTags[i].tagname, 1);
end;


procedure resetAlarmGlobal;
var
  s: string;
  i: integer;
begin
  if (not hasLastMessage) or (not lastMessage.acknowledged) then exit;

  SetDelimiter(',');
  i:=1;
  repeat
    s := Trim(GetToken(_PrjAlarmResetTagName, i));
    SetTagValueByName(s, 1);
  until not NextToken(_PrjAlarmResetTagName, i);
end;

procedure updatePlaySound;
begin
  MesSound.updatePlaySound((hasLastMessage) and (not lastMessage.acknowledged) and (not lastMessage.finished));
end;


initialization

finalization

end.
