Criando um dashboard de automação industrial

Quando se começa a aprender a programar, uma das primeiras coisas que as pessoas dizem é: desenvolver interfaces gráficas (GUI) é difícil. E de fato isto é uma grande verdade. Ainda mais quando performance é uma preocupação. Mas também é verdade que está ficando cada vez mais fácil construir interface gráficas, vistos os novos frameworks que estão surgindo.

O objetivo deste artigo é mostrar como eu, um estudante de Engenharia de Controle e Automação, criou uma interface de painel para automação industrial.

O framework escolhido para este projeto foi o TotalCross, seguindo o padrão de arquitetura que ele orienta (para melhorar a manutenção/ajustes no código no futuro). 

Criando o projeto

O primeiro passo é efetuar o login usando o plug-in VSCode e criar um novo projeto. Para aqueles que ainda não tenham um registro neste framework, devem acessar o Get Started no site do TotalCross.

Os campos devem ser preenchidos da seguinte maneira: 

  • GroupId: com.totalcross.sample.showData
  • Nome: showData

Dentro do projeto, deve-se acessar path>src> main> java> com> totalcross>sample>showData e criar as pastas ui e util. Sendo que:

  • pasta ui: onde ficam todas as classes de interface do usuário (GUI)
  • pasta util: onde vão as classes de constantes que serão usadas durante o desenvolvimento.

Em seguida, deve-se acessar projeto> src> main e criar as pastas de resources. Os arquivos que serão usados ​​na interface gráfica (fontes, cores e imagens) estarão nesta pasta .

Util

Conforme comentado, nesta pasta vão as classes que serão usadas para criar as constantes que iremos usar no projeto, como fontes, imagens e cores. Criar uma classe para cada um desses tipos de constantes é uma prática que o próprio framework orienta (clique aqui para ler mais). O TotalCross orienta esse padrão para concentrar todas as imagens e cores em uma única classe. Desta forma, todas as imagens e cores são referenciadas em uma mesma classe, evitando ter várias classes onde a cor de background é afetada várias vezes. Isto facilita bastante a manutenção do código, pois uma simples mudança de cores de interface necessitará acessar apenas uma classe ao invés de várias.

De volta ao código, nesta pasta util deve-se criar 3 arquivos: Colors.java, Fonts.java e MaterialConstants.java. Eles adicionam as respectivas bibliotecas e bibliotecas adicionais para modificar as variáveis que precisam ser alteradas, devido a alguma especificidade da aplicação.

Por exemplo, o código Colors.java:

package com.totalcross.sample.showData.util;
import totalcross.ui.gfx.Color;
public class Colors {
    public static int BLUE = 0x4a91e2;
    public static int WHITE = Color.WHITE;
    public static int GRAY = Color.getRGB(241, 241, 241);
    public static int DARK_GRAY = Color.getRGB(50,50,50);
    public static final int P_700 = 0x3e72c1;
    public static final int P_200 = 0x98c7f1;
    public static int BACK = Color.getRGB(254, 254, 254);
}

A constante BLUE, biblioteca nativa Colors, não possui o tom de azul que queremos e por isso vamos colocar o valor hexadecimal logo após o padrão 0x  do TotalCross (0x<valor_hexadecimal>).
As constantes GREY e DARK_GRAY nesse tom, também não existem e como serão utilizadas (e muito!), deve-se usar o método Color.getRGB(r,g,b) para colocar a cor que queremos.
A cor branca já está na biblioteca Color, não necessitando nenhuma ação adicional.
Basicamente esta é a forma de setar cores utilizando TotalCross. 

Para acessar o código dos outros dois arquivos (Fonts e Images), clique aqui.

(G)UI

Todas as classes de interface com o usuário devem ser criadas nesta pasta. 

