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

C# 如何从Proficy Historian查询原始数据?,c#,oledb,proficy,historian,C#,Oledb,Proficy,Historian,如何从Proficy Historian/iHistorian检索原始时间序列数据 理想情况下,我会要求提供两个日期之间某个特定标签的数据。我的一位同事将这些数据放在一起: 在web.config中: <add name="HistorianConnectionString" providerName="ihOLEDB.iHistorian.1" connectionString=" Provider=ihOLEDB.iHistorian;

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


理想情况下,我会要求提供两个日期之间某个特定标签的数据。

我的一位同事将这些数据放在一起:

在web.config中:

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

更新:

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

我通过提出三个问题来解决这个问题:

  • 开始日期之前的上一个值
  • 开始日期和结束日期之间的点
  • 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];
        }
    }
    

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

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

    有几种不同的采样模式可供您尝试

    • 生的
    • 插入
    • 实验室
    • 趋势
    • 算计
    这些模式可使用以下所有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版。请特别注意以下部分

    • 使用OLEDB提供程序
    • 高级主题|检索
    下面是如何构造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也不能这样做

    我们编写了一个包装器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之类的操作,以便其他应用程序不会崩溃。我们通过动态删除/添加注册表项来实现这一点

    另一个是,如果您轮询10000个样本,并且其中1个样本已损坏,则会删除所有10000个样本。您需要实现一个坏数据处理程序,该处理程序将从坏数据的任一侧开始,并逐步递增,以获取坏样本任一侧的所有数据


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

    @reallyJim我相信它们是的,不过,在我的情况下,我需要原始数据。几年前,我在PI系统中遇到了同样的问题,所以我确实感受到了你的痛苦!我知道有点晚,但迟做总比不做好。另外,回答关于SO的老问题也是我的风格。我现在没有时间来测试,但这正是我开始时想要的。谢谢你能顺便添加一个关于电子书通常位于何处的注释吗?关于如何以这种方式使用诸如PreviousValue之类的内置historian函数,你有什么想法吗?oledb能做到这一点吗?类似于选择“timestamp,PreviousValue(Time,tagname)from…”的内容@roviuser:我不这么认为,但是发布另一个问题,并清楚地描述您想要什么,我可能会提供帮助。看看我的答案BrianOLE非常慢。使用API具有更好的性能,您还可以编写扩展方法,使您能够完成比OLE开箱即用所支持的更多的工作。对于不获取样本,您可以使用“ReadSamplesByCount”代替时间,这样您至少可以获取1个样本。如果样本数量不多,则该样本可能在您希望显示的时间段之后数年。
    [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
                );
                ....