简体   繁体   English

如何从Proficy Historian查询原始数据?

[英]How do I query raw data from a Proficy Historian?

How can I retrieve raw time-series data from a Proficy Historian/iHistorian? 如何从Proficy Historian / iHistorian检索原始时间序列数据?

Ideally, I would ask for data for a particular tag between two dates. 理想情况下,我会要求两个日期之间的特定标签的数据。

There are several different sampling modes you can experiment with. 您可以尝试几种不同的采样模式。

  • Raw 生的
  • Interpolated 内插
  • Lab 实验室
  • Trend 趋势
  • Calculated 计算

These modes are available using all of the following APIs. 使用以下所有API可以使用这些模式。

  • User API (ihuapi.dll) 用户API(ihuapi.dll)
  • SDK (ihsdk.dll) SDK(ihsdk.dll)
  • OLEDB (iholedb.dll) OLEDB(iholedb.dll)
  • Client Acess API (Proficy.Historian.ClientAccess.API) 客户端访问API(Proficy.Historian.ClientAccess.API)

Of these the trend sampling mode is probably what you want since it is specifically designed for charting/trending. 其中趋势采样模式可能是您想要的,因为它专门用于制图/趋势。 Though, lab and interpolated may be useful as well. 虽然,实验室和插值也可能有用。

Read the electronic book for more information on each sampling mode. 有关每种采样模式的更多信息,请阅读电子书。 On my machine it is stored as C:\\Program Files\\GE Fanuc\\Proficy Historian\\Docs\\iHistorian.chm and I have version 3.5 installed. 在我的机器上,它存储为C:\\Program Files\\GE Fanuc\\Proficy Historian\\Docs\\iHistorian.chm ,我安装了3.5版本。 Pay particular attention to the following sections. 请特别注意以下部分。

  • Using the Historian OLE DB Provider 使用Historian OLE DB提供程序
  • Advanced Topics | 高级主题| Retrieval 恢复

Here is how you can construct an OLEDB to do trend sampling. 以下是如何构建OLEDB以进行趋势采样。

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'

Showing the equivalent methods using the User API and the SDK are complex (more so with the User API) since they require a lot of plumbing in the code to get setup. 使用User API和SDK显示等效方法很复杂(使用User API更是如此)因为它们需要在代码中进行大量管道才能进行设置。 The Client Access API is newer and uses WCF behind the scenes. Client Access API更新,并在后台使用WCF。

By the way, there are a few limitations with the OLEDB method though. 顺便说一句,OLEDB方法有一些限制。

  • Despite what the documentation says I have never been able to get native query parameters to work. 尽管文档说我从来没有能够获得本机查询参数。 That is a showstopper if you want to use it with SQL Server Reporting Services for example. 如果您想将它与SQL Server Reporting Services一起使用,那就是一个showstopper。
  • You cannot write samples into the archive or in any way make changes to the Historian configuration including adding/changing tags, writing messages, etc. 您无法将样本写入存档或以任何方式更改Historian配置,包括添加/更改标记,编写消息等。
  • It can be a little slow in some cases. 在某些情况下,它可能会有点慢。
  • It has no provision for crosstabbing multiple tagnames into the columns and then carrying forward samples so that a value exists for each timestamp and tag combination. 它没有规定将多个标记名交叉到列中,然后传送样本,以便为每个时间戳和标记组合存在一个值。 The trend sampling mode gets you halfway there, but still does not crosstab and does not actually load raw samples. 趋势采样模式可以让您到达中途,但仍然没有交叉表,并且实际上并未加载原始样本。 Then again the User API and SDK cannot do this either. 然后,用户API和SDK也无法执行此操作。

A coworker of mine put this together: 我的同事把它放在一起:

In web.config: 在web.config中:

<add name="HistorianConnectionString" 
     providerName="ihOLEDB.iHistorian.1" 
     connectionString="
       Provider=ihOLEDB.iHistorian;
       User Id=;
       Password=;
       Data Source=localhost;"
/>

In the data layer: 在数据层中:

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];
    }
}

Update: 更新:

This worked well but we ran into an issue with tags that don't update very often. 这很好用,但我们遇到了一个问题,这个问题不经常更新。 If the tag didn't update near the start or end of the requested startDate and endDate, the trends would look bad. 如果标记未在请求的startDate和endDate的开头或结尾附近更新,则趋势看起来会很糟糕。 Worse, still were cases where there were no explicit points during the window requested--we'd get no data back. 更糟糕的是,仍然是在请求的窗口期间没有明确点的情况 - 我们没有得到任何数据。

I resolved this by making three queries: 我通过三个查询来解决这个问题:

  1. The previous value before the start-date 开始日期之前的前一个值
  2. The points between startDate and endDate startDate和endDate之间的点
  3. The next value after the endDate endDate 之后的下一个值

This is a potentially inefficient way to do it but It Works: 这是一种潜在的低效方法,但它可行:

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];
    }
}

And yes, I know, those queries should be parameterized. 是的,我知道,这些查询应该参数化。

Michael--in IP21 there is an "Interpolated" table, as well as the "actual" data point table. Michael - 在IP21中有一个“Interpolated”表,以及“实际”数据点表。 Does Proficy have that as well? Proficy也有这个吗?

We wrote a wrapper DLL that looked like this like this: 我们编写了一个如下所示的包装DLL:

[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
            );
            ....

Some notes are that iFIX will check if the DLL is loaded on startup so you need to do things like dynamically load/unload the DLL so that other applications don't crash. 一些注意事项是iFIX将检查DLL是否在启动时加载,因此您需要执行诸如动态加载/卸载DLL以使其他应用程序不会崩溃的操作。 We did this by deleting/adding registry keys on the fly. 我们通过动态删除/添加注册表项来完成此操作。

Another one is if you poll 10,000 samples and 1 of the samples are corrupted it will drop all 10,000 samples. 另一个是如果您轮询10,000个样本并且其中一个样本已损坏,则会丢弃所有10,000个样本。 You need to implement a bad data handler that will start at either side of the bad data and increment in steps to get all data either side of the bad sample. 您需要实现一个错误的数据处理程序,该处理程序将从错误数据的任一侧开始,并逐步递增以获取错误样本的任何一侧的所有数据。

There are several C header files that contain all of the error codes and the function header for the DLL. 有几个C头文件包含DLL的所有错误代码和函数头。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM