• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

nokPT

Aplicação deixa de responder

9 mensagens neste tópico

Bom dia,

Tenho uma aplicação relativamente simples e sem grande processamento.

No início -> leio alguns ficheiros para a memória, estabeleço comunicação com um OPC Server e faço alguns cálculos (a 1ª vez que faz os cálculos demora algum tempo)

Depois a cada segundo -> renova os dados do OPC Server, recalcula e actualiza o ecran

A cada minuto -> grava os dados no disco.

A parte da renovação OPC está assincrona.

O problema é que no computador do cliente a aplicação deixa de responder.

O SplashScreen ainda funciona bem, depois Desenha (e mal ou nem acaba) o Form Principal, sempre que passo o rato por cima da form, aparece uma ampulheta, quando carrego num botão as ordens aparentemente não são executadas (na realidade são passado largos segundos ou minutos).

Os restantes programas do computador estão a funcionar em pleno (aparentemente) só o meu é que se passa.

Abri o taskman, a minha aplicação tem sempre o CPU a 0% excepto a cada minuto quando grava que vai entre 1% a 15% (momentaneamente), o CPU está disponível 99%.

No meu PC raramente está nos 0% (costuma andar entre os 2 e 3% com picos aos 8 a 15%)

No Taskman do cliente já aumentei a prioridade para o máximo, o resultado foi o mesmo

A memória usada ronda os 32k

Já pensei em acrescentar no temporizador do segundo Application.DoEvents(), só que não sei se vai resolver, no meu computador fiquei com a aplicação mais lenta, principalmente a redesenhar a form.

O computador do cliente tem alguns programas instalados (meio marados) que bloqueiam o acesso ao taskman, ao menu iniciar, etc... Esses programas também estão a comunicar com o OPC Server, mas a empresa que desenvolveu o OPC Server diz que aceita várias ligações de clientes e efectivamente no meu programa já tive várias ligações em simultâneo sem problema nenhum.

O que posso/devo fazer para o meu programa não ficar completamente esquecido?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

usa threads.

para:

comunicação,

ler ficheiros,

actualizar GUI.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Nas comunicações já estou a usar threads (comunicação assincrona), ler ficheiros só acontece no início, escrever, é um instante, é um ficheiro com 19k (mesmo assim já tentei usar threads, mando gravar um array com 1440 registo, só que com a threads quando mandava grava em vez de gravar o array existente, criava novo, mas como é um instantinho a cada minuto, não me preocupou)

relativamente às actualizações, nas forms complicadas tenho theads, mas na principal altero apenas 3 textbox e 1 botão, para isso não é necessário uma thead.

Além do mais o programa está a 0% no taskman, não me parece que seja esse o problema, o problema é que o programa deixa mesmo de responder, inclusive, na barra dos programas quando carregamos com o botão do lado direito aparece um menu, para fechar, minimizar, etc..., neste caso aparece um rectângulo cinza sem texto, e se carregar no X (fechar) do programa nada acontece só passado alguns largos segundos ou minutos.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

ok, se estás a usar threads, e estão correctamente "implementadas", só mesmo vendo o software para determinar qual o problema.

cumps.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

No início só tenho 1 thread activa, as outras estão noutras forms que mal consigo abrir.

O código é este:

    Friend Sub PLC2Valores()
        ' Leitura assincrona
        Try
            If OPCServerConnect = True And OPCServerUpdate = True Then
                Static Dim wTransID As Integer = 1000
                Dim wCancelID As Integer
                Dim Errors As Array

                wTransID = wTransID + 1
                OPCMyGroup.AsyncRead(NúmeroItems, sH, Errors, wTransID, wCancelID)
                Exit Sub
            Else
                Dim i%
                For i = 1 To NúmeroItems
                    wQuality(i) = 0
                Next i

                TrataDados()
                mdlDados.RepresentarValores() ' Passa 2 valores para a textbox
                mdlDados.RepresentarLinha() ' Passa 1 valor para outra textbox

            End If
        Catch ex As Exception
            GravaErro("Erro #2 -> mdlOPC -> " & ex.Message)
            Exit Sub
        End Try
    End Sub

    Private Sub OPCMyGroup_AsyncReadComplete(ByVal TransactionID As Integer, ByVal NumItems As Integer, ByRef ClientHandles As System.Array, ByRef ItemValues As System.Array, ByRef Qualities As System.Array, ByRef TimeStamps As System.Array, ByRef Errors As System.Array) Handles OPCMyGroup.AsyncReadComplete
        Try
            For i As Integer = 1 To NumItems
                oVal(ClientHandles(i)) = ItemValues(i)
                dTime(ClientHandles(i)) = TimeStamps(i)
                wQuality(ClientHandles(i)) = Qualities(i)
            Next i

            TrataDados()
            mdlDados.RepresentarValores()
            mdlDados.RepresentarLinha()
        Catch ex As Exception
            GravaErro("Erro #3 -> mdlOPC -> " & ex.Message)
        End Try
    End Sub

No meu PC, com montes de programas abertos e a correr funcionava lindamente (piorou com a inclusão do Application.DoEvents())

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

podes colocar aqui a forma como invocas o PLC2Valores() e o pedaço de codigo que chama o Doevents?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Chamo a rotina a cada segundo:

    Private Sub tmrCalcula_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tmrActualiza.Tick
        Try
            ' Envia emails
            Dim Ontem As System.DateTime = Now.AddDays(-1) ' Data de ontem
            ' Envia o email diário às 0h0m0s de cada dia se houver destinatários
            If Now.Hour = 0 And Now.Minute = 0 And Now.Second() = 0 And (mdlEmails.Email.Para <> "" Or mdlEmails.Email.Cc <> "" Or mdlEmails.Email.Bcc <> "") Then
                Email.Para = RelatórioDiário.Destinatários.Para
                Email.Cc = RelatórioDiário.Destinatários.Cc
                Email.Bcc = RelatórioDiário.Destinatários.Bcc
                Email.Ano = Ontem.Year
                Email.Mes = Ontem.Month
                Email.Dia = Ontem.Day
                Email.TipoRelatório = "Diário"
                Dim ThreadEmail As New Thread(New ThreadStart(AddressOf EnviaEmail))
                ThreadEmail.Name = "ThreadEmailDiário"
                ThreadEmail.Start()
            End If
            ' Envia o email mensal às 0h0m10s do 1º dia do mês se houver destinatários
            If Now.Day = 1 And Now.Hour = 0 And Now.Minute = 0 And Now.Second() = 10 And (mdlEmails.Email.Para <> "" Or mdlEmails.Email.Cc <> "" Or mdlEmails.Email.Bcc <> "") Then
                Email.Para = RelatórioMensal.Destinatários.Para
                Email.Cc = RelatórioMensal.Destinatários.Cc
                Email.Bcc = RelatórioMensal.Destinatários.Bcc
                Email.Ano = Ontem.Year
                Email.Mes = Ontem.Month
                Email.Dia = Ontem.Day
                Email.TipoRelatório = "Mensal"
                Dim ThreadEmail As New Thread(New ThreadStart(AddressOf EnviaEmail))
                ThreadEmail.Name = "ThreadEmailMensal"
                ThreadEmail.Start()
            End If
            ' Envia o email anual às 0h0m20s do 1º dia do ano se houver destinatários
            If Now.Month = 1 And Now.Day = 1 And Now.Hour = 0 And Now.Minute = 20 And Now.Second() = 10 And (mdlEmails.Email.Para <> "" Or mdlEmails.Email.Cc <> "" Or mdlEmails.Email.Bcc <> "") Then
                Email.Para = RelatórioAnual.Destinatários.Para
                Email.Cc = RelatórioAnual.Destinatários.Cc
                Email.Bcc = RelatórioAnual.Destinatários.Bcc
                Email.Ano = Ontem.Year
                Email.Mes = Ontem.Month
                Email.Dia = Ontem.Day
                Email.TipoRelatório = "Anual"
                Dim ThreadEmail As New Thread(New ThreadStart(AddressOf EnviaEmail))
                ThreadEmail.Name = "ThreadEmailAnual"
                ThreadEmail.Start()
            End If

            PLC2Valores()       ' Faz refress aos valores

            If Now.Second = 1 Then ' Só grava a cada minuto
                Valores2Disco()
                CalculaValores()    ' Recalcula os valores
            End If

            ' Sinaliza alarmes activos
            If NúmeroAlarmesActivos > 0 Then ' Há um alarme activo
                btnAlarmes.BackColor = Color.Red
                btnAlarmes.ForeColor = Color.Yellow
            ElseIf NúmeroAlarmesActivos < 0 Then ' Há um erro de comunicação num dos alarmes
                btnAlarmes.BackColor = Color.Yellow
                btnAlarmes.ForeColor = Color.Black
            Else ' Não há alarmes nem erros de comunicação
                btnAlarmes.BackColor = Color.WhiteSmoke
                btnAlarmes.ForeColor = Color.Black
            End If

            Application.DoEvents()

        Catch ex As Exception
            GravaErro("Erro #6 -> frmMain -> " & ex.Message)
        End Try
    End Sub

1º testo para enviar uns email (para enviar uso threads), mas só envia à meia-noite

Depois chamo a rotina (todos os segundo)

Calculo e gravo (a cada minuto)

Finalmente altero a cor de um botão

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

boas.

chamas a rotina todos os segundos ?

logo chamas o  PLC2Valores()  todos os segundos.

e chamas o Doevents todos os segundos.

basicamente só usas threads para os emails, embora a comunicação com o servidor opc seja assincrona.

a "logica" está mal concebida, por isso encrava.

podes fazer isto de varias formas.

1)Garantir que em cada ciclo do timer só chamas uma vez o PLC2Valores()  e esperas que este termine. Para isso no inicio do tmrCalcula_Tick paras o timer e só o reactivas na função OPCMyGroup_AsyncReadComplete

isto implica que o timer não corra em cada segundo. Se isto for critico podes fazer da seguinte forma:

2)em cada interacção do Timer tmrCalcula_Tick , colocas os dados que são necessários para o funcionamento do PLC2Valores()  numa queue.

2.1)Tens outro timer com um timming que definas > que 2minutos cuja função é ir retirar dados à queue e executar a função PLC2Valores()  usando o principio do ponto 1).

em ambas as abordagens deves retirar o doEvents. do Timer.

outra questão. Não devem ser lançadas threads (neste caso mail)à lá lagardene... especialmente dentro de um timer. Neste caso particular não há problema pois elas só são lançadas numa hh:mm:ss muito especifico.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Depois de muitas cabeçadas  :wallbash:e após muito pensar :hmm: cheguei à conclusão que estava a fazer erros infantis  :-[:

Estava a testar a minha aplicação com uma ligação ao autómato em ethernet@100Megas e cliente tem uma ligação RS232@19,2K

Reduzi drasticamente o número de variáveis de comunicação (passei de 200 para 53), aumentei o tempo de polling (passei de 1x por segundo para 1x por cada 5 segundos)

Apesar da comunicação ser assíncrona, passai a chamar todo o processo de comunicação noutra thread

E o resultado foi uma substancial melhora no funcionamento

Vou continuar com os testes e escalonar ainda mais as comunicações (por exemplo 20% das variáveis/segundo = 100% em 5 segundos) para ver se melhor ainda mais, porque se deixar o meu programa ligado durante 10minutos e desligar, continua a haver tráfego na porta durante alguns segundos, o que é mau para uma aplicação que vai funcionar 24horas por dia.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora