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(<, &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(<ime, &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(<ime, &tmr); // somebody must call tzset() before this.
724 0 : ltime = (time_t) _history[index]->base_time;
725 0 : localtime_r(<ime, &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(<ime, -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(<ime, -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(<, -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(<ime, -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(<ime, -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(<ime, -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(<ime, -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(<ime, &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(<ime, 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(<ime, &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 : */
|