LCOV - code coverage report
Current view: top level - src - history.cxx (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 1735 0
Test Date: 2025-11-11 10:26:08 Functions: 0.0 % 51 0

            Line data    Source code
       1              : /********************************************************************\
       2              : 
       3              :   Name:         HISTORY.C
       4              :   Created by:   Stefan Ritt
       5              : 
       6              :   Contents:     MIDAS history functions
       7              : 
       8              :   $Id$
       9              : 
      10              : \********************************************************************/
      11              : 
      12              : #undef NDEBUG // midas required assert() to be always enabled
      13              : 
      14              : #include <assert.h>
      15              : #include <math.h> // sqrt()
      16              : #include <stdio.h>
      17              : #include <stdlib.h>
      18              : #include <string.h>
      19              : #include <assert.h>
      20              : #include <map>
      21              : 
      22              : #include "midas.h"
      23              : #include "msystem.h"
      24              : #include "mstrlcpy.h"
      25              : #include "history.h"
      26              : 
      27              : /** @defgroup hsfunctioncode Midas History Functions (hs_xxx)
      28              :  */
      29              : 
      30              : /**dox***************************************************************/
      31              : /** @addtogroup hsfunctioncode
      32              :  *
      33              :  *  @{  */
      34              : 
      35              : #if !defined(OS_VXWORKS)
      36              : /********************************************************************\
      37              : *                                                                    *
      38              : *                 History functions                                  *
      39              : *                                                                    *
      40              : \********************************************************************/
      41              : 
      42              : /**dox***************************************************************/
      43              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
      44              : 
      45              : static std::vector<HISTORY*> _history;
      46              : static std::string _hs_path_name;
      47              : 
      48              : /**dox***************************************************************/
      49              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
      50              : 
      51            0 : static bool xwrite(const std::string& fn, int fh, const void *buf, size_t count)
      52              : {
      53            0 :    ssize_t wr = write(fh, buf, count);
      54              : 
      55            0 :    if (wr < 0) {
      56            0 :       cm_msg(MERROR, "xwrite", "Error writing %d bytes to file \"%s\" errno %d (%s)", (int)count, fn.c_str(), errno, strerror(errno));
      57            0 :       return false;
      58              :    }
      59              : 
      60            0 :    if ((size_t)wr != count) {
      61            0 :       cm_msg(MERROR, "xwrite", "Error writing %d bytes to file \"%s\", short write %d bytes", (int)count, fn.c_str(), (int)wr);
      62            0 :       return false;
      63              :    }
      64              : 
      65            0 :    return true;
      66              : }
      67              : 
      68              : // xread() return value:
      69              : // 1 = ok
      70              : // 0 = expected EOF, if eof_ok == true
      71              : // -1 = error or short read or unexpected eof
      72              : 
      73            0 : static int xread(const std::string& fn, int fh, void *buf, size_t count, bool eof_ok = false)
      74              : {
      75            0 :    ssize_t rd = read(fh, buf, count);
      76              : 
      77            0 :    if (rd < 0) {
      78            0 :       cm_msg(MERROR, "xread", "Error reading from file \"%s\" errno %d (%s)", fn.c_str(), errno, strerror(errno));
      79            0 :       return -1;
      80              :    }
      81              : 
      82            0 :    if (rd == 0) {
      83            0 :       if (eof_ok)
      84            0 :          return 0;
      85            0 :       cm_msg(MERROR, "xread", "Error: Unexpected end-of-file when reading file \"%s\"", fn.c_str());
      86            0 :       return -1;
      87              :    }
      88              : 
      89            0 :    if ((size_t)rd != count) {
      90            0 :       cm_msg(MERROR, "xread", "Error: Truncated read from file \"%s\", requested %d bytes, read %d bytes", fn.c_str(), (int)count, (int)rd);
      91            0 :       return -1;
      92              :    }
      93              : 
      94            0 :    return 1;
      95              : }
      96              : 
      97            0 : static bool xseek(const std::string& fn, int fh, DWORD pos)
      98              : {
      99            0 :    off_t off = lseek(fh, pos, SEEK_SET);
     100              : 
     101            0 :    if (off < 0) {
     102            0 :       cm_msg(MERROR, "xseek", "Error in lseek(%llu, SEEK_SET) for file \"%s\", errno %d (%s)", (unsigned long long)pos, fn.c_str(), errno, strerror(errno));
     103            0 :       return false;
     104              :    }
     105              : 
     106            0 :    return true;
     107              : }
     108              : 
     109            0 : static bool xseek_end(const std::string& fn, int fh)
     110              : {
     111            0 :    off_t off = lseek(fh, 0, SEEK_END);
     112              : 
     113            0 :    if (off < 0) {
     114            0 :       cm_msg(MERROR, "xseek_end", "Error in lseek(SEEK_END) to end-of-file for file \"%s\", errno %d (%s)", fn.c_str(), errno, strerror(errno));
     115            0 :       return false;
     116              :    }
     117              : 
     118            0 :    return true;
     119              : }
     120              : 
     121            0 : static bool xseek_cur(const std::string& fn, int fh, int offset)
     122              : {
     123            0 :    off_t off = lseek(fh, offset, SEEK_CUR);
     124              : 
     125            0 :    if (off < 0) {
     126            0 :       cm_msg(MERROR, "xseek_cur", "Error in lseek(%d, SEEK_CUR) for file \"%s\", errno %d (%s)", offset, fn.c_str(), errno, strerror(errno));
     127            0 :       return false;
     128              :    }
     129              : 
     130            0 :    return true;
     131              : }
     132              : 
     133            0 : static DWORD xcurpos(const std::string& fn, int fh)
     134              : {
     135            0 :    off_t off = lseek(fh, 0, SEEK_CUR);
     136              : 
     137            0 :    if (off < 0) {
     138            0 :       cm_msg(MERROR, "xcurpos", "Error in lseek(0, SEEK_CUR) for file \"%s\", errno %d (%s)", fn.c_str(), errno, strerror(errno));
     139            0 :       return -1;
     140              :    }
     141              : 
     142            0 :    DWORD dw = off;
     143              : 
     144            0 :    if (dw != off) {
     145            0 :       cm_msg(MERROR, "xcurpos", "Error: lseek(0, SEEK_CUR) for file \"%s\" returned value %llu does not fir into a DWORD, maybe file is bigger than 2GiB or 4GiB", fn.c_str(), (unsigned long long)off);
     146            0 :       return -1;
     147              :    }
     148              : 
     149            0 :    return dw;
     150              : }
     151              : 
     152            0 : static bool xtruncate(const std::string& fn, int fh, DWORD pos)
     153              : {
     154            0 :    off_t off = lseek(fh, pos, SEEK_SET);
     155              : 
     156            0 :    if (off < 0) {
     157            0 :       cm_msg(MERROR, "xtruncate", "Error in lseek(%llu) for file \"%s\", errno %d (%s)", (unsigned long long)pos, fn.c_str(), errno, strerror(errno));
     158            0 :       return false;
     159              :    }
     160              : 
     161            0 :    int status = ftruncate(fh, pos);
     162              : 
     163            0 :    if (status != 0) {
     164            0 :       cm_msg(MERROR, "xtruncate", "Error setting file size of \"%s\" to %llu, errno %d (%s)", fn.c_str(), (unsigned long long)pos, errno, strerror(errno));
     165            0 :       return false;
     166              :    }
     167              : 
     168            0 :    return true;
     169              : }
     170              : 
     171              : /********************************************************************/
     172              : /**
     173              : Sets the path for future history file accesses. Should
     174              : be called before any other history function is called.
     175              : @param path             Directory where history files reside
     176              : @return HS_SUCCESS
     177              : */
     178            0 : static INT hs_set_path(const char *path)
     179              : {
     180            0 :    assert(path);
     181            0 :    assert(path[0] != 0);
     182              :    
     183            0 :    _hs_path_name = path;
     184              : 
     185              :    /* check for trailing directory seperator */
     186            0 :    if (_hs_path_name.back() != DIR_SEPARATOR)
     187            0 :       _hs_path_name += DIR_SEPARATOR_STR;
     188              : 
     189            0 :    return HS_SUCCESS;
     190              : }
     191              : 
     192              : /**dox***************************************************************/
     193              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
     194              : 
     195              : /********************************************************************/
     196              : 
     197              : /**
     198              : Open history file belonging to certain date. Internal use
     199              :            only.
     200              : @param ltime          Date for which a history file should be opened.
     201              : @param suffix         File name suffix like "hst", "idx", "idf"
     202              : @param mode           R/W access mode
     203              : @param fh             File handle
     204              : @return HS_SUCCESS
     205              : */
     206            0 : static INT hs_open_file(time_t ltime, const char *suffix, INT mode, std::string *pfile_name, int *fh)
     207              : {
     208              :    struct tm tms;
     209              :    time_t ttime;
     210              : 
     211              :    /* generate new file name YYMMDD.xxx */
     212            0 :    ss_tzset(); // required by localtime_r()
     213            0 :    ttime = (time_t) ltime;
     214            0 :    localtime_r(&ttime, &tms);
     215              : 
     216              :    //sprintf(file_name, "%s%02d%02d%02d.%s", _hs_path_name, tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday, suffix);
     217            0 :    std::string file_name;
     218            0 :    file_name += _hs_path_name;
     219              :    char tmp[100];
     220            0 :    sprintf(tmp, "%02d%02d%02d", tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday);
     221            0 :    file_name += tmp;
     222            0 :    file_name += ".";
     223            0 :    file_name += suffix;
     224              : 
     225            0 :    if (pfile_name)
     226            0 :       *pfile_name = file_name;
     227              : 
     228              :    /* open file, add O_BINARY flag for Windows NT */
     229            0 :    *fh = open(file_name.c_str(), mode | O_BINARY, 0644);
     230              : 
     231              :    //printf("hs_open_file: time %d, file \'%s\', fh %d\n", (int)ltime, file_name.c_str(), *fh);
     232              : 
     233            0 :    return HS_SUCCESS;
     234            0 : }
     235              : 
     236              : /********************************************************************/
     237            0 : static INT hs_gen_index(DWORD ltime)
     238              : /********************************************************************\
     239              : 
     240              :   Routine: hs_gen_index
     241              : 
     242              :   Purpose: Regenerate index files ("idx" and "idf" files) for a given
     243              :            history file ("hst"). Interal use only.
     244              : 
     245              :   Input:
     246              :     time_t ltime            Date for which a history file should
     247              :                             be analyzed.
     248              : 
     249              :   Output:
     250              :     none
     251              : 
     252              :   Function value:
     253              :     HS_SUCCESS              Successful completion
     254              :     HS_FILE_ERROR           Index files cannot be created
     255              : 
     256              : \********************************************************************/
     257              : {
     258              :    char event_name[NAME_LENGTH];
     259              :    int fh, fhd, fhi;
     260              :    HIST_RECORD rec;
     261              :    INDEX_RECORD irec;
     262              :    DEF_RECORD def_rec;
     263            0 :    int recovering = 0;
     264              :    //time_t now = time(NULL);
     265              : 
     266            0 :    cm_msg(MINFO, "hs_gen_index", "generating index files for time %d", (int) ltime);
     267            0 :    printf("Recovering index files...\n");
     268              : 
     269            0 :    if (ltime == 0)
     270            0 :       ltime = (DWORD) time(NULL);
     271              : 
     272            0 :    std::string fni;
     273            0 :    std::string fnd;
     274            0 :    std::string fn;
     275              : 
     276              :    /* open new index file */
     277            0 :    hs_open_file(ltime, "idx", O_RDWR | O_CREAT | O_TRUNC, &fni, &fhi);
     278            0 :    hs_open_file(ltime, "idf", O_RDWR | O_CREAT | O_TRUNC, &fnd, &fhd);
     279              : 
     280            0 :    if (fhd < 0 || fhi < 0) {
     281            0 :       cm_msg(MERROR, "hs_gen_index", "cannot create index file");
     282            0 :       return HS_FILE_ERROR;
     283              :    }
     284              : 
     285              :    /* open history file */
     286            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
     287            0 :    if (fh < 0)
     288            0 :       return HS_FILE_ERROR;
     289            0 :    xseek(fn, fh, 0);
     290              : 
     291              :    /* loop over file records in .hst file */
     292              :    do {
     293            0 :       if (xread(fn, fh, (char *) &rec, sizeof(rec), true) <= 0) {
     294            0 :          break;
     295              :       }
     296              : 
     297              :       /* check if record type is definition */
     298            0 :       if (rec.record_type == RT_DEF) {
     299              :          /* read name */
     300            0 :          if (xread(fn, fh, event_name, sizeof(event_name)) < 0)
     301            0 :             return HS_FILE_ERROR;
     302              : 
     303            0 :          printf("Event definition %s, ID %d\n", event_name, rec.event_id);
     304              : 
     305              :          /* write definition index record */
     306            0 :          def_rec.event_id = rec.event_id;
     307            0 :          memcpy(def_rec.event_name, event_name, sizeof(event_name));
     308            0 :          DWORD pos = xcurpos(fn, fh);
     309            0 :          if (pos == (DWORD)-1) return HS_FILE_ERROR;
     310            0 :          def_rec.def_offset = pos - sizeof(event_name) - sizeof(rec);
     311            0 :          xwrite(fnd, fhd, (char *) &def_rec, sizeof(def_rec));
     312              : 
     313              :          //printf("data def at %d (age %d)\n", rec.time, now-rec.time);
     314              : 
     315              :          /* skip tags */
     316            0 :          xseek_cur(fn, fh, rec.data_size);
     317            0 :       } else if (rec.record_type == RT_DATA && rec.data_size > 1 && rec.data_size < 1 * 1024 * 1024) {
     318              :          /* write index record */
     319            0 :          irec.event_id = rec.event_id;
     320            0 :          irec.time = rec.time;
     321            0 :          DWORD pos = xcurpos(fn, fh);
     322            0 :          if (pos == (DWORD)-1) return HS_FILE_ERROR;
     323            0 :          irec.offset = pos - sizeof(rec);
     324            0 :          xwrite(fni, fhi, (char *) &irec, sizeof(irec));
     325              : 
     326              :          //printf("data rec at %d (age %d)\n", rec.time, now-rec.time);
     327              : 
     328              :          /* skip data */
     329            0 :          xseek_cur(fn, fh, rec.data_size);
     330            0 :       } else {
     331            0 :          if (!recovering)
     332            0 :             cm_msg(MERROR, "hs_gen_index", "broken history file for time %d, trying to recover", (int) ltime);
     333              : 
     334            0 :          recovering = 1;
     335            0 :          xseek_cur(fn, fh, 1 - (int)sizeof(rec));
     336              : 
     337            0 :          continue;
     338              :       }
     339              : 
     340            0 :    } while (TRUE);
     341              : 
     342            0 :    close(fh);
     343            0 :    close(fhi);
     344            0 :    close(fhd);
     345              : 
     346            0 :    printf("...done.\n");
     347              : 
     348            0 :    return HS_SUCCESS;
     349            0 : }
     350              : 
     351              : 
     352              : /********************************************************************/
     353            0 : static INT hs_search_file(DWORD * ltime, INT direction)
     354              : /********************************************************************\
     355              : 
     356              :   Routine: hs_search_file
     357              : 
     358              :   Purpose: Search an history file for a given date. If not found,
     359              :            look for files after date (direction==1) or before date
     360              :            (direction==-1) up to one year.
     361              : 
     362              :   Input:
     363              :     DWORD  *ltime           Date of history file
     364              :     INT    direction        Search direction
     365              : 
     366              :   Output:
     367              :     DWORD  *ltime           Date of history file found
     368              : 
     369              :   Function value:
     370              :     HS_SUCCESS              Successful completion
     371              :     HS_FILE_ERROR           No file found
     372              : 
     373              : \********************************************************************/
     374              : {
     375              :    time_t lt;
     376              :    int fh, fhd, fhi;
     377            0 :    std::string fn;
     378              : 
     379            0 :    ss_tzset(); // required by localtime_r()
     380              : 
     381            0 :    if (*ltime == 0)
     382            0 :       *ltime = ss_time();
     383              : 
     384            0 :    lt = (time_t) * ltime;
     385              :    do {
     386              :       /* try to open history file for date "lt" */
     387            0 :       hs_open_file(lt, "hst", O_RDONLY, &fn, &fh);
     388              : 
     389              :       /* if not found, look for next day */
     390            0 :       if (fh < 0)
     391            0 :          lt += direction * 3600 * 24;
     392              : 
     393              :       /* stop if more than a year before starting point or in the future */
     394            0 :    } while (fh < 0 && (INT) * ltime - (INT) lt < 3600 * 24 * 365 && lt <= (time_t) ss_time());
     395              : 
     396            0 :    if (fh < 0)
     397            0 :       return HS_FILE_ERROR;
     398              : 
     399            0 :    if (lt != (time_t) *ltime) {
     400              :       /* if switched to new day, set start_time to 0:00 */
     401              :       struct tm tms;
     402            0 :       localtime_r(&lt, &tms);
     403            0 :       tms.tm_hour = tms.tm_min = tms.tm_sec = 0;
     404            0 :       *ltime = (DWORD) ss_mktime(&tms);
     405              :    }
     406              : 
     407              :    /* check if index files are there */
     408            0 :    hs_open_file(*ltime, "idf", O_RDONLY, NULL, &fhd);
     409            0 :    hs_open_file(*ltime, "idx", O_RDONLY, NULL, &fhi);
     410              : 
     411            0 :    if (fh > 0)
     412            0 :       close(fh);
     413            0 :    if (fhd > 0)
     414            0 :       close(fhd);
     415            0 :    if (fhi > 0)
     416            0 :       close(fhi);
     417              : 
     418              :    /* generate them if not */
     419            0 :    if (fhd < 0 || fhi < 0)
     420            0 :       hs_gen_index(*ltime);
     421              : 
     422            0 :    return HS_SUCCESS;
     423            0 : }
     424              : 
     425              : 
     426              : /********************************************************************/
     427            0 : static INT hs_define_event(DWORD event_id, const char *name, const TAG * tag, DWORD size)
     428              : /********************************************************************\
     429              : 
     430              :   Routine: hs_define_event
     431              : 
     432              :   Purpose: Define a new event for which a history should be recorded.
     433              :            This routine must be called before any call to
     434              :            hs_write_event. It also should be called if the definition
     435              :            of the event has changed.
     436              : 
     437              :            The event definition is written directly to the history
     438              :            file. If the definition is identical to a previous
     439              :            definition, it is not written to the file.
     440              : 
     441              : 
     442              :   Input:
     443              :     DWORD  event_id         ID for this event. Must be unique.
     444              :     char   name             Name of this event
     445              :     TAG    tag              Tag list containing names and types of
     446              :                             variables in this event.
     447              :     DWORD  size             Size of tag array
     448              : 
     449              :   Output:
     450              :     <none>
     451              : 
     452              :   Function value:
     453              :     HS_SUCCESS              Successful completion
     454              :     HS_NO_MEMEORY           Out of memory
     455              :     HS_FILE_ERROR           Cannot open history file
     456              : 
     457              : \********************************************************************/
     458              : {
     459              :    {
     460              :       HIST_RECORD rec, prev_rec;
     461              :       DEF_RECORD def_rec;
     462              :       time_t ltime;
     463              :       char str[256], event_name[NAME_LENGTH], *buffer;
     464              :       int fh, fhi, fhd;
     465            0 :       std::string fn, fni, fnd;
     466              :       INT n, status, semaphore;
     467              : 
     468              :       //printf("hs_define_event: event_id %d, name [%s]\n", event_id, name);
     469              : 
     470              :       /* request semaphore */
     471            0 :       cm_get_experiment_semaphore(NULL, NULL, &semaphore, NULL);
     472            0 :       status = ss_semaphore_wait_for(semaphore, 5 * 1000);
     473            0 :       if (status != SS_SUCCESS)
     474            0 :          return SUCCESS;        /* someone else blocked the history system */
     475              : 
     476              :       /* allocate new space for the new history descriptor */
     477              :       /* check if history already open */
     478            0 :       int index = -1;
     479            0 :       for (unsigned i = 0; i < _history.size(); i++)
     480            0 :          if (_history[i]->event_id == event_id) {
     481            0 :             index = i;
     482            0 :             break;
     483              :          }
     484              : 
     485              :       /* if not found, create new one */
     486            0 :       if (index < 0) {
     487            0 :          index = _history.size();
     488            0 :          _history.push_back(new HISTORY);
     489              :       }
     490              : 
     491              :       /* assemble definition record header */
     492            0 :       rec.record_type = RT_DEF;
     493            0 :       rec.event_id = event_id;
     494            0 :       rec.time = (DWORD) time(NULL);
     495            0 :       rec.data_size = size;
     496            0 :       mstrlcpy(event_name, name, NAME_LENGTH);
     497              : 
     498              :       /* if history structure not set up, do so now */
     499            0 :       if (!_history[index]->hist_fh) {
     500              :          /* open history file */
     501            0 :          hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fn, &fh);
     502            0 :          if (fh < 0) {
     503            0 :             ss_semaphore_release(semaphore);
     504            0 :             return HS_FILE_ERROR;
     505              :          }
     506              : 
     507              :          /* open index files */
     508            0 :          hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fnd, &fhd);
     509            0 :          hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fni, &fhi);
     510            0 :          xseek_end(fn, fh);
     511            0 :          xseek_end(fni, fhi);
     512            0 :          xseek_end(fnd, fhd);
     513              : 
     514            0 :          DWORD fh_pos = xcurpos(fn, fh);
     515            0 :          DWORD fhd_pos = xcurpos(fnd, fhd);
     516              : 
     517            0 :          if (fh_pos == (DWORD)-1) return HS_FILE_ERROR;
     518            0 :          if (fhd_pos == (DWORD)-1) return HS_FILE_ERROR;
     519              : 
     520              :          /* regenerate index if missing */
     521            0 :          if (fh_pos > 0 && fhd_pos == 0) {
     522            0 :             close(fh);
     523            0 :             close(fhi);
     524            0 :             close(fhd);
     525            0 :             hs_gen_index(rec.time);
     526            0 :             hs_open_file(rec.time, "hst", O_RDWR, &fn, &fh);
     527            0 :             hs_open_file(rec.time, "idx", O_RDWR, &fni, &fhi);
     528            0 :             hs_open_file(rec.time, "idf", O_RDWR, &fnd, &fhd);
     529            0 :             xseek_end(fn, fh);
     530            0 :             xseek_end(fni, fhi);
     531            0 :             xseek_end(fnd, fhd);
     532              :          }
     533              : 
     534            0 :          ltime = (time_t) rec.time;
     535              :          struct tm tmb;
     536            0 :          localtime_r(&ltime, &tmb);
     537            0 :          tmb.tm_hour = tmb.tm_min = tmb.tm_sec = 0;
     538              : 
     539            0 :          DWORD pos = xcurpos(fn, fh);
     540            0 :          if (pos == (DWORD)-1) return HS_FILE_ERROR;
     541              : 
     542              :          /* setup history structure */
     543            0 :          _history[index]->hist_fn = fn;
     544            0 :          _history[index]->index_fn = fni;
     545            0 :          _history[index]->def_fn = fnd;
     546            0 :          _history[index]->hist_fh = fh;
     547            0 :          _history[index]->index_fh = fhi;
     548            0 :          _history[index]->def_fh = fhd;
     549            0 :          _history[index]->def_offset = pos;
     550            0 :          _history[index]->event_id = event_id;
     551            0 :          _history[index]->event_name = event_name;
     552            0 :          _history[index]->base_time = (DWORD) ss_mktime(&tmb);
     553            0 :          _history[index]->n_tag = size / sizeof(TAG);
     554            0 :          _history[index]->tag = (TAG *) M_MALLOC(size);
     555            0 :          memcpy(_history[index]->tag, tag, size);
     556              : 
     557              :          /* search previous definition */
     558            0 :          fhd_pos = xcurpos(fnd, fhd);
     559            0 :          if (fhd_pos == (DWORD)-1) return HS_FILE_ERROR;
     560            0 :          n = fhd_pos / sizeof(def_rec);
     561            0 :          def_rec.event_id = 0;
     562            0 :          for (int i = n - 1; i >= 0; i--) {
     563            0 :             if (!xseek(fnd, fhd, i * sizeof(def_rec))) return HS_FILE_ERROR;
     564            0 :             if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec)) < 0) return HS_FILE_ERROR;
     565            0 :             if (def_rec.event_id == event_id)
     566            0 :                break;
     567              :          }
     568            0 :          xseek_end(fnd, fhd);
     569              : 
     570              :          /* if definition found, compare it with new one */
     571            0 :          if (def_rec.event_id == event_id) {
     572            0 :             buffer = (char *) M_MALLOC(size);
     573            0 :             memset(buffer, 0, size);
     574              : 
     575            0 :             xseek(fn, fh, def_rec.def_offset);
     576            0 :             xread(fn, fh, (char *) &prev_rec, sizeof(prev_rec));
     577            0 :             xread(fn, fh, str, NAME_LENGTH);
     578            0 :             xread(fn, fh, buffer, size);
     579            0 :             xseek_end(fn, fh);
     580              : 
     581            0 :             if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
     582              :                /* write definition to history file */
     583            0 :                xwrite(fn, fh, (char *) &rec, sizeof(rec));
     584            0 :                xwrite(fn, fh, event_name, NAME_LENGTH);
     585            0 :                xwrite(fn, fh, (char *) tag, size);
     586              : 
     587              :                /* write index record */
     588            0 :                def_rec.event_id = event_id;
     589            0 :                memcpy(def_rec.event_name, event_name, sizeof(event_name));
     590            0 :                def_rec.def_offset = _history[index]->def_offset;
     591            0 :                xwrite(fnd, fhd, (char *) &def_rec, sizeof(def_rec));
     592              :             } else
     593              :                /* definition identical, just remember old offset */
     594            0 :                _history[index]->def_offset = def_rec.def_offset;
     595              : 
     596            0 :             M_FREE(buffer);
     597              :          } else {
     598              :             /* write definition to history file */
     599            0 :             xwrite(fn, fh, (char *) &rec, sizeof(rec));
     600            0 :             xwrite(fn, fh, event_name, NAME_LENGTH);
     601            0 :             xwrite(fn, fh, (char *) tag, size);
     602              : 
     603              :             /* write definition index record */
     604            0 :             def_rec.event_id = event_id;
     605            0 :             memcpy(def_rec.event_name, event_name, sizeof(event_name));
     606            0 :             def_rec.def_offset = _history[index]->def_offset;
     607            0 :             xwrite(fn, fhd, (char *) &def_rec, sizeof(def_rec));
     608              :          }
     609              :       } else {
     610            0 :          fn = _history[index]->hist_fn;
     611            0 :          fnd = _history[index]->def_fn;
     612            0 :          fh = _history[index]->hist_fh;
     613            0 :          fhd = _history[index]->def_fh;
     614              : 
     615              :          /* compare definition with previous definition */
     616            0 :          buffer = (char *) M_MALLOC(size);
     617            0 :          memset(buffer, 0, size);
     618              : 
     619            0 :          xseek(fn, fh, _history[index]->def_offset);
     620            0 :          xread(fn, fh, (char *) &prev_rec, sizeof(prev_rec));
     621            0 :          xread(fn, fh, str, NAME_LENGTH);
     622            0 :          xread(fn, fh, buffer, size);
     623              : 
     624            0 :          xseek_end(fn, fh);
     625            0 :          xseek_end(fnd, fhd);
     626              : 
     627            0 :          if (prev_rec.data_size != size || strcmp(str, event_name) != 0 || memcmp(buffer, tag, size) != 0) {
     628              :             /* save new definition offset */
     629            0 :             DWORD pos = xcurpos(fn, fh);
     630            0 :             if (pos == (DWORD)-1) return HS_FILE_ERROR;
     631            0 :             _history[index]->def_offset = pos;
     632              : 
     633              :             /* write definition to history file */
     634            0 :             xwrite(fn, fh, (char *) &rec, sizeof(rec));
     635            0 :             xwrite(fn, fh, event_name, NAME_LENGTH);
     636            0 :             xwrite(fn, fh, (char *) tag, size);
     637              : 
     638              :             /* write index record */
     639            0 :             def_rec.event_id = event_id;
     640            0 :             memcpy(def_rec.event_name, event_name, sizeof(event_name));
     641            0 :             def_rec.def_offset = _history[index]->def_offset;
     642            0 :             xwrite(fnd, fhd, (char *) &def_rec, sizeof(def_rec));
     643              :          }
     644              : 
     645            0 :          M_FREE(buffer);
     646              :       }
     647              : 
     648            0 :       ss_semaphore_release(semaphore);
     649            0 :    }
     650              : 
     651            0 :    return HS_SUCCESS;
     652              : }
     653              : 
     654              : 
     655              : /********************************************************************/
     656            0 : static INT hs_write_event(DWORD event_id, const void *data, DWORD size)
     657              : /********************************************************************\
     658              : 
     659              :   Routine: hs_write_event
     660              : 
     661              :   Purpose: Write an event to a history file.
     662              : 
     663              :   Input:
     664              :     DWORD  event_id         Event ID
     665              :     void   *data            Data buffer containing event
     666              :     DWORD  size             Data buffer size in bytes
     667              : 
     668              :   Output:
     669              :     none
     670              :                             future hs_write_event
     671              : 
     672              :   Function value:
     673              :     HS_SUCCESS              Successful completion
     674              :     HS_NO_MEMEORY           Out of memory
     675              :     HS_FILE_ERROR           Cannot write to history file
     676              :     HS_UNDEFINED_EVENT      Event was not defined via hs_define_event
     677              : 
     678              : \********************************************************************/
     679              : {
     680              :    HIST_RECORD rec, drec;
     681              :    DEF_RECORD def_rec;
     682              :    INDEX_RECORD irec;
     683              :    int fh, fhi, fhd;
     684              :    DWORD last_pos_data, last_pos_index;
     685            0 :    std::string fn, fni, fnd;
     686              :    INT semaphore;
     687              :    int status;
     688              :    struct tm tmb, tmr;
     689              :    time_t ltime;
     690              : 
     691              :    /* request semaphore */
     692            0 :    cm_get_experiment_semaphore(NULL, NULL, &semaphore, NULL);
     693            0 :    status = ss_semaphore_wait_for(semaphore, 5 * 1000);
     694            0 :    if (status != SS_SUCCESS) {
     695            0 :       cm_msg(MERROR, "hs_write_event", "semaphore timeout");
     696            0 :       return SUCCESS;           /* someone else blocked the history system */
     697              :    }
     698              : 
     699              :    /* find index to history structure */
     700            0 :    int index = -1;
     701            0 :    for (unsigned i = 0; i < _history.size(); i++)
     702            0 :       if (_history[i]->event_id == event_id) {
     703            0 :          index = i;
     704            0 :          break;
     705              :       }
     706            0 :    if (index < 0) {
     707            0 :       ss_semaphore_release(semaphore);
     708            0 :       return HS_UNDEFINED_EVENT;
     709              :    }
     710              : 
     711              :    /* assemble record header */
     712            0 :    rec.record_type = RT_DATA;
     713            0 :    rec.event_id = _history[index]->event_id;
     714            0 :    rec.time = (DWORD) time(NULL);
     715            0 :    rec.def_offset = _history[index]->def_offset;
     716            0 :    rec.data_size = size;
     717              : 
     718            0 :    irec.event_id = _history[index]->event_id;
     719            0 :    irec.time = rec.time;
     720              : 
     721              :    /* check if new day */
     722            0 :    ltime = (time_t) rec.time;
     723            0 :    localtime_r(&ltime, &tmr); // somebody must call tzset() before this.
     724            0 :    ltime = (time_t) _history[index]->base_time;
     725            0 :    localtime_r(&ltime, &tmb); // somebody must call tzset() before this.
     726              : 
     727            0 :    if (tmr.tm_yday != tmb.tm_yday) {
     728              :       /* close current history file */
     729            0 :       close(_history[index]->hist_fh);
     730            0 :       close(_history[index]->def_fh);
     731            0 :       close(_history[index]->index_fh);
     732              : 
     733              :       /* open new history file */
     734            0 :       hs_open_file(rec.time, "hst", O_CREAT | O_RDWR, &fn, &fh);
     735            0 :       if (fh < 0) {
     736            0 :          ss_semaphore_release(semaphore);
     737            0 :          return HS_FILE_ERROR;
     738              :       }
     739              : 
     740              :       /* open new index file */
     741            0 :       hs_open_file(rec.time, "idx", O_CREAT | O_RDWR, &fni, &fhi);
     742            0 :       if (fhi < 0) {
     743            0 :          ss_semaphore_release(semaphore);
     744            0 :          return HS_FILE_ERROR;
     745              :       }
     746              : 
     747              :       /* open new definition index file */
     748            0 :       hs_open_file(rec.time, "idf", O_CREAT | O_RDWR, &fnd, &fhd);
     749            0 :       if (fhd < 0) {
     750            0 :          ss_semaphore_release(semaphore);
     751            0 :          return HS_FILE_ERROR;
     752              :       }
     753              : 
     754            0 :       xseek_end(fn, fh);
     755            0 :       xseek_end(fni, fhi);
     756            0 :       xseek_end(fnd, fhd);
     757              : 
     758              :       /* remember new file handles */
     759            0 :       _history[index]->hist_fn = fn;
     760            0 :       _history[index]->index_fn = fni;
     761            0 :       _history[index]->def_fn = fnd;
     762              : 
     763            0 :       _history[index]->hist_fh = fh;
     764            0 :       _history[index]->index_fh = fhi;
     765            0 :       _history[index]->def_fh = fhd;
     766              : 
     767            0 :       _history[index]->def_offset = xcurpos(fn, fh);
     768            0 :       rec.def_offset = _history[index]->def_offset;
     769              : 
     770            0 :       tmr.tm_hour = tmr.tm_min = tmr.tm_sec = 0;
     771            0 :       _history[index]->base_time = (DWORD) ss_mktime(&tmr);
     772              : 
     773              :       /* write definition from _history structure */
     774            0 :       drec.record_type = RT_DEF;
     775            0 :       drec.event_id = _history[index]->event_id;
     776            0 :       drec.time = rec.time;
     777            0 :       drec.data_size = _history[index]->n_tag * sizeof(TAG);
     778              : 
     779            0 :       xwrite(fn, fh, (char *) &drec, sizeof(drec));
     780            0 :       xwrite(fn, fh, _history[index]->event_name.c_str(), NAME_LENGTH);
     781            0 :       xwrite(fn, fh, (char *) _history[index]->tag, drec.data_size);
     782              : 
     783              :       /* write definition index record */
     784            0 :       def_rec.event_id = _history[index]->event_id;
     785            0 :       memcpy(def_rec.event_name, _history[index]->event_name.c_str(), sizeof(def_rec.event_name));
     786            0 :       def_rec.def_offset = _history[index]->def_offset;
     787            0 :       xwrite(fnd, fhd, (char *) &def_rec, sizeof(def_rec));
     788              :    }
     789              : 
     790              :    /* go to end of file */
     791            0 :    xseek_end(_history[index]->hist_fn, _history[index]->hist_fh);
     792            0 :    last_pos_data = irec.offset = xcurpos(_history[index]->hist_fn, _history[index]->hist_fh);
     793              : 
     794              :    /* write record header */
     795            0 :    xwrite(_history[index]->hist_fn, _history[index]->hist_fh, (char *) &rec, sizeof(rec));
     796              : 
     797              :    /* write data */
     798            0 :    if (!xwrite(_history[index]->hist_fn, _history[index]->hist_fh, (char *) data, size)) {
     799              :       /* disk maybe full? Do a roll-back! */
     800            0 :       xtruncate(_history[index]->hist_fn, _history[index]->hist_fh, last_pos_data);
     801            0 :       ss_semaphore_release(semaphore);
     802            0 :       return HS_FILE_ERROR;
     803              :    }
     804              : 
     805              :    /* write index record */
     806            0 :    xseek_end(_history[index]->index_fn, _history[index]->index_fh);
     807            0 :    last_pos_index = xcurpos(_history[index]->index_fn, _history[index]->index_fh);
     808            0 :    int size_of_irec = sizeof(irec);
     809            0 :    if (!xwrite(_history[index]->index_fn, _history[index]->index_fh, (char *) &irec, size_of_irec)) {
     810              :       /* disk maybe full? Do a roll-back! */
     811            0 :       xtruncate(_history[index]->hist_fn, _history[index]->hist_fh, last_pos_data);
     812            0 :       xtruncate(_history[index]->index_fn, _history[index]->index_fh, last_pos_index);
     813            0 :       ss_semaphore_release(semaphore);
     814            0 :       return HS_FILE_ERROR;
     815              :    }
     816              : 
     817            0 :    ss_semaphore_release(semaphore);
     818            0 :    return HS_SUCCESS;
     819            0 : }
     820              : 
     821              : 
     822              : /********************************************************************/
     823            0 : static INT hs_enum_events(DWORD ltime, char *event_name, DWORD * name_size, INT event_id[], DWORD * id_size)
     824              : /********************************************************************\
     825              : 
     826              :   Routine: hs_enum_events
     827              : 
     828              :   Purpose: Enumerate events for a given date
     829              : 
     830              :   Input:
     831              :     DWORD  ltime            Date at which events should be enumerated
     832              : 
     833              :   Output:
     834              :     char   *event_name      Array containing event names
     835              :     DWORD  *name_size       Size of name array
     836              :     char   *event_id        Array containing event IDs
     837              :     DWORD  *id_size         Size of ID array
     838              : 
     839              :   Function value:
     840              :     HS_SUCCESS              Successful completion
     841              :     HS_NO_MEMEORY           Out of memory
     842              :     HS_FILE_ERROR           Cannot open history file
     843              : 
     844              : \********************************************************************/
     845              : {
     846              :    int fh, fhd;
     847            0 :    std::string fn, fnd;
     848              :    INT status, i, n;
     849              :    DEF_RECORD def_rec;
     850              : 
     851              :    /* search latest history file */
     852            0 :    status = hs_search_file(&ltime, -1);
     853            0 :    if (status != HS_SUCCESS) {
     854            0 :       cm_msg(MERROR, "hs_enum_events", "cannot find recent history file");
     855            0 :       return HS_FILE_ERROR;
     856              :    }
     857              : 
     858              :    /* open history and definition files */
     859            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
     860            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
     861            0 :    if (fh < 0 || fhd < 0) {
     862            0 :       cm_msg(MERROR, "hs_enum_events", "cannot open index files");
     863            0 :       return HS_FILE_ERROR;
     864              :    }
     865            0 :    xseek(fnd, fhd, 0);
     866              : 
     867              :    /* loop over definition index file */
     868            0 :    n = 0;
     869              :    do {
     870              :       /* read event definition */
     871            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec), true) <= 0)
     872            0 :          break;
     873              : 
     874              :       /* look for existing entry for this event id */
     875            0 :       for (i = 0; i < n; i++)
     876            0 :          if (event_id[i] == (INT) def_rec.event_id) {
     877            0 :             strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
     878            0 :             break;
     879              :          }
     880              : 
     881              :       /* new entry found */
     882            0 :       if (i == n) {
     883            0 :          if (((i * NAME_LENGTH) > ((INT) * name_size)) || ((i * sizeof(INT)) > (*id_size))) {
     884            0 :             cm_msg(MERROR, "hs_enum_events", "index buffer too small");
     885            0 :             close(fh);
     886            0 :             close(fhd);
     887            0 :             return HS_NO_MEMORY;
     888              :          }
     889              : 
     890              :          /* copy definition record */
     891            0 :          strcpy(event_name + i * NAME_LENGTH, def_rec.event_name);
     892            0 :          event_id[i] = def_rec.event_id;
     893            0 :          n++;
     894              :       }
     895              :    } while (TRUE);
     896              : 
     897            0 :    close(fh);
     898            0 :    close(fhd);
     899            0 :    *name_size = n * NAME_LENGTH;
     900            0 :    *id_size = n * sizeof(INT);
     901              : 
     902            0 :    return HS_SUCCESS;
     903            0 : }
     904              : 
     905              : 
     906              : /********************************************************************/
     907            0 : static INT hs_count_events(DWORD ltime, DWORD * count)
     908              : /********************************************************************\
     909              : 
     910              :   Routine: hs_count_events
     911              : 
     912              :   Purpose: Count number of different events for a given date
     913              : 
     914              :   Input:
     915              :     DWORD  ltime            Date at which events should be counted
     916              : 
     917              :   Output:
     918              :     DWORD  *count           Number of different events found
     919              : 
     920              :   Function value:
     921              :     HS_SUCCESS              Successful completion
     922              :     HS_FILE_ERROR           Cannot open history file
     923              : 
     924              : \********************************************************************/
     925              : {
     926              :    int fh, fhd;
     927            0 :    std::string fn, fnd;
     928              :    INT status, i, n;
     929              :    DWORD *id;
     930              :    DEF_RECORD def_rec;
     931              : 
     932              :    /* search latest history file */
     933            0 :    status = hs_search_file(&ltime, -1);
     934            0 :    if (status != HS_SUCCESS) {
     935            0 :       cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
     936            0 :       return HS_FILE_ERROR;
     937              :    }
     938              : 
     939              :    /* open history and definition files */
     940            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
     941            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
     942            0 :    if (fh < 0 || fhd < 0) {
     943            0 :       cm_msg(MERROR, "hs_count_events", "cannot open index files");
     944            0 :       return HS_FILE_ERROR;
     945              :    }
     946              : 
     947              :    /* allocate event id array */
     948            0 :    xseek_end(fnd, fhd);
     949            0 :    DWORD pos_fhd = xcurpos(fnd, fhd);
     950            0 :    if (pos_fhd == (DWORD)-1) return HS_FILE_ERROR;
     951            0 :    id = (DWORD *) M_MALLOC(pos_fhd/sizeof(def_rec) * sizeof(DWORD));
     952            0 :    xseek(fnd, fhd, 0);
     953              : 
     954              :    /* loop over index file */
     955            0 :    n = 0;
     956              :    do {
     957              :       /* read definition index record */
     958            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec), true) <= 0)
     959            0 :          break;
     960              : 
     961              :       /* look for existing entries */
     962            0 :       for (i = 0; i < n; i++)
     963            0 :          if (id[i] == def_rec.event_id)
     964            0 :             break;
     965              : 
     966              :       /* new entry found */
     967            0 :       if (i == n) {
     968            0 :          id[i] = def_rec.event_id;
     969            0 :          n++;
     970              :       }
     971              :    } while (TRUE);
     972              : 
     973              : 
     974            0 :    M_FREE(id);
     975            0 :    close(fh);
     976            0 :    close(fhd);
     977            0 :    *count = n;
     978              : 
     979            0 :    return HS_SUCCESS;
     980            0 : }
     981              : 
     982              : 
     983              : /********************************************************************/
     984            0 : static INT hs_get_event_id(DWORD ltime, const char *name, DWORD * id)
     985              : /********************************************************************\
     986              : 
     987              :   Routine: hs_get_event_id
     988              : 
     989              :   Purpose: Return event ID for a given name. If event cannot be found
     990              :            in current definition file, go back in time until found
     991              : 
     992              :   Input:
     993              :     DWORD  ltime            Date at which event ID should be looked for
     994              : 
     995              :   Output:
     996              :     DWORD  *id              Event ID
     997              : 
     998              :   Function value:
     999              :     HS_SUCCESS              Successful completion
    1000              :     HS_FILE_ERROR           Cannot open history file
    1001              :     HS_UNDEFINED_EVENT      Event "name" not found
    1002              : 
    1003              : \********************************************************************/
    1004              : {
    1005              :    int fh, fhd;
    1006            0 :    std::string fn, fnd;
    1007              :    INT status;
    1008              :    DWORD lt;
    1009              :    DEF_RECORD def_rec;
    1010              : 
    1011              :    /* search latest history file */
    1012            0 :    if (ltime == 0)
    1013            0 :       ltime = (DWORD) time(NULL);
    1014              : 
    1015            0 :    lt = ltime;
    1016              : 
    1017              :    do {
    1018            0 :       status = hs_search_file(&lt, -1);
    1019            0 :       if (status != HS_SUCCESS) {
    1020            0 :          cm_msg(MERROR, "hs_count_events", "cannot find recent history file");
    1021            0 :          return HS_FILE_ERROR;
    1022              :       }
    1023              : 
    1024              :       /* open history and definition files */
    1025            0 :       hs_open_file(lt, "hst", O_RDONLY, &fn, &fh);
    1026            0 :       hs_open_file(lt, "idf", O_RDONLY, &fnd, &fhd);
    1027            0 :       if (fh < 0 || fhd < 0) {
    1028            0 :          cm_msg(MERROR, "hs_count_events", "cannot open index files");
    1029            0 :          return HS_FILE_ERROR;
    1030              :       }
    1031              : 
    1032              :       /* loop over index file */
    1033            0 :       *id = 0;
    1034              :       do {
    1035              :          /* read definition index record */
    1036            0 :          if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec), true) <= 0)
    1037            0 :             break;
    1038              : 
    1039            0 :          if (strcmp(name, def_rec.event_name) == 0) {
    1040            0 :             *id = def_rec.event_id;
    1041            0 :             close(fh);
    1042            0 :             close(fhd);
    1043            0 :             return HS_SUCCESS;
    1044              :          }
    1045              :       } while (TRUE);
    1046              : 
    1047            0 :       close(fh);
    1048            0 :       close(fhd);
    1049              : 
    1050              :       /* not found -> go back one day */
    1051            0 :       lt -= 3600 * 24;
    1052              : 
    1053            0 :    } while (lt > ltime - 3600 * 24 * 365 * 10); /* maximum 10 years */
    1054              : 
    1055            0 :    return HS_UNDEFINED_EVENT;
    1056            0 : }
    1057              : 
    1058              : 
    1059              : /********************************************************************/
    1060            0 : static INT hs_count_vars(DWORD ltime, DWORD event_id, DWORD * count)
    1061              : /********************************************************************\
    1062              : 
    1063              :   Routine: hs_count_vars
    1064              : 
    1065              :   Purpose: Count number of variables for a given date and event id
    1066              : 
    1067              :   Input:
    1068              :     DWORD  ltime            Date at which tags should be counted
    1069              : 
    1070              :   Output:
    1071              :     DWORD  *count           Number of tags
    1072              : 
    1073              :   Function value:
    1074              :     HS_SUCCESS              Successful completion
    1075              :     HS_FILE_ERROR           Cannot open history file
    1076              : 
    1077              : \********************************************************************/
    1078              : {
    1079              :    int fh, fhd;
    1080            0 :    std::string fn, fnd;
    1081              :    INT i, n, status;
    1082              :    DEF_RECORD def_rec;
    1083              :    HIST_RECORD rec;
    1084              : 
    1085              :    /* search latest history file */
    1086            0 :    status = hs_search_file(&ltime, -1);
    1087            0 :    if (status != HS_SUCCESS) {
    1088            0 :       cm_msg(MERROR, "hs_count_tags", "cannot find recent history file");
    1089            0 :       return HS_FILE_ERROR;
    1090              :    }
    1091              : 
    1092              :    /* open history and definition files */
    1093            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
    1094            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
    1095            0 :    if (fh < 0 || fhd < 0) {
    1096            0 :       cm_msg(MERROR, "hs_count_tags", "cannot open index files");
    1097            0 :       return HS_FILE_ERROR;
    1098              :    }
    1099              : 
    1100              :    /* search last definition */
    1101            0 :    xseek_end(fnd, fhd);
    1102            0 :    n = xcurpos(fnd, fhd) / sizeof(def_rec);
    1103            0 :    def_rec.event_id = 0;
    1104            0 :    for (i = n - 1; i >= 0; i--) {
    1105            0 :       if (!xseek(fnd, fhd, i * sizeof(def_rec))) return HS_FILE_ERROR;
    1106            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec)) < 0) return HS_FILE_ERROR;
    1107            0 :       if (def_rec.event_id == event_id)
    1108            0 :          break;
    1109              :    }
    1110            0 :    if (def_rec.event_id != event_id) {
    1111            0 :       cm_msg(MERROR, "hs_count_tags", "event %d not found in index file", event_id);
    1112            0 :       return HS_FILE_ERROR;
    1113              :    }
    1114              : 
    1115              :    /* read definition */
    1116            0 :    xseek(fn, fh, def_rec.def_offset);
    1117            0 :    xread(fn, fh, (char *) &rec, sizeof(rec));
    1118            0 :    *count = rec.data_size / sizeof(TAG);
    1119              : 
    1120            0 :    close(fh);
    1121            0 :    close(fhd);
    1122              : 
    1123            0 :    return HS_SUCCESS;
    1124            0 : }
    1125              : 
    1126              : 
    1127              : /********************************************************************/
    1128            0 : static INT hs_enum_vars(DWORD ltime, DWORD event_id, char *var_name, DWORD * size, DWORD * var_n, DWORD * n_size)
    1129              : /********************************************************************\
    1130              : 
    1131              :   Routine: hs_enum_vars
    1132              : 
    1133              :   Purpose: Enumerate variable tags for a given date and event id
    1134              : 
    1135              :   Input:
    1136              :     DWORD  ltime            Date at which tags should be enumerated
    1137              :     DWORD  event_id         Event ID
    1138              : 
    1139              :   Output:
    1140              :     char   *var_name        Array containing variable names
    1141              :     DWORD  *size            Size of name array
    1142              :     DWORD  *var_n           Array size of variable
    1143              :     DWORD  *n_size          Size of n array
    1144              : 
    1145              :   Function value:
    1146              :     HS_SUCCESS              Successful completion
    1147              :     HS_NO_MEMEORY           Out of memory
    1148              :     HS_FILE_ERROR           Cannot open history file
    1149              : 
    1150              : \********************************************************************/
    1151              : {
    1152              :    char str[256];
    1153              :    int fh, fhd;
    1154            0 :    std::string fn, fnd;
    1155              :    INT i, n, status;
    1156              :    DEF_RECORD def_rec;
    1157              :    HIST_RECORD rec;
    1158              :    TAG *tag;
    1159              : 
    1160              :    /* search latest history file */
    1161            0 :    status = hs_search_file(&ltime, -1);
    1162            0 :    if (status != HS_SUCCESS) {
    1163            0 :       cm_msg(MERROR, "hs_enum_vars", "cannot find recent history file");
    1164            0 :       return HS_FILE_ERROR;
    1165              :    }
    1166              : 
    1167              :    /* open history and definition files */
    1168            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
    1169            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
    1170            0 :    if (fh < 0 || fhd < 0) {
    1171            0 :       cm_msg(MERROR, "hs_enum_vars", "cannot open index files");
    1172            0 :       return HS_FILE_ERROR;
    1173              :    }
    1174              : 
    1175              :    /* search last definition */
    1176            0 :    xseek_end(fnd, fhd);
    1177            0 :    n = xcurpos(fnd, fhd) / sizeof(def_rec);
    1178            0 :    def_rec.event_id = 0;
    1179            0 :    for (i = n - 1; i >= 0; i--) {
    1180            0 :       if (!xseek(fnd, fhd, i * sizeof(def_rec))) return HS_FILE_ERROR;
    1181            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec)) < 0) return HS_FILE_ERROR;
    1182            0 :       if (def_rec.event_id == event_id)
    1183            0 :          break;
    1184              :    }
    1185            0 :    if (def_rec.event_id != event_id) {
    1186            0 :       cm_msg(MERROR, "hs_enum_vars", "event %d not found in index file", event_id);
    1187            0 :       return HS_FILE_ERROR;
    1188              :    }
    1189              : 
    1190              :    /* read definition header */
    1191            0 :    xseek(fn, fh, def_rec.def_offset);
    1192            0 :    xread(fn, fh, (char *) &rec, sizeof(rec));
    1193            0 :    xread(fn, fh, str, NAME_LENGTH);
    1194              : 
    1195              :    /* read event definition */
    1196            0 :    n = rec.data_size / sizeof(TAG);
    1197            0 :    tag = (TAG *) M_MALLOC(rec.data_size);
    1198            0 :    xread(fn, fh, (char *) tag, rec.data_size);
    1199              : 
    1200            0 :    if (n * NAME_LENGTH > (INT) * size || n * sizeof(DWORD) > *n_size) {
    1201              : 
    1202              :       /* store partial definition */
    1203            0 :       for (i = 0; i < (INT) * size / NAME_LENGTH; i++) {
    1204            0 :          strcpy(var_name + i * NAME_LENGTH, tag[i].name);
    1205            0 :          var_n[i] = tag[i].n_data;
    1206              :       }
    1207              : 
    1208            0 :       cm_msg(MERROR, "hs_enum_vars", "tag buffer too small");
    1209            0 :       M_FREE(tag);
    1210            0 :       close(fh);
    1211            0 :       close(fhd);
    1212            0 :       return HS_NO_MEMORY;
    1213              :    }
    1214              : 
    1215              :    /* store full definition */
    1216            0 :    for (i = 0; i < n; i++) {
    1217            0 :       strcpy(var_name + i * NAME_LENGTH, tag[i].name);
    1218            0 :       var_n[i] = tag[i].n_data;
    1219              :    }
    1220            0 :    *size = n * NAME_LENGTH;
    1221            0 :    *n_size = n * sizeof(DWORD);
    1222              : 
    1223            0 :    M_FREE(tag);
    1224            0 :    close(fh);
    1225            0 :    close(fhd);
    1226              : 
    1227            0 :    return HS_SUCCESS;
    1228            0 : }
    1229              : 
    1230              : 
    1231              : /********************************************************************/
    1232            0 : static INT hs_get_var(DWORD ltime, DWORD event_id, const char *var_name, DWORD * type, INT * n_data)
    1233              : /********************************************************************\
    1234              : 
    1235              :   Routine: hs_get_var
    1236              : 
    1237              :   Purpose: Get definition for certain variable
    1238              : 
    1239              :   Input:
    1240              :     DWORD  ltime            Date at which variable definition should
    1241              :                             be returned
    1242              :     DWORD  event_id         Event ID
    1243              :     char   *var_name        Name of variable
    1244              : 
    1245              :   Output:
    1246              :     INT    *type            Type of variable
    1247              :     INT    *n_data          Number of items in variable
    1248              : 
    1249              :   Function value:
    1250              :     HS_SUCCESS              Successful completion
    1251              :     HS_NO_MEMEORY           Out of memory
    1252              :     HS_FILE_ERROR           Cannot open history file
    1253              : 
    1254              : \********************************************************************/
    1255              : {
    1256              :    char str[256];
    1257              :    int fh, fhd;
    1258            0 :    std::string fn, fnd;
    1259              :    INT i, n, status;
    1260              :    DEF_RECORD def_rec;
    1261              :    HIST_RECORD rec;
    1262              :    TAG *tag;
    1263              : 
    1264              :    /* search latest history file */
    1265            0 :    status = hs_search_file(&ltime, -1);
    1266            0 :    if (status != HS_SUCCESS) {
    1267            0 :       cm_msg(MERROR, "hs_get_var", "cannot find recent history file");
    1268            0 :       return HS_FILE_ERROR;
    1269              :    }
    1270              : 
    1271              :    /* open history and definition files */
    1272            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
    1273            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
    1274            0 :    if (fh < 0 || fhd < 0) {
    1275            0 :       cm_msg(MERROR, "hs_get_var", "cannot open index files");
    1276            0 :       return HS_FILE_ERROR;
    1277              :    }
    1278              : 
    1279              :    /* search last definition */
    1280            0 :    xseek_end(fnd, fhd);
    1281            0 :    n = xcurpos(fnd, fhd) / sizeof(def_rec);
    1282            0 :    def_rec.event_id = 0;
    1283            0 :    for (i = n - 1; i >= 0; i--) {
    1284            0 :       if (!xseek(fnd, fhd, i * sizeof(def_rec))) return HS_FILE_ERROR;
    1285            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec)) < 0) return HS_FILE_ERROR;
    1286            0 :       if (def_rec.event_id == event_id)
    1287            0 :          break;
    1288              :    }
    1289            0 :    if (def_rec.event_id != event_id) {
    1290            0 :       cm_msg(MERROR, "hs_get_var", "event %d not found in index file", event_id);
    1291            0 :       return HS_FILE_ERROR;
    1292              :    }
    1293              : 
    1294              :    /* read definition header */
    1295            0 :    xseek(fn, fh, def_rec.def_offset);
    1296            0 :    xread(fn, fh, (char *) &rec, sizeof(rec));
    1297            0 :    xread(fn, fh, str, NAME_LENGTH);
    1298              : 
    1299              :    /* read event definition */
    1300            0 :    n = rec.data_size / sizeof(TAG);
    1301            0 :    tag = (TAG *) M_MALLOC(rec.data_size);
    1302            0 :    xread(fn, fh, (char *) tag, rec.data_size);
    1303              : 
    1304              :    /* search variable */
    1305            0 :    for (i = 0; i < n; i++)
    1306            0 :       if (strcmp(tag[i].name, var_name) == 0)
    1307            0 :          break;
    1308              : 
    1309            0 :    close(fh);
    1310            0 :    close(fhd);
    1311              : 
    1312            0 :    if (i < n) {
    1313            0 :       *type = tag[i].type;
    1314            0 :       *n_data = tag[i].n_data;
    1315              :    } else {
    1316            0 :       *type = *n_data = 0;
    1317            0 :       cm_msg(MERROR, "hs_get_var", "variable %s not found", var_name);
    1318            0 :       M_FREE(tag);
    1319            0 :       return HS_UNDEFINED_VAR;
    1320              :    }
    1321              : 
    1322            0 :    M_FREE(tag);
    1323            0 :    return HS_SUCCESS;
    1324            0 : }
    1325              : 
    1326              : 
    1327              : /********************************************************************/
    1328            0 : static INT hs_get_tags(DWORD ltime, DWORD event_id, char event_name[NAME_LENGTH], int* n_tags, TAG** tags)
    1329              : /********************************************************************\
    1330              : 
    1331              :   Routine: hs_get_tags
    1332              : 
    1333              :   Purpose: Get tags for event id
    1334              : 
    1335              :   Input:
    1336              :     DWORD  ltime            Date at which variable definition should
    1337              :                             be returned
    1338              :     DWORD  event_id         Event ID
    1339              : 
    1340              :   Output:
    1341              :     char    event_name[NAME_LENGTH] Event name from history file
    1342              :     INT    *n_tags          Number of tags
    1343              :     TAG   **tags            Pointer to array of tags (should be free()ed by the caller)
    1344              : 
    1345              :   Function value:
    1346              :     HS_SUCCESS              Successful completion
    1347              :     HS_NO_MEMEORY           Out of memory
    1348              :     HS_FILE_ERROR           Cannot open history file
    1349              : 
    1350              : \********************************************************************/
    1351              : {
    1352              :    int fh, fhd;
    1353            0 :    std::string fn, fnd;
    1354              :    INT i, n, status;
    1355              :    DEF_RECORD def_rec;
    1356              :    HIST_RECORD rec;
    1357              : 
    1358            0 :    *n_tags = 0;
    1359            0 :    *tags = NULL;
    1360              : 
    1361            0 :    if (rpc_is_remote())
    1362            0 :       assert(!"RPC not implemented");
    1363              : 
    1364              :    /* search latest history file */
    1365            0 :    status = hs_search_file(&ltime, -1);
    1366            0 :    if (status != HS_SUCCESS) {
    1367            0 :       cm_msg(MERROR, "hs_get_tags", "cannot find recent history file, hs_search_file() status %d", status);
    1368            0 :       return HS_FILE_ERROR;
    1369              :    }
    1370              : 
    1371              :    /* open history and definition files */
    1372            0 :    hs_open_file(ltime, "hst", O_RDONLY, &fn, &fh);
    1373            0 :    hs_open_file(ltime, "idf", O_RDONLY, &fnd, &fhd);
    1374            0 :    if (fh < 0 || fhd < 0) {
    1375            0 :       cm_msg(MERROR, "hs_get_tags", "cannot open index files for time %d", ltime);
    1376            0 :       if (fh>0)
    1377            0 :          close(fh);
    1378            0 :       if (fhd>0)
    1379            0 :          close(fhd);
    1380            0 :       return HS_FILE_ERROR;
    1381              :    }
    1382              : 
    1383              :    /* search last definition */
    1384            0 :    xseek_end(fnd, fhd);
    1385            0 :    n = xcurpos(fnd, fhd) / sizeof(def_rec);
    1386            0 :    def_rec.event_id = 0;
    1387            0 :    for (i = n - 1; i >= 0; i--) {
    1388            0 :       if (!xseek(fnd, fhd, i * sizeof(def_rec))) return HS_FILE_ERROR;
    1389            0 :       if (xread(fnd, fhd, (char *) &def_rec, sizeof(def_rec)) < 0) return HS_FILE_ERROR;
    1390              :       //printf("reading index file found event_id %d, looking for %d\n", def_rec.event_id, event_id);
    1391            0 :       if (def_rec.event_id == event_id)
    1392            0 :          break;
    1393              :    }
    1394              : 
    1395            0 :    if (def_rec.event_id != event_id) {
    1396              :       //cm_msg(MERROR, "hs_get_tags", "event %d not found in index file", event_id);
    1397            0 :       close(fh);
    1398            0 :       close(fhd);
    1399            0 :       return HS_UNDEFINED_EVENT;
    1400              :    }
    1401              : 
    1402              :    /* read definition header */
    1403            0 :    if (!xseek(fn, fh, def_rec.def_offset)) return HS_FILE_ERROR;
    1404            0 :    if (xread(fn, fh, (char *) &rec, sizeof(rec)) < 0) return HS_FILE_ERROR;
    1405            0 :    if (xread(fn, fh, event_name, NAME_LENGTH) < 0)    return HS_FILE_ERROR;
    1406              : 
    1407              :    /* read event definition */
    1408            0 :    *n_tags = rec.data_size / sizeof(TAG);
    1409              : 
    1410            0 :    *tags = (TAG*) malloc(rec.data_size);
    1411              : 
    1412            0 :    if (xread(fn, fh, (char *) (*tags), rec.data_size) < 0) return HS_FILE_ERROR;
    1413              : 
    1414            0 :    close(fh);
    1415            0 :    close(fhd);
    1416              : 
    1417            0 :    return HS_SUCCESS;
    1418            0 : }
    1419              : 
    1420            0 : double hs_to_double(int tid, const void* ybuffer)
    1421              : {
    1422            0 :    int j = 0;
    1423              :    /* convert data to float */
    1424            0 :    switch (tid) {
    1425            0 :    default:
    1426            0 :       return 0;
    1427            0 :    case TID_BYTE:
    1428            0 :       return *(((BYTE *) ybuffer) + j);
    1429            0 :    case TID_SBYTE:
    1430            0 :       return *(((char *) ybuffer) + j);
    1431            0 :    case TID_CHAR:
    1432            0 :       return *(((char *) ybuffer) + j);
    1433            0 :    case TID_WORD:
    1434            0 :       return *(((WORD *) ybuffer) + j);
    1435            0 :    case TID_SHORT:
    1436            0 :       return *(((short *) ybuffer) + j);
    1437            0 :    case TID_DWORD:
    1438            0 :       return *(((DWORD *) ybuffer) + j);
    1439            0 :    case TID_INT:
    1440            0 :       return *(((INT *) ybuffer) + j);
    1441            0 :    case TID_BOOL:
    1442            0 :       return *(((BOOL *) ybuffer) + j);
    1443            0 :    case TID_FLOAT:
    1444            0 :       return *(((float *) ybuffer) + j);
    1445            0 :    case TID_DOUBLE:
    1446            0 :       return *(((double *) ybuffer) + j);
    1447              :    }
    1448              :    /* NOT REACHED */
    1449              : }
    1450              : 
    1451            0 : static INT hs_read(DWORD event_id, DWORD start_time, DWORD end_time, DWORD interval, const char *tag_name, DWORD var_index, DWORD * time_buffer, DWORD * tbsize, void *data_buffer, DWORD * dbsize, DWORD * data_type, DWORD * data_n, MidasHistoryBufferInterface* buffer)
    1452              : /********************************************************************\
    1453              : 
    1454              :   Routine: hs_read
    1455              : 
    1456              :   Purpose: Read history for a variable at a certain time interval
    1457              : 
    1458              :   Input:
    1459              :     DWORD  event_id         Event ID
    1460              :     DWORD  start_time       Starting Date/Time
    1461              :     DWORD  end_time         End Date/Time
    1462              :     DWORD  interval         Minimum time in seconds between reported
    1463              :                             events. Can be used to skip events
    1464              :     char   *tag_name        Variable name inside event
    1465              :     DWORD  var_index        Index if variable is array
    1466              : 
    1467              :   Output:
    1468              :     DWORD  *time_buffer     Buffer containing times for each value
    1469              :     DWORD  *tbsize          Size of time buffer
    1470              :     void   *data_buffer     Buffer containing variable values
    1471              :     DWORD  *dbsize          Data buffer size
    1472              :     DWORD  *type            Type of variable (one of TID_xxx)
    1473              :     DWORD  *n               Number of time/value pairs found
    1474              :                             in specified interval and placed into
    1475              :                             time_buffer and data_buffer
    1476              : 
    1477              : 
    1478              :   Function value:
    1479              :     HS_SUCCESS              Successful completion
    1480              :     HS_NO_MEMEORY           Out of memory
    1481              :     HS_FILE_ERROR           Cannot open history file
    1482              :     HS_WRONG_INDEX          var_index exceeds array size of variable
    1483              :     HS_UNDEFINED_VAR        Variable "tag_name" not found in event
    1484              :     HS_TRUNCATED            Buffer too small, data has been truncated
    1485              : 
    1486              : \********************************************************************/
    1487              : {
    1488              :    DWORD prev_time, last_irec_time;
    1489            0 :    int fh, fhd, fhi, cp = 0;
    1490            0 :    std::string fn, fnd, fni;
    1491              :    int delta;
    1492              :    int status;
    1493              :    int cache_size;
    1494              :    INDEX_RECORD irec, *pirec;
    1495              :    HIST_RECORD rec, drec;
    1496              :    INT old_def_offset;
    1497              :    TAG *tag;
    1498              :    char str[NAME_LENGTH];
    1499            0 :    char *cache = NULL;
    1500              :    time_t ltime;
    1501              : 
    1502            0 :    int tag_index = -1;
    1503            0 :    int var_type = -1;
    1504            0 :    unsigned var_size = 0;
    1505            0 :    unsigned var_offset = 0;
    1506              :          
    1507            0 :    int ieof = 0;
    1508              : 
    1509              :    //printf("hs_read event %d, time %d:%d, tagname: \'%s\', varindex: %d\n", event_id, start_time, end_time, tag_name, var_index);
    1510              : 
    1511            0 :    ss_tzset(); // required by localtime_r()
    1512              : 
    1513              :    /* if not time given, use present to one hour in past */
    1514            0 :    if (start_time == 0)
    1515            0 :       start_time = (DWORD) time(NULL) - 3600;
    1516            0 :    if (end_time == 0)
    1517            0 :       end_time = (DWORD) time(NULL);
    1518              : 
    1519            0 :    if (data_n)
    1520            0 :       *data_n = 0;
    1521            0 :    prev_time = 0;
    1522            0 :    last_irec_time = start_time;
    1523              : 
    1524              :    /* search history file for start_time */
    1525            0 :    status = hs_search_file(&start_time, 1);
    1526            0 :    if (status != HS_SUCCESS) {
    1527              :       //cm_msg(MERROR, "hs_read", "cannot find recent history file");
    1528            0 :       if (data_n)
    1529            0 :          *data_n = 0;
    1530            0 :       if (tbsize)
    1531            0 :          *tbsize = 0;
    1532            0 :       if (dbsize)
    1533            0 :          *dbsize = 0;
    1534            0 :       return HS_FILE_ERROR;
    1535              :    }
    1536              : 
    1537              :    /* open history and definition files */
    1538            0 :    hs_open_file(start_time, "hst", O_RDONLY, &fn, &fh);
    1539            0 :    hs_open_file(start_time, "idf", O_RDONLY, &fnd, &fhd);
    1540            0 :    hs_open_file(start_time, "idx", O_RDONLY, &fni, &fhi);
    1541            0 :    if (fh < 0 || fhd < 0 || fhi < 0) {
    1542            0 :       cm_msg(MERROR, "hs_read", "cannot open index files");
    1543            0 :       if (tbsize)
    1544            0 :          *tbsize = 0;
    1545            0 :       if (dbsize)
    1546            0 :          *dbsize = 0;
    1547            0 :       if (data_n)
    1548            0 :          *data_n = 0;
    1549            0 :       if (fh > 0)
    1550            0 :          close(fh);
    1551            0 :       if (fhd > 0)
    1552            0 :          close(fhd);
    1553            0 :       if (fhi > 0)
    1554            0 :          close(fhi);
    1555            0 :       return HS_FILE_ERROR;
    1556              :    }
    1557              : 
    1558              :    /* try to read index file into cache */
    1559            0 :    xseek_end(fni, fhi);
    1560            0 :    cache_size = xcurpos(fni, fhi);
    1561              : 
    1562            0 :    if (cache_size == 0) {
    1563            0 :       goto nextday;
    1564              :    }
    1565              : 
    1566            0 :    if (cache_size > 0) {
    1567            0 :       cache = (char *) M_MALLOC(cache_size);
    1568            0 :       if (cache) {
    1569            0 :          xseek(fni, fhi, 0);
    1570            0 :          if (xread(fni, fhi, cache, cache_size) < 0) {
    1571            0 :             M_FREE(cache);
    1572            0 :             if (fh > 0)
    1573            0 :                close(fh);
    1574            0 :             if (fhd > 0)
    1575            0 :                close(fhd);
    1576            0 :             if (fhi > 0)
    1577            0 :                close(fhi);
    1578            0 :             return HS_FILE_ERROR;
    1579              :          }
    1580              :       }
    1581              : 
    1582              :       /* search record closest to start time */
    1583            0 :       if (cache == NULL) {
    1584            0 :          xseek_end(fni, fhi);
    1585            0 :          delta = (xcurpos(fni, fhi) / sizeof(irec)) / 2;
    1586            0 :          xseek(fni, fhi, delta * sizeof(irec));
    1587              :          do {
    1588            0 :             delta = (int) (abs(delta) / 2.0 + 0.5);
    1589            0 :             if (xread(fni, fhi, (char *) &irec, sizeof(irec)) < 0)
    1590            0 :                return HS_FILE_ERROR;
    1591            0 :             if (irec.time > start_time)
    1592            0 :                delta = -delta;
    1593              : 
    1594            0 :             xseek_cur(fni, fhi, (delta - 1) * sizeof(irec));
    1595            0 :          } while (abs(delta) > 1 && irec.time != start_time);
    1596            0 :          if (xread(fni, fhi, (char *) &irec, sizeof(irec)) < 0)
    1597            0 :             return HS_FILE_ERROR;
    1598            0 :          if (irec.time > start_time)
    1599            0 :             delta = -abs(delta);
    1600              : 
    1601            0 :          int i = xcurpos(fni, fhi) + (delta - 1) * sizeof(irec);
    1602            0 :          if (i <= 0)
    1603            0 :             xseek(fni, fhi, 0);
    1604              :          else
    1605            0 :             xseek_cur(fni, fhi, (delta - 1) * sizeof(irec));
    1606            0 :          if (xread(fni, fhi, (char *) &irec, sizeof(irec)) < 0)
    1607            0 :             return HS_FILE_ERROR;
    1608              :       } else {
    1609            0 :          delta = (cache_size / sizeof(irec)) / 2;
    1610            0 :          cp = delta * sizeof(irec);
    1611              :          do {
    1612            0 :             delta = (int) (abs(delta) / 2.0 + 0.5);
    1613            0 :             pirec = (INDEX_RECORD *) (cache + cp);
    1614              : 
    1615              :             //printf("pirec %p, cache %p, cp %d\n", pirec, cache, cp);
    1616              : 
    1617            0 :             if (pirec->time > start_time)
    1618            0 :                delta = -delta;
    1619              : 
    1620            0 :             cp = cp + delta * sizeof(irec);
    1621              : 
    1622            0 :             if (cp < 0)
    1623            0 :                cp = 0;
    1624            0 :             if (cp >= cache_size)
    1625            0 :                cp = cache_size - sizeof(irec);
    1626            0 :          } while (abs(delta) > 1 && pirec->time != start_time);
    1627            0 :          pirec = (INDEX_RECORD *) (cache + cp);
    1628            0 :          if (pirec->time > start_time)
    1629            0 :             delta = -abs(delta);
    1630              : 
    1631            0 :          if (cp <= delta * (int) sizeof(irec))
    1632            0 :             cp = 0;
    1633              :          else
    1634            0 :             cp = cp + delta * sizeof(irec);
    1635              : 
    1636            0 :          if (cp >= cache_size)
    1637            0 :             cp = cache_size - sizeof(irec);
    1638            0 :          if (cp < 0)
    1639            0 :             cp = 0;
    1640              : 
    1641            0 :          memcpy(&irec, (INDEX_RECORD *) (cache + cp), sizeof(irec));
    1642            0 :          cp += sizeof(irec);
    1643              :       }
    1644              :    } else {                     /* file size > 0 */
    1645              : 
    1646            0 :       cache = NULL;
    1647            0 :       irec.time = start_time;
    1648              :    }
    1649              : 
    1650              :    /* read records, skip wrong IDs */
    1651            0 :    old_def_offset = -1;
    1652            0 :    last_irec_time = start_time - 24 * 60 * 60;
    1653              :    do {
    1654              :       //printf("time %d -> %d\n", last_irec_time, irec.time);
    1655              : 
    1656            0 :       if (irec.time < last_irec_time) {
    1657            0 :          cm_msg(MERROR, "hs_read", "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
    1658              :          //*tbsize = *dbsize = *n = 0;
    1659            0 :          if (fh > 0)
    1660            0 :             close(fh);
    1661            0 :          if (fhd > 0)
    1662            0 :             close(fhd);
    1663            0 :          if (fhi > 0)
    1664            0 :             close(fhi);
    1665            0 :          hs_gen_index(last_irec_time);
    1666            0 :          return HS_SUCCESS;
    1667              :       }
    1668            0 :       last_irec_time = irec.time;
    1669            0 :       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
    1670              :          /* check if record time more than "interval" seconds after previous time */
    1671            0 :          if (irec.time >= prev_time + interval) {
    1672            0 :             prev_time = irec.time;
    1673            0 :             xseek(fn, fh, irec.offset);
    1674            0 :             if (xread(fn, fh, (char *) &rec, sizeof(rec)) < 0) {
    1675            0 :                cm_msg(MERROR, "hs_read", "corrupted history data at time %d", (int) irec.time);
    1676              :                //*tbsize = *dbsize = *n = 0;
    1677            0 :                if (fh > 0)
    1678            0 :                   close(fh);
    1679            0 :                if (fhd > 0)
    1680            0 :                   close(fhd);
    1681            0 :                if (fhi > 0)
    1682            0 :                   close(fhi);
    1683            0 :                hs_gen_index(last_irec_time);
    1684            0 :                return HS_SUCCESS;
    1685              :             }
    1686              : 
    1687              :             /* if definition changed, read new definition */
    1688            0 :             if ((INT) rec.def_offset != old_def_offset) {
    1689            0 :                xseek(fn, fh, rec.def_offset);
    1690            0 :                xread(fn, fh, (char *) &drec, sizeof(drec));
    1691            0 :                xread(fn, fh, str, NAME_LENGTH);
    1692              : 
    1693            0 :                tag = (TAG *) M_MALLOC(drec.data_size);
    1694            0 :                if (tag == NULL) {
    1695            0 :                   if (data_n)
    1696            0 :                      *data_n = 0;
    1697            0 :                   if (tbsize)
    1698            0 :                      *tbsize = 0;
    1699            0 :                   if (dbsize)
    1700            0 :                      *dbsize = 0;
    1701            0 :                   if (cache)
    1702            0 :                      M_FREE(cache);
    1703            0 :                   if (fh > 0)
    1704            0 :                      close(fh);
    1705            0 :                   if (fhd > 0)
    1706            0 :                      close(fhd);
    1707            0 :                   if (fhi > 0)
    1708            0 :                      close(fhi);
    1709            0 :                   return HS_NO_MEMORY;
    1710              :                }
    1711            0 :                xread(fn, fh, (char *) tag, drec.data_size);
    1712              : 
    1713              :                /* find index of tag_name in new definition */
    1714            0 :                for (DWORD i = 0; i < drec.data_size / sizeof(TAG); i++)
    1715            0 :                   if (equal_ustring(tag[i].name, tag_name)) {
    1716            0 :                      tag_index = i;
    1717            0 :                      break;
    1718              :                   }
    1719              : 
    1720              :                /*
    1721              :                   if ((DWORD) i == drec.data_size/sizeof(TAG))
    1722              :                   {
    1723              :                   *n = *tbsize = *dbsize = 0;
    1724              :                   if (cache)
    1725              :                   M_FREE(cache);
    1726              : 
    1727              :                   return HS_UNDEFINED_VAR;
    1728              :                   }
    1729              :                 */
    1730              : 
    1731            0 :                if (tag_index >= 0 && var_index >= tag[tag_index].n_data) {
    1732            0 :                   if (data_n)
    1733            0 :                      *data_n = 0;
    1734            0 :                   if (tbsize)
    1735            0 :                      *tbsize = 0;
    1736            0 :                   if (dbsize)
    1737            0 :                      *dbsize = 0;
    1738            0 :                   if (cache)
    1739            0 :                      M_FREE(cache);
    1740            0 :                   M_FREE(tag);
    1741            0 :                   if (fh > 0)
    1742            0 :                      close(fh);
    1743            0 :                   if (fhd > 0)
    1744            0 :                      close(fhd);
    1745            0 :                   if (fhi > 0)
    1746            0 :                      close(fhi);
    1747            0 :                   return HS_WRONG_INDEX;
    1748              :                }
    1749              : 
    1750              :                /* calculate offset for variable */
    1751            0 :                if (tag_index >= 0) {
    1752            0 :                   var_type = tag[tag_index].type;
    1753              : 
    1754            0 :                   if (data_type)
    1755            0 :                      *data_type = var_type;
    1756              : 
    1757              :                   /* loop over all previous variables */
    1758            0 :                   var_offset = 0;
    1759            0 :                   for (int i=0; i<tag_index; i++)
    1760            0 :                      var_offset += rpc_tid_size(tag[i].type) * tag[i].n_data;
    1761              : 
    1762              :                   /* strings have size n_data */
    1763            0 :                   if (tag[tag_index].type == TID_STRING)
    1764            0 :                      var_size = tag[tag_index].n_data;
    1765              :                   else
    1766            0 :                      var_size = rpc_tid_size(tag[tag_index].type);
    1767              : 
    1768            0 :                   var_offset += var_size * var_index;
    1769              :                }
    1770              : 
    1771            0 :                M_FREE(tag);
    1772            0 :                old_def_offset = rec.def_offset;
    1773            0 :                xseek(fn, fh, irec.offset + sizeof(rec));
    1774              :             }
    1775              : 
    1776            0 :             if (buffer) {
    1777              :                /* copy time from header */
    1778            0 :                DWORD t = irec.time;
    1779              :                char buf[16]; // biggest data is 8-byte "double"
    1780            0 :                assert(var_size <= sizeof(buf));
    1781            0 :                xseek_cur(fn, fh, var_offset);
    1782            0 :                xread(fn, fh, buf, var_size);
    1783            0 :                buffer->Add(t, hs_to_double(var_type, buf));
    1784            0 :             } else if (tag_index >= 0 && data_n) {
    1785              :                /* check if data fits in buffers */
    1786            0 :                if ((*data_n) * sizeof(DWORD) >= *tbsize || (*data_n) * var_size >= *dbsize) {
    1787            0 :                   *dbsize = (*data_n) * var_size;
    1788            0 :                   *tbsize = (*data_n) * sizeof(DWORD);
    1789            0 :                   if (cache)
    1790            0 :                      M_FREE(cache);
    1791            0 :                   if (fh > 0)
    1792            0 :                      close(fh);
    1793            0 :                   if (fhd > 0)
    1794            0 :                      close(fhd);
    1795            0 :                   if (fhi > 0)
    1796            0 :                      close(fhi);
    1797            0 :                   return HS_TRUNCATED;
    1798              :                }
    1799              : 
    1800              :                /* copy time from header */
    1801            0 :                time_buffer[*data_n] = irec.time;
    1802              : 
    1803              :                /* copy data from record */
    1804            0 :                xseek_cur(fn, fh, var_offset);
    1805            0 :                xread(fn, fh, (char *) data_buffer + (*data_n) * var_size, var_size);
    1806              : 
    1807              :                /* increment counter */
    1808            0 :                (*data_n)++;
    1809              :             }
    1810              :          }
    1811              :       }
    1812              :          
    1813              :       /* read next index record */
    1814            0 :       if (cache) {
    1815            0 :          if (cp >= cache_size) {
    1816            0 :             ieof = -1;
    1817            0 :             M_FREE(cache);
    1818            0 :             cache = NULL;
    1819              :          } else {
    1820              : 
    1821            0 :           try_again:
    1822              : 
    1823            0 :             ieof = sizeof(irec);
    1824              : 
    1825            0 :             memcpy(&irec, cache + cp, sizeof(irec));
    1826            0 :             cp += sizeof(irec);
    1827              : 
    1828              :             /* if history file is broken ... */
    1829            0 :             if (irec.time < last_irec_time || irec.time > last_irec_time + 24 * 60 * 60) {
    1830              :                //if (irec.time < last_irec_time) {
    1831              :                //printf("time %d -> %d, cache_size %d, cp %d\n", last_irec_time, irec.time, cache_size, cp);
    1832              : 
    1833              :                //printf("Seeking next record...\n");
    1834              : 
    1835            0 :                while (cp < cache_size) {
    1836            0 :                   DWORD *evidp = (DWORD *) (cache + cp);
    1837            0 :                   if (*evidp == event_id) {
    1838              :                      //printf("Found at cp %d\n", cp);
    1839            0 :                      goto try_again;
    1840              :                   }
    1841              : 
    1842            0 :                   cp++;
    1843              :                }
    1844              : 
    1845            0 :                ieof = -1;
    1846              :             }
    1847              :          }
    1848              :       } else {
    1849            0 :          ieof = xread(fni, fhi, (char *) &irec, sizeof(irec), true);
    1850              :       }
    1851              : 
    1852              :       /* end of file: search next history file */
    1853            0 :       if (ieof <= 0) {
    1854            0 :          nextday:
    1855              : 
    1856            0 :          if (fh > 0)
    1857            0 :             close(fh);
    1858            0 :          if (fhd > 0)
    1859            0 :             close(fhd);
    1860            0 :          if (fhi > 0)
    1861            0 :             close(fhi);
    1862            0 :          fh = fhd = fhi = 0;
    1863              : 
    1864              :          /* advance one day */
    1865            0 :          ltime = (time_t) last_irec_time;
    1866              :          struct tm tms;
    1867            0 :          localtime_r(&ltime, &tms);
    1868            0 :          tms.tm_hour = tms.tm_min = tms.tm_sec = 0;
    1869            0 :          last_irec_time = (DWORD) ss_mktime(&tms);
    1870              : 
    1871            0 :          last_irec_time += 3600 * 24;
    1872              : 
    1873            0 :          if (last_irec_time > end_time)
    1874            0 :             break;
    1875              : 
    1876              :          /* search next file */
    1877            0 :          status = hs_search_file(&last_irec_time, 1);
    1878            0 :          if (status != HS_SUCCESS)
    1879            0 :             break;
    1880              : 
    1881              :          /* open history and definition files */
    1882            0 :          hs_open_file(last_irec_time, "hst", O_RDONLY, &fn, &fh);
    1883            0 :          hs_open_file(last_irec_time, "idf", O_RDONLY, &fnd, &fhd);
    1884            0 :          hs_open_file(last_irec_time, "idx", O_RDONLY, &fni, &fhi);
    1885            0 :          if (fh < 0 || fhd < 0 || fhi < 0) {
    1886            0 :             cm_msg(MERROR, "hs_read", "cannot open index files");
    1887            0 :             break;
    1888              :          }
    1889              : 
    1890              :          /* try to read index file into cache */
    1891            0 :          xseek_end(fni, fhi);
    1892            0 :          cache_size = xcurpos(fni, fhi);
    1893              : 
    1894            0 :          if (cache_size == 0) {
    1895            0 :             goto nextday;
    1896              :          }
    1897              : 
    1898            0 :          xseek(fni, fhi, 0);
    1899            0 :          cache = (char *) M_MALLOC(cache_size); // FIXME: is this a memory leak?
    1900            0 :          if (cache) {
    1901            0 :             if (xread(fni, fhi, cache, cache_size) < 0) {
    1902            0 :                break;
    1903              :             }
    1904              :             /* read first record */
    1905            0 :             cp = 0;
    1906            0 :             memcpy(&irec, cache, sizeof(irec));
    1907              :          } else {
    1908              :             /* read first record */
    1909            0 :             if (xread(fni, fhi, (char *) &irec, sizeof(irec)) < 0) {
    1910            0 :                break;
    1911              :             }
    1912              :          }
    1913              : 
    1914              :          /* old definition becomes invalid */
    1915            0 :          old_def_offset = -1;
    1916              :       }
    1917              :       //if (event_id==4 && irec.event_id == event_id)
    1918              :       //  printf("time %d end %d\n", irec.time, end_time);
    1919            0 :    } while (irec.time < end_time);
    1920              : 
    1921            0 :    if (cache)
    1922            0 :       M_FREE(cache);
    1923            0 :    if (fh)
    1924            0 :       close(fh);
    1925            0 :    if (fhd)
    1926            0 :       close(fhd);
    1927            0 :    if (fhi)
    1928            0 :       close(fhi);
    1929              : 
    1930            0 :    if (dbsize && data_n)
    1931            0 :       *dbsize = *data_n * var_size;
    1932            0 :    if (tbsize && data_n)
    1933            0 :       *tbsize = *data_n * sizeof(DWORD);
    1934              : 
    1935            0 :    return HS_SUCCESS;
    1936            0 : }
    1937              : 
    1938              : /**dox***************************************************************/
    1939              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    1940              : 
    1941              : /********************************************************************/
    1942              : /**
    1943              : Display history for a given event at stdout. The output
    1944              : can be redirected to be read by Excel for example. 
    1945              : @param event_id         Event ID
    1946              : @param start_time       Starting Date/Time
    1947              : @param end_time         End Date/Time
    1948              : @param interval         Minimum time in seconds between reported                                                                                
    1949              :                             events. Can be used to skip events
    1950              : @param binary_time      Display DWORD time stamp
    1951              : @return HS_SUCCESS, HS_FILE_ERROR
    1952              : */
    1953              : /********************************************************************/
    1954            0 : static INT hs_dump(DWORD event_id, DWORD start_time, DWORD end_time, DWORD interval, BOOL binary_time)
    1955              : {
    1956              :    DWORD prev_time, last_irec_time;
    1957              :    time_t ltime;
    1958              :    int fh, fhd, fhi;
    1959            0 :    std::string fn, fnd, fni;
    1960            0 :    INT i, j, delta, status, n_tag = 0, old_n_tag = 0;
    1961              :    INDEX_RECORD irec;
    1962              :    HIST_RECORD rec, drec;
    1963              :    INT old_def_offset, offset;
    1964            0 :    TAG *tag = NULL, *old_tag = NULL;
    1965              :    char data_buffer[10000];
    1966              : 
    1967            0 :    ss_tzset(); // required by localtime_r()
    1968              : 
    1969              :    /* if not time given, use present to one hour in past */
    1970            0 :    if (start_time == 0)
    1971            0 :       start_time = (DWORD) time(NULL) - 3600;
    1972            0 :    if (end_time == 0)
    1973            0 :       end_time = (DWORD) time(NULL);
    1974              : 
    1975              :    /* search history file for start_time */
    1976            0 :    status = hs_search_file(&start_time, 1);
    1977            0 :    if (status != HS_SUCCESS) {
    1978            0 :       cm_msg(MERROR, "hs_dump", "cannot find recent history file");
    1979            0 :       return HS_FILE_ERROR;
    1980              :    }
    1981              : 
    1982              :    /* open history and definition files */
    1983            0 :    hs_open_file(start_time, "hst", O_RDONLY, &fn, &fh);
    1984            0 :    hs_open_file(start_time, "idf", O_RDONLY, &fnd, &fhd);
    1985            0 :    hs_open_file(start_time, "idx", O_RDONLY, &fni, &fhi);
    1986            0 :    if (fh < 0 || fhd < 0 || fhi < 0) {
    1987            0 :       cm_msg(MERROR, "hs_dump", "cannot open index files");
    1988            0 :       return HS_FILE_ERROR;
    1989              :    }
    1990              : 
    1991              :    /* search record closest to start time */
    1992            0 :    xseek_end(fni, fhi);
    1993            0 :    delta = (xcurpos(fni, fhi) / sizeof(irec)) / 2;
    1994            0 :    xseek(fni, fhi, delta * sizeof(irec));
    1995              :    do {
    1996            0 :       delta = (int) (abs(delta) / 2.0 + 0.5);
    1997            0 :       xread(fni, fhi, (char *) &irec, sizeof(irec));
    1998            0 :       if (irec.time > start_time)
    1999            0 :          delta = -delta;
    2000              : 
    2001            0 :       xseek_cur(fni, fhi, (delta - 1) * sizeof(irec));
    2002            0 :    } while (abs(delta) > 1 && irec.time != start_time);
    2003            0 :    xread(fni, fhi, (char *) &irec, sizeof(irec));
    2004            0 :    if (irec.time > start_time)
    2005            0 :       delta = -abs(delta);
    2006              : 
    2007            0 :    i = xcurpos(fni, fhi) + (delta - 1) * sizeof(irec);
    2008            0 :    if (i <= 0)
    2009            0 :       xseek(fni, fhi, 0);
    2010              :    else
    2011            0 :       xseek_cur(fni, fhi, (delta - 1) * sizeof(irec));
    2012            0 :    xread(fni, fhi, (char *) &irec, sizeof(irec));
    2013              : 
    2014              :    /* read records, skip wrong IDs */
    2015            0 :    old_def_offset = -1;
    2016            0 :    prev_time = 0;
    2017            0 :    last_irec_time = 0;
    2018              :    do {
    2019            0 :       if (irec.time < last_irec_time) {
    2020            0 :          cm_msg(MERROR, "hs_dump", "corrupted history data: time does not increase: %d -> %d", last_irec_time, irec.time);
    2021            0 :          hs_gen_index(last_irec_time);
    2022            0 :          return HS_FILE_ERROR;
    2023              :       }
    2024            0 :       last_irec_time = irec.time;
    2025            0 :       if (irec.event_id == event_id && irec.time <= end_time && irec.time >= start_time) {
    2026            0 :          if (irec.time >= prev_time + interval) {
    2027            0 :             prev_time = irec.time;
    2028            0 :             xseek(fn, fh, irec.offset);
    2029            0 :             xread(fn, fh, (char *) &rec, sizeof(rec));
    2030              : 
    2031              :             /* if definition changed, read new definition */
    2032            0 :             if ((INT) rec.def_offset != old_def_offset) {
    2033              :                char buf_name_length[NAME_LENGTH];
    2034            0 :                xseek(fn, fh, rec.def_offset);
    2035            0 :                xread(fn, fh, (char *) &drec, sizeof(drec));
    2036            0 :                xread(fn, fh, buf_name_length, NAME_LENGTH);
    2037              : 
    2038            0 :                if (tag == NULL)
    2039            0 :                   tag = (TAG *) M_MALLOC(drec.data_size);
    2040              :                else
    2041            0 :                   tag = (TAG *) realloc(tag, drec.data_size);
    2042            0 :                if (tag == NULL)
    2043            0 :                   return HS_NO_MEMORY;
    2044            0 :                xread(fn, fh, (char *) tag, drec.data_size);
    2045            0 :                n_tag = drec.data_size / sizeof(TAG);
    2046              : 
    2047              :                /* print tag names if definition has changed */
    2048            0 :                if (old_tag == NULL || old_n_tag != n_tag || memcmp(old_tag, tag, drec.data_size) != 0) {
    2049            0 :                   printf("Date\t");
    2050            0 :                   for (i = 0; i < n_tag; i++) {
    2051            0 :                      if (tag[i].n_data == 1 || tag[i].type == TID_STRING)
    2052            0 :                         printf("%s\t", tag[i].name);
    2053              :                      else
    2054            0 :                         for (j = 0; j < (INT) tag[i].n_data; j++)
    2055            0 :                            printf("%s%d\t", tag[i].name, j);
    2056              :                   }
    2057            0 :                   printf("\n");
    2058              : 
    2059            0 :                   if (old_tag == NULL)
    2060            0 :                      old_tag = (TAG *) M_MALLOC(drec.data_size);
    2061              :                   else
    2062            0 :                      old_tag = (TAG *) realloc(old_tag, drec.data_size);
    2063            0 :                   memcpy(old_tag, tag, drec.data_size);
    2064            0 :                   old_n_tag = n_tag;
    2065              :                }
    2066              : 
    2067            0 :                old_def_offset = rec.def_offset;
    2068            0 :                xseek(fn, fh, irec.offset + sizeof(rec));
    2069              :             }
    2070              : 
    2071              :             /* print time from header */
    2072            0 :             if (binary_time)
    2073            0 :                printf("%d ", irec.time);
    2074              :             else {
    2075            0 :                ltime = (time_t) irec.time;
    2076              :                char ctimebuf[32];
    2077            0 :                ctime_r(&ltime, ctimebuf);
    2078              :                char str[256];
    2079            0 :                mstrlcpy(str, ctimebuf + 4, sizeof(str));
    2080            0 :                str[20] = '\t';
    2081            0 :                printf("%s", str);
    2082              :             }
    2083              : 
    2084              :             /* read data */
    2085            0 :             xread(fn, fh, data_buffer, rec.data_size);
    2086              : 
    2087              :             /* interprete data from tag definition */
    2088            0 :             offset = 0;
    2089            0 :             for (i = 0; i < n_tag; i++) {
    2090              :                /* strings have a length of n_data */
    2091            0 :                if (tag[i].type == TID_STRING) {
    2092            0 :                   printf("%s\t", data_buffer + offset);
    2093            0 :                   offset += tag[i].n_data;
    2094            0 :                } else if (tag[i].n_data == 1) {
    2095              :                   /* non-array data */
    2096            0 :                   std::string data_str = db_sprintf(data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
    2097            0 :                   printf("%s\t", data_str.c_str());
    2098            0 :                   offset += rpc_tid_size(tag[i].type);
    2099            0 :                } else
    2100              :                   /* loop over array data */
    2101            0 :                   for (j = 0; j < (INT) tag[i].n_data; j++) {
    2102            0 :                      std::string data_str = db_sprintf(data_buffer + offset, rpc_tid_size(tag[i].type), 0, tag[i].type);
    2103            0 :                      printf("%s\t", data_str.c_str());
    2104            0 :                      offset += rpc_tid_size(tag[i].type);
    2105            0 :                   }
    2106              :             }
    2107            0 :             printf("\n");
    2108              :          }
    2109              :       }
    2110              : 
    2111              :       /* read next index record */
    2112            0 :       i = xread(fni, fhi, (char *) &irec, sizeof(irec), true);
    2113              : 
    2114              :       /* end of file: search next history file */
    2115            0 :       if (i <= 0) {
    2116            0 :          close(fh);
    2117            0 :          close(fhd);
    2118            0 :          close(fhi);
    2119              : 
    2120              :          /* advance one day */
    2121            0 :          ltime = (time_t) last_irec_time;
    2122              :          struct tm tms;
    2123            0 :          localtime_r(&ltime, &tms);
    2124            0 :          tms.tm_hour = tms.tm_min = tms.tm_sec = 0;
    2125            0 :          last_irec_time = (DWORD) ss_mktime(&tms);
    2126              : 
    2127            0 :          last_irec_time += 3600 * 24;
    2128            0 :          if (last_irec_time > end_time)
    2129            0 :             break;
    2130              : 
    2131              :          /* search next file */
    2132            0 :          status = hs_search_file((DWORD *) & last_irec_time, 1);
    2133            0 :          if (status != HS_SUCCESS)
    2134            0 :             break;
    2135              : 
    2136              :          /* open history and definition files */
    2137            0 :          hs_open_file(last_irec_time, "hst", O_RDONLY, &fn, &fh);
    2138            0 :          hs_open_file(last_irec_time, "idf", O_RDONLY, &fnd, &fhd);
    2139            0 :          hs_open_file(last_irec_time, "idx", O_RDONLY, &fni, &fhi);
    2140            0 :          if (fh < 0 || fhd < 0 || fhi < 0) {
    2141            0 :             cm_msg(MERROR, "hs_dump", "cannot open index files");
    2142            0 :             break;
    2143              :          }
    2144              : 
    2145              :          /* read first record */
    2146            0 :          i = xread(fni, fhi, (char *) &irec, sizeof(irec), true);
    2147            0 :          if (i <= 0)
    2148            0 :             break;
    2149              : 
    2150              :          /* old definition becomes invalid */
    2151            0 :          old_def_offset = -1;
    2152              :       }
    2153            0 :    } while (irec.time < end_time);
    2154              : 
    2155            0 :    M_FREE(tag);
    2156            0 :    M_FREE(old_tag);
    2157            0 :    close(fh);
    2158            0 :    close(fhd);
    2159            0 :    close(fhi);
    2160              : 
    2161            0 :    return HS_SUCCESS;
    2162            0 : }
    2163              : 
    2164              : /**dox***************************************************************/
    2165              : #ifndef DOXYGEN_SHOULD_SKIP_THIS
    2166              : 
    2167              : #endif                          /* OS_VXWORKS hs section */
    2168              : 
    2169              : /**dox***************************************************************/
    2170              : #endif                          /* DOXYGEN_SHOULD_SKIP_THIS */
    2171              : 
    2172              : #if 0
    2173              : char* sort_names(char* names)
    2174              : {
    2175              :    int i, p;
    2176              :    int len = 0;
    2177              :    int num = 0;
    2178              :    char* arr_names;
    2179              :    struct poor_mans_list sorted;
    2180              : 
    2181              :    for (i=0, p=0; names[p]!=0; i++) {
    2182              :       const char*pp = names+p;
    2183              :       int pplen = strlen(pp);
    2184              :       //printf("%d [%s] %d\n", i, pp, pplen);
    2185              :       if (pplen > len)
    2186              :          len = pplen;
    2187              :       p += strlen(names+p) + 1;
    2188              :    }
    2189              : 
    2190              :    num = i;
    2191              : 
    2192              :    len+=1; // space for string terminator '\0'
    2193              : 
    2194              :    arr_names = (char*)malloc(len*num);
    2195              : 
    2196              :    for (i=0, p=0; names[p]!=0; i++) {
    2197              :       const char*pp = names+p;
    2198              :       mstrlcpy(arr_names+i*len, pp, len);
    2199              :       p += strlen(names+p) + 1;
    2200              :    }
    2201              : 
    2202              :    free(names);
    2203              : 
    2204              :    qsort(arr_names, num, len, sort_tags);
    2205              : 
    2206              :    list_init(&sorted);
    2207              : 
    2208              :    for (i=0; i<num; i++)
    2209              :       list_add(&sorted, arr_names+i*len);
    2210              : 
    2211              :    return sorted.names;
    2212              : }
    2213              : #endif
    2214              : 
    2215              : /*------------------------------------------------------------------*/
    2216              : 
    2217              : class MidasHistory: public MidasHistoryInterface
    2218              : {
    2219              : public:
    2220              :    HNDLE fDB;
    2221              :    int fDebug;
    2222              : 
    2223              :    std::vector<std::string> fEventsCache;
    2224              :    std::map<std::string, std::vector<TAG> > fTagsCache;
    2225              :    std::map<std::string, int > fEvidCache;
    2226              : 
    2227              : public:
    2228            0 :    MidasHistory() // ctor
    2229            0 :    {
    2230            0 :       fDebug = 0;
    2231            0 :    }
    2232              : 
    2233            0 :    ~MidasHistory() // dtor
    2234            0 :    {
    2235              :       // empty
    2236            0 :    }
    2237              : 
    2238              :    /*------------------------------------------------------------------*/
    2239              : 
    2240            0 :    int hs_connect(const char* path)
    2241              :    {
    2242            0 :       cm_get_experiment_database(&fDB, NULL);
    2243              : 
    2244              :       /* delete obsolete odb entries */
    2245              : 
    2246              :       if (1) {
    2247              :          HNDLE hKey;
    2248            0 :          int status = db_find_key(fDB, 0, "/History/ListSource", &hKey);
    2249            0 :          if (status == DB_SUCCESS)
    2250            0 :             db_delete_key(fDB, hKey, FALSE);
    2251              :       }
    2252              : 
    2253            0 :       ::hs_set_path(path);
    2254              : 
    2255            0 :       if (fDebug)
    2256            0 :          printf("hs_connect: path [%s]\n", path);
    2257              : 
    2258            0 :       return HS_SUCCESS;
    2259              :    }
    2260              : 
    2261              :    /*------------------------------------------------------------------*/
    2262              : 
    2263            0 :    int hs_disconnect()
    2264              :    {
    2265            0 :       hs_clear_cache();
    2266            0 :       return HS_SUCCESS;
    2267              :    }
    2268              : 
    2269              :    /*------------------------------------------------------------------*/
    2270              : 
    2271            0 :    int hs_set_debug(int debug)
    2272              :    {
    2273            0 :       return debug;
    2274              :    }
    2275              : 
    2276              :    /*------------------------------------------------------------------*/
    2277              : 
    2278            0 :    int hs_clear_cache()
    2279              :    {
    2280            0 :       if (fDebug)
    2281            0 :          printf("hs_clear_cache!\n");
    2282              : 
    2283            0 :       fEventsCache.clear();
    2284            0 :       fTagsCache.clear();
    2285            0 :       fEvidCache.clear();
    2286            0 :       return HS_SUCCESS;
    2287              :    }
    2288              : 
    2289              :    /*------------------------------------------------------------------*/
    2290              : 
    2291            0 :    int FindEventId(const char* event_name)
    2292              :    {
    2293              :       HNDLE hKeyRoot;
    2294              :       int status;
    2295              :       char name[256];
    2296            0 :       mstrlcpy(name, event_name, sizeof(name));
    2297            0 :       char *s = strchr(name, '/');
    2298            0 :       if (s)
    2299            0 :          *s = ':';
    2300              : 
    2301              :       //printf("Looking for event id for \'%s\'\n", name);
    2302              :    
    2303            0 :       status = db_find_key(fDB, 0, "/History/Events", &hKeyRoot);
    2304            0 :       if (status == DB_SUCCESS) {
    2305            0 :          for (int i = 0;; i++) {
    2306              :             HNDLE hKey;
    2307              :             KEY key;
    2308              :          
    2309            0 :             status = db_enum_key(fDB, hKeyRoot, i, &hKey);
    2310            0 :             if (status != DB_SUCCESS)
    2311            0 :                break;
    2312              :          
    2313            0 :             status = db_get_key(fDB, hKey, &key);
    2314            0 :             assert(status == DB_SUCCESS);
    2315              :          
    2316              :             //printf("key \'%s\'\n", key.name);
    2317              : 
    2318            0 :             int evid = (WORD) strtol(key.name, NULL, 0);
    2319            0 :             if (evid == 0)
    2320            0 :                continue;
    2321              : 
    2322              :             char tmp[NAME_LENGTH+NAME_LENGTH+2];
    2323            0 :             int size = sizeof(tmp);
    2324            0 :             status = db_get_data(fDB, hKey, tmp, &size, TID_STRING);
    2325            0 :             assert(status == DB_SUCCESS);
    2326              : 
    2327              :             //printf("got %d \'%s\' looking for \'%s\'\n", evid, tmp, name);
    2328              : 
    2329            0 :             if (equal_ustring(name, tmp))
    2330            0 :                return evid;
    2331            0 :          }
    2332              :       }
    2333              : 
    2334            0 :       return -1;
    2335              :    }
    2336              : 
    2337              :    /*------------------------------------------------------------------*/
    2338              : 
    2339            0 :    int AllocateEventId(const char* event_name)
    2340              :    {
    2341              :       int status;
    2342              :       char name[256];
    2343            0 :       mstrlcpy(name, event_name, sizeof(name));
    2344            0 :       char *s = strchr(name, '/');
    2345            0 :       if (s)
    2346            0 :          *s = ':';
    2347              :       
    2348              :       // special event id for run transitions
    2349            0 :       if (strcmp(name, "Run transitions")==0) {
    2350            0 :          status = db_set_value(fDB, 0, "/History/Events/0", name, strlen(name)+1, 1, TID_STRING);
    2351            0 :          assert(status == DB_SUCCESS);
    2352            0 :          return 0;
    2353              :       }
    2354              : 
    2355              :       if (1) {
    2356            0 :          std::string tmp = msprintf("/Equipment/%s/Common/Event ID", name);
    2357              : 
    2358            0 :          WORD evid = 0;
    2359            0 :          int size = sizeof(evid);
    2360            0 :          status = db_get_value(fDB, 0, tmp.c_str(), &evid, &size, TID_WORD, FALSE);
    2361            0 :          if (status == DB_SUCCESS) {
    2362              : 
    2363            0 :             std::string he = msprintf("/History/Events/%d", evid);
    2364              : 
    2365            0 :             std::string xname;
    2366            0 :             status = db_get_value_string(fDB, 0, he.c_str(), 0, &xname);
    2367            0 :             if (status == DB_SUCCESS && xname != event_name) {
    2368            0 :                cm_msg(MERROR, "add_event", "History events \"%s\" and \"%s\" use the same history event id %d. If both equipments write to the history, their data will be mixed up. To fix this, enable per-variable history, turn off the \"MIDAS\" history (use \"FILE\" history) or change event IDs or set \"common/log history\" to zero", event_name, xname.c_str(), evid);
    2369              :             }
    2370              : 
    2371            0 :             status = db_set_value(fDB, 0, he.c_str(), name, strlen(name)+1, 1, TID_STRING);
    2372            0 :             assert(status == DB_SUCCESS);
    2373              : 
    2374              :             //printf("AllocateEventId: event [%s] allocated common/event id %d\n", event_name, evid);
    2375              : 
    2376            0 :             return evid;
    2377            0 :          }
    2378            0 :       }
    2379              : 
    2380            0 :       for (int evid = 101; evid < 65000; evid++) {
    2381              :          char tmp[256];
    2382              :          HNDLE hKey;
    2383              : 
    2384            0 :          sprintf(tmp,"/History/Events/%d", evid);
    2385              : 
    2386            0 :          status = db_find_key(fDB, 0, tmp, &hKey);
    2387            0 :          if (status != DB_SUCCESS) {
    2388              : 
    2389            0 :             status = db_set_value(fDB, 0, tmp, name, strlen(name)+1, 1, TID_STRING);
    2390            0 :             assert(status == DB_SUCCESS);
    2391              : 
    2392              :             //printf("AllocateEventId: event [%s] allocated next sequential id %d\n", event_name, evid);
    2393              : 
    2394            0 :             return evid;
    2395              :          }
    2396              :       }
    2397              : 
    2398            0 :       cm_msg(MERROR, "AllocateEventId", "Cannot allocate history event id - all in use - please examine /History/Events");
    2399            0 :       return -1;
    2400              :    }
    2401              : 
    2402              :    /*------------------------------------------------------------------*/
    2403              : 
    2404            0 :    int CreateOdbTags(int event_id, const char* event_name, int ntags, const TAG tags[])
    2405              :    {
    2406              :       int disableTags;
    2407              :       int oldTags;
    2408              :       int size, status;
    2409              : 
    2410              :       /* create history tags for mhttpd */
    2411              : 
    2412            0 :       disableTags = 0;
    2413            0 :       size = sizeof(disableTags);
    2414            0 :       status = db_get_value(fDB, 0, "/History/DisableTags", &disableTags, &size, TID_BOOL, TRUE);
    2415              : 
    2416            0 :       oldTags = 0;
    2417            0 :       size = sizeof(oldTags);
    2418            0 :       status = db_get_value(fDB, 0, "/History/CreateOldTags", &oldTags, &size, TID_BOOL, FALSE);
    2419              : 
    2420            0 :       if (disableTags) {
    2421              :          HNDLE hKey;
    2422              : 
    2423            0 :          status = db_find_key(fDB, 0, "/History/Tags", &hKey);
    2424            0 :          if (status == DB_SUCCESS) {
    2425            0 :             status = db_delete_key(fDB, hKey, FALSE);
    2426            0 :             if (status != DB_SUCCESS)
    2427            0 :                cm_msg(MERROR, "add_event", "Cannot delete /History/Tags, db_delete_key() status %d", status);
    2428              :          }
    2429              : 
    2430            0 :       } else if (oldTags) {
    2431              : 
    2432              :          char buf[256];
    2433              : 
    2434            0 :          sprintf(buf, "/History/Tags/%d", event_id);
    2435              : 
    2436              :          //printf("Set tag \'%s\' = \'%s\'\n", buf, event_name);
    2437              : 
    2438            0 :          status = db_set_value(fDB, 0, buf, (void*)event_name, strlen(event_name)+1, 1, TID_STRING);
    2439            0 :          assert(status == DB_SUCCESS);
    2440              : 
    2441            0 :          for (int i=0; i<ntags; i++) {
    2442            0 :             WORD v = (WORD) tags[i].n_data;
    2443            0 :             sprintf(buf, "/History/Tags/Tags %d/%s", event_id, tags[i].name);
    2444              : 
    2445              :             //printf("Set tag \'%s\' = %d\n", buf, v);
    2446              : 
    2447            0 :             status = db_set_value(fDB, 0, buf, &v, sizeof(v), 1, TID_WORD);
    2448            0 :             assert(status == DB_SUCCESS);
    2449              : 
    2450            0 :             if (strlen(tags[i].name) == NAME_LENGTH-1)
    2451            0 :                cm_msg(MERROR, "add_event",
    2452              :                       "Tag name \'%s\' in event %d (%s) may have been truncated to %d characters",
    2453            0 :                       tags[i].name, event_id, event_name, NAME_LENGTH-1);
    2454              :          }
    2455              : 
    2456              :       } else {
    2457              : 
    2458            0 :          const int kLength = 32 + NAME_LENGTH + NAME_LENGTH;
    2459              :          char buf[kLength];
    2460              :          HNDLE hKey;
    2461              : 
    2462            0 :          sprintf(buf, "/History/Tags/%d", event_id);
    2463            0 :          status = db_find_key(fDB, 0, buf, &hKey);
    2464              : 
    2465            0 :          if (status == DB_SUCCESS) {
    2466              :             // add new tags
    2467              :             KEY key;
    2468              : 
    2469            0 :             status = db_get_key(fDB, hKey, &key);
    2470            0 :             assert(status == DB_SUCCESS);
    2471              : 
    2472            0 :             assert(key.type == TID_STRING);
    2473              : 
    2474            0 :             if (key.item_size < kLength && key.num_values == 1) {
    2475              :                // old style tags are present. Convert them to new style!
    2476              : 
    2477              :                HNDLE hTags;
    2478              : 
    2479            0 :                cm_msg(MINFO, "add_event", "Converting old event %d (%s) tags to new style", event_id, event_name);
    2480              : 
    2481            0 :                mstrlcpy(buf, event_name, kLength);
    2482              : 
    2483            0 :                status = db_set_data(fDB, hKey, buf, kLength, 1, TID_STRING);
    2484            0 :                assert(status == DB_SUCCESS);
    2485              : 
    2486            0 :                sprintf(buf, "/History/Tags/Tags %d", event_id);
    2487              : 
    2488            0 :                status = db_find_key(fDB, 0, buf, &hTags);
    2489              : 
    2490            0 :                if (status == DB_SUCCESS) {
    2491            0 :                   for (int i=0; ; i++) {
    2492              :                      HNDLE h;
    2493              :                      int size;
    2494              :                      KEY key;
    2495              :                      WORD w;
    2496              : 
    2497            0 :                      status = db_enum_key(fDB, hTags, i, &h);
    2498            0 :                      if (status == DB_NO_MORE_SUBKEYS)
    2499            0 :                         break;
    2500            0 :                      assert(status == DB_SUCCESS);
    2501              : 
    2502            0 :                      status = db_get_key(fDB, h, &key);
    2503              : 
    2504            0 :                      size = sizeof(w);
    2505            0 :                      status = db_get_data(fDB, h, &w, &size, TID_WORD);
    2506            0 :                      assert(status == DB_SUCCESS);
    2507              : 
    2508            0 :                      sprintf(buf, "%d[%d] %s", 0, w, key.name);
    2509              :                   
    2510            0 :                      status = db_set_data_index(fDB, hKey, buf, kLength, 1+i, TID_STRING);
    2511            0 :                      assert(status == DB_SUCCESS);
    2512            0 :                   }
    2513              : 
    2514            0 :                   status = db_delete_key(fDB, hTags, TRUE);
    2515            0 :                   assert(status == DB_SUCCESS);
    2516              :                }
    2517              : 
    2518              :                // format conversion has changed the key, get it again
    2519            0 :                status = db_get_key(fDB, hKey, &key);
    2520            0 :                assert(status == DB_SUCCESS);
    2521              :             }
    2522              : 
    2523              :             if (1) {
    2524              :                // add new tags
    2525              :          
    2526            0 :                int size = key.item_size * key.num_values;
    2527            0 :                int num = key.num_values;
    2528              : 
    2529            0 :                char* s = (char*)malloc(size);
    2530            0 :                assert(s != NULL);
    2531              : 
    2532            0 :                TAG* t = (TAG*)malloc(sizeof(TAG)*(key.num_values + ntags));
    2533            0 :                assert(t != NULL);
    2534              : 
    2535            0 :                status = db_get_data(fDB, hKey, s, &size, TID_STRING);
    2536            0 :                assert(status == DB_SUCCESS);
    2537              : 
    2538            0 :                for (int i=1; i<key.num_values; i++) {
    2539            0 :                   char* ss = s + i*key.item_size;
    2540              : 
    2541            0 :                   t[i].type = 0;
    2542            0 :                   t[i].n_data = 0;
    2543            0 :                   t[i].name[0] = 0;
    2544              : 
    2545            0 :                   if (isdigit(ss[0])) {
    2546              :                      //sscanf(ss, "%d[%d] %s", &t[i].type, &t[i].n_data, t[i].name);
    2547              : 
    2548            0 :                      t[i].type = strtoul(ss, &ss, 0);
    2549            0 :                      assert(*ss == '[');
    2550            0 :                      ss++;
    2551            0 :                      t[i].n_data = strtoul(ss, &ss, 0);
    2552            0 :                      assert(*ss == ']');
    2553            0 :                      ss++;
    2554            0 :                      assert(*ss == ' ');
    2555            0 :                      ss++;
    2556            0 :                      mstrlcpy(t[i].name, ss, sizeof(t[i].name));
    2557              : 
    2558              :                      //printf("type %d, n_data %d, name [%s]\n", t[i].type, t[i].n_data, t[i].name);
    2559              :                   }
    2560              :                }
    2561              : 
    2562            0 :                for (int i=0; i<ntags; i++) {
    2563            0 :                   int k = 0;
    2564              : 
    2565            0 :                   for (int j=1; j<key.num_values; j++) {
    2566            0 :                      if (equal_ustring((char*)tags[i].name, (char*)t[j].name)) {
    2567            0 :                         if ((tags[i].type!=t[j].type) || (tags[i].n_data!=t[j].n_data)) {
    2568            0 :                            cm_msg(MINFO, "add_event", "Event %d (%s) tag \"%s\" type and size changed from %d[%d] to %d[%d]",
    2569              :                                   event_id, event_name,
    2570            0 :                                   tags[i].name,
    2571            0 :                                   t[j].type, t[j].n_data,
    2572            0 :                                   tags[i].type, tags[i].n_data);
    2573            0 :                            k = j;
    2574            0 :                            break;
    2575              :                         }
    2576              : 
    2577            0 :                         k = -1;
    2578            0 :                         break;
    2579              :                      }
    2580              :                   }
    2581              : 
    2582              :                   // if tag not present, k==0, so append it to the array
    2583              : 
    2584            0 :                   if (k==0)
    2585            0 :                      k = num;
    2586              : 
    2587            0 :                   if (k > 0) {
    2588            0 :                      sprintf(buf, "%d[%d] %s", tags[i].type, tags[i].n_data, tags[i].name);
    2589              : 
    2590            0 :                      status = db_set_data_index(fDB, hKey, buf, kLength, k, TID_STRING);
    2591            0 :                      assert(status == DB_SUCCESS);
    2592              : 
    2593            0 :                      if (k >= num)
    2594            0 :                         num = k+1;
    2595              :                   }
    2596              :                }
    2597              : 
    2598            0 :                free(s);
    2599            0 :                free(t);
    2600              :             }
    2601              : 
    2602            0 :          } else if (status == DB_NO_KEY) {
    2603              :             // create new array of tags
    2604            0 :             status = db_create_key(fDB, 0, buf, TID_STRING);
    2605            0 :             assert(status == DB_SUCCESS);
    2606              : 
    2607            0 :             status = db_find_key(fDB, 0, buf, &hKey);
    2608            0 :             assert(status == DB_SUCCESS);
    2609              : 
    2610            0 :             mstrlcpy(buf, event_name, kLength);
    2611              : 
    2612            0 :             status = db_set_data(fDB, hKey, buf, kLength, 1, TID_STRING);
    2613            0 :             assert(status == DB_SUCCESS);
    2614              : 
    2615            0 :             for (int i=0; i<ntags; i++) {
    2616            0 :                sprintf(buf, "%d[%d] %s", tags[i].type, tags[i].n_data, tags[i].name);
    2617              : 
    2618            0 :                status = db_set_data_index(fDB, hKey, buf, kLength, 1+i, TID_STRING);
    2619            0 :                assert(status == DB_SUCCESS);
    2620              :             }
    2621              :          } else {
    2622            0 :             cm_msg(MERROR, "add_event", "Error: db_find_key(%s) status %d", buf, status);
    2623            0 :             return HS_FILE_ERROR;
    2624              :          }
    2625              :       }
    2626              : 
    2627            0 :       return HS_SUCCESS;
    2628              :    }
    2629              : 
    2630              :    /*------------------------------------------------------------------*/
    2631              : 
    2632            0 :    int hs_define_event(const char* event_name, time_t timestamp, int ntags, const TAG tags[])
    2633              :    {
    2634            0 :       int event_id = FindEventId(event_name);
    2635            0 :       if (event_id < 0)
    2636            0 :          event_id = AllocateEventId(event_name);
    2637            0 :       if (event_id < 0)
    2638            0 :          return HS_FILE_ERROR;
    2639            0 :       fEvidCache[event_name] = event_id;
    2640            0 :       CreateOdbTags(event_id, event_name, ntags, tags);
    2641            0 :       return ::hs_define_event(event_id, (char*)event_name, (TAG*)tags, ntags*sizeof(TAG));
    2642              :    }
    2643              : 
    2644              :    /*------------------------------------------------------------------*/
    2645              : 
    2646            0 :    int hs_write_event(const char*  event_name, time_t timestamp, int data_size, const char* data)
    2647              :    {
    2648            0 :       int event_id = fEvidCache[event_name];
    2649              :       //printf("write event [%s] evid %d\n", event_name, event_id);
    2650            0 :       return ::hs_write_event(event_id, (void*)data, data_size);
    2651              :    }
    2652              : 
    2653              :    /*------------------------------------------------------------------*/
    2654              : 
    2655            0 :    int hs_flush_buffers()
    2656              :    {
    2657              :       //printf("hs_flush_buffers!\n");
    2658            0 :       return HS_SUCCESS;
    2659              :    }
    2660              : 
    2661              :    /*------------------------------------------------------------------*/
    2662              : 
    2663            0 :    int GetEventsFromOdbEvents(std::vector<std::string> *events)
    2664              :    {
    2665              :       HNDLE hKeyRoot;
    2666              :       int status;
    2667              : 
    2668            0 :       status = db_find_key(fDB, 0, "/History/Events", &hKeyRoot);
    2669            0 :       if (status != DB_SUCCESS) {
    2670            0 :          return HS_FILE_ERROR;
    2671              :       }
    2672              : 
    2673              :       /* loop over tags to display event names */
    2674            0 :       for (int i = 0;; i++) {
    2675              :          HNDLE hKeyEq;
    2676              :          char *s;
    2677              :          char evname[1024+NAME_LENGTH];
    2678              :          int size;
    2679              :       
    2680            0 :          status = db_enum_key(fDB, hKeyRoot, i, &hKeyEq);
    2681            0 :          if (status != DB_SUCCESS)
    2682            0 :             break;
    2683              :     
    2684            0 :          size = sizeof(evname);
    2685            0 :          status = db_get_data(fDB, hKeyEq, evname, &size, TID_STRING);
    2686            0 :          assert(status == DB_SUCCESS);
    2687              : 
    2688            0 :          s = strchr(evname,':');
    2689            0 :          if (s)
    2690            0 :             *s = '/';
    2691              : 
    2692              :          /* skip duplicated event names */
    2693              : 
    2694            0 :          int found = 0;
    2695            0 :          for (unsigned i=0; i<events->size(); i++) {
    2696            0 :             if (equal_ustring(evname, (*events)[i].c_str())) {
    2697            0 :                found = 1;
    2698            0 :                break;
    2699              :             }
    2700              :          }
    2701              :     
    2702            0 :          if (found)
    2703            0 :             continue;
    2704              : 
    2705            0 :          events->push_back(evname);
    2706              : 
    2707              :          //printf("event \'%s\'\n", evname);
    2708            0 :       }
    2709              : 
    2710            0 :       return HS_SUCCESS;
    2711              :    }
    2712              : 
    2713            0 :    int GetEventsFromOdbTags(std::vector<std::string> *events)
    2714              :    {
    2715              :       HNDLE hKeyRoot;
    2716              :       int status;
    2717              : 
    2718            0 :       status = db_find_key(fDB, 0, "/History/Tags", &hKeyRoot);
    2719            0 :       if (status != DB_SUCCESS) {
    2720            0 :          return HS_FILE_ERROR;
    2721              :       }
    2722              :    
    2723              :       /* loop over tags to display event names */
    2724            0 :       for (int i = 0;; i++) {
    2725              :          HNDLE hKeyEq;
    2726              :          KEY key;
    2727              :          char *s;
    2728              :          WORD event_id;
    2729              :          char evname[1024+NAME_LENGTH];
    2730              :          int size;
    2731              :       
    2732            0 :          status = db_enum_key(fDB, hKeyRoot, i, &hKeyEq);
    2733            0 :          if (status != DB_SUCCESS)
    2734            0 :             break;
    2735              :     
    2736              :          /* get event name */
    2737            0 :          db_get_key(fDB, hKeyEq, &key);
    2738              :       
    2739              :          //printf("key \'%s\'\n", key.name);
    2740              :       
    2741            0 :          if (key.type != TID_STRING)
    2742            0 :             continue;
    2743              : 
    2744              :          /* parse event name in format: "event_id" or "event_id:var_name" */
    2745            0 :          s = key.name;
    2746              :       
    2747            0 :          event_id = (WORD)strtoul(s,&s,0);
    2748            0 :          if (event_id == 0)
    2749            0 :             continue;
    2750            0 :          if (s[0] != 0)
    2751            0 :             continue;
    2752              : 
    2753            0 :          size = sizeof(evname);
    2754            0 :          status = db_get_data_index(fDB, hKeyEq, evname, &size, 0, TID_STRING);
    2755            0 :          assert(status == DB_SUCCESS);
    2756              : 
    2757              :          /* skip duplicated event names */
    2758              : 
    2759            0 :          int found = 0;
    2760            0 :          for (unsigned i=0; i<events->size(); i++) {
    2761            0 :             if (equal_ustring(evname, (*events)[i].c_str())) {
    2762            0 :                found = 1;
    2763            0 :                break;
    2764              :             }
    2765              :          }
    2766              :     
    2767            0 :          if (found)
    2768            0 :             continue;
    2769              : 
    2770            0 :          events->push_back(evname);
    2771              : 
    2772              :          //printf("event %d \'%s\'\n", event_id, evname);
    2773            0 :       }
    2774              : 
    2775            0 :       return HS_SUCCESS;
    2776              :    }
    2777              : 
    2778            0 :    int hs_get_events(time_t t, std::vector<std::string> *pevents)
    2779              :    {
    2780            0 :       assert(pevents);
    2781            0 :       pevents->clear();
    2782              : 
    2783            0 :       if (fEventsCache.size() == 0) {
    2784              :          int status;
    2785              : 
    2786            0 :          if (fDebug)
    2787            0 :             printf("hs_get_events: reading events list!\n");
    2788              :          
    2789            0 :          status = GetEventsFromOdbTags(&fEventsCache);
    2790              : 
    2791            0 :          if (status != HS_SUCCESS)
    2792            0 :             status = GetEventsFromOdbEvents(&fEventsCache);
    2793              : 
    2794            0 :          if (status != HS_SUCCESS)
    2795            0 :             return status;
    2796              :       }
    2797              : 
    2798            0 :       for (unsigned i=0; i<fEventsCache.size(); i++)
    2799            0 :          pevents->push_back(fEventsCache[i]);
    2800              :          
    2801            0 :       return HS_SUCCESS;
    2802              :    }
    2803              : 
    2804            0 :    int GetEventIdFromHS(time_t ltime, const char* evname, const char* tagname)
    2805              :    {
    2806              :       HNDLE hKeyRoot;
    2807              :       int status;
    2808              : 
    2809            0 :       status = db_find_key(fDB, 0, "/History/Events", &hKeyRoot);
    2810            0 :       if (status != DB_SUCCESS) {
    2811            0 :          return -1;
    2812              :       }
    2813              : 
    2814            0 :       for (int i = 0;; i++) {
    2815              :          HNDLE hKey;
    2816              :          KEY key;
    2817              :          int  evid;
    2818              :          char buf[256];
    2819              :          int size;
    2820              :          char *s;
    2821            0 :          int ntags = 0;
    2822            0 :          TAG* tags = NULL;
    2823              :          char event_name[NAME_LENGTH];
    2824              : 
    2825            0 :          status = db_enum_key(fDB, hKeyRoot, i, &hKey);
    2826            0 :          if (status != DB_SUCCESS)
    2827            0 :             break;
    2828              : 
    2829            0 :          status = db_get_key(fDB, hKey, &key);
    2830            0 :          assert(status == DB_SUCCESS);
    2831              : 
    2832            0 :          if (!isdigit(key.name[0]))
    2833            0 :             continue;
    2834              : 
    2835            0 :          evid = atoi(key.name);
    2836              : 
    2837            0 :          assert(key.item_size < (int)sizeof(buf));
    2838              : 
    2839            0 :          size = sizeof(buf);
    2840            0 :          status = db_get_data(fDB, hKey, buf, &size, TID_STRING);
    2841            0 :          assert(status == DB_SUCCESS);
    2842              : 
    2843            0 :          mstrlcpy(event_name, buf, sizeof(event_name));
    2844              : 
    2845            0 :          s = strchr(buf,':');
    2846            0 :          if (s)
    2847            0 :             *s = 0;
    2848              : 
    2849              :          //printf("Found event %d, event [%s] name [%s], looking for [%s][%s]\n", evid, event_name, buf, evname, tagname);
    2850              : 
    2851            0 :          if (!equal_ustring((char *)evname, buf))
    2852            0 :             continue;
    2853              : 
    2854            0 :          status = ::hs_get_tags((DWORD)ltime, evid, event_name, &ntags, &tags);
    2855              : 
    2856            0 :          for (int j=0; j<ntags; j++) {
    2857              :             //printf("at %d [%s] looking for [%s]\n", j, tags[j].name, tagname);
    2858              : 
    2859            0 :             if (equal_ustring((char *)tagname, tags[j].name)) {
    2860            0 :                if (tags)
    2861            0 :                   free(tags);
    2862            0 :                return evid;
    2863              :             }
    2864              :          }
    2865              : 
    2866            0 :          if (tags)
    2867            0 :             free(tags);
    2868            0 :          tags = NULL;
    2869            0 :       }
    2870              : 
    2871            0 :       return -1;
    2872              :    }
    2873              : 
    2874            0 :    int GetEventIdFromOdbTags(const char* evname, const char* tagname)
    2875              :    {
    2876              :       HNDLE hKeyRoot;
    2877              :       int status;
    2878              :       
    2879            0 :       status = db_find_key(fDB, 0, "/History/Tags", &hKeyRoot);
    2880            0 :       if (status != DB_SUCCESS) {
    2881            0 :          return -1;
    2882              :       }
    2883              : 
    2884            0 :       for (int i = 0;; i++) {
    2885              :          HNDLE hKey;
    2886              :          KEY key;
    2887              :          int evid;
    2888              :          char buf[256];
    2889              :          int size;
    2890              :          char *s;
    2891              : 
    2892            0 :          status = db_enum_key(fDB, hKeyRoot, i, &hKey);
    2893            0 :          if (status != DB_SUCCESS)
    2894            0 :             break;
    2895              : 
    2896            0 :          status = db_get_key(fDB, hKey, &key);
    2897            0 :          assert(status == DB_SUCCESS);
    2898              : 
    2899            0 :          if (key.type != TID_STRING)
    2900            0 :             continue;
    2901              : 
    2902            0 :          if (!isdigit(key.name[0]))
    2903            0 :             continue;
    2904              : 
    2905            0 :          evid = atoi(key.name);
    2906              : 
    2907            0 :          assert(key.item_size < (int)sizeof(buf));
    2908              : 
    2909            0 :          size = sizeof(buf);
    2910            0 :          status = db_get_data_index(fDB, hKey, buf, &size, 0, TID_STRING);
    2911            0 :          assert(status == DB_SUCCESS);
    2912              : 
    2913            0 :          s = strchr(buf,'/');
    2914            0 :          if (s)
    2915            0 :             *s = 0;
    2916              : 
    2917              :          //printf("Found event %d, name [%s], looking for [%s][%s]\n", evid, buf, evname, tagname);
    2918              : 
    2919            0 :          if (!equal_ustring((char *)evname, buf))
    2920            0 :             continue;
    2921              : 
    2922            0 :          for (int j=1; j<key.num_values; j++) {
    2923            0 :             size = sizeof(buf);
    2924            0 :             status = db_get_data_index(fDB, hKey, buf, &size, j, TID_STRING);
    2925            0 :             assert(status == DB_SUCCESS);
    2926              : 
    2927            0 :             if (!isdigit(buf[0]))
    2928            0 :                continue;
    2929              : 
    2930            0 :             s = strchr(buf,' ');
    2931            0 :             if (!s)
    2932            0 :                continue;
    2933              : 
    2934            0 :             s++;
    2935              :  
    2936              :             //printf("at %d [%s] [%s] compare to [%s]\n", j, buf, s, tagname);
    2937              : 
    2938            0 :             if (equal_ustring((char *)tagname, s)) {
    2939              :                //printf("Found evid %d\n", evid);
    2940            0 :                return evid;
    2941              :             }
    2942              :          }
    2943            0 :       }
    2944              : 
    2945            0 :       return -1;
    2946              :    }
    2947              : 
    2948            0 :    int GetEventId(time_t t, const char* event_name, const char* tag_name, int *pevid)
    2949              :    {
    2950            0 :       int event_id = -1;
    2951              : 
    2952            0 :       if (fDebug && event_name != NULL && tag_name != NULL)
    2953            0 :          printf("xhs_event_id for event [%s], tag [%s]\n", event_name, tag_name);
    2954              : 
    2955            0 :       *pevid = 0;
    2956              : 
    2957              :       /* use "/History/Tags" if available */
    2958            0 :       event_id = GetEventIdFromOdbTags(event_name, tag_name);
    2959              :       
    2960              :       /* if no Tags, use "/History/Events" and hs_get_tags() to read definition from history files */
    2961            0 :       if (event_id < 0)
    2962            0 :          event_id = GetEventIdFromHS(t, event_name, tag_name);
    2963              : 
    2964              :       /* if nothing works, use hs_get_event_id() */
    2965            0 :       if (event_id <= 0) {
    2966            0 :          DWORD evid = 0;
    2967            0 :          int status = ::hs_get_event_id((DWORD)t, (char*)event_name, &evid);
    2968            0 :          if (status != HS_SUCCESS)
    2969            0 :             return status;
    2970            0 :          event_id = evid;
    2971              :       }
    2972              :       
    2973            0 :       if (event_id < 0)
    2974            0 :          return HS_UNDEFINED_VAR;
    2975              :       
    2976            0 :       *pevid = event_id;
    2977              :       
    2978            0 :       return HS_SUCCESS;
    2979              :    }
    2980              :    
    2981            0 :    int GetTagsFromHS(const char* event_name, std::vector<TAG> *ptags)
    2982              :    {
    2983            0 :       time_t now = time(NULL);
    2984              :       int evid;
    2985            0 :       int status = GetEventId(now, event_name, NULL, &evid);
    2986            0 :       if (status != HS_SUCCESS)
    2987            0 :          return status;
    2988              :       
    2989            0 :       if (fDebug)
    2990            0 :          printf("hs_get_tags: get tags for event [%s] %d\n", event_name, evid);
    2991              :       
    2992              :       int ntags;
    2993              :       TAG* tags;
    2994            0 :       status =  ::hs_get_tags((DWORD)now, evid, (char*)event_name, &ntags, &tags);
    2995              :       
    2996            0 :       if (status != HS_SUCCESS)
    2997            0 :          return status;
    2998              :       
    2999            0 :       for (int i=0; i<ntags; i++)
    3000            0 :          ptags->push_back(tags[i]);
    3001              :       
    3002            0 :       if (tags)
    3003            0 :          free(tags);
    3004              :       
    3005            0 :       if (fDebug)
    3006            0 :          printf("hs_get_tags: get tags for event [%s] %d, found %d tags\n", event_name, evid, ntags);
    3007              : 
    3008            0 :       return HS_SUCCESS;
    3009              :    }
    3010              : 
    3011            0 :    int GetTagsFromOdb(const char* event_name, std::vector<TAG> *ptags)
    3012              :    {
    3013              :       HNDLE hKeyRoot;
    3014              :       int status;
    3015              : 
    3016            0 :       status = db_find_key(fDB, 0, "/History/Tags", &hKeyRoot);
    3017            0 :       if (status != DB_SUCCESS) {
    3018            0 :          return HS_FILE_ERROR;
    3019              :       }
    3020              :    
    3021              :       /* loop over equipment to display event name */
    3022            0 :       for (int i = 0;; i++) {
    3023              :          HNDLE hKey;
    3024              :          KEY key;
    3025              :          WORD event_id;
    3026              :          char buf[256];
    3027              :          int size;
    3028              :          char* s;
    3029              :       
    3030            0 :          status = db_enum_key(fDB, hKeyRoot, i, &hKey);
    3031            0 :          if (status != DB_SUCCESS)
    3032            0 :             break;
    3033              :     
    3034              :          /* get event name */
    3035            0 :          status = db_get_key(fDB, hKey, &key);
    3036            0 :          assert(status == DB_SUCCESS);
    3037              :       
    3038              :          /* parse event id */
    3039            0 :          if (!isdigit(key.name[0]))
    3040            0 :             continue;
    3041              : 
    3042            0 :          event_id = atoi(key.name);
    3043            0 :          if (event_id == 0)
    3044            0 :             continue;
    3045              : 
    3046            0 :          if (key.item_size >= (int)sizeof(buf))
    3047            0 :             continue;
    3048              : 
    3049            0 :          if (key.num_values == 1) { // old format of "/History/Tags"
    3050              : 
    3051              :             HNDLE hKeyDir;
    3052            0 :             sprintf(buf, "Tags %d", event_id);
    3053            0 :             status = db_find_key(fDB, hKeyRoot, buf, &hKeyDir);
    3054            0 :             if (status != DB_SUCCESS)
    3055            0 :                continue;
    3056              : 
    3057              :             /* loop over tags */
    3058            0 :             for (int j=0; ; j++) {
    3059              :                HNDLE hKey;
    3060              :                WORD array;
    3061              :                int size;
    3062              :                char var_name[NAME_LENGTH];
    3063              :             
    3064            0 :                status = db_enum_key(fDB, hKeyDir, j, &hKey);
    3065            0 :                if (status != DB_SUCCESS)
    3066            0 :                   break;
    3067              :             
    3068              :                /* get event name */
    3069            0 :                status = db_get_key(fDB, hKey, &key);
    3070            0 :                assert(status == DB_SUCCESS);
    3071              :             
    3072            0 :                array = 1;
    3073            0 :                size  = sizeof(array);
    3074            0 :                status = db_get_data(fDB, hKey, &array, &size, TID_WORD);
    3075            0 :                assert(status == DB_SUCCESS);
    3076              :             
    3077            0 :                mstrlcpy(var_name, key.name, sizeof(var_name));
    3078              :             
    3079              :                //printf("Found %s, event %d (%s), tag (%s) array %d\n", key.name, event_id, event_name, var_name, array);
    3080              :             
    3081              :                TAG t;
    3082            0 :                mstrlcpy(t.name, var_name, sizeof(t.name));
    3083            0 :                t.n_data = array;
    3084            0 :                t.type = 0;
    3085              :                
    3086            0 :                ptags->push_back(t);
    3087            0 :             }
    3088              : 
    3089            0 :             continue;
    3090            0 :          }
    3091              : 
    3092            0 :          if (key.type != TID_STRING)
    3093            0 :             continue;
    3094              : 
    3095            0 :          size = sizeof(buf);
    3096            0 :          status = db_get_data_index(fDB, hKey, buf, &size, 0, TID_STRING);
    3097            0 :          assert(status == DB_SUCCESS);
    3098              : 
    3099            0 :          if (strchr(event_name, '/')==NULL) {
    3100            0 :             char* s = strchr(buf, '/');
    3101            0 :             if (s)
    3102            0 :                *s = 0;
    3103              :          }
    3104              : 
    3105              :          //printf("evid %d, name [%s]\n", event_id, buf);
    3106              : 
    3107            0 :          if (!equal_ustring(buf, event_name))
    3108            0 :             continue;
    3109              : 
    3110              :          /* loop over tags */
    3111            0 :          for (int j=1; j<key.num_values; j++) {
    3112              :             int array;
    3113              :             int size;
    3114              :             char var_name[NAME_LENGTH];
    3115              :             int ev_type;
    3116              :          
    3117            0 :             size = sizeof(buf);
    3118            0 :             status = db_get_data_index(fDB, hKey, buf, &size, j, TID_STRING);
    3119            0 :             assert(status == DB_SUCCESS);
    3120              : 
    3121              :             //printf("index %d [%s]\n", j, buf);
    3122              : 
    3123            0 :             if (!isdigit(buf[0]))
    3124            0 :                continue;
    3125              : 
    3126            0 :             sscanf(buf, "%d[%d]", &ev_type, &array);
    3127              : 
    3128            0 :             s = strchr(buf, ' ');
    3129            0 :             if (!s)
    3130            0 :                continue;
    3131            0 :             s++;
    3132              : 
    3133            0 :             mstrlcpy(var_name, s, sizeof(var_name));
    3134              : 
    3135              :             TAG t;
    3136            0 :             mstrlcpy(t.name, var_name, sizeof(t.name));
    3137            0 :             t.n_data = array;
    3138            0 :             t.type = ev_type;
    3139              : 
    3140              :             //printf("Found %s, event %d, tag (%s) array %d, type %d\n", buf, event_id, var_name, array, ev_type);
    3141              : 
    3142            0 :             ptags->push_back(t);
    3143              :          }
    3144            0 :       }
    3145              : 
    3146            0 :       return HS_SUCCESS;
    3147              :    }
    3148              : 
    3149              :    /*------------------------------------------------------------------*/
    3150              : 
    3151            0 :    int hs_get_tags(const char* event_name, time_t t, std::vector<TAG> *ptags)
    3152              :    {
    3153            0 :       std::vector<TAG>& ttt = fTagsCache[event_name];
    3154              : 
    3155            0 :       if (ttt.size() == 0) {
    3156            0 :          int status = HS_FILE_ERROR;
    3157              : 
    3158            0 :          if (fDebug)
    3159            0 :             printf("hs_get_tags: reading tags for event [%s]\n", event_name);
    3160              : 
    3161            0 :          status = GetTagsFromOdb(event_name, &ttt);
    3162              : 
    3163            0 :          if (status != HS_SUCCESS)
    3164            0 :             status = GetTagsFromHS(event_name, &ttt);
    3165              : 
    3166            0 :          if (status != HS_SUCCESS)
    3167            0 :             return status;
    3168              :       }
    3169              : 
    3170            0 :       for (unsigned i=0; i<ttt.size(); i++)
    3171            0 :          ptags->push_back(ttt[i]);
    3172              : 
    3173            0 :       return HS_SUCCESS;
    3174              :    }
    3175              : 
    3176              :    /*------------------------------------------------------------------*/
    3177              : 
    3178            0 :    int hs_get_last_written(time_t start_time, int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[], time_t last_written[])
    3179              :    {
    3180            0 :       for (int i=0; i<num_var; i++)
    3181            0 :          last_written[i] = 0;
    3182            0 :       return HS_FILE_ERROR;
    3183              :    }
    3184              : 
    3185              :    /*------------------------------------------------------------------*/
    3186              : 
    3187              : 
    3188            0 :    int hs_read(time_t start_time, time_t end_time, time_t interval,
    3189              :                int num_var,
    3190              :                const char* const event_name[], const char* const tag_name[], const int var_index[],
    3191              :                int num_entries[],
    3192              :                time_t* time_buffer[], double* data_buffer[],
    3193              :                int read_status[])
    3194              :    {
    3195            0 :       DWORD* tbuffer = NULL;
    3196            0 :       char* ybuffer = NULL;
    3197              :       DWORD bsize, tsize;
    3198            0 :       int hbuffer_size = 0;
    3199              :       
    3200            0 :       if (hbuffer_size == 0) {
    3201            0 :          hbuffer_size = 1000 * sizeof(DWORD);
    3202            0 :          tbuffer = (DWORD*)malloc(hbuffer_size);
    3203            0 :          ybuffer = (char*)malloc(hbuffer_size);
    3204              :       }
    3205              : 
    3206            0 :       for (int i=0; i<num_var; i++) {
    3207            0 :          DWORD tid = 0;
    3208            0 :          int event_id = 0;
    3209              : 
    3210            0 :          if (event_name[i]==NULL) {
    3211            0 :             read_status[i] = HS_UNDEFINED_EVENT;
    3212            0 :             num_entries[i] = 0;
    3213            0 :             continue;
    3214              :          }
    3215              :          
    3216            0 :          int status = GetEventId(end_time, event_name[i], tag_name[i], &event_id);
    3217              :          
    3218            0 :          if (status != HS_SUCCESS) {
    3219            0 :             read_status[i] = status;
    3220            0 :             continue;
    3221              :          }
    3222              :          
    3223            0 :          DWORD n_point = 0;
    3224              :          
    3225              :          do {
    3226            0 :             bsize = tsize = hbuffer_size;
    3227            0 :             memset(ybuffer, 0, bsize);
    3228            0 :             status = ::hs_read(event_id, (DWORD)start_time, (DWORD)end_time, (DWORD)interval,
    3229            0 :                                tag_name[i], var_index[i],
    3230              :                                tbuffer, &tsize,
    3231              :                                ybuffer, &bsize,
    3232              :                                &tid, &n_point,
    3233              :                                NULL);
    3234              :          
    3235            0 :             if (fDebug)
    3236            0 :                printf("hs_read %d \'%s\' [%d] returned %d, %d entries\n", event_id, tag_name[i], var_index[i], status, n_point);
    3237              :          
    3238            0 :             if (status == HS_TRUNCATED) {
    3239            0 :                hbuffer_size *= 2;
    3240            0 :                tbuffer = (DWORD*)realloc(tbuffer, hbuffer_size);
    3241            0 :                assert(tbuffer);
    3242            0 :                ybuffer = (char*)realloc(ybuffer, hbuffer_size);
    3243            0 :                assert(ybuffer);
    3244              :             }
    3245              : 
    3246            0 :          } while (status == HS_TRUNCATED);
    3247              :         
    3248            0 :          read_status[i] = status;
    3249              : 
    3250            0 :          time_t* x = (time_t*)malloc(n_point*sizeof(time_t));
    3251            0 :          assert(x);
    3252            0 :          double* y = (double*)malloc(n_point*sizeof(double));
    3253            0 :          assert(y);
    3254              : 
    3255            0 :          time_buffer[i] = x;
    3256            0 :          data_buffer[i] = y;
    3257              : 
    3258            0 :          int n_vp = 0;
    3259              : 
    3260            0 :          for (unsigned j = 0; j < n_point; j++) {
    3261            0 :             x[n_vp] = tbuffer[j];
    3262              :           
    3263              :             /* convert data to float */
    3264            0 :             switch (tid) {
    3265            0 :             default:
    3266            0 :                y[n_vp] =  0;
    3267            0 :                break;
    3268            0 :             case TID_BYTE:
    3269            0 :                y[n_vp] =  *(((BYTE *) ybuffer) + j);
    3270            0 :                break;
    3271            0 :             case TID_SBYTE:
    3272            0 :                y[n_vp] =  *(((char *) ybuffer) + j);
    3273            0 :                break;
    3274            0 :             case TID_CHAR:
    3275            0 :                y[n_vp] =  *(((char *) ybuffer) + j);
    3276            0 :                break;
    3277            0 :             case TID_WORD:
    3278            0 :                y[n_vp] =  *(((WORD *) ybuffer) + j);
    3279            0 :                break;
    3280            0 :             case TID_SHORT:
    3281            0 :                y[n_vp] =  *(((short *) ybuffer) + j);
    3282            0 :                break;
    3283            0 :             case TID_DWORD:
    3284            0 :                y[n_vp] =  *(((DWORD *) ybuffer) + j);
    3285            0 :                break;
    3286            0 :             case TID_INT:
    3287            0 :                y[n_vp] =  *(((INT *) ybuffer) + j);
    3288            0 :                break;
    3289            0 :             case TID_BOOL:
    3290            0 :                y[n_vp] =  *(((BOOL *) ybuffer) + j);
    3291            0 :                break;
    3292            0 :             case TID_FLOAT:
    3293            0 :                y[n_vp] =  *(((float *) ybuffer) + j);
    3294            0 :                break;
    3295            0 :             case TID_DOUBLE:
    3296            0 :                y[n_vp] =  *(((double *) ybuffer) + j);
    3297            0 :                break;
    3298              :             }
    3299              :           
    3300            0 :             n_vp++;
    3301              :          }
    3302              : 
    3303            0 :          num_entries[i] = n_vp;
    3304              :       }
    3305              : 
    3306            0 :       if (ybuffer)
    3307            0 :          free(ybuffer);
    3308            0 :       if (tbuffer)
    3309            0 :          free(tbuffer);
    3310              : 
    3311            0 :       return HS_SUCCESS;
    3312              :    }
    3313              : 
    3314              :    /*------------------------------------------------------------------*/
    3315              : #if 0
    3316              :    int hs_read2(time_t start_time, time_t end_time, time_t interval,
    3317              :                 int num_var,
    3318              :                 const char* const event_name[], const char* const tag_name[], const int var_index[],
    3319              :                 int num_entries[],
    3320              :                 time_t* time_buffer[],
    3321              :                 double* mean_buffer[],
    3322              :                 double* rms_buffer[],
    3323              :                 double* min_buffer[],
    3324              :                 double* max_buffer[],
    3325              :                 int read_status[])
    3326              :    {
    3327              :       int status = hs_read(start_time, end_time, interval, num_var, event_name, tag_name, var_index, num_entries, time_buffer, mean_buffer, read_status);
    3328              : 
    3329              :       for (int i=0; i<num_var; i++) {
    3330              :          int num = num_entries[i];
    3331              :          rms_buffer[i] = (double*)malloc(sizeof(double)*num);
    3332              :          min_buffer[i] = (double*)malloc(sizeof(double)*num);
    3333              :          max_buffer[i] = (double*)malloc(sizeof(double)*num);
    3334              : 
    3335              :          for (int j=0; j<num; j++) {
    3336              :             rms_buffer[i][j] = 0;
    3337              :             min_buffer[i][j] = mean_buffer[i][j];
    3338              :             max_buffer[i][j] = mean_buffer[i][j];
    3339              :          }
    3340              :       }
    3341              :       
    3342              :       return status;
    3343              :    }
    3344              : #endif
    3345              : 
    3346            0 :    int hs_read_buffer(time_t start_time, time_t end_time,
    3347              :                       int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[],
    3348              :                       MidasHistoryBufferInterface* buffer[],
    3349              :                       int read_status[])
    3350              :    {
    3351            0 :       for (int i=0; i<num_var; i++) {
    3352            0 :          int event_id = 0;
    3353              : 
    3354            0 :          if (event_name[i]==NULL) {
    3355            0 :             read_status[i] = HS_UNDEFINED_EVENT;
    3356            0 :             continue;
    3357              :          }
    3358              :          
    3359            0 :          int status = GetEventId(end_time, event_name[i], tag_name[i], &event_id);
    3360              :          
    3361            0 :          if (status != HS_SUCCESS) {
    3362            0 :             read_status[i] = status;
    3363            0 :             continue;
    3364              :          }
    3365              :          
    3366            0 :          status = ::hs_read(event_id, (DWORD)start_time, (DWORD)end_time, 0,
    3367            0 :                             tag_name[i], var_index[i],
    3368              :                             NULL, NULL,
    3369              :                             NULL, NULL,
    3370              :                             NULL, NULL,
    3371            0 :                             buffer[i]);
    3372              :          
    3373            0 :          if (fDebug) {
    3374            0 :             printf("hs_read %d \'%s\' [%d] returned %d\n", event_id, tag_name[i], var_index[i], status);
    3375              :          }
    3376              :         
    3377            0 :          read_status[i] = status;
    3378              :       }
    3379              : 
    3380            0 :       return HS_SUCCESS;
    3381              :    }
    3382              :    
    3383            0 :    int hs_read_binned(time_t start_time, time_t end_time, int num_bins,
    3384              :                       int num_var, const char* const event_name[], const char* const tag_name[], const int var_index[],
    3385              :                       int num_entries[],
    3386              :                       int* count_bins[], double* mean_bins[], double* rms_bins[], double* min_bins[], double* max_bins[],
    3387              :                       time_t* bins_first_time[], double* bins_first_value[],
    3388              :                       time_t* bins_last_time[], double* bins_last_value[],
    3389              :                       time_t last_time[], double last_value[],
    3390              :                       int read_status[])
    3391              :    {
    3392              :       int status;
    3393              : 
    3394            0 :       MidasHistoryBinnedBuffer** buffer = new MidasHistoryBinnedBuffer*[num_var];
    3395            0 :       MidasHistoryBufferInterface** xbuffer = new MidasHistoryBufferInterface*[num_var];
    3396              : 
    3397            0 :       for (int i=0; i<num_var; i++) {
    3398            0 :          buffer[i] = new MidasHistoryBinnedBuffer(start_time, end_time, num_bins);
    3399            0 :          xbuffer[i] = buffer[i];
    3400              : 
    3401            0 :          if (count_bins)
    3402            0 :             buffer[i]->fCount = count_bins[i];
    3403            0 :          if (mean_bins)
    3404            0 :             buffer[i]->fMean = mean_bins[i];
    3405            0 :          if (rms_bins)
    3406            0 :             buffer[i]->fRms = rms_bins[i];
    3407            0 :          if (min_bins)
    3408            0 :             buffer[i]->fMin = min_bins[i];
    3409            0 :          if (max_bins)
    3410            0 :             buffer[i]->fMax = max_bins[i];
    3411            0 :          if (bins_first_time)
    3412            0 :             buffer[i]->fBinsFirstTime = bins_first_time[i];
    3413            0 :          if (bins_first_value)
    3414            0 :             buffer[i]->fBinsFirstValue = bins_first_value[i];
    3415            0 :          if (bins_last_time)
    3416            0 :             buffer[i]->fBinsLastTime = bins_last_time[i];
    3417            0 :          if (bins_last_value)
    3418            0 :             buffer[i]->fBinsLastValue = bins_last_value[i];
    3419            0 :          if (last_time)
    3420            0 :             buffer[i]->fLastTimePtr = &last_time[i];
    3421            0 :          if (last_value)
    3422            0 :             buffer[i]->fLastValuePtr = &last_value[i];
    3423              : 
    3424            0 :          buffer[i]->Start();
    3425              :       }
    3426              : 
    3427            0 :       status = hs_read_buffer(start_time, end_time,
    3428              :                               num_var, event_name, tag_name, var_index,
    3429              :                               xbuffer,
    3430              :                               read_status);
    3431              : 
    3432            0 :       for (int i=0; i<num_var; i++) {
    3433            0 :          buffer[i]->Finish();
    3434            0 :          if (num_entries)
    3435            0 :             num_entries[i] = buffer[i]->fNumEntries;
    3436              :          //if (0) {
    3437              :          //   for (int j=0; j<num_bins; j++) {
    3438              :          //      printf("var %d bin %d count %d, first %s last %s value first %f last %f\n", i, j, count_bins[i][j], TimeToString(bins_first_time[i][j]).c_str(), TimeToString(bins_last_time[i][j]).c_str(), bins_first_value[i][j], bins_last_value[i][j]);
    3439              :          //   }
    3440              :          //}
    3441            0 :          delete buffer[i];
    3442              :       }
    3443              : 
    3444            0 :       delete[] buffer;
    3445            0 :       delete[] xbuffer;
    3446              : 
    3447            0 :       return status;
    3448              :    }
    3449              : 
    3450              : }; // end class
    3451              : 
    3452              : 
    3453              : /********************************************************************/
    3454              : /**
    3455              : Define history panel in ODB with certain variables and default
    3456              : values for everything else
    3457              : @param group            History group name
    3458              : @param panel            Historyh panel name
    3459              : @param var              Vector of variables
    3460              : @return HS_SUCCESS
    3461              : */
    3462              : /********************************************************************/
    3463              : 
    3464              : #define HISTORY_PANEL(_name) const char *_name[] = {\
    3465              : "[.]",\
    3466              : "Variables = STRING : [64] :",\
    3467              : "Timescale = STRING : [32] 10m",\
    3468              : "Zero ylow = BOOL : n",\
    3469              : "Show run markers = BOOL : y",\
    3470              : "Buttons = STRING[7] :",\
    3471              : "[32] 10m",\
    3472              : "[32] 1h",\
    3473              : "[32] 3h",\
    3474              : "[32] 12h",\
    3475              : "[32] 24h",\
    3476              : "[32] 3d",\
    3477              : "[32] 7d",\
    3478              : "Log axis = BOOL : n",\
    3479              : "Show values = BOOL : y",\
    3480              : "Sort vars = BOOL : n",\
    3481              : "Show old vars = BOOL : n",\
    3482              : "Minimum = DOUBLE : -inf",\
    3483              : "Maximum = DOUBLE : inf",\
    3484              : "Label = STRING : [32] ",\
    3485              : "Colour = STRING : [32] ",\
    3486              : "Formula = STRING : [64] ",\
    3487              : "Show fill = BOOL : y",\
    3488              : "",\
    3489              : NULL }
    3490              : 
    3491              : 
    3492            0 : INT hs_define_panel(const char *group, const char *panel, const std::vector<std::string> var)
    3493              : {
    3494              :    HNDLE hDB, hKey, hKeyVar;
    3495            0 :    HISTORY_PANEL(history_panel_str);
    3496              :    char str[256];
    3497              : 
    3498            0 :    const char *color[] = {
    3499              :            "#00AAFF", "#FF9000", "#FF00A0", "#00C030",
    3500              :            "#A0C0D0", "#D0A060", "#C04010", "#807060",
    3501              :            "#F0C000", "#2090A0", "#D040D0", "#90B000",
    3502              :            "#B0B040", "#B0B0FF", "#FFA0A0", "#A0FFA0",
    3503              :            "#808080"};
    3504              : 
    3505            0 :    cm_get_experiment_database(&hDB, nullptr);
    3506              : 
    3507            0 :    snprintf(str, sizeof(str), "/History/Display/%s/%s", group, panel);
    3508              : 
    3509            0 :    db_create_record(hDB, 0, str, strcomb1(history_panel_str).c_str());
    3510            0 :    db_find_key(hDB, 0, str, &hKey);
    3511            0 :    if (!hKey)
    3512            0 :       return DB_NO_MEMORY;
    3513              : 
    3514            0 :    int i=0;
    3515            0 :    for(auto const& v: var) {
    3516            0 :       db_find_key(hDB, hKey, "Variables", &hKeyVar);
    3517            0 :       db_set_data_index(hDB, hKeyVar, v.c_str(), 64, i, TID_STRING);
    3518              : 
    3519            0 :       str[0] = 0;
    3520            0 :       db_set_value_index(hDB, hKey, "Formula", str, 64, i, TID_STRING, false);
    3521            0 :       db_set_value_index(hDB, hKey, "Label", str, 32, i, TID_STRING, false);
    3522            0 :       db_set_value_index(hDB, hKey, "Colour", color[i < 16 ? i : 16], 32, i, TID_STRING, false);
    3523              : 
    3524            0 :       i++;
    3525              :    }
    3526              : 
    3527            0 :    return HS_SUCCESS;
    3528              : }
    3529              : 
    3530            0 : INT hs_define_panel2(const char *group, const char *panel, const std::vector<std::string> var,
    3531              :                      const std::vector<std::string> label, const std::vector<std::string> formula,
    3532              :                      const std::vector<std::string> color)
    3533              : {
    3534              :    HNDLE hDB, hKey, hKeyVar;
    3535            0 :    HISTORY_PANEL(history_panel_str);
    3536              :    char str[256];
    3537              : 
    3538            0 :    const char *default_color[] = {
    3539              :       "#00AAFF", "#FF9000", "#FF00A0", "#00C030",
    3540              :       "#A0C0D0", "#D0A060", "#C04010", "#807060",
    3541              :       "#F0C000", "#2090A0", "#D040D0", "#90B000",
    3542              :       "#B0B040", "#B0B0FF", "#FFA0A0", "#A0FFA0",
    3543              :       "#808080"};
    3544              : 
    3545            0 :    cm_get_experiment_database(&hDB, nullptr);
    3546              : 
    3547            0 :    snprintf(str, sizeof(str), "/History/Display/%s/%s", group, panel);
    3548              : 
    3549            0 :    db_create_record(hDB, 0, str, strcomb1(history_panel_str).c_str());
    3550            0 :    db_find_key(hDB, 0, str, &hKey);
    3551            0 :    if (!hKey)
    3552            0 :       return DB_NO_MEMORY;
    3553              : 
    3554            0 :    int i=0;
    3555            0 :    for(auto const& v: var) {
    3556            0 :       db_find_key(hDB, hKey, "Variables", &hKeyVar);
    3557            0 :       db_set_data_index(hDB, hKeyVar, v.c_str(), 64, i, TID_STRING);
    3558              : 
    3559            0 :       if (i < (int)formula.size())
    3560            0 :          mstrlcpy(str, formula[i].c_str(), sizeof(str)-1);
    3561              :       else
    3562            0 :          str[0] = 0;
    3563            0 :       db_set_value_index(hDB, hKey, "Formula", str, 64, i, TID_STRING, false);
    3564              : 
    3565            0 :       if (i < (int)label.size())
    3566            0 :          mstrlcpy(str, label[i].c_str(), sizeof(str)-1);
    3567              :       else
    3568            0 :          str[0] = 0;
    3569            0 :       db_set_value_index(hDB, hKey, "Label", str, 32, i, TID_STRING, false);
    3570              : 
    3571            0 :       if (i < (int)color.size())
    3572            0 :          mstrlcpy(str, color[i].c_str(), sizeof(str)-1);
    3573              :       else
    3574            0 :          mstrlcpy(str, default_color[i < 16 ? i : 16], sizeof(str)-1);
    3575            0 :       db_set_value_index(hDB, hKey, "Colour", str, 32, i, TID_STRING, false);
    3576              : 
    3577            0 :       i++;
    3578              :    }
    3579              : 
    3580            0 :    return HS_SUCCESS;
    3581              : }
    3582              : 
    3583            0 : MidasHistoryInterface* MakeMidasHistory()
    3584              : {
    3585              : #if 0
    3586              :    // midas history is a singleton class
    3587              :    static MidasHistory* gh = NULL;
    3588              :    if (!gh)
    3589              :       gh = new MidasHistory;
    3590              :    return gh;
    3591              : #endif
    3592            0 :    return new MidasHistory();
    3593              : }
    3594              : 
    3595              : /* emacs
    3596              :  * Local Variables:
    3597              :  * tab-width: 8
    3598              :  * c-basic-offset: 3
    3599              :  * indent-tabs-mode: nil
    3600              :  * End:
    3601              :  */
        

Generated by: LCOV version 2.0-1