O arquivo ShowMainDataWindow.java, deve ser colocado dentro desta pasta, será o arquivo principal, onde colocamos o estilo de interface e, caso queira publicar sua aplicação para Android ou iOS, é nesta classe onde você estabelece os padrões. Nesta classe também que são chamadas a interface principal. 

Estas duas classes também devem ser criadas:

  • ShowDataContainer.java: : o arquivo onde se encontram os containers que serão exibidos na interface principal;
  • TimerContainer.java: Parte da interface onde as informações do temporizador são mostradas
  • ActuatorContainer.java: Parte da interface onde são exibidos o estado dos atuadores (outputs).
  • SensorContainer.java: Parte da interface onde as informações de sensores.

Para saber melhor o que é container, basta clicar aqui.

Obs: Cada parte do aplicativo usa os componentes Hbox e Vbox, Edit, Label, Button e Scroll Container.

Timer Container

Esta é a classe onde vamos criar o container o container que ficará no meio da interface da sua aplicação onde você poderá definir o horário de início e término do confinamento para um dos pinos GPIO, acender um led em uma determinada saída e excluir qualquer uma dessas configurações.

Este tipo de aplicação é utilizada em diversos cenários, como controle de alimentadores de animais e programa de irrigação de plantações.

O código abaixo faz parte do código do TimerContainer. A mesma estrutura é usada em outras partes do código. 

Primeiro, os componentes são criados e algumas características são definidas. Depois são adicionados a um Hbox que é adicionado à tela centralizada. Portanto, não é necessário adicionar cada componente individualmente, o que torna a interface mais limpa e facilita a alocação de componentes.

Edit pintEdit = new Edit(); // edit for recive pin for timer
pintEdit.caption = "GPIO"; // Edit text
pintEdit.setFont(Font.getFont(true, MaterialConstants.TEXT_SIZE)); 
pintEdit.setMode(Edit.CURRENCY); // set mode numeric
pintEdit.setKeyboard(Edit.KBD_NUMERIC); // lock all non-numeric keys for this edit
pintEdit.setBackForeColors(Colors.GRAY, Color.BLACK);
pintEdit.captionColor = Color.BLACK;
Button savetBt = new Button("Save"); // button for save configuation of timer
savetBt.setFont(Font.getFont(true, MaterialConstants.TEXT_SIZE));
// size
savetBt.setBackForeColors(Colors.BLUE, Colors.WHITE);
Edit starttEdit = new Edit("99" + Settings.timeSeparator + "99" + Settings.timeSeparator + "99");
starttEdit.caption = "Start";
starttEdit.setValidChars("0123456789AMP");
starttEdit.setMode(Edit.NORMAL, true);
starttEdit.setKeyboard(Edit.KBD_TIME);
starttEdit.setFont(Font.getFont(true, MaterialConstants.TEXT_SIZE));
starttEdit.setBackForeColors(Colors.GRAY, Color.BLACK);
starttEdit.captionColor = Color.BLACK;
Edit endtEdit = new Edit("99" + Settings.timeSeparator + "99" + Settings.timeSeparator + "99");
endtEdit.caption = "End";
endtEdit.setValidChars("0123456789AMP");
endtEdit.setMode(Edit.NORMAL, true);
endtEdit.setKeyboard(Edit.KBD_TIME);
endtEdit.setFont(Font.getFont(true, MaterialConstants.TEXT_SIZE));
endtEdit.setBackForeColors(Colors.GRAY, Color.BLACK);
endtEdit.captionColor = Color.BLACK;
HBox box = new HBox(HBox.LAYOUT_FILL, HBox.ALIGNMENT_STRETCH);
box.add(starttEdit);
box.add(endtEdit);
box.setSpacing(MaterialConstants.COMPONENT_SPACING);
// add components for timer
sc.add(timerLb, CENTER, TOP, sc.getWidth(), MaterialConstants.EDIT_HEIGHT);
sc.add(box, LEFT + MaterialConstants.BORDER_SPACING, AFTER + aterialConstants.COMPONENT_SPACING,FILL -MaterialConstants.BORDER_SPACING, MaterialConstants.EDIT_HEIGHT);
box = new HBox(HBox.LAYOUT_FILL, HBox.ALIGNMENT_STRETCH);
box.add(pintEdit);
box.add(savetBt);
box.setSpacing(MaterialConstants.COMPONENT_SPACING);
sc.add(box, LEFT + MaterialConstants.BORDER_SPACING, AFTER + MaterialConstants.COMPONENT_SPACING,FILL -MaterialConstants.BORDER_SPACING, MaterialConstants.EDIT_HEIGHT);

