Como crear un origen de datos para Xendra Historian
From XendraWiki
Requisitos
- Microsoft Visual Studio .NET 2005
- Código fuente de Xendra Historian
- wxWidgets (Recomendado: 2.8.7) - http://www.wxwidgets.org
- libCURL (Recomandado: 7.17.1) - http://curl.haxx.se/
- libXML2 (Recomendado: 2.6.30) - http://xmlsoft.org
Creación del proyecto
Se crea un proyecto con el nombre deseado, dentro de la solución Historian. En este caso, OPC. El proyecto debe tener la siguiente configuración:
- Tipo: Aplication (.exe)
- Output Directory: $(SolutionDir)Bin\$(ConfigurationName) $(PlatformName)
- Intermediate Directory: $(SolutionDir)Intermediate\$(ConfigurationName) $(PlatformName)
- Runtime: Multi-threaded Debug/Release
- Directorios de Includes: "$(SolutionDir)Include";"$(SolutionDir)DataSource";"$(SolutionDir)HistorianLib";"$(SolutionDir)RVPFLib"
- Librerías: "$(OutDir)\DataSource.lib" "$(OutDir)\HistorianLib.lib" "$(OutDir)\RVPFLib.lib" libxml2_a.lib curllibd.lib wxbase28d.lib wxbase28d_net.lib wxbase28d_odbc.lib wxbase28d_xml.lib wxexpatd.lib wxjpegd.lib wxmsw28d_adv.lib wxmsw28d_aui.lib wxmsw28d_core.lib wxmsw28d_dbgrid.lib wxmsw28d_gl.lib wxmsw28d_html.lib wxmsw28d_media.lib wxmsw28d_qa.lib wxmsw28d_richtext.lib wxmsw28d_xrc.lib wxpngd.lib wxregexd.lib wxtiffd.lib wxzlibd.lib comctl32.lib rpcrt4.lib ws2_32.lib winmm.lib
- Output File: $(OutDir)\$(ProjectName).exe
- Preprocessor Definitions:
WIN32 _DEBUG __WXMSW__ __WXDEBUG__ _WINDOWS NOPCH
- Precompiled Headers: No
Archivos a crear
Lo recomendado para un origen de datos es crear dos archivos: un header y un archivo de codigo. Además, pueden existir archivos adicionales propios de la comunicación con la planta(Ej. OPC. ProfiBus, ModBus, etc).
Definición del Header
El header de la aplicación define la clase base de un DataSource. Debe tener num nombre descriptivo, el mismo que será utilizado para el archivo de código, y, en especial, para el nombre de la clase. Por ejemplo, OPCDataSource.
El contenido del header debe ser simlilar a este (OPCDataSource.h):
#pragma once
#include <DataSource.h>
class OPCDataSource : public DataSource {
public:
OPCDataSource();
virtual ~OPCDataSource();
private:
virtual bool OnConfigLoading_Callback(Node* n);
virtual Node* OnConfigSaving_Callback();
virtual bool OnServerReady_Callback();
virtual bool Plant_Connect();
virtual bool Plant_Disconnect();
virtual bool Plant_IsConnected();
virtual bool Plant_GetVars(std::vector<std::string>& vars);
virtual bool Plant_PrepareVar(std::string VarName);
virtual bool Plant_ReadVar(std::string VarName,std::string& VarVal,time_t* rtime);
};
Todas las funciones son abstractas en la clase DataSource, por lo cual deberán ser implementadas en la clase. Además de las funciones requeridas por DataSource, pueden agregarse funciones y variables según se requiera.
Archivo de código
El archivo de código debe implementar todas las funciones definidas en el header. Su estructura básica es asi(OPCDataSource.cpp):
#include "OPCDataSource.h"
IMPLEMENT_APP(OPCDataSource);
OPCDataSource::OPCDataSource() : DataSource() { }
OPCDataSource::~OPCDataSource() { }
bool OPCDataSource::OnConfigLoading_Callback(Node* n) { }
Node* OPCDataSource::OnConfigSaving_Callback() { }
bool OPCDataSource::OnServerReady_Callback() { }
bool OPCDataSource::Plant_Connect() { }
bool OPCDataSource::Plant_Disconnect() { }
bool OPCDataSource::Plant_IsConnected() { }
bool OPCDataSource::Plant_GetVars(std::vector<std::string>& vars) { }
bool OPCDataSource::Plant_PrepareVar(std::string VarName) { }
bool OPCDataSource::Plant_ReadVar(std::string VarName,std::string& VarVal,time_t* rtime) { }
Implementación de la aplicación
Puesto que la clase base es abstracta, no puede ser implementada como clase de aplicación para wxWidgets. Por ello, se tiene que implementar a la clase derivada como clase de aplicación. Para ello se utiliza el macro IMPLEMENT_APP, con el nombre de la clase como único parámetro.
Constructor y Destructor
Ni el constructor ni el destructor son estrictamente necesarios, aunque es recomendable implementarlos para evitar pérdidas de memoria.
bool OPCDataSource::OnConfigLoading_Callback(Node* n)
Esta función es llamada por la clase base durante la carga de la configuración. El parámetro n contiene el nodo de la configuración de donde la clase derivada deberá cargar toda su configuración. Esta función nunca recibirá un puntero nulo. Para inicializar la configuración, se utilizará orto callback.
Si esta función retorna true, se continuará la carga de la aplicación. Si retorna false, se abortará la carga de la aplicación.
Si la clase derivada no requiere configuración adicional, la función deberá ser implementada de la siguiente manera:
bool OPCDataSource::OnConfigLoading_Callback(Node* n) {
return true
}
Node* OPCDataSource::OnConfigSaving_Callback()
Esta función es llamada por la clase base para que la clase derivada inicialize su nodo de configuración. En esta función, la clase derivada podrá mostrar al usuario las opciones de configuración propias de su implementación.
Si la función se ejecutó normalmente, deberá retornar un nodo válido. En caso contrario, deberá retornar NULL, para generar un error en la aplicación.
Si la clase derivada no requiere configuración específica, esta función deberá ser implementada de la siguiente manera:
Node* OPCDataSource::OnConfigSaving_Callback() {
Node* ret = new Node();
ret->SetName("datasource2");
return ret;
}
bool OPCDataSource::OnServerReady_Callback()
Esta función será llamada por la clase base cuando inmediatamente después de haberse conectado al servidor. Actualmente, no hay ningún uso propio de esta función, por lo que deberá ser implementada de la siguiente forma:
bool OPCDataSource::OnServerReady_Callback() {
return true;
}
bool OPCDataSource::Plant_Connect()
Esta función es llamada durante la inicialización para que, de ser necesario, la clase derivada se conecte al servidor de planta correspondiente. La información para conectarse al servidor de planta debe haber sido tomada del nodo recibido en OnConfigLoading_Callback.
La función debe retornar true si la operación fué exitosa. En caso contrario, deberá retornar false.
Si la clase no requiere conexión a un servidor de plante, esta función deberá ser implementada de la siguiente manera:
bool OPCDataSource::Plant_Connect() {
return true;
}
Si la clase requiere conexión al servidor por cada lectura, deberá realizar la conexión en la función Plant_ReadVar.
bool OPCDataSource::Plant_Disconnect()
Esta función será llamada durante la finalización de la aplicación para que, de ser necesario, se cierre la conexión con el servidor de planta.
Aunque esta función puede retornar true si no ocurrieron errores o false si ocurrieron, el valor será ignorado por la clase base.
Si la clase derivada no requiere conexión al servidor de planta, o si maneja conexión por cada lectura, deberá implementar esta funcion de la siguiente manera:
bool OPCDataSource::Plant_Disconnect() {
return true;
}
bool OPCDataSource::Plant_IsConnected()
Esta función es llamada por la clase base para determinar si está abierta la conexión con el servidor de planta. Puede retornar true o false, según la conexión esté estalecida o no.
Si la clase no requiere conexión al servidor de planta, deberá implementar la función de la siguiente manera:
bool OPCDataSource::Plant_IsConnected() {
return true;
}
Si la clase maneja conexión por lectura, deberá implementarlo de la misma manera que si no maneja conexión. Será por medio de la función Plant_ReadVar que podrá retornar un error si falló la conexión con el servidor.
bool OPCDataSource::Plant_GetVars(std::vector<std::string>& vars)
Esta función es llamada durante la inicialización por la clase base para conocer las variables existentes en el servidor de planta. El arreglo vars deberá ser llenado con la lista de nombres únicos de las variables, de manera que después puedan ser leidas utilizando el mismo nombre.
La función deberá retornar true si pudo obtener la lista, o false si no pudo obtenerla. En tal caso, se abortará la inicialización.
Aunque la clase derivada puede dejar vacío el arreglo, no resulta de utilidad, pues la finalidad del DataSource es proporcionar variables para ser escritas en el servidor Historian.
bool OPCDataSource::Plant_PrepareVar(std::string VarName)
Esta función será llamada por la clase base durante la inicialización para que la clase derivada prepara todo lo necesario para poder leer la variable indicada por VarName. Esto puede ser, en el caso de OPC, el agregar la variable en la interfaz OPC para obtener un Handle de lectura.
Si la función retorna true, se continuará con la carga de la aplicación. Si retorna false, se abortará la carga.
Si la clase derivada no requiere preparar valores para leer las variables, deberá implementar la función de la siguiente manera:
bool OPCDataSource::Plant_PrepareVar(std::string VarName) {
return true;
}
Cualquier recurso alojado por esta función deberá ser liberado al desconectar de la planta, o en el destructor de la clase.
bool OPCDataSource::Plant_ReadVar(std::string VarName,std::string& VarVal,time_t* rtime)
Esta función será llamada por la clase base durante el loop de lectura, para leer la variable antes de escribirla en el servidor Historian. El nombre de la variable a leer está en VarName. El valor deberá ser almacenado en VarVal. rtime es un puntero a un time_t donde se deberá almacenar el tiempo al cual fué leido el valor.
Si la lectura es exitosa, la función deberá retornar true. En caso contrario, deberá retornar false.
ADVERTENCIA: En esta función, el tiempo es crítico, pues el Historian puede manejar grandes cantidades de operaciones de lectura-escritura por segundo. Por tanto, el código deberá ser lo mas rápido posible, buscando apoyarse en las otras funciones para no tener que realizar muchas operaciones.