Comunicação entre autómatos e computadores
Uma parte integrante de um sistema de automação (tal como qualquer equipamento do nosso dia-a-dia), é o diálogo homem-máquina.
Botoneiras e sinalizadores
Um simples botão para ligar o equipamento tem que dizer On/Off ou O/I ou Ligar/Desligar, caso contrário torna-se muito difícil operar.
À medida que a complexidade do sistema de automação vai aumentando, aumenta o número de botões de comando e as sinalizações para informação do que se está a passar com o equipamento:
O uso de botoneiras e sinalizadores têm as suas vantagens: são mais baratas (unitariamente), mais robustas ao manuseamento e de fácil manutenção. No entanto, o comando gerado por uma botoneira ou para um sinalizador está limitado a Sim/Não (podendo uma botoneira ter várias posições, mas sempre limitadas).
Consolas
Uma alternativa ou complemento ao interface de botoneiras e sinalizadores são as consolas de comando e/ou tácteis. Vantagens:
- São mais compactas, ocupando menos espaço físico,
- Têm a capacidade de guardar histórico de eventos e/ou alarmes
- Apresentar uma sinalização mais completa por exemplo: numa avaria podem dizer o que está avariado e ter uma indicação das possíveis causas, um sinalizador apenas sinaliza o que está avariado.
- Permitem ordens mais complexas, um botão “diz” apenas liga ou não liga (desliga), numa consola podemos dizer liga a X%, por exemplo.
- Depois de adquirida uma consola, o custo de mais um botão ou sinalização virtual dentro da consola, é nulo.
- Dão um ar mais profissional à coisa, principalmente quando são policromáticas.
Estes equipamentos têm, no entanto, algumas desvantagens a ter em conta:
- São desenvolvidos pelo fabricante para comunicar com alguns tipos de autómatos, não todos.
- Têm que ser programadas com um software específico do fabricante, à excepção da PanelView Component da Allen-Bradley que são programadas com um browser da internet.
- Capacidade de memória limitada.
- O seu custo, quando comparado com um botão, é imenso, quando comparado com um conjunto de botões e sinalizadores, pode ser inferior. Quando comparado com um computador, pelo preço de uma mini-consola compramos um computador standard.
Computador
Da mesma maneira, que há aplicações em que é necessário o uso de consolas porque o uso de botoneiras e sinalizadores não é suficiente, há aplicações que exigem o uso de computadores, por exemplo:
- Grande complexidade do sistema
- Rastreabilidade
- DataLog ou registos
- Relatórios automáticos
- Envio de dados para a gestão da empresa
Por outro lado, como:
- O custo do computador é relativamente baixo (quando comparado com uma consola mediana).
- As capacidades, potencialidades e possibilidade de expansão de um computador são muito superiores.
- A capacidade de armazenamento é ilimitada.
- O custo de reparação é baixo.
Tornam atractivo o uso do computador, num sistema de automação, na vez de uma consola.
Introdução
Neste artigo, vamos debater exclusivamente o uso do computador como software de visualização ou SCADA num sistema de automação. Vamos falar sobre:
- Software dos fabricantes vs software “caseiros”,
- Exemplos concretos de software “caseiros” a funcionar
- Vantagens, desvantagens, limitações
- Alguns cuidados a ter em conta
Softwares dos fabricantes
Uma hipótese para fazermos um programa de visualização, é usarmos um programa específico de desenvolvimento do fabricante do autómato que estamos ou vamos utilizar.
Teremos que adquirir um software de desenvolvimento e em cada aplicação teremos que adquirir e fornecer ao cliente um software de runtime.
Vantagens
- Garantia de comunicação com o nosso autómato.
- Integração directa (sem engenharia) das variáveis do autómato, no nosso programa (há excepções a nível de fabricantes e/ou gama de autómatos).
- Software do fabricante desenvolvido para desenho gráfico.
- De fácil implementação.
- Existência de alguns templates.
- Suporte técnico especializado.
- Prestígio da marca.
Desvantagens
Custo do software de runtime
O custo do software de runtime virgem a fornecer ao cliente é tanto maior quanto maior for a instalação.
O custo do software, por norma, depende do número de tags. Uma tag é uma variável de comunicação autómato-computador, um bit (On/off), um byte (8 bits) ou uma string (conjunto de N bytes) é considerado uma tag.
Estes programas são vendidos para trabalhar com pacotes de tags, por exemplo 128, 512 ou 2048 tags.
Há softwares, por exemplo o RSViewSE da Allen-Bradley, que em vez de contabilizar tags, contabiliza número de ecrãs: 25, 100 ou 250 ecrã, neste caso o nº de tags por ecrã é ilimitado, mas o princípio é o mesmo, quanto mais ecrãs mais caro o programa.
Software vendido “às peças”
Por norma os softwares base dos fabricantes, apenas permitem comunicar com o autómato e fazer representação gráfica.
As opções, também são pagas. Por exemplo no WinCC Flexible da Siemens se quisermos gravar e/ou ler ficheiros/arquivos do computador temos que comprar a respectiva opção, se quisermos ter receitas, temos que comprar outra opção.
Software limitado
Com o software dos fabricantes, a nossa imaginação (ou do cliente) não é o limite, estamos limitados às possibilidades disponibilizadas pelo fabricante.
VB limitado
Por norma, os softwares permitem o uso de VB ou VBA, mas a sua utilização é limitada, temos que fazer algumas funções simples à mão, por exemplo arredondar um valor.
O seu uso intenso em alguns softwares fazem-no tornar extremamente pesado e lento.
Ficamos eternamente presos ao software
A partir do momento em que desenvolvemos um projecto num software de um fabricante, não é possível migrar ou converter para outro software (excepto claro do mesmo fabricante, com as devidas alterações e correcções).
Compatibilidade entre sistemas operativos limitada ou inexistente
Por norma, os softwares dos fabricantes são desenvolvidos para o Windows e não são compatíveis com outros sistemas operativos.
E mesmo dentro dos sistemas Windows, a sua migração do XP para o Vista, demorou alguns anos a ser concretizada.
Softwares “Caseiros”
Ao referir software caseiro, não estou minimamente a menosprezar ou a dar menos qualidade ao software em si, estou apenas a distingui-lo dos softwares desenvolvidos pelos fabricantes dos autómatos.
Há duas maneiras de fazer um software caseiro:
- Usando um OPC Server
- Desenvolvendo comunicação directa com o autómato
OPC Server
Um OPC Server é um programa cuja finalidade é estabelecer a comunicação entre o autómato e computador.
Ao adquirirmos um OPC Server também temos a garantia de comunicação autómato-computador.
No nosso programa, temos que implementar um OPC Client.
Para receber ou enviar dados do ou para o autómato, usamos o OPC Cliente para comunicar com o OPC Server para este comunicar com o autómato.
Vantagens:
- O custo de um OPC Server é (muito) inferior ao custo de um Runtime.
- Há mais concorrentes a fazer e desenvolver OPC Server.
- Do lado da comunicação OPC Server-Autómato, só varia o tipo ou marca de OPC Server, o OPC Client e o nosso software são sempre os mesmos.
- Depois de desenvolvido o OPC Client é fácil a sua adaptação e expansão.
- Não está limitado em número de tags ou ecrãs.
- Por norma, cada OPC Server permite a ligação de vários OPC Clients.
- É multi-plataforma.
- Pode facilmente ser usado um OPC Server de outro fabricante, para ser OPC Server tem que ser certificado pela organização OPC, garantindo assim a sua função.
Desvantagens:
- É necessário um técnico mais especializado para desenvolver o software de visualização e configurar o OPC Server.
- Suporte técnico do fabricante do autómato mais limitado.
- Não há integração das variáveis de comunicação com o projecto de automação, têm que ser linkadas à mão.
- Software feito à medida em linguagem não standard industrial.
- Por norma, há dois tipos de licenças: para comunicar só com 1 autómato (licença mais barata), para comunicar com vários autómatos (licença mais cara).
Exemplo
Escolha do tipo de OPC
Há vários tipos de OPC Server, o que nos interessa, nesta fase, é o OPC Server DA.
Este OPC Server para cada tag ou item de comunicação disponibiliza 3 variáveis:
- Value – o valor actual ou o último valor recebido.
- Quality – representa a qualidade de comunicação desta variável, uma má qualidade, quer dizer que não está a comunicar.
- TimeStamp – Data e Hora do último valor recebido.
Os itens estão (opcionalmente) agrupados em Groups, permitindo intervalos de update diferentes.
Configuração dos OPC
O primeiro passo que temos que fazer é escolher, adquirir, instalar e configurar o OPC Server.
A configuração do OPC Server varia de fabricante para fabricante, é recomendável a leitura do quick-starter do fabricante.
Teste de comunicação do OPC
Depois de configurar o OPC Server é necessário testá-lo.
Para isso o fabricante fornece no pacote um OPC Client.
Caso não forneça, há vários OPC Cliente grátis na internet.
Desenvolvimento da aplicação
' IMPORTANTE -> Não esquecer de incluir na Reference -> Interop.OPCAutomation.dll do OPC Server instalado
' Módulo relativo às configurações e comunicações OPC
Module mdlOPC
' Estrutura de um item de comunicação com o OPC Server
Structure stuItem
Dim Value As Object ' Valor actual ou último valor recebido
Dim TimeStamp As Date ' Data e Hora do último valor recebido
Dim Quality As Short ' Qualidade da comunicação com este item
End Structure
' Estrutura das variáveis que vamos comunicar com o autómato
Structure stuValoresAutómato
Dim PrimeiroValor As stuItem
Dim SegundoValor As stuItem
' criar as restantes variáveis
End Structure
' Dados de comunicação com o autómato, o que realmente vamos tratar no resto do programa
Dim ValoresAutómato As stuValoresAutómato
' Dados relativos ao servidor OPC
Dim WithEvents OPCMyServer As OPCAutomation.OPCServer
Dim WithEvents OPCMyGroups As OPCAutomation.OPCGroups
Dim WithEvents OPCMyGroup As OPCAutomation.OPCGroup ' Nome do meu grupo de itens (Podemos criar mais grupos com tempos de actualização diferentes)
Dim OPCMyGroupItems As OPCAutomation.OPCItems ' Os itens do meu grupo
Friend OPCServerConnected As Boolean = False ' Estado de ligado/desligado ao OPC Server
Friend OPCServerConnectError As Boolean = False ' Erro de ligação ao OPC Server
Friend OPCServerUpdate As Boolean = True ' Actualiza ou não os valores
Friend Const NúmeroItemsMyGroup As Integer = 10 ' Número de itens dentro do meu grupo
Dim sItemNameMyGroup(NúmeroItemsMyGroup) As String ' Nome/endereço do meu item
Dim cHMyGroup(NúmeroItemsMyGroup) As Integer
Dim sHMyGroup(NúmeroItemsMyGroup) As Integer
Friend oValMyGroup(NúmeroItemsMyGroup) As Object
Friend dTimeMyGroup(NúmeroItemsMyGroup) As Date
Friend wQualityMyGroup(NúmeroItemsMyGroup) As Short
Friend sItemIDsMyGroup(NúmeroItemsMyGroup) As String
Friend Sub LigaçãoOPC()
Try
' Liga se estiver desligado
' Desliga se estiver ligado
If OPCServerConnected = False Then
' O Servidor não está ligado
'--- connect OPCServer
OPCMyServer = New OPCAutomation.OPCServer
OPCMyServer.Connect("CimQuestInc.IGOPCAB.1", "") ' Mudar o nome para ligar a outro OPC Server, por exemplo para RSLinx da Allen-Bradley
OPCMyGroups = OPCMyServer.OPCGroups
' Cria os item do meu grupo e liga-os ao servidor, se o resultado for False, então houve um erro na ligação
OPCServerConnectError = Not CriaItemsMyGroup()
' Caso haja mais grupos criados, fazer o mesmo (que a linha de cima) para os outros grupos
OPCServerConnected = True ' Fez uma ligação, pelo que o estado é verdadeiro mesmo que esteja em erro
OPCServerUpdate = True ' Activa a actualização dos dados
' Caso tenha ligado em erro,
If OPCServerConnectError = True Then
Dim Respostas As Integer
Respostas = MsgBox("Erro na Ligação ao OPC", MsgBoxStyle.AbortRetryIgnore)
If Respostas = vbCancel Then
LigaçãoOPC() ' Como está ligado, desliga
ElseIf Respostas = vbRetry Then
LigaçãoOPC() ' Como está ligado, desliga
LigaçãoOPC() ' volta a ligar
End If
End If
' Por causa da leitura assincrona, se não tiver estas linhas dá erro ### não sei porquê
OPCMyGroup.IsActive = Not OPCMyGroup.IsActive
OPCMyGroup.IsSubscribed = OPCMyGroup.IsActive
Else
' Já está ligado, por isso desligamos
'--- disconnect OPCServer
OPCMyGroup.IsActive = False ' Desactiva o grupo
OPCMyGroups.Remove(OPCMyGroup.ServerHandle) ' retira os itens do grupo
OPCMyGroupItems = Nothing
OPCMyGroups = Nothing
OPCMyGroup = Nothing
OPCMyServer.Disconnect()
OPCMyServer = Nothing
System.GC.Collect() 'Execute Garbage Collection compulsorily.
OPCServerConnected = False
End If
Catch ex As Exception
MsgBox("Erro #1 -> mdlOPC -> " & ex.Message)
OPCServerConnectError = True
End Try
End Sub
Private Function CriaItemsMyGroup()
' Cria os itens do meu grupo e estabelece a comunicação
Dim Resultado As Boolean = True
Try
Dim i As Integer
Dim ServerHandles As Array
Dim Errors As Array
' Configura o grupo
OPCMyGroup = OPCMyGroups.Add("MyGroup") ' Nome do meu grupo
OPCMyGroup.UpdateRate = 1000 ' Actualiza do grupo a cada X milisegundos
OPCMyGroup.IsActive = False
OPCMyGroup.IsSubscribed = OPCMyGroup.IsActive
OPCMyGroupItems = OPCMyGroup.OPCItems
' Configura cada item do grupo
i = 0
i += 1
sItemIDsMyGroup(i) = "CPU.Alarms.ESD_fault" ' Endereço da 1ª variável de comunicação do grupo, neste caso estou a usar a mnemónica configurada no OPC da Ingear
cHMyGroup(i) = i
i += 1
sItemIDsMyGroup(i) = "Device.N7:0" ' Endereço da 2ª variável de comunicação do grupo, neste caso estou a usar o endereçamento directo num autómato da Allen-Bradley de nome CPU
cHMyGroup(i) = i
'... até ao fim dos itens deste grupo
' Adiciona os itens ao grupo
OPCMyGroupItems.AddItems(NúmeroItemsMyGroup, sItemIDsMyGroup, cHMyGroup, ServerHandles, Errors) ', RequestedDataTypes, AccessPaths)
' Testa o sucesso e transfere ho Handle
For i = 1 To NúmeroItemsMyGroup
If Errors(i) = 0 Then
sHMyGroup(i) = ServerHandles(i)
Else
MsgBox("Erro no Item: (" & i.ToString & ") - " & sItemIDsMyGroup(i))
Resultado = False
End If
Next
Catch ex As Exception
MsgBox("Erro #2 -> mdlOPC -> " & ex.Message)
Resultado = False
End Try
Return Resultado
End Function
Friend Sub PLC2MyGroup()
' Executar esta rotina periodicamente para ler os valores deste grupo
' Podemos ler os valores sincronamente ou assincronamente
' Vamos usar a leitura assincrona
' Ou seja, mandamos ler o grupo e continuamos com o nosso trabalho
' Quando a leitura de todos os itens acabar, chama a rotina respectiva
' A vantagem deste método nota-se principalmente nas comunicações RS232 de muitos itens.
' nesse caso na leitura sincrona, o nosso programa deixa de responder neste momento até à conclusão da comunicação
Try
' Como esta rotina é executada periodicamente e o tempo, até recebermos os dados, pode variar, não queremos chamar a rotina 2 vezes sem receber os dados, por isso
' se ainda não fizemos stop ao timer que chama esta rotina, devemos fazer agora
If OPCServerConnected = True And OPCServerUpdate = True Then
' Se estivermos ligado ao OPC Server estado de actualização é verdadeiro
Static Dim wTransID As Integer = 1100 ' Para cada grupo diferente devemos ter um valor diferente
Dim wCancelID As Integer
Dim Errors As Array
wTransID = wTransID + 1
OPCMyGroup.AsyncRead(NúmeroItemsMyGroup, sHMyGroup, Errors, wTransID, wCancelID) ' Dá início para leitura assincrona
Exit Sub
Else
' Como não está ligado ou não está a actualizar os valores, pomos a qualidade a 0 ou seja má qualidade
Dim i%
For i = 1 To NúmeroItemsMyGroup
wQualityMyGroup(i) = 0
Next i
End If
Catch ex As Exception
MsgBox("Erro #3 -> 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
' Esta rotina é chamada quando acaba de receber os dados pedidos na rotina acima
Try
For i As Integer = 1 To NumItems
oValMyGroup(ClientHandles(i)) = ItemValues(i)
dTimeMyGroup(ClientHandles(i)) = TimeStamps(i)
wQualityMyGroup(ClientHandles(i)) = Qualities(i)
Next i
' Passa os valores recebidos para as nossas variáveis
Dim j As Integer = 1
ValoresAutómato.PrimeiroValor.Value = oValMyGroup(j)
ValoresAutómato.PrimeiroValor.Quality = wQualityMyGroup(j)
j += 1
ValoresAutómato.SegundoValor.Value = oValMyGroup(j)
ValoresAutómato.SegundoValor.Quality = wQualityMyGroup(j)
' Fazer o mesmo para os restantes valores
' A partir deste ponto já temos os valores deste grupo
' Já podemos tratar os valores, representar os resultados, etc...
' Podemos também dar start ao timer de actualização deste grupo
Catch ex As Exception
MsgBox("Erro #4 -> mdlOPC -> " & ex.Message)
End Try
End Sub
Private Sub MyGroup2PLC(ByVal sender As System.Object, ByVal e As System.EventArgs)
Try
' Da mesma maneira que lemos assincronamente, devemos escrever assincronamente
' Devemos ter os mesmos cuidados a escrever que tivemos a ler, por exemplo: o temporizador
Static Dim wTransID As Integer = 2000
Dim wCancelID As Integer
Dim Errors As Array
Dim i As Integer = 1
oValMyGroup(i) = ValoresAutómato.PrimeiroValor.Value
i += 1
oValMyGroup(i) = ValoresAutómato.SegundoValor.Value
' incrementar o i e fazer o mesmo para as restantes variáveis
wTransID = wTransID + 1
OPCMyGroup.AsyncWrite(NúmeroItemsMyGroup, sHMyGroup, oValMyGroup, Errors, wTransID, wCancelID)
Catch ex As Exception
MsgBox("Erro #5 -> mdlOPC -> " & ex.Message)
End Try
End Sub
Private Sub OPCMyGroup_AsyncWriteComplete(ByVal TransactionID As Integer, ByVal NumItems As Integer, ByRef ClientHandles As System.Array, ByRef Errors As System.Array) Handles OPCMyGroup.AsyncWriteComplete
Try
' Acabou de escrever no autómato, activar novamente o temporizador para a escrita
Catch ex As Exception
MsgBox("Erro #6 -> mdlOPC -> " & ex.Message)
End Try
End Sub
End Module
Comunicação directa com o autómato
A comunicação directa com o autómato varia naturalmente com o tipo de rede que vamos estabelecer com o autómato e varia de marca para marca.
Vantagens
- O custo de aquisição do software para o runtime é nulo.
- É multi-plataforma
- Com um software pode comunicar com vários autómatos sem custos extras
Desvantagens
- Estamos dependentes de um suporte técnico duvidoso.
- Não podemos aproveitar a parte de comunicação entre fabricantes de autómato mesmo que a aplicação seja a mesma.
- Quando usamos livrarias pré-compiladas, não temos a garantia de continuidade do projecto, por parte de quem o desenvolveu.
Exemplo Autómatos Siemens S7
Para os autómatos Siemens S7 vamos usar uma livraria pré-compilada libnodave.
Pode ser descarregada em http://libnodave.sourceforge.net/
Permite comunicar com S7-200 e S7-300/400 em PPI, MPI ou Ethernet.
Permite usar as principais línguas de programação e inclusive VBA a partir, por exemplo, do Excel.
Além da simples comunicação com o autómato, permite-nos dar ordem de Stop ou Run e fazer download de programa na globalidade ou parcialmente.
Apesar de vir acompanhada com exemplos, source-code e documentação, a sua implementação no nosso projecto pode não ser simples.
Vem acompanhada com ficheiros .exe que servem para teste de comunicação com o autómato.
Nota: Apesar de toda a informação disponibilizada por libnodave, não é fácil começar a comunicar com o autómato.
Um bom auxiliar, à informação disponibilizada, é o ficheiro Modul12.bas do exemplo em Excel.
' IMPORTANTE -> Não esquecer de incluir em References -> libnodave.net.dll do libnodave
' Módulo relativo às configurações e comunicações com a livraria libnodave
' Com S7-200 via porta Série
' Com S7-200 via ethernet CP243
Module mdlLibnodave
' Como num exemplo vamos tratar de 2 tipos de comunicação...
Enum Comunicação
Série
Ethernet
End Enum
Private Sub Comms()
Dim TipoComunicação As Comunicação
Dim localMPI As Integer = 0 ' Configuração dos endereços PPI/MPI do computador,por norma é o Zero
Dim plcMPI As Integer = 2 ' Configuração dos endereços PPI/MPI do autómato, por norma é o Dois, caso deseje comunicar com mais autómatos, crie mais variáveis com os endereços de cada um
Dim IP_Adress As String = "192.168.1.30" ' Configuração do IP da carta CP243
Dim fds As libnodave.daveOSserialType
Dim di As libnodave.daveInterface
Dim dc As libnodave.daveConnection
Dim res As Integer
Dim buf(1000) As Byte
Dim inputsres As Integer
Dim InputBuffer(1000) As Byte ' Buffer para lermos/escrevermos as entradas
Dim Inputs(6 * 8) As Integer ' Entradas do autómato -> 6 bytes IB0.. IB5
Dim outputsres As Integer
Dim OutputBuffer(1000) As Byte ' Buffer para lermos/escrevermos as saídas
Dim Outputs(6 * 8) As Integer ' Saídas do autómato -> 6 bytes QB0.. QB5
Dim memoryres As Integer
Dim MemoryBuffer(1000) As Byte ' Buffer para lermos/escrevermos as memórias M
Dim Memory(15 * 8) As Integer ' Memórias M do autómato -> 15 bytes MB0.. MB14
Dim XMTBuffer(100) As Byte ' Buffer de dados a ser transmitidos para o autómato
If TipoComunicação = Comunicação.Série Then
' Abrimos a comunicação com a porta para leitura
fds.rfd = libnodave.setPort("com1", "19200", AscW("E")) ' Porta=COM1, BaudRate=19200, Paridade= Par
ElseIf TipoComunicação = Comunicação.Ethernet Then
' Abrimos um socket para a comunicação
fds.rfd = libnodave.openSocket(102, IP_Adress)
Else
fds.rfd = -10000 ' geramos um erro nosso
End If
' Abrimos a comunicação com a porta para escrever igual à que usamos para ler
fds.wfd = fds.rfd
If fds.rfd > 0 Then
' Se conseguimos abrir a comunicação com a porta
' Criamos um interface novo
If TipoComunicação = TipoComunicação.Série Then
di = New libnodave.daveInterface(fds, "My Interface 1", localMPI, libnodave.daveProtoPPI, libnodave.daveSpeed19k) ' Importante, caso não estejamos a usar PPI, devemos alterar o daveProtoPPI, e caso o baudrate seja diferente, também devemos alterar
ElseIf TipoComunicação = TipoComunicação.Ethernet Then
di = New libnodave.daveInterface(fds, "IF1", localMPI, libnodave.daveProtoISOTCP243, libnodave.daveSpeed187k)
End If
di.setTimeout(100000) ' Caso receba muitos erros -1025 devemos aumentar este valor
res = di.initAdapter ' Iniciamos o Adaptador
If res = 0 Then
' Se o resultado de res é ZERO, então não há erro, podemos continuar
dc = New libnodave.daveConnection(di, plcMPI, 0, 2) ' criamos uma nova ligação, Nota: caso MPI a rack e slot não são importantes
res = dc.connectPLC() ' Ligamos ao autómato
If res = 0 Then
' Caso o resultado de res é ZERO, então não há erro, podemos continuar
inputsres = dc.readBytes(libnodave.daveInputs, 0, 0, 6, InputBuffer) ' Mandamos ler as entradas
outputsres = dc.readBytes(libnodave.daveOutputs, 0, 0, 6, OutputBuffer) ' Mandamos ler as saídas
memoryres = dc.readBytes(libnodave.daveFlags, 0, 0, 16, MemoryBuffer) ' Mandamos ler as memórias
' Cada vez que mandamos ler, perdemos tempo/performance
' É preferível ler muitos dados e usar só metade, do que ler uma parte, depois outra, depois...
' Podemos ler Timers, Counters, Memórias V, bancos de memória DB, etc...
If inputsres = 0 And outputsres = 0 And memoryres = 0 Then
' Caso o resultado de res é ZERO, então não há erro, podemos continuar
Dim h As Integer = 0
' Neste exemplo recebemos bytes, mas queremos bits, então é necessário converter
' No caso de lermos valores reais ou floats, há uma função específica para fazer a conversão
For i As Integer = 0 To 5
For j As Integer = i * 8 To (i * 8 + 7)
Inputs(j) = InputBuffer(i) And (2 ^ h)
Outputs(j) = OutputBuffer(i) And (2 ^ h)
Memory(j) = MemoryBuffer(i) And (2 ^ h)
If Inputs(j) > 1 Then Inputs(j) = 1
If Outputs(j) > 1 Then Outputs(j) = 1
If Memory(j) > 1 Then Memory(j) = 1
h += 1
Next
h = 0
Next
' Neste momento já temos os dados todos que lemos do autómato, já podemos tratar e representar no programa
If Inputs(0) = 0 Then
' fazer código para ZERO
Else
' fazer código para UM
End If
' Fazer o mesmo ou equivalente para os restantes valores
' Não esquecer que agora os bits estão todos seguidos e não agrupados em bytes, ou seja:
' I0.0 corresponde a Inputs(0), I1.0 corresponde a Inputs(8), I2.0 correspondente a Inputs(16), etc...
' Organizar os valores e metê-los no array XMTBuffer para serem enviados para o autómato
' Escreve nas memórias V
If dc.writeBytes(libnodave.daveDB, 1, 1020, 16, XMTBuffer) <> 0 Then
' Se o resultado for diferente de ZERO, então houve um erro
MsgBox(TimeOfDay & " - Erro na escrita de Ordens VB1020 -> " & res & vbCrLf) ' " <> " & libnodave.daveStrerror(res) & vbCrLf)
End If
Else
' Erro ao ler os valores
MsgBox("Error {0:d}={1:s} in readBytes.res = " & res & vbCrLf)
End If
dc.disconnectPLC() ' Desligamo-nos do autómato
Else
' Erro de ligação ao autómato
MsgBox("Error {0:d}={1:s} in connectPLC.res = " & res & vbCrLf)
End If
di.disconnectAdapter() ' Desligamos o adapter
Else
' Erro no adapter
MsgBox("Error {0:d}={1:s} in initAdapter.res = " & res & vbCrLf)
End If
' Fechamos a porta
libnodave.closePort(fds.rfd) ' Clean up
Else
' Erro ao abrir a porta
MsgBox("Erro ao abrir a porta de comunicação!" & vbCrLf)
End If
End Sub
End Module
Exemplo em Modbus/TCP Over Ethernet
O Modbus também é uma rede de comunicação amplamente usada.
O exemplo que apresentámos foi desenvolvido para receber dados de um analisador de energia SENTRON PAC3200 da Siemens em Modbus/TCP Over Ethernet.
Nota: Neste projecto não estava contemplado o envio de dados para o analisador, apenas a leitura de estados. A adaptação para escrever dados não é complexa, só não foi executada para este exemplo por falta de equipamentos de teste, ou seja, impossibilidade de garantir o seu funcionamento.
' Módulo relativo às configurações e comunicações em Modbus Over Ethernet
Imports System.Net
Imports System.Net.Sockets
Module mdlModbus
Private ClientSocket As Socket
Dim MbusQuery(11) As Byte ' Telegrama que vamos enviar para a comunicação
Private Sub LerDados() ' Cria o Socket, liga ao socket e aponta para a rotina de fim de ligação
Try
Dim Addr As IPAddress = Dns.GetHostEntry("192.168.1.30").AddressList(0)
If Not Addr Is Nothing Then
' Cria um Endpoint IP
Dim EP As New IPEndPoint(Addr, 502)
' Cria um novo Socket
ClientSocket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
' Manda ligar ao endereço IP
ClientSocket.BeginConnect(EP, AddressOf ConnectCallBack, Nothing) ' É uma ligação assincrona, por isso...
End If
Catch ex As Exception
MsgBox("Erro #1 -> mdlModbus -> " & ex.Message)
End Try
End Sub
Private Sub ConnectCallBack(ByVal ar As IAsyncResult)
' Esta rotina é chamada assincronamente quando o socket é ligado
Try
ClientSocket.EndConnect(ar) ' -- Terminou a ligação
' Os dados que queremos ler estão armazenados numa tabela
' O endereço e o comprimento dos valores são compostos em Word, mas temos que compor um telegrama em Bytes
Dim StartLow As Byte ' LSB do endereço que vamos ler
Dim StartHigh As Byte ' MSB do endereços que vamos ler
Dim LengthLow As Byte ' LSB do comprimento de dados
Dim LengthHigh As Byte 'MSB do comprimento de dados
' Neste exemplo vamos ler os dados do endereço 801
' e os 40 seguidos
StartLow = 801 Mod 256
StartHigh = 801 \ 256
LengthLow = 40 Mod 256 'cada float representa 4 bytes
LengthHigh = 40 \ 256 'cada float representa 4 bytes
' Construção do telegrama a ser enviado
MbusQuery(0) = 1 \ 256 ' MSB Endereço Modbus do destinatário
MbusQuery(1) = 1 Mod 256 ' LSB Endereço Modbus do destinatário
MbusQuery(2) = 0 'Protocol Identifier
MbusQuery(3) = 0 'Protocol Identifier
MbusQuery(4) = 0 'Comprimento High
MbusQuery(5) = 6 'Comprimento Low
MbusQuery(6) = 1 'ID
MbusQuery(7) = 3 'Código da Função 0x03 ou 0x04 ### Para mais informações, consultar os catálogos técnicos do Modbus
MbusQuery(8) = StartHigh
MbusQuery(9) = StartLow
MbusQuery(10) = LengthHigh
MbusQuery(11) = LengthLow
'MbusQuery = Chr(0) + Chr(0) + Chr(0) + Chr(0) + Chr(0) + Chr(6) + Chr(1) + Chr(3) + Chr(StartHigh) + Chr(StartLow) + Chr(LengthHigh) + Chr(LengthLow)
' Envia o Telegrama de pedido de dados
ClientSocket.Send(MbusQuery, MbusQuery.Length, SocketFlags.None)
Dim bytes(4095) As Byte ' buffer de bytes recebido (ou a receber)
ClientSocket.BeginReceive(bytes, 0, bytes.Length, SocketFlags.None, AddressOf ReceiveCallBack, bytes) ' É uma ligação assincrona, por isso...
Catch ex As Exception
MsgBox("Erro #2 -> mdlModbus -> " & ex.Message)
End Try
End Sub
Private Sub ReceiveCallBack(ByVal ar As IAsyncResult)
' Esta rotina é chamada assincronamente, quando recebemos a resposta ao pedido de dados
Try
Dim bytes() As Byte = CType(ar.AsyncState, Byte())
Dim RemoteEP As String = ClientSocket.RemoteEndPoint.ToString
Dim numbytes As Int32 = ClientSocket.EndReceive(ar)
If numbytes = 0 Then
' ERRO não recebeu dados
Else
' Caso contrário, fazer rotina de tratamento de dados.
' Não esquecer que há um limite de dados enviados por telegrama (205 bytes), ultrapassando esse limite, os dados ficam a zero
Dim i As Integer
i = 9 ' Os primeiros bytes são os endereços e comprimentos, consultar catálogo técnico sobre modbus para mais informação
' Tratar os dados, não esquecer que os dados vêm organizados em Bytes e é necessário convertê-los para Inteiros, Reais, etc...
i = i + 4 ' Não esquecer que como recebemos 4 bytes por valor, para tratarmos do próximo valor temos que somar 4 ao índice do array
End If
' Limpa o buffer e fecha a ligação
ClientSocket.Shutdown(SocketShutdown.Both)
ClientSocket.Close()
' Neste momento, já podemos fazer start ao temporizador para voltar a pedir dados
' Não esquecer que quando pedimos dados, devemos parar o temporizador
Catch ex As Exception
MsgBox("Erro #3 -> mdlModbus -> " & ex.Message)
End Try
End Sub
End Module
Exemplo Autómato Allen-Bradley em DF1
Pode ser descarregada em http://sourceforge.net/projects/abdf1/
Tem uma class em VB.Net 2005, um exemplo completo e documentação.