W&T conecta
Interfaces para TCP/IP, Ethernet, RS-232, RS-485, USB, 20mA, fibra óptica de vidrio y de plástico, http, SNMP, OPC, Modbus TCP, I/O digital, I/O analógico, ISA, PCI

Aplicación al Web-IO digital:

Dirigir y controlar Web-IO digital con Visual C++ con Linux


La aplicación descrita a continuación permite dirigir el Web-IO con Linux. La superficie de manejo se recopiló con el diseñador Qt en la versión 3.3.5.

Web-IO con Linux

Con el ejemplo siguiente de programa C++ puede representar su Web-IO Digital con sus Inputs y Outputs en una aplicación Linux. Además de ello puede conectar los Outputs del Web-IO.

Preparativos

Ya ha abastecido su Web-IO Digital

1. Recopilación de los diferentes elementos de manejo y objetos de visualización en diseñador Qt

Diseñador Qt

Para crear esta aplicación se genera en el diseñador Qt un nuevo proyecto C++ al que se le puede dar un nombre cualquiera. Nosotros lo hemos llamado "Client". En el próximo paso se genera una ventana principal, en la que se colocan los elementos gráficos y se nombran sus características.

Para facilitar el compilar la aplicación en un sistema Unix, creamos dos archivos llamados "Uic" y "Qmake". Así pues obtenemos un texto origen C++ funcionable, pues se puede compilar sin problemas con un archivo Makefile.

Se cera un nuevo archivo de texto y se escribe en él las dos siguientes líneas. A continuación se salva el archivo en "Uic".

Aviso: Estas líneas funcionan sólo mientras se use qt3 y esté instalado en el mismo lugar en el sistema. De lo contrario adaptar las dos líneas.

/usr/lib/qt3/bin/uic $1.ui -o $1.h
/usr/lib/qt3/bin/uic -impl $1.h $1.ui -o $1.cpp
		

Ahora se crea un archivo de texto, que se salva a continuación en "Qmake".

/usr/lib/qt3/bin/qmake -o Makefile $1.pro

Una vez creados los dos archivos, se los deposita en el directorio, en el que se encuentra también el proyecto. Después se configuran los dos archivos de tal modo que sean ejecutables. Para ello se puede usar el comando chmod.

-> chmod a+x Dateiname

A continuación se puede convertir favorablemente el proyecto en el código de origen de C++ y compilarse. Para ello sólo son necesarios tres pasos.

./Uic Dateiname(.ui)
./Qmake Projekt(.pro)
make

Ahora se creó con éxito un archivo ejecutable, que se llama igual que el proyecto.

2. Crear el archivo de código origen de la superficie gráfica

Si ha arrancado el diseñador Qt y se ve delante abierta la ventana principal, con un chasqueo doble en la ventana se crea un archivo de código origen .ui.h. A continuación se le describe con detalle qué pasos son necesarios para poder hacer funcionar esta aplicación.

Para crear nuevos zócalos, se puede abrir una ventana de Características chasqueando con la tecla derecha del ratón en la ventana principal y elegir allí la entrada Slots. Allí se generan los zócalos necesarios, que aparecen entonces automáticamente con un cuerpo vacío en el archivo creado .ui.h .

Ahora incluimos todavía algunos archivos Header necesarios. El clientDlg.h y el clientDlg.cpp se crean después de la aplicación con ./Uic del archivo .ui (ventana principal). Para poder acceder a los elementos se incluye aquí el archivo Header.


#include "clientDlg.h"
#include <qsocket.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qtimer.h>
#include <iostream>

A continuación se variantes globales para una conexión TCP y se declara el Polling y se vuelve accesible así cualquier método de la clase. Dado que no definimos ningún espacio de nombre propio, se utiliza el espacio de nombre estándar.

using namespace std;

QSocket* client;
QTimer* output;
QTimer* input;
QTimer* counter;

3. Arranque de programa

Instalar los elementos de manejo

El grupo con los elementos de manejo para el Web-IO se bloquea primero para el manejo. Tan pronto como se establezca una conexión, se da línea libre a todos los elementos necesarios. Para ello se puede asentar en las características del GroupBox cb_io_control, así como en todos los demás elementos, la función Enable en ’false’ o ’true’.

El nombre del elemento respectivo de manejo se deriva del elemento mismo según el contexto. Los dos primeros signos del nombre significan el tipo del elemento (cb -> Checkbox, bt -> Button, gb -> Groupbox y le -> LineEdit).

4. El control de conexión

Introducir la conexión

Entrando la dirección IP del Web-IO en el campo de texto le_ip y en el puerto 80 en el campo de texto le_port se puede establecer una conexión activando el botón bt_connect. Si no se entra ninguna dirección IP o ningún puerto, se da un mensaje por la aplicación en la barra de estado.

La conexión

Para poder establecer una conexión TCP se inicializa la variable ya declarada de zócalo. Si se ha entrado todo correctamente intenta ahora establecer una conexión. Para saber si ha sido realizada también la conexión, se espera una señal del zócalo, que avisa una conexión positiva.

void clientDlg::onConnect()
{
	client = new QSocket(this);
	bool ok;
	connect(client,
	SIGNAL(connected()), SLOT(connectDone()));
	connect(client,
	SIGNAL(error(int)),
	SLOT(connectError(int)));

	if(le_ip->text() == "")
	le_statusBar->setText("No IP entered!");
	else if(le_port->text() == "")
	le_statusBar->setText("No port entered!");
	else
	client->connectToHost(le_ip->text(), (le_port->text()).toInt(&ok,10));
} 

En caso de un error en al conexión se emite un mensaje en la barra de estado.

void clientDlg::connectError(int)
{
	le_statusBar->setText("Error while connecting!");
}
		
Se establece la conexión

Tras una conexión positiva se da línea libre a todos los elementos de manejo útiles de la aplicación y se desactiva el botón Connect. Además la aplicación comienza inmediatamente a pasar a disposición de recepción.

void clientDlg::connectDone()
{
	le_statusBar->setText("Connected to " + le_ip->text());
	connect(client, SIGNAL(readyRead()), SLOT(onReceive()));

	output = new QTimer(this);
	connect(output,
	SIGNAL(timeout()),
	SLOT(timerEvent()));

	input = new
	QTimer(this);
	connect(input,
	SIGNAL(timeout()),
	SLOT(timerEvent()));

	counter = new
	QTimer(this);
	connect(counter,
	SIGNAL(timeout()),
	SLOT(timerEvent()));

	gb_io_control->setEnabled(true);
	bt_disconnect->setEnabled(true);
	bt_connect->setEnabled(false);
}
Separar la conexión

La conexión permanece tanto tiempo hasta que el usuario la finalice chasqueando el botón Disconnect o el Web-IO finalice la conexión. Después de pulsar el botón se emite un mensaje de que se va a finalizar la conexión.

void clientDlg::onDisconnect()
{
	client->close();

	output->stop();
	input->stop();
	counter->stop();

	gb_io_control->setEnabled(false);
	bt_disconnect->setEnabled(false);
	bt_connect->setEnabled(true);
	le_statusBar->setText("Disconnected!");
}

Con el fin de la conexión se bloquean de nuevo todos los elementos para el manejo.

5. Manejo y comunicación por parte del cliente

Tan pronto como se ha establecido la conexión con el Web-IO, el usuario puede enviar comandos al Web-IO manejando los correspondientes elementos de programa

Al enviar una noticia al Web-IO se trabaja asincrónicamente. Con ello la aplicación no está bloqueada en un proceso enviar/recibir.

Los Outputs del Web-IO pueden conectarse con ayuda de las dos cajas de chequeo cb_output0 y cb_output1.

En el paso siguiente se controla si la caja de chequeo ya está asentada y se pone el Output correspondiente en ON u OFF.

