Árvore de páginas

Cria um Serviço de compartilhamento de Dados em cache de memória permitindo que os dados sejam armazenados em estrutura de Fila de mensagens.

Este serviço pode ser usado para compartilhar dados entre servidores, permitindo troca de informações entre eles e e com isto permitindo também a continuação de tarefas iniciadas em um servidor e finalizadas em outro.

Cria uma infra estrutura que permite uma "granularização" das tarefas que podem ser executadas por quaisquer servidores.

A Fila pode ser criada pode ser criada para Windows e Linux usando o serviço do Redis como infra-estrutura (Serviço de Cache), ou ser criada em Linux usando a SQS ambientes da Amazon (AWS) (https://aws.amazon.com/pt/sqs/).


  

Hierarquia

  • TQueueListSvc

Construtores

Propriedades

Métodos

Exemplos

Exemplo 1
#include 'protheus.ch'
// Setup Redis
Static cRedisHost := "tec-clima"
Static nRedisPort := 6379
 
User Function FilPut()
  Local nRet       := 0
  // ID da mensagem
  Local cMsgId1    := ""  
  // Mensagem a enviar
  Local cMsg       := '{"inteiro":100,"double":10.32,"OK":true,"NOK":false,"NULO":null,"VAZIO":"","ROWS":[{"Valor":5000,"OBJ":{"x":2},"Data":"2014-08-29"}, "ricardo"], "VETOR":[1,2,3,4]}'
  // Nome da Fila
  Local cQueueName := "Fila_TQLS"
  // Objeto de Fila
  Local oTQLS := Nil
  Local nNO_MSGS := 0
  Local cMsgGet := ""

  // Cria um novo objeto de Fila
  oTQLS := TQueueSvc():New(cQueueName)
  If(oTQLS == Nil)
    ConOut("### ERRO ### " + "Erro na criacao da Fila - " + cQueueName)
    Return .F.
  Else
    ConOut("Criacao da Fila  OK - " + oTQLS:cName)
  EndIf
  // Configurando a Fila
  nRet := oTQLS:Setup(cRedisHost, nRedisPort)
  If nRet != 0
    ConOut("### ERRO ### " + "Erro ao fazer o Setup" + " Erro: " + AllTrim(Str(nRet)))
    Return .F.
  Else
    ConOut("Setup de Fila    OK - " + oTQLS:cName + " nMsgRetPer: " + AllTrim(Str(oTQLS:nMsgRetPer)) + " nVisTimeOut: " + AllTrim(Str(oTQLS:nVisTimeOut)))
  EndIf

  // Gravando uma mensagem
  nRet := oTQLS:PutMsg( @cMsgId1, cMsg )
  If nRet != 0
    ConOut("### ERRO ### " + "Erro ao enviar mensagem" + " Erro: " + AllTrim(Str(nRet)))
    Return .F.
  Else
    ConOut("Enviou msg Fila  OK - " + oTQLS:cName + " com ID: " + cMsgId1 + " Tamanho: " + AllTrim(Str(Len(cMsg))))
  EndIf
 
  // Obtendo valor de indicacao de que nao tem mais mensagens
  nNO_MSGS := oTQLS:eNO_MSGS
  // Obtem a mensagem
  nRet := oTQLS:WaitMsg( @cMsgId1, @cMsgGet, , 3 )
  If nRet != 0
    If(nRet == nNO_MSGS)
      ConOut("OK, nao existem mensagens na Fila.")
    Else
      ConOut("### ERRO ### " + "Erro ao receber mensagem" + " Erro: " + AllTrim(Str(nRet)))
      Return .F.
    EndIf
  Else
    ConOut("Recebeu msg Fila OK - " + oTQLS:cName + " com ID: " + cMsgId1 + " Tamanho: " + AllTrim(Str(Len(cMsgGet))))
  EndIf
 
  // Removendo a mensagem
  nRet := oTQLS:DelMsg( cMsgId1 )
  If nRet != 0
    ConOut("### ERRO ### " + "Erro ao remover mensagem" + " Erro: " + AllTrim(Str(nRet)))
    Return .F.
  Else
    ConOut("Removeu msg Fila OK - " + oTQLS:cName + " com ID: " + cMsgId1)
  EndIf
Return .T.
Exemplo 2 - Chat
/*
Prototipo de um chat para demonstracao de uso de Filas/Listas e servidor de cache

O usarios podem estar rodando em:
- diferentes servidores e servicos (Maquina1 (appserver.exe), Maquina2 (appserver.exe), ...) ou 
- diferentes servicos (Maquina1 (appserver1.exe), Maquina1 (appserver2.exe), ...) ou 
- mesmo servico (Maquina1 (appserver.exe)) ou 
- qualquer combinacao acima

Configuracao:
  Para configurar no seu teste basta voce instalar e configurar o servico do Redis e atualizar
  o IP e a porta onde esta instalado:
  Ex: Redis maquina 10.172.32.7 e porta 6379
  cRedisServer := "10.172.32.7"
  nRedisPort   := 6379
  e/ou
  cFlLsServer  := "10.172.32.7"
  nFlLsPort    := 6379
Obs. se tiver instalado algum Redis com autenticacao nao esqueca de informar em "cRedisAuth" e/ou "cFlLsAuth"
Obs. os servicos de Cache do Redis e de Fila/Lista podem estar em servicos diferentes do Redis


Uso:
  u_ChatLogin
Obs. Abre a tela para fazer login de um usuario


Autor: Ricardo Castro Tavares de Lima (Ricardo Clima)
email: [email protected]


Versao usada no teste:
***  TOTVS S/A  ***
***  www.totvs.com.br  ***
* TOTVS - Build 7.00.170117A - Mar 14 2019 - 11:19:21
* Build: 64 bits
* SVN Revision: 22367
* Build Version: 17.3.0.9
*/

#include "protheus.ch"
#include "Fileio.ch"

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static cRedisServer     := "127.0.0.1"  // Endereco do servidor do Redis
Static nRedisPort       := 6379         // Porta do servidor do Redis
Static cRedisAuth       := ""           // Autenticacao do Redis
Static cFlLsServer      := "127.0.0.1"  // Endereco do servidor de Filas/Listas
Static nFlLsPort        := 6379         // Porta do servidor de Filas/Listas
Static cFlLsAuth        := ""           // Autenticacao do servidor de Filas/Listas

Static oRedis := Nil
Static oLista := Nil

#define LISTA_USUARIOS      "CHAT_LISTA_USUARIOS"
#define LISTA_CONVERSAS     "CHAT_LISTA_CONVERSAS"
#define LISTA_IMAGENS       "CHAT_LISTA_IMAGENS"

#define NOME_ARQ_IMAGEM     "_apagar.bmp"

Static cFimConversa := Chr(3) + "_FIM_DE_CONVERSA_"
Static cImagem      := Chr(3) + "_IMAGEM_"

#define PRT_ERROR(x)        ConOut(time() + " [Thr: " + Strzero(ThreadId(), 5) + "]" + " # ERROR # " + x)
#define PRT_MSG(x)          ConOut(time() + " [Thr: " + Strzero(ThreadId(), 5) + "]" + "           " + x)

#define PRE_CONVERSA        (time() + " -> ")

//////////////////////////////////////////////////////////////////////////////////////////////////////////

User Function ChatLogin
  Local oDlg1
  Local oButton1
  Local oButton2
  Local oButton3
  Local oButton4
  Local oGet1
  Private cGet1

  Public oList2
  Public aList := {}
  Public oOK := LoadBitmap(GetResources(),'br_verde')
  Public oNO := LoadBitmap(GetResources(),'br_vermelho')
  // Usuario Logado
  Public cUsuario := Nil

  Public oList3

  cGet1 := space(20)

  connect()

  DEFINE MSDIALOG oDlg1 FROM 0,0 TO 160,250 PIXEL TITLE "TOTVS CHAT - Velho Novo ADVPL"

  @ 01,01 SAY "Usuario:"
  @ 01,04 MSGET oGet1 VAR cGet1 SIZE 80,10

  oButton1 := tButton():New(30, 85, 'Login',          oDlg1, {|| AddUser()},           35, 15, ,,,.T.)
  oButton2 := tButton():New(30, 10, 'Logout',         oDlg1, {|| cUsuario := Nil},     35, 15, ,,,.T.)
  oButton3 := tButton():New(50, 10, 'LIMPAR CHAT',    oDlg1, {|| CleanAll()},          35, 15, ,,,.T.)
  oButton4 := tButton():New(50, 85, 'SAIR',           oDlg1, {|| oDlg1:End()},         35, 15, ,,,.T.)

  ACTIVATE MSDIALOG oDlg1 CENTERED

Return Nil

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function AddUser()
  Local cCmd := Nil
  Local xRetCmd := Nil
  Local cMsg := Nil
  Local cLog := ""

  If(connect() == .F.)
    Return .F.
  EndIf

  cLog := AllTrim(cGet1)
  cGet1 := space(20)

  If(ValType(cLog) != 'C' .Or. Len(cLog) <=0)
    cMsg := "ta apressado ??? calma, digita o nome primeiro !!!  "
    MessageBox(cMsg, "Nome de usuario invalido", 48)
    PRT_ERROR(cMsg)

    Return .F.
  EndIf

  If(ValType(cUsuario) == 'C' .And. Len(cUsuario) > 0 .And. ! cUsuario == cLog)
    cMsg := "ta apressado mesmo !!! faca o Logout de '" + cUsuario + "'"
    MessageBox(cMsg, "Usuario ja Logado", 48)
    PRT_ERROR(cMsg)
    Return .F.
  EndIf

  cCmd := "SADD " + LISTA_USUARIOS + " '" + cLog + "'"
  If .Not. execCmd(cCmd, @xRetCmd)
    Return .F.
  EndIf

  If(ValType(xRetCmd) == 'N')
    // 1 - adicionou  | 0 - ja estava adicionado
    If(xRetCmd == 1 .Or. xRetCmd == 0)
      If(cUsuario == Nil)
        cUsuario := cLog
      EndIf

      TcUser()
      Return .T.
    Else
      PRT_ERROR("AddUser: " + cLog + " Retorno invalido: " + cValToChar(xRetCmd))
      VarInfo("AddUser", xRetCmd)
      Return .F.
    EndIf
  Else
    PRT_ERROR("AddUser: " + cLog + " Tipo invalido: " + ValType(xRetCmd))
    VarInfo("AddUser", xRetCmd)
    Return .F.
  EndIf

Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function AdcConversa(cConversa)
  Local cCmd := Nil
  Local xRetCmd := Nil

  If(connect() == .F.)
    Return .F.
  EndIf

  cCmd := "SADD " + LISTA_CONVERSAS + " '" + cConversa + "'"
  If .Not. execCmd(cCmd, @xRetCmd)
    Return .F.
  EndIf

  If(ValType(xRetCmd) == 'N')
    // 1 - adicionou  | 0 - ja estava adicionado
    If(xRetCmd == 1 .Or. xRetCmd == 0)
      Return .T.
    Else
      PRT_ERROR("AdcConversa: " + cConversa + " Retorno invalido: " + cValToChar(xRetCmd))
      VarInfo("AdcConversa", xRetCmd)
      Return .F.
    EndIf
  Else
    PRT_ERROR("AdcConversa: " + cConversa + " Tipo invalido: " + ValType(xRetCmd))
    VarInfo("AdcConversa", xRetCmd)
    Return .F.
  EndIf

Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function obtemLista(cNomeLista, aItens, cLog, cIgnora)
  Local nX := 0
  Local cCmd := Nil
  Local xRetCmd := Nil

  aItens := {}

  If(connect() == .F.)
    Return .F.
  EndIf

  cCmd := "SMEMBERS " + cNomeLista
  If .Not. execCmd(cCmd, @xRetCmd)
    Return .F.
  EndIf
  //VarInfo("obtemLista", xRetCmd)

  ASort(xRetCmd)

  For nX := 1 to Len(xRetCmd)
    If(cIgnora == Nil .Or. .Not. (cIgnora == xRetCmd[nX]))
      AAdd(aItens, {.F., xRetCmd[nX]})
      //PRT_MSG(cLog + "[" + cValToChar(nX) + "] = " + xRetCmd[nX])
    EndIf
  Next
  //VarInfo("aItens", aItens)

Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function ListaConversas(aConv)
Return obtemLista(LISTA_CONVERSAS, @aConv, "Conversa", Nil)

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function ListaUsers(aUsers, cUser)
// Se passar o usuario (cUsuario) filtra e nao mostra na lista (deixando por hora so para demonstracao erro ao se tentar conversar consigo mesmo)
  Local lRet := obtemLista(LISTA_USUARIOS, @aUsers, "Usuario", cUser)

  If(oList2 != Nil .And. Len(aUsers) > 0)
    // Seta o vetor a ser utilizado
    oList2:SetArray(@aUsers)

    // Monta a linha a ser exibida no Browse
    oList2:bLine := {||{ If(aUsers != Nil .And. oList2 != Nil .And. aUsers[oList2:nAt, 01], oOK, oNO), If(aUsers != Nil .And. oList2 != Nil, aUsers[oList2:nAt,02], ) } }
    // Evento de DuploClick (troca o valor do primeiro elemento do Vetor)
    oList2:bLDblClick := {|| If(aUsers != Nil .And. oList2 != Nil, aUsers[oList2:nAt][1] := !aUsers[oList2:nAt][1], ), If(aUsers != Nil .And. oList2 != Nil, oList2:DrawSelect(), ), If(aUsers != Nil .And. oList2 != Nil, NovaConversa(aUsers[oList2:nAt][2]), ) }

    oList2:Refresh() // refresh da lista
  EndIf

Return lRet

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function TcUser()
  Local oDlg2

  DEFINE MSDIALOG oDlg2 FROM 0,0 TO 220,300 PIXEL TITLE 'Lista amigos de: ' + cUsuario + " - Velho Novo ADVPL"
  // Cria objeto de fonte que sera usado na Browse
  Define Font oFont Name 'Courier New' Size 0, -12
  // Cria Browse
  oList2 := TCBrowse():New(01 ,01, 120, 100, ,{'CHAT', 'Usuario'},{30, 50},oDlg2,,,,,{||},,oFont,,,,,.F.,,.T.,,.F.,,, )

  ListaUsers(@aList, /*cUsuario*/)

  nMilissegundos := 2000 // Disparo sera de 2 em 2 segundos
  oTimer := TTimer():New(nMilissegundos, {|| ListaUsers(@aList, /*cUsuario*/) }, oDlg2)
  oTimer:Activate()

  oList2:Refresh() // refresh da lista
  ACTIVATE MSDIALOG oDlg2 CENTERED
Return

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function NovaConversa(cNovoUsuario)
  Local cMsg := ""

  If(cUsuario == Nil .Or. Len(cUsuario) <= 0)
    PRT_ERROR("Nao tem usuario Logado")
    Return .F.
  EndIf

  If(cUsuario == cNovoUsuario)
      cMsg := 'E "' + cUsuario + '" disse: ' + CRLF + '- Voce nao deve conversar com voce mesmo "' + cUsuario + '"'
      MessageBox(cMsg, "???", 48)
      PRT_ERROR(cMsg)
    Return .F.
  EndIf

  PRT_MSG(cUsuario + " iniciando com versa com: " + cNovoUsuario)

  TcConversa(cUsuario, cNovoUsuario)
Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function TcConversa(cMeuUsuario, cNovoUsuario)
  Local oFilaEnv := Nil
  Local oFilaRec := Nil
  Local cQueueEnv := Nil
  Local cQueueRec := Nil
  Local nId := cMeuUsuario + "_" + cNovoUsuario
  Local cIdMsgEnv := ""
  Local aLstMsg := {}
  Local oBtEnv := Nil
  Local oBtImg := Nil  
  Local oBtFim := Nil  
  Local oMsgEnv := Nil
  Local oBmp := Nil
  Local cImgPath := ""  
  Private cMsgEnv := ""
  Private oDlg3 := Nil

  PRT_MSG(nId + " " + cMeuUsuario + " conversando com " + cNovoUsuario)

  If(connect() == .F.)
    Return .F.
  EndIf

  AAdd(aLstMsg, {PRE_CONVERSA + ("conversa com " + cNovoUsuario), ""})

  cQueueEnv := LISTA_CONVERSAS + "_" + cMeuUsuario + "_" + cNovoUsuario
  cQueueRec := LISTA_CONVERSAS + "_" + cNovoUsuario + "_" + cMeuUsuario

  oFilaEnv := conFila(cQueueEnv)
  If(oFilaEnv == Nil)
    PRT_ERROR(nId + " Falhou criacao da fila: " + cQueueEnv)
    Return .F.
  EndIf
  If(!AdcConversa(cQueueEnv))
    Return .F.
  EndIf

  oFilaRec := conFila(cQueueRec)
  If(oFilaRec == Nil)  
    PRT_ERROR(nId + " Falhou criacao da fila: " + cQueueRec)
    Return .F.
  EndIf
  If(!AdcConversa(cQueueRec))
    Return .F.
  EndIf

  DEFINE MSDIALOG oDlg3 FROM 0,0 TO 780,600 PIXEL TITLE 'Converva de ' + cMeuUsuario + " com o usuario " + cNovoUsuario
  // Cria objeto de fonte que sera usado na Browse
  Define Font oFont Name 'Courier New' Size 0, -12

  cMsgEnv := space(50)

  // Cria Browse
  oList3 := TCBrowse():New( 01 , 01, 300, 200,,{cMeuUsuario, cNovoUsuario},{150, 150},oDlg3,,,,,{||},,oFont,,,,,.F.,,.T.,,.F.,,, )

  @ 16,01 SAY "Mensagem:"
  @ 16,05 MSGET oMsgEnv VAR cMsgEnv SIZE 150,10
  oBtEnv := tButton():New(207, 210, 'Enviar',  oDlg3, {|| EnviaMensagem(nId, @oFilaEnv, @oFilaRec, oLista, @cIdMsgEnv, aLstMsg, .F., "")}, 35, 12, , , ,.T.)
  oBtImg := SButton():New(207, 260, 15,               {|| cImgPath := AllTrim(cGetFile("Arquivos BMP (*.bmp)|*.bmp", "Envio de Arquivo", 1, "", .F., GETF_LOCALHARD, .F., .T.)), oDlg3:Refresh(), EnviaMensagem(nId, @oFilaEnv, @oFilaRec, oLista, @cIdMsgEnv, aLstMsg, .F., cImgPath) }, oDlg3, .T., , )
  oBtFim := TButton():New(227, 260, 'FIM',     oDlg3, {|| EnviaMensagem(nId, @oFilaEnv, @oFilaRec, oLista, @cIdMsgEnv, aLstMsg, .T., "")}, 35, 12, , , ,.T.)

  LerMessagem(nId, @oFilaRec, @oFilaEnv, oLista, @aLstMsg, @oBmp)

  nMilissegundos := 1000 // Disparo sera de 1 em 1 segundos
  oTimer := TTimer():New(nMilissegundos, {|| LerMessagem(nId, @oFilaRec, @oFilaEnv, oLista, @aLstMsg, @oBmp) }, oDlg3)
  oTimer:Activate()

  //oList3:Refresh() // refresh da lista
  ACTIVATE MSDIALOG oDlg3 CENTERED
  //on init (oDlg3:gotop())
Return

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function EnviaMensagem(nId, oFlEnv, oFlRec, oLst, cIdMsgEnv, aLstMsg, lFim, cImg)
  Local nRet := 0
  Local cMyMsg := ""
  Local cMsgImg := ""

  If (oFlEnv == Nil)
    Return .F.
  EndIf

  If(lFim)
    cMyMsg := cFimConversa
    AAdd(aLstMsg, {PRE_CONVERSA + "#[FIM DE CONVERSA]#", ""})      
  ElseIf(cImg != "")
    If(LeArq(cImg, @cMsgImg))
      cIdImg := UUIDRandom()
      nRet := oLst:PutMsg(cIdImg, cMsgImg)
      If nRet != 0
        PRT_ERROR(nId + " Erro ao enviar imagem" + " Erro: " + AllTrim(Str(nRet)))
        Return .F.
      Else
        PRT_MSG(nId + " Enviou  img Lista - " + oLst:cName + " com ID: " + cIdImg + " Tamanho: " + AllTrim(Str(Len(cMsgImg))))
      EndIf
      cMyMsg := cImagem + cIdImg
      AAdd(aLstMsg, {PRE_CONVERSA + "[enviou imagem: " + cImg + ". Tamanho: " + AllTrim(Str(Len(cMsgImg))) + "]", ""})
    Else
      PRT_ERROR(nId + " Erro ao enviar ler imagem: " + cImg)
      Return .F.
    EndIf
  Else
    cMyMsg := AllTrim(cMsgEnv)
    
    // Nao manda mensagens sem conteudo (remove espacos inicio e fim)
    If(Len(cMyMsg) <= 0)
      cMsgEnv := space(50)
      Return .F.
    EndIf

    AAdd(aLstMsg, {PRE_CONVERSA + cMyMsg, ""})
  EndIf
  cMsgEnv := space(50)

  nRet := oFlEnv:PutMsg(@cIdMsgEnv, cMyMsg)
  If nRet != 0
    PRT_ERROR(nId + "Erro ao enviar mensagem" + " Erro: " + AllTrim(Str(nRet)))
    Return .F.
  Else
    PRT_MSG(nId + " Enviou  msg Fila  - " + oFlEnv:cName + " com ID: " + cIdMsgEnv + " Tamanho: " + AllTrim(Str(Len(cMyMsg))))
  EndIf
  If(lFim)
    // Obs. Poderia finalizar todas as referencias da conversa aqui
    oFlEnv := Nil
    oFlRec := Nil
  EndIf
Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function LerMessagem(nId, oFlRec, oFlEnv, oLst, aLstMsg, oImg)
  Local cIdMsg := ""
  Local cMsg := ""
  Local cIdImg := ""
  Local cMsgImg := ""
  Local nRet := 0
  Local lConversando := .T.
  Local lRet := .F.
  Local lRecNewMsg := .F.

  While(lConversando .And. oFlRec != Nil)
    nRet := oFlRec:WaitMsg(@cIdMsg, @cMsg, , 0)
    If nRet != 0
      If(nRet == oFlRec:eNO_MSGS)
          //PRT_MSG(nId + " " + oFlRec:cName + " OK, nao existem mensagens na Fila")
          lRet := .T.
          lConversando := .F.
          Exit
      Else
          PRT_ERROR(nId + "Erro ao receber mensagem" + " Erro: " + AllTrim(Str(nRet)))
          lRet := .F.
          Exit
      EndIf
    Else
      PRT_MSG(nId + " Recebeu msg Fila  - " + oFlRec:cName + " com ID: " + cIdMsg + " Tamanho: " + AllTrim(Str(Len(cMsg))) + " msg: |" + cMsg + "|")
      nRet := oFlRec:DelMsg(cIdMsg)
      If nRet != 0
        PRT_ERROR(nId + " Erro ao remover mensagem tratada" + " com ID: " + cIdMsg + " Erro: " + AllTrim(Str(nRet)))
        lRet := .F.
        Exit
      Else
        PRT_MSG(nId + " Removeu msg Fila  - " + oFlRec:cName + " com ID: " + cIdMsg + " Tamanho: " + AllTrim(Str(Len(cMsg))))
      EndIf

      If(cMsg == cFimConversa)
        AAdd(aLstMsg, {"", PRE_CONVERSA + "#[SAIU DA CONVERSA]#"})
        lRecNewMsg := .T.
        PRT_MSG(nId + " Saindo da conversa")
        lRet := .F.

        // Obs. Poderia finalizar todas as referencias da conversa aqui
        oFlRec := Nil
        oFlEnv := Nil
        Exit
      ElseIf(cMsg = cImagem)
        cIdImg := SubStr(cMsg, Len(cImagem)+1)
        PRT_MSG(nId + " Procurando Imagem: " + cIdImg)
        nRet := oLst:GetMsg(cIdImg, @cMsgImg)
        If nRet != 0
          PRT_ERROR(nId + "Erro ao receber imagem" + " Erro: " + AllTrim(Str(nRet)))
          lRet := .F.
          Exit
        Else
          PRT_MSG(nId + " Recebeu img Lista - " + oLst:cName + " com ID: " + cIdImg + " Tamanho: " + AllTrim(Str(Len(cMsgImg))))
        EndIf
        nRet := oLst:DelMsg(cIdImg)
        If nRet != 0
          PRT_ERROR(nId + " Erro ao remover mensagem fila imagens " + " com ID: " + cIdImg + " Erro: " + AllTrim(Str(nRet)))
          lRet := .F.
          Exit
        Else
          PRT_MSG(nId + " Removeu img Lista - " + oLst:cName + " com ID: " + cIdImg + " Tamanho: " + AllTrim(Str(Len(cMsgImg))))
        EndIf
        PRT_MSG(nId + " Achou Imagem: " + cIdImg)

        If(.Not. GravaArq(NOME_ARQ_IMAGEM, cMsgImg))
          PRT_ERROR(nId + " Erro ao gravar imagem" + " com ID: " + cIdMsg)
          lRet := .F.
          Exit
        Else
          //PRT_MSG(nId + " Gravou Imagem: " + NOME_ARQ_IMAGEM)
        EndIf

        AAdd(aLstMsg, {"", PRE_CONVERSA + "[recebeu imagem. Tamanho: " + AllTrim(Str(Len(cMsgImg))) + "]"})
        lRecNewMsg := .T.
 
        If(oImg != Nil)
          oImg:Free()
        EndIf

        oImg := TBitMap():New(230, 10, 180, 150, , NOME_ARQ_IMAGEM, .F., oDlg3, , , , .T., , , , , .T.)
        oImg:Refresh()

        If(.Not. RemoveArq(NOME_ARQ_IMAGEM))
          PRT_ERROR(nId + " Erro ao remover imagem" + " com ID: " + NOME_ARQ_IMAGEM)
          lRet := .F.
          Exit
        Else
          //PRT_MSG(nId + " Removeu Imagem: " + NOME_ARQ_IMAGEM)
        EndIf
      Else
        AAdd(aLstMsg, {"", PRE_CONVERSA + cMsg})
        lRecNewMsg := .T.
      EndIf
    EndIf
  End

  If(oList3 != Nil .And. Len(aLstMsg) > 0)
    // Seta o vetor a ser utilizado
    oList3:SetArray(@aLstMsg)

    // Monta a linha a ser exibida no Browse
    oList3:bLine := {||{ aLstMsg[oList3:nAt, 01], aLstMsg[oList3:nAt,02] } }

    If(lRecNewMsg)
      oList3:GoDown()
    EndIf

    oList3:Refresh() // refresh da lista
  EndIf

Return lRet

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function connect()
  If(oLista == Nil)
    If(.Not. conLista(@oLista, LISTA_IMAGENS))
      Return .F.
    EndIf
  Else
  EndIf
  If(oRedis == Nil)
    oRedis := tRedisClient():New()
    oRedis:Connect(cRedisServer, nRedisPort, cRedisAuth)

    If oRedis:isConnected()
      PRT_MSG("Redis conectado.")
      Return .T.
    Else
      PRT_ERROR("Falha de conexao com o Redis.")

      oRedis:Disconnect()
      oRedis := Nil
      Return .F.
    EndIf
  Else
    Return .T.
  EndIf
Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function conFila(cNome)
  Local oFila := Nil
  Local nRet := 0
  Local nMsgRetPeriod := 60
  Local nVisibTimeOut := 5

  If(oFila == Nil)
    oFila := TQueueSvc():New(cNome)

    // Configurando a Fila
    nRet := oFila:Setup(cFlLsServer, nFlLsPort, cFlLsAuth, nMsgRetPeriod, nVisibTimeOut)

    If (nRet == 0)
      PRT_MSG("Setup de Fila OK: " + oFila:cName)
      Return oFila
    Else
      PRT_ERROR("Falha de conexao com a Fila: " + oFila:cName + " Erro: " + cValToChar(nRet))
      oFila := Nil
    EndIf
  Else
    Return oFila
  EndIf
Return Nil

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function conLista(oList, cNome)
  Local nRet := 0
  Local nMsgRetPeriod := 60

  If(oList == Nil)
    oList := TListSvc():New(cNome)

    // Configurando a Lista
    nRet := oList:Setup(cFlLsServer, nFlLsPort, cFlLsAuth, nMsgRetPeriod)

    If (nRet == 0)
      PRT_MSG("Setup de Lista OK: " + oList:cName)
      Return .T.
    Else
      PRT_ERROR("Falha de conexao com a Lista: " + oList:cName + " Erro: " + cValToChar(nRet))
      oList := Nil
    EndIf
  Else
    Return .T.
  EndIf
Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanAll()
  Local nX := 0
  Local aUsrs := {}
  Local aConv := {}
  Local lRet := 0

  If(connect() == .F.)
    Return .F.
  EndIf

  ListaConversas(@aConv)
  For nX := 1 to Len(aConv)
    lRet := CleanFila(aConv[nX][2])
    If(lRet)
      CleanConversa(aConv[nX][2])
    EndIf
  Next

  ListaUsers(@aUsrs, Nil)
  For nX := 1 to Len(aUsrs)
    CleanUser(aUsrs[nX][2])
  Next

  CleanLista()

Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanFila(cNome)
  Local oFila := Nil
  Local nRet  := 0

  If(connect() == .F.)
    Return .F.
  EndIf

  oFila := conFila(cNome)
  If(oFila == Nil)
    PRT_ERROR(nId + " Falhou Setup da fila: " + cNome)
    Return .F.
  EndIf

  nRet := oFila:Destroy()

  If nRet != 0
    PRT_ERROR("Falhou remocao da fila: " + cNome + " erro: " + cValToChar(nRet))
    Return .F.
  Else
    oFila := Nil
    PRT_MSG("Removeu fila: " + cNome)
    Return .T.
  EndIf
Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanLista()
  Local nRet  := 0

  If(oLista == Nil)
    Return .T.
  Else
  EndIf

  nRet := oLista:Destroy()
  If nRet != 0
    PRT_ERROR("Falhou remocao da lista: " + LISTA_IMAGENS + " erro: " + cValToChar(nRet))
    Return .F.
  Else
    oLista := Nil
    PRT_MSG("Removeu lista: " + LISTA_IMAGENS)
    Return .T.
  EndIf
Return .F.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanItem(cNomeLista, aItem, cLog)
  Local cCmd := Nil
  Local xRetCmd := Nil

  If(connect() == .F.)
    Return .F.
  EndIf

  cCmd := "SREM " + cNomeLista + " '" + aItem + "'"
  PRT_MSG('Removendo ' + cLog + ': "' + aItem + '" ...')
  If .Not. execCmd(cCmd, @xRetCmd)
    Return .F.
  EndIf

  //VarInfo("CleanItem " + cLog, xRetCmd)

Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanUser(cUsr)
Return CleanItem(LISTA_USUARIOS, cUsr, "Usuario")

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function CleanConversa(cConv)
Return CleanItem(LISTA_CONVERSAS, cConv, "Conversa")

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function execCmd(cCmd, xRetCmd)
  If .Not. oRedis:exec(cCmd, @xRetCmd):ok()
    PRT_ERROR('Cmd: "' + cCmd + '" erro: ' + Alltrim(Str(oRedis:nError)) + " | " + AllTrim(oRedis:cError))
    Return .F.
  EndIf
Return .T.

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function LeArq(cArq, cRet)
  Local nBloco  := 512
  Local cBuffer := Space(512)  
  Local nBytes  := 0
  Local hArq    := 0
  Local lRet    := .T.

  cRet := ""
  hArq := FOpen(cArq) // Abre o arquivo binario    

  If FError() <> 0
    PRT_ERROR("Arquivo: " + cArq + " Erro de acesso ao arquivo nº " + Str(FError()))
    Return .F.
  EndIf

  While FError() == 0
    nBytes := FRead(hArq, @cBuffer, nBloco) // Le os bytes
    If nBytes < 0
      PRT_ERROR("Arquivo: " + cArq + " Erro de leitura nº " + Str(FError()))
      lRet := .F.
      Exit
    ElseIf nBytes == 0
      PRT_MSG("Arquivo: " + cArq + " lido corretamente")
      lRet := .T.
      Exit
    Else
      cRet += cBuffer
    EndIf
  End
  FClose(hArq) // Fecha o arquivo
Return lRet

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function GravaArq(cArq, cConteudo)
  Local nBytes  := 0
  Local hArq    := 0  
  Local lRet    := .T.

  cRet := ""
  hArq := FCreate(cArq, FC_NORMAL) // Abre o arquivo para gravacao

  If FError() <> 0
    PRT_ERROR("Arquivo: " + cArq + " Erro de acesso ao arquivo nº " + Str(FError()))
    Return .F.
  EndIf

  nBytes := FWrite(hArq, cConteudo) // Grava os bytes
  If nBytes != Len(cConteudo)
    PRT_ERROR("Arquivo: " + cArq + " Erro de gravacao nº " + Str(FError()))
    lRet := .F.
  Else
    //PRT_MSG("Arquivo: " + cArq + " gravado corretamente.")
    lRet := .T.
  EndIf

  FClose(hArq) // Fecha o arquivo
  
Return lRet

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Static Function RemoveArq(cArq)
  Local lRet    := .T.
  Local nRet    := 0

  nRet := FErase(cArq) // Remove o arquivo
  If nRet != 0
    PRT_ERROR("Arquivo: " + cArq + " Erro de remocao nº " + Str(FError()) + " ret: "  + AllTrim(Str(nRet))) 
    lRet := .F.
  Else
    //PRT_MSG("Arquivo: " + cArq + " removido corretamente.")
    lRet := .T.
  EndIf

Return lRet

//////////////////////////////////////////////////////////////////////////////////////////////////////////

Observações

Para Windows a versão requer uma biblioteca para acesso ao Redis (rdwincli.dll), e para Linux acessando o SQS da AWS em versões superior "7.00.131227A-20180417 NG" requer a biblioteca (libaws-totvs-interface.so).

Arquivos: rdwincli_32bits.zip  |  rdwincli_64bits.zip  |  libaws-totvs-interface_32bits.zip  |  libaws-totvs-interface_64bits.zip

Abrangência

Totvs Application Server com release superior a 7.00.131227A-20160120 NG


Veja também

  • Sem rótulos