Как получить исходные данные временного ряда из Proficy Historian/iHistorian?
В идеале я бы запросил данные для определенного тега между двумя датами.
Как получить исходные данные временного ряда из Proficy Historian/iHistorian?
В идеале я бы запросил данные для определенного тега между двумя датами.
Существует несколько различных режимов выборки, с которыми вы можете экспериментировать.
Эти режимы доступны с использованием всех следующих API.
Из этих режимов выборки тренда, вероятно, вы хотите, так как он специально разработан для составления диаграмм/трендов. Хотя лабораторные и интерполированные могут быть полезны.
Прочтите электронную книгу для получения дополнительной информации о каждом режиме выборки. На моей машине он хранится как C:\Program Files\GE Fanuc\Proficy Historian\Docs\iHistorian.chm
, и у меня установлена версия 3.5. Обратите особое внимание на следующие разделы.
Вот как вы можете построить 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.
Мой коллега положил это вместе:
В 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, тенденции выглядели бы плохо. Хуже того, все еще были случаи, когда в течение запрошенного окна не было явных точек - мы не возвращаем данные.
Я решил это, выполнив три запроса:
Это потенциально неэффективный способ сделать это, но он работает:
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];
}
}
И да, я знаю, эти запросы должны быть параметризованы.
Майкл - в IP21 есть таблица "Интерполированная", а также таблица фактических данных. Имеет ли Proficy это также?
Мы написали DLL-оболочку, которая выглядела так:
[DllImport("IHUAPI.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "[email protected]")]
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.