void clientDlg::onOutput0()
{
	QString message1 = "GET /outputaccess0?PW=" + le_password->text() + "&State=ON&";
	QString message2 = "GET /outputaccess0?PW=" + le_password->text() + "&State=OFF&";

	if(cb_output0->isChecked())
	client->writeBlock(message1, message1.length());
	else
	client->writeBlock(message2, message2.length());

	client->flush();
} 
void clientDlg::onOutput1()
{
	QString message1 = "GET /outputaccess1?PW=" + le_password->text() + "&State=ON&";
	QString message2 = "GET /outputaccess1?PW=" + le_password->text() + "&State=OFF&";

	if(cb_output1->isChecked())
	client->writeBlock(message1, message1.length());
	else
	client->writeBlock(message2, message2.length());

	client->flush();
} 

Solicitar estado de Output/Input

void clientDlg::onReadallOutputs()
{
	QString message = "GET /output?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
} 
void clientDlg::onReadallInputs()
{
	QString message = "GET /input?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
} 

Solicitar Contadores

void clientDlg::onReadCounter0()
{
	QString message = "GET /counter0?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
} 
void clientDlg::onReadCounter1()
{
	QString message = "GET /counter1?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
}

Naturalmente también se pueden solicitar todos los estados de contador con un solo comando.

void clientDlg::onReadallCounter()
{
	QString message = "GET /counter?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
}
Reposición a cero de contadores

Además también se puede poner a 0 los contadores después de la lectura.

void clientDlg::onClearCounter0()
{
	QString message = "GET /counterclear0?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
}
		
void clientDlg::onClearCounter1()
{
	QString message = "GET /counterclear1?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
} 

Naturalmente también se pueden reponer a cero todos los contadores con un sólo comando.

void clientDlg::onClearallCounter()
{
	QString message = "GET /counterclear?PW=" + le_password->text() + "&";
	client->writeBlock(message, message.length());
	client->flush();
} 

Puesto que todos los estados de contador se pueden leer o reponer a cero con un sólo comando, tiene que implementarse todavía un método, que procese el string de respuesta del Web-IO y que asigne su estado específico a cada contador en la aplicación.

void clientDlg::readAndClearCounter(QString data)
{
	QString counter[12];
	int j = 0;
	int length = data.length();
	for(int i = 0; i < length; i++)
	{
	if(data[i] == ';')
			j++;
	else
			counter[j] += data[i];
	}
	le_counter0->setText(counter[0]);
	le_counter1->setText(counter[1]);
}

6. Recepción de datos del Web-IO

Evaluar e indicar los datos recibidos
  • Todos los comandos y solicitudes al Web-IO se confirman con un String de respuesta. Aquí las respuestas presentan una estructura específica según el tipo:
  • Para los Outputs: output;<valor binario del estado de salida en formato hexadecimal>
  • Para un Output especial: outputx;<ON u OFF>
  • Para los Inputs: input;<valor binario del estado de salida en formato hexadecimal>
  • Para un Input especial: inputx;<ON u OFF>
  • Después hay también el string de respuesta para un contador que tiene el siguiente aspecto.
  • Contadores: counterx;<estado decimal de contador >
  • o counter;<estado decimal de contador 0 >; <estado decimal de contador 0 >; ...... si todos los contadores se deben leer de una sola vez.
  • Todos los Strings de respuesta se finalizan con un Byte 0.

En nuestra aplicación se llama el método OnReceive() para recibir una noticia tal. En este método se recibe y procesa el string de respuesta. Aquí lo especial es que esta función se llama todo el tiempo a sí misma y busca ininterrumpidamente noticias que hayan sido enviadas posiblemente por el Web-IO. Hemos determinado esto directamente después de que haya sido inicializada una conexión con éxito (connectDone()).