Acesse o código completo aqui.

Actuator Container

Este container mostra o estado atual dos atuadores, o valor e permite disparar e alterar os pinos de GPIO, que foram definidos como a saída.

Normalmente isso é usado para unidades digitais, como acender uma lâmpada ou abrir um portão. E com a alteração manual do valor é possível, por exemplo, definir a intensidade de iluminação de uma lâmpada ou a velocidade de rotação de um motor.

A maior mudança nesse container é que, usando o Vbox, é possível colocar todos os componentes com o mesmo tamanho em um único Vbox. Adicionar cada conjunto à tela fica mais fácil e o código ainda fica menor do que o do formulário usado no TimerContainer.

O código a seguir mostra melhor a diferença:

VBox boxi = new VBox(VBox.LAYOUT_FILL, VBox.ALIGNMENT_STRETCH);
boxi.add(indicator1);
boxi.add(indicator2);
boxi.add(indicator3);
boxi.add(indicator4);
boxi.setSpacing(MaterialConstants.COMPONENT_SPACING);
sc.add(boxi,RIGHT-MaterialConstants.BORDER_SPACING, AFTER+MaterialConstants.COMPONENT_SPACING,MaterialConstants.EDIT_HEIGHT,(MaterialConstants.COMPONENT_SPACING + MaterialConstants.EDIT_HEIGHT)*4);
VBox boxv = new VBox(VBox.LAYOUT_FILL, VBox.ALIGNMENT_STRETCH);
boxv.add(value1);
boxv.add(value2);
boxv.add(value3);
boxv.add(value4);
boxv.setSpacing(MaterialConstants.COMPONENT_SPACING);
sc.add(boxv,BEFORE - MaterialConstants.BORDER_SPACING, SAME, PREFERRED, boxi.getHeight());
VBox boxa = new VBox(VBox.LAYOUT_FILL, VBox.ALIGNMENT_STRETCH);
boxa.add(Actuator1);
boxa.add(Actuator2);
boxa.add(Actuator3);
boxa.add(Actuator4);
boxa.setSpacing(MaterialConstants.COMPONENT_SPACING);
sc.add(boxa,LEFT + MaterialConstants.BORDER_SPACING, SAME, boxv.getX() -MaterialConstants.COMPONENT_SPACING*3, boxv.getHeight());

Acesse o código completo clique aqui.

Sensor Container

O SensorContainer permite ver o estado/valor atual dos sensores, se estiver recebendo dados, mas pode também vinculá-lo a um atuador (output). O usuário pode definir um valor de acionamento e desacionamento para o pino e então, quando houver uma alguma entrada que atenda aos requisitos indicados pelo usuário, ative ou desative o atuador.


Este é um processo de trabalho comum usado para termostatos.

Essa tela tem as mesmas características que a anterior, com componentes verticais semelhantes no Vbox e componentes horizontais no Hbox.


Clique aqui para acessar o código completo.

Show data container

E por último, a classe que reúne todos os containers anteriores e os exibe ​​para o usuário. Para chamar os containers, basta tratá-los como objeto da lib ScrollContainer, pois foram criados como extensão dela. Veja abaixo:

package com.totalcross.sample.showData.ui;
import com.totalcross.sample.showData.util.Colors;
import totalcross.sys.Settings;
import totalcross.ui.Container;
import totalcross.ui.Label;
import totalcross.ui.ScrollContainer;
import totalcross.ui.font.Font;
public class ShowDataContainer extends Container {
    private ScrollContainer sc;
    @Override
    public void initUI() {
        // add containers
        sc = new ScrollContainer(true, true);
        sc.setBackColor(Colors.GRAY);
        add(sc, LEFT, TOP, FILL, FILL);
TimerContainer tcont = new TimerContainer();//
instantiating TimerContainer
        ActuatorContainer Acont = new ActuatorContainer();//
instantiating ActuatorContainer
        SensorContainer Scont = new SensorContainer();// instantiating SensorContainer
sc.add(Acont, CENTER, TOP + (int) (Settings.screenHeight * 0.15), (int) (Settings.screenWidth * 0.3),(int)(Settings.screenHeight * 0.8));// add Actuator container
sc.add(tcont, CENTER - (int) (Settings.screenWidth * 0.02) - Acont.getWidth(), SAME, Acont.getWidth(), Acont.getHeight());//add Timer container
sc.add(Scont, CENTER + (int) (Settings.screenWidth * 0.02) + Acont.getWidth(), SAME, Acont.getWidth(), Acont.getHeight());//add Sensor container
// APP Title
        Label Title = new Label("Show Data");        
        Title.setFont(Font.getFont(true, (int) (0.05 *     Settings.screenHeight)));// add Title label
        sc.add(Title, CENTER, TOP + 10);
    }
}

ShowDataMainWindow

Este arquivo é onde as características básicas do aplicativo são definidas, como pode ser visto no código a seguir.

Os componentes seguem o estilo FLAT_UI, pois devido ao seu design simples, reduz o consumo de RAM da aplicação. Se não houver limitações de consumo de memória,outros conjuntos de componentes com estilos mais elaborados poderão ser usados, como MATERIAL_UI e ANDROID_UI.

package com.totalcross.sample.showData.ui;
import totalcross.ui.MainWindow;
import totalcross.ui.font.Font;
import com.totalcross.sample.showData.util.Fonts;
import totalcross.sys.Settings;
public class ShowDataMainWindow extends MainWindow {
public static final String version = "1.0.0";  // app version
    public ShowDataMainWindow() {
    setUIStyle(Settings.FLAT_UI); // define design
    setDefaultFont(Font.getFont(Fonts.FONT_DEFAULT_SIZE));//define font
    }
    static {
Settings.applicationId = "SDSA"; //app id
        Settings.appVersion = version;
    }
    @Override
    public void initUI() {
super.initUI();
        ShowDataContainer container = new ShowDataContainer(); // get app screen
        swap(container);//  update screen
    }
}

Essa foi a última classe necessária para montar a interface de painel para automação industrial. 

Se deseja fazer o download da aplicação para testar, basta baixar no GitHub da TotalCross.

Resultados

O resultado final do projeto pode ser encontrado nas imagens abaixo:

Devido a simplicidade, o plug-in Vscode foi usado para rodar a aplicação. Com o simulador da TotalCross é possível ajustar a resolução e a densidade de pixels, podendo assim verificar previamente o resultado antes de rodar no aparelho.

Nas imagens abaixo você pode conferir o resultado já no Raspberry PI 3:

A imagem acima é de um RPI, mas é possível rodar em outras SBCs e computadores em módulos.

Conclusão

Este exemplo demonstra o que seria um “front end” (GUI) para uma aplicação embarcada que acessa o GPIO. É possível aproveitar a lógica aplicada nesse exemplo de maneiras bem variadas, dependendo da necessidade de informação, do design da aplicação e o do quanto de memória pode-se consumir. Vale bastante a pena testar com material UI, pois o aumento de memória não é tão significativo (o que planejo publicar sobre no futuro).

Referências

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
0 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Home » Linux Embarcado » Criando um dashboard de automação industrial

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: