Ir para conteúdo
  • Cadastre-se

dev botao

Possível correção para problemas de Out of Memory


  • Este tópico foi criado há 337 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Citando o problema occorido no post sobre Out of Memory no Bloco I, estava pensando em um forma de modificar o componente com o menor impacto possível. Achei uma solução que me parece ser adequada e gostaria de saber se é aceitavel esse tipo de modificação.

Hoje, o componente trabalha com alguns dados sendo alimentados em um TObjectList, o qual são percorridos quando chama a função SaveFileTXT. A minha ideia foi, ao inves de ficar segurando esses itens até o fim, porque não ir alimentado o arquivo conforme os registros de cada bloco iriam sendo finalizados? Com isso em mente, eu precisava informar pro componente que iria querer trabalhar de uma forma diferenciada quanto a alimentação do arquivo, o qual nos leva a primera modificação no ACBrSpedContabil.pas:

procedure IniciaGeracao(const pWriteManual: Boolean = False);

Esse parametro será salvo em uma variavel (privada) que será usada ao longo dos WriteBlocos presentes no componente. Como o nome indica, os Writes (tanto WriteBloco_? quanto WriteRegistro????, com exceção dos ?990 que são totalizadores) serão feitos manualmente, ou seja, o aplicativo precisa chamar esses comandos, o que nos leva a segunda modificação no ACBrSpedContabil.pas:

public
  procedure WriteRegistro0000;
  procedure WriteRegistro0001;
  ...
  procedure WriteRegistroI001;
  ...
  procedure WriteRegistroJ001;
  ...

Com esses comandos em public, a sua aplicação ja consegue acessa-los.

Apartir daqui, precisei informar aos WriteBloco_? que quando for em modo WriteManual, ele não deve chamar as rotinas que não sejam de totalizadores. Segue (alguma das, são basicamente iguais em todos os blocos, menos no 9) a terceira modificação no ACBrSpedContabil.pas:

procedure TACBrSPEDContabil.WriteBloco_0;
begin
  if Bloco_0.Gravado then exit ;

  if not FInicializado then
     raise Exception.Create( 'Métodos "IniciaGeracao" não foi executado' );

  if not(FWriteManual) then
  begin
    WriteRegistro0000;
    WriteRegistro0001;
    WriteRegistro0007;
    WriteRegistro0035;
    WriteRegistro0020;
    WriteRegistro0150;
    //WriteRegistro0180;
  end;

  WriteRegistro0990;

  Bloco_0.WriteBuffer;
  Bloco_0.Conteudo.Clear;
  Bloco_0.Gravado := true;
end;

Os outros blocos seguem o mesmo padrão, apenas o WriteRegistro?990 esta fora da validação do WriteManual, exceto o WriteBloco_9. Segue o mesmo:

procedure TACBrSPEDContabil.WriteBloco_9;
begin
  if Bloco_9.Gravado then Exit;

  if not Bloco_9.Gravado then
    WriteBloco_K;

  if not(FWriteManual) then
    WriteRegistro9001;

  WriteRegistro9900;
  WriteRegistro9990;
  WriteRegistro9999;

  Bloco_9.WriteBuffer;
  Bloco_9.Conteudo.Clear;
  Bloco_9.Gravado := true;
end;

Como o proprio bloco ja é um totalizador em si, e só é necessário informar o registro 9001, ele contem outras rotinas fora da validação do WriteManual.

Falando no Bloco 9, ele me apresentou casos que me fizeram modificar o ACBrECDBloco_9.pas. Segue ela:

function TRegistro9900List.FindByREG_BLC(const pValue: String; out pReg: TRegistro9900): Boolean;
var
  i: Integer;
begin
  Result := False;

  for i := 0 to (Count - 1) do
    if (TRegistro9900(Items[i]).REG_BLC = pValue) then
    begin
      pReg := TRegistro9900(Items[i]);
      Result := True;
      Break;
    end;
end;

Em registros como o do I200, K030, existe a relação de mestre-detalhe entre os itens, e como os WriteRegistro???? sempre chamam a rotina que cria o seu respectivo totalizador no bloco 9, foi preciso fazer um jeito de que quando existisse mais de um registro mestre, ele não ficasse criando registros subsequentes no bloco 9, e apartir disso, surgiu a rotina acima.

