Transferência e Supervisão
Início  Anterior  Próximo



Quando o usuário seleciona uma opção de menu ou disca um número de ramal, o atendedor tenta transferir para este ramal. Caso dê ocupado ou não atender ele transfere para um ramal de fuga e se ainda assim não conseguir, transfere para outro ramal de fuga. Este ciclo é idêntico para o ramal desejado e para os ramais de fuga independente da quantidade.

Por isso, utilizamos a técnica de criar uma lista para os ramais que serão discados. Obviamente se o ramal desejado for atendido, a ligação é entregue e o ciclo termina. Esta lista é do tipo TStringList e se chama lstRamal.

Ela deve ser declarada como global, criada no evento OnShow e destruída no evento OnClose.

A supervisão consiste no sistema monitorar se um ramal está sendo chamado sem sucesso, der ocupado ou ainda ser atendido. No caso de ramal ocupado, a VoicerLib gera o evento OnBusyDetected. Já o tom de chamada gera o evento OnCalling e o atendimento deve ser tratado no evento OnAnswerDetected.

No sistema de atendimento da Digivoice, como em tantos outros, para transferir a ligação é necessário dar um flash e em seguida discar o ramal desejado. O início da discagem foi implementado no evento OnPlayStop porque sempre antes de transferir uma ligação o sistema falará "aguarde um instante..." como pode ser observado na listagem anterior (PlayFile).

A mensagem "aguarde um instante..." não admite nenhum tipo de interrupção por dígito, por isso somente o caso ssNormal do evento OnPlayStop precisa ser considerado:
 
procedure TForm1.voice1PlayStop(Sender: TObject; Port,  
  StopStatus: Smallint);  
begin  
 
  case StopStatus of  
 ssNormal:  //MENSAGEM FOI ATE O FIM  
 begin  
       case Estado[Port] of  
      (........)  
       TRANSFERENCIA:  
       begin  
         if (lstRamal.Count > 0) then  //ainda tem ramais                      
         begin  
           if nEstadoRamal[Port] <> LIVRE then  
           begin  
             //verifica se  
             nEstadoRamal[Port] := LIVRE;  
             voice1.PlayFile(Port,'sisaguar.sig','');  
           end  
           else  
           Begin  
             //prepara discagem  
             InsereDado(Port,'Executando Flash...');  
             nEstadoFlash[Port] := DISCA;  
             voice1.Flash(Port,700,1500);  
           end;  
         end  
         else  
         begin  
           //Acabaram os ramais da lista  
      //Fala "tente mais tarde"  
           Estado[Port] := TENTEMAISTARDE;  
           voice1.PlayFile(Port,'MAISTARD.SIG','');  
         end;  
       end;  
       TENTEMAISTARDE:  
       begin  
         Sleep(100);  
         voice1.HangUp(Port);  
         Estado[Port] := NADA;  
         InsereDado(Port,'Não ouve atendimento. Desligado!');  
         InsereDado(Port,'Esperando nova ligação...');  
       end;  
    end;  
     ssDigitReceived:          
     begin  
    (........)  
 end;   //submenu  
  end;  
(........)  
 

Repare que a lógica de tratamento da lista é idêntica à lista de frases de boas vindas no início do atendimento. Cada vez que o sistema entrar neste bloco, ele terá discado para o ramal, supervisionado e detectado que o ramal não atendeu o estava ocupado. Isto se repetirá até que a lista lstRamal esteja vazia. Quando isso acontecer, o sistema falará "tente mais tarde", sairá da rotina e voltará a ela no tratamento do estado TENTEMAISTARDE que desligará e colocará no estado NADA, pronto para esperar uma nova ligação.
 
Mais uma vez reforçamos esta característica assíncrona que a VoicerLib tem. Repare que dentro do evento OnPlayStop é executado o PlayFile. O fluxo continua e sai do OnPlayStop. Quando a mensagem termina o evento é chamado novamente. Não se trata de recursividade.  
 
O primeiro bloco da rotina anterior inicia a transferência através do comando Flash. Antes é necessário atribuir a variável nEstadoFlash com o valor DISCA para que possa ser diferenciado a situação de flash para discar e flash para retomar a ligação (em caso de ocupado ou não atender). Como o flash é um comando lento, sua conclusão dar-se-á no evento OnAfterFlash:  
 
procedure TForm1.voice1AfterFlash(Sender: TObject; Port:   
                                               Smallint);  
begin  
   case nEstadoFlash[Port] of  
  DISCA:  
  begin  
    InsereDado(Port,'Discando para o Ramal ' +   
                                    lstRamal.Strings[0]);  
    voice1.Dial(Port, lstRamal.Strings[0],1000);  
    nChamando[Port] := 1;  
  end;  
(......)  
 
A variável nChamando[Port] é inicializada em 1 para contar os toques de chamada. No nosso sistema as ligações são retomadas após 5 toques.

O início da supervisão se dará após a discagem no evento OnAfterDial:
 
procedure TForm1.voice1AfterDial(Sender: TObject; Port:   
                                                Smallint);  
begin  
  case Estado[Port] of  
TRANSFERENCIA:  
begin  
   InsereDado(Port,'Iniciando Supervisão...');  
   voice1.EnableCallProgress(Port);  
   voice1.EnableAnswerDetection(Port);  
   //12 segundos ate o primeiro toque  
   nContaTimeOutAtende[Port] := 12;    
end;  
 end;  
end;  
 
A supervisão é iniciada com o EnableCallProgress que monitora os tons de "chamando" e ocupado e o EnableAnswerDetection que monitora o atendimento.  
 
Com a placa VoicerPhone utilizando um headset é necessário desligar o microfone antes de habilitar as supervisões de linha  
 
Neste momento o sistema deverá estar preparado para 3 situações: atendimento, ocupado e não atende.  
 
O atendimento poderá ser tratado de duas maneiras: a primeira é através da detecção propriamente dita que gera o evento OnAnswerDetected:  
 
procedure TForm1.voice1AnswerDetected(Sender: TObject; Port:   
                                                  Smallint);  
begin  
  case Estado[Port] of  
TRANSFERENCIA:  
begin  
   voice1.DisableAnswerDetection(Port);  
   voice1.DisableCallProgress(Port);  
   InsereDado(Port,'Ligação Entregue. Esperando nova...');  
   voice1.HangUp(Port);  
   Estado[Port] := NADA;  
end;  
  end;  
end;  

A segunda forma de detecção de atendimento não envolve comandos da placa. Em algumas ocasiões a placa poderá não detectar o atendimento através do método acima. Isso é devido a condições de linha, etc... Nestes casos o sistema jamais entenderia o atendimento e consequentemente não transferiria a ligação, causando um comportamento indesejado.

Para cobrir 100% das vezes é necessário criar um timeout para atendimento através de um componente do tipo Timer, colado no form. Este timer seria utilizado da seguinte maneira.
 
procedure TForm1.tmrGeralTimer(Sender: TObject);  
var  
  Port: integer;  
begin  
  for Port := 1 to 20 do  
   begin  
 case Estado[Port] of  
 TRANSFERENCIA:  
 begin  
    Dec(nContaTimeOutAtende[Port]);  
    if nContaTimeOutAtende[Port] = 0 then  
    begin  
      //timeout, significa que atendeu  
      InsereDado(Port,'Timeout de atendimento');  
      //chama a rotina que trata o atendimento  
      voice1AnswerDetected(Sender,Port);  
    end;  
 end;  
end;  
  end;  
end;  

A partir do momento que o sistema disca, considera-se que, se não houver sinal de chamada ou ocupado por um determinado tempo, houve um atendimento. Por isso que no evento OnAfterDial a variável nContaTimeOutAtende é inicializada com 12, indicando que o sistema esperará até 12 segundos para o primeiro sinal de chamada (OnCalling). Após o primeiro, o timeout cairá para 8 segundos entre cada sinal de chamada. Para maiores detalhes observe também a listagem do evento OnCalling logo a seguir.

Ao ser detectado o atendimento, é importante desabilitar todas as supervisões para não interferir no funcionamento do próximo atendimento. Em seguida desliga-se e muda a variável Estado para NADA.

A cada sinal de "chamando" o evento OnCalling é chamado:
 
procedure TForm1.voice1Calling(Sender: TObject; Port:   
                                                   Smallint);  
begin  
  case Estado[Port] of  
TRANSFERENCIA:  
begin  
   InsereDado(Port,IntToStr(nChamando[Port])+   
                                        'o. Toque...');  
   //8 segundos ate os proximos toques  
   nContaTimeOutAtende[Port] := 8;               
   if nChamando[Port] > NUMTOQUESRETOMADA then  
   begin  
       //deu ocupado  
       voice1.DisableAnswerDetection(Port);  
       Sleep(10);  
       voice1.DisableCallProgress(Port);  
       InsereDado(Port,'Ramal não atende...');  
       nEstadoRamal[Port] := NAOATENDE;  
       nEstadoFlash[Port] := RETOMA;  
       voice1.Flash(Port,700,1500);  
   end  
  else  
      Inc(nChamando[Port]);  
end;  
   end;  
end;  

A variável nChamando[Port] é incrementada a cada tom recebido. Se ela ultrapassar NUMTOQUESRETOMADA o programa prepara a retomada da ligação. Seta a variável nEstadoRamal para NAOATENDE e a nEstadoFlash para RETOMA. A primeira é necessária para falar "ramal não atende" e a segunda para o tratamento específico de retomada no evento OnAfterFlash (listada mais adiante):

No caso de dar ocupado, o evento OnBusyDetected é gerado:
 
procedure TForm1.voice1BusyDetected(Sender: TObject; Port:   
                                                   Smallint);  
begin  
   case Estado[Port] of  
 TRANSFERENCIA:  
 begin  
   //deu ocupado na transferencia  
   voice1.DisableAnswerDetection(Port);  
   Sleep(10);  
   voice1.DisableCallProgress(Port);  
   InsereDado(Port,'Ocupado na Supervisão...');  
   nEstadoRamal[Port] := OCUPADO;  
   nEstadoFlash[Port] := RETOMA;  
   voice1.Flash(Port,700,1500);  
 end;  
 else  
 begin     //qualquer outro caso desliga  
   Sleep(100);  
   voice1.HangUp(Port);  
   Estado[Port] := NADA;  
   InsereDado(Port,'Cliente Desligou.');  
   InsereDado(Port,'Esperando nova ligação...');  
  end;  
 end;  
end;  

Você tem o caso de TRANSFERENCIA e todos os outros no bloco else. Nos casos diferentes de transferência, ao receber um ocupado o sistema simplesmente desliga a ligação e volta o Estado para NADA, esperando novas chamadas.

No caso de TRANSFERENCIA o sistema desabilita as supervisões, seta a variável nEstadoRamal para OCUPADO e a nEstadoFlash para RETOMA. A primeira é necessária para falar "ramal ocupado" e a segunda para o tratamento específico de retomada no evento OnAfterFlash:
 
procedure TForm1.voice1AfterFlash(Sender: TObject; Port:   
                                               Smallint);  
begin  
   case nEstadoFlash[Port] of  
      (......)  
  RETOMA:  
  begin  
    InsereDado(Port,'Retomando Ligação...');  
    try  
       lstRamal.Delete(0);  
    except  
    end;  
    //fala ramal ocupado ou ramal nao atende  
    if nEstadoRamal[Port] = OCUPADO then  
    begin  
         voice1.PLayFile(Port,'SISROCUP.SIG','')  
    end  
    else  
    begin  
          voice1.PLayFile(Port,'SISRNAOA.SIG','');  
       end;  
 end;   
(......)  

Ao retomar a ligação, depois do flash, é necessário excluir o primeiro ramal da lista (para que o próximo se torne o ramal corrente) e falar "ramal ocupado" ou "ramal não atende". A discagem para o próximo ramal (se houver) será iniciada novamente no evento OnPlayStop (consulte a listagem da página 70). Este ciclo se repetirá até que não haja mais ramais.