如何从 Proficy Historian 查询原始数据?
题
如何从 Proficy Historian/iHistorian 检索原始时间序列数据?
理想情况下,我会要求提供两个日期之间特定标签的数据。
解决方案
您可以尝试多种不同的采样模式。
- 生的
- 插值
- 实验室
- 趋势
- 已计算
使用以下所有 API 均可使用这些模式。
- 用户API (ihuapi.dll)
- SDK(ihsdk.dll)
- OLEDB (iholedb.dll)
- 客户端访问 API (Proficy.Historian.ClientAccess.API)
其中趋势采样模式可能是您想要的,因为它是专门为图表/趋势设计的。不过,实验室和插值也可能有用。
请阅读电子书以获取有关每种采样模式的更多信息。在我的机器上它存储为 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'
使用 User API 和 SDK 显示等效方法很复杂(使用 User API 更是如此),因为它们需要代码中的大量管道才能进行设置。客户端访问 API 较新,并在幕后使用 WCF。
顺便说一下,OLEDB 方法有一些限制。
- 尽管文档说我有 绝不 能够使本机查询参数发挥作用。例如,如果您想将其与 SQL Server Reporting Services 一起使用,那么这将是一个令人惊叹的问题。
- 您不能将示例写入存档或以任何方式更改 Historian 配置,包括添加/更改标签、写入消息等。
- 在某些情况下可能会有点慢。
- 它没有提供将多个标记名交叉连接到列中,然后进行样本以便每个时间戳和标记组合都存在一个值的规定。趋势采样模式可以让您完成一半,但仍然不会交叉制表,并且不会实际加载原始样本。但用户 API 和 SDK 也无法做到这一点。
其他提示
我的一位同事把它放在一起:
在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的开头或结尾附近更新,则趋势看起来会很糟糕。更糟糕的是,仍然是在请求的窗口期间没有明确点的情况 - 我们没有得到任何数据。
我通过三个查询解决了这个问题:
- 之前的值 开始日期
- 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];
}
}
是的,我知道,这些查询应该参数化。
我们编写了一个如下所示的包装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个样本并且其中一个样本已损坏,则会丢弃所有10,000个样本。您需要实现一个错误的数据处理程序,该处理程序将从错误数据的任一侧开始,并逐步递增以获取错误样本的任何一侧的所有数据。
有几个C头文件包含DLL的所有错误代码和函数头。