SCADA no Visual Studio – Controle de Temperatura

Índice

  1. O que é SCADA?
  2. Desenvolvimento SCADA
  3. Apresentação da Interface Desenvolvida
  4. Código Fonte

1. O que é SCADA?

SCADA se trata de uma abreviação para Supervisory Control and Data Acquisition ou em português, Sistemas de Supervisão de Aquisição de Dados. No caso, são sistemas que utilizam software para o monitoramento, controle e supervisão das variáveis e dos dispositivos envolvidos em um processo.

2. Desenvolvimento SCADA

Existem inúmeros softwares no mercado, voltados para auxiliar no desenvolvimento de sistemas SCADA. Normalmente, eles vem com um pacote imenso de ferramentas que auxiliam de forma significativa o processo de desenvolvimento, além de possuir uma robustez que dá grande segurança ao colocá-los como responsáveis por processos industriais complexos. Porém a maioria desses sistemas exige uma licença que por vezes, pode dificultar o seu uso em projetos mais simples. Assim uma alternativa, é desenvolver um sistema de monitoramento, da mesma forma que se desenvolve qualquer outro software de computador. De fato, desenvolver um supervisório, sem o auxílio de uma plataforma específica para isso pode parecer um grande desafio. Mas utilizando as ferramentas adequadas, pode-se alcançar um resultado final extremamente satisfatório, em um curto período de tempo. Nesse artigo, demonstraremos um software de supervisório desenvolvido do zero utilizando a ferramenta Visual Studio Community da microsoft, que no caso é gratuita.

3. Apresentação da Interface Desenvolvida

Abaixo segue o vídeo do resultado final, demonstrando as funcionalidades da interface desenvolvida.

4. Código Fonte

Vamos demonstrar passo-a-passo o funcionamento do código da tela principal do supervisório. Para ter acesso ao código fonte completo, basta acessar o repositório da aplicação no github.

Construtor da Tela

No método construtor, são inicializados os componentes gráficos da tela criados utilizando o próprio designer do visual studio, e é feita a inicialização do evento responsável por capturar os dados do processo, no caso utilizando o protocolo BqBus.

        public Home()
        {
            InitializeComponent();
            Program.App.Arduino.BqBus.OnData += BqBus_OnData;
        }

Feito isso, toda vez que o evento da rede BqBus ‘OnData’ for disparado, ou seja, toda vez que houver uma troca de dados, o método ‘BqBus_OnData’ é chamado.

Comunicação

O método ‘BqBus_OnData’ é responsável por fazer o processamento dos dados recebidos. No caso esse processamento é divido em duas etapas. Primeiro são processados os dados de estado do processo (estado do forno <ligado/desligado/falha>, e estado da temperatura <valor inteiro>).

        private void BqBus_OnData(object sender, EventArgs e)
        {
            ProcessStatus();
            ProcessAlarms();
        }

A lógica que processa os estados, realiza comparações com as variáveis recebidas do Arduino (Program.App.Arduino), e modifica os componentes da tela para que se possa visualizar em tempo real as alterações no processo.

        private void ProcessStatus()
        {
            picFurnaceStatus.Status = Program.App.Arduino.FurnaceStatus == 1;
            statusFurnace.StatusCode = Program.App.Arduino.FurnaceStatus;
            temperatureIndicator.Temp = Program.App.Arduino.FurnaceTemperature;
        }

A lógica que processa os alarmes, é semelhante a logica anterior. Ela realiza comparações para verificar se os valores recebidos, estão em condição de disparar algum alarme, mas também testa o idioma da aplicação, para apresentar o texto correto na tabela de alarmes.

        private static void ProcessAlarms()
        {
            if (Program.App.Arduino.FurnaceStatus == 2)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("General Fail");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Falha Geral");
            }
            if (Program.App.Arduino.FurnaceTemperature > Program.App.Settings.Parameters.AlarmHigh)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("Furnace High Temperature");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Temperatura alta no forno");
            }
            if (Program.App.Arduino.FurnaceTemperature < Program.App.Settings.Parameters.AlarmLow)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("Furnace Low Temperature");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Temperatura baixa no forno");
            }
        }
Leitura dos componentes gráficos

As lógicas de leitura dos componentes gráficos são extremamente simples. Para seu correto funcionamento, elas são criadas usando o designer, bastando digitar seus nomes na tabela de eventos do componente selecionado. Segue abaixo um exemplo.

