¿Cómo consulto datos sin procesar de un Proficy Historian?
Pregunta
¿Cómo puedo recuperar datos brutos de series temporales de un Proficy Historian / iHistorian?
Idealmente, pediría datos para una etiqueta particular entre dos fechas.
Solución
Hay varios modos de muestreo diferentes con los que puede experimentar.
- Crudo
- Interpolado
- Laboratorio
- Tendencia
- Calculado
Estos modos están disponibles con todas las siguientes API.
- API de usuario (ihuapi.dll)
- SDK (ihsdk.dll)
- OLEDB (iholedb.dll)
- API de acceso de cliente (Proficy.Historian.ClientAccess.API)
De estos, el modo de muestreo de tendencias es probablemente lo que desea, ya que está específicamente diseñado para gráficos / tendencias. Sin embargo, el laboratorio y el interpolado también pueden ser útiles.
Lea el libro electrónico para obtener más información sobre cada modo de muestreo. En mi máquina se almacena como C: \ Archivos de programa \ GE Fanuc \ Proficy Historian \ Docs \ iHistorian.chm
y tengo instalada la versión 3.5. Presta especial atención a las siguientes secciones.
- Uso del proveedor OLE DB Historian
- Temas avanzados | Recuperación
Aquí es cómo puede construir un OLEDB para hacer un muestreo de tendencias.
set
SamplingMode = 'Trend',
StartTime = '2010-07-01 00:00:00',
EndTime = '2010-07-02 00:00:00',
IntervalMilliseconds = 1h
select
timestamp,
value,
quality
from
ihRawData
where
tagname = 'YOUR_TAG'
Mostrar los métodos equivalentes que usan la API de usuario y el SDK son complejos (más aún con la API de usuario) ya que requieren mucha conexión en el código para su configuración. La API de acceso de cliente es más nueva y usa WCF detrás de escena.
Por cierto, existen algunas limitaciones con el método OLEDB.
- A pesar de lo que dice la documentación, nunca he podido hacer funcionar parámetros de consulta nativos. Eso es un showtopper si desea usarlo con SQL Server Reporting Services, por ejemplo.
- No puede escribir muestras en el archivo o de ninguna manera realizar cambios en la configuración de Historian incluyendo agregar / cambiar etiquetas, escribir mensajes, etc.
- Puede ser un poco lento en algunos casos.
- No tiene ninguna disposición para cruzar varios nombres de etiquetas en las columnas y luego llevar muestras para que exista un valor para cada combinación de marca de tiempo y etiqueta. El modo de muestreo de tendencia lo lleva a la mitad del camino, pero aún no cruza y no carga muestras crudas. Por otra parte, la API de usuario y el SDK tampoco pueden hacer esto.
Otros consejos
Un compañero de trabajo mío armó esto:
En web.config:
<add name="HistorianConnectionString"
providerName="ihOLEDB.iHistorian.1"
connectionString="
Provider=ihOLEDB.iHistorian;
User Id=;
Password=;
Data Source=localhost;"
/>
En la capa de datos:
public DataTable GetProficyData(string tagName, DateTime startDate, DateTime endDate)
{
using (System.Data.OleDb.OleDbConnection cn = new System.Data.OleDb.OleDbConnection())
{
cn.ConnectionString = webConfig.ConnectionStrings.ConnectionStrings["HistorianConnectionString"];
cn.Open();
string queryString = string.Format(
"set samplingmode = rawbytime\n select value as theValue,Timestamp from ihrawdata where tagname = '{0}' AND timestamp between '{1}' and '{2}' and value > 0 order by timestamp",
tagName.Replace("'", "\""), startDate, endDate);
System.Data.OleDb.OleDbDataAdapter adp = new System.Data.OleDb.OleDbDataAdapter(queryString, cn);
DataSet ds = new DataSet();
adp.Fill(ds);
return ds.Tables[0];
}
}
Actualización:
Esto funcionó bien, pero encontramos un problema con las etiquetas que no se actualizan con mucha frecuencia. Si la etiqueta no se actualizó cerca del inicio o el final de la fecha de inicio y la fecha de finalización solicitadas, las tendencias se verían mal. Peor aún, todavía hubo casos en los que no hubo puntos explícitos durante la ventana solicitada: no recuperaríamos los datos.
Resolví esto haciendo tres consultas:
- El valor anterior antes de la fecha de inicio
- Los puntos entre startDate y endDate
- El siguiente valor después de el endDate
Esta es una forma potencialmente ineficiente de hacerlo, pero funciona:
public DataTable GetProficyData(string tagName, DateTime startDate, DateTime endDate)
{
DataSet ds = new DataSet();
string queryString;
System.Data.OleDb.OleDbDataAdapter adp;
using (System.Data.OleDb.OleDbConnection cn = new System.Data.OleDb.OleDbConnection())
{
cn.ConnectionString = proficyConn.ConnectionString;
cn.Open();
// always get a start value
queryString = string.Format(
"set samplingmode = lab\nselect value as theValue,Timestamp from ihrawdata where tagname = '{0}' AND timestamp between '{1}' and '{2}' order by timestamp",
tagName.Replace("'", "\""), startDate.AddMinutes(-1), startDate);
adp = new System.Data.OleDb.OleDbDataAdapter(queryString, cn);
adp.Fill(ds);
// get the range
queryString = string.Format(
"set samplingmode = rawbytime\nselect value as theValue,Timestamp from ihrawdata where tagname = '{0}' AND timestamp between '{1}' and '{2}' order by timestamp",
tagName.Replace("'", "\""), startDate, endDate);
adp = new System.Data.OleDb.OleDbDataAdapter(queryString, cn);
adp.Fill(ds);
// always get an end value
queryString = string.Format(
"set samplingmode = lab\nselect value as theValue,Timestamp from ihrawdata where tagname = '{0}' AND timestamp between '{1}' and '{2}' order by timestamp",
tagName.Replace("'", "\""), endDate.AddMinutes(-1), endDate);
adp = new System.Data.OleDb.OleDbDataAdapter(queryString, cn);
adp.Fill(ds);
return ds.Tables[0];
}
}
Y sí, lo sé, esas consultas deberían ser parametrizadas.
Michael - en IP21 hay un "Interpolado" tabla, así como la " real " tabla de puntos de datos. ¿Proficy tiene eso también?
Escribimos una DLL de envoltorio que se veía así:
[DllImport("IHUAPI.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "ihuReadRawDataByTime@24")]
public static extern int ihuReadRawDataByTime(int serverhandle, string tagname, ref IHU_TIMESTAMP startTime, ref IHU_TIMESTAMP endTime, ref int noOfSamples, ref IHU_DATA_SAMPLE* dataValues);
...
private int _handle;
public HistorianTypes.ErrorCode ReadRawByTime(string tagName, DateTime startTime, DateTime endTime,
out double[] timeStamps, out double[] values, out IhuComment [] comments)
{
var startTimeStruct = new IhuApi.IHU_TIMESTAMP(); //Custom datetime to epoch extension method
var endTimeStruct = new IhuApi.IHU_TIMESTAMP();
int lRet = 0;
int noOfSamples = 0;
startTimeStruct = DateTimeToTimeStruct(dstZone.ToUniversalTime(startTime));
endTimeStruct = DateTimeToTimeStruct(dstZone.ToUniversalTime(endTime));
IhuApi.IHU_DATA_SAMPLE* dataSample = (IhuApi.IHU_DATA_SAMPLE*)new IntPtr(0);
try {
lRet = IhuApi.ihuReadRawDataByTime
(
_handle, // the handle returned from the connect
tagName, // the single tagname to retrieve
ref startTimeStruct, // start time for query
ref endTimeStruct, // end time for query
ref noOfSamples, // will be set by API
ref dataSample // will be allocated and populated in the user API
);
....
Algunas notas son que iFIX verificará si la DLL está cargada en el inicio, por lo que debe hacer cosas como cargar / descargar dinámicamente la DLL para que otras aplicaciones no se bloqueen. Lo hicimos eliminando / agregando claves de registro sobre la marcha.
Otra es que si sondea 10,000 muestras y 1 de las muestras está dañada, eliminará todas las 10,000 muestras. Debe implementar un controlador de datos incorrectos que comenzará a ambos lados de los datos incorrectos y se incrementará en pasos para obtener todos los datos a ambos lados de la muestra incorrecta.
Hay varios archivos de encabezado C que contienen todos los códigos de error y el encabezado de función para la DLL.