Aplicación para Web-IO y pure.box
Estados de conmutación de Web-IO guardados en MariaDB
con Golang en pure.box
Este tutorial muestra un sencillo programa Go para la pure.box. A través de la interfaz REST de un Web-IO se realiza la lectura de los estados de conmutación de este y la escritura en la MariaDB de pure.box.
Preparación
1. Activar base de datos en pure.box
2. Crear una base de datos
Maria-DB en pure.box contiene una base de datos preconfigurada pero vacía llamada userdb a la que se puede acceder a través del usuario del equipo.
CREATE TABLE `iostates` (
`id` int(11) NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`in0` int(11) NOT NULL,
`in1` int(11) NOT NULL,
`out0` int(11) NOT NULL,
`out1` int(11) NOT NULL,
`count0` int(11) NOT NULL,
`count1` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_german2_ci;
Para crear una base de datos en su pure.box con ese esquema de base de datos utilice el comanado siguiente:
mysql -u username -h box-ip -p userdb userdb.sql
o bien utilice una herramienta como phpMyAdmin o Mysql-Workbench.
3. Instalar el controlador MySQL para Go
Para poder activar la base de datos de pure.box desde Go se necesita primero un controlador de base de datos:
go get github.com/go-sql-driver/mysql
4. Activar la interfaz REST del Web-IO
Párrafo abreviado
Corto y conciso: REST y JSON
Web-IO 4.0 cuenta con una interfaz REST que pone a disposición la información del sistema, los estados y contadores actuales en diferentes formatos de datos. Como Golang de forma estándar trabaja con XML, JSON y Plaintext y JSON es más fácil de leer que XML, al menos para registros de datos más pequeños, en este tutorial vamos a referirnos a JSON. Encontrará información más detallada sobre la compatibilidad de los Web-IO con REST en los manuales de uso y de programación.
Se puede acceder a los estados de los Web-IO a través del siguiente URL:
http://ADRESSE/rest/json/iostate
La respuesta (sin datos de título) tiene la estructura siguiente:
{"iostate": {
"input": [{
"number": 0,
"state": 0
}, {
"number": 1,
"state": 0
}],
"output": [{
"number": 0,
"state": 0
}, {
"number": 1,
"state": 0
}],
"counter": [{
"number": 0,
"state": 0
}, {
"number": 1,
"state": 0
}]
}
}
El programa Go
El paqueteencoding/json
lee una cadena JSON y la representa en tipos. Por lo tanto, primero deben definirse los tipos que representen la estructura de JSON.
package main
//Result ist the root element of the JSOBN
type Result struct {
Iostate IOState `json:"iostate"`
}
//IOState keeps collections of a device’s inputs, outputs and counters
type IOState struct {
Input []IO `json:"input"`
Output []IO `json:"output"`
Counter []IO `json:"counter"`
}
//IO keeps the number if an IO and its current state
type IO struct {
Number int `json:"number"`
State int `json:"state"`
}
Aquí deben tenerse en cuenta dos cosas: los campos incluidos en el proceso de importar comienzan con una mayúscula, según la convención de Go. Las etiquetas en JSON asignan un identificador JSON explícitamente a un campo.
Lectura de Web-IO vía HTTP
Para leer los estados de I/O vía REST se importan tres paquetes en la cabecera del archivo:
import(
"encoding/json"
"io/ioutil"
"net/http"
)
encoding/json: este paquete pone a disposición las rutinas para un análisis rápido y elegante de JSON.
io/ioutil: este paquete ayuda a escribir todos los datos de un lector en una slice en byte
net/http: este pone a disposición la función HTTP-Get requerida.
El IP y el DataURL son puestos a disposición como variables globales:
var(
WebIOIP string = "10.40.38.100"
WebIODataURL string = "http://" + WebIOIP + "/rest/json/iostate"
)
La función getWebIOStates consulta los estados actuales vía REST y nos devuelve un tipo de Result
de jsontypes.go:
func getWebIOStates() Result {
//get HTTP response via REST
response, err := http.Get(WebIODataURL)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
//use ioutil to get json data as byte slice from response
jsondata, err := ioutil.ReadAll(response.Body)
//use encoding/json to unmarshal JSON
var result Result
err = json.Unmarshal(jsondata, &result)
if err != nil {
log.Fatal("Could not decode JSON!", err)
}
return result
}
Línea 3: la consulta de los estados se realiza a través de
http.Get()
.Línea 7: con la palabra clave
defer
se garantiza que se libere el recurso de respuesta al finalizar la función.Línea 10:
ioutil.ReadAll()
devuelve el cuerpo HTTP como slice en byte.Línea 14:
json.Unmarshall()
analizajsondata
y escribe el resultado enresult
.
Conexión en la base de datos
Para conectar a la base de datos se necesita el paquete database/sql
y el controlador descargado de antemano. El tipo de integración puede parecer a primera vista algo confusa: El paquete database/sql/Driver
pone a disposición una interfaz. Una vez importada una implementación de la misma, la database/sql
podrá utilizarla.
Pero, como en el código fuente de un programa normalmente no se sigue accediendo a este controlador, el Go-Compiler genera un error. La solución es "Importar efecto de página", que cambia el nombre del paquete por el identificador vacío ’_’ al importar.
import(
"encoding/json"
"io/ioutil"
"net/http"
"database/sql
_ "github.com/go-sql-driver/mysql"
)
También para la conexión con pure.box son necesarios algunos parámetros:
var (
PureBoxIP string = "192.168.100.25"
PureBoxDBPort string = "3306"
PureBoxDBName string = "userdb"
PureBoxUser string = "admin"
PureBoxPassword string = ""
WebIOIP string = "192.168.100.20"
WebIODataURL string = "http://" + WebIOIP + "/rest/json/iostate"
)
Ahora ya se puede realizar la función que escribe los valores en la base de datos:
func writeDatabase(webiodata Result) {
//Shorten variable names for the IO states
i0 := webiodata.Iostate.Input[0].State
i1 := webiodata.Iostate.Input[0].State
o0 := webiodata.Iostate.Output[0].State
o1 := webiodata.Iostate.Output[0].State
c0 := webiodata.Iostate.Counter[0].State
c1 := webiodata.Iostate.Counter[0].State
//Create dsn
dsn := PureBoxUser + ":" + PureBoxPassword +
"@" + "tcp(" + PureBoxIP + ":" + PureBoxDBPort + ")" +
"/" + PureBoxDBName
//Connect to database
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal("No database connection!", err)
}
defer db.Close()
//insert data
sqlstring := "insert into iostates (in0, in1, out0, out1, count0, count1) values(?,?,?,?,?,?)"
_, err = db.Query(sqlstring, i0, i1, o0, o1, c0, c1)
if err != nil {
log.Fatal("Could not write to Database! ", err)
}
}
Líneas 4-9: aquí se crean variables locales con el fin de obtener una notación de curso para los estados.
Líneas 12-14: la conexión con la base de datos se establece a través del nombre de la fuente de los datos. En este ejemplo sería:
admin:foo@192.168.100.25:3306/userdb
Líneas 17-19: aquí se establece la conexión con la base de datos y se cierra al salir de la función.
Líneas 24-28: aquí tiene lugar la escritura en la base de datos. La función
db.Query()
sustituye los ? en la cadena SQL por los parámetros transferidos.
El programa principal
Para la completa implementación solo falta ya un parámetro: el intervalo de tiempo entre dos consultas:
var (
LoggingIntervall time.Duration = 10 * time.Second
PureBoxIP string = "192.168.100.25"
PureBoxDBPort string = "3306"
PureBoxDBName string = "userdb"
PureBoxUser string = "admin"
PureBoxPassword string = "foo"
WebIOIP string = "192.168.100.20"
WebIODataURL string = "http://" + WebIOIP + "/rest/json/iostate"
)
El programa principal es sencillo:
func main() {
for {
iostates := getWebIOStates()
writeDatabase(iostates)
time.Sleep(LoggingIntervall)
}
}
Línea 2: un
for
sin condición inicia un bucle infinito.Línea 3: en esta línea primero se leen los IO.
Línea 4: luego se escribe en la base de datos.
Línea 5: y, por último, se espera un
LoggingIntervall
antes de iniciarse un nuevo bucle.
El programa completo
El resultado como texto fuente completo es:
//purebox_webio project main.go
package main
import (
"log"
"time"
"encoding/json"
"io/ioutil"
"net/http"
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var (
LoggingIntervall time.Duration = 10 * time.Second
PureBoxIP string = "10.40.38.10"
PureBoxDBPort string = "3306"
PureBoxDBName string = "userdb"
PureBoxUser string = "admin"
PureBoxPassword string = ""
WebIOIP string = "10.40.38.100"
WebIODataURL string = "http://" + WebIOIP + "/rest/json/iostate"
)
//Result ist the root element of the JSOBN
type Result struct {
Iostate IOState `json:"iostate"`
}
//IOState keeps collections of a device’s inputs, outputs and counters
type IOState struct {
Input []IO `json:"input"`
Output []IO `json:"output"`
Counter []IO `json:"counter"`
}
//IO keeps the number if an IO and its current state
type IO struct {
Number int `json:"number"`
State int `json:"state"`
}
func getWebIOStates() Result {
//get HTTP response via REST
response, err := http.Get(WebIODataURL)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
//use ioutil to get json data as byte slice from response
jsondata, err := ioutil.ReadAll(response.Body)
//use encoding/json to unmarshal JSON
var result Result
err = json.Unmarshal(jsondata, &result)
if err != nil {
log.Fatal("Could not decode JSON!", err)
}
return result
}
func writeDatabase(webiodata Result) {
//Shorten IO names fore readability
i0 := webiodata.Iostate.Input[0].State
i1 := webiodata.Iostate.Input[0].State
o0 := webiodata.Iostate.Output[0].State
o1 := webiodata.Iostate.Output[0].State
c0 := webiodata.Iostate.Counter[0].State
c1 := webiodata.Iostate.Counter[0].State
//Create dsn
dsn := PureBoxUser + ":" + PureBoxPassword +
"@" + "tcp(" + PureBoxIP + ":" + PureBoxDBPort + ")" +
"/" + PureBoxDBName
//Connect to database
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal("No database connection!", err)
}
defer db.Close()
//insert data
sqlstring := "insert into iostates (in0, in1, out0, out1, count0, count1) values(?,?,?,?,?,?)"
_, err = db.Query(sqlstring, i0, i1, o0, o1, c0, c1)
if err != nil {
log.Fatal("Could not write to Database! ", err)
}
}
func main() {
// Never stop doing
for {
iostates := getWebIOStates()
log.Println(iostates)
writeDatabase(iostates)
time.Sleep(LoggingIntervall)
}
}