Nesse exemplo, selecionando o componente ‘toggleAutomatic’, na sua tabela de eventos, criei um método chamado ‘toggleAutomatic_OnToggle’, que no caso será disparado toda vez que o usuário alternar o estado da chave de seleção.

Segue abaixo as respectivas lógicas das chaves de seleção

        private void toggleAutomatic_OnToggle(object sender, EventArgs e)
        {
            toggleFurnace.Enabled = toggleAutomatic.ToggleStatus == false;
            Program.App.Arduino.AutomaticControl = toggleAutomatic.ToggleStatus;
            Program.App.Settings.Parameters.AutomaticControl = toggleAutomatic.ToggleStatus;
        }

        private void toggleFurnace_OnToggle(object sender, EventArgs e)
        {
            Program.App.Arduino.FurnaceControl = toggleFurnace.ToggleStatus;
            Program.App.Settings.Parameters.ManualToogle = toggleFurnace.ToggleStatus;
        }

Além alterar o estado da variável do controlador (Program.App.Arduino.AutomaticControl), seu estado é salvo em uma estrutura (‘Program.App.Settings’). Isso é feito para que seus valores possam ser salvos quando a aplicação for encerrada. Assim quando o usuário for reutilizá-la, todos os estados das chaves, e os parâmetros do processo serão recuperados.

Por fim temos as leituras dos parâmetros de controle. Primeiramente um processamento é realizado, para impedir que o usuário digite caracteres diferentes de números nas caixas de texto.

        private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
        }

Feito isso, basta ler os valores digitados, e enviá-los para o controlador através da rede BqBus, usando a estrutura ‘Program.App.Arduino’.

    private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
    {
        e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
    }

    private void tbSetPoint_TextChanged(object sender, EventArgs e)
    {
        var tb = (MetroTextBox)sender;
        Program.App.Arduino.ParameterSetPoint = int.Parse(tb.Text);
        Program.App.Settings.Parameters.SetPoint = int.Parse(tb.Text);
    }

    private void tbHisteresis_TextChanged(object sender, EventArgs e)
    {
        var tb = (MetroTextBox)sender;
        Program.App.Arduino.ParameterHisteresis = int.Parse(tb.Text);
        Program.App.Settings.Parameters.Histeresis = int.Parse(tb.Text);
    }

    private void tbAlarmHigh_TextChanged(object sender, EventArgs e)
    {
        var tb = (MetroTextBox)sender;
        Program.App.Settings.Parameters.AlarmHigh = int.Parse(tb.Text);
    }

    private void tbAlarmLow_TextChanged(object sender, EventArgs e)
    {
        var tb = (MetroTextBox)sender;
        Program.App.Settings.Parameters.AlarmLow = int.Parse(tb.Text);
    }
Recuperando os dados na inicalização da aplicação

Para que os dados salvos no encerramento da aplicação, possam ser recuperados pela tela na sua próxima inicialização, uma lógica é vinculada ao evento ‘ParentChanged’. Esse evento é disparado toda vez que a tela é inserida em algum formulário pai (Parent). E ela é responsável por obter os valores da estrutura ‘Program.App.Settings’ e alterar os valores dos componentes gráficos da tela.

    private void Home_ParentChanged(object sender, EventArgs e)
    {
        toggleAutomatic.ToggleStatus = Program.App.Settings.Parameters.AutomaticControl;
        toggleFurnace.ToggleStatus = Program.App.Settings.Parameters.ManualToogle;
        toggleAutomatic_OnToggle(toggleAutomatic, e);
        toggleFurnace_OnToggle(toggleFurnace, e);
        tbSetPoint.Text = Program.App.Settings.Parameters.SetPoint.ToString();
        tbHisteresis.Text = Program.App.Settings.Parameters.Histeresis.ToString();
        tbAlarmHigh.Text = Program.App.Settings.Parameters.AlarmHigh.ToString();
        tbAlarmLow.Text = Program.App.Settings.Parameters.AlarmLow.ToString();
    }

O código completo da tela, pode ser observado abaixo

using System;
using System.Windows.Forms;
using MetroFramework.Controls;
using System.Threading;

namespace TemperatureControl
{
    public partial class Home : UserControl
    {
        public Home()
        {
            InitializeComponent();
            Program.App.Arduino.BqBus.OnData += BqBus_OnData;
        }

        private void BqBus_OnData(object sender, EventArgs e)
        {
            ProcessStatus();
            ProcessAlarms();
        }

        private void ProcessStatus()
        {
            picFurnaceStatus.Status = Program.App.Arduino.FurnaceStatus == 1;
            statusFurnace.StatusCode = Program.App.Arduino.FurnaceStatus;
            temperatureIndicator.Temp = Program.App.Arduino.FurnaceTemperature;
        }

        private static void ProcessAlarms()
        {
            if (Program.App.Arduino.FurnaceStatus == 2)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("General Fail");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Falha Geral");
            }
            if (Program.App.Arduino.FurnaceTemperature > Program.App.Settings.Parameters.AlarmHigh)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("Furnace High Temperature");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Temperatura alta no forno");
            }
            if (Program.App.Arduino.FurnaceTemperature < Program.App.Settings.Parameters.AlarmLow)
            {
                if (Thread.CurrentThread.CurrentCulture.Name == "en-EU")
                    Program.App.Screens.Alarms.SetAlarm("Furnace Low Temperature");
                if (Thread.CurrentThread.CurrentCulture.Name == "pt-BR")
                    Program.App.Screens.Alarms.SetAlarm("Temperatura baixa no forno");
            }
        }

        private void toggleAutomatic_OnToggle(object sender, EventArgs e)
        {
            toggleFurnace.Enabled = toggleAutomatic.ToggleStatus == false;
            Program.App.Arduino.AutomaticControl = toggleAutomatic.ToggleStatus;
            Program.App.Settings.Parameters.AutomaticControl = toggleAutomatic.ToggleStatus;
        }

        private void toggleFurnace_OnToggle(object sender, EventArgs e)
        {
            Program.App.Arduino.FurnaceControl = toggleFurnace.ToggleStatus;
            Program.App.Settings.Parameters.ManualToogle = toggleFurnace.ToggleStatus;
        }

        private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
        }

        private void tbSetPoint_TextChanged(object sender, EventArgs e)
        {
            var tb = (MetroTextBox)sender;
            Program.App.Arduino.ParameterSetPoint = int.Parse(tb.Text);
            Program.App.Settings.Parameters.SetPoint = int.Parse(tb.Text);
        }

        private void tbHisteresis_TextChanged(object sender, EventArgs e)
        {
            var tb = (MetroTextBox)sender;
            Program.App.Arduino.ParameterHisteresis = int.Parse(tb.Text);
            Program.App.Settings.Parameters.Histeresis = int.Parse(tb.Text);
        }

        private void tbAlarmHigh_TextChanged(object sender, EventArgs e)
        {
            var tb = (MetroTextBox)sender;
            Program.App.Settings.Parameters.AlarmHigh = int.Parse(tb.Text);
        }

        private void tbAlarmLow_TextChanged(object sender, EventArgs e)
        {
            var tb = (MetroTextBox)sender;
            Program.App.Settings.Parameters.AlarmLow = int.Parse(tb.Text);
        }

        private void Home_ParentChanged(object sender, EventArgs e)
        {
            toggleAutomatic.ToggleStatus = Program.App.Settings.Parameters.AutomaticControl;
            toggleFurnace.ToggleStatus = Program.App.Settings.Parameters.ManualToogle;
            toggleAutomatic_OnToggle(toggleAutomatic, e);
            toggleFurnace_OnToggle(toggleFurnace, e);
            tbSetPoint.Text = Program.App.Settings.Parameters.SetPoint.ToString();
            tbHisteresis.Text = Program.App.Settings.Parameters.Histeresis.ToString();
            tbAlarmHigh.Text = Program.App.Settings.Parameters.AlarmHigh.ToString();
            tbAlarmLow.Text = Program.App.Settings.Parameters.AlarmLow.ToString();
        }
    }
}

Assim, através de um código simples, podemos desenvolver interfaces gráficas de monitoramento e controle com design moderno e intuitivo, sem perder a eficiência no desenvolvimento

Para receber em primeira mão nossos conteúdos, se inscreva no nosso blog!

Publicado por WESLEY JOSE SANTOS

Independent developer, with great knowledge about Arduino programming and plataforms of monitoring and process control, also having experience with PLC's, IHM's and SCADA based systems. Desenvolvedor indepentente, possuindo conhecimentos em programação de Arduino e em plataformas de monitoramento e controle de processos, bem como experiência em programação de PLC's, IHM's e sistemas de supervisório (SCADA).

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: