Implementação de Controle de Compressores de Ar com WinForms e Protocolo Modbus

O desenvolvimento de interfaces de monitoramento industrial (IHM) exige a criação de componentes visuais que reflitam o estado real dos equipamentos e permitam o envio de comandos via protocolos de comunicação, como o Modbus. Abaixo, detalhamos a construção de um sistema de controle para compressores e bombas utilizando Windows Forms.

1. Criação de Componentes Visuais Customizados

Para representar uma bomba de resfriamento ou compressor com animação, criamos um UserControl. Este componente utiliza um temporizador para alternar entre frames de imagem, simulando o movimento quando o equipamento está ativo.


using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections.Generic;

namespace IndustrialApp.UI
{
    public partial class ControleBomba : UserControl
    {
        private Timer _timerAnimacao;
        private int _frameAtual = 0;
        private const int TotalFrames = 11;

        public string IdEstadoDispositivo { get; set; }
        public string NomeDispositivo { get; set; }
        public string TagPartida { get; set; }
        public string TagParada { get; set; }
        public bool EstaAtivo { get; set; }

        public ControleBomba()
        {
            InitializeComponent();
            _timerAnimacao = new Timer { Interval = 100 };
            _timerAnimacao.Tick += ProcessarFrame;
            _timerAnimacao.Start();
        }

        private void ProcessarFrame(object sender, EventArgs e)
        {
            // Verifica o estado atual no cache global de variáveis
            if (!string.IsNullOrEmpty(IdEstadoDispositivo))
            {
                if (GerenciadorDados.ValoresAtuais.TryGetValue(IdEstadoDispositivo, out string valor))
                {
                    EstaAtivo = valor == "1";
                }
            }

            if (EstaAtivo)
            {
                _frameAtual = (_frameAtual + 1) % TotalFrames;
                AtualizarRepresentacaoVisual();
            }
        }

        private void AtualizarRepresentacaoVisual()
        {
            // Atribui a imagem correspondente ao frame atual
            string nomeRecurso = $"bomba_frame_{_frameAtual}";
            picBoxPrincipal.Image = (Image)Properties.Resources.ResourceManager.GetObject(nomeRecurso);
        }

        public event EventHandler DispositivoSelecionado;

        private void picBoxPrincipal_DoubleClick(object sender, EventArgs e)
        {
            DispositivoSelecionado?.Invoke(this, EventArgs.Empty);
        }
    }
}

2. Inicialização e Mapeamento de Variáveis

No início da aplicação, é fundamental carregar as definições de registros Modbus (Coils, Holding Registers, etc.) a partir de arquivos de configuração (XML ou INI). Esses dados vinculam os nomes lógicos das variáveis aos seus endereços físicos.


private void CarregarConfiguracoesTecnicas()
{
    // Carregamento de entidades Modbus e endereçamentos
    var listaVariaveis = ServicoConfiguracao.CarregarVariaveisXml("config/tags.xml");
    
    foreach (var tag in listaVariaveis)
    {
        if (!GerenciadorDados.ValoresAtuais.ContainsKey(tag.Nome))
        {
            GerenciadorDados.ValoresAtuais.Add(tag.Nome, string.Empty);
            GerenciadorDados.MapeamentoEnderecos.Add(tag.Nome, tag.EnderecoFisico);
        }

        // Segregação por área de memória Modbus
        switch (tag.AreaMemoria)
        {
            case "01 Coil":
                comunicador.ListaCoils.Add(tag);
                break;
            case "03 Holding Register":
                comunicador.ListaRegistros.Add(tag);
                break;
        }
    }
}

3. Lógica de Interação com o Operador

Quando o operador interage com o componente visual, o sisttema deve identificar quais endereços Modbus correspondem aos comandos de partida e parada daquele equipamento específico.


private void OnDispositivo_DoubleClick(object sender, EventArgs e)
{
    string enderecoPartida = string.Empty;
    string enderecoParada = string.Empty;
    string tituloEquipamento = string.Empty;

    if (sender is ControleBomba componente)
    {
        tituloEquipamento = componente.NomeDispositivo;
        
        // Busca os endereços físicos baseados nas tags configuradas no componente
        GerenciadorDados.MapeamentoEnderecos.TryGetValue(componente.TagPartida, out enderecoPartida);
        GerenciadorDados.MapeamentoEnderecos.TryGetValue(componente.TagParada, out enderecoParada);
    }

    if (!string.IsNullOrEmpty(enderecoPartida) && !string.IsNullOrEmpty(enderecoParada))
    {
        using (var telaComando = new FormControleManual(tituloEquipamento, enderecoPartida, enderecoParada))
        {
            telaComando.ShowDialog();
        }
    }
    else
    {
        MessageBox.Show("Tags de controle não configuradas para este dispositivo.");
    }
}

4. Execução de Comandos via Modbus

A escrita de dados em CLPs (Controladores Lógicos Programáveis) geralmente envolve o envio de um pulso. No exemplo abaixo, escreve-se um valor alto (256 ou 1) seguido de um valor baixo (0) para simular o acionamento de um botão.


public partial class FormControleManual : Form
{
    private string _addrStart;
    private string _addrStop;

    public FormControleManual(string nome, string addrStart, string addrStop)
    {
        InitializeComponent();
        _addrStart = addrStart;
        _addrStop = addrStop;
        lblStatus.Text = $"Gerenciar: {nome}";
    }

    private async void btnIniciar_Click(object sender, EventArgs e)
    {
        await ExecutarComandoModbus(_addrStart, "Ativação");
    }

    private async void btnDesligar_Click(object sender, EventArgs e)
    {
        await ExecutarComandoModbus(_addrStop, "Desativação");
    }

    private async Task ExecutarComandoModbus(string endereco, string acao)
    {
        try 
        {
            GerenciadorDados.BloquearLeitura = true; // Evita conflitos no barramento
            
            // Simulação de pulso: Liga e desliga o registro
            bool ok1 = driverModbus.WriteRegister(int.Parse(endereco), 256);
            await Task.Delay(500); 
            bool ok2 = driverModbus.WriteRegister(int.Parse(endereco), 0);

            if (ok1 && ok2)
                MessageBox.Show($"{acao} executada com sucesso.");
            else
                MessageBox.Show($"Falha ao comunicar com o endereço {endereco}.");
        }
        finally
        {
            GerenciadorDados.BloquearLeitura = false;
        }
    }
}

Tags: WinForms C# Modbus Automação-Industrial .NET

Publicado em 6-9 19:24 por Thomas