Ler e Salvar Arquivos de Configuração *.ini

Bom dia,

Estou em processo de migração de um sistema que utiliza o Elipse Scada para o Elipse E3.

No Scada, eu utilizava alguns arquivos .ini para salvar backup de parâmetros.

Embora no E3 a idéia seja salvar estes parâmetros nas instâncias dos Xobjects, eu gostaria de poder importar os parâmetros dos arquivos .ini através de um script.

Existem no E3 métodos similares aos ReadIniNumber / WriteIni do Elipse Scada? Como posso fazer essa leitura pelo E3?

Atenciosamente,

Nivaldo Nicolau

@nivaldonicolau, boa tarde.

Como a ideia é ler/escrever parâmetros em um arquivo, há duas formas:

  1. Importar/Exportar arquivos

2) Lendo/Escrevendo arquivos TXT

Sds.

@Fernando,

Não existe um método específico para ler/escrever configurações de um arquivo .ini?

Veja como está a estrutura do meu arquivo:

[TP-124]
HH=95
H=55
L=50
LL=0
BM=0
[BC-112]
KP=50
KI=10
KD=0
LS=85
LI=10
[TP-126]
HH=95
H=90
L=70
LL=0
BM=0
[BC-113]
KP=-500
KI=10
KD=0
LS=100
LI=10

Queria algo pronto, tal qual existe no Elipse Scada.

No Elipse Scada, quando eu quero ler os atributos de um determinado equipamento eu utilizo assim:

Variavel1 = ReadIniNumber(“C:\caminhodoarquivo\nomearquivo.ini”, “TP-124”, “HH”)

E assim, Variavel1 recebe o valor “95”.

Da mesma forma, quando quero salvar um parâmetro no arquivo eu utilizo assim:

WriteIni(“c:\caminhodoarquivo\nomearquivo.ini”, “TP-214”, “HH”, “95”)

@nivaldonicolau,

Não há um método específico para isto. Contudo o artigo do item 2 deve ajudar.

EDIT 1: Neste site tem dois scripts exemplificando como fazer isso, mas ainda não testei. Irei testar e retorno.

EDIT 2: Testei os scripts do site e funcionam. É necessário fazer algumas modificações. Aqui tem uma aplicação exemplo.

Sds.

@Fernando,

Coincidentemente já estava com este site aqui aberto. Entretanto como é um código que precisaria ser reutilizado várias vezes estava à procura de algo já integrado.

Como você indicaria a implementação de uma função parametrizável no E3, para evitar a repetição do mesmo código para cada XObject/XControl diferente?

@nivaldonicolau, bom dia.

Em qual momento precisaria reutilizá-lo? Tem algum evento específico? Seria algo dinâmico?

Desculpe-me, mas não compreendi bem a sua dúvida.
Veja se estou certo: o que gostaria que eu apresentasse seria uma forma de executar esta função apenas chamando um método assim como no Elipse SCADA? Por exemplo, está desenvolvendo um script num lugar qualquer e, ao chamar o ReadIni, gostaria que realizasse uma função internamente?

Fico à disposição.

Sds.

@Fernando, bom dia.

Ficou claro pra mim que a função não existe implementada no E3, tal como no Scada.

O que eu gostaria de saber agora é qual a melhor forma de encapsular código no E3, de forma a ser replicado dentro de uma aplicação, dinamicamente.

Podemos pegar a própria função em questão, a ReadIni.

Se eu informar os parâmetros de entrada: arquivo, seção e variável, eu tenho um retorno diferente.

Se eu fosse fazer num sistema de linguagem aberta, eu implementaria uma função:

Public Function ReadIni (arquivo, secao, variavel)
(
{Código de Implementação da função}
)

E depois chamaria a função quantas vezes fosse necessário alterando os parâmetros de entrada.

Valor1 = ReadIni(“teste.ini”, “Secao1”, “Variavel1”)
Valor2 = ReadIni(“teste.ini”, “Secao1”, “Variavel2”)
etc.

A pergunta é como como fazer isto de forma otimizada no E3?

@nivaldonicolau,

Desta forma ficou mais claro.

Sugiro a utilização de XObject neste caso.

Obs.: Seria algo parecido com a aplicação exemplo disponibilizada aqui, mas os valores seriam mudados nas propriedades do XObject.

Sds.

1 Like

@Fernando

Não estou conseguindo acessar o seu exemplo. Link quebrado.

@nivaldonicolau,

Tenta neste (este link é temporário).

1 Like

@Fernando, bom dia!

Agora consegui abrir!
Me ajudou bastante! Não tinha ciência de que era possível criar Function dessa maneira. Entretanto ela só funciona dentro do contexto local.

Tentei criar um xObject como você sugeriu, mas estou com dificuldade de fazer funcionar.

Eu fiz assim:

Criei 6 propriedades no xObject:

  • strArquivo - String
  • strSecao - String
  • strChave - String
  • strValor - String
  • Ler - Boolean
  • Escrever - Boolean

Depois defini o script a seguir no evento OnPropertyChange da Propriedade Ler:

Sub Funcoes_OnLerChanged()
strValor = ReadIni(strArquivo, strSecao, strChave)
End Sub
Public Function ReadIni( myFilePath, mySection, myKey )
’ This function returns a value read from an INI file

’ Arguments:
’ myFilePath [string] the (path and) file name of the INI file
’ mySection [string] the section in the INI file to be searched
’ myKey [string] the key whose value is to be returned

’ Returns:
’ the [string] value for the specified key in the specified section

’ CAVEAT: Will return a space if key exists but value is blank

’ Written by Keith Lacelle
’ Modified by Denis St-Pierre and Rob van der Woude

Const ForReading   = 1
Const ForWriting   = 2
Const ForAppending = 8
Dim intEqualPos
Dim objFSO, objIniFile
Dim strFilePath, strKey, strLeftString, strLine, strSection
Set objFSO = CreateObject( "Scripting.FileSystemObject" )
ReadIni     = ""
strFilePath = Trim( myFilePath )
strSection  = Trim( mySection )
strKey      = Trim( myKey )
If objFSO.FileExists( strFilePath ) Then
    Set objIniFile = objFSO.OpenTextFile( strFilePath, ForReading, False )
    Do While objIniFile.AtEndOfStream = False
        strLine = Trim( objIniFile.ReadLine )
        ' Check if section is found in the current line
        If LCase( strLine ) = "[" & LCase( strSection ) & "]" Then
            strLine = Trim( objIniFile.ReadLine )
            ' Parse lines until the next section is reached
            Do While Left( strLine, 1 ) <> "["
                ' Find position of equal sign in the line
                intEqualPos = InStr( 1, strLine, "=", 1 )
                If intEqualPos > 0 Then
                    strLeftString = Trim( Left( strLine, intEqualPos - 1 ) )
                    ' Check if item is found in the current line
                    If LCase( strLeftString ) = LCase( strKey ) Then
                        ReadIni = Trim( Mid( strLine, intEqualPos + 1 ) )
                        ' In case the item exists but value is blank
                        If ReadIni = "" Then
                            ReadIni = " "
                        End If
                        ' Abort loop when item is found
                        Exit Do
                    End If
                End If
                ' Abort if the end of the INI file is reached
                If objIniFile.AtEndOfStream Then Exit Do
                ' Continue with next line
                strLine = Trim( objIniFile.ReadLine )
            Loop
        Exit Do
        End If
    Loop
    objIniFile.Close
    
Else
    MsgBox strFilePath & " doesn't exists. Exiting..."
    Wscript.Quit 1
End If

End Function

Sub Vazio()
End Sub

A seguir criei uma instância do xObject com o nome Funcoes e onde eu preciso executá-la estou usando o código a seguir:

Dim Funcoes
Set Funcoes = Application.GetObject("DadosPCC.Funcoes1")
Funcoes.strArquivo = "\\MAIN_SERVER\TESTE_AUTOMACAO\RECEITAS\Backup.ini"
Funcoes.strSecao = ComandoAnalogico.FonteAnalogica.Tag
Funcoes.strChave = "HH"
Funcoes.Ler = True
Funcoes.Ler = False
Msgbox Funcoes.strValor

Consigo acompanhar a alteração das propriedades que são atribuídas diretamente corretamente, porém a propriedade strValor continua em branco.

Não consigo entender o que estou fazendo de errado. Você pode me dizer o que eu preciso fazer diferente?

Desde já agradeço a atenção.

Olá Nivaldo!

Está faltando retornar um valor para a função. Veja um exemplo no KB da Elipse (link abaixo).

http://kb.elipse.com.br/pt-br/questions/1057

Bom dia @pgustavo!

Se você observar o script está codificado para passar o retorno da função para a propriedade strValor.

Sub Funcoes_OnLerChanged()
strValor = ReadIni(strArquivo, strSecao, strChave)
End Sub
Public Function ReadIni( myFilePath, mySection, myKey )
–Código da função
End Function
Sub Vazio()
End Sub

Porém acho que este script não está sendo executado quando faço a atribuição:
Ler = True

Codificando a função em um botão como o exemplo do KB que você postou funcionou corretamente. Entretanto eu gostaria de codificar a função globalmente.

Nivaldo,

No final da Function tem que ter algo assim:

ReadIni = Valor

@pgustavo,

Eu já testei a função e ela funciona corretamente.
A minha dúvida é que vou precisar da mesma função em várias partes do meu supervisório e não gostaria de ter que replicar o código da função várias vezes. Segundo o @Fernando, eu deveria usar um xObject para codificar, só que estou perdido em como fazer esta parte.
A minha aproximação usando o evento OnPropertyChange de uma variável booleana não funcionou e agora não sei o que fazer a não ser repetir o código da função toda vez que precisar chamá-la em cada botão de cada xControl diferente espalhado pelo aplicativo.

Nivaldo,

A propriedade strValor do seu XObject está vazia porque a função ReadIni não está retornando nenhum valor. Para que a função retorne um valor, você precisa ter o código que eu mencionei acima. Como no exemplo do artigo que eu passei:

Function Soma(a, b)
Soma = a + b
End Function

Oi @pgustavo,

Aparentemente identifiquei o problema.

A função estava retornando o valor em branco porque não estava localizando o arquivo.

Alterei a função para quando não encontrar o arquivo retornar a mensagem “Arquivo Inexistente”, ficando assim:

Sub Funcoes_OnLerChanged()
	Funcoes.strValor =  "Evento aconteceu!! " & strArquivo & " | " & strSecao & " | " & strChave & " | " & ReadIni(Funcoes.strArquivo, Funcoes.strSecao, Funcoes.strChave)
End Sub
Function ReadIni( myFilePath, mySection, myKey ) 
    Const ForReading   = 1
    Const ForWriting   = 2
    Const ForAppending = 8

    Dim intEqualPos
    Dim objFSO, objIniFile
    Dim strFilePath, strKey, strLeftString, strLine, strSection

    Set objFSO = CreateObject( "Scripting.FileSystemObject" )

    ReadIni     = ""
    strFilePath = Trim( myFilePath )
    strSection  = Trim( mySection )
    strKey      = Trim( myKey )

    If objFSO.FileExists( strFilePath ) Then
        Set objIniFile = objFSO.OpenTextFile( strFilePath, ForReading, False )
        Do While objIniFile.AtEndOfStream = False
            strLine = Trim( objIniFile.ReadLine )

            ' Check if section is found in the current line
            If LCase( strLine ) = "[" & LCase( strSection ) & "]" Then
                strLine = Trim( objIniFile.ReadLine )

                ' Parse lines until the next section is reached
                Do While Left( strLine, 1 ) <> "["
                    ' Find position of equal sign in the line
                    intEqualPos = InStr( 1, strLine, "=", 1 )
                    If intEqualPos > 0 Then
                        strLeftString = Trim( Left( strLine, intEqualPos - 1 ) )
                        ' Check if item is found in the current line
                        If LCase( strLeftString ) = LCase( strKey ) Then
                            ReadIni = Trim( Mid( strLine, intEqualPos + 1 ) )
                            ' In case the item exists but value is blank
                            If ReadIni = "" Then
                                ReadIni = " "
                            End If
                            ' Abort loop when item is found
                            Exit Do
                        End If
                    End If

                    ' Abort if the end of the INI file is reached
                    If objIniFile.AtEndOfStream Then Exit Do

                    ' Continue with next line
                    strLine = Trim( objIniFile.ReadLine )
                Loop
            Exit Do
            End If
        Loop
        objIniFile.Close
        
    Else
    	ReadIni = "Arquivo Inexistente"
    End If
End Function

Sub Vazio()
End Sub

A função quando executada no viewer (com o código num botão do xControl por exemplo) localiza o arquivo pelo caminho UNC normalmente.
Já a mesma função quando executada pelo servidor (com o código no xObject) não localizou o arquivo pelo caminho UNC.

Copiando o arquivo para o C: da máquina servidora e chamando o arquivo localmente a função retornou os valores corretamente.

PS.: Todos os testes foram feitos na mesma máquina (servidor).

Você sabe porque esse erro ocorre?

O Viewer é local ou remoto?

Terei os 2. Fiz os testes no viewer local.

Edit 1: Sendo mais específico.

Se eu executar:

Com o meu xObject implementado.

Na tela crio um botão e nele coloco o seguinte código, me retorna uma mensagem com o valor: 95

Sub CommandButton1_Click()
	Dim Funcoes
	Set Funcoes = Application.GetObject("DadosPCC.Funcoes1")
	Funcoes.strArquivo = "C:\Backup.ini"
	Funcoes.strSecao ="TP-124"
	Funcoes.strChave = "HH"
	Funcoes.Ler = True
	MsgBox Funcoes.strValor
End Sub

Se aponto para o arquivo na rede me retorna uma mensagem com “Arquivo Inexistente”

Sub CommandButton2_Click()
	Dim Funcoes
	Set Funcoes = Application.GetObject("DadosPCC.Funcoes1")
	Funcoes.strArquivo = "\\MAIN_SERVER\TESTES_AUTOMACAO\RECEITAS\Backup.ini"
	Funcoes.strSecao ="TP-124"
	Funcoes.strChave = "HH"
	Funcoes.Ler = True
	MsgBox Funcoes.strValor
End Sub

Agora se crio um terceiro botão com a função ReadIni implementada e chamo ela pelo botão, a função retorna o valor correto:

Sub CommandButton3_Click()
	MsgBox ReadIni("MAIN_SERVER\TESTE_AUTOMACAO\RECEITAS\Backup.ini", "TP-124", "HH")
End Sub
Function ReadIni( myFilePath, mySection, myKey ) 
    Const ForReading   = 1
    Const ForWriting   = 2
    Const ForAppending = 8

    Dim intEqualPos
    Dim objFSO, objIniFile
    Dim strFilePath, strKey, strLeftString, strLine, strSection

    Set objFSO = CreateObject( "Scripting.FileSystemObject" )

    ReadIni     = ""
    strFilePath = Trim( myFilePath )
    strSection  = Trim( mySection )
    strKey      = Trim( myKey )

    If objFSO.FileExists( strFilePath ) Then
        Set objIniFile = objFSO.OpenTextFile( strFilePath, ForReading, False )
        Do While objIniFile.AtEndOfStream = False
            strLine = Trim( objIniFile.ReadLine )

            ' Check if section is found in the current line
            If LCase( strLine ) = "[" & LCase( strSection ) & "]" Then
                strLine = Trim( objIniFile.ReadLine )

                ' Parse lines until the next section is reached
                Do While Left( strLine, 1 ) <> "["
                    ' Find position of equal sign in the line
                    intEqualPos = InStr( 1, strLine, "=", 1 )
                    If intEqualPos > 0 Then
                        strLeftString = Trim( Left( strLine, intEqualPos - 1 ) )
                        ' Check if item is found in the current line
                        If LCase( strLeftString ) = LCase( strKey ) Then
                            ReadIni = Trim( Mid( strLine, intEqualPos + 1 ) )
                            ' In case the item exists but value is blank
                            If ReadIni = "" Then
                                ReadIni = " "
                            End If
                            ' Abort loop when item is found
                            Exit Do
                        End If
                    End If

                    ' Abort if the end of the INI file is reached
                    If objIniFile.AtEndOfStream Then Exit Do

                    ' Continue with next line
                    strLine = Trim( objIniFile.ReadLine )
                Loop
            Exit Do
            End If
        Loop
        objIniFile.Close
        
    Else
    	ReadIni = "Arquivo Inexistente"
    End If
End Function

Sub Vazio()
End Sub

Por algum motivo, o xObject que executa no servidor não está encontrando o arquivo pelo caminho UNC.

Quando o script está no XObject, ele será executado no processo do E3Run, que roda na conta do usuário SYSTEM. Talvez esteja faltando liberar o acesso ao arquivo para este usuário.

1 Like