Voltando ao ACBrSpedContabil.pas, como precisava usar a FindByREG_BLC, ja aproveitei e dei ao luxo de centralizar a rotina que gera os totalizadores do bloco 9. Segue ela:

procedure TACBrSPEDContabil.AddRegistro9900(const pREG_BLC: String; const pQTD_REG_BLC: Integer);
var
  Reg: TRegistro9900;
begin
  if (pQTD_REG_BLC > 0) then
  begin
    if not(Bloco_9.Registro9900.FindByREG_BLC(pREG_BLC, Reg)) then
    begin
      Reg := Bloco_9.Registro9900.New;
      Reg.REG_BLC := pREG_BLC;
    end;

    Reg.QTD_REG_BLC := Reg.QTD_REG_BLC + pQTD_REG_BLC;
  end;
end;

Com essa rotina, os WriteRegistro???? passaram a utilizar elas ao inves de cada um chamar o bloco 9 individualmente. Segue alguns exemplos:

// NOVO
procedure TACBrSPEDContabil.WriteRegistro0000;
begin
  Bloco_0.WriteRegistro0000;
  AddRegistro9900('0000', 1);
end;
// ANTIGO
procedure TACBrSPEDContabil.WriteRegistro0000;
begin
   with Bloco_9.Registro9900.New do
   begin
      REG_BLC := '0000';
      QTD_REG_BLC := 1;
   end;
   Bloco_0.WriteRegistro0000;
end;
// NOVO
procedure TACBrSPEDContabil.WriteRegistro0007;
begin
  Bloco_0.WriteRegistro0007;
  AddRegistro9900('0007', Bloco_0.Registro0007.Count);

  if FWriteManual then
    Bloco_0.Registro0007.Clear;
end;
// ANTIGO
procedure TACBrSPEDContabil.WriteRegistro0007;
begin
   if Bloco_0.Registro0007.Count > 0 then
   begin
      with Bloco_9.Registro9900.New do
      begin
         REG_BLC := '0007';
         QTD_REG_BLC := Bloco_0.Registro0007.Count;
      end;
   end;
   Bloco_0.WriteRegistro0007;
end;
// NOVO
procedure TACBrSPEDContabil.WriteRegistro0150;
begin
  Bloco_0.WriteRegistro0150;
  AddRegistro9900('0150', Bloco_0.Registro0150.Count);
  AddRegistro9900('0180', Bloco_0.Registro0180Count);

  if FWriteManual then
  begin
    Bloco_0.Registro0150.Clear;
    Bloco_0.Registro0180Count := 0;
  end;
end;
// ANTIGO
procedure TACBrSPEDContabil.WriteRegistro0150;
begin

   Bloco_0.WriteRegistro0150;

   if Bloco_0.Registro0150.Count > 0 then
   begin
      with Bloco_9.Registro9900.New do
      begin
         REG_BLC := '0150';
         QTD_REG_BLC := Bloco_0.Registro0150.Count;
      end;
   end;

   if Bloco_0.Registro0180Count > 0 then
   begin
      with Bloco_9.Registro9900.New do
      begin
         REG_BLC := '0180';
         QTD_REG_BLC := Bloco_0.Registro0180Count;
      end;
   end;

end;

Como você pode perceber, algumas dessas rotinas começarão a dar Clear no TObjectList, e, zerar as variaveis de Count dos detalhes, quando presente. Isso acontece pois cada Bloco_?.WriteRegistro???? percorre sempre todo o TObjectList, então cada vez que chamarmos manualmente essa rotina, ele não pode mais alimentar o que ja havia sido alimentado antes. O mesmo serve para a variavel de Count, como a rotina AddRegistro9900 é incremental, precisa zera-la, para que na proxima leva, incremente apenas o que foi informado ali.

Feito isso, o componente já esta preparado para funcionar dessa nova forma de alimentação.

Como implementar isso na minha aplicação?

Usando apenas os blocos acimas para exemplificar, segue como é feito hoje:

SPED.IniciaGeracao;

with SPED.Bloco_0 do
begin
  with Registro0000 do
    ...

  with Registro0007.New do
    ...

  for i := 0 to 2 do
    with Registro0150.New do
    begin
      ...
    
      with Registro0180.New do
        ...
    end;
end;

SPED.SaveFileTXT;

E como deverá ser feito, caso queira utilizar o WriteManual:

SPED.IniciaGeracao(True); // NOVO

with SPED.Bloco_0 do
begin
  with Registro0000 do
    ...
  WriteRegistro0000; // NOVO

  with Registro0007.New do
    ...
  WriteRegistro0007; // NOVO

  for i := 0 to 2 do
  begin
    with Registro0150.New do
    begin
      ...

      with Registro0180.New do
        ...
    end;

    WriteRegistro0150; // NOVO
  end;
end;

SPED.WriteBloco_0; // NOVO

SPED.SaveFileTXT;

Seguindo essa lógica para os outros blocos/registros, seus dados finais deverão ser identicos ao mesmo metodo de hoje, e também identicos quando for usado WriteManual False.

Também modifiquei a rotina TotalizarTermos usando um método que achei na internet (não consigo mais achar) pois a base de dados que estava usando para fazer os testes resulta em arquivos consideravelmente grandes (4gb+, 40mi+ linhas) e a rotina antiga simplismente não finalizava.

ACBrECDBloco_9.pas ACBrSpedContabil.pas

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Só uma correção no exemplo de como fica pra utilizar esse novo metodo (os Writes deveriam ser do SPED.WriteRegistro???? não do with SPED.Bloco_0 do)

SPED.IniciaGeracao(True); // NOVO

with SPED.Bloco_0 do
begin
  with Registro0000 do
    ...
  SPED.WriteRegistro0000; // NOVO

  with Registro0007.New do
    ...
  SPED.WriteRegistro0007; // NOVO

  for i := 0 to 2 do
  begin
    with Registro0150.New do
    begin
      ...

      with Registro0180.New do
        ...
    end;

    SPED.WriteRegistro0150; // NOVO
  end;
end;

SPED.WriteBloco_0; // NOVO

SPED.SaveFileTXT;
Link para o comentário
Compartilhar em outros sites

  • 1 mês depois ...

 

Em 06/02/2023 at 13:14, Daniel InfoCotidiano disse:

@geovanesilveira
Boa tarde !
Obrigado pela contribuição, foi gerada uma tarefa TK-3583 para análise.
 

Opa, percebi só agora que havia feito o upload dos arquivos errados. Segue os corretos agora

ACBrECDBloco_9.pas

ACBrSpedContabil.pas

Editado por geovanesilveira
  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • 1 mês depois ...
  • Moderadores
28 minutos atrás, nickolas.deluca disse:

Bom dia, alguma novidade sobre isso? Toda vez que atualizamos o nosso repositório interno somos obrigados a aplicar essa correção em cima dos fontes do ACBr. Seria bem interessante se essa solução fosse incorporada ao repositório oficial.

favor aguardar. vai para a lista de tarefas da semana  e assim que estiver ok será enviado. temos diversos tasks

Consultor SAC ACBr Juliomar Marchetti
 

Projeto ACBr

skype: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br
MVP_NewLogo_100x100_Black-02.png
 

 

Link para o comentário
Compartilhar em outros sites

  • Moderadores
10 minutos atrás, nickolas.deluca disse:

Por acaso temos acesso a essa lista para acompanhamento?

Não. ela é interna. mas é assinalado aqui no tópico quando subir ao svn

  • Obrigado 1
Consultor SAC ACBr Juliomar Marchetti
 

Projeto ACBr

skype: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br
MVP_NewLogo_100x100_Black-02.png
 

 

Link para o comentário
Compartilhar em outros sites

  • Administradores

Boa tarde,

Novamente obrigada pelo interesse em contribuir, alocamos para a sprint desta semana.

At.

  • Curtir 1
Consultora SAC ACBr

Juliana Tamizou

Gerente de Projetos ACBr / Diretora de Marketing AFRAC
Ajude o Projeto ACBr crescer - Seja Pro

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.  Discord

Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil


Participe de nosso canal no Discord e fique ainda mais próximo da Comunidade !!

Link para o comentário
Compartilhar em outros sites

  • Consultores

@geovanesilveira Fizemos uma atualização no SVN com a sua implementação, porém de forma parcial.

A rotina relativa ao controle dos registros do SPED foi implementada. Obrigado!
Porém o ajuste que você fez para a rotina TotalizarTermos não seria compatível com o Delphi 7 e por esse motivo não a incluímos no momento.
Como você mesmo cita que encontrou a rotina na internet (localizamos algo aqui https://stackoverflow.com/a/35572202/460775), talvez seja o caso de avaliar uma alternativa que possa manter a compatibilidade com o Delphi 7.

Nesse momento, se for possível, gostaria que você atualize seus fontes e reinstale os componentes e verifique se a rotina da forma que está é funcional para você.

A ideia a partir de agora é buscar uma rotina que possa fazer a substituição de texto em arquivos grandes e que possamos incluir a mesma em uma das units ACBrUtil, mas sempre levando em conta que temos que atender implementações a partir do Delphi 7 também.

Agradecemos mais uma vez a contribuição e aguardamos um retorno das validações!

  • Curtir 1
Consultor SAC ACBr

Alexandre de Paula
Ajude o Projeto ACBr crescer - Assine o SAC                    

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.  ícone Discórdia Discord   

Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil

 

 

Link para o comentário
Compartilhar em outros sites

Atenção, a classe helper da que falo abaixo deverá ser apenas utilizada por programadores que também tem problema na rotina TotalizarTermos, executada pelo SaveFileTXT. Se você não tem esse problema, ignore este post.

Fiz os testes aqui, e esta funcionando normal a parte dos registros, porem, ele não consegue totalizar os termos. Olhando bem, acho que esse tipo de situação poderia ser resolvida pelo proprio programador, disponibilizando a rotina TotalizarTermos para ser usada em classes helper. Com isso, a rotina SaveFileTXT não poderia ser mais utilizada, ja que é ela que finalizava o processo. Agora seria necessário chamar o TotalizarTermos e FinalizarGeracao (rotina nova) no lugar do SaveFileTXT.

Do jeito que eu havia feito nos uploads anteriores, é preciso acessar a variavel FACBrTXT, logo, precisei alterar a TACBrSPEDContabil pra disponibilizar o acesso à esse objeto.

Após reinstalar os componentes do ACBr, criei o seguinte helper:

unit ExemploHelper;
...
TACBrSPEDContabilHelper = class helper for TACBrSPEDContabil 
public
  procedure TotalizarTermos;
end;

Comparação de como era o código antes e de como vai ficar:

// ANTIGO
SPED.IniciaGeracao(True);
...
SPED.SaveFileTXT;

// NOVO
uses
  ExemploHelper;
...
try
  SPED.IniciaGeracao(True);
  ...
  SPED.TotalizarTermos;
finally
  SPED.FinalizarGeracao;
end;

 

ACBrSpedContabil.pas

Editado por geovanesilveira
  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Consultores

Obrigado mais uma vez pela contribuição @geovanesilveira.

Mas de qualquer forma precisamos manter a padronização dos componentes e portanto ainda vamos avaliar como podemos fazer com que funcione tanto no Delphi 7 quanto nas versões mais novas e infelizmente os "Class Helpers" não funcionam no Delphi 7 também.

Consultor SAC ACBr

Alexandre de Paula
Ajude o Projeto ACBr crescer - Assine o SAC                    

Projeto ACBr     Telefone:(15) 2105-0750 WhatsApp(15)99790-2976.  ícone Discórdia Discord   

Projeto ACBr - A maior comunidade Open Source de Automação Comercial do Brasil

 

 

Link para o comentário
Compartilhar em outros sites

3 minutos atrás, Alexandre de Paula disse:

Obrigado mais uma vez pela contribuição @geovanesilveira.

Mas de qualquer forma precisamos manter a padronização dos componentes e vamos ainda avaliar como podemos fazer com q

Confesso que não considero uma boa solução mesmo, foi apenas o jeito mais rapido e pratico que achei de dar uma alternativa às pessoas que tiveram esse mesmo problema na rotina, e que ainda queiram usufruir dos fontes comitados, sem precisar ter um controle dessa unit por fora, ou por um repositorio local

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Este tópico foi criado há 337 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.