Wiesemann & Theis GmbH

Tecnología de redes, sensores e interfaces para la industria, la oficina y la informática

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 paquete encoding/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() analiza jsondata y escribe el resultado en result.

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)
							}
						}
						
					

^