Gere e modifique gráficos SVG de dados de sensores conectados à IoT com JavaScript

Gere e modifique gráficos SVG de dados de sensores conectados à IoT com JavaScript

Gere e modifique gráficos SVG de dados de sensores conectados à IoT com JavaScript

Nesta última parte da série de artigos sobre desenho gráficos com dados de sensores conectados à Internet das Coisas, é hora de falar sobre como gerar ou modificar com JavaScript desenhos em formato SVG e alguns dos elementos HTML que sirvam de container ou que apresentem informações complementares aos gráficos.

Tabela de conteúdos

    Gráficos de dados de sensores conectados ao contêiner da Internet das Coisas (IoT) em HTMLGráficos de dados de sensores conectados à Internet das Coisas (IoT) definição de aparência em CSSGráficos de dados de sensores conectados à Internet das Coisas (IoT) desenhando com SVGGráficos de dados de sensores conectados à Internet das Coisas (IoT) Geração e modificação com JavaScript

    Os usuários-alvo deste tutorial devem formar um perfil de eletrônica e programação de computadores. microcontroladores, eles podem não estar familiarizados com HTML, APF o SVG; Por este motivo, nas edições anteriores foi feita uma breve introdução à linguagem ou à tecnologia correspondente. Nesta última parte a abordagem é um pouco diferente, já que os leitores com certeza sabem programar, é possível que utilizando a linguagem C + + que como JavaScript, compartilha sintaxe básica com C e pode ser usado como referência para pular a maior parte dos conceitos básicos de programação e assim focar nas diferenças e no uso específico que nos interessa para criar gráficos de sensores na IoT.

    O nome dá uma pista para a primeira diferença: JavaScript É uma linguagem de programação escrita (hífen) e como tal, é interpretado, não há necessidade de compilá-lo; o contexto em que o escrita (um navegador web, por exemplo) irá ler, traduzir e executar as ordens. Para ser mais preciso, na maioria dos casos existe uma compilação em tempo de execução (JIT), mas para o processo de escrita de código JavaScript Isso não nos afeta, simplesmente escrevemos o código e ele pode funcionar.

    O nome também contém a primeira confusão: JavaScript não tem a menor relação com Java. Inicialmente, quando foi desenvolvido Netscape para seu navegador, foi chamado primeiro de Mocha e depois de LiveScript, menos confuso. Após sua implementação bem-sucedida em navegadores, e transcendendo-os, foi padronizado como ECMAScript (Para ECMA-262, versão 6 no momento em que este artigo foi escrito) para se tornar neutro em relação aos navegadores que o implementam. Atualmente também existe um padrão ISO da versão 5, 2011 (ISO / IEC 16262:2011 no momento da redação do artigo)

    Variáveis, tipos de dados básicos e objetos em JavaScript

    Ao contrário do que acontece, por exemplo, em C + +, en JavaScript tipo de dados não incluído ao declarar uma variável e também o tipo associado a uma variável não é fixo, é possível atribuir um valor de tipo diferente ao longo da execução do programa.

    No exemplo anterior, a variável "coisa" foi declarada (sem indicar o tipo de dados), então são atribuídos dados de um tipo diferente e são consultados com typeof o tipo que JavaScript que ele interpretou. Para depurar o código você pode escrevê-lo no console do inspetor do navegador (o que não afetará a apresentação da web) com console.log().

    Para forçar a conversão de dados para um tipo específico, especialmente texto para numérico, você pode usar funções como parseInt() o parseFloat() que são convertidos em números inteiros ou de ponto flutuante, respectivamente. A conversão oposta pode ser feita com String(), embora seja improvável que seja necessário, pois a conversão automática geralmente é suficiente. Com parseFloat()Por exemplo, você pode obter o valor de uma propriedade de página da Web, como a largura ou a altura de um objeto, que inclui unidades; Desta forma, a expressão parseFloat("50px"); retornará 50, um valor numérico, como resultado.

    En JavaScript não há distinção entre aspas duplas e simples; O tipo de dados em ambos os casos é string, e cada um deles pode incluir o outro sem a necessidade de códigos de escape.

    No exemplo anterior pode-se observar que uma variável, quando foi declarada (existe) mas não lhe foi atribuído nenhum valor, contém um tipo de dados indefinido (undefined). Um objeto não atribuído tem o valor null; Ou seja, o objeto existe, mas sem valor; uma variável que a referenciasse não teria um typeof undefined mas object. Um objeto também pode estar vazio, ou seja, não ser nulo, mas não ter nenhuma propriedade.

    Pára definir um objeto em JavaScript estão entre colchetes ({ y }) as propriedades ou métodos, separados pelo sinal de dois pontos (:) nome da propriedade valor da propriedade e por vírgula (,) as diferentes propriedades. Você pode encontrar mais informações sobre esta forma de expressar um objeto no artigo sobre o Formato JSON.

    Embora você possa usar uma sintaxe que possa levá-lo a pensar de outra forma, en JavaScript Não existem classes, mas protótiposOu seja, para que um objeto herde propriedades e métodos, é criado outro objeto (o protótipo) que os demais (os filhos) utilizam como referência. A sintaxe mais próxima do estilo de JavaScript usar um protótipo é Object.create embora também seja possível (e às vezes útil) usar new como em outras linguagens orientadas a objetos.

    Pára consultar se um objeto é uma instância de outro, se você usar como protótipo, se herdar suas propriedades, enfim, você pode usar instanceof (criado com new) Ou isPrototypeOf (criado com Object.create) que será avaliado como verdadeiro quando o objeto usar o protótipo e falso quando não o fizer.

    Uma vez criado um objeto usando outro como protótipo, ou seja, uma vez instanciado um objeto, ele pode ser adicione novas propriedades ou substitua as propriedades do protótipo usando sintaxe de ponto como em gato.peso=2.5.

    La matrizes em JavaScript Eles são diferentes daqueles que você provavelmente conhece C. Para começar, são declarados sem a necessidade de indicar seu comprimento, apenas com os sinais de abertura e fechamento de colchetes ([ y ]), os componentes podem ser heterogêneos (diferentes tipos de dados no mesmo array) e novos elementos podem ser adicionados sem ficarem restritos a um limite. As matrizes de JavaScript são na verdade listas (coleções) de elementos aos quais referenciado por um índice numérico ou por um nome. Um array pode conter simultaneamente índices numéricos e nomes de elementos, mas é comum usar objetos (propriedades) para explorar o segundo tipo.

    Como pode ser visto no exemplo anterior, para saber se uma variável corresponde a uma instância de um array (é um objeto array) você pode usar instanceof, como já foi usado com objetos genéricos ou, em versões mais recentes do JavaScript pode-se recorrer a Array.isArray()

    Para acessar os elementos do array você pode usar seu índice (matriz[7]) ou pelo nome da propriedade com o nome entre colchetes (matriz["nombre"]) ou com a sintaxe de ponto usual para objetos (matriz.nombre). Como o nome é uma string de texto, uma expressão, incluindo variáveis, pode ser usada para compô-lo. Para percorrer um array com propriedades, um loop com o formato pode ser usado for(propiedad in matriz).

    É interessante para o nosso objetivo tratar o objeto Date, com o qual representa e gerencia data e hora em JavaScript. O objeto pode ser instanciado sem dados, portanto ele usará a data e hora atuais, ou pode ser criado indicando uma data como valor, seja em milissegundos desde 1º de janeiro de 1970 (como Hora Unix ou hora POSIX mas expresso em milissegundos em vez de segundos) ou especificando valores separados de ano, mês, dia, hora...

    O objeto inclui uma série completa de métodos para consultar ou definir a data e hora:

    • now()
      Retorna a data e hora atuais expressas em milissegundos desde 1º de janeiro de 1970

    • getTime() | setTime()
      Obtém ou altera, respectivamente, o valor do tempo em milissegundos desde 1º de janeiro de 1970. Usando valueOf(), que é um método presente na maioria dos objetos, também é obtido o valor do objeto Date correspondente, como getTime() com Hora Unix ou hora POSIX expresso em ms.

    • getMilliseconds() | setMilliseconds()
      Usado para consultar ou definir a parte fracionária de milissegundos do objeto Date em que é executado. Se consultado, o valor obtido está entre 0 e 999, mas podem ser atribuídos valores maiores que serão acumulados na data e hora total, portanto, como os demais métodos get, serve para aumentar o valor do objeto Date (ou diminua, se forem usados ​​valores negativos).

    • getSeconds() | setSeconds()
      Retorna ou altera, respectivamente, o valor dos segundos do objeto Date.

    • getMinutes() | setMinutes()
      Utilizado para consultar ou acertar os minutos do objeto Date.

    • getHours() | setHours()
      Permite consultar ou modificar os horários (de 0 a 23) do objeto Date.

    • getDay()
      Retorna o dia da semana da data, expresso como um valor de 0 a 6 (domingo a sábado).

    • getDate() | setDate()
      Retorna ou altera o dia do mês do objeto Date em que é aplicado.

    • getMonth() | setMonth()
      Utilizado para consultar ou modificar o número do mês do objeto Date.

    • getFullYear() | setFullYear()
      Consulta ou define o valor do ano no objeto que contém a data e a hora.

    Os métodos anteriores de Date incluir uma versão UTC poder trabalhar diretamente com a hora universal sem ter que fazer cálculos intermediários. Nesse sentido, por exemplo, getHours() tem uma versão getUTCHours() o getMilliseconds() uma alternativa getUTCMilliseconds() trabalhar alternativamente com a hora oficial (legal) ou universal. Com getTimezoneOffset() Você pode saber a diferença que existe entre a hora universal e a hora oficial local.

    Funções JavaScript

    Se você está lendo isso com certeza sabe programar. microcontroladores en C o em C + + e conhecer o conceito de função. Embora a ideia básica seja a mesma, em JavaScript A forma como são definidos e usados ​​é um pouco diferente. Para começar, já foi dito, JavaScript Ele não usa tipos de dados explicitamente, então você não precisa indicá-lo ao definir a função. Para seguir, Não é obrigatório que uma função tenha nome, elas podem ser anônimas. Eles podem ser associados a uma variável para invocá-los, mas também pode não ser necessário, pois, às vezes, é útil invocá-los imediatamente, para o que os parênteses e parâmetros são adicionados após a definição da função.

    Para definir uma função, prefixe function, se aplicável, escreva o nome, os argumentos (os parâmetros passados ​​para a função) entre parênteses e o código que será executado quando a função for invocada entre colchetes.

    Certamente, no exemplo anterior a variável “resultado” não era necessária, mas é uma boa desculpa para lembrar o escopo variável, que funciona como você espera: a variável "resultado" só existe dentro da função "duplo". Em JavaScript também pode ser usado let, ao invés de var, para definir o escopo de uma variável para um contexto de bloco de código (entre chaves, { y })

    Ao falar sobre objetos na seção anterior, faltava algo fundamental: as propriedades foram definidas, mas os métodos não foram definidos. Como esperado, métodos de objeto são funções, eles não têm nome e são usados ​​(invocados) a partir do nome (propriedade) atribuído pela definição do objeto.

    No exemplo anterior já existe um método, "view_temperature", que exibe o valor da propriedade "current_temperature" através do console. Não é muito útil, mas dá uma ideia mais completa de como é a definição de um objeto em JavaScript.

    Para acessar os métodos de um objeto (funções) às suas propriedades, use this, como no exemplo anterior da linha 11, ao utilizar a propriedade “temperatura_atual”.

    Acesse o Document Object Model (DOM) com JavaScript

    Na JavaScript Você tem acesso ao conteúdo da página da Web em que ele é executado, bem como a alguns aspectos do navegador que exibe essa página, embora não aos recursos do sistema. A estrutura de dados que suporta as propriedades e métodos acessados ​​de JavaScript parte do objeto janela, especificamente, o conteúdo do objeto (o documento HTML) corresponde ao objeto document. Embora às vezes seja usado para maior clareza, não é necessário preceder window aos métodos ou propriedades para se referir a eles, basta, por exemplo, usar document, não há necessidade de escrever o nome do objeto raiz como em window.document, desde que a janela atual seja referenciada.

    A forma mais utilizada de encontrar um objeto dentro do documento HTML É através do método getElementById(), para o qual o id que foi indicado na criação do código é passado como argumento HTML. Pelo que foi explicado nas seções anteriores, é fácil assumir que você também pode acessar os componentes dentro do objeto document usando sintaxe de ponto (document.componente) ou colchetes usando o nome (document["componente"]), os mais úteis, como o índice numérico, de difícil utilização e pouco prático no acesso ao conteúdo de uma página web composta manualmente.

    Com JavaScript pode ser obtenha o elemento que contém outro elemento (elemento ou nó pai) consultando seu imóvel parentNode ou sua propriedade parentElement, a diferença é que o elemento pai (parentElement) do elemento final da string DOM É nulo (null) e o nó pai (parentNode) é o próprio documento (document).

    Pára modificar o conteúdo de um elemento HTML, por exemplo, o de um rótulo <div>, Isso pode ser usado innerHTML e para alterar suas propriedades você pode escolher atribuir a ele uma classe diferente com className ou alterar suas propriedades individualmente com style. Consultar o estilo exibido por um elemento na página web não é necessariamente útil style uma vez que pode depender de vários fatores ou simplesmente não ter sido explicitamente especificado. Para verificar o estilo de um elemento finalmente exibido na página web, o método getComputedStyle é usado.

    Para um elemento de documento HTML Várias classes podem ser atribuídas a ele para determinar sua aparência e comportamento, para gerenciar a lista de classes de um objeto de JavaScript pode-se recorrer a classList que oferece os métodos add para adicionar uma nova classe à lista, remove para removê-lo, toggle substituí-lo ou consultar o conteúdo da lista de classes de um elemento com item e com contains, que retorna a classe que ocupa determinada posição na lista e um valor true o false se uma determinada classe está ou não na lista.

    No exemplo anterior ele está localizado com getElementById o objeto que você deseja manipular (um elemento <div> sua id), antes de alterar a aparência, o conteúdo é excluído atribuindo-se com innerHTML uma string de texto vazia, é atribuída uma nova classe com className e seu estilo é modificado com style dependendo do valor do conteúdo (temperatura), alterando a cor, se for o caso, através da propriedade color. Uma vez estabelecido o aspecto, o valor é escrito usando novamente innerHTML.

    Na segunda parte do exemplo acima (linhas 9 a 19) um elemento de código é acessado HTML usando a sintaxe document[] e a propriedade id do elemento para alterar sua lista de classes com o método classList.remove() e com o métodoclassList.add(), com base no resultado de diversas consultas realizadas em execuções condicionais, que comparam usando classList.contains().

    Quando vai referir-se a um elemento HTML vários vezes ao longo do código JavaScript, é um pouco mais eficiente atribuí-lo a uma variável ou use seu índice em vez do nome, pois, caso contrário, o método que você usaria JavaScript obtê-la a cada vez exigiria a busca por seu nome, consumindo um pouco mais de tempo do que se uma variável fosse acessada.

    Pára adicione novos objetos ao documento HTML, eles podem ser criados primeiro com o método createElement de document e posteriormente incorporá-los ao resto dos elementos no ponto da árvore que for necessário com appendChild. Para criar um objeto XML, como objetos SVG que usamos para desenhar o gráfico dos sensores IoT, você pode usar createElementNS (NS para espaço de nome). Conforme explicado ao falar sobre o formato SVG, o namespace que corresponde a ele (para a versão atual) é http://www.w3.org/2000/svg, que deve ser passado para createElementNS como um argumento junto com o tipo de elemento, svg, neste caso.

    Uma alternativa para innerHTML para adicionar texto como conteúdo a um elemento do documento HTML é o método createTextNode() do objeto document. Com esta alternativa você pode criar novo texto (que é acessado posteriormente se for atribuído a uma variável) que é incorporado na árvore de objetos com o método appendChild(). Curtir alternativa para appendChild(), que adiciona o novo conteúdo ao final do que já existe no nó ao qual foi adicionado, você pode usar o método insertBefore(), que adiciona um novo objeto na frente de um existente. Vestir insertBefore() em vez de appendChild() fornece um método que serve, por exemplo, para classificar novos objetos na frente dos existentes quando um elemento deve estar na frente de outro (como em uma lista) ou cobrir ou ser coberto por uma estrutura gráfica na qual existem elementos mais próximos do primeiro plano ou do fundo.

    Reaja a eventos com JavaScript

    Quando o caminho de usar uma página da web como contêiner para gráficos de sensores conectados à IoT utilizou-se onload Na etiqueta <body> para começar a desenhar o gráfico. Esta propriedade, associada aos objetos de código HTML, refere-se a eventos JavaScript. Como já explicado, ele executa uma função quando a página é carregada. Embora tenha sido associado ao código HTML para mantê-lo mais em mente, poderia ter sido escrito no código JavaScript como body.onload=dibujar; ser dibujar o nome da função que deve ser iniciada quando a página da web for carregada.

    Nas versões mais recentes JavaScript eventos podem ser associados a funções usando addEventListener com o formato objeto.addEventListener(evento,función); ou usando a sintaxe objeto.evento=función; que funciona também em implementações mais antigas. Para desvincular a função associada ao evento, você tem removeEventListener que tem o mesmo formato que addEventListener.

    JavaScript É capaz de reagir a uma infinidade de eventos que podem ocorrer em uma página web. Por exemplo, ele pode detectar quando um elemento é clicado HTML com onmousedown, ou quando clicado com onclick, quando uma tecla é pressionada com onkeydown, operando a barra de rolagem com onscroll. Para o nosso propósito, basta-nos detectar o carregamento da página com onload e seu redimensionamento com onresize. Associaremos esses eventos aos objetos body y window De DOM respectivamente. O primeiro pode ser atribuído no código HTML, como visto e o segundo dentro do código JavaScript dentro da função chamada pelo primeiro e com o formato window.onresize=redimensionar; ser redimensionar a função que será chamada toda vez que a janela mudar de tamanho.

    Executar após um intervalo de tempo

    JavaScript tem dois recursos para execução diferida: setTimeout, que executa uma função após um intervalo de tempo e setInterval que executará uma função a cada determinado intervalo de tempo. Ambos os métodos requerem como parâmetros (1) a função invocada e (2) o intervalo de tempo expresso em milissegundos. Para interromper sua operação, você pode atribuir o resultado retornado por essas funções a variáveis ​​e passá-las como argumento para clearTimeout ou clearInterval quando você não deseja invocá-los novamente (ou quando não deseja que eles sejam executados pela primeira vez) setTimeout o setInterval respectivamente.

    No exemplo anterior o método é introduzido alert que serve para exibir um sinal de alerta. Embora tenha sido amplamente utilizado no passado, atualmente está quase banido do código JavaScript por causa de quão agressivo (intrusivo) é cobrir a página da web com uma caixa de diálogo.

    Em um programa escrito para um microcontrolador de uma pequena série (como a que está no prato Arduino Uno) é comum usar variáveis ​​globais, como no exemplo anterior em JavaScript, uma vez que o código é breve e não particularmente confuso, porque muitas vezes as funções são implementadas ad hoc e porque a utilização de variáveis ​​globais permite prever o uso da memória de uma forma muito simples e intuitiva, o que é crítico em sistemas com poucos recursos . Em vez de, en JavaScript É comum reduzir ao mínimo possível o uso de variáveis ​​globais. porque não precisa apressar o uso da memória, pois funciona normalmente em um CPU com recursos muito superiores aos de uma MCU, porque é provável que coexista com muitos códigos de terceiros com os quais deve trabalhar sem interferir e por se tratar de um sistema aberto, o contexto de execução futuro não pode ser previsto (o programa de um microcontrolador small determina completamente seu funcionamento sem adicionar mais código uma vez em operação) e porque as dimensões das aplicações podem dificultar a leitura se o código não encapsular seu funcionamento, tornando os métodos o mais autocontidos possível.

    Operações matemáticas com o objeto JavaScript Math

    As operações matemáticas de cálculos matemáticos mais complicados são agrupadas no objeto Math. Este objeto é utilizado diretamente, não é necessário instanciá-lo para utilizar os métodos ou propriedades (constantes) que incorpora.

    • Math.abs(n) Valor absoluto do parâmetro n
    • Math.acos(n) Arco cosseno do parâmetro n (resultado em radianos)
    • Math.asin(n) Arco seno do parâmetro n (resultado em radianos)
    • Math.atan(n) Arco tangente do parâmetro n (resultado em radianos)
    • Math.atan2(n,m) Arctangente de n/m (resultado em radianos)
    • Math.ceil(n) Arredonde o parâmetro para o número inteiro mais próximo
    • Math.cos(α) Cosseno do parâmetro α (α em radianos)
    • Math.E número e (≃2.718281828459045)
    • Math.exp(n) e elevado ao parâmetro n: en
    • Math.floor(n) Arredonde o parâmetro n para o número inteiro mais próximo
    • Math.log(n) Logaritmo natural (base e) do parâmetro n
    • Math.LN2 Logaritmo natural (base e) de 2 (≃0.6931471805599453)
    • Math.LN10 Logaritmo natural (base e) de 10 (≃2.302585092994046)
    • Math.LOG2E Logaritmo de base 2 de e (≃1.4426950408889634)
    • Math.LOG10E Logaritmo de base 10 de e (≃0.4342944819032518)
    • Math.max(a,b,c,…) Maior valor da lista de parâmetros passados
    • Math.min(a,b,c,…) Menor valor da lista de parâmetros passados
    • Math.PI Número π (≃3.141592653589793)
    • Math.pow(n,m) Primeiro parâmetro n elevado ao segundo parâmetro m: nm
    • Math.random() (Quase) número aleatório entre 0.0 e 1.0
    • Math.round(n) Arredonde o parâmetro n para o número inteiro mais próximo
    • Math.sin(α) Seno do parâmetro α (α em radianos)
    • Math.sqrt(n) Raiz quadrada do parâmetro n
    • Math.SQRT1_2 Raiz quadrada de 1/2 (≃0.7071067811865476)
    • Math.SQRT2 Raiz quadrada de 2 (≃1.4142135623730951)
    • Math.tan(α) Tangente do parâmetro α (α em radianos)

    Carregar dados do servidor com AJAX

    O método seguido para desenhar as informações armazenadas na IoT consiste em carregar os dados do servidor de tempos em tempos e redesenhar o gráfico com o qual eles são representados. Para ler os dados do servidor, é usada tecnologia AJAX (JavaScript e XML assíncronos) através de um objeto XMLHttpRequest de JavaScript. A plotagem do gráfico de dados é feita reutilizando um objeto SVG que já está no código HTML e que contém um gráfico cujas coordenadas são modificadas para que correspondam aos novos dados carregados.

    No exemplo desta proposta, além de atualizar o desenho, também é atualizado um texto na página web que mostra a data e o valor dos últimos dados medidos para cada gráfico.

    No lado do servidor existe um banco de dados que contém as informações que os sensores conectados à IoT estão monitorando. Este banco de dados é lido pela solicitação do objeto XMLHttpRequest respondendo com informações codificadas no Formato JSON, embora o nome do método utilizado sugira uma relação com o formato XML.

    No primeiro tutorial polaridad.es sobre o Armazenamento de dados IoT Você pode ver um exemplo de infraestrutura para gerenciar, do lado do servidor, as informações fornecidas pelos dispositivos conectados à Internet das Coisas. Nesta série de artigos, um servidor é usado como recurso apache a partir do qual você pode usar a linguagem de programação PHP acessar um banco de dados MySQL o MariaDB. Em servidores utilizados para suporte à IoT é muito comum encontrar bancos de dados MongoDB (NoSQL) e a linguagem de programação JavaScript em Node.js como infraestrutura de software.

    A próxima função é responsável por solicitar os dados mais recentes de um dos sensores do servidor. Na chamada de função, o objeto é usado como argumento JavaScript que suporta os dados que são desenhados. Se o mesmo gráfico representar vários valores, por exemplo para procurar visualmente uma correlação, pode-se fazer uma solicitação ao servidor para retornar vários valores simultaneamente, um método mais ideal devido à forma como o servidor funciona. Protocolo HTTP.

    Na terceira linha do exemplo anterior é preparada a consulta que será feita ao servidor, na qual será passado o argumento “zona”, cujo valor será o nome ou código do local monitorado desde que informações sobre o área pode coexistir no mesmo banco de dados sensores diferentes (por exemplo, termômetros que medem a temperatura em salas diferentes). Espera-se que o parâmetro passado para a função anterior, o objeto com os dados do gráfico, inclua uma propriedade com o nome da sala (“name_suffix”).

    Entre as linhas 7 e 14 do código anterior, o objeto XMLHttpRequest que é armazenado na variável "ajax". Antes de escolher como criar o objeto, você pesquisa window por si XMLHttpRequest não estava disponível (algo que acontecia nas versões antigas do explorer da Microsoft e embora esteja muito atrás, serve como exemplo de alternativas para criar o objeto usando a sintaxe (mais nativa)) Object.create o new, semelhante ao de outras linguagens orientadas a objetos.

    Para poder gerenciar a resposta imediatamente, o código que a trata é preparado nas linhas 15 a 26 antes de fazer a solicitação ao servidor.

    O caminho de faça a consulta HTTP para o servidor consiste em abrir uma conexão com open indicando tipo e página (opcionalmente nome de usuário e senha), prepare os cabeçalhos do protocolo com setRequestHeader y envie o pedido com send. O cabeçalho HTTP Content-length você precisará saber o comprimento da consulta (número de caracteres) que é calculado usando length.

    Quando o pedido AJAX está pronto, a função associada ao evento é executada onreadystatechange. Em vez de atribuir uma função, no exemplo anterior é definida instantaneamente uma função anônima que gerenciará a recepção dos dados que chegam do servidor. Primeiramente, na linha 18, verifica-se que o status da solicitação é “finalizado”, o que corresponde ao valor 4 da propriedade readyState, que o status é "OK" do Protocolo HTTP (código 200) que pode ser obtido na propriedade status e que os dados que chegaram são Formato JSON, consultando o imóvel responseType.

    Depois de verificado que o status da resposta é o esperado, na linha 20 do exemplo anterior cria um objeto com o resultado, convertendo o texto JSON. A resposta prevê uma data a ser retornada, isso nos permite ver se o resultado que o servidor envia já havia sido representado anteriormente no gráfico, o que é verificado na linha 21. Se os dados forem novos, na linha 23 A função que é responsável por redesenhar o gráfico com as novas informações é chamado.

    A ideia ao propor este método de leitura é que os dados sejam atualizados com muita frequência. Se a informação apresentada corresponder a um longo prazo (como as temperaturas de um dia ou de uma semana), pode ser implementado um pedido inicial que recolhe todos os dados disponíveis e depois um, semelhante ao do exemplo, que os atualiza em o período correspondente.

    Gere dados aleatórios para teste

    Quando toda a infraestrutura de servidores e clientes estiver pronta, uma função como a da seção anterior se encarregará de ler os dados e desenhar o gráfico com eles, mas Na fase de testes pode ser mais prático usar números aleatórios dentro de uma faixa controlada para ver se o código que está sendo escrito está correto. A função a seguir pode servir de exemplo para obter dados durante a construção da aplicação final.

    Em vez de ler as informações de um banco de dados, o exemplo acima as gera aleatoriamente e as repassa para a função responsável por desenhar o gráfico. O dado inventado é um vetor formado por uma data expressa em milissegundos, o momento de registro das informações do sensor e os dados monitorados, que estão entre um valor máximo e um valor mínimo.

    Neste exemplo, ao gerar uma data ela pode ser atrasada em até um segundo (1000 milissegundos) em relação à data no momento da invenção. Como Math.random() gera um número entre 0.0 e 1.0, multiplicá-lo por 1000 produz um número entre 0 e 1000 que é então convertido em um número inteiro. Da mesma forma, o valor é obtido multiplicando o número aleatório pelo intervalo (máximo menos mínimo) e somando o mínimo.

    Desenhe o gráfico dos sensores IoT com um gráfico SVG

    Como vimos como podemos obter os valores que queremos representar (temperatura, no exemplo) e sua localização temporal, que podem ser expressas em conjunto na forma de coordenadas, o exemplo abaixo mostra uma função para traçar um caminho que une esses pontos e opcionalmente uma área colorida delimitada por essa linha no topo. O resultado seria como a imagem a seguir.

    Exemplo de gráfico gerado com SVG e JavaScript para representar dados de sensores IoT

    O eixo horizontal (X) do gráfico representa o tempo e o eixo vertical (Y) os valores que os sensores conectados à IoT vêm monitorando. O intervalo horizontal é de alguns segundos, pois nesta proposta o gráfico é atualizado com muita frequência (a cada segundo, por exemplo) para fornecer informações quase em tempo real sobre o estado dos sensores.

    No código anterior existem dois aspectos interessantes, primeiro o cálculo que permite adaptar a faixa de valores que são representados e em segundo lugar o construção de propriedade d que indica as coordenadas dos pontos no layout (path).

    Para adaptar a faixa de valores representados, eles são movidos de um mínimo e dimensionados para que a magnitude visível corresponda ao tamanho do gráfico. No caso do tempo, o deslocamento é obtido subtraindo o intervalo que se deseja exibir do tempo mais longo (a data e a hora mais próximas da atual) (20 segundos no exemplo). O deslocamento dos valores de temperatura é o da faixa inferior (um grau) menos o valor mais baixo, de forma que os dados abaixo sejam os mais semelhantes ao valor mais baixo permitido, mas deixando uma margem que nos permite apreciar o que é passado

    O coeficiente que multiplica os valores de tempo para obter as coordenadas horizontais do gráfico é obtido dividindo a largura total do gráfico (100 unidades no exemplo) pelo intervalo de tempo representado (20 segundos no exemplo). Para obter o coeficiente com os valores escalares de temperatura, deve-se lembrar que a faixa representada vai de uma margem abaixo do valor mínimo até uma margem acima do máximo, um grau em ambos os casos. Desta forma, o coeficiente de escala vertical resulta da divisão da altura do gráfico (100 unidades no exemplo) pelo valor máximo, menos o mínimo mais a margem superior e inferior. Como esses valores podem evoluir completamente em temperaturas negativas, usamos Math.abs() para usar o valor absoluto da diferença.

    A propriedade d do objeto path É construído concatenando as coordenadas dos pontos em um texto. Cada par de coordenadas é precedido por um código SVG L, que desenha uma linha da posição atual até um valor absoluto indicado pelas coordenadas. Os valores X e Y são separados por vírgulas e cada operação SVG é separado por um espaço do próximo.

    Para iniciar o layout, use o código M (mover para uma coordenada absoluta). No caso da plotagem fechada e preenchida, inicia-se no canto inferior direito, no caso da plotagem aberta que desenha o perfil de dados, inicia-se pelo último valor representado (o mais recente). Para finalizar o layout fechado, utiliza-se o código Z somando como último ponto aquele que possui o mesmo valor da coordenada X do último ponto da reta e como coordenada Y o menor valor representado.

    Neste exemplo, a função dibujar_grafico(), que é a chamada no carregamento da página, obtém os valores iniciais para testar (não o último valor em tempo real) e prepara o intervalo em que os dados serão renderizados: 20 segundos (20000 ms) na horizontal e 15°C na vertical de -5°C a +10°C com margem superior e inferior de um grau. Faça duas ligações para actualizar_grafico(), na primeira passagem true como argumento, que indica que o gráfico deve ser fechado para representar uma área preenchida, e na segunda chamada passa false estabelecer um limite. Em cada caso, o objeto path modificado é aquele que tem o aspecto correspondente, com preenchimento e sem borda no primeiro caso e com certa espessura de linha e sem preenchimento no segundo.

    A função actualizar_grafico() trabalhar em um objeto SVG que usa o seguinte código como contêiner HTML. O objeto SVG contém dois caminhos, um para desenhar a linha e outro para desenhar a área preenchida. Ao carregar a página web, a partir do elemento <body> a função anterior é chamada automaticamente, dibujar_grafico() graças ao evento JavaScript onload.

    Na linha 10 do código HTML acima, é estabelecida no estilo uma largura (por exemplo) de 820 px e uma altura de 150 px (algo que, na versão final, será aconselhável fazer com uma classe e um documento APF). Parece estranho que as linhas 13 e 14 definam o tamanho do objeto SVG como 100% de largura e altura (que melhor corresponde às dimensões da janela, 100×100). Como já mencionado, a razão para fazer isso é trabalhar sempre com dimensões conhecidas e ajustar os valores representados a elas. As outras alternativas seriam calcular o espaço do gráfico a cada vez e depois reajustar os valores ou forçar dimensões fixas para o gráfico, às quais o documento deverá aderir.

    Tendo optado por um gráfico cujas dimensões mudam de acordo com o código HTML, é necessário incluir a propriedade vector-effect com a coragem non-scaling-stroke evitar que as espessuras das linhas sejam deformadas quando o gráfico não mantém as proporções 1:1 escolhidas na página web em que é exibido, como ocorre na proposta anterior.

    Para "cortar" o gráfico e mostrar apenas a área escolhida, use viewBox. Neste caso optamos por ver a parte do gráfico que começa em 0,0 (canto superior esquerdo) e mede 100x100 para baixo e para a direita. A parte do desenho localizada em coordenadas com valores negativos ou maiores que 100 não será exibida na página web mesmo que existam no objeto SVG

    Adicione novos elementos ao desenho SVG

    No exemplo anterior, a função actualizar_grafico() usar um layout SVG para o qual a propriedade é alterada d, que é o que expressa a cadeia de coordenadas. A alternativa seria criar o objeto inteiro toda vez que ele for redesenhado. A vantagem da primeira opção é que a aparência gráfica (como espessura ou cor) é definida no código HTML, a limitação é que os objetos devem ser criados previamente.

    Para criar objetos SVG, use createElementNS(), o que permite incluir o espaço para nome. No exemplo abaixo, um novo objeto de texto é criado (text) e está associado a um elemento SVG que já existe no código HTML do site. Depois que o novo elemento é criado, suas propriedades são atribuídas com setAttribute() e é adicionado a SVG com appendChild().

    Modifique a proporção dos elementos do desenho

    Se você tentou rotular com a função do exemplo da seção anterior, terá visto que o texto parece deformado quando a proporção do objeto na página web (width y height De código HTML) não é igual ao da área representada (viewBox). Para adaptar a proporção é necessário conhecer as medidas do objeto SVG para o qual você pode consultar o estilo do objeto ou do contêiner HTML, se o objeto SVG transferir esta propriedade. Atribuindo propriedade transform para os objetos SVG que dependem da proporção, a deformação pode ser corrigida aplicando uma operação de escala scale() em que o coeficiente em X é diferente daquele em Y.

    SVG permite que vários objetos sejam agrupados formando um novo elemento composto que também suporta propriedades, como objetos simples. Para aplicar a mesma transformação a uma série de objetos de uma vez em vez de cada objeto separadamente, você pode agrupá-los de acordo com este recurso e aplicar uma única propriedade transform para todos eles.

    Como explicado ao falar sobre Formato SVG, os elementos de um grupo são colocados entre os rótulos <g> y </g>. Para adicionar de JavaScript elementos para um grupo SVG é usado, como visto no exemplo anterior, appendChild() assim que o novo objeto for definido.

    Para estabelecer uma origem ao aplicar transformações, a propriedade pode ser usada em objetos SVG transform-origin, cujo valor são as coordenadas X e Y do ponto de início da transformação. Caso não seja indicado expressamente um valor para a origem da transformação (no navegador web), utiliza-se o centro de coordenadas. Infelizmente, no momento em que este artigo foi escrito, especificar o comportamento das transformações usando uma fonte diferente da padrão não é homogêneo entre navegadores e deve ser usado com cautela.

    Junto com a transformação de escala com scale Existem outros, como rotação com rotation e o movimento com translate, que oferecem um alternativa à representação gráfica: em vez de obter novas coordenadas, você pode representá-las em seu próprio espaço e transformar o gráfico para caber no formato em que deseja representá-las.

    Adicione referências ao gráfico

    Agora que a parte principal do gráfico está resolvida traçando os valores com um perfil e uma área preenchida, ele pode ser completado com referências que auxiliam sua leitura. Como exemplo, vamos começar traçando algumas referências horizontais (linhas) que marcam os valores máximo e mínimo aceitáveis, bem como um valor desejado. Conforme explicado, você pode optar por adicionar os objetos ao SVG direto de JavaScript ou inclua-os manualmente no código HTML e modificá-los mais tarde com JavaScript.

    Parece lógico rotular essas referências horizontais com um texto que esclareça o valor que representam. Para destacar o texto, você pode usar retângulos que se destaquem do fundo e do gráfico. Como os textos deverão ser dimensionados para compensar a deformação, todos poderão ser agrupados em um objeto ao qual será aplicada a escala; A principal vantagem de fazer desta forma é poder modificá-los em uma única operação caso o contêiner do gráfico (a janela do navegador) seja redimensionado e mude a proporção que a escala corrige.

    Existem vários aspectos interessantes no código de exemplo acima. Primeiramente comente que constantes (variáveis ​​globais) foram utilizadas para tornar o exemplo mais legível para usuários vindos da programação. microcontroladores en C o em C + +. Como será visto mais adiante, a maneira ideal de programá-lo em JavaScript Seria utilizar objetos que conteriam esses valores e métodos que gerenciariam as referências neste exemplo ou o gráfico, em geral, em um sistema de produção.

    Por outro lado, avançando qual seria o código mais genérico, foram desenvolvidas funções separadas que calculam os diferentes coeficientes que corrigem a proporção do gráfico para ajustar o texto proporcion_grafico(), a escala dos valores dependendo do seu intervalo escala() e um fator de correção para medições conhecidas em valor absoluto, como medições em referências medida_grafico().

    A leitura deste código deve ajudar a esclarecer o contexto em que funciona uma aplicação como esta, que desenha gráficos em tempo real e deve ser flexível para ser apresentada em vários contextos gráficos (vários tamanhos e proporções, no mínimo). Primeiro de tudo, os objetos devem ser gerados SVG, seja "manualmente" no código HTML, seja por meio de código JavaScript e em qualquer caso, as referências a esses objetos devem ser obtidas posteriormente para manipulá-los a partir de JavaScript para que novos gráficos possam ser desenhados e a representação de um gráfico já desenhado possa ser adaptada a uma mudança no meio em que é apresentado.

    Outra referência que pode ajudar a interpretar facilmente um gráfico são os pontos que representam valores específicos (os nós da reta). Neste exemplo, em que representamos uma única magnitude, a escolha de um símbolo não é crítica, mas se vários valores diferentes forem sobrepostos para buscar correlação, é interessante distinguir, além de utilizar outros recursos como cor , desenhando símbolos diferentes. Os gráficos utilizados para o nó de linha devem ser modificados em tamanho e proporção, como ocorre, por exemplo, com os textos, para que suas dimensões sejam absolutas e para que suas proporções sejam mantidas mesmo que as da caixa que contém mudem.

    No exemplo anterior já vimos como calcular os diferentes coeficientes para redimensionar e corrigir a proporção do desenho; Quanto a como implementar o gerenciamento dos símbolos dos nós ou vértices do grafo, uma possível solução pode ser armazenar os objetos SVG em um vetor e modificar sua posição quando o gráfico for atualizado lendo um novo valor, ou quando for redesenhado redimensionando o contêiner. No primeiro caso a sua posição teria que ser modificada e no segundo a sua proporção com a propriedade transform e o valor de scale. O código a seguir é uma modificação da função actualizar_grafico() para incluir o reposicionamento dos símbolos dos vértices do gráfico.

    Modificações feitas na função actualizar_grafico() para obter a nova função actualizar_grafico_puntos() São os destacados no código do exemplo anterior. Primeiro, na linha 5, pegamos um vetor de objetos SVG como parâmetro. Este vetor conterá os símbolos que precisam ser reposicionados nos novos nós do gráfico.

    Nas linhas 39 e 40 são atribuídas as novas coordenadas do centro, cx y cy, aos dos valores que estão sendo representados. Se o símbolo não estiver baseado no centro, provavelmente será necessário adicionar um deslocamento no cx metade da largura e em cy de metade da altura para reposicioná-los exatamente no nó do gráfico.

    Nas linhas 57 a 61, os pontos que correspondem às coordenadas que não são desenhadas por estarem recortadas pela borda esquerda são reposicionados fora do gráfico. A coordenada de cy para zero e o de cx para qualquer número negativo (maior que o próprio ponto) para que não seja mostrado quando cortado, como a parte esquerda do gráfico, pela janela do SVG.

    Gerencie o gráfico a partir de um objeto com JavaScript

    Todas as operações explicadas até agora podem ser integradas em um objeto para gerenciar o gráfico com um estilo mais típico das novas versões do JavaScript. Esta alternativa de implementação tem a vantagem adicional de simplificar a incorporação de vários gráficos, de valores diferentes, numa mesma página web.

    Antes de discutir a implementação, vamos revisar as formas mais comuns de criar objetos com JavaScript e algumas peculiaridades das funções que afetam a proposta de desenho de gráficos de sensores IoT.

    Já foi explicado que a nova forma de criar objetos em JavaScript (disponível desde a versão 5 do ECMAScript) consiste em usar Object.create, que você deve se acostumar a usar em vez do "clássico" new, que obviamente ainda funciona corretamente, embora seu objetivo seja mais simular o estilo de linguagens com objetos baseados em classes (JavaScript baseia a criação de objetos em protótipos) do que uma alternativa funcional.

    O código anterior permite lembrar as diferenças entre criar os objetos com Object.create ou com new. Serve também para enfatizar que, embora a função com a qual o objeto é criado com new pode estar em qualquer lugar do código, o objeto já deve existir antes de poder ser instanciado com Object.create (O objeto ES5_Object não é uma função).

    Nas linhas 3 e 4, para definir um valor padrão para as propriedades na função que cria o objeto com new, cada propriedade é atribuída ao valor do argumento correspondente ou (||), se nenhum argumento tiver sido passado, ou seja, se forem indefinidos (undefined), já que essa circunstância é avaliada como false, o valor padrão será atribuído.

    O contexto em que uma função é executada JavaScript levanta duas questões que é importante ter em mente e que também podem ser confusas ao usar esta linguagem de programação depois de ter trabalhado com outras, como C o C + +, no nosso caso. O contexto inclui as variáveis ​​definidas no escopo da função (e as globais) o que, aliás, levanta um conceito interessante, os "fechamentos" que estabelecem todo um estilo de programação em JavaScript. Dito isto, seria de esperar que this, que se refere ao objeto quando utilizado dentro do código que o define, o contexto de execução no qual foi definido é mantido, mas aquele que utiliza é o contexto a partir do qual a função é chamada. Este comportamento é transparente na maioria dos casos, mas há duas circunstâncias em que pode ser confuso: uma função definida dentro de outra função e um método chamado a partir de um evento de objeto. window.

    Ao executar o código anterior, o texto comentado ao final é exibido no console. As duas linhas marcadas refletem um comportamento que pode ser confuso: o contexto de execução da função probar_dentro() não probar(), como seria de esperar, mas window, que mostra as variáveis ​​globais e não as propriedades de mesmo nome. Se você não quiser esse comportamento, simplesmente crie uma variável na função de nível mais alto e atribua-a a this, como no código a seguir.

    Para controlar o contexto de execução quando um método é chamado a partir de um evento window, por exemplo redimensionando a janela do navegador, outra peculiaridade do JavaScript: a possibilidade de programar "fábricas de funções", ou seja, funções que geram outras funções, retornando-as com return.

    No código de exemplo acima, o método llamar() de objetos Contexto Ele não faz o trabalho, mas retorna uma função anônima que cuida disso. Para verificar se tudo funciona conforme o esperado, existe uma variável global com o mesmo nome da propriedade que a função exibe no console; Se o contexto estiver correto, será exibido o valor da propriedade e não o da variável global.

    JavaScript Tente corrigir os sinais de ponto e vírgula que omitimos no final das frases. Isso permite um estilo de escrita descontraído, mas é uma faca de dois gumes que deve ser tratada com cuidado. Na maioria dos casos, para evitar os efeitos indesejáveis ​​que isso produz em expressões que ocupam várias linhas, pode-se usar parênteses ou preceder a forma como JavaScript interpretará o código; É por isso que a linha 8 do exemplo inclui function atrás de return, se eu tivesse usado outra linha o significado seria muito diferente. Na minha opinião, a solução mais legível é usar uma variável intermediária (dispensável) como na versão a seguir; Obviamente, uma vez compreendido o comportamento, a decisão cabe ao programador.

    No mesmo sentido de avaliar uma expressão como uma função, ou seja, retornar uma função e não o valor que a função retorna; na linha 21 do último exemplo (estava na linha 19 do anterior) para com clearInterval a função chamada com setInterval. Para que atue por 30 segundos, a parada é diferida com setTimeout, que por sua vez precisa de uma função como primeiro argumento; para entregar a execução como parâmetro clearInterval com a variável que contém a chamada periódica (e não a função clearInterval) é para isso que a função anônima na última linha foi criada.

    A escolha entre escrever o código integrando a definição da função, mais compacta (como na linha 21) ou utilizando uma variável auxiliar, na minha opinião, mais legível (como nas linhas 19 e 20) varia pouco no desempenho e depende de mais estilo e legibilidade para manutenção.

    Para testar o código, antes de ter os dados no servidor, você pode utilizar um gerador de valores aleatórios no intervalo desejado ou preparar tabelas com valores controlados que simulem o funcionamento nas condições desejadas. O exemplo a seguir usa um gerador de dados simples em todo o intervalo, por isso eles parecem um pouco exagerados.

    Para testar, você pode baixe o código completo do exemplo formado por uma página web escrita em HTML, o estilo APF e o código JavaScript. Este último é o mais relevante, pois os demais componentes são apenas de suporte mínimo, muito simplificados e muito mais desenvolvidos nos artigos das seções correspondentes.

    Postar Comentário

    Você pode ter perdido