void clientDlg::onReceive()
{
	char buffer[50];
	client->readBlock(buffer, 49);
	QString rcv = buffer;
	bool ok;
	if(rcv[0] == 'o')
	{
		int value = rcv.right(rcv.length()-7).toInt(&ok, 16);
	if((value & 1) == 1)
		cb_output0->setChecked(true);
	else
		cb_output0->setChecked(false);
	if((value & 2) == 2)
		cb_output1->setChecked(true);
	else
		cb_output1->setChecked(false);
	}
	if(rcv[0] == 'i')
	{
		int value = rcv.right(rcv.length()-6).toInt(&ok, 16);
		if((value & 1) == 1)
		cb_input0->setChecked(true);
	else
		cb_input0->setChecked(false);
	if((value & 2) == 2)
	cb_input1->setChecked(true);
	else
	cb_input1->setChecked(false);
	}
	if(rcv[0] == 'c')
	{
	if(rcv[7] == '0') le_counter0->setText(rcv.right(rcv.length()-9));
	if(rcv[7] == '1') le_counter1->setText(rcv.right(rcv.length()-9));
	if(rcv[7] == ';') readAndClearCounter(rcv.right(rcv.length()-8));
	}
}
Solicitud cíclica de determinados valores

Es de desear que el estado de un sólo componente se actualice por si mismo. Para ello se utiliza un temporizador en este programa, que envía consultas al Web-IO cíclicamente en un intervalo de tiempo fijado por el usuario.

El tiempo de ciclo se entra en el campo le_interval.

Naturalmente también se captura en el caso de que el usuario realice una entrada absurda, como p. ej. un valor negativo de tiempo. Entonces aparece inmediatamente un mensaje y no se asume el valor.

7. Polling

bool clientDlg::checkRange()
{
	bool ok;
	int tmp = (le_interval->text()).toInt(&ok, 10);
	if(ok && tmp > 0)
	{
		le_statusBar->setText("Range changed to " + le_interval->text() + " ms!");
		return
		true;
	}
	else
	{
		le_statusBar->setText("Please type a positive integer value for the range of polling!");
		return
		false;
	}
} 

Para realizar ahora también la solicitud cíclica de los estados del Web-IO, lo que se llama también Polling, existe la elección entre el Polling de los Outputs, de los Inputs o de los contadores.

Si se activa la caja de chequeo cb_polling_outputs se aplica el Polling a los Outputs.

void clientDlg::onPollingOutputs()
{
if(checkRange())
	{
	if(cb_polling_outputs->isChecked())
		output->start((le_interval->text()).toInt(), false);
	else
		output->stop();
	}
}

Si se activa la caja de chequeo cb_polling_inputs se aplica el Polling a los Inputs.

void clientDlg::onPollingInputs()
{
	if(checkRange())
	{
	if(cb_polling_inputs->isChecked())
		input->start((le_interval->text()).toInt(), false);
	else
		input->stop();
	}
}

Si se deben consultar también los contadores con Polling, entonces se puede utilizar para ello la caja de chequeo cb_polling_counter.

void clientDlg::onPollingCounter()
{
	if(checkRange())
	{
	if(cb_polling_counter->isChecked())
	counter->start((le_interval->text()).toInt(),false);
	else
	counter->stop();
	}
}

Se inicializaron tres temporizadores diferentes, que disparan una acción en un intervalo de tiempo ajustado. Para capturar los eventos tiene que implementarse todavía un método.

void clientDlg::timerEvent()
{
	if(output->isActive()) onReadallOutputs();
	if(input->isActive()) onReadallInputs();
	if(counter->isActive()) onReadallCounter();
}

El programa ejemplo asiste todas las funciones corrientes del Web-IO en el modo String de comando, optimado para el Web-IO 2x Entradas digitales, 2x Salidas digitales. Para los otros modelos Web-IO tienen que realizarse en caso necesario adaptaciones en el programa. Otros ejemplos de programa para la programación del zócalo los encontrarán en las páginas de herramientas al Web-IO. Una descripción detallada de la interfaz del zócalo de los modelos Web-IO digitales la encontrarán en el manual de referencia.

Descargar el programa ejemplo

¿No tiene todavía un Web-IO y quiere probar el ejemplo presentado?

No hay problema: Le ponemos a disposición el Web-IO Digital 2xInput, 2xOutput gratis durante 30 días. Rellene sencillamente un pedido muestra y le enviaremos el Web-IO para probar a cuenta abierta. Si nos devuelve el aparato dentro de los 30 días, le abonamos la factura completa.

Al pedido muestra