13

如何从 Proficy Historian/iHistorian 中检索原始时间序列数据?

理想情况下,我会询问两个日期之间特定标签的数据。

4

4 回答 4

17

您可以尝试几种不同的采样模式。

  • 生的
  • 插值
  • 实验室
  • 趋势
  • 计算出来的

这些模式可通过以下所有 API 使用。

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

其中趋势采样模式可能是您想要的,因为它是专门为图表/趋势设计的。不过,lab 和 interpolated 也可能有用。

阅读电子书以获取有关每种采样模式的更多信息。在我的机器上它被存储为C:\Program Files\GE Fanuc\Proficy Historian\Docs\iHistorian.chm并且我安装了 3.5 版。请特别注意以下部分。

  • 使用 Historian OLE DB 提供程序
  • 高级主题 | 恢复

以下是构建 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'

使用用户 API 和 SDK 显示等效方法很复杂(使用用户 API 更是如此),因为它们需要在代码中进行大量的设置才能进行设置。客户端访问 API 较新,并在幕后使用 WCF。

顺便说一句,OLEDB 方法有一些限制。

  • 尽管文档说我从来没有能够让本机查询参数工作。例如,如果您想将它与 SQL Server Reporting Services 一起使用,那将是一个很好的选择。
  • 您不能将样本写入存档或以任何方式更改 Historian 配置,包括添加/更改标签、写入消息等。
  • 在某些情况下,它可能会有点慢。
  • 它没有规定将多个标记名交叉制表到列中,然后继续执行样本,以便每个时间戳和标记组合都存在一个值。趋势采样模式让您成功了一半,但仍然没有交叉表,也没有实际加载原始样本。然后用户 API 和 SDK 也不能这样做。
于 2010-07-23T16:45:39.797 回答
4

我的一个同事把这个放在一起:

在 web.config 中:

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

在数据层:

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

更新:

这运作良好,但我们遇到了标签不经常更新的问题。如果标签在请求的 startDate 和 endDate 的开始或结束附近没有更新,则趋势看起来很糟糕。更糟糕的是,在请求的窗口期间仍然没有明确的点——我们不会得到任何数据。

我通过三个查询解决了这个问题:

  1. 开始日期之前的上一个值
  2. startDate 和 endDate 之间的点
  3. endDate之后的下一个值

这是一种潜在的低效方法,但它有效:

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

是的,我知道,这些查询应该被参数化。

于 2008-11-20T19:59:48.010 回答
0

Michael——在 IP21 中有一个“插值”表,以及“实际”数据点表。Proficy也有吗?

于 2010-02-01T02:46:26.377 回答
0

我们编写了一个如下所示的包装 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
            );
            ....

一些注意事项是,iFIX 将检查 DLL 是否在启动时加载,因此您需要执行动态加载/卸载 DLL 等操作,以免其他应用程序崩溃。我们通过动态删除/添加注册表项来做到这一点。

另一种情况是,如果您轮询 10,000 个样本并且其中 1 个样本已损坏,它将丢弃所有 10,000 个样本。您需要实现一个不良数据处理程序,该处理程序将从不良数据的任一侧开始并逐步递增以获取不良样本任一侧的所有数据。

有几个 C 头文件包含所有错误代码和 DLL 的函数头。

于 2016-07-25T03:19:18.577 回答