Generer og modifiser SVG-grafikk av data fra sensorer koblet til IoT med JavaScript
I denne siste delen av artikkelserien om tegning grafikk med data fra sensorer koblet til tingenes internett, er det på tide å snakke om hvordan du genererer eller modifiserer med Javascript tegninger i format SVG og noen av elementene HTML som fungerer som en beholder eller som gir utfyllende informasjon til grafikken.
Målbrukerne av denne opplæringen er ment å danne en elektronikk- og dataprogrammeringsprofil. mikrokontrollere, er de kanskje ikke kjent med HTML, CSS o SVG; Av denne grunn ble det i de tidligere delene laget en kort introduksjon til språket eller den tilsvarende teknologien. I denne siste delen er tilnærmingen litt annerledes, siden leserne sikkert vet hvordan de skal programmere, er det mulig å bruke språket C + + det, liksom Javascript, deler grunnleggende syntaks med C og det kan tas som en referanse å hoppe over de fleste grunnleggende programmeringskonsepter og dermed fokusere på forskjellene og den spesifikke bruken som interesserer oss for å lage sensorgrafikk i IoT.
Navnet gir en pekepinn på den første forskjellen: Javascript Det er et programmeringsspråk script (bindestrek) og som sådan er det det tolket, det er ikke nødvendig å kompilere det; konteksten der script (en nettleser, for eksempel) vil lese, oversette og utføre ordrene. For å være presis er det i de fleste tilfeller en runtime compilation (JIT), men for kodeskrivingsprosessen Javascript Det påvirker oss ikke, vi skriver bare koden og det kan fungere.
Navnet inneholder også den første forvirringen: Javascript har ikke det minste forhold til Java. I utgangspunktet, da den ble utviklet Netscape for nettleseren ble den først kalt Mocha og deretter det mindre forvirrende LiveScript. Etter den vellykkede implementeringen i nettlesere, og transcendere dem, ble den standardisert som ECMAScript (At ECMA-262, versjon 6 i skrivende stund) for å bli nøytral med hensyn til nettlesere som implementerer det. Foreløpig er det også en standard ISO fra versjon 5, 2011 (ISO / IEC 16262: 2011 på tidspunktet for skriving av artikkelen)
Variabler, grunnleggende datatyper og objekter i JavaScript
I motsetning til det som for eksempel skjer i C + +, en Javascript datatype ikke inkludert når du deklarerer en variabel og også typen assosiert med en variabel ikke er fast, er det mulig å tilordne en verdi av en annen type under gjennomføringen av programmet.
1
2
3
4
5
6
7
|
var cosa;
cosa=“texto”;
console.log(typeof cosa); // Debería mostrar string en la consola
cosa=123;
console.log(typeof cosa); // Debería mostrar number en la consola
cosa={temperatura:22,corriente:1.5};
console.log(typeof cosa); // Debería mostrar object en la consola
|
I forrige eksempel er variabelen "ting" blitt deklarert (uten å angi datatypen), deretter tilordnes data av en annen type og den konsulteres med typeof
typen som Javascript som han har tolket. For å feilsøke koden kan du skrive den i nettleserens inspektørkonsoll (som ikke vil påvirke presentasjonen av nettet) med console.log()
.
For å tvinge fram konvertering av data til en bestemt type, spesielt tekst til numerisk, kan du bruke funksjoner som f.eks parseInt()
o parseFloat()
som konverteres til henholdsvis heltall eller flyttall. Den motsatte konverteringen kan gjøres med String()
, selv om det neppe er nødvendig da automatisk konvertering vanligvis er tilstrekkelig. Med parseFloat()
Du kan for eksempel få verdien av en nettsideegenskap, for eksempel bredden eller høyden til et objekt, som inkluderer enheter; På denne måten uttrykket parseFloat("50px");
vil returnere 50, en numerisk verdi, som et resultat.
En Javascript det er ingen forskjell mellom doble og enkle anførselstegn; Datatypen i begge tilfeller er string
, og hver av dem kan inkludere den andre uten behov for escape-koder.
1
2
3
4
5
6
7
8
9
10
|
var texto;
console.log(typeof texto); // Debería mostrar string en la undefined
texto=“esto es un texto”;
console.log(typeof texto); // Debería mostrar string en la consola
texto=‘A’;
console.log(typeof texto); // Debería mostrar string en la consola
texto=“esto es un ‘texto'”;
console.log(typeof texto); // Debería mostrar string en la consola
texto=‘”A”‘;
console.log(typeof texto); // Debería mostrar string en la consola
|
I forrige eksempel kan det ses at en variabel, når den er deklarert (eksisterer) men ikke har blitt tildelt noen verdi, inneholder en udefinert datatype (undefined
). Et ikke-tilordnet objekt har verdien null
; Det vil si at objektet eksisterer, men uten verdi; en variabel som refererte til den ville ikke ha en typeof
undefined
men object
. Et objekt kan også være tomt, det vil si ikke null, men ikke ha noen egenskaper.
Til definere et objekt i Javascript er innelukket i seler ({
y }
) egenskapene eller metodene, atskilt med kolontegnet (:
) egenskapsnavn egenskapsverdi og med komma (,
) de forskjellige egenskapene. Du kan finne mer informasjon om denne måten å uttrykke et objekt på i artikkelen om JSON-format.
Selv om du kan bruke syntaks som kan få deg til å tenke noe annet, en Javascript Det er ingen klasser, men prototyperDet vil si at for at et objekt skal arve egenskaper og metoder, opprettes et annet objekt (prototypen) som de andre (barna) bruker som referanse. Syntaksen nærmest stilen til Javascript å bruke en prototype er Object.create
selv om det også er mulig (og noen ganger nyttig) å bruke new
som i andre objektorienterte språk.
1
2
3
4
|
var perro=new Mamifero(); // Esto funciona, pero no es exactamente el nuevo estilo JavaScript
console.log(perro instanceof Mamifero);
var gato=Object.create(Mamifero); // Crear un objeto usando un prototipo al estilo JavaScript
console.log(Mamifero.isPrototypeOf(gato));
|
Til spør om ett objekt er en forekomst av et annet, hvis du bruker den som en prototype, hvis du arver dens egenskaper, kort sagt, kan du bruke instanceof
(opprettet med new
) eller isPrototypeOf
(opprettet med Object.create
) som vil evalueres til sann når objektet bruker prototypen og falsk når det ikke gjør det.
Når et objekt er opprettet ved å bruke et annet som en prototype, det vil si at når et objekt er instansiert, kan det legge til nye egenskaper eller overstyre prototypeegenskaper ved å bruke punktsyntaks som i gato.peso=2.5
.
La arrays i Javascript De er forskjellige fra de du sikkert kjenner i C. Til å begynne med er de deklarert uten at det er nødvendig å angi lengden, bare med tegn på åpning og lukking av firkantede parenteser ([
y ]
), komponenter kan være heterogene (ulike datatyper i samme array) og nye elementer kan legges til uten å være begrenset til en grense. Matrisene til Javascript er faktisk lister (samlinger) av elementer som referert av en numerisk indeks eller ved et navn. En matrise kan samtidig inneholde numeriske indekser og elementnavn, men det er vanlig å bruke objekter (egenskaper) for å utnytte den andre typen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// Declarar matrices (arrays)
var preparada=[]; // La matriz ha sido declarada pero (todavía) no contiene valores
var cosas=[“silla”,“mesa”,“caja”]; // Matriz declarada con componentes formada por cadenas de texto
var valores=[200,“lleno”,0.5,true,“simple”,false,false,10]; // Matriz declarada con componentes heterogéneos
var ramas=[20,“abc”,[1,2,3],false,[10,20,[“uno”,“dos”]]]; // Matriz que contiene matrices
var demode=new Array(10,20,30,4,3,2,1); // La sintaxis con new no es la preferida de JavaScript aunque funciona…
var peligrosa=new Array(10); // …pero con el riesgo de confundir índices con elementos: la matriz peligrosa tiene 10 elementos, no un elemento de valor 10
// Acceder a los valores de la matriz
preparada.push(33.33); // Añade un nuevo valor al final de la matriz
console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 1 elemento
console.log(cosas[0]); // Muestra en la consola el primer valor de la matriz (las matrices empiezan en el índice cero)
cosas[2]=“tarro”;
preparada[10]=50; // Los índices no tienen que ser consecutivos
console.log(“La matriz ‘preparada’ contiene “+preparada.length+” elementos”); // Ahora contine 11 elementos
console.log(“Elemento sexto: “+preparada[5]); // undefined
// Verificar si una variable es (apunta a) una matriz
console.log(Array.isArray(cosas)); // Nuevas versiones de JavaScript (ECMAScript versión 5 o superior)
console.log(cosas instanceof Array); // Implementaciones de JavaScript (ECMAScript) más viejas
// Para esto es mejor usar objetos
var frutas=[];
frutas[“peras”]=20;
frutas[“manzanas”]=30;
frutas[4]=10;
console.log(frutas.peras);
console.log(frutas[“manzanas”]);
console.log(frutas[4]);
console.log(frutas[3]); // undefined
|
Som du kan se i forrige eksempel, for å vite om en variabel tilsvarer en forekomst av en matrise (det er et matriseobjekt) kan du bruke instanceof
, som allerede har blitt brukt med generiske objekter eller, i nyere versjoner av Javascript du kan ty til Array.isArray()
For å få tilgang til elementene i matrisen kan du bruke indeksen (matriz[7]
) eller ved eiendomsnavnet med navnet i hakeparenteser (matriz["nombre"]
) eller med den vanlige punktsyntaksen for objekter (matriz.nombre
). Siden navnet er en tekststreng, kan et uttrykk, inkludert variabler, brukes til å komponere det. For å gå gjennom en matrise med egenskaper, kan en loop med formatet brukes for(propiedad in matriz)
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var matriz=[];
matriz[“color”]=“verde”;
matriz[“grosor”]=10;
matriz[“estado”]=“nuevo”;
matriz[0]=25.0;
matriz[1]=“uno”;
for(propiedad in matriz)
{
console.log(propiedad+” valor “+matriz[propiedad]);
}
/* El resultado en la consola será:
0 valor 25
1 valor uno
color valor verde
grosor valor 10
estado valor nuevo
*/
|
Det er interessant for vårt mål å behandle objektet Date
, for å representere og administrere dato og klokkeslett i Javascript. Objektet kan instansieres uten data, så det vil ta gjeldende dato og klokkeslett, eller det kan opprettes ved å angi en dato som en verdi, enten i millisekunder siden 1. januar 1970 (som f.eks. Unix-tid eller POSIX-tid men uttrykt i millisekunder i stedet for sekunder) eller spesifisere separate verdier for år, måned, dag, time...
Objektet inkluderer en komplett serie av metoder for å spørre eller stille inn dato og klokkeslett:
-
now()
Returnerer gjeldende dato og klokkeslett uttrykt i millisekunder siden 1. januar 1970 -
getTime()
|setTime()
Henter eller endrer henholdsvis tidsverdien i millisekunder siden 1. januar 1970.valueOf()
, som er en metode som finnes i de fleste objekter, oppnås også verdien til det tilsvarende Dato-objektet, som f.eksgetTime()
med Unix-tid eller POSIX-tid uttrykt i ms. -
getMilliseconds()
|setMilliseconds()
Brukes til å spørre eller angi brøkdelen av millisekund av objektetDate
som den er utført på. Hvis det konsulteres, er verdien som oppnås mellom 0 og 999, men større verdier kan tildeles som vil samle seg i den totale datoen og klokkeslettet, så som resten av get-metodene tjener det til å øke verdien til objektetDate
(eller reduser den hvis negative verdier brukes). -
getSeconds()
|setSeconds()
Returnerer eller endrer henholdsvis verdien av objektets sekunderDate
. -
getMinutes()
|setMinutes()
Brukes til å konsultere eller stille inn referatet til objektetDate
. -
getHours()
|setHours()
Lar deg konsultere eller endre timene (fra 0 til 23) for objektetDate
. -
getDay()
Returnerer ukedagen for datoen, uttrykt som en verdi fra 0 til 6 (søndag til lørdag). -
getDate()
|setDate()
Returnerer eller endrer dagen i måneden for objektetDate
som den brukes på. -
getMonth()
|setMonth()
Brukes til å konsultere eller endre månedsnummeret til objektetDate
. -
getFullYear()
|setFullYear()
Spørrer eller setter årsverdien på objektet som inneholder dato og klokkeslett.
De tidligere metodene for Date
inkludere en versjon UTC å kunne jobbe direkte med universell tid uten å måtte gjøre mellomregninger. I den forstand er f.eks. getHours()
har en versjon getUTCHours()
o getMilliseconds()
et alternativ getUTCMilliseconds()
å jobbe alternativt med offisiell (lovlig) eller universell tid. Med getTimezoneOffset()
Du kan vite forskjellen som eksisterer mellom universell tid og lokal offisiell tid.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
var dia_semana=[“domingo”,“lunes”,“martes”,“miércoles”,“jueves”,“viernes”,“sábado”];
var nombre_mes=[“enero”,“febrero”,“marzo”,“abril”,“mayo”,“junio”,“julio”,“agosto”,“septiembre”,“octubre”,“noviembre”,“diciembre”];
var digitos_hora;
var hoy=new Date();
var texto_hoy=“”;
texto_hoy+=“Hoy es “;
texto_hoy+=dia_semana[hoy.getDay()];
texto_hoy+=“, “;
texto_hoy+=hoy.getDate();
texto_hoy+=” de “;
texto_hoy+=nombre_mes[hoy.getMonth()];
texto_hoy+=” de “;
texto_hoy+=hoy.getFullYear();
texto_hoy+=” y son las “;
digitos_hora=hoy.getHours();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
texto_hoy+=“:”;
digitos_hora=hoy.getMinutes();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
texto_hoy+=“:”;
digitos_hora=hoy.getSeconds();
texto_hoy+=digitos_hora>9?digitos_hora:“0”+digitos_hora;
|
JavaScript-funksjoner
Hvis du leser dette, vet du sikkert hvordan du programmerer. mikrokontrollere en C o en C + + og kjenner funksjonsbegrepet. Selv om den grunnleggende ideen er den samme, i Javascript Måten de er definert og brukt på er litt annerledes. Til å begynne med har det allerede blitt sagt, Javascript Den bruker ikke eksplisitt datatyper, så du trenger ikke å angi det når du definerer funksjonen. Å følge, Det er ikke obligatorisk for en funksjon å ha et navn, de kan være anonyme. De kan assosieres med en variabel for å påkalle dem, men det kan heller ikke være nødvendig siden det noen ganger er nyttig å påkalle dem umiddelbart, for hvilke parenteser og parametere legges til etter definisjonen av funksjonen.
For å definere en funksjon, prefiks function
, hvis aktuelt, skriv navnet, argumentene (parameterne som sendes til funksjonen) i parentes, og koden som vil bli utført når funksjonen påkalles i klammeparenteser.
1
2
3
4
5
|
function doble(numero)
{
var resultado=numero*2;
return resultado;
}
|
Visst, i forrige eksempel var "resultat"-variabelen ikke nødvendig i det hele tatt, men det er en god unnskyldning for å huske variabelt omfang, som fungerer som du forventer: «resultat»-variabelen eksisterer kun innenfor «dobbel»-funksjonen. I Javascript kan også brukes let
, i stedet for var
, for å dekke en variabel til en kodeblokkkontekst (omsluttet av krøllete klammeparenteser, {
y }
)
Når man snakker om objekter i forrige avsnitt, manglet noe grunnleggende: egenskaper er definert, men metoder er ikke definert. Som forventet, objektmetoder er funksjoner, de har ikke noe navn og brukes (påkalles) fra (egenskaps)navnet tildelt av objektdefinisjonen.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var
termostato=
{
temperatura_actual:0.0,
temperatura_frio:18.5,
temperatura_calor:22.0,
consumo:0,
ver_temperatura:
function()
{
console.log(“Temperatura actual: “+this.temperatura_actual+” °C”);
}
}
|
I forrige eksempel er det allerede en metode, "view_temperature", som viser verdien av "current_temperature"-egenskapen gjennom konsollen. Det er ikke veldig nyttig, men det gir en mer fullstendig ide om hvordan definisjonen av et objekt er Javascript.
For å få tilgang til metodene til et objekt (funksjoner) til dets egenskaper, bruk this
, som i forrige eksempel på linje 11, når du bruker egenskapen "current_temperature".
Få tilgang til Document Object Model (DOM) med JavaScript
Fra Javascript Du har tilgang til innholdet på nettsiden den kjører på, samt noen aspekter av nettleseren som viser den siden, men ikke til systemressurser. Datastrukturen som støtter egenskapene og metodene du får tilgang til fra Javascript del av vindusobjektet, spesifikt innholdet i objektet (dokumentet HTML) tilsvarer objektet document
. Selv om det noen ganger brukes for klarhet, er det ikke nødvendig å gå foran metodene eller egenskapene med vindu for å referere til dem, det er nok for eksempel å bruke document
, er det ikke nødvendig å skrive navnet på rotobjektet som i window.document
, så lenge det refereres til det gjeldende vinduet.
Den mest brukte formen for finne et objekt i dokumentet HTML Det er gjennom metoden getElementById()
, som IDen som ble angitt da koden ble opprettet, sendes som et argument HTML. Fra det som ble forklart i tidligere avsnitt, er det lett å anta at du også kan få tilgang til komponentene inne i objektet document
ved hjelp av punktsyntaks (document.componente
) eller parentes med både navnet (document["componente"]
), den mest nyttige, for eksempel den numeriske indeksen, vanskelig å bruke og upraktisk når du får tilgang til innholdet på en manuelt komponert nettside.
med Javascript kan få elementet som inneholder et annet element (element eller overordnet node) konsultere din eiendom parentNode
eller eiendommen din parentElement
, forskjellen er at det overordnede elementet (parentElement
) av det siste elementet i strengen DOM Den er null (null
) og overordnet node (parentNode
) er selve dokumentet (document
).
Til endre innholdet i et element HTML, for eksempel etiketten <div>
, Den kan brukes innerHTML
og for å endre egenskapene kan du velge å tilordne den en annen klasse med className
eller endre egenskapene individuelt med style
. Å se stilen som vises av et element på nettsiden er ikke nødvendigvis nyttig style
siden det kan avhenge av flere faktorer eller rett og slett ikke har blitt spesifisert. For å sjekke stilen til et element som til slutt vises på nettsiden, brukes getComputedStyle-metoden.
Til et dokumentelement HTML Det kan tildeles flere klasser for å bestemme utseendet og oppførselen administrere listen over klasser til et objekt fra Javascript du kan ty til classList
som tilbyr metodene add
for å legge til en ny klasse i listen, remove
for å fjerne det, toggle
for å erstatte det eller se innholdet i klasselisten til et element med item
og contains
, som returnerer klassen som har en bestemt plassering i listen og en verdi true
o false
om en bestemt klasse er på listen eller ikke.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var contenedor_temperatura=document.getElementById(“temperatura”); // Encontrar el div con id=”temperatura”
contenedor_temperatura.innerHTML=“”; // Eliminar el contenido provisionalmente para que no se vean los cambios
contenedor.className=“bloque_temperatura”; // Asignar una nueva clase
if(temperatura>20) // Si la temperatura es mayor que 20 °C…
{
contenedor.style.color=“#FF6666”; // …usar el color rojo en lugar del color normal de la clase
}
contenedor_temperatura.innerHTML=“La temperatura es “+temperatura+” °C”; // Cuando el aspecto esté preparado mostrar el valor
if(document[“titulo”].classList.contains(“estilo_titulo”)) // Si el objeto “titulo” tiene la clase “estilo_titulo”…
{
if(document[“titulo”].classList.contains(“general”)) // …y la clase “general”…
{
document[“titulo”].classList.remove(“general”); // …quitar la clase “general”
}
}
else // Si el objeto “titulo” no tiene la clase “estilo_titulo”…
{
document[“titulo”].classList.add(“estilo_titulo”); // …añadir la clase “estilo_titulo” (da igual que tenga o no la clase “general”)
}
|
I forrige eksempel er den plassert med getElementById
objektet du vil manipulere (et element <div>
for hans id
), før du endrer utseendet, slettes innholdet ved å tilordne med innerHTML
en tom tekststreng, den tildeles en ny klasse med className
og stilen er modifisert med style
avhengig av verdien av innholdet (temperatur), endre fargen, hvis aktuelt, gjennom eiendommen color
. Når aspektet er etablert, skrives verdien på nytt innerHTML
.
I den andre delen av eksemplet ovenfor (linje 9 til 19) åpnes et kodeelement HTML ved hjelp av syntaksen document[]
og eiendommen id
av elementet for å endre klasselisten med metoden classList.remove()
og med metodenclassList.add()
, basert på resultatet av flere spørringer som utføres i betingede kjøringer, som de sammenligner med classList.contains()
.
Når skal det referere til et element HTML varias ganger gjennom hele koden Javascript, det er litt mer effektivt å tilordne den til en variabel eller bruk indeksen i stedet for navnet siden ellers metoden du ville brukt Javascript for å få det hver gang ville det kreve å søke etter navnet, noe som forbrukte litt mer tid enn om en variabel ble åpnet.
Til legge til nye objekter i dokumentet HTML, kan de opprettes først med metoden createElement
de document
og senere innlemme dem til resten av elementene på punktet av treet som er nødvendig med appendChild
. For å lage et objekt XML, som gjenstander SVG som vi bruker til å tegne grafen til IoT-sensorene, kan du bruke createElementNS
(NS for navneplass). Som forklart når vi snakker om formatet SVG, navneområdet som tilsvarer det (for gjeldende versjon) er http://www.w3.org/2000/svg
, som skal sendes til createElementNS
som et argument sammen med elementtypen, svg
, i dette tilfellet.
en alternativ til innerHTML
for å legge til tekst som innhold i et dokumentelement HTML er metoden createTextNode()
av objektet document
. Med dette alternativet kan du lage ny tekst (som senere åpnes hvis den er tilordnet en variabel) som er inkorporert i objekttreet med metoden appendChild()
. som alternativ til appendChild()
, som legger til det nye innholdet på slutten av det som allerede eksisterer i noden det er lagt til, kan du bruke metoden insertBefore()
, som legger til et nytt objekt foran et eksisterende. Ha på insertBefore()
i stedet for appendChild()
gir en metode som tjener for eksempel til sortere nye objekter foran eksisterende når et element må være foran et annet (som i en liste) eller dekke eller være dekket i en grafisk struktur der det er elementer nærmere forgrunnen eller bakgrunnen.
Reager på hendelser med JavaScript
Når veien til bruke en nettside som en beholder for IoT-tilkoblede sensorgrafer den ble brukt onload
I merkelappen <body>
for å begynne å tegne grafen. Denne egenskapen, knyttet til kodeobjektene HTML, refererer til Hendelser Javascript. Som allerede forklart, utfører den en funksjon når siden er lastet inn. Selv om det har vært knyttet til koden HTML for å ha det mer i bakhodet, kunne det vært skrevet i koden Javascript som body.onload=dibujar;
være dibujar
navnet på funksjonen som skal startes når nettsiden lastes.
I de nyeste versjonene av Javascript hendelser kan assosieres med funksjoner som bruker addEventListener
med formatet objeto.addEventListener(evento,función);
eller ved å bruke syntaksen objeto.evento=función;
som fungerer også i eldre implementeringer. For å koble fra funksjonen knyttet til arrangementet, har du removeEventListener
som har samme format som addEventListener
.
Javascript Den er i stand til å reagere på en rekke hendelser som kan oppstå på en nettside. Den kan for eksempel oppdage når et element klikkes HTML med onmousedown
, eller når du klikker med onclick
, når en tast trykkes med onkeydown
, ved å betjene rullefeltet med onscroll
. For vårt formål er det nok for oss oppdage sideinnlasting med onload
og endre størrelsen med onresize
. Vi vil knytte disse hendelsene til objektene body
y window
den DOM hhv. Den første kan tildeles i koden HTML, som sett og den andre i koden Javascript inne i funksjonen kalt av den første og med formatet window.onresize=redimensionar;
være redimensionar
funksjonen som blir kalt hver gang vinduet endrer størrelse.
Løp etter et tidsintervall
Javascript har to ressurser for utsatt utførelse: setTimeout
, som utfører en funksjon etter et tidsintervall og setInterval
som vil utføre en funksjon hvert bestemt tidsintervall. Begge metodene krever som parametere (1) den påkalte funksjonen og (2) tidsintervallet uttrykt i millisekunder. For å stoppe operasjonen kan du tilordne resultatet som returneres av disse funksjonene til variabler og sende dem som et argument til clearTimeout
oa clearInterval
når du ikke vil påkalle dem igjen (eller når du ikke vil at de skal utføres for første gang) setTimeout
o setInterval
henholdsvis.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
var cuenta_atras=setTimeout(descansar,1000*60*20); // Recordar que hay que descansar cuando pasen 20 minutos
var repeticion=setInterval(consultar_correo,1000*60*5); // Revisar el correo cada 5 minutos
function descansar()
{
alert(“Puedes descansar un rato”); // Esto solamente aparecerá una vez
}
function consultar_correo()
{
alert(“Revisa el correo electrónico”); // Esto aparecerá cada cinco minutos
}
function detener_cuenta_atras() // No utiliza argumentos sino la variable global
{
clearTimeout(cuenta_atras); // Detener la cuenta atrás para avisar a los 20 minutos
}
function no_avisar_lectura_correo() // No utiliza argumentos sino la variable global
{
clearInterval(repeticion); // Dejar de avisar cada 5 minutos
}
|
I forrige eksempel er metoden introdusert alert
som tjener til å vise et varselskilt. Selv om det ble mye brukt tidligere, er det for tiden nesten utestengt fra koden Javascript på grunn av hvor aggressivt (påtrengende) det er å dekke nettsiden med en dialogboks.
I et program skrevet for en mikrokontroller av en liten serie (som den på tallerkenen Arduino Uno) er det vanlig å bruke globale variabler, som i forrige eksempel i Javascript, siden koden er kort og ikke spesielt forvirrende, fordi funksjonene mange ganger implementeres ad hoc og fordi bruken av globale variabler gjør at minnebruken kan forutsies på en veldig enkel og intuitiv måte, noe som er kritisk i systemer med få ressurser. I stedet, en Javascript Det er vanlig å redusere bruken av globale variabler til et minimum. fordi den ikke trenger å forhaste minnebruken, siden den kjører normalt på en prosessor med ressurser som er langt overlegne en MCU, fordi det sannsynligvis vil eksistere sammen med mye tredjepartskode som det må fungere med uten å forstyrre, og siden det er et åpent system, kan den fremtidige utførelseskonteksten ikke forutsies (programmet til en mikrokontroller small bestemmer driften fullstendig uten å legge til mer kode når den er i drift) og fordi dimensjonene til applikasjonene kan gjøre lesing vanskelig hvis koden ikke innkapsler driften, noe som gjør metodene så selvstendige som mulig.
Matematiske operasjoner med JavaScript Math-objektet
De matematiske operasjonene til mer kompliserte matematiske beregninger er gruppert i objektet Math
. Dette objektet brukes direkte, det er ikke nødvendig å instansiere det for å bruke metodene eller egenskapene (konstantene) det inkorporerer.
Math.abs(n)
Absolutt verdi av parameter nMath.acos(n)
Arccosine av parameter n (resultat i radianer)Math.asin(n)
Arcsinus til parameter n (resultat i radianer)Math.atan(n)
Arktangens av parameter n (resultat i radianer)Math.atan2(n,m)
Arktangens på n/m (resultat i radianer)Math.ceil(n)
Avrund parameteren til nærmeste heltall oppMath.cos(α)
Cosinus til parameter α (α i radianer)Math.E
e-nummer (≃2.718281828459045)Math.exp(n)
e hevet til parameteren n: enMath.floor(n)
Avrund parameter n til nærmeste heltall nedoverMath.log(n)
Naturlig logaritme (grunnlag e) av parameter nMath.LN2
Naturlig logaritme (grunnlag e) på 2 (≃0.6931471805599453)Math.LN10
Naturlig logaritme (grunnlag e) på 10 (≃2.302585092994046)Math.LOG2E
Base 2-logaritme av e (≃1.4426950408889634)Math.LOG10E
Base 10-logaritme av e (≃0.4342944819032518)Math.max(a,b,c,…)
Største verdien av listen over parametere som er beståttMath.min(a,b,c,…)
Minste verdi av listen over parametere som er beståttMath.PI
Nummer π (≃3.141592653589793)Math.pow(n,m)
Første parameter n hevet til den andre parameteren m: nmMath.random()
(Nesten) tilfeldig tall mellom 0.0 og 1.0Math.round(n)
Avrund parameter n til nærmeste heltallMath.sin(α)
Sinus til parameter α (α i radianer)Math.sqrt(n)
Kvadratroten av parameter nMath.SQRT1_2
Kvadratrot av 1/2 (≃0.7071067811865476)Math.SQRT2
Kvadratroten av 2 (≃1.4142135623730951)Math.tan(α)
Tangent til parameter α (α i radianer)
Last inn data fra server med AJAX
Metoden som følges for å tegne informasjonen som er lagret i IoT består av å laste dataene fra serveren fra tid til annen og tegne om grafen som de er representert med. For å lese data fra serveren brukes teknologi AJAX (asynkron JavaScript og XML) gjennom en gjenstand XMLHttpRequest
de Javascript. Plottet datagrafen gjøres ved å gjenbruke et objekt SVG som allerede er i koden HTML og som inneholder et plott hvis koordinater er modifisert for å få dem til å samsvare med de nye dataene som er lastet.
I eksemplet med dette forslaget, i tillegg til å oppdatere tegningen, oppdateres også en tekst på nettsiden som viser dato og verdi av siste målte data for hver graf.
På serversiden er det en database som inneholder informasjonen at sensorene koblet til IoT har overvåket. Denne databasen leses av objektforespørselen XMLHttpRequest
svarer med informasjon kodet i JSON-format, selv om navnet på metoden som brukes antyder et forhold til formatet XML.
I den første polaridad.es-opplæringen om IoT-datalagring Du kan se et eksempel på en infrastruktur for å administrere, fra serversiden, informasjonen som leveres av enheter koblet til tingenes internett. I denne artikkelserien brukes en server som en ressurs Apache hvorfra du kan bruke programmeringsspråket PHP for å få tilgang til en database MySQL o mariadb. På servere som brukes til å støtte IoT er det svært vanlig å finne databaser MongoDB (NoSQL) og programmeringsspråket Javascript på node.js som programvareinfrastruktur.
Den neste funksjonen er ansvarlig for å be om de siste dataene fra en av sensorene fra serveren. I funksjonskallet brukes objektet som et argument Javascript som støtter dataene som er tegnet. Hvis den samme grafen representerer flere verdier, for eksempel for å visuelt søke etter en korrelasjon, kan en forespørsel sendes til serveren om å returnere flere samtidig, en mer optimal metode på grunn av måten serveren fungerer på. HTTP-protokoll.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
function consultar_ultimo_valor_sensores(objeto_grafico)
{
var consulta=‘zona=”+objeto_grafico.sufijo_nombre;
var pagina=“ultimo_valor_sensor.php”;
var resultado;
var ajax;
if(window.XMLHttpRequest)
{
ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest);
}
else // Versiones antiguas de MS Internet Explorer
{
ajax=new ActiveXObject(“Microsoft.XMLHTTP”);
}
ajax.onreadystatechange=
function()
{
if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”)
{
resultado=JSON.parse(ajax.responseText);
if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1])
{
// Normalmente se gestionará la respuesta utilizando el objeto
redibujar_grafico(objeto_grafico,resultado);
// Si los datos son sencillos y tienen una estructura clara puede ser más práctico usarla directamente
// redibujar_grafico(objeto_grafico,[resultado.fecha,resultado.temperatura]);
}
}
}
ajax.open(“POST”,pagina);
ajax.setRequestHeader(“Method”,“POST “+pagina+” HTTP/1.1″);
ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
ajax.setRequestHeader(“Content-length”,consulta.length);
ajax.setRequestHeader(“Connection”,“close”);
ajax.send(consulta);
}
|
I den tredje linjen i forrige eksempel er spørringen som vil bli gjort til serveren forberedt, der argumentet "sone" vil bli sendt, hvis verdi vil være navnet eller koden til det overvåkede stedet siden informasjon om område kan eksistere side om side i samme database forskjellige sensorer (for eksempel termometre som måler temperaturen i forskjellige rom). Parameteren som sendes til forrige funksjon, objektet med kartdataene, forventes å inkludere en egenskap med navnet på rommet ("navn_suffiks").
Mellom linje 7 og 14 i forrige kode, vises objekt XMLHttpRequest
som er lagret i variabelen "ajax". Før du velger hvordan du skal lage objektet, søker du window
hvis XMLHttpRequest
var ikke tilgjengelig (noe som skjedde i gamle versjoner av Microsofts utforsker, og selv om det er langt bak, fungerer det som et eksempel på alternativer for å lage objektet ved å bruke den (mer native) syntaksen. Object.create
o new
, som ligner på andre objektorienterte språk.
For å kunne administrere svaret umiddelbart, klargjøres koden som håndterer den på linje 15 til 26 før forespørselen sendes til serveren.
Måten å utføre spørringen HTTP til serveren består av åpne en forbindelse med open
angir type og side (valgfritt brukernavn og passord), klargjør overskriftene av protokollen med setRequestHeader
y send forespørselen med send
. Overskriften HTTP Content-length
du må vite lengden på spørringen (antall tegn) som beregnes ved hjelp av length
.
Når forespørselen AJAX er klar, utføres funksjonen knyttet til hendelsen onreadystatechange
. I stedet for å tilordne en funksjon, er det i det forrige eksemplet definert en anonym funksjon som skal administrere mottak av data som kommer fra serveren. Først av alt, på linje 18, blir det verifisert at statusen til forespørselen er "ferdig", som tilsvarer verdien 4
av eiendommen readyState
, at statusen er "OK" for HTTP-protokoll (kode 200
) som kan fås fra eiendommen status
og at dataene som har kommet er JSON-format, konsultere eiendommen responseType
.
Når det er bekreftet at statusen til svaret er som forventet, i linje 20 i forrige eksempel oppretter et objekt med resultatet, og konverterer teksten JSON. Svaret sørger for at en dato skal returneres, dette lar oss se om resultatet som serveren sender allerede tidligere har vært representert i grafen, som er verifisert på linje 21. Hvis dataene er nye, på linje 23 Funksjonen som er ansvarlig for å tegne grafen på nytt med den nye informasjonen kalles.
Tanken med å foreslå denne lesemetoden er at dataene vil bli oppdatert svært ofte. Hvis informasjonen som presenteres tilsvarer en lang sikt (som temperaturen på en dag eller en uke), kan en første forespørsel implementeres som samler inn alle tilgjengelige data og deretter en, lik den i eksempelet, som oppdaterer den i periodekorrespondenten.
Generer tilfeldige data for testing
Når all server- og klientinfrastruktur er klar, vil en funksjon som den i forrige seksjon ha ansvaret for å lese dataene og tegne grafen med den, men I testfasen kan det være mer praktisk å bruke tilfeldige tall innenfor et kontrollert område for å se om koden som skrives er riktig. Følgende funksjon kan tjene som et eksempel for å innhente data mens du bygger den endelige applikasjonen.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
function consultar_ultimo_valor_sensores
(
objeto_grafico,// Objeto dentro del elemento SVG que representa el trazado
valor_maximo, // Valor máximo
valor_minimo, // Valor mínimo
margen_valor // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
)
{
// La forma más genérica es usar objetos
/*
var nuevo_valor_inventado=
{
fecha:Math.round(Date.now()+Math.random()*1000),
temperatura:Math.random()*Math.abs(valor_maximo-valor_minimo)+valor_minimo
};
*/
// En este caso concreto, como son pocos datos y con una estructura concreta es más práctico usar un vector
var nuevo_valor_inventado=
[
Math.round(Date.now()+Math.random()*1000),
Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo
];
redibujar_grafico(objeto_grafico,nuevo_valor_inventado);
}
|
I stedet for å lese informasjonen fra en database, genererer eksemplet ovenfor dem tilfeldig og sender dem til funksjonen som har ansvaret for å tegne grafen. De oppfunne dataene er en vektor dannet av en dato uttrykt som en verdi i millisekunder, øyeblikket for registrering av sensorinformasjonen og de overvåkede dataene, som er mellom en maksimumsverdi og en minimumsverdi.
I dette eksemplet, når du genererer en dato, kan den forsinkes opptil ett sekund (1000 millisekunder) i forhold til datoen på oppfinnelsestidspunktet. Som Math.random()
genererer et tall mellom 0.0 og 1.0, multipliser det med 1000 gir et tall mellom 0 og 1000 som deretter konverteres til et heltall. På samme måte oppnås verdien ved å multiplisere det tilfeldige tallet med området (maksimum minus minimum) og legge til minimum.
Tegn grafen for IoT-sensorer med et SVG-plott
Siden vi har sett hvordan vi kan oppnå verdiene som vi ønsker å representere (temperatur, i eksemplet) og deres tidsmessige plassering, som kan uttrykkes sammen i form av koordinater, viser eksemplet nedenfor en funksjon for å tegne en bane som forbinder disse punktene og eventuelt et farget område avgrenset av den linjen øverst. Resultatet blir som det følgende bildet.
Den horisontale aksen (X) i grafen representerer tid og den vertikale aksen (Y) verdiene som sensorene koblet til IoT har overvåket. Det horisontale intervallet er noen få sekunder siden i dette forslaget oppdateres grafen svært ofte (for eksempel hvert sekund) for å gi nesten sanntidsinformasjon om tilstanden til sensorene.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
function actualizar_grafico
(
grafico, // Objeto SVG con el que se dibuja la gráfico
coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo
tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos)
valor_maximo, // Valor máximo aceptable antes de emitir una alarma
valor_minimo, // Valor mínimo aceptable antes de emitir una alarma
margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false)
parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0)
parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0)
)
{
var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false)
var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0)
var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0)
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico
var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
if(cerrado) // Si se dibuja un path (trazado) cerrado…
{
coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo
contador_valor—; // Pasar al siguiente valor
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
}
|
I den forrige koden er det to interessante aspekter, for det første beregningen som tillater tilpasse utvalget av verdier som er representert og for det andre eiendomsbygging d
som indikerer koordinatene til punktene på oppsettet (path
).
For å tilpasse rekkevidden av verdier som er representert, flyttes de fra et minimum og skaleres slik at den synlige størrelsen tilsvarer størrelsen på grafen. Når det gjelder tid, oppnås forskyvningen ved å trekke intervallet du ønsker å vise fra den lengste tiden (datoen og klokkeslettet nærmest den gjeldende) (20 sekunder i eksemplet). Forskyvningen av temperaturverdiene er den for det nedre området (en grad) minus den laveste verdien, slik at dataene nedenfor er mest lik den laveste verdien som er tillatt, men etterlater en margin som lar oss sette pris på hva som er bestått
Koeffisienten som multipliserer tidsverdiene for å oppnå de horisontale koordinatene til grafen oppnås ved å dele den totale bredden på grafen (100 enheter i eksemplet) med tidsområdet representert (20 sekunder i eksemplet). For å få koeffisienten med skalartemperaturverdiene, må det huskes at området som er representert går fra en margin under minimumsverdien til en margin over maksimum, en grad i begge tilfeller. På denne måten resulterer den vertikale skala-koeffisienten fra å dele høyden på grafen (100 enheter i eksemplet) med maksimumsverdien, minus minimum pluss øvre og nedre margin. Siden disse verdiene kan utvikle seg helt ved negative temperaturer, bruker vi Math.abs()
å bruke den absolutte verdien av differansen.
Eiendommen d
av objektet path
Den er konstruert ved å sette sammen koordinatene til punktene i en tekst. Hvert par koordinater er innledet av en kode SVG L
, som trekker en linje fra gjeldende posisjon til en absolutt verdi som er indikert av koordinatene. X- og Y-verdiene er atskilt med komma og hver operasjon SVG er atskilt med et mellomrom fra neste.
For å starte oppsettet, bruk koden M
(flytt til en absolutt koordinat). Ved det lukkede og fylte plottet starter man nederst til høyre, ved det åpne plottet som tegner dataprofilen starter man med den siste verdien som er representert (den nyeste). For å fullføre det lukkede oppsettet, brukes koden Z
legge til som siste punkt det som har samme X-koordinatverdi som det siste punktet på linjen og som Y-koordinat den minste verdien som er representert.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
function dibujar_grafico()
{
var tiempo_mostrado=20000; // Se representan 20 segundos (20000 milisegundos)
var valor_maximo=10; // 10 grados sobre cero
var valor_minimo=–5; // Cinco grados bajo cero
var fecha_hora=Date.now(); // Hora actual
var matriz_de_coordenadas_de_prueba=[]; // Preparar el vector de coordenadas
for(var contador=0;contador<20;contador++)
{
fecha_hora+=500+1500*Math.random(); // Añadir medio segundo a la hora anterior y entre 0 y segundo y medio aleatoriamente
matriz_de_coordenadas_de_prueba[contador]=[]; // Preparar el siguiente punto del vector de coordenadas
matriz_de_coordenadas_de_prueba[contador][0]=fecha_hora; // En la coordenada horizontal, situar la hora
matriz_de_coordenadas_de_prueba[contador][1]=Math.random()*Math.abs(valor_maximo–valor_minimo)+valor_minimo; // En la coordenada vertical situar el valor del sensor
}
actualizar_grafico
(
document.getElementById(“relleno_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
true // Cerrar el trazado para representar el área rellena
);
actualizar_grafico
(
document.getElementById(“linea_temperatura”), // Trazado para el relleno definido en el código HTML
matriz_de_coordenadas_de_prueba,
tiempo_mostrado,
valor_maximo, // Diez grados de valor máximo
valor_minimo, // Cinco grados bajo cero como valor mínimo
1, // Un grado por encima y por debajo de las temperatura mínimas y máximas respectivamente
false // No cerrar el trazado para representar la linea que une los puntos que representan las temperaturas
);
}
|
I dette eksemplet er funksjonen dibujar_grafico()
, som er anropet ved sideinnlasting, får de innledende verdiene som skal testes (ikke den siste sanntidsverdien) og forbereder området der dataene skal gjengis: 20 sekunder (20000 15 ms) horisontalt og 5 °C i vertikal fra -10°C til +XNUMX°C med én grads topp- og bunnmargin. Foreta to anrop til actualizar_grafico()
, i første pass true
som et argument, som indikerer at diagrammet skal være lukket for å representere et fylt område, og ved den andre samtalen passerer det false
å trekke linjen. I hvert tilfelle objektet path
modifisert er den som har tilsvarende utseende, med en fylling og ingen kant i det første tilfellet og med en viss linjetykkelse og ingen fylling i det andre.
Funksjonen actualizar_grafico()
arbeid på en gjenstand SVG som bruker følgende kode som en beholder HTML. Objektet SVG inneholder to baner, en for å tegne linjen og en annen for å tegne det fylte området. Når du laster inn nettsiden, fra elementet <body>
den forrige funksjonen kalles automatisk, dibujar_grafico()
takket være arrangementet Javascript onload
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Temperatura</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script>
</head>
<body onload=“dibujar_grafico();” style=“margin:0;”> <!– Cuerpo del documento HTML. Al cargar el contenido llama a la función JavaScript dibujar_grafico() –>
<div id=“temperatura”>
<div id=“bloque_temperatura” style=“width:820px;height:150px”>
<svg
id=“grafico_temperatura”
width=“100%”
height=“100%”
viewBox=“0 0 100 100”
preserveAspectRatio=“none”>
<path
id=“relleno_temperatura”
d=“”
style=“fill:#A8C3EA;stroke:none;”
vector-effect=“non-scaling-stroke”
/>
<path
id=“linea_temperatura”
d=“”
style=“fill:none;stroke:#205587;stroke-width:4;stroke-opacity:1;”
vector-effect=“non-scaling-stroke”
/>
</svg>
</div>
</div>
</body>
</html>
|
På linje 10 i koden HTML ovenfor er en bredde (som et eksempel) på 820 px og en høyde på 150 px etablert i stilen (noe som i den endelige versjonen vil være tilrådelig å gjøre med en klasse og et dokument CSS). Det virker merkelig at linje 13 og 14 definerer størrelsen på objektet SVG som 100 % bredde og høyde (som passer best med vindusmålene, 100×100). Som allerede nevnt, er grunnen til å gjøre dette å alltid jobbe med kjente dimensjoner og justere de representerte verdiene til det. De andre alternativene vil være å beregne plassen til grafen hver gang og deretter justere verdiene eller kraftfaste dimensjoner for grafen, som dokumentet må følge.
Etter å ha valgt en graf hvis dimensjoner endres i henhold til koden HTML, er det nødvendig å inkludere eiendommen vector-effect
med motet non-scaling-stroke
for å hindre at linjetykkelser blir deformert når grafen ikke opprettholder de valgte 1:1-proporsjonene på nettsiden den vises på, slik det var i forrige forslag.
For å "beskjære" grafen og vise kun området du velger, bruk viewBox
. I dette tilfellet har vi valgt å se den delen av grafen som starter på 0,0 (øvre venstre hjørne) og måler 100x100 ned og til høyre. Den delen av tegningen som ligger i koordinater med negative verdier eller større enn 100 vil ikke vises på nettsiden selv om de finnes i objektet SVG
Legg til nye elementer i SVG-tegning
I forrige eksempel, funksjonen actualizar_grafico()
bruke et oppsett SVG som eierskapet endres til d
, som er det som uttrykker koordinatkjeden. Alternativet ville være å lage hele objektet hver gang det tegnes på nytt. Fordelen med det første alternativet er at det grafiske aspektet (som tykkelse eller farge) er definert i koden HTML, er begrensningen at objektene må være opprettet på forhånd.
For å lage SVG-objekter, bruk createElementNS()
, som tillater inkludert navneområdet. I eksemplet nedenfor opprettes et nytt tekstobjekt (text
) og er knyttet til et element SVG som allerede finnes i koden HTML av nettstedet. Når det nye elementet er opprettet, blir dets egenskaper tildelt setAttribute()
og legges til SVG med appendChild()
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function
rotular
(
objeto_grafico,
texto,
inicio=[0,0],
altura=10.0,
tipo_letra=“SircuitoRegularMedium”,
color_texto=“#000000”,
color_fondo=“#FFFFFF”
)
{
nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,“text”);
nuevo_objeto_svg.setAttribute(“x”,inicio[0]);
nuevo_objeto_svg.setAttribute(“y”,inicio[1]);
nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra);
nuevo_objeto_svg.setAttribute(“font-size”,altura);
nuevo_objeto_svg.setAttribute(“fill”,color_texto);
nuevo_objeto_svg.textContent=texto;
objeto_grafico.appendChild(nuevo_objeto_svg);
}
//rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]);
|
Endre andelen tegningselementer
Hvis du har prøvd å merke med funksjonen i eksempelet i forrige avsnitt, vil du ha sett at teksten fremstår som deformert når andelen av objektet på nettsiden (width
y height
Av kode HTML) er ikke lik arealet som er representert (viewBox
). For å tilpasse andelen er det nødvendig å kjenne til målene til objektet SVG som du kan se stilen til objektet eller beholderen for HTML, hvis objektet SVG overføre denne eiendommen. Tildeling av eierskap transform
til gjenstander SVG som avhenger av andelen, kan deformasjonen korrigeres ved å bruke en skaleringsoperasjon scale()
der koeffisienten i X er forskjellig fra den i Y.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
function
rotular
(
objeto_grafico,
texto,
inicio=[0,0],
altura=10.0,
proporcion=1.0,
tipo_letra=“SircuitoRegularMedium”,
color_texto=“#000000”,
color_fondo=“#FFFFFF”
)
{
var escala_horizontal=parseFloat(getComputedStyle(objeto_grafico).height)/parseFloat(getComputedStyle(objeto_grafico).width);
nuevo_objeto_svg=document.createElementNS(“http://www.w3.org/2000/svg”,‘text’);
nuevo_objeto_svg.setAttribute(“transform”,“scale(“+escala_horizontal+“,1.0)”); // scale permite cambiar la escala en X e Y
//nuevo_objeto_svg.setAttribute(“transform”,”scaleX(“+escala_horizontal+”)”); // Como se sabe que sólo cambia la escala en X, se puede usar scaleX
nuevo_objeto_svg.setAttribute(“x”,inicio[0]);
nuevo_objeto_svg.setAttribute(“y”,inicio[1]);
nuevo_objeto_svg.setAttribute(“font-family”,tipo_letra);
nuevo_objeto_svg.setAttribute(“font-size”,altura);
nuevo_objeto_svg.setAttribute(“fill”,color_texto);
nuevo_objeto_svg.textContent=texto;
objeto_grafico.appendChild(nuevo_objeto_svg);
}
//rotular(document.getElementById(“cosa_svg”),”HOLA”,[10,10]);
|
SVG lar flere objekter grupperes og danner et nytt sammensatt element som også støtter egenskaper, som enkle gjenstander. For å bruke den samme transformasjonen på en serie objekter samtidig i stedet for hvert objekt separat, kan du gruppere dem i henhold til denne ressursen og bruke en enkelt egenskap transform
til dem alle.
Som forklart når du snakker om SVG-format, er elementene i en gruppe omsluttet av etikettene <g>
y </g>
. Å legge til fra Javascript elementer til en gruppe SVG brukes, som vist i forrige eksempel, appendChild()
når det nye objektet er definert.
For å etablere et opphav ved bruk av transformasjoner, kan egenskapen brukes på objekter SVG transform-origin
, hvis verdi er X- og Y-koordinatene til punktet der transformasjonen begynner. Hvis en verdi for opprinnelsen til transformasjonen ikke er uttrykkelig angitt (i nettleseren), brukes koordinatene. Dessverre, i skrivende stund, er det ikke homogent på tvers av nettlesere å spesifisere oppførselen til transformasjoner ved å bruke en annen kilde enn standardkilden og bør brukes med forsiktighet.
Sammen med skalaen transformasjon med scale
Det er andre, for eksempel rotasjon med rotation
og bevegelsen med translate
, som tilbyr en alternativ til grafrepresentasjon: i stedet for å få nye koordinater, kan du representere dem i deres eget rom og transformere grafen slik at den passer til formatet du vil representere dem i.
Legg til referanser til diagrammet
Nå som hoveddelen av grafen er løst ved å plotte verdiene med en profil og et fylt område, kan den kompletteres med referanser som hjelper lesingen. Som et eksempel, la oss starte med å tegne noen horisontale referanser (linjer) som markerer maksimum og minimum akseptable verdier samt en ønsket verdi. Som forklart kan du velge å legge til objektene i SVG rett fra Javascript eller inkludere dem manuelt i koden HTML og endre dem senere med Javascript.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT
var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS)
var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_DESEADO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
var MARGEN_VALOR=1.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
function inicializar_grafico()
{
CONTENEDOR_SVG=document.getElementById(“contenedor_svg”);
crear_referencia_horizontal_svg(VALOR_MAXIMO,“#FF0000”);
crear_referencia_horizontal_svg(VALOR_DESEADO,“#00FF00”);
crear_referencia_horizontal_svg(VALOR_MINIMO,“#0000FF”);
}
function crear_referencia_horizontal_svg
(
altura=0.0,
color=“#000000”,
grosor=0.5,
opacidad=1.0
)
{
var altura_corregida=ALTO_CAJA–(altura+MARGEN_VALOR–VALOR_MINIMO)*ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2);
var referencia_horizontal=document.createElementNS(NS,‘line’);
referencia_horizontal.setAttribute(“x1”,0.0);
referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA);
referencia_horizontal.setAttribute(“y1”,altura_corregida);
referencia_horizontal.setAttribute(“y2”,altura_corregida);
referencia_horizontal.style.stroke=color;
referencia_horizontal.style.strokeWidth=grosor;
referencia_horizontal.style.strokeOpacity=opacidad;
CONTENEDOR_SVG.appendChild(referencia_horizontal);
}
//inicializar_grafico();
|
Det virker logisk å merke disse horisontale referansene med tekst som tydeliggjør verdien de representerer. For å fremheve teksten kan du bruke rektangler som vil skille seg ut fra bakgrunnen og grafikken. Siden tekstene vil måtte skaleres for å kompensere for deformasjonen, kan de alle grupperes i et objekt som skalaen skal brukes på; Den største fordelen med å gjøre det på denne måten er å kunne endre dem i en enkelt operasjon hvis grafbeholderen (nettleservinduet) endres og endrer den andelen som skalaen korrigerer.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
// Este código de ejemplo usa constantes para hacerlo más legible a desarrolladores de aplicaciones para microcontroladores de series pequeñas. La opción más recomendable, y más propia del estilo JavaScript, es crear un objeto cuyas propiedades serían las constantes y que incluiría los métodos (aquí funciones) que generan o modifican el gráfico o en este caso las referencias
var CONTENEDOR_SVG; // Objeto SVG que contiene el gráfico que representa los valores monitorizados por los sensores en la IoT
var NS=“http://www.w3.org/2000/svg”; // Nombre del espacio de nombres (name space NS)
var ANCHO_CAJA=100.0; // Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var ALTO_CAJA=100.0; // Alto del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones
var TIEMPO_REPRESENTADO=30000; // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado)
var VALOR_MAXIMO=10.0; // Mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_MINIMO=–5.0; // Menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
var VALOR_OPTIMO=5.0; // Mejor valor del parámetro medido (temperatura). Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
var MARGEN_VALOR=2.0; // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
var desplazamiento_valor=desplazamiento(Date.now());
var escala_valor=escala();
var TIPOGRAFIA=“SircuitoRegularMedium”; // Tipografía con la que se rotula todo el gráfico (se usa una constante buscando la uniformidad, pero se puede rotular usando diferentes tipos de letra si es necesario)
var ALTURA_TEXTO=10.0; // Altura de los textos en valor absoluto (píxeles)
var COLOR_MINIMO=“#621D87”; // Color de la referencia que indica el valor mínimo
var COLOR_OPTIMO=“#1D8762”; // Color de la referencia que indica el valor máximo
var COLOR_MAXIMO=“#871E35”; // Color de la referencia que indica el valor óptimo
var COLOR_TIPOGRAFIA=“#A8C3EA”; // Color del tipo de letra con que se rotulan las referencias
var GROSOR_REFERENCIA=0.5; // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles)
var OPACIDAD_REFERENCIA=1.0; // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella
var RELLENO_FONDO_REFERENCIA=4.0; // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
var MARGEN_FONDO_REFERENCIA=10.0; // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
var ANCHO_FONDO_REFERENCIA=34.0; // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto
function proporcion_grafico()
{
return parseFloat(getComputedStyle(CONTENEDOR_SVG).height)/parseFloat(getComputedStyle(CONTENEDOR_SVG).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
}
function medida_grafico()
{
var proporcion=[]; // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web // Coeficiente que calcula la medida absoluta en píxeles en función del ancho/alto base del gráfico y de la representación en la página web
proporcion[0]=ANCHO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).width);
proporcion[1]=ALTO_CAJA/parseFloat(getComputedStyle(CONTENEDOR_SVG).height);
return proporcion;
}
function desplazamiento(valor_mayor)
{
var desplazamiento_valor=[];
desplazamiento_valor[0]=valor_mayor–TIEMPO_REPRESENTADO; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento_valor[1]=MARGEN_VALOR–VALOR_MINIMO; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
return desplazamiento_valor;
}
function escala()
{
var escala_valor=[];
escala_valor[0]=ANCHO_CAJA/TIEMPO_REPRESENTADO; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala_valor[1]=ALTO_CAJA/(Math.abs(VALOR_MAXIMO–VALOR_MINIMO)+MARGEN_VALOR*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
return escala_valor;
}
function crear_referencia_horizontal_svg
(
posicion=0.0,
color_dibujo=“#000000”,
grosor_linea=GROSOR_REFERENCIA,
opacidad_linea=OPACIDAD_REFERENCIA,
color_texto=COLOR_TIPOGRAFIA,
altura_texto=ALTURA_TEXTO
)
{
var proporcion_horizontal=proporcion_grafico(); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
var coeficiente_medida=medida_grafico();
var posicion_corregida=ALTO_CAJA–(posicion+desplazamiento_valor[1])*escala_valor[1];
var referencia_horizontal=document.createElementNS(NS,‘line’); // El orden en el que se crean los objetos determina qué tapa (lo último) y que es tapado (lo primero)
var fondo_referencia=document.createElementNS(NS,‘rect’); // El rectángulo (opaco) se creará después de la línea (que puede ser un poco transparente) para definir con claridad el fondo del texto
var texto_referencia=document.createElementNS(NS,‘text’); // El texto se creará en último lugar para que quede sobre los otros objetos
referencia_horizontal.setAttribute(“x1”,0.0);
referencia_horizontal.setAttribute(“x2”,ANCHO_CAJA);
referencia_horizontal.setAttribute(“y1”,posicion_corregida);
referencia_horizontal.setAttribute(“y2”,posicion_corregida);
referencia_horizontal.style.stroke=color_dibujo;
referencia_horizontal.style.strokeWidth=grosor_linea*coeficiente_medida[1];
referencia_horizontal.style.strokeOpacity=opacidad_linea;
CONTENEDOR_SVG.appendChild(referencia_horizontal); // Añadir la línea de referencia lo más abajo
fondo_referencia.setAttribute(“x”,MARGEN_FONDO_REFERENCIA*coeficiente_medida[0]);
fondo_referencia.setAttribute(“y”,posicion_corregida–(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]/2.0);
fondo_referencia.setAttribute(“width”,ANCHO_FONDO_REFERENCIA*coeficiente_medida[0]);
fondo_referencia.setAttribute(“height”,(ALTURA_TEXTO+RELLENO_FONDO_REFERENCIA*2.0)*coeficiente_medida[1]);
fondo_referencia.style.fill=color_dibujo;
fondo_referencia.style.fillOpacity=1.0;
fondo_referencia.style.strokeWidth=0;
CONTENEDOR_SVG.appendChild(fondo_referencia);
texto_referencia.setAttribute(“x”,(MARGEN_FONDO_REFERENCIA+RELLENO_FONDO_REFERENCIA)*coeficiente_medida[0]/proporcion_horizontal);
texto_referencia.setAttribute(“y”,posicion_corregida+ALTURA_TEXTO/2.0*coeficiente_medida[1]);
texto_referencia.setAttribute(“font-family”,TIPOGRAFIA);
texto_referencia.setAttribute(“font-size”,ALTURA_TEXTO*coeficiente_medida[1]);
texto_referencia.setAttribute(“fill”,COLOR_TIPOGRAFIA);
texto_referencia.setAttribute(“transform”,“scale(“+proporcion_horizontal+“,1.0)”);
texto_referencia.textContent=(posicion>=0?“+”:“”)+posicion;
CONTENEDOR_SVG.appendChild(texto_referencia);
}
function inicializar_grafico()
{
CONTENEDOR_SVG=document.getElementById(“contenedor_svg”);
crear_referencia_horizontal_svg(VALOR_MAXIMO,COLOR_MAXIMO);
crear_referencia_horizontal_svg(VALOR_OPTIMO,COLOR_OPTIMO);
crear_referencia_horizontal_svg(VALOR_MINIMO,COLOR_MINIMO);
}
//inicializar_grafico();
|
Det er flere interessante aspekter i eksempelkoden ovenfor. Kommenter først og fremst at konstanter (globale variabler) har blitt brukt for å gjøre eksemplet mer lesbart for brukere som kommer fra programmering. mikrokontrollere en C o en C + +. Som vil bli sett senere, den optimale måten å programmere den inn på Javascript Det ville være å bruke objekter som ville inneholde disse verdiene og metodene som ville administrere referansene i dette eksemplet eller grafen generelt i et produksjonssystem.
På den annen side, for å fremme hva den mer generiske koden ville være, har det blitt utviklet separate funksjoner som beregner de forskjellige koeffisientene som korrigerer andelen av grafen for å justere teksten proporcion_grafico()
, skalaen til verdiene avhengig av deres rekkevidde escala()
og en korreksjonsfaktor for målinger som er kjent i absolutt verdi, slik som målinger i referanser medida_grafico()
.
Å lese denne koden bør bidra til å avklare konteksten et program som dette fungerer i, som tegner grafikk i sanntid og må være fleksibelt for å kunne presenteres i ulike grafiske sammenhenger (i det minste forskjellige størrelser og proporsjoner). Først av alt må objektene genereres SVG, enten "manuelt" i koden HTML, enten gjennom kode Javascript og i alle fall må referanser til disse objektene senere innhentes for å manipulere dem fra Javascript slik at nye grafer kan tegnes og representasjonen av en allerede tegnet graf kan tilpasses en endring i mediet den presenteres i.
En annen referanse som kan hjelpe enkelt å tolke en graf er punktene som representerer spesifikke verdier (nodene på linjen). I dette eksemplet, der vi representerer en enkelt størrelse, er valget av et symbol ikke kritisk, men hvis flere forskjellige verdier er lagt over hverandre for å se etter korrelasjon, er det interessant å skille, i tillegg til å bruke andre ressurser som farge , ved å tegne forskjellige symboler. Grafikken som brukes til linjenoden må endres i størrelse og proporsjon, slik det for eksempel skjer med tekster, slik at dens dimensjoner er absolutte og slik at proporsjonene opprettholdes selv om de i boksen den inneholder endrer grafikken.
I forrige eksempel så vi allerede hvordan man beregner de forskjellige koeffisientene for å omskalere og korrigere andelen av tegningen; Når det gjelder hvordan man implementerer styringen av symbolene til nodene eller toppunktene i grafen, kan en mulig løsning være å lagre objektene SVG inn i en vektor og endre dens posisjon når grafen oppdateres ved å lese en ny verdi, eller når den tegnes på nytt ved å endre størrelse på beholderen. I det første tilfellet må posisjonen endres, og i det andre må dens forhold til eiendommen transform
og verdien av scale
. Følgende kode er en modifikasjon av funksjonen actualizar_grafico()
å inkludere reposisjonering av grafens toppunktsymboler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
function actualizar_grafico_puntos
(
grafico, // Objeto SVG con el que se dibuja la gráfico
coordenada, // Matriz con las coordenadas del trazado formada por pares [tiempo,valor] ordenados primero el más antiguo (tiempo menor) último el más nuevo
puntos, // Matriz con los objetos SVG que se representan en los nodos de la línea (los valores reales)
tiempo_total_representado, // Tiempo total representado por la gráfico (en milisegundos)
valor_maximo, // Valor máximo aceptable antes de emitir una alarma
valor_minimo, // Valor mínimo aceptable antes de emitir una alarma
margen_valor, // Cantidad extra que se representa sobre/bajo el valor máximo/mínimo
parametro_cerrado, // Valor booleano que indica si el trazado se cierra o no (por defecto false)
parametro_ancho_caja, // Ancho de la caja que contiene la gráfico (por defecto 100.0)
parametro_alto_caja // Alto de la caja que contiene la gráfico (por defecto 100.0)
)
{
var cerrado=parametro_cerrado||false; // Valor booleano que indica si el trazado se cierra o no (por defecto false)
var ancho_caja=parametro_ancho_caja||100.0; // Ancho de la caja que contiene la gráfico (por defecto 100.0)
var alto_caja=parametro_alto_caja||100.0; // Alto de la caja que contiene la gráfico (por defecto 100.0)
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
var desplazamiento=[]; // Desplazamientos X e Y para posicionar el tiempo y el valor representado dentro del rango del gráfico
var escala=[]; // Coeficientes X e Y para calcular el tamaño al representar el gráfico
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var contador_valor=coordenada.length–1; // Variable para recorrer los valores (índice)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
escala[0]=ancho_caja/tiempo_total_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
escala[1]=alto_caja/(Math.abs(valor_maximo–valor_minimo)+margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
desplazamiento[0]=coordenada[coordenada.length–1][0]–tiempo_total_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
desplazamiento[1]=margen_valor–valor_minimo; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
if(cerrado) // Si se dibuja un path (trazado) cerrado…
{
coordenadas_trazado+=ancho_caja+“,”+alto_caja+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(coordenada[contador_valor][0]–desplazamiento[0])*escala[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=alto_caja–(coordenada[contador_valor][1]+desplazamiento[1])*escala[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
punto[contador_valor].setAttribute(“cx”,posicion[0]);
punto[contador_valor].setAttribute(“cy”,posicion[1]);
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que represntar, añadir una nueva línea (código L) para el próximo
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
contador_valor—; // Pasar al siguiente valor
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+alto_caja+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
grafico.setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
for(;contador_valor>=0;contador_valor—)
{
punto[contador_valor].setAttribute(“cx”,–10000);
punto[contador_valor].setAttribute(“cy”,0);
}
}
|
Endringer gjort i funksjonen actualizar_grafico()
for å få den nye funksjonen actualizar_grafico_puntos()
Det er de som er uthevet i koden i forrige eksempel. Først, i linje 5, tar vi en vektor av objekter SVG som en parameter. Denne vektoren vil inneholde symbolene som må omplasseres i de nye nodene i grafen.
I linjene 39 og 40 er de nye koordinatene til senteret tildelt, cx
y cy
, til de av verdiene som blir representert. Hvis symbolet ikke var basert på midten, vil det sannsynligvis være nødvendig å legge til en offset cx
halve bredden og inn cy
av halve høyden for å omplassere dem nøyaktig på grafnoden.
I linjene 57 til 61 er punktene som tilsvarer koordinater som ikke er tegnet fordi de er avskåret av venstre kant, flyttet utenfor grafen. Koordinaten til cy
til null og det av cx
til et hvilket som helst negativt tall (større enn selve punktet) slik at det ikke vises når det kuttes, som venstre del av grafen, av vinduet til SVG.
Administrer diagrammet fra et objekt med JavaScript
Alle operasjonene som har blitt forklart så langt kan integreres i et objekt for å administrere grafen med en stil som er mer typisk for de nye versjonene av Javascript. Dette implementeringsalternativet har den ekstra fordelen at det forenkler inkorporeringen av flere grafer, med forskjellige verdier, på samme nettside.
Før vi diskuterer implementeringen, la oss gå gjennom de vanligste måtene å lage objekter med Javascript og noen av særegenhetene til funksjonene som påvirker forslaget om å tegne IoT-sensorgrafikk.
Det ble allerede forklart at den nye måten å lage objekter på Javascript (tilgjengelig siden versjon 5 av ECMAScript) består av å bruke Object.create
, som bør venne seg til å bruke i stedet for "klassikeren" new
, som selvfølgelig fortsatt fungerer riktig, selv om formålet er mer å simulere stilen til språk med klassebaserte objekter (Javascript baserer opprettelsen av objekter på prototyper) enn et fungerende alternativ.
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Crear objetos con new o con Objetct.create</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/crear_objetos.js”></script>
</head>
<body onload=“empezar();”>
</body>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
function Objeto_clasico(primero,segundo)
{
this.primero=primero||1;
this.segundo=segundo||2;
this.hacer_algo=function()
{
console.log(“El objeto clásico le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”);
}
}
var Objeto_ES5=
{
primero:10,
segundo:20,
hacer_algo:
function()
{
console.log(“El objeto ES5 le saluda (primero=”+this.primero+” y segundo=”+this.segundo+“)”);
}
}
var objeto_clasico=new Objeto_clasico();
var objeto_ES5=Object.create(Objeto_ES5);
function empezar()
{
console.log(“Objeto clásico (“+objeto_clasico.primero+“,”+objeto_clasico.segundo+“)”);
console.log(“Objeto ES5 (“+objeto_ES5.primero+“,”+objeto_ES5.segundo+“)”);
objeto_clasico.hacer_algo();
objeto_ES5.hacer_algo();
}
|
Den forrige koden lar deg huske forskjellene mellom å lage objektene med Object.create
med new
. Det tjener også til å understreke det, mens funksjonen som objektet er skapt med new
kan være hvor som helst i koden, må objektet allerede eksistere før det kan instansieres med Object.create
(ES5_Objektobjekt er ikke en funksjon).
På linje 3 og 4, for å sette en standardverdi til egenskapene i funksjonen som oppretter objektet med new
, er hver egenskap tilordnet verdien av det tilsvarende argumentet eller (||
), hvis ingen argumenter har blitt bestått, det vil si hvis de er udefinerte (undefined
), slik den omstendigheten vurderes som false
, er standardverdien tildelt.
Konteksten som en funksjon utføres i Javascript tar opp to forhold som er viktige å huske på og som også kan være forvirrende når man bruker dette programmeringsspråket etter å ha jobbet med andre, som f.eks. C o C + +, i vårt tilfelle. Konteksten inkluderer variablene som er definert i omfanget av funksjonen (og de globale) som forresten reiser et interessant konsept, "lukkingene" som etablerer en hel programmeringsstil i Javascript. Når det er sagt, kunne man forvente det this
, som refererer til objektet når det brukes i koden som definerer det, opprettholdes utførelseskonteksten der det er definert, men den det bruker er konteksten som funksjonen kalles fra. Denne oppførselen er gjennomsiktig i de fleste tilfeller, men det er to omstendigheter der den kan være forvirrende: en funksjon definert i en annen funksjon og en metode kalt fra en hendelse av objektet. window
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
var primero=“Primero global”;
var segundo=“Segundo global”;
var Contexto=
{
primero:“Primero en contexto”,
segundo:“Segundo en contexto”,
probar:
function()
{
console.log(“Primero en contexto: “+this.primero);
console.log(“Segundo en contexto: “+this.segundo);
function probar_dentro()
{
console.log(“Primero dentro: “+this.primero);
console.log(“Segundo dentro: “+this.segundo);
}
probar_dentro();
}
}
var probador=Object.create(Contexto);
probador.probar();
/*
Primero en contexto: Primero en contexto
Segundo en contexto: Segundo en contexto
Primero dentro: Primero global
Segundo dentro: Segundo global
*/
|
Når du kjører forrige kode, vises den kommenterte teksten på slutten i konsollen. De to markerte linjene gjenspeiler atferd som kan være forvirrende: funksjonsutførelseskonteksten probar_dentro()
ikke probar()
, som man kunne forvente, men window
, som viser de globale variablene og ikke egenskapene med samme navn. Hvis du ikke vil ha denne oppførselen, oppretter du ganske enkelt en variabel i funksjonen på høyeste nivå og tilordner den til this
, som i følgende kode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
var primero=“Primero global”;
var segundo=“Segundo global”;
var Contexto=
{
primero:“Primero en contexto”,
segundo:“Segundo en contexto”,
probar:
function()
{
var esto=this;
console.log(“Primero en contexto: “+esto.primero);
console.log(“Segundo en contexto: “+esto.segundo);
function probar_dentro()
{
console.log(“Primero dentro: “+esto.primero);
console.log(“Segundo dentro: “+esto.segundo);
}
probar_dentro();
}
}
var probador=Object.create(Contexto);
probador.probar();
/*
Primero en contexto: Primero en contexto
Segundo en contexto: Segundo en contexto
Primero dentro: Primero en contexto
Segundo dentro: Segundo en contexto
*/
|
For å kontrollere utførelseskonteksten når en metode kalles fra en hendelse window
, for eksempel ved å endre størrelse på nettleservinduet, en annen særegenhet ved Javascript: muligheten for å programmere "funksjonsfabrikker", det vil si funksjoner som genererer andre funksjoner, returnere dem med return
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var Contexto=
{
prueba:“objeto”, // La propiedad prueba es de los objetos Contexto
llamar:
function()
{
esto=this;
return function()
{
console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba);
};
}
}
var prueba=“global”; // Esta variable prueba es global (window.prueba)
var hora=Date.now();
var probador=Object.create(Contexto);
var cosa=probador.llamar();
var pesadez=setInterval(cosa,3000);
setTimeout(function(){clearInterval(pesadez)},30100); // Desactivar la llamada periódica
|
I eksempelkoden ovenfor, metoden llamar()
av gjenstandene Contexto
Den gjør ikke jobben, men returnerer en anonym funksjon som tar seg av det. For å verifisere at alt fungerer som forventet, er det en global variabel med samme navn som egenskapen som funksjonen viser i konsollen; Hvis konteksten er riktig, vil verdien til egenskapen vises og ikke verdien til den globale variabelen.
Javascript Prøv å korrigere semikolontegnene som vi utelater på slutten av setningene. Dette gir en avslappet skrivestil, men er et tveegget sverd som må behandles forsiktig. I de fleste tilfeller, for å unngå de uønskede effektene som dette gir i uttrykk som opptar flere linjer, kan du bruke parentes eller gå foran måten som Javascript vil tolke koden; Det er derfor linje 8 i eksemplet inkluderer function
bak return
, hvis jeg hadde brukt en annen linje ville betydningen vært veldig annerledes. Etter min mening er den mest lesbare løsningen å bruke en mellomliggende (dispenserbar) variabel som i følgende versjon; Når oppførselen er forstått, tilsvarer avgjørelsen åpenbart programmereren.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
var Contexto=
{
prueba:“objeto”, // La propiedad prueba es de los objetos Contexto
llamar:
function()
{
esto=this;
var variable_auxiliar=
function()
{
console.log(“H + “+(Date.now()–hora)+” Contexto: “+esto.prueba);
};
return variable_auxiliar;
}
}
var prueba=“global”; // Esta variable prueba es global (window.prueba)
var hora=Date.now();
var probador=Object.create(Contexto);
var cosa=probador.llamar();
var pesadez=setInterval(cosa,3000);
setTimeout(function(){clearInterval(pesadez)},30100);
|
På samme måte som å evaluere et uttrykk som en funksjon, det vil si å returnere en funksjon og ikke verdien som funksjonen returnerer; på linje 21 i det siste eksemplet (det var på linje 19 i det forrige) stopper det med clearInterval
funksjonen kalt med setInterval
. For at den skal virke i 30 sekunder, blir stoppet utsatt med setTimeout
, som igjen trenger en funksjon som det første argumentet; å levere utførelsen som en parameter clearInterval
med variabelen som inneholder det periodiske kallet (og ikke funksjonen clearInterval
) er hva den anonyme funksjonen i den siste linjen er opprettet for.
Valget mellom å skrive koden som integrerer funksjonsdefinisjonen, mer kompakt (som i linje 21) eller bruke en hjelpevariabel, etter min mening, mer lesbar (som i linje 19 og 20) varierer lite i ytelse og avhenger mer stil og lesbarhet for vedlikehold.
For å teste koden, før du har data på serveren, kan du bruke en generator av tilfeldige verdier i ønsket område eller forberede tabeller med kontrollerte verdier som simulerer drift under de ønskede forholdene. Følgende eksempel bruker en enkel datagenerator over hele området, og det er grunnen til at de virker litt overdrevne.
For å teste, kan du last ned hele koden for eksempelet dannet av en nettside skrevet i HTML, stilen CSS og koden Javascript. Sistnevnte er den mest relevante, siden de andre komponentene bare er minimal støtte, veldig forenklet og er mye mer utviklet i artiklene i de tilsvarende delene.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!DOCTYPE html>
<html lang=“es”>
<head>
<meta charset=“utf-8”>
<title>Temperatura</title>
<script type=“text/javascript” src=“https://polaridad.es/javascript-grafico-svg-sensor-internet-de-las-cosas-iot/grafico.js”></script>
<link rel=“stylesheet” href=“estilo.css” type=“text/css” media=“all”>
</head>
<body onload=“iniciar_grafico(‘grafico’,’valor’,’fecha’);”>
<div id=“temperatura”>
<div id=“ultimo_valor”>
<div id=“fecha”></div>
<div id=“valor”></div>
</div>
<div id=“grafico”></div>
</div>
</body>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
body
{
margin:0px 0px 0px 0px;
}
#ultimo_valor
{
box–sizing:border–box;
width:100%;
height:40px;
padding:8px 0px 8px 14px;
font–family:monospaced;
font–size:18px;
color:#205587;
background–color:#86A7D0;
}
#fecha
{
float:left;
margin–right:15px;
}
#valor
{
float:left;
}
#grafico
{
width:100%;
height:200px;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
var Grafico=
{
contenedor_svg:void(0), // Objeto SVG con el que se dibuja el gráfico. Se inicializa con void(0) para asegurarse de que es === undefined al principio
contenedor_html:void(0), // Objeto HTML que contiene al objeto SVG con el gráfico (seguramente un div). Se inicializa con void(0) para asegurarse de que es === undefined al principio
contenedor_valor:void(0), // Objeto HTML en el que se muestra el último valor cargado del servidor
contenedor_fecha:void(0), // Objeto HTML para mostrar la fecha del último valor
pagina_servidor:“ultimo_valor_sensor.php”, // página en la que se consultan los datos
consulta:“fr1”, // Valor del parámetro con el que se consulta el servidor. La URL sería algo como http://servidoriot.com/ultimo_valor_sensor.php?zona=fr1
NS:“http://www.w3.org/2000/svg”, // Nombre del espacio de nombres (name space NS)
MINIMO:0, // Índice del menor valor admisible en el gráfico SVG. Los valores menores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
OPTIMO:1, // Índice del mejor valor del parámetro medido. Valor deseado para el parámetro monitorizado. Sirve para tener una idea rápida de cómo de correcto es el estado del sistema
MAXIMO:2, // Índice del mayor valor admisible en el gráfico SVG. Los valores mayores se saldrán del gráfico salvo por un margen que permite tener una idea de la tendencia
maximo_puntos:0, // Número máximo de vértices representados (pueden dibujarse menos si se recortan por la parte izquierda)
valor:[], // Últimos valores cargados del servidor
trazado:[], // Trazados representados (en principio se trata de representar uno con relleno y otro con línea)
cerrado:true, // Cuando vale true se dibuja el perfil y el área (la línea del gráfico y una zona rellena debajo). Cuando vale false solo se dibuja la línea
simbolo:[], // Objetos SVG utilizados para marcar los vértices de la línea (círculos)
linea_referencia:[], // Líneas horizontales en los valores de referencia
texto_referencia:[], // Objetos texto que rotulan los valores de referencia
fondo_referencia:[], // Rectángulos bajo los textos de los valores de referencia
medida_caja_svg:[100.0,100.0], // Ancho,Ancho del objeto SVG (aunque al dibujarlo con el código HTML tomará otras dimensiones ya que se dimensiona al 100% de su contenedor
correccion_caja_svg:[1.0,1.0], // Coeficientes para calcular la medida en el gráfico de un objeto del que se sabe el valor absoluto (Se usa el píxel como unidad)
tiempo_representado:0, // Milisegundos visibles en el gráfico empezando en el valor mayor (fecha y hora del último valor monitorizado)
valor_referencia:[0,0,0], // Valores de referencia [mínimo,óptimo,máximo]
margen_valor:0, // Zona por encima del valor mayor y por debajo del valor menor que se representa para tener una idea aproximada de la tendencia cuando los datos monitorizados rebasen los valores máximo y/o mínimo
escala_valor:[1.0,1.0], // Coeficientes que multiplican el valor monitorizado (y almacenado en el servidor) para calcular el representado según las dimensiones del gráfico
desplazamiento_valor:[0.0,0.0], // Desplazamiento que se suma al tiempo (X) y al valor (Y) para representarlo en el rango especificado para el gráfico
area_bajo_linea:true, // True para dibujar una zona rellena además del perfil (línea) del gráfico
medida_simbolo:[5,5], // Valor absoluto (en píxeles) del ancho y el alto del dibujo que se hace en los vértices de la línea del gráfico
tipografia:“sans-serif”, // Tipo de letra usado para rotular el gráfico (se usa solamente una tipografía por uniformidad)
altura_texto:10.0, // Altura de los textos en valor absoluto (píxeles). No todos los navegadores soportan todos los valores intermedios, será necesario coordinarlo con los valores que estén relacionados
escala_horizontal_texto:1, // Transformación que se aplica al texto para corregir la que implica la proporción del gráfico
color_fondo:“”, // Color de fondo del contenedor HTML del objeto SVG (Si es una cadena vacía no se cambia)
color_simbolos:“#000000”, // Color de los símbolos que se dibujan en los vértices de la línea del gráfico
color_referencia:[“#000000”,“#000000”,“#000000”], // Color de la referencia que indica el valor [mínimo,óptimo,máximo]
color_tipografia:“#FFFFFF”, // Color del tipo de letra con que se rotulan las referencias
grosor_trazado:4, // Grosor de la línea del gráfico
color_trazado:“#000000”, // Color de la línea del gráfico
color_relleno:“#000000”, // Color del relleno del gráfico
opacidad_relleno:1.0, // Opacidad del relleno del gráfico
grosor_referencia:0.5, // Grosor de la línea que se dibuja como referencia en valor absoluto (píxeles)
opacidad_referencia:1.0, // Opacidad de la línea de referencia. Si se dibuja sobre el gráfico con cierta transparencia permite ver el dibujo bajo ella
relleno_fondo_referencia:7.0, // Margen entre el fondo de la referencia y el texto medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
margen_fondo_referencia:10.0, // Separación de la referencia y el borde izquierdo del gráfico medido en valor absoluto (la línea empieza en el margen izquierdo y recorre todo el gráfico)
ancho_fondo_referencia:50.0, // Medida horizontal del rectángulo que hace de fondo al texto de la referencia medido en valor absoluto
texto_valor:[“temperatura: “,” °C”], // Prefijo y sufico con los que se rotula el valor monitorizado
texto_hora:[“hora “,“”], // Prefijo y sufijo con los que se rotula la hora (cuando solamente se rotula la hora)
texto_fecha:[“”,“”], // Prefijo y sufijo con los que se rotula la fecha (cuando solamente se rotula la fecha)
texto_fecha_hora:[“el “,” a las “,” | “], // Prefijo con el que se rotula la hora, separador de fecha y hora y sufjo de la hora cuando se rotula la fecha y la hora
HORA:0, // Constante que representa la hora para el modo de la fecha/hora
FECHA:1, // Constante que representa la fehca para el modo de la fecha/hora
FECHA_Y_HORA:2, // Constante que representa rotular la fecha y la hora (para el modo de fecha/hora)
modo_fecha:2, // 0->hora, 1-> fecha 2-> fecha y hora
repetir:null, // Función llamada por setInterval
nueva_proporcion: // Calcular la nueva proporción (al iniciar y al redimensionar el contenedor del gráfico) para transformar el texto
function()
{
if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto
{
if(this.contenedor_svg.nodeName===“svg”) // Si el objeto asignado es un SVG
{
this.escala_horizontal_texto=parseFloat(getComputedStyle(this.contenedor_svg).height)/parseFloat(getComputedStyle(this.contenedor_svg).width); // La escala debe calcularse cada vez ya que no se sabe si se ha redimensionado el objeto HTML que contiene al objeto SVG y que le da el tamaño
}
}
},
nueva_correccion_caja_svg:
function()
{
if(this.contenedor_svg!==undefined) // Se entiende que si no es undefined se ha asignado un objeto
{
if(this.contenedor_svg.nodeName.toLowerCase()===“svg”) // Si el objeto asignado es un SVG
{
this.correccion_caja_svg[0]=this.medida_caja_svg[0]/parseFloat(getComputedStyle(this.contenedor_svg).width);
this.correccion_caja_svg[1]=this.medida_caja_svg[1]/parseFloat(getComputedStyle(this.contenedor_svg).height);
}
}
},
nueva_escala:
function()
{
if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero
{
this.escala_valor[0]=this.medida_caja_svg[0]/this.tiempo_representado; // Coeficiente que multiplica a los valores horizontales (tiempo) para calcular las coordenadas X
this.escala_valor[1]=this.medida_caja_svg[1]/(Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.margen_valor*2); // Coeficiente que multiplica a los valores (vertical) para calcular las coordenadas Y
}
},
nuevo_desplazamiento:
function()
{
if(this.tiempo_representado>0) // Antes de inicializar el objeto el tiempo representado es cero
{
this.desplazamiento_valor[0]=this.valor[this.valor.length–1][0]–this.tiempo_representado; // Valor desde el que se empieza a contar el tiempo: el valor mayor (último) menos el rango de tiempo representado
this.desplazamiento_valor[1]=this.margen_valor–this.valor_referencia[this.MINIMO]; // Valor menor mostrado (al menor se le añade un margen para visualizar el principio de los valores fuera del rango permitido)
}
},
crear_svg: // Crear el objeto SVG dentro del objeto HTML
function()
{
if(this.contenedor_html!==null&&this.contenedor_html!==undefined) // Ya se ha asignado el objeto HTML
{
if(this.contenedor_html.nodeType===1) // Es un objeto HTML válido
{
// Color de fondo del objeto HTML
if(this.color_fondo!==“”)
{
this.contenedor_html.style.backgroundColor=this.color_fondo;
}
// Contenedor SVG
this.contenedor_svg=document.createElementNS(this.NS,“svg”); // Crear un objeto SVG
this.contenedor_svg.setAttribute(“width”,“100%”); // Ancho del objeto SVG
this.contenedor_svg.setAttribute(“height”,“100%”); // Alto del objeto SVG
this.contenedor_svg.setAttribute(“viewBox”,“0 0 “+this.medida_caja_svg[0]+” “+this.medida_caja_svg[1]); // Zona del SVG que se muestra
this.contenedor_svg.setAttribute(“preserveAspectRatio”,“none”); // No preservar la proporción para ocupar todo el objeto HTML que hace de contenedor
this.contenedor_html.appendChild(this.contenedor_svg); // Añadir al contenedor HTML el objeto SVG (que será el contenedor del gráfico)
// Trazado
this.trazado[0]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el relleno
this.trazado[0].style.fill=this.color_relleno;
this.trazado[0].style.fillOpacity=this.opacidad_relleno;
this.trazado[0].style.stroke=“none”;
this.contenedor_svg.appendChild(this.trazado[0]); // Añadir el relleno al SVG
this.trazado[1]=document.createElementNS(this.NS,“path”); // Crear el trazado que soporta el perfil
this.trazado[1].setAttribute(“vector-effect”,“non-scaling-stroke”); // No mantener la proporción en el trazado (no deformarlo)
this.trazado[1].style.fill=“none”;
this.trazado[1].style.stroke=this.color_trazado;
this.trazado[1].style.strokeWidth=this.grosor_trazado;
//this.trazado[1].style.strokeOpacity=1.0; // La opacidad por defecto es 1.0
this.contenedor_svg.appendChild(this.trazado[1]); // Añadir el perfil al SVG
// Símbolos para los vértices
var ahora=Date.now()–30000; // Fecha y hora actual menos 30 segundos
for(var contador_vertices=0;contador_vertices<this.maximo_puntos;contador_vertices++)
{
this.valor[contador_vertices]=[];
this.valor[contador_vertices][0]=ahora; // Inicializar los valores a la fecha y hora actual menos 30 segundos
this.valor[contador_vertices][1]=this.valor_referencia[this.OPTIMO]; // Inicializar los valores al óptimo
this.simbolo[contador_vertices]=document.createElementNS(this.NS,“ellipse”); // Crear una elipse (círculo) para cada vértice
this.simbolo[contador_vertices].style.fill=this.color_simbolos;
this.simbolo[contador_vertices].style.fillOpacity=1.0;
this.simbolo[contador_vertices].style.stroke=“none”;
this.contenedor_svg.appendChild(this.simbolo[contador_vertices]); // Añadir el símbolo al SVG
}
//this.nuevo_desplazamiento(); // Necesario si se asignaran las alturas de las referencias
for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++)
{
// Línea de referencia
//var posicion_corregida=this.medida_caja_svg[1]-(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Necesario si se asignaran las alturas de las referencias
this.linea_referencia[contador_referencia]=document.createElementNS(this.NS,“line”); // Crear la línea que representa el valor mínimo
this.linea_referencia[contador_referencia].style.stroke=this.color_referencia[contador_referencia];
this.linea_referencia[contador_referencia].style.strokeWidth=this.grosor_referencia;
this.linea_referencia[contador_referencia].style.strokeOpacity=this.opacidad_referencia;
this.linea_referencia[contador_referencia].setAttribute(“x1”,0.0); // Las líneas de referencia empiezan en el borde izquierdo…
this.linea_referencia[contador_referencia].setAttribute(“x2”,this.medida_caja_svg[0]); // …y terminan en el derecho
//this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo]
//this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida); // Ambos extremos a la altura correspondiente a la referencia [mínimo,óptimo,máximo]
this.contenedor_svg.appendChild(this.linea_referencia[contador_referencia]);
// Rectángulo de fondo del texto que indica el valor de referencia
this.fondo_referencia[contador_referencia]=document.createElementNS(this.NS,‘rect’);
this.fondo_referencia[contador_referencia].style.fill=this.color_referencia[contador_referencia];
this.fondo_referencia[contador_referencia].style.fillOpacity=1.0;
this.fondo_referencia[contador_referencia].style.stroke=“none”;
this.contenedor_svg.appendChild(this.fondo_referencia[contador_referencia]);
// Texto de referencia
this.texto_referencia[contador_referencia]=document.createElementNS(this.NS,“text”); // Crear el texto para rotular la referencia
this.texto_referencia[contador_referencia].setAttribute(“font-family”,this.tipografia); // Asignar el tipo de letra
this.texto_referencia[contador_referencia].setAttribute(“fill”,this.color_tipografia); // Asignar el color
//this.texto_referencia[contador_referencia].textContent=””+(this.valor_referencia[contador_referencia]>=0?”+”:””)+this.valor_referencia[contador_referencia]; // Texto que se rotula (los valores máximo, óptimo y mínimo ya deben estar asignados)
this.contenedor_svg.appendChild(this.texto_referencia[contador_referencia]);
}
}
}
},
ajustar_prporcion:
function()
{
this.nueva_proporcion();
this.nueva_correccion_caja_svg();
var posicion_corregida;
for(var contador_vertices=0;contador_vertices<this.simbolo.length;contador_vertices++)
{
this.simbolo[contador_vertices].setAttribute(“rx”,this.medida_simbolo[0]*this.correccion_caja_svg[0]);
this.simbolo[contador_vertices].setAttribute(“ry”,this.medida_simbolo[1]*this.correccion_caja_svg[1]);
}
for(var contador_referencia=0;contador_referencia<this.valor_referencia.length;contador_referencia++)
{
posicion_corregida=this.medida_caja_svg[1]–(this.valor_referencia[contador_referencia]+this.desplazamiento_valor[1])*this.escala_valor[1];
this.fondo_referencia[contador_referencia].setAttribute(“x”,this.margen_fondo_referencia*this.correccion_caja_svg[0]);
this.fondo_referencia[contador_referencia].setAttribute(“y”,posicion_corregida–(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]/2.0);
this.fondo_referencia[contador_referencia].setAttribute(“width”,this.ancho_fondo_referencia*this.correccion_caja_svg[0]);
this.fondo_referencia[contador_referencia].setAttribute(“height”,(this.altura_texto+this.relleno_fondo_referencia*2.0)*this.correccion_caja_svg[1]);
this.linea_referencia[contador_referencia].setAttribute(“y1”,posicion_corregida);
this.linea_referencia[contador_referencia].setAttribute(“y2”,posicion_corregida);
this.texto_referencia[contador_referencia].setAttribute(“transform”,“scale(“+this.escala_horizontal_texto+“,1.0)”);
this.texto_referencia[contador_referencia].setAttribute(“font-size”,this.altura_texto*this.correccion_caja_svg[1]);
this.texto_referencia[contador_referencia].setAttribute(“x”,(this.margen_fondo_referencia+this.relleno_fondo_referencia)*this.correccion_caja_svg[0]/this.escala_horizontal_texto);
this.texto_referencia[contador_referencia].setAttribute(“y”,posicion_corregida+this.altura_texto/2.0*this.correccion_caja_svg[1]);
this.texto_referencia[contador_referencia].textContent=(this.valor_referencia[contador_referencia]>=0?“+”:“”)+this.valor_referencia[contador_referencia]; // Texto que se rotula
}
},
rotar_valores:
function()
{
for(var contador_valor=0;contador_valor<this.valor.length–1;contador_valor++)
{
this.valor[contador_valor][0]=this.valor[contador_valor+1][0];
this.valor[contador_valor][1]=this.valor[contador_valor+1][1];
}
},
nuevo_valor_aleatorio:
function()
{
this.rotar_valores();
this.valor[this.valor.length–1][0]=this.valor[this.valor.length–2][0]+1000+Math.random()*1000; // El nuevo tiempo es el anterior más un segundo más un valor entre 0 y un segundo
this.valor[this.valor.length–1][1]=Math.random()*Math.abs(this.valor_referencia[this.MAXIMO]–this.valor_referencia[this.MINIMO])+this.valor_referencia[this.MINIMO]; // Un valor aleatorio entre el máximo y el mínimo
this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal
this.dibujar_nuevo_valor(true);
this.dibujar_nuevo_valor(false);
this.rotular_nuevo_valor();
this.ritular_nueva_fecha();
},
cargar_nuevo_valor:
function()
{
var consulta=‘zona=”+this.consulta;
var resultado;
var ajax;
if(window.XMLHttpRequest)
{
ajax=new XMLHttpRequest(); // ajax=Object.create(XMLHttpRequest);
}
else // Versiones antiguas de MS Internet Explorer
{
ajax=new ActiveXObject(“Microsoft.XMLHTTP”);
}
ajax.onreadystatechange=
function()
{
if(ajax.readyState==4&&ajax.status==200&&ajax.responseType==“json”)
{
resultado=JSON.parse(ajax.responseText);
if(resultado.fecha>objeto_grafico.fecha[objeto_grafico.fecha.length–1])
{
this.rotar_valores();
this.valor[this.valor.length–1][0]=resultado.fecha;
this.valor[this.valor.length–1][1]=resultado.temperatura;
this.nuevo_desplazamiento(); // Cada nuevo valor cambia el desplazamiento en horizontal
this.dibujar_nuevo_valor(true);
this.dibujar_nuevo_valor(false);
this.rotular_nuevo_valor();
this.ritular_nueva_fecha();
}
}
}
ajax.open(“POST”,this.pagina_servidor);
ajax.setRequestHeader(“Method”,“POST “+this.pagina_servidor+” HTTP/1.1″);
ajax.setRequestHeader(“Content-type”,“application/x-www-form-urlencoded”);
ajax.setRequestHeader(“Content-length”,consulta.length);
ajax.setRequestHeader(“Connection”,“close”);
ajax.send(consulta);
},
rotular_nuevo_valor:
function()
{
var valor_redondeado=Math.round(this.valor[this.valor.length–1][1]*100.0)/100.0;
this.contenedor_valor.innerHTML=this.texto_valor[0]+valor_redondeado+this.texto_valor[1];
},
ritular_nueva_fecha:
function()
{
var fecha_lectura=new Date(this.valor[this.valor.length–1][0]);
var texto_fecha=this.texto_hora[0];
texto_fecha+=(fecha_lectura.getHours()<10?“0”:“”)+fecha_lectura.getHours();
texto_fecha+=“:”;
texto_fecha+=(fecha_lectura.getMinutes()<10?“0”:“”)+fecha_lectura.getMinutes();
texto_fecha+=“:”;
texto_fecha+=(fecha_lectura.getSeconds()<10?“0”:“”)+fecha_lectura.getSeconds();
texto_fecha+=this.texto_hora[1];
this.contenedor_fecha.innerHTML=texto_fecha;
},
dibujar_nuevo_valor: // Dibujar el gráfico cuando llega un nuevo valor (que estará almacenado en el vector valor
function(cerrado)
{
// Si cerrado es undefined se evalúa a false, que se toma como valor por defecto
var contador_valor=this.valor.length–1; // Variable para recorrer los valores (índice)
var sin_recortar=true; // False si la gráfico se sale de la caja (si se sale, si recorta, se hace false para no seguir dibujando puntos)
var posicion=[]; // Variable intermedia (para hacer más legible el código) con la que calcular la posición horizontal y vertical de un punto del trazado
var coordenadas_trazado=“M “; // Cadena de texto que representa la propiedad “d” del trazado SVG
if(cerrado) // Si el trazado que se está dibujando está cerrado (no confundir con this.cerrado que determina si se dibujan los dos trazados, la línea y el relleno)
{
coordenadas_trazado+=this.medida_caja_svg[0]+“,”+this.medida_caja_svg[1]+” L “; // …se empieza por la parte inferior de la caja
}
while(contador_valor>=0&&sin_recortar) // Mientras queden valores por representar y no se haya llegado al borde izquierdo del gráfico…
{
posicion[0]=(this.valor[contador_valor][0]–this.desplazamiento_valor[0])*this.escala_valor[0]; // Calcular la X restando al tiempo el desplazamiento y convirtiéndolo a la escala del gráfico con el coeficiente horizontal
posicion[1]=this.medida_caja_svg[1]–(this.valor[contador_valor][1]+this.desplazamiento_valor[1])*this.escala_valor[1]; // Calcular la Y restando del alto de la caja (la Y crece hacia abajo en SVG)
this.simbolo[contador_valor].setAttribute(“cx”,posicion[0]);
this.simbolo[contador_valor].setAttribute(“cy”,posicion[1]);
coordenadas_trazado+=posicion[0]+“,”+posicion[1]; // Formar la coordenada con la X y la Y
if(posicion[0]>0) // Si no se ha rebasado el margen izquierdo…
{
coordenadas_trazado+=contador_valor>0?” L “:“”; // …y quedan valores que representar, añadir una nueva línea (código L) para el próximo
contador_valor—; // Pasar al siguiente valor
}
else // Si se ha rebasado el margen izquierdo…
{
sin_recortar=false; // …abandonar el modo sin recorte (lo que terminará de calcular coordenadas)
}
}
if(cerrado) // Si se dibuja un trazado (path) cerrado…
{
coordenadas_trazado+=” L “+posicion[0]+“,”+this.medida_caja_svg[1]+” Z”; // …se termina por la parte inferior de la caja y se añade Z para cerrarlo en SVG
}
this.trazado[cerrado?0:1].setAttribute(“d”,coordenadas_trazado); // Cambiar las coordenadas del trazado (propiedad “d”) por las que se han calculado
for(;contador_valor>=0;contador_valor—)
{
this.simbolo[contador_valor].setAttribute(“cx”,–10000);
this.simbolo[contador_valor].setAttribute(“cy”,0);
}
}
}
var temperatura_frigorifico;
function iniciar_grafico(nombre_objeto_grafico,nombre_objeto_valor,nombre_objeto_fecha)
{
temperatura_frigorifico=Object.create(Grafico);
temperatura_frigorifico.contenedor_html=document.getElementById(nombre_objeto_grafico);
temperatura_frigorifico.contenedor_valor=document.getElementById(nombre_objeto_valor);
temperatura_frigorifico.contenedor_fecha=document.getElementById(nombre_objeto_fecha);
temperatura_frigorifico.color_fondo=“#A8C3EA”;
temperatura_frigorifico.color_trazado=“#205587”;
temperatura_frigorifico.grosor_trazado=3;
temperatura_frigorifico.color_relleno=“#205587”;
temperatura_frigorifico.opacidad_relleno=0.5;
temperatura_frigorifico.color_simbolos=“#205587”;
temperatura_frigorifico.color_referencia[Grafico.MINIMO]=“#621D87”;
temperatura_frigorifico.color_referencia[Grafico.OPTIMO]=“#1D8762”;
temperatura_frigorifico.color_referencia[Grafico.MAXIMO]=“#871E35”;
temperatura_frigorifico.grosor_referencia=1.5;
temperatura_frigorifico.opacidad_referencia=0.5;
temperatura_frigorifico.tipografia=“monospaced”;
temperatura_frigorifico.color_tipografia=“#A8C3EA”;
temperatura_frigorifico.cerrado=true; // Dibujar una línea y una zona rellena debajo (true por defecto)
temperatura_frigorifico.maximo_puntos=32; // Un espacio de 30 segundos representados con un intervalo mínimo de 1 segundo necesitan como máximo 31 puntos, se añade uno por seguridad (por si algún valor monitorizado estuviera por debajo del segundo)
temperatura_frigorifico.crear_svg();
temperatura_frigorifico.nueva_correccion_caja_svg();
temperatura_frigorifico.tiempo_representado=30000;
temperatura_frigorifico.valor_referencia[Grafico.MINIMO]=–5;
temperatura_frigorifico.valor_referencia[Grafico.OPTIMO]=5;
temperatura_frigorifico.valor_referencia[Grafico.MAXIMO]=10;
temperatura_frigorifico.margen_valor=3;
temperatura_frigorifico.nueva_escala();
temperatura_frigorifico.nuevo_desplazamiento(); // El desplazamiento se (re)calcula cada vez que se añade un valor
temperatura_frigorifico.ajustar_prporcion();
window.addEventListener(“resize”,function(){temperatura_frigorifico.ajustar_prporcion()}); // Versión moderna
temperatura_frigorifico.repetir=setInterval(function(){temperatura_frigorifico.nuevo_valor_aleatorio()},1000);
}
|
Legg inn kommentar