Как выполнить запрос необработанных данных из Proficy Historian?

Как получить исходные данные временного ряда из Proficy Historian/iHistorian?

В идеале я бы запросил данные для определенного тега между двумя датами.

Ответ 1

Существует несколько различных режимов выборки, с которыми вы можете экспериментировать.

  • Сырье
  • Интерполированное
  • Лаборатория
  • Trend
  • Рассчитано

Эти режимы доступны с использованием всех следующих API.

  • Пользовательский API (ihuapi.dll)
  • SDK (ihsdk.dll)
  • OLEDB (iholedb.dll)
  • API Client Acess (Proficy.Historian.ClientAccess.API)

Из этих режимов выборки тренда, вероятно, вы хотите, так как он специально разработан для составления диаграмм/трендов. Хотя лабораторные и интерполированные могут быть полезны.

Прочтите электронную книгу для получения дополнительной информации о каждом режиме выборки. На моей машине он хранится как C:\Program Files\GE Fanuc\Proficy Historian\Docs\iHistorian.chm, и у меня установлена ​​версия 3.5. Обратите особое внимание на следующие разделы.

  • Использование поставщика OLE DB Historian
  • Расширенные темы | Индексирование

Вот как вы можете построить 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.

  • Несмотря на то, что в документации говорится, что я никогда не мог заставить собственные параметры запроса работать. Это showstopper, если вы хотите использовать его с SQL Server Reporting Services, например.
  • Вы не можете записывать образцы в архив или каким-либо образом вносить изменения в конфигурацию Historian, включая добавление/изменение тегов, запись сообщений и т.д.
  • В некоторых случаях это может быть немного медленнее.
  • Он не предусматривает кросс-табулирование нескольких тэгов в столбцы, а затем переносит образцы, чтобы существовало значение для каждой метки времени и комбинации тегов. Режим выборки трендов получает вас на полпути, но все же не кросс-таблицы и на самом деле не загружают необработанные образцы. Затем пользовательский API и SDK тоже не могут этого сделать.

Ответ 2

Мой коллега положил это вместе:

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

И да, я знаю, эти запросы должны быть параметризованы.

Ответ 3

Майкл - в IP21 есть таблица "Интерполированная", а также таблица фактических данных. Имеет ли Proficy это также?

Ответ 4

Мы написали 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.